closure-compiler-20130227+dfsg1/0000755000175000017500000000000014433667712014375 5ustar apoapoclosure-compiler-20130227+dfsg1/test/0000755000175000017500000000000012115204405015331 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/0000755000175000017500000000000012115204405016107 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/0000755000175000017500000000000012115204405017363 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/debugging/0000755000175000017500000000000012115204405021316 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/0000755000175000017500000000000012115204405023314 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapGeneratorV1Test.java0000644000175000017500000004677312115204405031014 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.javascript.jscomp.SourceMap; import com.google.javascript.jscomp.SourceMap.Format; /** * Tests for {@link SourceMap}. * */ public class SourceMapGeneratorV1Test extends SourceMapTestCase { public SourceMapGeneratorV1Test() { disableColumnValidation(); } @Override protected SourceMapConsumer getSourceMapConsumer() { return new SourceMapConsumerV1(); } @Override protected Format getSourceMapFormat() { return SourceMap.Format.V1; } @Override public void setUp() { detailLevel = SourceMap.DetailLevel.ALL; } public void testBasicMapping() throws Exception { compileAndCheck("function __BASIC__() { }"); } public void testLiteralMappings() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + "var __VAR__ = '__STR__'; }"); } public void testMultilineMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}"); } public void testMultiFunctionMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}\n\n" + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + "var __VAR2__ = '__STR2__';\n" + "var __ANO2__ = \"__STR3__\";\n" + "}\n\n"); } public void testGoldenOutput0() throws Exception { // Empty source map test checkSourceMap("", "/** Begin line maps. **/{ \"file\" : \"testcode\"," + " \"count\": 1 }\n" + "[]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n"); } public void testFunctionNameOutput1() throws Exception { checkSourceMap("function f() {}", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0,0,0,0,0,0,1,1,2,2,3,3]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0,\"f\"]\n" + "[\"testcode\",1,9,\"f\"]\n" + "[\"testcode\",1,10]\n" + "[\"testcode\",1,13]\n"); } public void testFunctionNameOutput2() throws Exception { checkSourceMap("a.b.c = function () {};", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[3,2,2,1,1,0,4,4,4,4,4,4,4,4,5,5,6,6]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0]\n" + "[\"testcode\",1,0,\"c\"]\n" + "[\"testcode\",1,0,\"b\"]\n" + "[\"testcode\",1,0,\"a\"]\n" + "[\"testcode\",1,8,\"a.b.c\"]\n" + "[\"testcode\",1,17]\n" + "[\"testcode\",1,20]\n"); } public void testFunctionNameOutput3() throws Exception { checkSourceMap("var q = function () {};", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0,0,1,1,2,2,2,2,2,2,2,2,3,3,4,4]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0]\n" + "[\"testcode\",1,4,\"q\"]\n" + "[\"testcode\",1,8,\"q\"]\n" + "[\"testcode\",1,17]\n" + "[\"testcode\",1,20]\n"); } public void testFunctionNameOutput4() throws Exception { checkSourceMap("({ 'q' : function () {} })", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,1,1,1,0,2,2,2,2,2,2,2,2,3,3,4,4,0,0]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,1]\n" + "[\"testcode\",1,3]\n" + "[\"testcode\",1,9,\"q\"]\n" + "[\"testcode\",1,18]\n" + "[\"testcode\",1,21]\n"); } public void testGoldenOutput1() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0,0,0,0,0,0,1,1,2,3,3,3,2,4,4,4,2,5,7,7,7,6,8,8,8,6," + "9,9,9,6,10,11,11,11,11,11,11,11,12,12,12,12,5]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0,\"f\"]\n" + "[\"testcode\",1,9,\"f\"]\n" + "[\"testcode\",1,10]\n" + "[\"testcode\",1,11,\"foo\"]\n" + "[\"testcode\",1,16,\"bar\"]\n" + "[\"testcode\",1,21]\n" + "[\"testcode\",1,23]\n" + "[\"testcode\",1,23,\"foo\"]\n" + "[\"testcode\",1,29,\"foo\"]\n" + "[\"testcode\",1,35,\"bar\"]\n" + "[\"testcode\",1,41]\n" + "[\"testcode\",1,44]\n" + "[\"testcode\",1,51,\"foo\"]\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0,0,0,0,0,0,1,1,0,2,2,2,0,3,3,3,0,0,4,4,4,0,5,5,5,0," + "6,6,6,0,0,0,0,0,0,0,0,0,7,7,7,7,0]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0,\"f\"]\n" + "[\"testcode\",1,9,\"f\"]\n" + "[\"testcode\",1,11,\"foo\"]\n" + "[\"testcode\",1,16,\"bar\"]\n" + "[\"testcode\",1,23,\"foo\"]\n" + "[\"testcode\",1,29,\"foo\"]\n" + "[\"testcode\",1,35,\"bar\"]\n" + "[\"testcode\",1,51,\"foo\"]\n"); } public void testGoldenOutput2() throws Exception { checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + "\nreturn foo;\n}", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0,0,0,0,0,0,1,1,2,3,3,3,2,4,4,4,2,5,7,7,7,6,8,8,8," + "6,9,9,9,6,10,10,10,11,11,11,11,11,11,11,12,12,12," + "12,5]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"testcode\",1,0,\"f\"]\n" + "[\"testcode\",1,9,\"f\"]\n" + "[\"testcode\",1,10]\n" + "[\"testcode\",1,11,\"foo\"]\n" + "[\"testcode\",1,16,\"bar\"]\n" + "[\"testcode\",1,21]\n" + "[\"testcode\",5,0]\n" + "[\"testcode\",5,0,\"foo\"]\n" + "[\"testcode\",5,6,\"foo\"]\n" + "[\"testcode\",5,12,\"bar\"]\n" + "[\"testcode\",5,18,\"foo\"]\n" + "[\"testcode\",6,0]\n" + "[\"testcode\",6,7,\"foo\"]\n"); } public void testGoldenOutput3() throws Exception { checkSourceMap("c:\\myfile.js", "foo;", "/** Begin line maps. **/{ \"file\" : \"testcode\", " + "\"count\": 1 }\n" + "[0,0,0]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"c:\\\\myfile.js\",1,0,\"foo\"]\n"); } public void testGoldenOutput4() throws Exception { checkSourceMap("c:\\myfile.js", "foo; boo; goo;", "/** Begin line maps. **/" + "{ \"file\" : \"testcode\", \"count\": 1 }\n" + "[0,0,0,1,1,1,1,2,2,2,2]\n" + "/** Begin file information. **/\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"c:\\\\myfile.js\",1,0,\"foo\"]\n" + "[\"c:\\\\myfile.js\",1,7,\"boo\"]\n" + "[\"c:\\\\myfile.js\",1,14,\"goo\"]\n"); } public void testGoldenOutput5() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap("c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "/** Begin line maps. **/" + "{ \"file\" : \"testcode\", \"count\": 6 }\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[0,0,0,0,1,1,1,1,2,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3," + "3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]\n" + "[4,1,5,1,6]\n" + "/** Begin file information. **/\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"c:\\\\myfile.js\",4,0]\n" + "[\"c:\\\\myfile.js\",4,4,\"foo\"]\n" + "[\"c:\\\\myfile.js\",4,8,\"a\"]\n" + "[\"c:\\\\myfile.js\",4,12]\n" + "[\"c:\\\\myfile.js\",4,1314,\"c\"]\n" + "[\"c:\\\\myfile.js\",4,1318,\"d\"]\n" + "[\"c:\\\\myfile.js\",4,1322,\"e\"]\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "/** Begin line maps. **/" + "{ \"file\" : \"testcode\", \"count\": 6 }\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[-1,-1,-1,-1,0,0,0,0,1]\n" + "[2,0,3,0,4]\n" + "/** Begin file information. **/\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "[]\n" + "/** Begin mapping definitions. **/\n" + "[\"c:\\\\myfile.js\",4,4,\"foo\"]\n" + "[\"c:\\\\myfile.js\",4,8,\"a\"]\n" + "[\"c:\\\\myfile.js\",4,1314,\"c\"]\n" + "[\"c:\\\\myfile.js\",4,1318,\"d\"]\n" + "[\"c:\\\\myfile.js\",4,1322,\"e\"]\n"); } public void testBasicDeterminism() throws Exception { RunResult result1 = compile("file1", "foo;", "file2", "bar;"); RunResult result2 = compile("file2", "foo;", "file1", "bar;"); String map1 = getSourceMap(result1); String map2 = getSourceMap(result2); // Assert that the files section of the maps are the same. The actual // entries will differ, so we cannot do a simple full comparison. // Line 5 has the file information. String files1 = map1.split("\n")[4]; String files2 = map2.split("\n")[4]; assertEquals(files1, files2); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/Base64Test.java0000644000175000017500000000245112115204405026045 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import junit.framework.TestCase; /** * @author johnlenz@google.com (John Lenz) */ public class Base64Test extends TestCase { public void testBase64() { for (int i = 0; i < 64; i++) { testValue(i); } } public void testBase64EncodeInt() { assertEquals("AAAAAA", Base64.base64EncodeInt(0)); assertEquals("AAAAAQ", Base64.base64EncodeInt(1)); assertEquals("AAAAKg", Base64.base64EncodeInt(42)); assertEquals("////nA", Base64.base64EncodeInt(-100)); assertEquals("/////w", Base64.base64EncodeInt(0xffffffff)); } private void testValue(int value) { assertEquals(value, Base64.fromBase64(Base64.toBase64(value))); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapConsumerV2Test.java0000644000175000017500000001333212115204405030643 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.debugging.sourcemap.SourceMapConsumerV2; import com.google.debugging.sourcemap.SourceMapParseException; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import junit.framework.TestCase; public class SourceMapConsumerV2Test extends TestCase { public SourceMapConsumerV2Test() { } public SourceMapConsumerV2Test(String name) { super(name); } public void testEmptyMap() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("{\n"); sb.append("\"version\": 2,\n"); sb.append("\"file\": \"somefile.js\",\n"); sb.append("\"lineCount\": 0,\n"); sb.append("\"lineMaps\": [],\n"); sb.append("\"sources\": [],\n"); sb.append("\"mappings\": []\n"); sb.append("}\n"); SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); sourceMap.parse(sb.toString()); } public void testGetMappingForLine() throws Exception { // Input Code: function f(foo, bar) { foo = foo + bar + 2; return foo; } String mapData = "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":" + "[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + "\"sources\":[\"testcode\"],\n" + "\"mappings\":[[0,1,9,\"f\"],\n" + "[0,1,9,\"f\"],\n" + "[0,1,10],\n" + "[0,1,11,\"foo\"],\n" + "[0,1,16,\"bar\"],\n" + "[0,1,21],\n" + "[0,1,23],\n" + "[0,1,23,\"foo\"],\n" + "[0,1,29,\"foo\"],\n" + "[0,1,35,\"bar\"],\n" + "[0,1,41],\n" + "[0,1,44],\n" + "[0,1,51,\"foo\"],\n" + "]\n" + "}\n"; SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); sourceMap.parse(mapData); OriginalMapping mapping = sourceMap.getMappingForLine(1, 10); assertNotNull(mapping); assertEquals("testcode", mapping.getOriginalFile()); assertEquals(1, mapping.getLineNumber()); assertEquals(9, mapping.getColumnPosition()); assertEquals("f", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(1, 40); assertNotNull(mapping); assertEquals("testcode", mapping.getOriginalFile()); assertEquals(1, mapping.getLineNumber()); assertEquals(44, mapping.getColumnPosition()); assertEquals("", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(1, 42); assertNotNull(mapping); assertEquals("testcode", mapping.getOriginalFile()); assertEquals(1, mapping.getLineNumber()); assertEquals(51, mapping.getColumnPosition()); assertEquals("foo", mapping.getIdentifier()); assertNull(sourceMap.getMappingForLine(Integer.MAX_VALUE, 1)); assertNull(sourceMap.getMappingForLine(1, Integer.MAX_VALUE)); } public void testGetMappingForLineWithNameIndex() throws Exception { String mapData = "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":" + "[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\": [\"f\"],\n" + "\"mappings\":[[0,1,9,0],\n" + "[0,1,9,0]\n" + "]\n" + "}\n"; SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); sourceMap.parse(mapData); OriginalMapping mapping = sourceMap.getMappingForLine(1, 10); assertNotNull(mapping); assertEquals("testcode", mapping.getOriginalFile()); assertEquals(1, mapping.getLineNumber()); assertEquals(9, mapping.getColumnPosition()); assertEquals("f", mapping.getIdentifier()); } public void testInvalidJSONFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("notjson"); assertExceptionStartsWith("JSON parse exception: org.json.JSONException: " + "A JSONObject text must begin " + "with '{' at character 1", sb); } public void testUnknownVersion() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("{\"version\": 3}"); assertException("Unknown version: 3", sb); } public void testMissingFile() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("{\"version\": 2, \"file\": \"\"}"); assertException("File entry is missing or empty", sb); } private void assertException(String exception, StringBuilder sb) { boolean exceptionRaised = false; try { SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); sourceMap.parse(sb.toString()); } catch (SourceMapParseException pe) { assertEquals(exception, pe.getMessage()); exceptionRaised = true; } assertTrue(exceptionRaised); } private void assertExceptionStartsWith(String exception, StringBuilder sb) { boolean exceptionRaised = false; try { SourceMapConsumerV2 sourceMap = new SourceMapConsumerV2(); sourceMap.parse(sb.toString()); } catch (SourceMapParseException pe) { assertTrue( "expected <" + exception +"> but was <"+ pe.getMessage() +">", pe.getMessage().startsWith(exception)); exceptionRaised = true; } assertTrue(exceptionRaised); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapGeneratorV3Test.java0000644000175000017500000004371212115204405031004 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.SourceMap; import com.google.javascript.jscomp.SourceMap.Format; import java.io.IOException; import java.io.StringWriter; import java.util.LinkedHashMap; import java.util.List; /** * @author johnlenz@google.com (John Lenz) */ public class SourceMapGeneratorV3Test extends SourceMapTestCase { public SourceMapGeneratorV3Test() { } @Override protected SourceMapConsumer getSourceMapConsumer() { return new SourceMapConsumerV3(); } @Override protected Format getSourceMapFormat() { return SourceMap.Format.V3; } public void testBasicMapping1() throws Exception { compileAndCheck("function __BASIC__() { }"); } public void testBasicMappingGoldenOutput() throws Exception { // Empty source map test checkSourceMap("function __BASIC__() { }", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,QAASA,UAAS,EAAG;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"__BASIC__\"]\n" + "}\n"); } public void testBasicMapping2() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__) {}"); } public void testLiteralMappings() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + "var __VAR__ = '__STR__'; }"); } public void testLiteralMappingsGoldenOutput() throws Exception { // Empty source map test checkSourceMap("function __BASIC__(__PARAM1__, __PARAM2__) { " + "var __VAR__ = '__STR__'; }", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,QAASA,UAAS,CAACC,UAAD,CAAaC,UAAb," + "CAAyB,CAAE,IAAIC,QAAU,SAAhB;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"__BASIC__\",\"__PARAM1__\",\"__PARAM2__\"," + "\"__VAR__\"]\n" + "}\n"); } public void testMultilineMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}"); } public void testMultilineMapping2() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = 1;\n" + "var __ANO__ = 2;\n" + "}"); } public void testMultiFunctionMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}\n" + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + "var __VAR2__ = '__STR2__';\n" + "var __ANO2__ = \"__STR3__\";\n" + "}\n"); } public void testGoldenOutput0() throws Exception { // Empty source map test checkSourceMap("", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\";\",\n" + "\"sources\":[],\n" + "\"names\":[]\n" + "}\n"); } public void testGoldenOutput0a() throws Exception { // Empty source map test checkSourceMap("a;", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"a\"]\n" + "}\n"); } public void testGoldenOutput1() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,QAASA,EAAC,CAACC,GAAD,CAAMC,GAAN," + "CAAW,CAAED,GAAA,CAAMA,GAAN,CAAYC,GAAZ,CAAkB,CAAG," + "OAAOD,IAA9B;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,QAASA,EAATA,CAAWC,GAAXD,CAAgBE," + "GAAhBF,EAAuBC,GAAvBD,CAA6BC,GAA7BD,CAAmCE,GAAnCF," + "SAAmDC,IAAnDD;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); } public void testGoldenOutput2() throws Exception { checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + "\nreturn foo;\n}", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,QAASA,EAAC,CAACC,GAAD,CAAMC,GAAN," + "CAAW,CAIrBD,GAAA,CAAMA,GAAN,CAAYC,GAAZ,CAAkBD," + "GAClB,OAAOA,IALc;\",\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); } public void testGoldenOutput3() throws Exception { checkSourceMap("c:\\myfile.js", "foo;", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA;\",\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\"]\n" + "}\n"); } public void testGoldenOutput4() throws Exception { checkSourceMap("c:\\myfile.js", "foo; boo; goo;", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\"AAAAA,GAAOC,IAAOC;\",\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"boo\",\"goo\"]\n" + "}\n"); } public void testGoldenOutput5() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap( "c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":6,\n" + "\"mappings\":\"A;;;;AAGA,IAAIA,IAAIC,CAAJD,CAAQ,mxCAARA;AAA8xCE," + "CAA9xCF,CAAkyCG,CAAlyCH,CAAsyCI;\",\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + "}\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "{\n" + "\"version\":3,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":6,\n" + "\"mappings\":\"A;;;;IAGIA,IAAIC,CAAJD;AAA8xCE,CAA9xCF,CAAkyCG," + "CAAlyCH,CAAsyCI;\",\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + "}\n"); } public void testBasicDeterminism() throws Exception { RunResult result1 = compile("file1", "foo;", "file2", "bar;"); RunResult result2 = compile("file2", "foo;", "file1", "bar;"); String map1 = getSourceMap(result1); String map2 = getSourceMap(result2); // Assert that the files section of the maps are the same. The actual // entries will differ, so we cannot do a simple full comparison. // Line 5 has the file information. String files1 = map1.split("\n")[4]; String files2 = map2.split("\n")[4]; assertEquals(files1, files2); } public void testWriteMetaMap() throws IOException { StringWriter out = new StringWriter(); String name = "./app.js"; List appSections = Lists.newArrayList( SourceMapSection.forURL("src1", 0, 0), SourceMapSection.forURL("src2", 100, 10), SourceMapSection.forURL("src3", 150, 5)); SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); generator.appendIndexMapTo(out, name, appSections); assertEquals( "{\n" + "\"version\":3,\n" + "\"file\":\"./app.js\",\n" + "\"sections\":[\n" + "{\n" + "\"offset\":{\n" + "\"line\":0,\n" + "\"column\":0\n" + "},\n" + "\"url\":\"src1\"\n" + "},\n" + "{\n" + "\"offset\":{\n" + "\"line\":100,\n" + "\"column\":10\n" + "},\n" + "\"url\":\"src2\"\n" + "},\n" + "{\n" + "\"offset\":{\n" + "\"line\":150,\n" + "\"column\":5\n" + "},\n" + "\"url\":\"src3\"\n" + "}\n" + "]\n" + "}\n", out.toString()); } private String getEmptyMapFor(String name) throws IOException { StringWriter out = new StringWriter(); SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); generator.appendTo(out, name); return out.toString(); } public void testWriteMetaMap2() throws IOException { StringWriter out = new StringWriter(); String name = "./app.js"; List appSections = Lists.newArrayList( // Map and URLs can be mixed. SourceMapSection.forMap(getEmptyMapFor("./part.js"), 0, 0), SourceMapSection.forURL("src2", 100, 10)); SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); generator.appendIndexMapTo(out, name, appSections); assertEquals( "{\n" + "\"version\":3,\n" + "\"file\":\"./app.js\",\n" + "\"sections\":[\n" + "{\n" + "\"offset\":{\n" + "\"line\":0,\n" + "\"column\":0\n" + "},\n" + "\"map\":{\n" + "\"version\":3,\n" + "\"file\":\"./part.js\",\n" + "\"lineCount\":1,\n" + "\"mappings\":\";\",\n" + "\"sources\":[],\n" + "\"names\":[]\n" + "}\n" + "\n" + "},\n" + "{\n" + "\"offset\":{\n" + "\"line\":100,\n" + "\"column\":10\n" + "},\n" + "\"url\":\"src2\"\n" + "}\n" + "]\n" + "}\n", out.toString()); } public void testParseSourceMetaMap() throws Exception { final String INPUT1 = "file1"; final String INPUT2 = "file2"; LinkedHashMap inputs = Maps.newLinkedHashMap(); inputs.put(INPUT1, "var __FOO__ = 1;"); inputs.put(INPUT2, "var __BAR__ = 2;"); RunResult result1 = compile(inputs.get(INPUT1), INPUT1); RunResult result2 = compile(inputs.get(INPUT2), INPUT2); final String MAP1 = "map1"; final String MAP2 = "map2"; final LinkedHashMap maps = Maps.newLinkedHashMap(); maps.put(MAP1, result1.sourceMapFileContent); maps.put(MAP2, result2.sourceMapFileContent); List sections = Lists.newArrayList(); StringBuilder output = new StringBuilder(); FilePosition offset = appendAndCount(output, result1.generatedSource); sections.add(SourceMapSection.forURL(MAP1, 0, 0)); output.append(result2.generatedSource); sections.add( SourceMapSection.forURL(MAP2, offset.getLine(), offset.getColumn())); SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); StringBuilder mapContents = new StringBuilder(); generator.appendIndexMapTo(mapContents, "out.js", sections); check(inputs, output.toString(), mapContents.toString(), new SourceMapSupplier() { @Override public String getSourceMap(String url){ return maps.get(url); }}); } public void testSourceMapMerging() throws Exception { final String INPUT1 = "file1"; final String INPUT2 = "file2"; LinkedHashMap inputs = Maps.newLinkedHashMap(); inputs.put(INPUT1, "var __FOO__ = 1;"); inputs.put(INPUT2, "var __BAR__ = 2;"); RunResult result1 = compile(inputs.get(INPUT1), INPUT1); RunResult result2 = compile(inputs.get(INPUT2), INPUT2); StringBuilder output = new StringBuilder(); FilePosition offset = appendAndCount(output, result1.generatedSource); output.append(result2.generatedSource); SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); generator.mergeMapSection(0, 0, result1.sourceMapFileContent); generator.mergeMapSection(offset.getLine(), offset.getColumn(), result2.sourceMapFileContent); StringBuilder mapContents = new StringBuilder(); generator.appendTo(mapContents, "out.js"); check(inputs, output.toString(), mapContents.toString()); } FilePosition count(String js) { int line = 0, column = 0; for (int i = 0; i < js.length(); i++) { if (js.charAt(i) == '\n') { line++; column = 0; } else { column++; } } return new FilePosition(line, column); } FilePosition appendAndCount(Appendable out, String js) throws IOException { out.append(js); return count(js); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapTestCase.java0000644000175000017500000002263212115204405027516 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.jscomp.SourceMap; import com.google.javascript.jscomp.SourceMap.DetailLevel; import junit.framework.TestCase; import java.io.IOException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * @author johnlenz@google.com (John Lenz) */ public abstract class SourceMapTestCase extends TestCase { private boolean validateColumns = true; public SourceMapTestCase() { } void disableColumnValidation() { validateColumns = false; } static final List EXTERNS = ImmutableList.of( SourceFile.fromCode("externs", "")); protected DetailLevel detailLevel = SourceMap.DetailLevel.ALL; protected static class RunResult { String generatedSource; SourceMap sourceMap; public String sourceMapFileContent; } protected static class Token { final String tokenName; final String inputName; final FilePosition position; Token(String tokenName, String inputName, FilePosition position) { this.tokenName = tokenName; this.inputName = inputName; this.position = position; } } @Override public void setUp() { detailLevel = SourceMap.DetailLevel.ALL; } /** * Creates a source map for the given JS code and asserts it is * equal to the expected golden map. */ protected void checkSourceMap(String js, String expectedMap) throws IOException { checkSourceMap("testcode", js, expectedMap); } protected String getSourceMap(RunResult result) throws IOException { StringBuilder sb = new StringBuilder(); result.sourceMap.appendTo(sb, "testcode"); return sb.toString(); } protected void checkSourceMap(String fileName, String js, String expectedMap) throws IOException { RunResult result = compile(js, fileName); assertEquals(expectedMap, result.sourceMapFileContent); assertEquals(result.sourceMapFileContent, getSourceMap(result)); } /** * Finds the all the __XX__ tokens in the given JavaScript * string. */ private Map findTokens(Map inputs) { Map tokens = Maps.newLinkedHashMap(); for (Entry entry : inputs.entrySet()) { findTokens(tokens, entry.getKey(), entry.getValue()); } return tokens; } /** * Finds the all the __XX__ tokens in the given JavaScript * string. */ private Map findTokens(String src) { Map tokens = Maps.newLinkedHashMap(); findTokens(tokens, "", src); return tokens; } /** * Finds the all the __XX__ tokens in the given JavaScript * string. */ private Map findTokens( Map tokens, String inputName, String js) { int currentLine = 0; int positionOffset = 0; for (int i = 0; i < js.length(); ++i) { char current = js.charAt(i); if (current == '\n') { positionOffset = i + 1; currentLine++; continue; } if (current == '_' && (i < js.length() - 5)) { // Check for the _ token. if (js.charAt(i + 1) != '_') { continue; } // Loop until we have another _ token. String tokenName = ""; int j = i + 2; for (; j < js.length(); ++j) { if (js.charAt(j) == '_') { break; } tokenName += js.charAt(j); } if (tokenName.length() > 0) { int currentPosition = i - positionOffset; Token token = new Token( tokenName, inputName, new FilePosition(currentLine, currentPosition)); tokens.put(tokenName, token); } i = j; } } return tokens; } abstract protected SourceMap.Format getSourceMapFormat(); abstract protected SourceMapConsumer getSourceMapConsumer(); protected void compileAndCheck(String js) { String inputName = "testcode"; RunResult result = compile(js, inputName); check(inputName, js, result.generatedSource, result.sourceMapFileContent); } protected void check( String inputName, String input, String output, String sourceMapFileContent) { Map inputMap = new LinkedHashMap(); inputMap.put(inputName, input); check(inputMap, output, sourceMapFileContent); } protected void check( Map originalInputs, String generatedSource, String sourceMapFileContent) { check(originalInputs, generatedSource, sourceMapFileContent, null); } protected void check( Map originalInputs, String generatedSource, String sourceMapFileContent, SourceMapSupplier supplier) { // Find all instances of the __XXX__ pattern in the original // source code. Map originalTokens = findTokens(originalInputs); // Find all instances of the __XXX__ pattern in the generated // source code. Map resultTokens = findTokens(generatedSource); // Ensure that the generated instances match via the source map // to the original source code. // Ensure the token counts match. assertEquals(originalTokens.size(), resultTokens.size()); SourceMapping reader; try { reader = SourceMapConsumerFactory.parse(sourceMapFileContent, supplier); } catch (SourceMapParseException e) { throw new RuntimeException("unexpected exception", e); } // Map the tokens from the generated source back to the // input source and ensure that the map is correct. for (Token token : resultTokens.values()) { OriginalMapping mapping = reader.getMappingForLine( token.position.getLine() + 1, token.position.getColumn() + 1); assertNotNull(mapping); // Find the associated token in the input source. Token inputToken = originalTokens.get(token.tokenName); assertNotNull(inputToken); assertEquals(mapping.getOriginalFile(), inputToken.inputName); // Ensure that the map correctly points to the token (we add 1 // to normalize versus the Rhino line number indexing scheme). assertEquals(mapping.getLineNumber(), inputToken.position.getLine() + 1); int start = inputToken.position.getColumn() + 1; if (inputToken.tokenName.startsWith("STR")) { // include the preceding quote. start--; } if (validateColumns) { assertEquals(start, mapping.getColumnPosition()); } // Ensure that if the token name does not being with an 'STR' (meaning a // string) it has an original name. if (!inputToken.tokenName.startsWith("STR")) { assertTrue("missing name for " + inputToken.tokenName, !mapping.getIdentifier().isEmpty()); } // Ensure that if the mapping has a name, it matches the token. if (!mapping.getIdentifier().isEmpty()) { assertEquals(mapping.getIdentifier(), "__" + inputToken.tokenName + "__"); } } } protected RunResult compile(String js, String fileName) { return compile(js, fileName, null, null); } protected CompilerOptions getCompilerOptions() { CompilerOptions options = new CompilerOptions(); options.sourceMapOutputPath = "testcode_source_map.out"; options.sourceMapFormat = getSourceMapFormat(); options.sourceMapDetailLevel = detailLevel; return options; } protected RunResult compile( String js1, String fileName1, String js2, String fileName2) { Compiler compiler = new Compiler(); CompilerOptions options = getCompilerOptions(); // Turn on IDE mode to get rid of optimizations. options.ideMode = true; List inputs = ImmutableList.of(SourceFile.fromCode(fileName1, js1)); if (js2 != null && fileName2 != null) { inputs = ImmutableList.of( SourceFile.fromCode(fileName1, js1), SourceFile.fromCode(fileName2, js2)); } Result result = compiler.compile(EXTERNS, inputs, options); assertTrue("compilation failed", result.success); String source = compiler.toSource(); StringBuilder sb = new StringBuilder(); try { result.sourceMap.validate(true); result.sourceMap.appendTo(sb, "testcode"); } catch (IOException e) { throw new RuntimeException("unexpected exception", e); } RunResult rr = new RunResult(); rr.generatedSource = source; rr.sourceMap = result.sourceMap; rr.sourceMapFileContent = sb.toString(); return rr; } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapGeneratorV2Test.java0000644000175000017500000004144412115204405031003 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.debugging.sourcemap.SourceMapGeneratorV2.LineMapEncoder; import com.google.javascript.jscomp.SourceMap; import com.google.javascript.jscomp.SourceMap.Format; import java.io.IOException; /** * Tests for {@link SourceMap}. * */ public class SourceMapGeneratorV2Test extends SourceMapTestCase { public SourceMapGeneratorV2Test() { disableColumnValidation(); } @Override protected SourceMapConsumer getSourceMapConsumer() { return new SourceMapConsumerV2(); } @Override protected Format getSourceMapFormat() { return SourceMap.Format.V2; } @Override public void setUp() { detailLevel = SourceMap.DetailLevel.ALL; } public void testBasicMapping() throws Exception { compileAndCheck("function __BASIC__() { }"); } public void testBasicMappingGoldenOutput() throws Exception { // Empty source map test checkSourceMap("function __BASIC__() { }", //"/** Source Map **/\n" + "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"cAkBEBEB\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,9,0],\n" + "[0,1,18],\n" + "[0,1,21],\n" + "],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"__BASIC__\"]\n" + "}\n"); } public void testLiteralMappings() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) { " + "var __VAR__ = '__STR__'; }"); } public void testLiteralMappingsGoldenOutput() throws Exception { // Empty source map test checkSourceMap("function __BASIC__(__PARAM1__, __PARAM2__) { " + "var __VAR__ = '__STR__'; }", //"/** Source Map **/\n" + "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"cAkBABkBA/kCA+ADMBcBgBA9\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,9,0],\n" + "[0,1,18],\n" + "[0,1,19,1],\n" + "[0,1,31,2],\n" + "[0,1,43],\n" + "[0,1,45],\n" + "[0,1,49,3],\n" + "[0,1,59],\n" + "],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[" + "\"__BASIC__\",\"__PARAM1__\",\"__PARAM2__\"," + "\"__VAR__\"]\n" + "}\n"); } public void testMultilineMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}"); } public void testMultiFunctionMapping() throws Exception { compileAndCheck("function __BASIC__(__PARAM1__, __PARAM2__) {\n" + "var __VAR__ = '__STR__';\n" + "var __ANO__ = \"__STR2__\";\n" + "}\n\n" + "function __BASIC2__(__PARAM3__, __PARAM4__) {\n" + "var __VAR2__ = '__STR2__';\n" + "var __ANO2__ = \"__STR3__\";\n" + "}\n\n"); } public void testGoldenOutput0() throws Exception { // Empty source map test checkSourceMap("", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"\"],\n" + "\"mappings\":[],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[]\n" + "}\n"); } public void testGoldenOutput1() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap( "function f(foo, bar) { foo = foo + bar + 2; return foo; }", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"cAEBABIBA/ICA+ADICA/ICA+IDA9AEYBMBA5\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,9,0],\n" + "[0,1,10],\n" + "[0,1,11,1],\n" + "[0,1,16,2],\n" + "[0,1,21],\n" + "[0,1,23],\n" + "[0,1,23,1],\n" + "[0,1,29,1],\n" + "[0,1,35,2],\n" + "[0,1,41],\n" + "[0,1,44],\n" + "[0,1,51,1],\n" + "],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("function f(foo, bar) { foo = foo + bar + 2; return foo; }", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"cAEBA/ICA+IDE9IEA8IFA7IGg6MHA5\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,9,0],\n" + "[0,1,11,1],\n" + "[0,1,16,2],\n" + "[0,1,23,1],\n" + "[0,1,29,1],\n" + "[0,1,35,2],\n" + "[0,1,51,1],\n" + "],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); } public void testGoldenOutput2() throws Exception { checkSourceMap("function f(foo, bar) {\r\n\n\n\nfoo = foo + bar + foo;" + "\nreturn foo;\n}", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[" + "\"cAEBABIBA/ICA+ADICA/ICA+IDA9IEYBMBA5\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,9,0],\n" + "[0,1,10],\n" + "[0,1,11,1],\n" + "[0,1,16,2],\n" + "[0,1,21],\n" + "[0,5,0],\n" + "[0,5,0,1],\n" + "[0,5,6,1],\n" + "[0,5,12,2],\n" + "[0,5,18,1],\n" + "[0,6,0],\n" + "[0,6,7,1],\n" + "],\n" + "\"sources\":[\"testcode\"],\n" + "\"names\":[\"f\",\"foo\",\"bar\"]\n" + "}\n"); } public void testGoldenOutput3() throws Exception { checkSourceMap("c:\\myfile.js", "foo;", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"IA\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "],\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\"]\n" + "}\n"); } public void testGoldenOutput4() throws Exception { checkSourceMap("c:\\myfile.js", "foo; boo; goo;", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"IAMBMB\"],\n" + "\"mappings\":[[0,1,0,0],\n" + "[0,1,7,1],\n" + "[0,1,14,2],\n" + "],\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"boo\",\"goo\"]\n" + "}\n"); } public void testGoldenOutput5() throws Exception { detailLevel = SourceMap.DetailLevel.ALL; checkSourceMap("c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":6,\n" + "\"lineMaps\":[\"\",\n" + "\"\",\n" + "\"\",\n" + "\"\",\n" + "\"MAMBABA/!!AUSC\",\n" + "\"AEA9AEA8AF\"],\n" + "\"mappings\":[[0,4,0],\n" + "[0,4,4,0],\n" + "[0,4,8,1],\n" + "[0,4,12],\n" + "[0,4,1314,2],\n" + "[0,4,1318,3],\n" + "[0,4,1322,4],\n" + "],\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + "}\n"); detailLevel = SourceMap.DetailLevel.SYMBOLS; checkSourceMap("c:\\myfile.js", "/** @preserve\n" + " * this is a test.\n" + " */\n" + "var foo=a + 'this is a really long line that will force the" + " mapping to span multiple lines 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + " 123456789 123456789 123456789 123456789 123456789" + "' + c + d + e;", "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":6,\n" + "\"lineMaps\":[\"\",\n" + "\"\",\n" + "\"\",\n" + "\"\",\n" + "\"M/MBAB\",\n" + "\"ACA+ADA9AE\"],\n" + "\"mappings\":[[0,4,4,0],\n" + "[0,4,8,1],\n" + "[0,4,1314,2],\n" + "[0,4,1318,3],\n" + "[0,4,1322,4],\n" + "],\n" + "\"sources\":[\"c:\\\\myfile.js\"],\n" + "\"names\":[\"foo\",\"a\",\"c\",\"d\",\"e\"]\n" + "}\n"); } public void testBasicDeterminism() throws Exception { RunResult result1 = compile("file1", "foo;", "file2", "bar;"); RunResult result2 = compile("file2", "foo;", "file1", "bar;"); String map1 = getSourceMap(result1); String map2 = getSourceMap(result2); // Assert that the files section of the maps are the same. The actual // entries will differ, so we cannot do a simple full comparison. // Line 5 has the file information. String files1 = map1.split("\n")[4]; String files2 = map2.split("\n")[4]; assertEquals(files1, files2); } private int getRelativeId(int id, int lastId) { int length = LineMapEncoder.getRelativeMappingIdLength(id, lastId); int result = LineMapEncoder.getRelativeMappingId(id, length, lastId); int inverse = SourceMapLineDecoder.getIdFromRelativeId( result, length, lastId); assertEquals(id, inverse); return result; } public void testEncodingRelativeId() { assertEquals(0, getRelativeId(0, 0)); assertEquals(64 + (-1), getRelativeId(-1, 0)); assertEquals(64 + (-32), getRelativeId(0, 32)); assertEquals(31, getRelativeId(31, 0)); assertEquals(4096 + (-33), getRelativeId(0, 33)); assertEquals(32, getRelativeId(32, 0)); } public void testEncodingIdLength() { assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(0, 0)); assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(-1, 0)); assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(0, 32)); assertEquals(1, LineMapEncoder.getRelativeMappingIdLength(31, 0)); assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(0, 33)); assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(32, 0)); assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(2047, 0)); assertEquals(3, LineMapEncoder.getRelativeMappingIdLength(2048, 0)); assertEquals(2, LineMapEncoder.getRelativeMappingIdLength(0, 2048)); assertEquals(3, LineMapEncoder.getRelativeMappingIdLength(0, 2049)); } private String getEntry(int id, int lastId, int reps) throws IOException { StringBuilder sb = new StringBuilder(); LineMapEncoder.encodeEntry(sb, id, lastId, reps); return sb.toString(); } public void testEncoding() throws IOException { assertEquals("AA", getEntry(0, 0, 1)); assertEquals("EA", getEntry(0, 0, 2)); assertEquals("8A", getEntry(0, 0, 16)); assertEquals("!AQA", getEntry(0, 0, 17)); assertEquals("!ARA", getEntry(0, 0, 18)); assertEquals("!A+A", getEntry(0, 0, 63)); assertEquals("!A/A", getEntry(0, 0, 64)); assertEquals("!!ABAA", getEntry(0, 0, 65)); assertEquals("!!A//A", getEntry(0, 0, 4096)); assertEquals("!!!ABAAA", getEntry(0, 0, 4097)); assertEquals("Af", getEntry(31, 0, 1)); assertEquals("BAg", getEntry(32, 0, 1)); assertEquals("AB", getEntry(32, 31, 1)); assertEquals("!AQf", getEntry(31, 0, 17)); assertEquals("!BQAg", getEntry(32, 0, 17)); assertEquals("!AQB", getEntry(32, 31, 17)); assertEquals("!A/B", getEntry(32, 31, 64)); assertEquals("!!ABAB", getEntry(32, 31, 65)); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/SourceMapConsumerV1Test.java0000644000175000017500000002667112115204405030654 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.debugging.sourcemap.SourceMapConsumerV1; import com.google.debugging.sourcemap.SourceMapParseException; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import junit.framework.TestCase; public class SourceMapConsumerV1Test extends TestCase { public SourceMapConsumerV1Test() { } public SourceMapConsumerV1Test(String name) { super(name); } public void testGetMappingForLine() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 2 }\n"); sb.append("[0,,,,1,,2]\n"); sb.append("[3,,,,,,,]\n"); sb.append("/** Begin file information. **/\n"); sb.append("['test.js']\n"); sb.append("['foo.js']\n"); sb.append("/** Begin mapping definitions. **/\n"); sb.append("['test.js', 0, 1]\n"); sb.append("['bleg.js', 5, 8, 'hello']\n"); sb.append("['bleg.js', 12, 78]\n"); sb.append("['foo.js', 15, 16, 'yo!']"); SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); assertNotNull(mapping); assertEquals("test.js", mapping.getOriginalFile()); assertEquals(0, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); assertEquals("", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(1, 6); assertNotNull(mapping); assertEquals("bleg.js", mapping.getOriginalFile()); assertEquals(5, mapping.getLineNumber()); assertEquals(8, mapping.getColumnPosition()); assertEquals("hello", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(2, 4); assertNotNull(mapping); assertEquals("foo.js", mapping.getOriginalFile()); assertEquals(15, mapping.getLineNumber()); assertEquals(16, mapping.getColumnPosition()); assertEquals("yo!", mapping.getIdentifier()); assertNull(sourceMap.getMappingForLine(Integer.MAX_VALUE, 1)); assertNotNull(sourceMap.getMappingForLine(1, Integer.MAX_VALUE)); } public void testLineEdges() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 2 }\n"); sb.append("[100]\n"); sb.append("[200]\n"); sb.append("/** Begin file information. **/\n"); sb.append("['test.js']\n"); sb.append("['foo.js']\n"); sb.append("/** Begin mapping definitions. **/\n"); for (int i = 0; i <= 200; i++) { sb.append("['foo.js', ").append(i).append(", 1]\n"); } SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(-1, 1); assertNull(mapping); mapping = sourceMap.getMappingForLine(0, 1); assertNull(mapping); mapping = sourceMap.getMappingForLine(1, 1); assertEquals(100, mapping.getLineNumber()); mapping = sourceMap.getMappingForLine(2, 1); assertEquals(200, mapping.getLineNumber()); mapping = sourceMap.getMappingForLine(3, 1); assertNull(mapping); } public void testColumnEdges() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[100, 101]\n"); sb.append("/** Begin file information. **/\n"); sb.append("\n"); sb.append("/** Begin mapping definitions. **/\n"); for (int i = 0; i <= 200; i++) { sb.append("['foo.js', ").append(i).append(", 1]\n"); } SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(1, -1); assertNull(mapping); mapping = sourceMap.getMappingForLine(1, 0); assertNull(mapping); mapping = sourceMap.getMappingForLine(1, 1); assertEquals(100, mapping.getLineNumber()); mapping = sourceMap.getMappingForLine(1, 2); assertEquals(101, mapping.getLineNumber()); // Columns beyond the end of the line are treated the same // as the last column. mapping = sourceMap.getMappingForLine(1, 3); assertEquals(101, mapping.getLineNumber()); } public void testNegativeOneInLineMap() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[200,-1,199,-1]\n"); sb.append("/** Begin file information. **/\n"); sb.append("\n"); sb.append("/** Begin mapping definitions. **/\n"); for (int i = 0; i <= 200; i++) { sb.append("['foo.js', 1, ").append(i).append("]\n"); } SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(1, 2); assertNull(mapping); mapping = sourceMap.getMappingForLine(1, 4); assertNull(mapping); } public void testSimpleParse() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,,,,1,2]\n"); sb.append("/** Begin file information. **/\n"); sb.append("['test.js']\n"); sb.append("/** Begin mapping definitions. **/\n"); sb.append("['test.js', 0, 1]\n"); sb.append("['test.js', 0, 1, 'hello']\n"); sb.append("['test.js', 0, 1]"); SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); } public void testBlankLine() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("\n"); sb.append("/** Begin file information. **/\n"); sb.append("\n"); sb.append("/** Begin mapping definitions. **/\n"); SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); } public void testCountFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ counter : 1 }\n"); assertException("Missing 'count'", sb); } public void testInvalidCountFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 0 }\n"); assertException("Count must be >= 1", sb); } public void testInvalidJSONFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 2 }\n"); sb.append("[0,,,,2\n"); assertExceptionStartsWith("JSON parse exception: org.json.JSONException: " + "Expected a ',' or ']' at ", sb); } public void testInvalidHeaderFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,,,1]\n"); sb.append("[3,,,4]\n"); assertException( "Expected /** Begin file information. **/ got [3,,,4]", sb); } public void testInvalidPostHeaderToken() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,,,1]\n"); sb.append("/** Begin file information. **/f\n"); assertException("Expected /** Begin file information. **/" + " got /** Begin file information. **/f", sb); } public void testInvalidMappingArrayFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,,,,1,2]\n"); sb.append("/** Begin file information. **/\n"); sb.append("['test.js']\n"); sb.append("/** Begin mapping definitions. **/\n"); sb.append("['test.js', 0]\n"); assertException("Invalid mapping array", sb); } public void testMultipleLineFragments() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,1,2,3,260,261,262]\n"); sb.append("/** Begin file information. **/\n"); sb.append("\n"); sb.append("/** Begin mapping definitions. **/\n"); for (int i = 0; i < 262; i++) { sb.append("['frog/test" + i + ".js', " + i + ", 1]\n"); } sb.append("['frog/testigloo.js', 500, 1]"); SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); assertNotNull(mapping); assertEquals("frog/test0.js", mapping.getOriginalFile()); assertEquals(0, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); assertEquals("", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(1, 6); assertNotNull(mapping); assertEquals("frog/test261.js", mapping.getOriginalFile()); assertEquals(261, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); } public void testMultipleMappingFragments() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("/** Begin line maps. **/{ count : 1 }\n"); sb.append("[0,1,2,3,260,261,262,1023]\n"); sb.append("/** Begin file information. **/\n"); sb.append("\n"); sb.append("/** Begin mapping definitions. **/\n"); for (int i = 0; i < 2000; i++) { sb.append("['frog/test" + (i / 100) + ".js', " + i + ", 1]\n"); } sb.append("['frog/testigloo.js', 500, 1]"); SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); OriginalMapping mapping = sourceMap.getMappingForLine(1, 1); assertNotNull(mapping); assertEquals("frog/test0.js", mapping.getOriginalFile()); assertEquals(0, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); assertEquals("", mapping.getIdentifier()); mapping = sourceMap.getMappingForLine(1, 6); assertNotNull(mapping); assertEquals("frog/test2.js", mapping.getOriginalFile()); assertEquals(261, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); mapping = sourceMap.getMappingForLine(1, 8); assertNotNull(mapping); assertEquals("frog/test10.js", mapping.getOriginalFile()); assertEquals(1023, mapping.getLineNumber()); assertEquals(1, mapping.getColumnPosition()); } private void assertException(String exception, StringBuilder sb) { boolean exceptionRaised = false; try { SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); } catch (SourceMapParseException pe) { assertEquals(exception, pe.getMessage()); exceptionRaised = true; } assertTrue(exceptionRaised); } private void assertExceptionStartsWith(String exception, StringBuilder sb) { boolean exceptionRaised = false; try { SourceMapConsumerV1 sourceMap = new SourceMapConsumerV1(); sourceMap.parse(sb.toString()); } catch (SourceMapParseException pe) { assertTrue( "expected <" + exception +"> but was <"+ pe.getMessage() +">", pe.getMessage().startsWith(exception)); exceptionRaised = true; } assertTrue(exceptionRaised); } } closure-compiler-20130227+dfsg1/test/com/google/debugging/sourcemap/Base64VLQTest.java0000644000175000017500000000757112115204405026440 0ustar apoapo/* * Copyright 2011 The Closure Compiler Authors. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.google.debugging.sourcemap; import junit.framework.TestCase; /** * @author johnlenz@google.com (John Lenz) */ public class Base64VLQTest extends TestCase { public void testBase64VLQSelectedValues1() { for (int i = 0; i < 63; i++) { testValue(i); } } public void testBase64VLQSelectedValues2() { int base = 1; for (int i = 0; i < 30; i++) { testValue(base-1); testValue(base); base *= 2; } } public void testBase64VLQSelectedSignedValues1() { for (int i = -(64*64-1); i < (64*64-1); i++) { testValue(i); } } public void testBase64VLQSelectedSignedValues2() { int base = 1; for (int i = 0; i < 30; i++) { testValue(base-1); testValue(base); base *= 2; } base = -1; for (int i = 0; i < 30; i++) { testValue(base-1); testValue(base); base *= 2; } } static class CharIteratorImpl implements Base64VLQ.CharIterator { private int current; private int length; private CharSequence cs; void set(CharSequence sb) { this.current = 0; this.length = sb.length(); this.cs = sb; } @Override public boolean hasNext() { return current < length; } @Override public char next() { return cs.charAt(current++); } } // Disable this test if it is flaky. public void testSpeed() { long start = System.currentTimeMillis(); CharIteratorImpl ci = new CharIteratorImpl(); try { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000000; i++) { Base64VLQ.encode(sb, i); ci.set(sb); int result = Base64VLQ.decode(ci); assertEquals(i, result); sb.setLength(0); } } catch (Exception e) { throw new RuntimeException("failed.", e); } long end = System.currentTimeMillis(); // Was 200ms or less, use a larger number to prevent flakiness assertTrue("too slow", end-start < 1000); } private void testValue(int value) { try { StringBuilder sb = new StringBuilder(); Base64VLQ.encode(sb, value); CharIteratorImpl ci = new CharIteratorImpl(); ci.set(sb); int result = Base64VLQ.decode(ci); assertEquals(value, result); } catch (Exception e) { throw new RuntimeException("failed for value " + value, e); } } }closure-compiler-20130227+dfsg1/test/com/google/javascript/0000755000175000017500000000000012115204405021531 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/0000755000175000017500000000000012115204405023024 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/JsMessageVisitorTest.java0000644000175000017500000005162512115204405030001 0ustar apoapo/* * 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.base.Joiner; import com.google.common.collect.Lists; import static com.google.javascript.jscomp.JsMessage.Style; import static com.google.javascript.jscomp.JsMessage.Style.CLOSURE; import static com.google.javascript.jscomp.JsMessage.Style.LEGACY; import static com.google.javascript.jscomp.JsMessage.Style.RELAX; import static com.google.javascript.jscomp.JsMessageVisitor.isLowerCamelCaseWithNumericSuffixes; import static com.google.javascript.jscomp.JsMessageVisitor.toLowerCamelCaseWithNumericSuffixes; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.List; /** * Test for {@link JsMessageVisitor}. * * @author anatol@google.com (Anatol Pomazau) */ public class JsMessageVisitorTest extends TestCase { private Compiler compiler; private List messages; private boolean allowLegacyMessages; @Override protected void setUp() throws Exception { messages = Lists.newLinkedList(); allowLegacyMessages = true; } public void testJsMessageOnVar() { extractMessagesSafely( "/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')"); assertEquals(0, compiler.getWarningCount()); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_HELLO", msg.getKey()); assertEquals("Hello", msg.getDesc()); } public void testJsMessageOnProperty() { extractMessagesSafely("/** @desc a */ " + "pint.sub.MSG_MENU_MARK_AS_UNREAD = goog.getMsg('a')"); assertEquals(0, compiler.getWarningCount()); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_MENU_MARK_AS_UNREAD", msg.getKey()); assertEquals("a", msg.getDesc()); } public void testOrphanedJsMessage() { extractMessagesSafely("goog.getMsg('a')"); assertEquals(1, compiler.getWarningCount()); assertEquals(0, messages.size()); JSError warn = compiler.getWarnings()[0]; assertEquals(JsMessageVisitor.MESSAGE_NODE_IS_ORPHANED, warn.getType()); } public void testMessageWithoutDescription() { extractMessagesSafely("var MSG_HELLO = goog.getMsg('a')"); assertEquals(1, compiler.getWarningCount()); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_HELLO", msg.getKey()); assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_DESCRIPTION, compiler.getWarnings()[0].getType()); } public void testIncorrectMessageReporting() { extractMessages("var MSG_HELLO = goog.getMsg('a' + + 'b')"); assertEquals(1, compiler.getErrorCount()); assertEquals(0, compiler.getWarningCount()); assertEquals(0, messages.size()); JSError mailformedTreeError = compiler.getErrors()[0]; assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, mailformedTreeError.getType()); assertEquals("Message parse tree malformed. " + "STRING or ADD node expected; found: POS", mailformedTreeError.description); } public void testEmptyMessage() { // This is an edge case. Empty messages are useless, but shouldn't fail extractMessagesSafely("var MSG_EMPTY = '';"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_EMPTY", msg.getKey()); assertEquals("", msg.toString()); } public void testConcatOfStrings() { extractMessagesSafely("var MSG_NOTEMPTY = 'aa' + 'bbb' \n + ' ccc';"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_NOTEMPTY", msg.getKey()); assertEquals("aabbb ccc", msg.toString()); } public void testLegacyFormatDescription() { extractMessagesSafely("var MSG_SILLY = 'silly test message';\n" + "var MSG_SILLY_HELP = 'help text';"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_SILLY", msg.getKey()); assertEquals("help text", msg.getDesc()); assertEquals("silly test message", msg.toString()); } public void testLegacyFormatParametizedFunction() { extractMessagesSafely("var MSG_SILLY = function(one, two) {" + " return one + ', ' + two + ', buckle my shoe';" + "};"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_SILLY", msg.getKey()); assertEquals(null, msg.getDesc()); assertEquals("{$one}, {$two}, buckle my shoe", msg.toString()); } public void testLegacyMessageWithDescAnnotation() { // Well, is was better do not allow legacy messages with @desc annotations, // but people love to mix styles so we need to check @desc also. extractMessagesSafely( "/** @desc The description */ var MSG_A = 'The Message';"); assertEquals(1, messages.size()); assertEquals(1, compiler.getWarningCount()); JsMessage msg = messages.get(0); assertEquals("MSG_A", msg.getKey()); assertEquals("The Message", msg.toString()); assertEquals("The description", msg.getDesc()); } public void testLegacyMessageWithDescAnnotationAndHelpVar() { // Well, is was better do not allow legacy messages with @desc annotations, // but people love to mix styles so we need to check @desc also. extractMessagesSafely( "var MSG_A_HELP = 'This is a help var';\n" + "/** @desc The description in @desc*/ var MSG_A = 'The Message';"); assertEquals(1, messages.size()); assertEquals(1, compiler.getWarningCount()); JsMessage msg = messages.get(0); assertEquals("MSG_A", msg.getKey()); assertEquals("The Message", msg.toString()); assertEquals("The description in @desc", msg.getDesc()); } public void testClosureMessageWithHelpPostfix() { extractMessagesSafely("/** @desc help text */\n" + "var MSG_FOO_HELP = goog.getMsg('Help!');"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_FOO_HELP", msg.getKey()); assertEquals("help text", msg.getDesc()); assertEquals("Help!", msg.toString()); } public void testClosureMessageWithoutGoogGetmsg() { allowLegacyMessages = false; extractMessages("var MSG_FOO_HELP = 'I am a bad message';"); assertEquals(1, messages.size()); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals(JsMessageVisitor.MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX, error.getType()); } public void testClosureFormatParametizedFunction() { extractMessagesSafely("/** @desc help text */" + "var MSG_SILLY = goog.getMsg('{$adjective} ' + 'message', " + "{'adjective': 'silly'});"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_SILLY", msg.getKey()); assertEquals("help text", msg.getDesc()); assertEquals("{$adjective} message", msg.toString()); } public void testHugeMessage() { extractMessagesSafely("/**" + " * @desc A message with lots of stuff.\n" + " * @hidden\n" + " */" + "var MSG_HUGE = goog.getMsg(" + " '{$startLink_1}Google{$endLink}' +" + " '{$startLink_2}blah{$endLink}{$boo}{$foo_001}{$boo}' +" + " '{$foo_002}{$xxx_001}{$image}{$image_001}{$xxx_002}'," + " {'startLink_1': ''," + " 'endLink': ''," + " 'startLink_2': ''," + " 'boo': opt_data.boo," + " 'foo_001': opt_data.foo," + " 'foo_002': opt_data.boo.foo," + " 'xxx_001': opt_data.boo + opt_data.foo," + " 'image': htmlTag7," + " 'image_001': opt_data.image," + " 'xxx_002': foo.callWithOnlyTopLevelKeys(" + " bogusFn, opt_data, null, 'bogusKey1'," + " opt_data.moo, 'bogusKey2', param10)});"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_HUGE", msg.getKey()); assertEquals("A message with lots of stuff.", msg.getDesc()); assertTrue(msg.isHidden()); assertEquals("{$startLink_1}Google{$endLink}{$startLink_2}blah{$endLink}" + "{$boo}{$foo_001}{$boo}{$foo_002}{$xxx_001}{$image}" + "{$image_001}{$xxx_002}", msg.toString()); } public void testUnnamedGoogleMessage() { extractMessagesSafely("var MSG_UNNAMED_2 = goog.getMsg('Hullo');"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals(null, msg.getDesc()); assertEquals("MSG_16LJMYKCXT84X", msg.getKey()); assertEquals("MSG_16LJMYKCXT84X", msg.getId()); } public void testEmptyTextMessage() { extractMessagesSafely("/** @desc text */ var MSG_FOO = goog.getMsg('');"); assertEquals(1, messages.size()); assertEquals(1, compiler.getWarningCount()); assertEquals("Message value of MSG_FOO is just an empty string. " + "Empty messages are forbidden.", compiler.getWarnings()[0].description); } public void testEmptyTextComplexMessage() { extractMessagesSafely("/** @desc text */ var MSG_BAR = goog.getMsg(" + "'' + '' + '' + ''\n+'');"); assertEquals(1, messages.size()); assertEquals(1, compiler.getWarningCount()); assertEquals("Message value of MSG_BAR is just an empty string. " + "Empty messages are forbidden.", compiler.getWarnings()[0].description); } public void testMessageIsNoUnnamed() { extractMessagesSafely("var MSG_UNNAMED_ITEM = goog.getMsg('Hullo');"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_UNNAMED_ITEM", msg.getKey()); assertFalse(msg.isHidden()); } public void testMsgVarWithoutAssignment() { extractMessages("var MSG_SILLY;"); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals(JsMessageVisitor.MESSAGE_HAS_NO_VALUE, error.getType()); } public void testRegularVarWithoutAssignment() { extractMessagesSafely("var SILLY;"); assertTrue(messages.isEmpty()); } public void itIsNotImplementedYet_testMsgPropertyWithoutAssignment() { extractMessages("goog.message.MSG_SILLY_PROP;"); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals("Message MSG_SILLY_PROP has no value", error.description); } public void testMsgVarWithIncorrectRightSide() { extractMessages("var MSG_SILLY = 0;"); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals("Message parse tree malformed. Cannot parse value of " + "message MSG_SILLY", error.description); } public void testIncorrectMessage() { extractMessages("DP_DatePicker.MSG_DATE_SELECTION = {};"); assertEquals(0, messages.size()); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals("Message parse tree malformed. "+ "Message must be initialized using goog.getMsg function.", error.description); } public void testUnrecognizedFunction() { allowLegacyMessages = false; extractMessages("DP_DatePicker.MSG_DATE_SELECTION = somefunc('a')"); assertEquals(0, messages.size()); assertEquals(1, compiler.getErrors().length); JSError error = compiler.getErrors()[0]; assertEquals("Message parse tree malformed. "+ "Message initialized using unrecognized function. " + "Please use goog.getMsg() instead.", error.description); } public void testExtractPropertyMessage() { extractMessagesSafely("/**" + " * @desc A message that demonstrates placeholders\n" + " * @hidden\n" + " */" + "a.b.MSG_SILLY = goog.getMsg(\n" + " '{$adjective} ' + '{$someNoun}',\n" + " {'adjective': adj, 'someNoun': noun});"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_SILLY", msg.getKey()); assertEquals("{$adjective} {$someNoun}", msg.toString()); assertEquals("A message that demonstrates placeholders", msg.getDesc()); assertTrue(msg.isHidden()); } public void testAlmostButNotExternalMessage() { extractMessagesSafely( "/** @desc External */ var MSG_EXTERNAL = goog.getMsg('External');"); assertEquals(0, compiler.getWarningCount()); assertEquals(1, messages.size()); assertFalse(messages.get(0).isExternal()); assertEquals("MSG_EXTERNAL", messages.get(0).getKey()); } public void testExternalMessage() { extractMessagesSafely("var MSG_EXTERNAL_111 = goog.getMsg('Hello World');"); assertEquals(0, compiler.getWarningCount()); assertEquals(1, messages.size()); assertTrue(messages.get(0).isExternal()); assertEquals("111", messages.get(0).getId()); } public void testIsValidMessageNameStrict() { JsMessageVisitor visitor = new DummyJsVisitor(CLOSURE); assertTrue(visitor.isMessageName("MSG_HELLO", true)); assertTrue(visitor.isMessageName("MSG_", true)); assertTrue(visitor.isMessageName("MSG_HELP", true)); assertTrue(visitor.isMessageName("MSG_FOO_HELP", true)); assertFalse(visitor.isMessageName("_FOO_HELP", true)); assertFalse(visitor.isMessageName("MSGFOOP", true)); } public void testIsValidMessageNameRelax() { JsMessageVisitor visitor = new DummyJsVisitor(RELAX); assertFalse(visitor.isMessageName("MSG_HELP", false)); assertFalse(visitor.isMessageName("MSG_FOO_HELP", false)); } public void testIsValidMessageNameLegacy() { theseAreLegacyMessageNames(new DummyJsVisitor(RELAX)); theseAreLegacyMessageNames(new DummyJsVisitor(LEGACY)); } private void theseAreLegacyMessageNames(JsMessageVisitor visitor) { assertTrue(visitor.isMessageName("MSG_HELLO", false)); assertTrue(visitor.isMessageName("MSG_", false)); assertFalse(visitor.isMessageName("MSG_HELP", false)); assertFalse(visitor.isMessageName("MSG_FOO_HELP", false)); assertFalse(visitor.isMessageName("_FOO_HELP", false)); assertFalse(visitor.isMessageName("MSGFOOP", false)); } public void testUnexistedPlaceholders() { extractMessages("var MSG_FOO = goog.getMsg('{$foo}:', {});"); assertEquals(0, messages.size()); JSError[] errors = compiler.getErrors(); assertEquals(1, errors.length); JSError error = errors[0]; assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); assertEquals("Message parse tree malformed. Unrecognized message " + "placeholder referenced: foo", error.description); } public void testUnusedReferenesAreNotOK() { extractMessages("/** @desc AA */ " + "var MSG_FOO = goog.getMsg('lalala:', {foo:1});"); assertEquals(0, messages.size()); JSError[] errors = compiler.getErrors(); assertEquals(1, errors.length); JSError error = errors[0]; assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); assertEquals("Message parse tree malformed. Unused message placeholder: " + "foo", error.description); } public void testDuplicatePlaceHoldersAreBad() { extractMessages("var MSG_FOO = goog.getMsg(" + "'{$foo}:', {'foo': 1, 'foo' : 2});"); assertEquals(0, messages.size()); JSError[] errors = compiler.getErrors(); assertEquals(1, errors.length); JSError error = errors[0]; assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); assertEquals("Message parse tree malformed. Duplicate placeholder " + "name: foo", error.description); } public void testDuplicatePlaceholderReferencesAreOk() { extractMessagesSafely("var MSG_FOO = goog.getMsg(" + "'{$foo}:, {$foo}', {'foo': 1});"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("{$foo}:, {$foo}", msg.toString()); } public void testCamelcasePlaceholderNamesAreOk() { extractMessagesSafely("var MSG_WITH_CAMELCASE = goog.getMsg(" + "'Slide {$slideNumber}:', {'slideNumber': opt_index + 1});"); assertEquals(1, messages.size()); JsMessage msg = messages.get(0); assertEquals("MSG_WITH_CAMELCASE", msg.getKey()); assertEquals("Slide {$slideNumber}:", msg.toString()); List parts = msg.parts(); assertEquals(3, parts.size()); assertEquals("slideNumber", ((JsMessage.PlaceholderReference)parts.get(1)).getName()); } public void testWithNonCamelcasePlaceholderNamesAreNotOk() { extractMessages("var MSG_WITH_CAMELCASE = goog.getMsg(" + "'Slide {$slide_number}:', {'slide_number': opt_index + 1});"); assertEquals(0, messages.size()); JSError[] errors = compiler.getErrors(); assertEquals(1, errors.length); JSError error = errors[0]; assertEquals(JsMessageVisitor.MESSAGE_TREE_MALFORMED, error.getType()); assertEquals("Message parse tree malformed. Placeholder name not in " + "lowerCamelCase: slide_number", error.description); } public void testUnquotedPlaceholdersAreOk() { extractMessagesSafely("/** @desc Hello */ " + "var MSG_FOO = goog.getMsg('foo {$unquoted}:', {unquoted: 12});"); assertEquals(1, messages.size()); assertEquals(0, compiler.getWarningCount()); } public void testIsLowerCamelCaseWithNumericSuffixes() { assertTrue(isLowerCamelCaseWithNumericSuffixes("name")); assertFalse(isLowerCamelCaseWithNumericSuffixes("NAME")); assertFalse(isLowerCamelCaseWithNumericSuffixes("Name")); assertTrue(isLowerCamelCaseWithNumericSuffixes("a4Letter")); assertFalse(isLowerCamelCaseWithNumericSuffixes("A4_LETTER")); assertTrue(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23")); assertFalse(isLowerCamelCaseWithNumericSuffixes("startSpan_1_23b")); assertFalse(isLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23")); assertFalse(isLowerCamelCaseWithNumericSuffixes("")); } public void testToLowerCamelCaseWithNumericSuffixes() { assertEquals("name", toLowerCamelCaseWithNumericSuffixes("NAME")); assertEquals("a4Letter", toLowerCamelCaseWithNumericSuffixes("A4_LETTER")); assertEquals("startSpan_1_23", toLowerCamelCaseWithNumericSuffixes("START_SPAN_1_23")); } public void testDuplicateMessageError() { extractMessages( "(function () {/** @desc Hello */ var MSG_HELLO = goog.getMsg('a')})" + "(function () {/** @desc Hello2 */ var MSG_HELLO = goog.getMsg('a')})"); assertEquals(0, compiler.getWarningCount()); assertOneError(JsMessageVisitor.MESSAGE_DUPLICATE_KEY); } public void testNoDuplicateErrorOnExternMessage() { extractMessagesSafely( "(function () {/** @desc Hello */ " + "var MSG_EXTERNAL_2 = goog.getMsg('a')})" + "(function () {/** @desc Hello2 */ " + "var MSG_EXTERNAL_2 = goog.getMsg('a')})"); } public void testErrorWhenUsingMsgPrefixWithFallback() { extractMessages( "/** @desc Hello */ var MSG_HELLO_1 = goog.getMsg('hello');\n" + "/** @desc Hello */ var MSG_HELLO_2 = goog.getMsg('hello');\n" + "/** @desc Hello */ " + "var MSG_HELLO_3 = goog.getMsgWithFallback(MSG_HELLO_1, MSG_HELLO_2);"); assertOneError(JsMessageVisitor.MESSAGE_TREE_MALFORMED); } private void assertOneError(DiagnosticType type) { String errors = Joiner.on("\n").join(compiler.getErrors()); assertEquals("There should be one error. " + errors, 1, compiler.getErrorCount()); JSError error = compiler.getErrors()[0]; assertEquals(type, error.getType()); } private void extractMessagesSafely(String input) { extractMessages(input); assertEquals( "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); } private void extractMessages(String input) { compiler = new Compiler(); Node root = compiler.parseTestCode(input); JsMessageVisitor visitor = new CollectMessages(compiler); visitor.process(null, root); } private class CollectMessages extends JsMessageVisitor { private CollectMessages(Compiler compiler) { super(compiler, true, Style.getFromParams(true, allowLegacyMessages), null); } @Override protected void processJsMessage(JsMessage message, JsMessageDefinition definition) { messages.add(message); } } private class DummyJsVisitor extends JsMessageVisitor { private DummyJsVisitor(Style style) { super(null, true, style, null); } @Override protected void processJsMessage(JsMessage message, JsMessageDefinition definition) { // no-op } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/StripCodeTest.java0000644000175000017500000003276112115204405026434 0ustar apoapo/* * Copyright 2007 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.Sets; import java.util.Set; /** * Tests for {@link StripCode}. * */ public class StripCodeTest extends CompilerTestCase { private static final String EXTERNS = ""; public StripCodeTest() { super(EXTERNS, true); } /** * Creates an instance for removing logging code. * * @param compiler The Compiler * @return A new {@link StripCode} instance */ private static StripCode createLoggerInstance(Compiler compiler) { Set stripTypes = Sets.newHashSet( "goog.debug.DebugWindow", "goog.debug.FancyWindow", "goog.debug.Formatter", "goog.debug.HtmlFormatter", "goog.debug.TextFormatter", "goog.debug.Logger", "goog.debug.LogManager", "goog.debug.LogRecord", "goog.net.BrowserChannel.LogSaver", "GA_GoogleDebugger"); Set stripNames = Sets.newHashSet( "logger", "logger_", "debugWindow", "debugWindow_", "logFormatter_", "logBuffer_"); Set stripNamePrefixes = Sets.newHashSet("trace"); Set stripTypePrefixes = Sets.newHashSet("e.f.Trace"); return new StripCode(compiler, stripTypes, stripNames, stripTypePrefixes, stripNamePrefixes); } @Override public CompilerPass getProcessor(Compiler compiler) { return createLoggerInstance(compiler); } public void testLoggerDefinedInConstructor() { test("a.b.c = function() {" + " this.logger = goog.debug.Logger.getLogger('a.b.c');" + "};", "a.b.c=function(){}"); } public void testLoggerDefinedInPrototype1() { test("a.b.c = function() {};" + "a.b.c.prototype.logger = goog.debug.Logger.getLogger('a.b.c');", "a.b.c=function(){}"); } public void testLoggerDefinedInPrototype2() { test("a.b.c = function() {};" + "a.b.c.prototype = {logger: goog.debug.Logger.getLogger('a.b.c')}", "a.b.c = function() {};" + "a.b.c.prototype = {}"); } public void testLoggerDefinedInPrototype3() { test("a.b.c = function() {};" + "a.b.c.prototype = { " + " get logger() {return goog.debug.Logger.getLogger('a.b.c')}" + "}", "a.b.c = function() {};" + "a.b.c.prototype = {}"); } public void testLoggerDefinedInPrototype4() { test("a.b.c = function() {};" + "a.b.c.prototype = { " + " set logger(a) {this.x = goog.debug.Logger.getLogger('a.b.c')}" + "}", "a.b.c = function() {};" + "a.b.c.prototype = {}"); } public void testLoggerDefinedInPrototype5() { test("a.b.c = function() {};" + "a.b.c.prototype = { " + " get f() {return this.x;}," + " set f(a) {this.x = goog.debug.Logger.getLogger('a.b.c')}" + "}", "a.b.c = function() {};" + "a.b.c.prototype = { " + " get f() {return this.x;}," + " set f(a) {this.x = null}" + "}"); } public void testLoggerDefinedStatically() { test("a.b.c = function() {};" + "a.b.c.logger = goog.debug.Logger.getLogger('a.b.c');", "a.b.c=function(){}"); } public void testLoggerDefinedInObjectLiteral1() { test("a.b.c = {" + " x: 0," + " logger: goog.debug.Logger.getLogger('a.b.c')" + "};", "a.b.c={x:0}"); } public void testLoggerDefinedInObjectLiteral2() { test("a.b.c = {" + " x: 0," + " get logger() {return goog.debug.Logger.getLogger('a.b.c')}" + "};", "a.b.c={x:0}"); } public void testLoggerDefinedInObjectLiteral3() { test("a.b.c = {" + " x: null," + " get logger() {return this.x}," + " set logger(a) {this.x = goog.debug.Logger.getLogger(a)}" + "};", "a.b.c={x:null}"); } public void testLoggerDefinedInObjectLiteral4() { test("a.b.c = {" + " x: null," + " get y() {return this.x}," + " set y(a) {this.x = goog.debug.Logger.getLogger(a)}" + "};", "a.b.c = {" + " x: null," + " get y() {return this.x}," + " set y(a) {this.x = null}" + "};"); } public void testLoggerDefinedInPrototypeAndUsedInConstructor() { test("a.b.c = function(level) {" + " if (!this.logger.isLoggable(level)) {" + " this.logger.setLevel(level);" + " }" + " this.logger.log(level, 'hi');" + "};" + "a.b.c.prototype.logger = goog.debug.Logger.getLogger('a.b.c');" + "a.b.c.prototype.go = function() { this.logger.finer('x'); };", "a.b.c=function(level){if(!null);};" + "a.b.c.prototype.go=function(){}"); } public void testLoggerDefinedStaticallyAndUsedInConstructor() { test("a.b.c = function(level) {" + " if (!a.b.c.logger.isLoggable(level)) {" + " a.b.c.logger.setLevel(level);" + " }" + " a.b.c.logger.log(level, 'hi');" + "};" + "a.b.c.logger = goog.debug.Logger.getLogger('a.b.c');", "a.b.c=function(level){if(!null);}"); } public void testLoggerVarDeclaration() { test("var logger = opt_logger || goog.debug.LogManager.getRoot();", ""); } public void testLoggerMethodCallByVariableType() { test("var x = goog.debug.Logger.getLogger('a.b.c'); y.info(a); x.info(a);", "y.info(a)"); } public void testSubPropertyAccessByVariableName() { test("var x, y = goog.debug.Logger.getLogger('a.b.c');" + "var logger = x;" + "var curlevel = logger.level_ ? logger.getLevel().name : 3;", "var x;var curlevel=null?null:3"); } public void testPrefixedVariableName() { test("this.blcLogger_ = goog.debug.Logger.getLogger('a.b.c');" + "this.blcLogger_.fine('Raised dirty states.');", ""); } public void testPrefixedPropertyName() { test("a.b.c.staticLogger_ = goog.debug.Logger.getLogger('a.b.c');" + "a.b.c.staticLogger_.fine('-' + a.b.c.d_())", ""); } public void testPrefixedClassName() { test("a.b.MyLogger = function(logger) {" + " this.logger_ = logger;" + "};" + "a.b.MyLogger.prototype.shout = function(msg, opt_x) {" + " this.logger_.log(goog.debug.Logger.Level.SHOUT, msg, opt_x);" + "};", "a.b.MyLogger=function(logger){};" + "a.b.MyLogger.prototype.shout=function(msg,opt_x){}"); } public void testLoggerClassDefinition() { test("goog.debug.Logger=function(name){this.name_=name}", ""); } public void testStaticLoggerPropertyDefinition() { test("goog.debug.Logger.Level.SHOUT=" + "new goog.debug.Logger.Level(x,1200)", ""); } public void testStaticLoggerMethodDefinition() { test("goog.debug.Logger.getLogger=function(name){" + "return goog.debug.LogManager.getLogger(name)" + "};", ""); } public void testPrototypeFieldDefinition() { test("goog.debug.Logger.prototype.level_=null;", ""); } public void testPrototypeFieldDefinitionWithoutAssignment() { test("goog.debug.Logger.prototype.level_;", ""); } public void testPrototypeMethodDefinition() { test("goog.debug.Logger.prototype.addHandler=" + "function(handler){this.handlers_.push(handler)};", ""); } public void testPublicPropertyAssignment() { // We don't eliminate property assignments on vars/properties that we // remove, since the debugging classes should have setter methods instead // of public properties. testSame("rootLogger.someProperty=3"); testSame("this.blcLogger_.level=x"); testSame("goog.ui.Component.logger.prop=y"); } public void testGlobalCallWithStrippedType() { testSame("window.alert(goog.debug.Logger)"); } public void testClassDefiningCallWithStripType1() { test("goog.debug.Logger.inherits(Object)", ""); } public void testClassDefiningCallWithStripType2() { test("goog.formatter=function(){};" + "goog.inherits(goog.debug.Formatter,goog.formatter)", "goog.formatter=function(){}"); } public void testClassDefiningCallWithStripType3() { test("goog.formatter=function(){};" + "goog.inherits(goog.formatter,goog.debug.Formatter)", null, StripCode.STRIP_TYPE_INHERIT_ERROR); } public void testClassDefiningCallWithStripType4() { test("goog.formatter=function(){};" + "goog.formatter.inherits(goog.debug.Formatter)", null, StripCode.STRIP_TYPE_INHERIT_ERROR); } public void testClassDefiningCallWithStripType5() { testSame("goog.formatter=function(){};" + "goog.formatter.inherits(goog.debug.FormatterFoo)"); } public void testClassDefiningCallWithStripType6() { test("goog.formatter=function(){};" + "goog.formatter.inherits(goog.debug.Formatter.Foo)", null, StripCode.STRIP_TYPE_INHERIT_ERROR); } public void testClassDefiningCallWithStripType7() { test("goog.inherits(goog.debug.TextFormatter,goog.debug.Formatter)", ""); } public void testClassDefiningCallWithStripType8() { // listed types should be removed. test("goog.debug.DebugWindow = function(){}", ""); test("goog.inherits(goog.debug.DebugWindow,Base)", ""); // types that happen to have strip types as prefix should not be // stripped. testSame("goog.debug.DebugWindowFoo=function(){}"); testSame("goog.inherits(goog.debug.DebugWindowFoo,Base)"); testSame("goog.debug.DebugWindowFoo"); testSame("goog.debug.DebugWindowFoo=1"); // qualified subtypes should be removed. test("goog.debug.DebugWindow.Foo=function(){}", ""); test("goog.inherits(goog.debug.DebugWindow.Foo,Base)", ""); test("goog.debug.DebugWindow.Foo", ""); test("goog.debug.DebugWindow.Foo=1", ""); } public void testPropertyWithEmptyStringKey() { test("goog.format.NUMERIC_SCALES_BINARY_ = {'': 1};", "goog.format.NUMERIC_SCALES_BINARY_={\"\":1}"); } public void testVarinIf() { test("if(x)var logger=null;else foo()", "if(x);else foo()"); } public void testGetElemInIf() { test("var logger=null;if(x)logger[f];else foo()", "if(x);else foo()"); } public void testAssignInIf() { test("var logger=null;if(x)logger=1;else foo()", "if(x);else foo()"); } public void testNamePrefix() { test("a = function(traceZZZ) {}; a.prototype.traceXXX = {x: 1};" + "a.prototype.z = function() { this.traceXXX.f(); };" + "var traceYYY = 0;", "a=function(traceZZZ){};a.prototype.z=function(){}"); } public void testTypePrefix() { test("e.f.TraceXXX = function() {}; " + "e.f.TraceXXX.prototype.yyy = 2;", ""); } public void testStripCallsToStrippedNames() { test("a = function() { this.logger_ = function(msg){}; };" + "a.prototype.b = function() { this.logger_('hi'); }", "a=function(){};a.prototype.b=function(){}"); test("a = function() {};" + "a.prototype.logger_ = function(msg) {};" + "a.prototype.b = function() { this.logger_('hi'); }", "a=function(){};a.prototype.b=function(){}"); } public void testStripVarsInitializedFromStrippedNames() { test("a = function() { this.logger_ = function() { return 1; }; };" + "a.prototype.b = function() { " + " var one = this.logger_(); if (one) foo() }", "a=function(){};a.prototype.b=function(){if(null)foo()}"); } public void testReportErrorOnStripInNestedAssignment() { // Strip name test("(foo.logger_ = 7) + 8", "(foo.logger_ = 7) + 8", StripCode.STRIP_ASSIGNMENT_ERROR); // Strip namespaced type test("(goog.debug.Logger.foo = 7) + 8", "(goog.debug.Logger.foo = 7) + 8", StripCode.STRIP_ASSIGNMENT_ERROR); // Strip non-namespaced type test("(GA_GoogleDebugger.foo = 7) + 8", "(GA_GoogleDebugger.foo = 7) + 8", StripCode.STRIP_ASSIGNMENT_ERROR); } public void testNewOperatior1() { test("function foo() {} foo.bar = new goog.debug.Logger();", "function foo() {} foo.bar = null;"); } public void testNewOperatior2() { test("function foo() {} foo.bar = (new goog.debug.Logger()).foo();", "function foo() {} foo.bar = null;"); } public void testCrazyNesting1() { test("var x = {}; x[new goog.debug.Logger()] = 3;", "var x = {}; x[null] = 3;"); } public void testCrazyNesting2() { test("var x = {}; x[goog.debug.Logger.getLogger()] = 3;", "var x = {}; x[null] = 3;"); } public void testCrazyNesting3() { test("var x = function() {}; x(new goog.debug.Logger());", "var x = function() {}; x(null);"); } public void testCrazyNesting4() { test("var x = function() {}; x(goog.debug.Logger.getLogger());", "var x = function() {}; x(null);"); } public void testCrazyNesting5() { test("var x = function() {}; var y = {}; " + "var z = goog.debug.Logger.getLogger(); x(y[z['foo']]);", "var x = function() {}; var y = {}; x(y[null]);"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckDebuggerStatementTest.java0000644000175000017500000000454612115204405031107 0ustar apoapo/* * Copyright 2011 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.CheckLevel; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.DiagnosticGroups; /** * {@link CheckDebuggerStatementTest} is a unit test for * {@link CheckDebuggerStatement}. * * @author bolinfest@google.com (Michael Bolin) */ public class CheckDebuggerStatementTest extends CompilerTestCase { private CheckLevel checkLevel; @Override public void tearDown() { checkLevel = null; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CheckDebuggerStatement(compiler); } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); if (checkLevel != null) { options.setWarningLevel( DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT, checkLevel); } return options; } public void testCheckDebuggerStatement() { checkLevel = CheckLevel.WARNING; testSame("debugger;", CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); testSame("function foo() { debugger; }", CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); } public void testCheckIsDisabledByDefault() { checkLevel = null; testSame("debugger;"); testSame("function foo() { debugger; }"); } public void testNoWarningWhenExplicitlyDisabled() { checkLevel = CheckLevel.OFF; testSame("debugger;"); testSame("function foo() { debugger; }"); } public void testCheckDebuggerKeywordMayAppearInComments() { checkLevel = CheckLevel.WARNING; test("// I like the debugger; it is helpful.", ""); } public void testCheckDebuggerStatementInEval() { checkLevel = CheckLevel.WARNING; testSame("eval('debugger');"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MaybeReachingVariableUseTest.java0000644000175000017500000001313012115204405031346 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.Collection; import java.util.List; /** * Tests for {@link MaybeReachingVariableUse}. * */ public class MaybeReachingVariableUseTest extends TestCase { private MaybeReachingVariableUse useDef = null; private Node def = null; private List uses = null; /* * The test cases consist of a short code snippet that has an instruction * labeled with D and one or more with label starting with U. When assertMatch * is called, the test suite verifies that all the uses with label starting * with U is reachable to the definition label at D. */ public void testStraightLine() { assertMatch("D:var x=1; U: x"); assertMatch("var x; D:x=1; U: x"); assertNotMatch("D:var x=1; x = 2; U: x"); assertMatch("var x=1; D:x=2; U: x"); assertNotMatch("U:x; D:var x = 1"); assertMatch("D: var x = 1; var y = 2; y; U:x"); } public void testIf() { assertMatch("var x; if(a){ D:x=1 }else { x=2 }; U:x"); assertMatch("var x; if(a){ x=1 }else { D:x=2 }; U:x"); assertMatch("D:var x=1; if(a){ U1: x }else { U2: x };"); } public void testLoops() { assertMatch("var x=0; while(a){ D:x=1 }; U:x"); assertMatch("var x=0; for(;;) { D:x=1 }; U:x"); assertMatch("D:var x=1; while(a) { U:x }"); assertMatch("D:var x=1; for(;;) { U:x }"); } public void testConditional() { assertMatch("var x=0; var y; D:(x=1)&&y; U:x"); assertMatch("var x=0; var y; D:y&&(x=1); U:x"); assertMatch("var x=0; var y=0; D:(x=1)&&(y=0); U:x"); assertMatch("var x=0; var y=0; D:(y=0)&&(x=1); U:x"); assertNotMatch("D: var x=0; var y=0; (x=1)&&(y=0); U:x"); assertMatch("D: var x=0; var y=0; (y=1)&&((y=2)||(x=1)); U:x"); assertMatch("D: var x=0; var y=0; (y=0)&&(x=1); U:x"); } public void testUseAndDefInSameInstruction() { assertNotMatch("D:var x=0; U:x=1,x"); assertMatch("D:var x=0; U:x,x=1"); } public void testAssignmentInExpressions() { assertMatch("var x=0; D:foo(bar(x=1)); U:x"); assertMatch("var x=0; D:foo(bar + (x = 1)); U:x"); } public void testHook() { assertMatch("var x=0; D:foo() ? x=1 : bar(); U:x"); assertMatch("var x=0; D:foo() ? x=1 : x=2; U:x"); } public void testAssignmentOps() { assertNotMatch("D: var x = 0; U: x = 100"); assertMatch("D: var x = 0; U: x += 100"); assertMatch("D: var x = 0; U: x -= 100"); } public void testInc() { assertMatch("D: var x = 0; U:x++"); assertMatch("var x = 0; D:x++; U:x"); } public void testForIn() { // Uses within FOR-IN header are hard to test. They are covered // by the tests in the flow sensitive inliner. assertNotMatch("D: var x = [], foo; U: for (x in foo) { }"); assertNotMatch("D: var x = [], foo; for (x in foo) { U:x }"); assertMatch("var x = [], foo; D: for (x in foo) { U:x }"); } public void testTryCatch() { assertMatch( "D: var x = 1; " + "try { U: var y = foo() + x; } catch (e) {} " + "U: var z = x;"); } /** * The def of x at D: may be used by the read of x at U:. */ private void assertMatch(String src) { computeUseDef(src); Collection result = useDef.getUses("x", def); assertTrue(result.size() == uses.size()); assertTrue(result.containsAll(uses)); } /** * The def of x at D: is not used by the read of x at U:. */ private void assertNotMatch(String src) { computeUseDef(src); assertFalse(useDef.getUses("x", def).contains(uses)); } /** * Computes reaching use on given source. */ private void computeUseDef(String src) { Compiler compiler = new Compiler(); src = "function _FUNCTION(param1, param2){" + src + "}"; Node n = compiler.parseTestCode(src).getFirstChild(); assertEquals(0, compiler.getErrorCount()); Scope scope = new SyntacticScopeCreator(compiler).createScope(n, null); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, n); ControlFlowGraph cfg = cfa.getCfg(); useDef = new MaybeReachingVariableUse(cfg, scope, compiler); useDef.analyze(); def = null; uses = Lists.newArrayList(); new NodeTraversal(compiler,new LabelFinder()).traverse(n); assertNotNull("Code should have an instruction labeled D", def); assertFalse("Code should have an instruction labeled starting withing U", uses.isEmpty()); } /** * Finds the D: and U: label and store which node they point to. */ private class LabelFinder extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isLabel()) { if (n.getFirstChild().getString().equals("D")) { def = n.getLastChild(); } else if (n.getFirstChild().getString().startsWith("U")) { uses.add(n.getLastChild()); } } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DotFormatterTest.java0000644000175000017500000000557412115204405027154 0ustar apoapo/* * Copyright 2007 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.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; public class DotFormatterTest extends TestCase { /** * Tests that keys are assigned sequentially. */ public void testKeyAssignementSequential() throws Exception { DotFormatter dot = DotFormatter.newInstanceForTesting(); assertEquals(0, dot.key(new Node(Token.BLOCK))); assertEquals(1, dot.key(new Node(Token.BLOCK))); assertEquals(2, dot.key(new Node(Token.BLOCK))); assertEquals(3, dot.key(new Node(Token.BLOCK))); assertEquals(4, dot.key(new Node(Token.BLOCK))); } /** * Tests that keys are assigned once per node. */ public void testKeyAssignementOncePerNode() throws Exception { DotFormatter dot = DotFormatter.newInstanceForTesting(); Node node0 = new Node(Token.BLOCK); Node node1 = new Node(Token.BLOCK); Node node2 = new Node(Token.BLOCK); assertEquals(0, dot.key(node0)); assertEquals(1, dot.key(node1)); assertEquals(2, dot.key(node2)); assertEquals(0, dot.key(node0)); assertEquals(1, dot.key(node1)); assertEquals(2, dot.key(node2)); } /** * Tests the formatting (simple tree). */ public void testToDotSimple() throws Exception { Node ast = new Node(Token.BITOR); String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"BITOR\"];\n" + "}\n"; test(expected, ast); } /** * Tests the formatting (3 element tree). */ public void testToDot3Elements() throws Exception { Node ast = new Node(Token.BLOCK); ast.addChildToBack(new Node(Token.NAME)); ast.addChildToBack(new Node(Token.STRING)); String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"BLOCK\"];\n" + " node1 [label=\"NAME\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"STRING\"];\n" + " node0 -> node2 [weight=1];\n" + "}\n"; test(expected, ast); } private void test(String expected, Node ast) { try { assertEquals(expected, DotFormatter.toDot(ast)); } catch (java.io.IOException e) { fail("Tests failed with IOExceptions"); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/LooseTypeCheckTest.java0000644000175000017500000074361212115204405027425 0ustar apoapo/* * Copyright 2006 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.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.testing.Asserts; import java.util.Arrays; import java.util.List; import java.util.Set; /** * Tests {@link TypeCheck}. * * This is a temporary fork of the TypeCheckTest for the experimental * "looseTypes" option. These tests should be be folded into TypeCheckTest * or removed along with the looseTypes option. * */ public class LooseTypeCheckTest extends CompilerTypeTestCase { @Override public CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.looseTypes = true; return options; } public void testInitialTypingScope() { Scope s = new TypedScopeCreator(compiler, CodingConventions.getDefault()).createInitialScope( new Node(Token.BLOCK)); assertTypeEquals(ARRAY_FUNCTION_TYPE, s.getVar("Array").getType()); assertTypeEquals(BOOLEAN_OBJECT_FUNCTION_TYPE, s.getVar("Boolean").getType()); assertTypeEquals(DATE_FUNCTION_TYPE, s.getVar("Date").getType()); assertTypeEquals(ERROR_FUNCTION_TYPE, s.getVar("Error").getType()); assertTypeEquals(EVAL_ERROR_FUNCTION_TYPE, s.getVar("EvalError").getType()); assertTypeEquals(NUMBER_OBJECT_FUNCTION_TYPE, s.getVar("Number").getType()); assertTypeEquals(OBJECT_FUNCTION_TYPE, s.getVar("Object").getType()); assertTypeEquals(RANGE_ERROR_FUNCTION_TYPE, s.getVar("RangeError").getType()); assertTypeEquals(REFERENCE_ERROR_FUNCTION_TYPE, s.getVar("ReferenceError").getType()); assertTypeEquals(REGEXP_FUNCTION_TYPE, s.getVar("RegExp").getType()); assertTypeEquals(STRING_OBJECT_FUNCTION_TYPE, s.getVar("String").getType()); assertTypeEquals(SYNTAX_ERROR_FUNCTION_TYPE, s.getVar("SyntaxError").getType()); assertTypeEquals(TYPE_ERROR_FUNCTION_TYPE, s.getVar("TypeError").getType()); assertTypeEquals(URI_ERROR_FUNCTION_TYPE, s.getVar("URIError").getType()); } public void testTypeCheck1() throws Exception { testTypes("/**@return {void}*/function foo(){ if (foo()) return; }"); } public void testTypeCheck2() throws Exception { testTypes("/**@return {void}*/function foo(){ var x=foo(); x--; }", "increment/decrement\n" + "found : undefined\n" + "required: number"); } public void testTypeCheck4() throws Exception { testTypes("/**@return {void}*/function foo(){ !foo(); }"); } public void testTypeCheck5() throws Exception { testTypes("/**@return {void}*/function foo(){ var a = +foo(); }", "sign operator\n" + "found : undefined\n" + "required: number"); } public void testTypeCheck6() throws Exception { testTypes( "/**@return {void}*/function foo(){" + "/** @type {undefined|number} */var a;if (a == foo())return;}"); } public void testTypeCheck8() throws Exception { testTypes("/**@return {void}*/function foo(){do {} while (foo());}"); } public void testTypeCheck9() throws Exception { testTypes("/**@return {void}*/function foo(){while (foo());}"); } public void testTypeCheck10() throws Exception { testTypes("/**@return {void}*/function foo(){for (;foo(););}"); } public void testTypeCheck11() throws Exception { testTypes("/**@type !Number */var a;" + "/**@type !String */var b;" + "a = b;", "assignment\n" + "found : String\n" + "required: Number"); } public void testTypeCheck12() throws Exception { testTypes("/**@return {!Object}*/function foo(){var a = 3^foo();}", "bad right operand to bitwise operator\n" + "found : Object\n" + "required: (boolean|null|number|string|undefined)"); } public void testTypeCheck13() throws Exception { testTypes("/**@type {!Number|!String}*/var i; i=/xx/;", "assignment\n" + "found : RegExp\n" + "required: (Number|String)"); } public void testTypeCheck14() throws Exception { testTypes("/**@param opt_a*/function foo(opt_a){}"); } public void testTypeCheck15() throws Exception { testTypes("/**@type {Number} */var x;x=null;x=10;", "assignment\n" + "found : number\n" + "required: (Number|null|undefined)"); } public void testTypeCheck16a() throws Exception { testTypes("/**@type {Number|null} */var x='';", "initializing variable\n" + "found : string\n" + "required: (Number|null|undefined)"); } public void testTypeCheck16b() throws Exception { testTypes("/**@type {!Number|null} */var x='';", "initializing variable\n" + "found : string\n" + "required: (Number|null)"); } public void testTypeCheck17() throws Exception { testTypes("/**@return {Number}\n@param {Number} opt_foo */\n" + "function a(opt_foo){\nreturn /**@type {Number}*/(opt_foo);\n}"); } public void testTypeCheck18() throws Exception { testTypes("/**@return {RegExp}\n*/\n function a(){return new RegExp();}"); } public void testTypeCheck19() throws Exception { testTypes("/**@return {Array}\n*/\n function a(){return new Array();}"); } public void testTypeCheck20() throws Exception { testTypes("/**@return {Date}\n*/\n function a(){return new Date();}"); } public void testTypeCheckBasicDowncast() throws Exception { testTypes("/** @constructor */function foo() {}\n" + "/** @type {Object} */ var bar = new foo();\n"); } public void testTypeCheckNoDowncastToNumber() throws Exception { testTypes("/** @constructor */function foo() {}\n" + "/** @type {!Number} */ var bar = new foo();\n", "initializing variable\n" + "found : foo\n" + "required: Number"); } public void testTypeCheck21() throws Exception { testTypes("/** @type Array. */var foo;"); } public void testTypeCheck22() throws Exception { testTypes("/** @param {Element|Object} p */\nfunction foo(p){}\n" + "/** @constructor */function Element(){}\n" + "/** @type {Element|Object} */var v;\n" + "foo(v);\n"); } public void testTypeCheck23() throws Exception { testTypes("/** @type {(Object,Null)} */var foo; foo = null;"); } public void testTypeCheck24() throws Exception { testTypes("/** @constructor */function MyType(){}\n" + "/** @type {(MyType,Null)} */var foo; foo = null;"); } public void testTypeCheckDefaultExterns() throws Exception { testTypes("/** @param {string} x */ function f(x) {}" + "f([].length);" , "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testTypeCheckCustomExterns() throws Exception { testTypes( DEFAULT_EXTERNS + "/** @type {boolean} */ Array.prototype.oogabooga;", "/** @param {string} x */ function f(x) {}" + "f([].oogabooga);" , "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: string", false); } public void testTemplatizedArray1() throws Exception { testTypes("/** @param {!Array.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedArray2() throws Exception { testTypes("/** @param {!Array.>} a\n" + "* @return {number}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : Array.\n" + "required: number"); } public void testTemplatizedArray3() throws Exception { testTypes("/** @param {!Array.} a\n" + "* @return {number}\n" + "*/ var f = function(a) { a[1] = 0; return a[0]; };"); } public void testTemplatizedArray4() throws Exception { testTypes("/** @param {!Array.} a\n" + "*/ var f = function(a) { a[0] = 'a'; };", "assignment\n" + "found : string\n" + "required: number"); } public void testTemplatizedArray5() throws Exception { testTypes("/** @param {!Array.<*>} a\n" + "*/ var f = function(a) { a[0] = 'a'; };"); } public void testTemplatizedArray6() throws Exception { testTypes("/** @param {!Array.<*>} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : *\n" + "required: string"); } public void testTemplatizedArray7() throws Exception { testTypes("/** @param {?Array.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject1() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject2() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject3() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "restricted index type\n" + "found : string\n" + "required: number"); } public void testTemplatizedObject4() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "restricted index type\n" + "found : string\n" + "required: E."); } public void testUnionOfFunctionAndType() throws Exception { testTypes("/** @type {null|(function(Number):void)} */ var a;" + "/** @type {(function(Number):void)|null} */ var b = null; a = b;"); } public void testOptionalParameterComparedToUndefined() throws Exception { testTypes("/**@param opt_a {Number}*/function foo(opt_a)" + "{if (opt_a==undefined) var b = 3;}"); } public void testOptionalAllType() throws Exception { testTypes("/** @param {*} opt_x */function f(opt_x) { return opt_x }\n" + "/** @type {*} */var y;\n" + "f(y);"); } public void testOptionalUnknownNamedType() throws Exception { testTypes("/** @param {!T} opt_x\n@return {undefined} */\n" + "function f(opt_x) { return opt_x; }\n" + "/** @constructor */var T = function() {};", "inconsistent return type\n" + "found : (T|undefined)\n" + "required: undefined"); } public void testOptionalArgFunctionParam() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a()};"); } public void testOptionalArgFunctionParam2() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a(3)};"); } public void testOptionalArgFunctionParam3() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a(undefined)};"); } public void testOptionalArgFunctionParam4() throws Exception { String expectedWarning = "Function a: called with 2 argument(s). " + "Function requires at least 0 argument(s) and no more than 1 " + "argument(s)."; testTypes("/** @param {function(number=)} a */function f(a) {a(3,4)};", expectedWarning, false); } public void testOptionalArgFunctionParamError() throws Exception { String expectedWarning = "Bad type annotation. variable length argument must be last"; testTypes("/** @param {function(...[number], number=)} a */" + "function f(a) {};", expectedWarning, false); } public void testOptionalNullableArgFunctionParam() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a()};"); } public void testOptionalNullableArgFunctionParam2() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a(null)};"); } public void testOptionalNullableArgFunctionParam3() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a(3)};"); } public void testOptionalArgFunctionReturn() throws Exception { testTypes("/** @return {function(number=)} */" + "function f() { return function(opt_x) { }; };" + "f()()"); } public void testOptionalArgFunctionReturn2() throws Exception { testTypes("/** @return {function(Object=)} */" + "function f() { return function(opt_x) { }; };" + "f()({})"); } public void testBooleanType() throws Exception { testTypes("/**@type {boolean} */var x = 1 < 2;"); } public void testBooleanReduction1() throws Exception { testTypes("/**@type {string} */var x; x = null || \"a\";"); } public void testBooleanReduction2() throws Exception { // It's important for the type system to recognize that in no case // can the boolean expression evaluate to a boolean value. testTypes("/** @param {string} s\n @return {string} */" + "(function(s) { return ((s == 'a') && s) || 'b'; })"); } public void testBooleanReduction3() throws Exception { testTypes("/** @param {string} s\n @return {string?} */" + "(function(s) { return s && null && 3; })"); } public void testBooleanReduction4() throws Exception { testTypes("/** @param {Object} x\n @return {Object} */" + "(function(x) { return null || x || null ; })"); } public void testBooleanReduction5() throws Exception { testTypes("/**\n" + "* @param {Array|string} x\n" + "* @return {string?}\n" + "*/\n" + "var f = function(x) {\n" + "if (!x || typeof x == 'string') {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testBooleanReduction6() throws Exception { testTypes("/**\n" + "* @param {Array|string|null} x\n" + "* @return {string?}\n" + "*/\n" + "var f = function(x) {\n" + "if (!(x && typeof x != 'string')) {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testBooleanReduction7() throws Exception { testTypes("/** @constructor */var T = function() {};\n" + "/**\n" + "* @param {Array|T} x\n" + "* @return {null|undefined}\n" + "*/\n" + "var f = function(x) {\n" + "if (!x) {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testNullAnd() throws Exception { testTypes("/** @type null */var x;\n" + "/** @type number */var r = x && x;", "initializing variable\n" + "found : null\n" + "required: number"); } public void testNullOr() throws Exception { testTypes("/** @type null */var x;\n" + "/** @type number */var r = x || x;", "initializing variable\n" + "found : null\n" + "required: number"); } public void testBooleanPreservation1() throws Exception { testTypes("/**@type {string} */var x = \"a\";" + "x = ((x == \"a\") && x) || x == \"b\";", "assignment\n" + "found : (boolean|string)\n" + "required: string"); } public void testBooleanPreservation2() throws Exception { testTypes("/**@type {string} */var x = \"a\"; x = (x == \"a\") || x;", "assignment\n" + "found : (boolean|string)\n" + "required: string"); } public void testBooleanPreservation3() throws Exception { testTypes("/** @param {Function?} x\n @return {boolean?} */" + "function f(x) { return x && x == \"a\"; }", "condition always evaluates to false\n" + "left : Function\n" + "right: string"); } public void testBooleanPreservation4() throws Exception { testTypes("/** @param {Function?|boolean} x\n @return {boolean} */" + "function f(x) { return x && x == \"a\"; }", "inconsistent return type\n" + "found : (boolean|null|undefined)\n" + "required: boolean"); } public void testTypeOfReduction1() throws Exception { testTypes("/** @param {string|number} x\n @return {string} */ " + "function f(x) { return typeof x == 'number' ? String(x) : x; }"); } public void testTypeOfReduction2() throws Exception { testTypes("/** @param {string|number} x\n @return {string} */ " + "function f(x) { return typeof x != 'string' ? String(x) : x; }"); } public void testTypeOfReduction3() throws Exception { testTypes("/** @param {number|null} x\n @return {number} */ " + "function f(x) { return typeof x == 'object' ? 1 : x; }"); } public void testTypeOfReduction4() throws Exception { testTypes("/** @param {Object|undefined} x\n @return {Object} */ " + "function f(x) { return typeof x == 'undefined' ? {} : x; }"); } public void testTypeOfReduction5() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {!E|number} x\n @return {string} */ " + "function f(x) { return typeof x != 'number' ? x : 'a'; }"); } public void testTypeOfReduction6() throws Exception { testTypes("/** @param {number|string} x\n@return {string} */\n" + "function f(x) {\n" + "return typeof x == 'string' && x.length == 3 ? x : 'a';\n" + "}"); } public void testTypeOfReduction7() throws Exception { testTypes("/** @return {string} */var f = function(x) { " + "return typeof x == 'number' ? x : 'a'; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testTypeOfReduction8() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {number|string} x\n@return {string} */\n" + "function f(x) {\n" + "return goog.isString(x) && x.length == 3 ? x : 'a';\n" + "}", null); } public void testTypeOfReduction9() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {!Array|string} x\n@return {string} */\n" + "function f(x) {\n" + "return goog.isArray(x) ? 'a' : x;\n" + "}", null); } public void testTypeOfReduction10() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {Array|string} x\n@return {Array} */\n" + "function f(x) {\n" + "return goog.isArray(x) ? x : [];\n" + "}", null); } public void testTypeOfReduction11() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {Array|string} x\n@return {Array} */\n" + "function f(x) {\n" + "return goog.isObject(x) ? x : [];\n" + "}", null); } public void testTypeOfReduction12() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {E|Array} x\n @return {Array} */ " + "function f(x) { return typeof x == 'object' ? x : []; }"); } public void testTypeOfReduction13() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {E|Array} x\n@return {Array} */ " + "function f(x) { return goog.isObject(x) ? x : []; }", null); } public void testTypeOfReduction14() throws Exception { // Don't do type inference on GETELEMs. testClosureTypes( CLOSURE_DEFS + "function f(x) { " + " return goog.isString(arguments[0]) ? arguments[0] : 0;" + "}", null); } public void testTypeOfReduction15() throws Exception { // Don't do type inference on GETELEMs. testClosureTypes( CLOSURE_DEFS + "function f(x) { " + " return typeof arguments[0] == 'string' ? arguments[0] : 0;" + "}", null); } public void testQualifiedNameReduction1() throws Exception { testTypes("var x = {}; /** @type {string?} */ x.a = 'a';\n" + "/** @return {string} */ var f = function() {\n" + "return x.a ? x.a : 'a'; }"); } public void testQualifiedNameReduction2() throws Exception { testTypes("/** @param {string?} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return this.a ? this.a : 'a'; }"); } public void testQualifiedNameReduction3() throws Exception { testTypes("/** @param {string|Array} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return typeof this.a == 'string' ? this.a : 'a'; }"); } public void testQualifiedNameReduction4() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {string|Array} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return goog.isString(this.a) ? this.a : 'a'; }", null); } public void testInstanceOfReduction1() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {T|string} x\n@return {T} */\n" + "var f = function(x) {\n" + "if (x instanceof T) { return x; } else { return new T(); }\n" + "};"); } public void testInstanceOfReduction2() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {!T|string} x\n@return {string} */\n" + "var f = function(x) {\n" + "if (x instanceof T) { return ''; } else { return x; }\n" + "};"); } public void testPropertyInferredPropagation() throws Exception { testTypes("/** @return {Object} */function f() { return {}; }\n" + "function g() { var x = f(); if (x.p) x.a = 'a'; else x.a = 'b'; }\n" + "function h() { var x = f(); x.a = false; }"); } public void testPropertyInference1() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference2() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "F.prototype.baz = function() { this.x_ = null; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference3() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "F.prototype.baz = function() { this.x_ = 3; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : (boolean|number)\n" + "required: string"); } public void testPropertyInference4() throws Exception { testTypes( "/** @constructor */ function F() { }" + "F.prototype.x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testPropertyInference5() throws Exception { testTypes( "/** @constructor */ function F() { }" + "F.prototype.baz = function() { this.x_ = 3; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };"); } public void testPropertyInference6() throws Exception { testTypes( "/** @constructor */ function F() { }" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };"); } public void testPropertyInference7() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference8() throws Exception { testTypes( "/** @constructor */ function F() { " + " /** @type {string} */ this.x_ = 'x';" + "}" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };", "assignment to property x_ of F\n" + "found : number\n" + "required: string"); } public void testNoPersistentTypeInferenceForObjectProperties() throws Exception { testTypes("/** @param {Object} o\n@param {string} x */\n" + "function s1(o,x) { o.x = x; }\n" + "/** @param {Object} o\n@return {string} */\n" + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + "/** @param {Object} o\n@param {number} x */\n" + "function s2(o,x) { o.x = x; }\n" + "/** @param {Object} o\n@return {number} */\n" + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); } public void testNoPersistentTypeInferenceForFunctionProperties() throws Exception { testTypes("/** @param {Function} o\n@param {string} x */\n" + "function s1(o,x) { o.x = x; }\n" + "/** @param {Function} o\n@return {string} */\n" + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + "/** @param {Function} o\n@param {number} x */\n" + "function s2(o,x) { o.x = x; }\n" + "/** @param {Function} o\n@return {number} */\n" + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); } public void testObjectPropertyTypeInferredInLocalScope1() throws Exception { testTypes("/** @param {!Object} o\n@return {string} */\n" + "function f(o) { o.x = 1; return o.x; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testObjectPropertyTypeInferredInLocalScope2() throws Exception { testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + "function f(o, x) { o.x = 'a';\nif (x) {o.x = x;}\nreturn o.x; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testObjectPropertyTypeInferredInLocalScope3() throws Exception { testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + "function f(o, x) { if (x) {o.x = x;} else {o.x = 'a';}\nreturn o.x; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty1() throws Exception { testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + "/** @type {number} */ T.prototype.x = 0;", "assignment to property x of T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty2() throws Exception { testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + "/** @type {number} */ T.prototype.x;", "assignment to property x of T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty3() throws Exception { testTypes("/** @type {Object} */ var n = {};\n" + "/** @constructor */ n.T = function() { this.x = ''; };\n" + "/** @type {number} */ n.T.prototype.x = 0;", "assignment to property x of n.T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty4() throws Exception { testTypes("var n = {};\n" + "/** @constructor */ n.T = function() { this.x = ''; };\n" + "/** @type {number} */ n.T.prototype.x = 0;", "assignment to property x of n.T\n" + "found : string\n" + "required: number"); } public void testPropertyUsedBeforeDefinition1() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @return {string} */" + "T.prototype.f = function() { return this.g(); };\n" + "/** @return {number} */ T.prototype.g = function() { return 1; };\n", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testPropertyUsedBeforeDefinition2() throws Exception { testTypes("var n = {};\n" + "/** @constructor */ n.T = function() {};\n" + "/** @return {string} */" + "n.T.prototype.f = function() { return this.g(); };\n" + "/** @return {number} */ n.T.prototype.g = function() { return 1; };\n", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testAdd1() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 'abc'+foo();}"); } public void testAdd2() throws Exception { testTypes("/**@return {void}*/function foo(){var a = foo()+4;}"); } public void testAdd3() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {string} */ var b = 'b';" + "/** @type {string} */ var c = a + b;"); } public void testAdd4() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {string} */ var b = 'b';" + "/** @type {string} */ var c = a + b;"); } public void testAdd5() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {number} */ var b = 5;" + "/** @type {string} */ var c = a + b;"); } public void testAdd6() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {number} */ var b = 5;" + "/** @type {number} */ var c = a + b;"); } public void testAdd7() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {string} */ var b = 'b';" + "/** @type {number} */ var c = a + b;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd8() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {number} */ var b = 5;" + "/** @type {number} */ var c = a + b;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd9() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {number} */ var b = 5;" + "/** @type {string} */ var c = a + b;", "initializing variable\n" + "found : number\n" + "required: string"); } public void testAdd10() throws Exception { // d.e.f will have unknown type. testTypes( suppressMissingProperty("e", "f") + "/** @type {number} */ var a = 5;" + "/** @type {string} */ var c = a + d.e.f;"); } public void testAdd11() throws Exception { // d.e.f will have unknown type. testTypes( suppressMissingProperty("e", "f") + "/** @type {number} */ var a = 5;" + "/** @type {number} */ var c = a + d.e.f;"); } public void testAdd12() throws Exception { testTypes("/** @return {(number,string)} */ function a() { return 5; }" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a() + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd13() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @return {(number,string)} */ function b() { return 5; }" + "/** @type {boolean} */ var c = a + b();", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd14() throws Exception { testTypes("/** @type {(null,string)} */ var a = null;" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd15() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @return {(number,string)} */ function b() { return 5; }" + "/** @type {boolean} */ var c = a + b();", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd16() throws Exception { testTypes("/** @type {(undefined,string)} */ var a = undefined;" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd17() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {(undefined,string)} */ var b = undefined;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd18() throws Exception { testTypes("function f() {};" + "/** @type {string} */ var a = 'a';" + "/** @type {number} */ var c = a + f();", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd19() throws Exception { testTypes("/** @param {number} opt_x\n@param {number} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testAdd20() throws Exception { testTypes("/** @param {!Number} opt_x\n@param {!Number} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testAdd21() throws Exception { testTypes("/** @param {Number|Boolean} opt_x\n" + "@param {number|boolean} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testNumericComparison1() throws Exception { testTypes("/**@param {number} a*/ function f(a) {return a < 3;}"); } public void testNumericComparison2() throws Exception { testTypes("/**@param {!Object} a*/ function f(a) {return a < 3;}", "left side of numeric comparison\n" + "found : Object\n" + "required: number"); } public void testNumericComparison3() throws Exception { testTypes("/**@param {string} a*/ function f(a) {return a < 3;}"); } public void testNumericComparison4() throws Exception { testTypes("/**@param {(number,undefined)} a*/ " + "function f(a) {return a < 3;}"); } public void testNumericComparison5() throws Exception { testTypes("/**@param {*} a*/ function f(a) {return a < 3;}", "left side of numeric comparison\n" + "found : *\n" + "required: number"); } public void testNumericComparison6() throws Exception { testTypes("/**@return {void}*/ function foo() { if (3 >= foo()) return; }", "right side of numeric comparison\n" + "found : undefined\n" + "required: number"); } public void testStringComparison1() throws Exception { testTypes("/**@param {string} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison2() throws Exception { testTypes("/**@param {Object} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison3() throws Exception { testTypes("/**@param {number} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison4() throws Exception { testTypes("/**@param {string|undefined} a*/ " + "function f(a) {return a < 'x';}"); } public void testStringComparison5() throws Exception { testTypes("/**@param {*} a*/ " + "function f(a) {return a < 'x';}"); } public void testStringComparison6() throws Exception { testTypes("/**@return {void} */ " + "function foo() { if ('a' >= foo()) return; }", "right side of comparison\n" + "found : undefined\n" + "required: string"); } public void testValueOfComparison1() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.valueOf = function() { return 1; };" + "/**@param {!O} a\n@param {!O} b*/ function f(a,b) { return a < b; }"); } public void testValueOfComparison2() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.valueOf = function() { return 1; };" + "/**@param {!O} a\n@param {number} b*/" + "function f(a,b) { return a < b; }"); } public void testValueOfComparison3() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.toString = function() { return 'o'; };" + "/**@param {!O} a\n@param {string} b*/" + "function f(a,b) { return a < b; }"); } public void testGenericRelationalExpression() throws Exception { testTypes("/**@param {*} a\n@param {*} b*/ " + "function f(a,b) {return a < b;}"); } public void testInstanceof1() throws Exception { testTypes("function foo(){" + "if (bar instanceof 3)return;}", "instanceof requires an object\n" + "found : number\n" + "required: Object"); } public void testInstanceof2() throws Exception { testTypes("/**@return {void}*/function foo(){" + "if (foo() instanceof Object)return;}", "deterministic instanceof yields false\n" + "found : undefined\n" + "required: NoObject"); } public void testInstanceof3() throws Exception { testTypes("/**@return {*} */function foo(){" + "if (foo() instanceof Object)return;}"); } public void testInstanceof4() throws Exception { testTypes("/**@return {(Object|number)} */function foo(){" + "if (foo() instanceof Object)return 3;}"); } public void testInstanceof5() throws Exception { // No warning for unknown types. testTypes("/** @return {?} */ function foo(){" + "if (foo() instanceof Object)return;}"); } public void testInstanceof6() throws Exception { testTypes("/**@return {(Array|number)} */function foo(){" + "if (foo() instanceof Object)return 3;}"); } public void testInstanceOfReduction3() throws Exception { testTypes( "/** \n" + " * @param {Object} x \n" + " * @param {Function} y \n" + " * @return {boolean} \n" + " */\n" + "var f = function(x, y) {\n" + " return x instanceof y;\n" + "};"); } public void testScoping1() throws Exception { testTypes( "/**@param {string} a*/function foo(a){" + " /**@param {Array|string} a*/function bar(a){" + " if (a instanceof Array)return;" + " }" + "}"); } public void testScoping2() throws Exception { testTypes( "/** @type number */ var a;" + "function Foo() {" + " /** @type string */ var a;" + "}"); } public void testScoping3() throws Exception { testTypes("\n\n/** @type{Number}*/var b;\n/** @type{!String} */var b;", "variable b redefined with type String, original " + "definition at [testcode]:3 with type (Number|null|undefined)"); } public void testScoping4() throws Exception { testTypes("/** @type{Number}*/var b; if (true) /** @type{!String} */var b;", "variable b redefined with type String, original " + "definition at [testcode]:1 with type (Number|null|undefined)"); } public void testScoping5() throws Exception { // multiple definitions are not checked by the type checker but by a // subsequent pass testTypes("if (true) var b; var b;"); } public void testScoping6() throws Exception { // multiple definitions are not checked by the type checker but by a // subsequent pass testTypes("if (true) var b; if (true) var b;"); } public void testScoping7() throws Exception { testTypes("/** @constructor */function A() {" + " /** @type !A */this.a = null;" + "}", "assignment to property a of A\n" + "found : null\n" + "required: A"); } public void testScoping8() throws Exception { testTypes("/** @constructor */function A() {}" + "/** @constructor */function B() {" + " /** @type !A */this.a = null;" + "}", "assignment to property a of B\n" + "found : null\n" + "required: A"); } public void testScoping9() throws Exception { testTypes("/** @constructor */function B() {" + " /** @type !A */this.a = null;" + "}" + "/** @constructor */function A() {}", "assignment to property a of B\n" + "found : null\n" + "required: A"); } public void testScoping10() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = function b(){};"); // a declared, b is not assertTrue(p.scope.isDeclared("a", false)); assertFalse(p.scope.isDeclared("b", false)); // checking that a has the correct assigned type assertEquals("function (): undefined", p.scope.getVar("a").getType().toString()); } public void testScoping11() throws Exception { // named anonymous functions create a binding in their body only // the return is wrong but the assignment is OK since the type of b is ? testTypes( "/** @return {number} */var a = function b(){ return b };", "inconsistent return type\n" + "found : function (): number\n" + "required: number"); } public void testFunctionArguments1() throws Exception { testFunctionType( "/** @param {number} a\n@return {string} */" + "function f(a) {}", "function (number): string"); } public void testFunctionArguments2() throws Exception { testFunctionType( "/** @param {number} opt_a\n@return {string} */" + "function f(opt_a) {}", "function (number=): string"); } public void testFunctionArguments3() throws Exception { testFunctionType( "/** @param {number} b\n@return {string} */" + "function f(a,b) {}", "function (?, number): string"); } public void testFunctionArguments4() throws Exception { testFunctionType( "/** @param {number} opt_a\n@return {string} */" + "function f(a,opt_a) {}", "function (?, number=): string"); } public void testFunctionArguments5() throws Exception { testTypes( "function a(opt_a,a) {}", "optional arguments must be at the end"); } public void testFunctionArguments6() throws Exception { testTypes( "function a(var_args,a) {}", "variable length argument must be last"); } public void testFunctionArguments7() throws Exception { testTypes( "/** @param {number} opt_a\n@return {string} */" + "function a(a,opt_a,var_args) {}"); } public void testFunctionArguments8() throws Exception { testTypes( "function a(a,opt_a,var_args,b) {}", "variable length argument must be last"); } public void testFunctionArguments9() throws Exception { // testing that only one error is reported testTypes( "function a(a,opt_a,var_args,b,c) {}", "variable length argument must be last"); } public void testFunctionArguments10() throws Exception { // testing that only one error is reported testTypes( "function a(a,opt_a,b,c) {}", "optional arguments must be at the end"); } public void testFunctionArguments11() throws Exception { testTypes( "function a(a,opt_a,b,c,var_args,d) {}", "optional arguments must be at the end"); } public void testFunctionArguments12() throws Exception { testTypes("/** @param foo {String} */function bar(baz){}", "parameter foo does not appear in bar's parameter list"); } public void testFunctionArguments13() throws Exception { // verifying that the argument type have non-inferable types testTypes( "/** @return {boolean} */ function u() { return true; }" + "/** @param {boolean} b\n@return {?boolean} */" + "function f(b) { if (u()) { b = null; } return b; }", "assignment\n" + "found : null\n" + "required: boolean"); } public void testFunctionArguments14() throws Exception { testTypes( "/**\n" + " * @param {string} x\n" + " * @param {number} opt_y\n" + " * @param {boolean} var_args\n" + " */ function f(x, opt_y, var_args) {}" + "f('3'); f('3', 2); f('3', 2, true); f('3', 2, true, false);"); } public void testFunctionArguments15() throws Exception { testTypes( "/** @param {?function(*)} f */" + "function g(f) { f(1, 2); }", "Function f: called with 2 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testPrintFunctionName1() throws Exception { // Ensures that the function name is pretty. testTypes( "var goog = {}; goog.run = function(f) {};" + "goog.run();", "Function goog.run: called with 0 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testPrintFunctionName2() throws Exception { testTypes( "/** @constructor */ var Foo = function() {}; " + "Foo.prototype.run = function(f) {};" + "(new Foo).run();", "Function Foo.prototype.run: called with 0 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testFunctionInference1() throws Exception { testFunctionType( "function f(a) {}", "function (?): undefined"); } public void testFunctionInference2() throws Exception { testFunctionType( "function f(a,b) {}", "function (?, ?): undefined"); } public void testFunctionInference3() throws Exception { testFunctionType( "function f(var_args) {}", "function (...[?]): undefined"); } public void testFunctionInference4() throws Exception { testFunctionType( "function f(a,b,c,var_args) {}", "function (?, ?, ?, ...[?]): undefined"); } public void testFunctionInference5() throws Exception { testFunctionType( "/** @this Date\n@return {string} */function f(a) {}", "function (this:Date, ?): string"); } public void testFunctionInference6() throws Exception { testFunctionType( "/** @this Date\n@return {string} */function f(opt_a) {}", "function (this:Date, ?=): string"); } public void testFunctionInference7() throws Exception { testFunctionType( "/** @this Date */function f(a,b,c,var_args) {}", "function (this:Date, ?, ?, ?, ...[?]): undefined"); } public void testFunctionInference8() throws Exception { testFunctionType( "function f() {}", "function (): undefined"); } public void testFunctionInference9() throws Exception { testFunctionType( "var f = function() {};", "function (): undefined"); } public void testFunctionInference10() throws Exception { testFunctionType( "/** @this Date\n@param {boolean} b\n@return {string} */" + "var f = function(a,b) {};", "function (this:Date, ?, boolean): string"); } public void testFunctionInference11() throws Exception { testFunctionType( "var goog = {};" + "/** @return {number}*/goog.f = function(){};", "goog.f", "function (): number"); } public void testFunctionInference12() throws Exception { testFunctionType( "var goog = {};" + "goog.f = function(){};", "goog.f", "function (): undefined"); } public void testFunctionInference13() throws Exception { testFunctionType( "var goog = {};" + "/** @constructor */ goog.Foo = function(){};" + "/** @param {!goog.Foo} f */function eatFoo(f){};", "eatFoo", "function (goog.Foo): undefined"); } public void testFunctionInference14() throws Exception { testFunctionType( "var goog = {};" + "/** @constructor */ goog.Foo = function(){};" + "/** @return {!goog.Foo} */function eatFoo(){ return new goog.Foo; };", "eatFoo", "function (): goog.Foo"); } public void testFunctionInference15() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "f.prototype.foo = function(){};", "f.prototype.foo", "function (this:f): undefined"); } public void testFunctionInference16() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "f.prototype.foo = function(){};", "(new f).foo", "function (this:f): undefined"); } public void testFunctionInference17() throws Exception { testFunctionType( "/** @constructor */ function f() {}" + "function abstractMethod() {}" + "/** @param {number} x */ f.prototype.foo = abstractMethod;", "(new f).foo", "function (this:f, number): ?"); } public void testFunctionInference18() throws Exception { testFunctionType( "var goog = {};" + "/** @this {Date} */ goog.eatWithDate;", "goog.eatWithDate", "function (this:Date): ?"); } public void testFunctionInference19() throws Exception { testFunctionType( "/** @param {string} x */ var f;", "f", "function (string): ?"); } public void testFunctionInference20() throws Exception { testFunctionType( "/** @this {Date} */ var f;", "f", "function (this:Date): ?"); } public void testInnerFunction1() throws Exception { testTypes( "function f() {" + " /** @type {number} */ var x = 3;\n" + " function g() { x = null; }" + " return x;" + "}", "assignment\n" + "found : null\n" + "required: number"); } public void testInnerFunction2() throws Exception { testTypes( "/** @return {number} */\n" + "function f() {" + " var x = null;\n" + " function g() { x = 3; }" + " g();" + " return x;" + "}", "inconsistent return type\n" + "found : (null|number)\n" + "required: number"); } public void testInnerFunction3() throws Exception { testTypes( "var x = null;" + "/** @return {number} */\n" + "function f() {" + " x = 3;\n" + " /** @return {number} */\n" + " function g() { x = true; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testInnerFunction4() throws Exception { testTypes( "var x = null;" + "/** @return {number} */\n" + "function f() {" + " x = '3';\n" + " /** @return {number} */\n" + " function g() { x = 3; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testInnerFunction5() throws Exception { testTypes( "/** @return {number} */\n" + "function f() {" + " var x = 3;\n" + " /** @return {number} */" + " function g() { var x = 3;x = true; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testInnerFunction6() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " var x = 0 || function() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction7() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " /** @type {number|function()} */" + " var x = 0 || function() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction8() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " function x() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction9() throws Exception { testTypes( "function f() {" + " var x = 3;\n" + " function g() { x = null; };\n" + " function h() { return x == null; }" + " return h();" + "}"); } public void testAbstractMethodHandling1() throws Exception { testTypes( "/** @type {Function} */ var abstractFn = function() {};" + "abstractFn(1);"); } public void testAbstractMethodHandling2() throws Exception { testTypes( "var abstractFn = function() {};" + "abstractFn(1);", "Function abstractFn: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testAbstractMethodHandling3() throws Exception { testTypes( "var goog = {};" + "/** @type {Function} */ goog.abstractFn = function() {};" + "goog.abstractFn(1);"); } public void testAbstractMethodHandling4() throws Exception { testTypes( "var goog = {};" + "goog.abstractFn = function() {};" + "goog.abstractFn(1);", "Function goog.abstractFn: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testAbstractMethodHandling5() throws Exception { testTypes( "/** @type {!Function} */ var abstractFn = function() {};" + "/** @param {number} x */ var f = abstractFn;" + "f('x');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testAbstractMethodHandling6() throws Exception { testTypes( "var goog = {};" + "/** @type {Function} */ goog.abstractFn = function() {};" + "/** @param {number} x */ goog.f = abstractFn;" + "goog.f('x');", "actual parameter 1 of goog.f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testMethodInference1() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @return {number} */ F.prototype.foo = function() { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference2() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.F = function() {};" + "/** @return {number} */ goog.F.prototype.foo = " + " function() { return 3; };" + "/** @constructor \n * @extends {goog.F} */ " + "goog.G = function() {};" + "/** @override */ goog.G.prototype.foo = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference3() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {boolean} x \n * @return {number} */ " + "F.prototype.foo = function(x) { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(x) { return x; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference4() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {boolean} x \n * @return {number} */ " + "F.prototype.foo = function(x) { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(y) { return y; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference5() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {number} x \n * @return {string} */ " + "F.prototype.foo = function(x) { return 'x'; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @type {number} */ G.prototype.num = 3;" + "/** @override */ " + "G.prototype.foo = function(y) { return this.num + y; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testMethodInference6() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {number} x */ F.prototype.foo = function(x) { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function() { };" + "(new G()).foo(1);"); } public void testMethodInference7() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function(x, y) { };", "mismatch of the foo property type and the type of the property " + "it overrides from superclass F\n" + "original: function (this:F): undefined\n" + "override: function (this:G, ?, ?): undefined"); } public void testMethodInference8() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(opt_b, var_args) { };" + "(new G()).foo(1, 2, 3);"); } public void testMethodInference9() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(var_args, opt_b) { };", "variable length argument must be last"); } public void testStaticMethodDeclaration1() throws Exception { testTypes( "/** @constructor */ function F() { F.foo(true); }" + "/** @param {number} x */ F.foo = function(x) {};", "actual parameter 1 of F.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testStaticMethodDeclaration2() throws Exception { testTypes( "var goog = goog || {}; function f() { goog.foo(true); }" + "/** @param {number} x */ goog.foo = function(x) {};", "actual parameter 1 of goog.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testStaticMethodDeclaration3() throws Exception { testTypes( "var goog = goog || {}; function f() { goog.foo(true); }" + "goog.foo = function() {};", "Function goog.foo: called with 1 argument(s). Function requires " + "at least 0 argument(s) and no more than 0 argument(s)."); } public void testDuplicateStaticMethodDecl1() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {number} x */ goog.foo = function(x) {};" + "/** @param {number} x */ goog.foo = function(x) {};", "variable goog.foo redefined with type function (number): undefined, " + "original definition at [testcode]:1 " + "with type function (number): undefined"); } public void testDuplicateStaticMethodDecl2() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {number} x */ goog.foo = function(x) {};" + "/** @param {number} x \n * @suppress {duplicate} */ " + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl3() throws Exception { testTypes( "var goog = goog || {};" + "goog.foo = function(x) {};" + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl4() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Function} */ goog.foo = function(x) {};" + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl5() throws Exception { testTypes( "var goog = goog || {};" + "goog.foo = function(x) {};" + "/** @return {undefined} */ goog.foo = function(x) {};", "variable goog.foo redefined with type function (?): undefined, " + "original definition at [testcode]:1 with type " + "function (?): undefined"); } public void testDuplicateStaticPropertyDecl1() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Foo} */ goog.foo;" + "/** @type {Foo} */ goog.foo;" + "/** @constructor */ function Foo() {}"); } public void testDuplicateStaticPropertyDecl2() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Foo} */ goog.foo;" + "/** @type {Foo} \n * @suppress {duplicate} */ goog.foo;" + "/** @constructor */ function Foo() {}"); } public void testDuplicateStaticPropertyDecl3() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string} */ goog.foo;" + "/** @constructor */ function Foo() {}", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo"); } public void testDuplicateStaticPropertyDecl4() throws Exception { testClosureTypesMultipleWarnings( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string} */ goog.foo = 'x';" + "/** @constructor */ function Foo() {}", Lists.newArrayList( "assignment to property foo of goog\n" + "found : string\n" + "required: Foo", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo")); } public void testDuplicateStaticPropertyDecl5() throws Exception { testClosureTypesMultipleWarnings( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';" + "/** @constructor */ function Foo() {}", Lists.newArrayList( "assignment to property foo of goog\n" + "found : string\n" + "required: Foo", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo")); } public void testDuplicateStaticPropertyDecl6() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {string} */ goog.foo = 'y';" + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';"); } public void testDuplicateStaticPropertyDecl7() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {string} x */ goog.foo;" + "/** @type {function(string)} */ goog.foo;"); } public void testDuplicateStaticPropertyDecl8() throws Exception { testTypes( "var goog = goog || {};" + "/** @return {EventCopy} */ goog.foo;" + "/** @constructor */ function EventCopy() {}" + "/** @return {EventCopy} */ goog.foo;"); } public void testDuplicateStaticPropertyDecl9() throws Exception { testTypes( "var goog = goog || {};" + "/** @return {EventCopy} */ goog.foo;" + "/** @return {EventCopy} */ goog.foo;" + "/** @constructor */ function EventCopy() {}"); } public void testDuplicateLocalVarDecl() throws Exception { testClosureTypesMultipleWarnings( "/** @param {number} x */\n" + "function f(x) { /** @type {string} */ var x = ''; }", Lists.newArrayList( "variable x redefined with type string, original definition" + " at [testcode]:2 with type number", "initializing variable\n" + "found : string\n" + "required: number")); } public void testStubFunctionDeclaration1() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "/** @param {number} x \n * @param {string} y \n" + " * @return {number} */ f.prototype.foo;", "(new f).foo", "function (this:f, number, string): number"); } public void testStubFunctionDeclaration2() throws Exception { testExternFunctionType( // externs "/** @constructor */ function f() {};" + "/** @constructor \n * @extends {f} */ f.subclass;", "f.subclass", "function (new:f.subclass): ?"); } public void testStubFunctionDeclaration3() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "/** @return {undefined} */ f.foo;", "f.foo", "function (): undefined"); } public void testStubFunctionDeclaration4() throws Exception { testFunctionType( "/** @constructor */ function f() { " + " /** @return {number} */ this.foo;" + "}", "(new f).foo", "function (this:f): number"); } public void testStubFunctionDeclaration5() throws Exception { testFunctionType( "/** @constructor */ function f() { " + " /** @type {Function} */ this.foo;" + "}", "(new f).foo", createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) .toString()); } public void testStubFunctionDeclaration6() throws Exception { testFunctionType( "/** @constructor */ function f() {} " + "/** @type {Function} */ f.prototype.foo;", "(new f).foo", createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) .toString()); } public void testStubFunctionDeclaration7() throws Exception { testFunctionType( "/** @constructor */ function f() {} " + "/** @type {Function} */ f.prototype.foo = function() {};", "(new f).foo", createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) .toString()); } public void testStubFunctionDeclaration8() throws Exception { testFunctionType( "/** @type {Function} */ var f = function() {}; ", "f", createOptionalType(createNullableType(U2U_CONSTRUCTOR_TYPE)) .toString()); } public void testStubFunctionDeclaration9() throws Exception { testFunctionType( "/** @type {function():number} */ var f; ", "f", "function (): number"); } public void testStubFunctionDeclaration10() throws Exception { testFunctionType( "/** @type {function(number):number} */ var f = function(x) {};", "f", "function (number): number"); } public void testNestedFunctionInference1() throws Exception { String nestedAssignOfFooAndBar = "/** @constructor */ function f() {};" + "f.prototype.foo = f.prototype.bar = function(){};"; testFunctionType(nestedAssignOfFooAndBar, "(new f).bar", "function (this:f): undefined"); } /** * Tests the type of a function definition. The function defined by * {@code functionDef} should be named {@code "f"}. */ private void testFunctionType(String functionDef, String functionType) throws Exception { testFunctionType(functionDef, "f", functionType); } /** * Tests the type of a function definition. The function defined by * {@code functionDef} should be named {@code functionName}. */ private void testFunctionType(String functionDef, String functionName, String functionType) throws Exception { // using the variable initialization check to verify the function's type testTypes( functionDef + "/** @type number */var a=" + functionName + ";", "initializing variable\n" + "found : " + functionType + "\n" + "required: number"); } /** * Tests the type of a function definition in externs. * The function defined by {@code functionDef} should be * named {@code functionName}. */ private void testExternFunctionType(String functionDef, String functionName, String functionType) throws Exception { testTypes( functionDef, "/** @type number */var a=" + functionName + ";", "initializing variable\n" + "found : " + functionType + "\n" + "required: number", false); } public void testTypeRedefinition() throws Exception { testClosureTypesMultipleWarnings( "a={};/**@enum {string}*/ a.A = {ZOR:'b'};" + "/** @constructor */ a.A = function() {}", Lists.newArrayList( "variable a.A redefined with type function (new:a.A): undefined, " + "original definition at [testcode]:1 with type enum{a.A}", "assignment to property A of a\n" + "found : function (new:a.A): undefined\n" + "required: enum{a.A}")); } public void testIn1() throws Exception { testTypes("'foo' in Object"); } public void testIn2() throws Exception { testTypes("3 in Object"); } public void testIn3() throws Exception { testTypes("undefined in Object"); } public void testIn4() throws Exception { testTypes("Date in Object", "left side of 'in'\n" + "found : function (new:Date, ?=, ?=, ?=, ?=, ?=, ?=, ?=): string\n" + "required: string"); } public void testIn5() throws Exception { testTypes("'x' in null", "'in' requires an object\n" + "found : null\n" + "required: Object"); } public void testIn6() throws Exception { testTypes( "/** @param {number} x */" + "function g(x) {}" + "g(1 in {});", "actual parameter 1 of g does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testIn7() throws Exception { // Make sure we do inference in the 'in' expression. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " return g(x.foo) in {};" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } // TODO(nicksantos): change this to something that makes sense. // public void testComparison1() throws Exception { // testTypes("/**@type null */var a;" + // "/**@type !Date */var b;" + // "if (a==b) {}", // "condition always evaluates to false\n" + // "left : null\n" + // "right: Date"); // } public void testComparison2() throws Exception { testTypes("/**@type number*/var a;" + "/**@type !Date */var b;" + "if (a!==b) {}", "condition always evaluates to true\n" + "left : number\n" + "right: Date"); } public void testComparison3() throws Exception { // Since null == undefined in JavaScript, this code is reasonable. testTypes("/** @type {(Object,undefined)} */var a;" + "var b = a == null"); } public void testComparison4() throws Exception { testTypes("/** @type {(!Object,undefined)} */var a;" + "/** @type {!Object} */var b;" + "var c = a == b"); } public void testComparison5() throws Exception { testTypes("/** @type null */var a;" + "/** @type null */var b;" + "a == b", "condition always evaluates to true\n" + "left : null\n" + "right: null"); } public void testComparison6() throws Exception { testTypes("/** @type null */var a;" + "/** @type null */var b;" + "a != b", "condition always evaluates to false\n" + "left : null\n" + "right: null"); } public void testComparison7() throws Exception { testTypes("var a;" + "var b;" + "a == b", "condition always evaluates to true\n" + "left : undefined\n" + "right: undefined"); } public void testComparison8() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] == null || a[1] == undefined"); } public void testComparison9() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] == null", "condition always evaluates to true\n" + "left : undefined\n" + "right: null"); } public void testComparison10() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] === null"); } public void testEnumStaticMethod1() throws Exception { testTypes( "/** @enum */ var Foo = {AAA: 1};" + "/** @param {number} x */ Foo.method = function(x) {};" + "Foo.method(true);", "actual parameter 1 of Foo.method does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testEnumStaticMethod2() throws Exception { testTypes( "/** @enum */ var Foo = {AAA: 1};" + "/** @param {number} x */ Foo.method = function(x) {};" + "function f() { Foo.method(true); }", "actual parameter 1 of Foo.method does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testEnum1() throws Exception { testTypes("/**@enum*/var a={BB:1,CC:2};\n" + "/**@type {a}*/var d;d=a.BB;"); } public void testEnum2() throws Exception { testTypes("/**@enum*/var a={b:1}", "enum key b must be a syntactic constant"); } public void testEnum3() throws Exception { testTypes("/**@enum*/var a={BB:1,BB:2}", "variable a.BB redefined with type a., " + "original definition at [testcode]:1 with type a."); } public void testEnum4() throws Exception { testTypes("/**@enum*/var a={BB:'string'}", "assignment to property BB of enum{a}\n" + "found : string\n" + "required: number"); } public void testEnum5() throws Exception { testTypes("/**@enum {String}*/var a={BB:'string'}", "assignment to property BB of enum{a}\n" + "found : string\n" + "required: (String|null|undefined)"); } public void testEnum6() throws Exception { testTypes("/**@enum*/var a={BB:1,CC:2};\n/**@type {!Array}*/var d;d=a.BB;", "assignment\n" + "found : a.\n" + "required: Array"); } public void testEnum7() throws Exception { testTypes("/** @enum */var a={AA:1,BB:2,CC:3};" + "/** @type a */var b=a.D;", "element D does not exist on this enum"); } public void testEnum8() throws Exception { testClosureTypesMultipleWarnings("/** @enum */var a=8;", Lists.newArrayList( "enum initializer must be an object literal or an enum", "initializing variable\n" + "found : number\n" + "required: enum{a}")); } public void testEnum9() throws Exception { testClosureTypesMultipleWarnings( "var goog = {};" + "/** @enum */goog.a=8;", Lists.newArrayList( "assignment to property a of goog\n" + "found : number\n" + "required: enum{goog.a}", "enum initializer must be an object literal or an enum")); } public void testEnum10() throws Exception { testTypes( "/** @enum {number} */" + "goog.K = { A : 3 };"); } public void testEnum11() throws Exception { testTypes( "/** @enum {number} */" + "goog.K = { 502 : 3 };"); } public void testEnum12() throws Exception { testTypes( "/** @enum {number} */ var a = {};" + "/** @enum */ var b = a;"); } public void testEnum13() throws Exception { testTypes( "/** @enum {number} */ var a = {};" + "/** @enum {string} */ var b = a;", "incompatible enum element types\n" + "found : number\n" + "required: string"); } public void testEnum14() throws Exception { testTypes( "/** @enum {number} */ var a = {FOO:5};" + "/** @enum */ var b = a;" + "var c = b.FOO;"); } public void testEnum15() throws Exception { testTypes( "/** @enum {number} */ var a = {FOO:5};" + "/** @enum */ var b = a;" + "var c = b.BAR;", "element BAR does not exist on this enum"); } public void testEnum16() throws Exception { testTypes("var goog = {};" + "/**@enum*/goog .a={BB:1,BB:2}", "variable goog.a.BB redefined with type goog.a., " + "original definition at [testcode]:1 with type goog.a."); } public void testEnum17() throws Exception { testTypes("var goog = {};" + "/**@enum*/goog.a={BB:'string'}", "assignment to property BB of enum{goog.a}\n" + "found : string\n" + "required: number"); } public void testEnum18() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {!E} x\n@return {number} */\n" + "var f = function(x) { return x; };"); } public void testEnum19() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {number} x\n@return {!E} */\n" + "var f = function(x) { return x; };", "inconsistent return type\n" + "found : number\n" + "required: E."); } public void testEnum20() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2}; var x = []; x[E.A] = 0;"); } public void testEnum21() throws Exception { Node n = parseAndTypeCheck( "/** @enum {string} */ var E = {A : 'a', B : 'b'};\n" + "/** @param {!E} x\n@return {!E} */ function f(x) { return x; }"); Node nodeX = n.getLastChild().getLastChild().getLastChild().getLastChild(); JSType typeE = nodeX.getJSType(); assertFalse(typeE.isObject()); assertFalse(typeE.isNullable()); } public void testEnum22() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum23() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {string} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.\n" + "required: string"); } public void testEnum24() throws Exception { testTypes("/**@enum {Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.<(Object|null|undefined)>\n" + "required: Object"); } public void testEnum25() throws Exception { testTypes("/**@enum {!Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}"); } public void testEnum26() throws Exception { testTypes("var a = {}; /**@enum*/ a.B = {A: 1, B: 2};" + "/** @param {a.B} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum27() throws Exception { // x is unknown testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "function f(x) { return A == x; }"); } public void testEnum28() throws Exception { // x is unknown testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "function f(x) { return A.B == x; }"); } public void testEnum29() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {number} */ function f() { return A; }", "inconsistent return type\n" + "found : enum{A}\n" + "required: number"); } public void testEnum30() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {number} */ function f() { return A.B; }"); } public void testEnum31() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {A} */ function f() { return A; }", "inconsistent return type\n" + "found : enum{A}\n" + "required: A."); } public void testEnum32() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {A} */ function f() { return A.B; }"); } public void testEnum34() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @param {number} x */ function f(x) { return x == A.B; }"); } public void testEnum35() throws Exception { testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + "/** @return {a.b} */ function f() { return a.b.C; }"); } public void testEnum36() throws Exception { testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + "/** @return {!a.b} */ function f() { return 1; }", "inconsistent return type\n" + "found : number\n" + "required: a.b."); } public void testEnum37() throws Exception { testTypes( "var goog = goog || {};" + "/** @enum {number} */ goog.a = {};" + "/** @enum */ var b = goog.a;"); } public void testEnum38() throws Exception { testTypes( "/** @enum {MyEnum} */ var MyEnum = {};" + "/** @param {MyEnum} x */ function f(x) {}", "Parse error. Cycle detected in inheritance chain " + "of type MyEnum"); } public void testEnum39() throws Exception { testTypes( "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + "/** @param {MyEnum} x \n * @return {number} */" + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testEnum40() throws Exception { testTypes( "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + "/** @param {number} x \n * @return {number} */" + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testAliasedEnum1() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {MyEnum} x */ function f(x) {} f(MyEnum.FOO);"); } public void testAliasedEnum2() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {YourEnum} x */ function f(x) {} f(MyEnum.FOO);"); } public void testAliasedEnum3() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {MyEnum} x */ function f(x) {} f(YourEnum.FOO);"); } public void testAliasedEnum4() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {YourEnum} x */ function f(x) {} f(YourEnum.FOO);"); } public void testAliasedEnum5() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {string} x */ function f(x) {} f(MyEnum.FOO);", "actual parameter 1 of f does not match formal parameter\n" + "found : YourEnum.\n" + "required: string"); } public void testBackwardsEnumUse1() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var MyEnum = {FOO: 'x'};"); } public void testBackwardsEnumUse2() throws Exception { testTypes( "/** @return {number} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var MyEnum = {FOO: 'x'};", "inconsistent return type\n" + "found : MyEnum.\n" + "required: number"); } public void testBackwardsEnumUse3() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;"); } public void testBackwardsEnumUse4() throws Exception { testTypes( "/** @return {number} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;", "inconsistent return type\n" + "found : YourEnum.\n" + "required: number"); } public void testBackwardsEnumUse5() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.BAR; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;", "element BAR does not exist on this enum"); } public void testBackwardsConstructor1() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = function(x) {};", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testBackwardsConstructor2() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var YourFoo = function(x) {};" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = YourFoo;", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testMinimalConstructorAnnotation() throws Exception { testTypes("/** @constructor */function Foo(){}"); } public void testGoodExtends1() throws Exception { // A minimal @extends example testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends2() throws Exception { testTypes("/** @constructor\n * @extends base */function derived() {}\n" + "/** @constructor */function base() {}\n"); } public void testGoodExtends3() throws Exception { testTypes("/** @constructor\n * @extends {Object} */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends4() throws Exception { // Ensure that @extends actually sets the base type of a constructor // correctly. Because this isn't part of the human-readable Function // definition, we need to crawl the prototype chain (eww). Node n = parseAndTypeCheck( "var goog = {};\n" + "/** @constructor */goog.Base = function(){};\n" + "/** @constructor\n" + " * @extends {goog.Base} */goog.Derived = function(){};\n"); Node subTypeName = n.getLastChild().getLastChild().getFirstChild(); assertEquals("goog.Derived", subTypeName.getQualifiedName()); FunctionType subCtorType = (FunctionType) subTypeName.getNext().getJSType(); assertEquals("goog.Derived", subCtorType.getInstanceType().toString()); JSType superType = subCtorType.getPrototype().getImplicitPrototype(); assertEquals("goog.Base", superType.toString()); } public void testGoodExtends5() throws Exception { // we allow for the extends annotation to be placed first testTypes("/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n"); } public void testGoodExtends6() throws Exception { testFunctionType( CLOSURE_DEFS + "/** @constructor */function base() {}\n" + "/** @return {number} */ " + " base.prototype.foo = function() { return 1; };\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "goog.inherits(derived, base);", "derived.superClass_.foo", "function (this:base): number"); } public void testGoodExtends7() throws Exception { testFunctionType( "Function.prototype.inherits = function(x) {};" + "/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "derived.inherits(base);", "(new derived).constructor", "function (new:derived, ...[?]): ?"); } public void testGoodExtends8() throws Exception { testTypes("/** @constructor \n @extends {Base} */ function Sub() {}" + "/** @return {number} */ function f() { return (new Sub()).foo; }" + "/** @constructor */ function Base() {}" + "/** @type {boolean} */ Base.prototype.foo = true;", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testGoodExtends9() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "Super.prototype.foo = function() {};" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "/** @override */ Sub.prototype.foo = function() {};"); } public void testGoodExtends10() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "/** @return {Super} */ function foo() { return new Sub(); }"); } public void testGoodExtends11() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "/** @param {boolean} x */ Super.prototype.foo = function(x) {};" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "(new Sub()).foo(0);", "actual parameter 1 of Super.prototype.foo " + "does not match formal parameter\n" + "found : number\n" + "required: boolean"); } public void testBadExtends1() throws Exception { testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {not_base} */function derived() {}\n", "Bad type annotation. Unknown type not_base"); } public void testBadExtends2() throws Exception { testTypes("/** @constructor */function base() {\n" + "/** @type {!Number}*/\n" + "this.baseMember = new Number(4);\n" + "}\n" + "/** @constructor\n" + " * @extends {base} */function derived() {}\n" + "/** @param {!String} x*/\n" + "function foo(x){ }\n" + "/** @type {!derived}*/var y;\n" + "foo(y.baseMember);\n", "actual parameter 1 of foo does not match formal parameter\n" + "found : Number\n" + "required: String"); } public void testBadExtends3() throws Exception { testTypes("/** @extends {Object} */function base() {}", "@extends used without @constructor or @interface for base"); } public void testLateExtends() throws Exception { testTypes( CLOSURE_DEFS + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.foo = function() {};\n" + "/** @constructor */function Bar() {}\n" + "goog.inherits(Foo, Bar);\n", "Missing @extends tag on type Foo"); } public void testSuperclassMatch() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);\n"); } public void testSuperclassMatchWithMixin() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor */ var Baz = function() {};\n" + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.mixin = function(y){};" + "Bar.inherits(Foo);\n" + "Bar.mixin(Baz);\n"); } public void testSuperclassMismatch1() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor \n @extends Object */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);\n", "Missing @extends tag on type Bar"); } public void testSuperclassMismatch2() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function(){};\n" + "/** @constructor */ var Bar = function(){};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);", "Missing @extends tag on type Bar"); } public void testSuperClassDefinedAfterSubClass1() throws Exception { testTypes( "/** @constructor \n * @extends {Base} */ function A() {}" + "/** @constructor \n * @extends {Base} */ function B() {}" + "/** @constructor */ function Base() {}" + "/** @param {A|B} x \n * @return {B|A} */ " + "function foo(x) { return x; }"); } public void testSuperClassDefinedAfterSubClass2() throws Exception { testTypes( "/** @constructor \n * @extends {Base} */ function A() {}" + "/** @constructor \n * @extends {Base} */ function B() {}" + "/** @param {A|B} x \n * @return {B|A} */ " + "function foo(x) { return x; }" + "/** @constructor */ function Base() {}"); } public void testDirectPrototypeAssignment1() throws Exception { testTypes( "/** @constructor */ function Base() {}" + "Base.prototype.foo = 3;" + "/** @constructor \n * @extends {Base} */ function A() {}" + "A.prototype = new Base();" + "/** @return {string} */ function foo() { return (new A).foo; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testDirectPrototypeAssignment2() throws Exception { // This ensures that we don't attach property 'foo' onto the Base // instance object. testTypes( "/** @constructor */ function Base() {}" + "/** @constructor \n * @extends {Base} */ function A() {}" + "A.prototype = new Base();" + "A.prototype.foo = 3;" + "/** @return {string} */ function foo() { return (new Base).foo; }"); } public void testGoodImplements1() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n * @constructor */function f() {}"); } public void testGoodImplements2() throws Exception { testTypes("/** @interface */function Base1() {}\n" + "/** @interface */function Base2() {}\n" + "/** @constructor\n" + " * @implements {Base1}\n" + " * @implements {Base2}\n" + " */ function derived() {}"); } public void testBadImplements1() throws Exception { testTypes("/** @interface */function Base1() {}\n" + "/** @interface */function Base2() {}\n" + "/** @constructor\n" + " * @implements {nonExistent}\n" + " * @implements {Base2}\n" + " */ function derived() {}", "Bad type annotation. Unknown type nonExistent"); } public void testBadImplements2() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n */function f() {}", "@implements used without @constructor for f"); } public void testBadImplements3() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n * @interface */function f() {}", "f cannot implement this type; an interface can only extend, " + "but not implement interfaces"); } public void testInterfaceExtends() throws Exception { testTypes("/** @interface */function A() {}\n" + "/** @interface \n * @extends {A} */function B() {}\n" + "/** @constructor\n" + " * @implements {B}\n" + " */ function derived() {}"); } public void testBadInterfaceExtends1() throws Exception { testTypes("/** @interface \n * @extends {nonExistent} */function A() {}", "Bad type annotation. Unknown type nonExistent"); } public void testBadInterfaceExtends2() throws Exception { testTypes("/** @constructor */function A() {}\n" + "/** @interface \n * @extends {A} */function B() {}", "B cannot extend this type; interfaces can only extend interfaces"); } public void testBadInterfaceExtends3() throws Exception { testTypes("/** @interface */function A() {}\n" + "/** @constructor \n * @extends {A} */function B() {}", "B cannot extend this type; constructors can only extend constructors"); } public void testBadInterfaceExtends4() throws Exception { // TODO(user): This should be detected as an error. Even if we enforce // that A cannot be used in the assignment, we should still detect the // inheritance chain as invalid. testTypes("/** @interface */function A() {}\n" + "/** @constructor */function B() {}\n" + "B.prototype = A;"); } public void testBadInterfaceExtends5() throws Exception { // TODO(user): This should be detected as an error. Even if we enforce // that A cannot be used in the assignment, we should still detect the // inheritance chain as invalid. testTypes("/** @constructor */function A() {}\n" + "/** @interface */function B() {}\n" + "B.prototype = A;"); } public void testBadImplementsAConstructor() throws Exception { testTypes("/** @constructor */function A() {}\n" + "/** @constructor \n * @implements {A} */function B() {}", "can only implement interfaces"); } public void testBadImplementsNonInterfaceType() throws Exception { testTypes("/** @constructor \n * @implements {Boolean} */function B() {}", "can only implement interfaces"); } public void testBadImplementsNonObjectType() throws Exception { testTypes("/** @constructor \n * @implements {string} */function S() {}", "can only implement interfaces"); } public void testInterfaceAssignment1() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {!I} */var i = t;"); } public void testInterfaceAssignment2() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor */var T = function() {};\n" + "var t = new T();\n" + "/** @type {!I} */var i = t;", "initializing variable\n" + "found : T\n" + "required: I"); } public void testInterfaceAssignment3() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {I|number} */var i = t;"); } public void testInterfaceAssignment4() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1|I2} */var i = t;"); } public void testInterfaceAssignment5() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1}\n@implements {I2}*/" + "var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1} */var i1 = t;\n" + "/** @type {I2} */var i2 = t;\n"); } public void testInterfaceAssignment6() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1} */var T = function() {};\n" + "/** @type {!I1} */var i1 = new T();\n" + "/** @type {!I2} */var i2 = i1;\n", "initializing variable\n" + "found : I1\n" + "required: I2"); } public void testInterfaceAssignment7() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface\n@extends {I1}*/var I2 = function() {};\n" + "/** @constructor\n@implements {I2}*/var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1} */var i1 = t;\n" + "/** @type {I2} */var i2 = t;\n" + "i1 = i2;\n"); } public void testInterfaceAssignment8() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @type {I} */var i;\n" + "/** @type {Object} */var o = i;"); } public void testInterfaceAssignment9() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @return {I?} */function f() { return null; }\n" + "/** @type {!I} */var i = f();\n", "initializing variable\n" + "found : (I|null|undefined)\n" + "required: I"); } public void testInterfaceAssignment10() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I2} */var T = function() {};\n" + "/** @return {!I1|!I2} */function f() { return new T(); }\n" + "/** @type {!I1} */var i1 = f();\n", "initializing variable\n" + "found : (I1|I2)\n" + "required: I1"); } public void testInterfaceAssignment11() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor */var T = function() {};\n" + "/** @return {!I1|!I2|!T} */function f() { return new T(); }\n" + "/** @type {!I1} */var i1 = f();\n", "initializing variable\n" + "found : (I1|I2|T)\n" + "required: I1"); } public void testInterfaceAssignment12() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements{I}*/var T1 = function() {};\n" + "/** @constructor\n@extends {T1}*/var T2 = function() {};\n" + "/** @return {I} */function f() { return new T2(); }"); } public void testInterfaceAssignment13() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I}*/var T = function() {};\n" + "/** @constructor */function Super() {};\n" + "/** @return {I} */Super.prototype.foo = " + "function() { return new T(); };\n" + "/** @constructor\n@extends {Super} */function Sub() {}\n" + "/** @override\n@return {T} */Sub.prototype.foo = " + "function() { return new T(); };\n"); } public void testGetprop1() throws Exception { testTypes("/** @return {void}*/function foo(){foo().bar;}", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testArrayAccess1() throws Exception { testTypes("var a = []; var b = a['hi'];"); } public void testArrayAccess2() throws Exception { testTypes("var a = []; var b = a[[1,2]];", "array access\n" + "found : Array\n" + "required: number"); } public void testArrayAccess3() throws Exception { testTypes("var bar = [];" + "/** @return {void} */function baz(){};" + "var foo = bar[baz()];", "array access\n" + "found : undefined\n" + "required: number"); } public void testArrayAccess4() throws Exception { testTypes("/**@return {!Array}*/function foo(){};var bar = foo()[foo()];", "array access\n" + "found : Array\n" + "required: number"); } public void testArrayAccess6() throws Exception { testTypes("var bar = null[1];", "only arrays or objects can be accessed\n" + "found : null\n" + "required: Object"); } public void testArrayAccess7() throws Exception { testTypes("var bar = void 0; bar[0];", "only arrays or objects can be accessed\n" + "found : undefined\n" + "required: Object"); } public void testArrayAccess8() throws Exception { // Verifies that we don't emit two warnings, because // the var has been dereferenced after the first one. testTypes("var bar = void 0; bar[0]; bar[1];", "only arrays or objects can be accessed\n" + "found : undefined\n" + "required: Object"); } public void testPropAccess() throws Exception { testTypes("/** @param {*} x */var f = function(x) {\n" + "var o = String(x);\n" + "if (typeof o['a'] != 'undefined') { return o['a']; }\n" + "return null;\n" + "};"); } public void testPropAccess2() throws Exception { testTypes("var bar = void 0; bar.baz;", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testPropAccess3() throws Exception { // Verifies that we don't emit two warnings, because // the var has been dereferenced after the first one. testTypes("var bar = void 0; bar.baz; bar.bax;", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testPropAccess4() throws Exception { testTypes("/** @param {*} x */ function f(x) { return x['hi']; }"); } public void testSwitchCase1() throws Exception { testTypes("/**@type number*/var a;" + "/**@type string*/var b;" + "switch(a){case b:;}", "case expression doesn't match switch\n" + "found : string\n" + "required: number"); } public void testSwitchCase2() throws Exception { testTypes("var a = null; switch (typeof a) { case 'foo': }"); } public void testVar1() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @type {(string,null)} */var a = null"); assertTypeEquals(createUnionType(STRING_TYPE, NULL_TYPE), p.scope.getVar("a").getType()); } public void testVar2() throws Exception { testTypes("/** @type {Function} */ var a = function(){}"); } public void testVar3() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = 3;"); assertTypeEquals(NUMBER_TYPE, p.scope.getVar("a").getType()); } public void testVar4() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var a = 3; a = 'string';"); assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), p.scope.getVar("a").getType()); } public void testVar5() throws Exception { testTypes("var goog = {};" + "/** @type string */goog.foo = 'hello';" + "/** @type number */var a = goog.foo;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testVar6() throws Exception { testTypes( "function f() {" + " return function() {" + " /** @type {!Date} */" + " var a = 7;" + " };" + "}", "initializing variable\n" + "found : number\n" + "required: Date"); } public void testVar7() throws Exception { testTypes("/** @type number */var a, b;", "declaration of multiple variables with shared type information"); } public void testVar8() throws Exception { testTypes("var a, b;"); } public void testVar9() throws Exception { testTypes("/** @enum */var a;", "enum initializer must be an object literal or an enum"); } public void testVar10() throws Exception { testTypes("/** @type !Number */var foo = 'abc';", "initializing variable\n" + "found : string\n" + "required: Number"); } public void testVar11() throws Exception { testTypes("var /** @type !Date */foo = 'abc';", "initializing variable\n" + "found : string\n" + "required: Date"); } public void testVar12() throws Exception { testTypes("var /** @type !Date */foo = 'abc', " + "/** @type !RegExp */bar = 5;", new String[] { "initializing variable\n" + "found : string\n" + "required: Date", "initializing variable\n" + "found : number\n" + "required: RegExp"}); } public void testVar13() throws Exception { // this caused an NPE testTypes("var /** @type number */a,a;"); } public void testVar14() throws Exception { testTypes("/** @return {number} */ function f() { var x; return x; }", "inconsistent return type\n" + "found : undefined\n" + "required: number"); } public void testVar15() throws Exception { testTypes("/** @return {number} */" + "function f() { var x = x || {}; return x; }", "inconsistent return type\n" + "found : {}\n" + "required: number"); } public void testAssign1() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 'hello';", "assignment to property foo of goog\n" + "found : string\n" + "required: number"); } public void testAssign2() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 3;" + "goog.foo = 'hello';", "assignment to property foo of goog\n" + "found : string\n" + "required: number"); } public void testAssign3() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 3;" + "goog.foo = 4;"); } public void testAssign4() throws Exception { testTypes("var goog = {};" + "goog.foo = 3;" + "goog.foo = 'hello';"); } public void testAssignInference() throws Exception { testTypes( "/**" + " * @param {Array} x" + " * @return {number}" + " */" + "function f(x) {" + " var y = null;" + " y = x[0];" + " if (y == null) { return 4; } else { return 6; }" + "}"); } public void testOr1() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "a + b || undefined;"); } public void testOr2() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "/** @type number */var c = a + b || undefined;", "initializing variable\n" + "found : (number|undefined)\n" + "required: number"); } public void testOr3() throws Exception { testTypes("/** @type {(number, undefined)} */var a;" + "/** @type number */var c = a || 3;"); } /** * Test that type inference continues with the right side, * when no short-circuiting is possible. * See bugid 1205387 for more details. */ public void testOr4() throws Exception { testTypes("/**@type {number} */var x;x=null || \"a\";", "assignment\n" + "found : string\n" + "required: number"); } /** * @see #testOr4() */ public void testOr5() throws Exception { testTypes("/**@type {number} */var x;x=undefined || \"a\";", "assignment\n" + "found : string\n" + "required: number"); } public void testAnd1() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "a + b && undefined;"); } public void testAnd2() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "/** @type number */var c = a + b && undefined;", "initializing variable\n" + "found : (number|undefined)\n" + "required: number"); } public void testAnd3() throws Exception { testTypes("/** @type {(!Array, undefined)} */var a;" + "/** @type number */var c = a && undefined;", "initializing variable\n" + "found : undefined\n" + "required: number"); } public void testAnd4() throws Exception { testTypes("/** @param {number} x */function f(x){};\n" + "/** @type null */var x; /** @type {number?} */var y;\n" + "if (x && y) { f(y) }"); } public void testAnd5() throws Exception { testTypes("/** @param {number} x\n@param {string} y*/function f(x,y){};\n" + "/** @type {number?} */var x; /** @type {string?} */var y;\n" + "if (x && y) { f(x, y) }"); } public void testAnd6() throws Exception { testTypes("/** @param {number} x */function f(x){};\n" + "/** @type {number|undefined} */var x;\n" + "if (x && f(x)) { f(x) }"); } public void testAnd7() throws Exception { // TODO(user): a deterministic warning should be generated for this // case since x && x is always false. The implementation of this requires // a more precise handling of a null value within a variable's type. // Currently, a null value defaults to ? which passes every check. testTypes("/** @type null */var x; if (x && x) {}"); } public void testHook() throws Exception { testTypes("/**@return {void}*/function foo(){ var x=foo()?a:b; }"); } public void testHookRestrictsType1() throws Exception { testTypes("/** @return {(string,null)} */" + "function f() { return null;}" + "/** @type {(string,null)} */ var a = f();" + "/** @type string */" + "var b = a ? a : 'default';"); } public void testHookRestrictsType2() throws Exception { testTypes("/** @type {String} */" + "var a = null;" + "/** @type (null|undefined) */" + "var b = a ? null : a;"); } public void testHookRestrictsType3() throws Exception { testTypes("/** @type {String} */" + "var a;" + "/** @type (null|undefined) */" + "var b = (!a) ? a : null;"); } public void testHookRestrictsType4() throws Exception { testTypes("/** @type {(boolean,undefined)} */" + "var a;" + "/** @type boolean */" + "var b = a != null ? a : true;"); } public void testHookRestrictsType5() throws Exception { testTypes("/** @type {(boolean,undefined)} */" + "var a;" + "/** @type {(undefined)} */" + "var b = a == null ? a : undefined;"); } public void testHookRestrictsType6() throws Exception { testTypes("/** @type {(number,null,undefined)} */" + "var a;" + "/** @type {number} */" + "var b = a == null ? 5 : a;"); } public void testHookRestrictsType7() throws Exception { testTypes("/** @type {(number,null,undefined)} */" + "var a;" + "/** @type {number} */" + "var b = a == undefined ? 5 : a;"); } public void testWhileRestrictsType1() throws Exception { testTypes("/** @param {null} x */ function g(x) {}" + "/** @param {number?} x */\n" + "function f(x) {\n" + "while (x) {\n" + "if (g(x)) { x = 1; }\n" + "x = x-1;\n}\n}", "actual parameter 1 of g does not match formal parameter\n" + "found : number\n" + "required: null"); } public void testWhileRestrictsType2() throws Exception { testTypes("/** @param {number?} x\n@return {number}*/\n" + "function f(x) {\n/** @type {number} */var y = 0;" + "while (x) {\n" + "y = x;\n" + "x = x-1;\n}\n" + "return y;}"); } public void testHigherOrderFunctions1() throws Exception { testTypes( "/** @type {function(number)} */var f;" + "f(true);", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testHigherOrderFunctions2() throws Exception { testTypes( "/** @type {function():!Date} */var f;" + "/** @type boolean */var a = f();", "initializing variable\n" + "found : Date\n" + "required: boolean"); } public void testHigherOrderFunctions3() throws Exception { testTypes( "/** @type {function(this:Error):Date} */var f; new f", "cannot instantiate non-constructor"); } public void testHigherOrderFunctions4() throws Exception { testTypes( "/** @type {function(this:Error,...[number]):Date} */var f; new f", "cannot instantiate non-constructor"); } public void testConstructorAlias1() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @type {number} */ Foo.prototype.bar = 3;" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {string} */ function foo() { " + " return (new FooAlias()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias2() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @type {number} */ FooAlias.prototype.bar = 3;" + "/** @return {string} */ function foo() { " + " return (new Foo()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias3() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @type {number} */ Foo.prototype.bar = 3;" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {string} */ function foo() { " + " return (new FooAlias()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias4() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "var FooAlias = Foo;" + "/** @type {number} */ FooAlias.prototype.bar = 3;" + "/** @return {string} */ function foo() { " + " return (new Foo()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias5() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {FooAlias} */ function foo() { " + " return new Foo(); }"); } public void testConstructorAlias6() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {Foo} */ function foo() { " + " return new FooAlias(); }"); } public void testConstructorAlias7() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @constructor */ goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias8() throws Exception { testTypes( "var goog = {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.Foo = function(x) {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(1); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias9() throws Exception { testTypes( "var goog = {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.Foo = function(x) {};" + "/** @constructor */ goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(1); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias10() throws Exception { testTypes( "/**\n * @param {number} x \n * @constructor */ " + "var Foo = function(x) {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {number} */ function foo() { " + " return new FooAlias(1); }", "inconsistent return type\n" + "found : Foo\n" + "required: number"); } public void testClosure1() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|undefined} */var a;" + "/** @type string */" + "var b = goog.isDef(a) ? a : 'default';", null); } public void testClosure2() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null} */var a;" + "/** @type string */" + "var b = goog.isNull(a) ? 'default' : a;", null); } public void testClosure3() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null|undefined} */var a;" + "/** @type string */" + "var b = goog.isDefAndNotNull(a) ? a : 'default';", null); } public void testClosure4() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|undefined} */var a;" + "/** @type string */" + "var b = !goog.isDef(a) ? 'default' : a;", null); } public void testClosure5() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null} */var a;" + "/** @type string */" + "var b = !goog.isNull(a) ? a : 'default';", null); } public void testClosure6() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null|undefined} */var a;" + "/** @type string */" + "var b = !goog.isDefAndNotNull(a) ? 'default' : a;", null); } public void testReturn1() throws Exception { testTypes("/**@return {void}*/function foo(){ return 3; }", "inconsistent return type\n" + "found : number\n" + "required: undefined"); } public void testReturn2() throws Exception { testTypes("/**@return {!Number}*/function foo(){ return; }", "inconsistent return type\n" + "found : undefined\n" + "required: Number"); } public void testReturn3() throws Exception { testTypes("/**@return {!Number}*/function foo(){ return 'abc'; }", "inconsistent return type\n" + "found : string\n" + "required: Number"); } public void testReturn4() throws Exception { testTypes("/**@return {!Number}\n*/\n function a(){return new Array();}", "inconsistent return type\n" + "found : Array\n" + "required: Number"); } public void testReturn5() throws Exception { testTypes("/** @param {number} n\n" + "@constructor */function n(n){return};"); } public void testReturn6() throws Exception { testTypes( "/** @param {number} opt_a\n@return {string} */" + "function a(opt_a) { return opt_a }", "inconsistent return type\n" + "found : (number|undefined)\n" + "required: string"); } public void testReturn7() throws Exception { testTypes("/** @constructor */var A = function() {};\n" + "/** @constructor */var B = function() {};\n" + "/** @return {!B} */A.f = function() { return 1; };", "inconsistent return type\n" + "found : number\n" + "required: B"); } public void testReturn8() throws Exception { testTypes("/** @constructor */var A = function() {};\n" + "/** @constructor */var B = function() {};\n" + "/** @return {!B} */A.prototype.f = function() { return 1; };", "inconsistent return type\n" + "found : number\n" + "required: B"); } public void testThis1() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){};" + "/** @return {number} */goog.A.prototype.n = " + " function() { return this };", "inconsistent return type\n" + "found : goog.A\n" + "required: number"); } public void testThis2() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " this.foo = null;" + "};" + "/** @return {number} */" + "goog.A.prototype.n = function() { return this.foo };", "inconsistent return type\n" + "found : null\n" + "required: number"); } public void testThis3() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " this.foo = null;" + " this.foo = 5;" + "};"); } public void testThis4() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " /** @type {string?} */this.foo = null;" + "};" + "/** @return {number} */goog.A.prototype.n = function() {" + " return this.foo };", "inconsistent return type\n" + "found : (null|string|undefined)\n" + "required: number"); } public void testThis5() throws Exception { testTypes("/** @this Date\n@return {number}*/function h() { return this }", "inconsistent return type\n" + "found : Date\n" + "required: number"); } public void testThis6() throws Exception { testTypes("var goog = {};" + "/** @constructor\n@return {!Date} */" + "goog.A = function(){ return this };", "inconsistent return type\n" + "found : goog.A\n" + "required: Date"); } public void testThis7() throws Exception { testTypes("/** @constructor */function A(){};" + "/** @return {number} */A.prototype.n = function() { return this };", "inconsistent return type\n" + "found : A\n" + "required: number"); } public void testThis8() throws Exception { testTypes("/** @constructor */function A(){" + " /** @type {string?} */this.foo = null;" + "};" + "/** @return {number} */A.prototype.n = function() {" + " return this.foo };", "inconsistent return type\n" + "found : (null|string|undefined)\n" + "required: number"); } public void testThis9() throws Exception { // In A.bar, the type of {@code this} is unknown. testTypes("/** @constructor */function A(){};" + "A.prototype.foo = 3;" + "/** @return {string} */ A.bar = function() { return this.foo; };"); } public void testThis10() throws Exception { // In A.bar, the type of {@code this} is inferred from the @this tag. testTypes("/** @constructor */function A(){};" + "A.prototype.foo = 3;" + "/** @this {A}\n@return {string} */" + "A.bar = function() { return this.foo; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testGlobalThis1() throws Exception { testTypes("/** @constructor */ function Window() {}" + "/** @param {string} msg */ " + "Window.prototype.alert = function(msg) {};" + "this.alert(3);", "actual parameter 1 of Window.prototype.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis2() throws Exception { testTypes("/** @constructor */ function Bindow() {}" + "/** @param {string} msg */ " + "Bindow.prototype.alert = function(msg) {};" + "this.alert = 3;" + "(new Bindow()).alert(this.alert)"); } public void testGlobalThis3() throws Exception { testTypes( "/** @param {string} msg */ " + "function alert(msg) {};" + "this.alert(3);", "actual parameter 1 of global this.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis4() throws Exception { testTypes( "/** @param {string} msg */ " + "var alert = function(msg) {};" + "this.alert(3);", "actual parameter 1 of global this.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis5() throws Exception { testTypes( "function f() {" + " /** @param {string} msg */ " + " var alert = function(msg) {};" + "}" + "this.alert(3);", "Property alert never defined on global this"); } public void testGlobalThis6() throws Exception { testTypes( "/** @param {string} msg */ " + "var alert = function(msg) {};" + "var x = 3;" + "x = 'msg';" + "this.alert(this.x);"); } public void testControlFlowRestrictsType1a() throws Exception { testTypes("/** @return {String?} */ function f() { return null; }\n" + "/** @type {String?} */ var a = f();\n" + "/** @type String */ var b = new String('foo');\n" + "/** @type (null|undefined) */ var c = null;\n" + "if (a) {\n" + " b = a;\n" + "} else {\n" + " c = a;\n" + "}"); } public void testControlFlowRestrictsType1b() throws Exception { testTypes("/** @return {!String|null} */ function f() { return null; }\n" + "/** @type {!String|null} */ var a = f();\n" + "/** @type String */ var b = new String('foo');\n" + "/** @type (null) */ var c = null;\n" + "if (a) {\n" + " b = a;\n" + "} else {\n" + " c = a;\n" + "}"); } public void testControlFlowRestrictsType1c() throws Exception { testTypes("/** @return {!String|undefined} */\n" + "function f() { return undefined; }\n" + "/** @type {!String|undefined} */ var a = f();\n" + "/** @type String */ var b = new String('foo');\n" + "/** @type undefined */ var c = undefined;\n" + "if (a) {\n" + " b = a;\n" + "} else {\n" + " c = a;\n" + "}"); } public void testControlFlowRestrictsType2() throws Exception { testTypes("/** @return {(string,null)} */ function f() { return null; }" + "/** @type {(string,null)} */ var a = f();" + "/** @type string */ var b = 'foo';" + "/** @type null */ var c = null;" + "if (a) {" + " b = a;" + "} else {" + " c = a;" + "}", "assignment\n" + "found : (null|string)\n" + "required: null"); } public void testControlFlowRestrictsType3() throws Exception { testTypes("/** @type {(string,void)} */" + "var a;" + "/** @type string */" + "var b = 'foo';" + "if (a) {" + " b = a;" + "}"); } public void testControlFlowRestrictsType4() throws Exception { testTypes("/** @param {string} a */ function f(a){}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);"); } public void testControlFlowRestrictsType5() throws Exception { testTypes("/** @param {undefined} a */ function f(a){}" + "/** @type {(!Array,undefined)} */ var a;" + "a || f(a);"); } public void testControlFlowRestrictsType6() throws Exception { testTypes("/** @param {undefined} x */ function f(x) {}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: undefined"); } public void testControlFlowRestrictsType7() throws Exception { testTypes("/** @param {undefined} x */ function f(x) {}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: undefined"); } public void testControlFlowRestrictsType8() throws Exception { testTypes("/** @param {undefined} a */ function f(a){}" + "/** @type {(!Array,undefined)} */ var a;" + "if (a || f(a)) {}"); } public void testControlFlowRestrictsType9() throws Exception { testTypes("/** @param {number?} x\n * @return {number}*/\n" + "var f = function(x) {\n" + "if (!x || x == 1) { return 1; } else { return x; }\n" + "};"); } public void testSwitchCase3() throws Exception { testTypes("/** @type String */" + "var a = new String('foo');" + "switch (a) { case 'A': }"); } public void testSwitchCase4() throws Exception { testTypes("/** @type {(string,Null)} */" + "var a = 'foo';" + "switch (a) { case 'A':break; case null:break; }"); } public void testSwitchCase5() throws Exception { testTypes("/** @type {(String,Null)} */" + "var a = new String('foo');" + "switch (a) { case 'A':break; case null:break; }"); } public void testSwitchCase6() throws Exception { testTypes("/** @type {(Number,Null)} */" + "var a = new Number(5);" + "switch (a) { case 5:break; case null:break; }"); } public void testSwitchCase7() throws Exception { // This really tests the inference inside the case. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " switch (3) { case g(x.foo): return 3; }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testSwitchCase8() throws Exception { // This really tests the inference inside the switch clause. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " switch (g(x.foo)) { case 3: return 3; }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testNoTypeCheck1() throws Exception { testTypes("/** @notypecheck */function foo() { new 4 }"); } public void testNoTypeCheck2() throws Exception { testTypes("/** @notypecheck */var foo = function() { new 4 }"); } public void testNoTypeCheck3() throws Exception { testTypes("/** @notypecheck */var foo = function bar() { new 4 }"); } public void testNoTypeCheck4() throws Exception { testTypes("var foo;" + "/** @notypecheck */foo = function() { new 4 }"); } public void testNoTypeCheck5() throws Exception { testTypes("var foo;" + "foo = /** @notypecheck */function() { new 4 }"); } public void testNoTypeCheck6() throws Exception { testTypes("var foo;" + "/** @notypecheck */foo = function bar() { new 4 }"); } public void testNoTypeCheck7() throws Exception { testTypes("var foo;" + "foo = /** @notypecheck */function bar() { new 4 }"); } public void testNoTypeCheck8() throws Exception { testTypes("/** @fileoverview \n * @notypecheck */ var foo;" + "var bar = 3; /** @param {string} x */ function f(x) {} f(bar);"); } public void testImplicitCast() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;", "(new Element).innerHTML = new Array();", null, false); } public void testImplicitCastSubclassAccess() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "/** @constructor \n @extends Element */" + "function DIVElement() {};", "(new DIVElement).innerHTML = new Array();", null, false); } public void testImplicitCastNotInExterns() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "(new Element).innerHTML = new Array();", new String[] { "Illegal annotation on innerHTML. @implicitCast may only be " + "used in externs.", "assignment to property innerHTML of Element\n" + "found : Array\n" + "required: string" }); } public void testNumberNode() throws Exception { Node n = typeCheck(Node.newNumber(0)); assertTypeEquals(NUMBER_TYPE, n.getJSType()); } public void testStringNode() throws Exception { Node n = typeCheck(Node.newString("hello")); assertTypeEquals(STRING_TYPE, n.getJSType()); } public void testBooleanNodeTrue() throws Exception { Node trueNode = typeCheck(new Node(Token.TRUE)); assertTypeEquals(BOOLEAN_TYPE, trueNode.getJSType()); } public void testBooleanNodeFalse() throws Exception { Node falseNode = typeCheck(new Node(Token.FALSE)); assertTypeEquals(BOOLEAN_TYPE, falseNode.getJSType()); } public void testUndefinedNode() throws Exception { Node p = new Node(Token.ADD); Node n = Node.newString(Token.NAME, "undefined"); p.addChildToBack(n); p.addChildToBack(Node.newNumber(5)); typeCheck(p); assertTypeEquals(VOID_TYPE, n.getJSType()); } public void testNumberAutoboxing() throws Exception { testTypes("/** @type Number */var a = 4;", "initializing variable\n" + "found : number\n" + "required: (Number|null|undefined)"); } public void testNumberUnboxing() throws Exception { testTypes("/** @type number */var a = new Number(4);", "initializing variable\n" + "found : Number\n" + "required: number"); } public void testStringAutoboxing() throws Exception { testTypes("/** @type String */var a = 'hello';", "initializing variable\n" + "found : string\n" + "required: (String|null|undefined)"); } public void testStringUnboxing() throws Exception { testTypes("/** @type string */var a = new String('hello');", "initializing variable\n" + "found : String\n" + "required: string"); } public void testBooleanAutoboxing() throws Exception { testTypes("/** @type Boolean */var a = true;", "initializing variable\n" + "found : boolean\n" + "required: (Boolean|null|undefined)"); } public void testBooleanUnboxing() throws Exception { testTypes("/** @type boolean */var a = new Boolean(false);", "initializing variable\n" + "found : Boolean\n" + "required: boolean"); } public void testIssue86() throws Exception { testTypes( "/** @interface */ function I() {}" + "/** @return {number} */ I.prototype.get = function(){};" + "/** @constructor \n * @implements {I} */ function F() {}" + "/** @override */ F.prototype.get = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testIssue124() throws Exception { testTypes( "var t = null;" + "function test() {" + " if (t != null) { t = null; }" + " t = 1;" + "}"); } public void testIssue124b() throws Exception { testTypes( "var t = null;" + "function test() {" + " if (t != null) { t = null; }" + " t = undefined;" + "}", "condition always evaluates to false\n" + "left : (null|undefined)\n" + "right: null"); } /** * Tests that the || operator is type checked correctly, that is of * the type of the first argument or of the second argument. See * bugid 592170 for more details. */ public void testBug592170() throws Exception { testTypes( "/** @param {Function} opt_f ... */" + "function foo(opt_f) {" + " /** @type {Function} */" + " return opt_f || function () {};" + "}", "Type annotations are not allowed here. Are you missing parentheses?"); } /** * Tests that undefined can be compared shallowly to a value of type * (number,undefined) regardless of the side on which the undefined * value is. */ public void testBug901455() throws Exception { testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + "var b = undefined === a()"); testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + "var b = a() === undefined"); } /** * Tests that the match method of strings returns nullable arrays. */ public void testBug908701() throws Exception { testTypes("/** @type {String} */var s = new String('foo');" + "var b = s.match(/a/) != null;"); } /** * Tests that named types play nicely with subtyping. */ public void testBug908625() throws Exception { testTypes("/** @constructor */function A(){}" + "/** @constructor\n * @extends A */function B(){}" + "/** @param {B} b" + "\n @return {(A,undefined)} */function foo(b){return b}"); } /** * Tests that assigning two untyped functions to a variable whose type is * inferred and calling this variable is legal. */ public void testBug911118() throws Exception { // verifying the type assigned to anonymous functions assigned variables Scope s = parseAndTypeCheckWithScope("var a = function(){};").scope; JSType type = s.getVar("a").getType(); assertEquals("function (): undefined", type.toString()); // verifying the bug example testTypes("function nullFunction() {};" + "var foo = nullFunction;" + "foo = function() {};" + "foo();"); } public void testBug909000() throws Exception { testTypes("/** @constructor */function A(){}\n" + "/** @param {!A} a\n" + "@return {boolean}*/\n" + "function y(a) { return a }", "inconsistent return type\n" + "found : A\n" + "required: boolean"); } public void testBug930117() throws Exception { testTypes( "/** @param {boolean} x */function f(x){}" + "f(null);", "actual parameter 1 of f does not match formal parameter\n" + "found : null\n" + "required: boolean"); } public void testBug1484445() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {number?} */ Foo.prototype.bar = null;" + "/** @type {number?} */ Foo.prototype.baz = null;" + "/** @param {Foo} foo */" + "function f(foo) {" + " while (true) {" + " if (foo.bar == null && foo.baz == null) {" + " foo.bar;" + " }" + " }" + "}"); } public void testBug1859535() throws Exception { testTypes( "/**\n" + " * @param {Function} childCtor Child class.\n" + " * @param {Function} parentCtor Parent class.\n" + " */" + "var inherits = function(childCtor, parentCtor) {" + " /** @constructor */" + " function tempCtor() {};" + " tempCtor.prototype = parentCtor.prototype;" + " childCtor.superClass_ = parentCtor.prototype;" + " childCtor.prototype = new tempCtor();" + " /** @override */ childCtor.prototype.constructor = childCtor;" + "};" + "/**" + " * @param {Function} constructor\n" + " * @param {Object} var_args\n" + " * @return {Object}\n" + " */" + "var factory = function(constructor, var_args) {" + " /** @constructor */" + " var tempCtor = function() {};" + " tempCtor.prototype = constructor.prototype;" + " var obj = new tempCtor();" + " constructor.apply(obj, arguments);" + " return obj;" + "};"); } public void testBug1940591() throws Exception { testTypes( "/** @type {Object} */" + "var a = {};\n" + "/** @type {number} */\n" + "a.name = 0;\n" + "/**\n" + " * @param {Function} x anything.\n" + " */\n" + "a.g = function(x) { x.name = 'a'; }"); } public void testBug1942972() throws Exception { testTypes( "var google = {\n"+ " gears: {\n" + " factory: {},\n" + " workerPool: {}\n" + " }\n" + "};\n" + "\n" + "google.gears = {factory: {}};\n"); } public void testBug1943776() throws Exception { testTypes( "/** @return {{foo: Array}} */" + "function bar() {" + " return {foo: []};" + "}"); } public void testBug1987544() throws Exception { testTypes( "/** @param {string} x */ function foo(x) {}" + "var duration;" + "if (true && !(duration = 3)) {" + " foo(duration);" + "}", "actual parameter 1 of foo does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testBug1940769() throws Exception { testTypes( "/** @return {!Object} */ " + "function proto(obj) { return obj.prototype; }" + "/** @constructor */ function Map() {}" + "/**\n" + " * @constructor\n" + " * @extends {Map}\n" + " */" + "function Map2() { Map.call(this); };" + "Map2.prototype = proto(Map);"); } public void testBug2335992() throws Exception { testTypes( "/** @return {*} */ function f() { return 3; }" + "var x = f();" + "/** @type {string} */" + "x.y = 3;", "assignment\n" + "found : number\n" + "required: string"); } public void testBug2341812() throws Exception { testTypes( "/** @interface */" + "function EventTarget() {}" + "/** @constructor \n * @implements {EventTarget} */" + "function Node() {}" + "/** @type {number} */ Node.prototype.index;" + "/** @param {EventTarget} x \n * @return {string} */" + "function foo(x) { return x.index; }"); } public void testScopedConstructors() throws Exception { testTypes( "function foo1() { " + " /** @constructor */ function Bar() { " + " /** @type {number} */ this.x = 3;" + " }" + "}" + "function foo2() { " + " /** @constructor */ function Bar() { " + " /** @type {string} */ this.x = 'y';" + " }" + " /** " + " * @param {Bar} b\n" + " * @return {number}\n" + " */" + " function baz(b) { return b.x; }" + "}", "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testQualifiedNameInference1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {number?} */ Foo.prototype.bar = null;" + "/** @type {number?} */ Foo.prototype.baz = null;" + "/** @param {Foo} foo */" + "function f(foo) {" + " while (true) {" + " if (!foo.baz) break; " + " foo.bar = null;" + " }" + // Tests a bug where this condition always evaluated to true. " return foo.bar == null;" + "}"); } public void testQualifiedNameInference2() throws Exception { testTypes( "var x = {};" + "x.y = c;" + "function f(a, b) {" + " if (a) {" + " if (b) " + " x.y = 2;" + " else " + " x.y = 1;" + " }" + " return x.y == null;" + "}"); } public void testQualifiedNameInference3() throws Exception { testTypes( "var x = {};" + "x.y = c;" + "function f(a, b) {" + " if (a) {" + " if (b) " + " x.y = 2;" + " else " + " x.y = 1;" + " }" + " return x.y == null;" + "} function g() { x.y = null; }"); } public void testQualifiedNameInference4() throws Exception { testTypes( "/** @param {string} x */ function f(x) {}\n" + "/**\n" + " * @param {?string} x \n" + " * @constructor\n" + " */" + "function Foo(x) { this.x_ = x; }\n" + "Foo.prototype.bar = function() {" + " if (this.x_) { f(this.x_); }" + "};"); } public void testSheqRefinedScope() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */function A() {}\n" + "/** @constructor \n @extends A */ function B() {}\n" + "/** @return {number} */\n" + "B.prototype.p = function() { return 1; }\n" + "/** @param {A} a\n @param {B} b */\n" + "function f(a, b) {\n" + " b.p();\n" + " if (a === b) {\n" + " b.p();\n" + " }\n" + "}"); Node nodeC = n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild(); JSType typeC = nodeC.getJSType(); assertTrue(typeC.isNumber()); Node nodeB = nodeC.getFirstChild().getFirstChild(); JSType typeB = nodeB.getJSType(); assertEquals("B", typeB.toString()); } public void testAssignToUntypedVariable() throws Exception { Node n = parseAndTypeCheck("var z; z = 1;"); Node assign = n.getLastChild().getFirstChild(); Node node = assign.getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertEquals("number", node.getJSType().toString()); } public void testAssignToUntypedProperty() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 1;" + "(new Foo).a;"); Node node = n.getLastChild().getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertTrue(node.getJSType().isNumber()); } public void testNew1() throws Exception { testTypes("new 4", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew2() throws Exception { testTypes("var Math = {}; new Math()", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew3() throws Exception { testTypes("new Date()"); } public void testNew4() throws Exception { testTypes("/** @constructor */function A(){}; new A();"); } public void testNew5() throws Exception { testTypes("function A(){}; new A();", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew6() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @constructor */function A(){};" + "var a = new A();"); JSType aType = p.scope.getVar("a").getType(); assertTrue(aType instanceof ObjectType); ObjectType aObjectType = (ObjectType) aType; assertEquals("A", aObjectType.getConstructor().getReferenceName()); } public void testNew7() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "if (opt_constructor) { new opt_constructor; }" + "}"); } public void testNew8() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "new opt_constructor;" + "}"); } public void testNew9() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "new (opt_constructor || Array);" + "}"); } public void testNew10() throws Exception { testTypes("var goog = {};" + "/** @param {Function} opt_constructor */" + "goog.Foo = function (opt_constructor) {" + "new (opt_constructor || Array);" + "}"); } public void testNew11() throws Exception { testTypes("/** @param {Function} c1 */" + "function f(c1) {" + " var c2 = function(){};" + " c1.prototype = new c2;" + "}", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew12() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = new Array();"); Var a = p.scope.getVar("a"); assertTypeEquals(ARRAY_TYPE, a.getType()); } public void testNew13() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "/** @constructor */function FooBar(){};" + "var a = new FooBar();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("FooBar", a.getType().toString()); } public void testNew14() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "/** @constructor */var FooBar = function(){};" + "var a = new FooBar();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("FooBar", a.getType().toString()); } public void testNew15() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "/** @constructor */goog.A = function(){};" + "var a = new goog.A();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("goog.A", a.getType().toString()); } public void testNew16() throws Exception { testTypes( "/** \n" + " * @param {string} x \n" + " * @constructor \n" + " */" + "function Foo(x) {}" + "function g() { new Foo(1); }", "actual parameter 1 of Foo does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testName1() throws Exception { assertTypeEquals(VOID_TYPE, testNameNode("undefined")); } public void testName2() throws Exception { assertTypeEquals(OBJECT_FUNCTION_TYPE, testNameNode("Object")); } public void testName3() throws Exception { assertTypeEquals(ARRAY_FUNCTION_TYPE, testNameNode("Array")); } public void testName4() throws Exception { assertTypeEquals(DATE_FUNCTION_TYPE, testNameNode("Date")); } public void testName5() throws Exception { assertTypeEquals(REGEXP_FUNCTION_TYPE, testNameNode("RegExp")); } /** * Type checks a NAME node and retrieve its type. */ private JSType testNameNode(String name) { Node node = Node.newString(Token.NAME, name); Node parent = new Node(Token.SCRIPT, node); parent.setInputId(new InputId("code")); Node externs = new Node(Token.SCRIPT); externs.setInputId(new InputId("externs")); Node externAndJsRoot = new Node(Token.BLOCK, externs, parent); externAndJsRoot.setIsSyntheticBlock(true); makeTypeCheck().processForTesting(null, parent); return node.getJSType(); } public void testBitOperation1() throws Exception { testTypes("/**@return {void}*/function foo(){ ~foo(); }", "operator ~ cannot be applied to undefined"); } public void testBitOperation2() throws Exception { testTypes("/**@return {void}*/function foo(){var a = foo()<<3;}", "operator << cannot be applied to undefined"); } public void testBitOperation3() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 3<>>3;}", "operator >>> cannot be applied to undefined"); } public void testBitOperation5() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 3>>>foo();}", "operator >>> cannot be applied to undefined"); } public void testBitOperation6() throws Exception { testTypes("/**@return {!Object}*/function foo(){var a = foo()&3;}", "bad left operand to bitwise operator\n" + "found : Object\n" + "required: (boolean|null|number|string|undefined)"); } public void testBitOperation7() throws Exception { testTypes("var x = null; x |= undefined; x &= 3; x ^= '3'; x |= true;"); } public void testBitOperation8() throws Exception { testTypes("var x = void 0; x |= new Number(3);"); } public void testBitOperation9() throws Exception { testTypes("var x = void 0; x |= {};", "bad right operand to bitwise operator\n" + "found : {}\n" + "required: (boolean|null|number|string|undefined)"); } public void testCall1() throws Exception { testTypes("3();", "number expressions are not callable"); } public void testCall2() throws Exception { testTypes("/** @param {!Number} foo*/function bar(foo){ bar('abc'); }", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: Number"); } public void testCall3() throws Exception { // We are checking that an unresolved named type can successfully // meet with a functional type to produce a callable type. testTypes("/** @type {Function|undefined} */var opt_f;" + "/** @type {some.unknown.type} */var f1;" + "var f2 = opt_f || f1;" + "f2();", "Bad type annotation. Unknown type some.unknown.type"); } public void testCall4() throws Exception { testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ bar('abc'); }", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall5() throws Exception { testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ foo('abc'); }", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall6() throws Exception { testTypes("/** @param {!Number} foo*/function bar(foo){}" + "bar('abc');", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: Number"); } public void testCall7() throws Exception { testTypes("/** @param {!RegExp} a*/var foo = function bar(a){};" + "foo('abc');", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall8() throws Exception { testTypes("/** @type {Function|number} */var f;f();", "(Function|number) expressions are " + "not callable"); } public void testCall9() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @param {!goog.Foo} a */ var bar = function(a){};" + "bar('abc');", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: goog.Foo"); } public void testCall10() throws Exception { testTypes("/** @type {Function} */var f;f();"); } public void testCall11() throws Exception { testTypes("var f = new Function(); f();"); } public void testFunctionCall1() throws Exception { testTypes( "/** @param {number} x */ var foo = function(x) {};" + "foo.call(null, 3);"); } public void testFunctionCall2() throws Exception { testTypes( "/** @param {number} x */ var foo = function(x) {};" + "foo.call(null, 'bar');", "actual parameter 2 of foo.call does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testFunctionCall3() throws Exception { testTypes( "/** @param {number} x \n * @constructor */ " + "var Foo = function(x) { this.bar.call(null, x); };" + "/** @type {function(number)} */ Foo.prototype.bar;"); } public void testFunctionCall4() throws Exception { testTypes( "/** @param {string} x \n * @constructor */ " + "var Foo = function(x) { this.bar.call(null, x); };" + "/** @type {function(number)} */ Foo.prototype.bar;", "actual parameter 2 of this.bar.call " + "does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testFunctionCall5() throws Exception { testTypes( "/** @param {Function} handler \n * @constructor */ " + "var Foo = function(handler) { handler.call(this, x); };"); } public void testFunctionCall6() throws Exception { testTypes( "/** @param {Function} handler \n * @constructor */ " + "var Foo = function(handler) { handler.apply(this, x); };"); } public void testFunctionCall7() throws Exception { testTypes( "/** @param {Function} handler \n * @param {Object} opt_context */ " + "var Foo = function(handler, opt_context) { " + " handler.call(opt_context, x);" + "};"); } public void testFunctionCall8() throws Exception { testTypes( "/** @param {Function} handler \n * @param {Object} opt_context */ " + "var Foo = function(handler, opt_context) { " + " handler.apply(opt_context, x);" + "};"); } public void testCast2() throws Exception { // can upcast to a base type. testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n @extends {base} */function derived() {}\n" + "/** @type {base} */ var baz = new derived();\n"); } public void testCast3() throws Exception { // cannot downcast testTypes("/** @constructor */function base() {}\n" + "/** @constructor @extends {base} */function derived() {}\n" + "/** @type {!derived} */ var baz = new base();\n", "initializing variable\n" + "found : base\n" + "required: derived"); } public void testCast4() throws Exception { // downcast must be explicit testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n" + "/** @type {!derived} */ var baz = " + "/** @type {!derived} */(new base());\n"); } public void testCast5() throws Exception { // cannot explicitly cast to an unrelated type testTypes("/** @constructor */function foo() {}\n" + "/** @constructor */function bar() {}\n" + "var baz = /** @type {!foo} */(new bar);\n", "invalid cast - must be a subtype or supertype\n" + "from: bar\n" + "to : foo"); } public void testCast6() throws Exception { // can explicitly cast to a subtype or supertype testTypes("/** @constructor */function foo() {}\n" + "/** @constructor \n @extends foo */function bar() {}\n" + "var baz = /** @type {!bar} */(new bar);\n" + "var baz = /** @type {!foo} */(new foo);\n" + "var baz = /** @type {bar} */(new bar);\n" + "var baz = /** @type {foo} */(new foo);\n" + "var baz = /** @type {!foo} */(new bar);\n" + "var baz = /** @type {!bar} */(new foo);\n" + "var baz = /** @type {foo} */(new bar);\n" + "var baz = /** @type {bar} */(new foo);\n"); } public void testCast7() throws Exception { testTypes("var x = /** @type {foo} */ (new Object());", "Bad type annotation. Unknown type foo"); } public void testCast8() throws Exception { testTypes("function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast9() throws Exception { testTypes("var foo = {};" + "function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast10() throws Exception { testTypes("var foo = function() {};" + "function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast11() throws Exception { testTypes("var goog = {}; goog.foo = {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast12() throws Exception { testTypes("var goog = {}; goog.foo = function() {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast13() throws Exception { // Test to make sure that the forward-declaration still allows for // a warning. testClosureTypes("var goog = {}; " + "goog.addDependency('zzz.js', ['goog.foo'], []);" + "goog.foo = function() {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast14() throws Exception { // Test to make sure that the forward-declaration still prevents // some warnings. testClosureTypes("var goog = {}; " + "goog.addDependency('zzz.js', ['goog.bar'], []);" + "function f() { return /** @type {goog.bar} */ (new Object()); }", null); } public void testCast15() throws Exception { // This fixes a bug where a type cast on an object literal // would cause a run-time cast exception if the node was visited // more than once. // // Some code assumes that an object literal must have a object type, // while because of the cast, it could have any type (including // a union). testTypes( "for (var i = 0; i < 10; i++) {" + "var x = /** @type {Object|number} */ ({foo: 3});" + "/** @param {number} x */ function f(x) {}" + "f(x.foo);" + "f([].foo);" + "}", "Property foo never defined on Array"); } public void testCast16() throws Exception { // Mostly verifying that rhino actually understands these JsDocs. testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = /** @type {Foo} */ ({})"); testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = (/** @type {Foo} */ y)"); } public void testNestedCasts() throws Exception { testTypes("/** @constructor */var T = function() {};\n" + "/** @constructor */var V = function() {};\n" + "/**\n" + "* @param {boolean} b\n" + "* @return {T|V}\n" + "*/\n" + "function f(b) { return b ? new T() : new V(); }\n" + "/**\n" + "* @param {boolean} b\n" + "* @return {boolean|undefined}\n" + "*/\n" + "function g(b) { return b ? true : undefined; }\n" + "/** @return {T} */\n" + "function h() {\n" + "return /** @type {T} */ (f(/** @type {boolean} */ (g(true))));\n" + "}"); } public void testNativeCast1() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(String(true));", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testNativeCast2() throws Exception { testTypes( "/** @param {string} x */ function f(x) {}" + "f(Number(true));", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testNativeCast3() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(Boolean(''));", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testNativeCast4() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(Error(''));", "actual parameter 1 of f does not match formal parameter\n" + "found : Error\n" + "required: number"); } public void testBadConstructorCall() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo();", "Constructor function (new:Foo): undefined should be called " + "with the \"new\" keyword"); } public void testTypeof() throws Exception { testTypes("/**@return {void}*/function foo(){ var a = typeof foo(); }"); } public void testConstructorType1() throws Exception { testTypes("/**@constructor*/function Foo(){}" + "/**@type{!Foo}*/var f = new Date();", "initializing variable\n" + "found : Date\n" + "required: Foo"); } public void testConstructorType2() throws Exception { testTypes("/**@constructor*/function Foo(){\n" + "/**@type{Number}*/this.bar = new Number(5);\n" + "}\n" + "/**@type{Foo}*/var f = new Foo();\n" + "/**@type{Number}*/var n = f.bar;"); } public void testConstructorType3() throws Exception { // Reverse the declaration order so that we know that Foo is getting set // even on an out-of-order declaration sequence. testTypes("/**@type{Foo}*/var f = new Foo();\n" + "/**@type{Number}*/var n = f.bar;" + "/**@constructor*/function Foo(){\n" + "/**@type{Number}*/this.bar = new Number(5);\n" + "}\n"); } public void testConstructorType4() throws Exception { testTypes("/**@constructor*/function Foo(){\n" + "/**@type{!Number}*/this.bar = new Number(5);\n" + "}\n" + "/**@type{!Foo}*/var f = new Foo();\n" + "/**@type{!String}*/var n = f.bar;", "initializing variable\n" + "found : Number\n" + "required: String"); } public void testConstructorType5() throws Exception { testTypes("/**@constructor*/function Foo(){}\n" + "if (Foo){}\n"); } public void testConstructorType6() throws Exception { testTypes("/** @constructor */\n" + "function bar() {}\n" + "function _foo() {\n" + " /** @param {bar} x */\n" + " function f(x) {}\n" + "}"); } public void testConstructorType7() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @constructor */function A(){};"); JSType type = p.scope.getVar("A").getType(); assertTrue(type instanceof FunctionType); FunctionType fType = (FunctionType) type; assertEquals("A", fType.getReferenceName()); } public void testAnonymousType1() throws Exception { testTypes("function f() { return {}; }" + "/** @constructor */\n" + "f().bar = function() {};"); } public void testAnonymousType2() throws Exception { testTypes("function f() { return {}; }" + "/** @interface */\n" + "f().bar = function() {};"); } public void testAnonymousType3() throws Exception { testTypes("function f() { return {}; }" + "/** @enum */\n" + "f().bar = {FOO: 1};"); } public void testBang1() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return x; }", "inconsistent return type\n" + "found : (Object|null|undefined)\n" + "required: Object"); } public void testBang2() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return x ? x : new Object(); }"); } public void testBang3() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return /** @type {!Object} */ (x); }"); } public void testBang4() throws Exception { testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + "function f(x, y) {\n" + "if (typeof x != 'undefined') { return x == y; }\n" + "else { return x != y; }\n}"); } public void testBang5() throws Exception { testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + "function f(x, y) { return !!x && x == y; }"); } public void testBang6() throws Exception { testTypes("/** @param {Object?} x\n@return {Object} */\n" + "function f(x) { return x; }"); } public void testBang7() throws Exception { testTypes("/**@param {(Object,string,null)} x\n" + "@return {(Object,string)}*/function f(x) { return x; }"); } public void testDefinePropertyOnNullableObject1() throws Exception { testTypes("/** @type {Object} */ var n = {};\n" + "/** @type {number} */ n.x = 1;\n" + "/** @return {boolean} */function f() { return n.x; }", "inconsistent return type\n" + "found : number\n" + "required: boolean"); } public void testDefinePropertyOnNullableObject2() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {T} t\n@return {boolean} */function f(t) {\n" + "t.x = 1; return t.x; }", "inconsistent return type\n" + "found : number\n" + "required: boolean"); } public void testUnknownConstructorInstanceType1() throws Exception { testTypes("/** @return {Array} */ function g(f) { return new f(); }"); } public void testUnknownConstructorInstanceType2() throws Exception { testTypes("function g(f) { return /** @type Array */ (new f()); }"); } public void testUnknownConstructorInstanceType3() throws Exception { testTypes("function g(f) { var x = new f(); x.a = 1; return x; }"); } public void testUnknownPrototypeChain() throws Exception { testTypes("/**\n" + "* @param {Object} co\n" + " * @return {Object}\n" + " */\n" + "function inst(co) {\n" + " /** @constructor */\n" + " var c = function() {};\n" + " c.prototype = co.prototype;\n" + " return new c;\n" + "}"); } public void testNamespacedConstructor() throws Exception { Node root = parseAndTypeCheck( "var goog = {};" + "/** @constructor */ goog.MyClass = function() {};" + "/** @return {!goog.MyClass} */ " + "function foo() { return new goog.MyClass(); }"); JSType typeOfFoo = root.getLastChild().getJSType(); assert(typeOfFoo instanceof FunctionType); JSType retType = ((FunctionType) typeOfFoo).getReturnType(); assert(retType instanceof ObjectType); assertEquals("goog.MyClass", ((ObjectType) retType).getReferenceName()); } public void testComplexNamespace() throws Exception { String js = "var goog = {};" + "goog.foo = {};" + "goog.foo.bar = 5;"; TypeCheckResult p = parseAndTypeCheckWithScope(js); // goog type in the scope JSType googScopeType = p.scope.getVar("goog").getType(); assertTrue(googScopeType instanceof ObjectType); assertTrue("foo property not present on goog type", ((ObjectType) googScopeType).hasProperty("foo")); assertFalse("bar property present on goog type", ((ObjectType) googScopeType).hasProperty("bar")); // goog type on the VAR node Node varNode = p.root.getFirstChild(); assertEquals(Token.VAR, varNode.getType()); JSType googNodeType = varNode.getFirstChild().getJSType(); assertTrue(googNodeType instanceof ObjectType); // goog scope type and goog type on VAR node must be the same assertTrue(googScopeType == googNodeType); // goog type on the left of the GETPROP node (under fist ASSIGN) Node getpropFoo1 = varNode.getNext().getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getpropFoo1.getType()); assertEquals("goog", getpropFoo1.getFirstChild().getString()); JSType googGetpropFoo1Type = getpropFoo1.getFirstChild().getJSType(); assertTrue(googGetpropFoo1Type instanceof ObjectType); // still the same type as the one on the variable assertTrue(googGetpropFoo1Type == googScopeType); // the foo property should be defined on goog JSType googFooType = ((ObjectType) googScopeType).getPropertyType("foo"); assertTrue(googFooType instanceof ObjectType); // goog type on the left of the GETPROP lower level node // (under second ASSIGN) Node getpropFoo2 = varNode.getNext().getNext() .getFirstChild().getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getpropFoo2.getType()); assertEquals("goog", getpropFoo2.getFirstChild().getString()); JSType googGetpropFoo2Type = getpropFoo2.getFirstChild().getJSType(); assertTrue(googGetpropFoo2Type instanceof ObjectType); // still the same type as the one on the variable assertTrue(googGetpropFoo2Type == googScopeType); // goog.foo type on the left of the top-level GETPROP node // (under second ASSIGN) JSType googFooGetprop2Type = getpropFoo2.getJSType(); assertTrue("goog.foo incorrectly annotated in goog.foo.bar selection", googFooGetprop2Type instanceof ObjectType); ObjectType googFooGetprop2ObjectType = (ObjectType) googFooGetprop2Type; assertFalse("foo property present on goog.foo type", googFooGetprop2ObjectType.hasProperty("foo")); assertTrue("bar property not present on goog.foo type", googFooGetprop2ObjectType.hasProperty("bar")); assertTypeEquals("bar property on goog.foo type incorrectly inferred", NUMBER_TYPE, googFooGetprop2ObjectType.getPropertyType("bar")); } public void testAddingMethodsUsingPrototypeIdiomSimpleNamespace() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype.m1 = 5"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 1, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); } public void testAddingMethodsUsingPrototypeIdiomComplexNamespace1() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "goog.A = /** @constructor */function() {};" + "/** @type number */goog.A.prototype.m1 = 5"); testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); } public void testAddingMethodsUsingPrototypeIdiomComplexNamespace2() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "/** @constructor */goog.A = function() {};" + "/** @type number */goog.A.prototype.m1 = 5"); testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); } private void testAddingMethodsUsingPrototypeIdiomComplexNamespace( TypeCheckResult p) { ObjectType goog = (ObjectType) p.scope.getVar("goog").getType(); assertEquals(NATIVE_PROPERTIES_COUNT + 1, goog.getPropertiesCount()); JSType googA = goog.getPropertyType("A"); assertNotNull(googA); assertTrue(googA instanceof FunctionType); FunctionType googAFunction = (FunctionType) googA; ObjectType classA = googAFunction.getInstanceType(); assertEquals(NATIVE_PROPERTIES_COUNT + 1, classA.getPropertiesCount()); checkObjectType(classA, "m1", NUMBER_TYPE); } public void testAddingMethodsPrototypeIdiomAndObjectLiteralSimpleNamespace() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true}"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 2, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); checkObjectType(instanceType, "m2", BOOLEAN_TYPE); } public void testDontAddMethodsIfNoConstructor() throws Exception { Node js1Node = parseAndTypeCheck( "function A() {}" + "A.prototype = {m1: 5, m2: true}"); JSType functionAType = js1Node.getFirstChild().getJSType(); assertEquals("function (): undefined", functionAType.toString()); assertTypeEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m1")); assertTypeEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m2")); } public void testFunctionAssignement() throws Exception { testTypes("/**" + "* @param {string} ph0" + "* @param {string} ph1" + "* @return {string}" + "*/" + "function MSG_CALENDAR_ACCESS_ERROR(ph0, ph1) {return ''}" + "/** @type {Function} */" + "var MSG_CALENDAR_ADD_ERROR = MSG_CALENDAR_ACCESS_ERROR;"); } public void testAddMethodsPrototypeTwoWays() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true};" + "A.prototype.m3 = 'third property!';"); ObjectType instanceType = getInstanceType(js1Node); assertEquals("A", instanceType.toString()); assertEquals(NATIVE_PROPERTIES_COUNT + 3, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); checkObjectType(instanceType, "m2", BOOLEAN_TYPE); checkObjectType(instanceType, "m3", STRING_TYPE); } public void testPrototypePropertyTypes() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {\n" + " /** @type string */ this.m1;\n" + " /** @type Object? */ this.m2 = {};\n" + " /** @type boolean */ this.m3;\n" + "}\n" + "/** @type string */ A.prototype.m4;\n" + "/** @type number */ A.prototype.m5 = 0;\n" + "/** @type boolean */ A.prototype.m6;\n"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 6, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", STRING_TYPE); checkObjectType(instanceType, "m2", createUnionType(createUnionType(OBJECT_TYPE, NULL_TYPE), VOID_TYPE)); checkObjectType(instanceType, "m3", BOOLEAN_TYPE); checkObjectType(instanceType, "m4", STRING_TYPE); checkObjectType(instanceType, "m5", NUMBER_TYPE); checkObjectType(instanceType, "m6", BOOLEAN_TYPE); } public void testValueTypeBuiltInPrototypePropertyType() throws Exception { Node node = parseAndTypeCheck("\"x\".charAt(0)"); assertTypeEquals(STRING_TYPE, node.getFirstChild().getFirstChild().getJSType()); } public void testDeclareBuiltInConstructor() throws Exception { // Built-in prototype properties should be accessible // even if the built-in constructor is declared. Node node = parseAndTypeCheck( "/** @constructor */ var String = function(opt_str) {};\n" + "(new String(\"x\")).charAt(0)"); assertTypeEquals(STRING_TYPE, node.getLastChild().getFirstChild().getJSType()); } public void testExtendBuiltInType1() throws Exception { String externs = "/** @constructor */ var String = function(opt_str) {};\n" + "/**\n" + "* @param {number} start\n" + "* @param {number} opt_length\n" + "* @return {string}\n" + "*/\n" + "String.prototype.substr = function(start, opt_length) {};\n"; Node n1 = parseAndTypeCheck(externs + "(new String(\"x\")).substr(0,1);"); assertTypeEquals(STRING_TYPE, n1.getLastChild().getFirstChild().getJSType()); } public void testExtendBuiltInType2() throws Exception { String externs = "/** @constructor */ var String = function(opt_str) {};\n" + "/**\n" + "* @param {number} start\n" + "* @param {number} opt_length\n" + "* @return {string}\n" + "*/\n" + "String.prototype.substr = function(start, opt_length) {};\n"; Node n2 = parseAndTypeCheck(externs + "\"x\".substr(0,1);"); assertTypeEquals(STRING_TYPE, n2.getLastChild().getFirstChild().getJSType()); } public void testExtendFunction1() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(new Function()).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertTypeEquals(NUMBER_TYPE, type); } public void testExtendFunction2() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(function() {}).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertTypeEquals(NUMBER_TYPE, type); } public void testInheritanceCheck1() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};"); } public void testInheritanceCheck2() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "property foo not defined on any superclass of Sub"); } public void testInheritanceCheck3() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on superclass Super; " + "use @override to override it"); } public void testInheritanceCheck4() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInheritanceCheck5() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() {};" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on superclass Root; " + "use @override to override it"); } public void testInheritanceCheck6() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() {};" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInheritanceCheck7() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "goog.Sub.prototype.foo = 5;"); } public void testInheritanceCheck8() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @override */goog.Sub.prototype.foo = 5;"); } public void testInheritanceCheck9_1() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() { return 3; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };"); } public void testInheritanceCheck9_2() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @return {number} */" + "Super.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo =\n" + "function() {};"); } public void testInheritanceCheck9_3() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @return {number} */" + "Super.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {string} */Sub.prototype.foo =\n" + "function() { return \"some string\" };", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Super\n" + "original: function (this:Super): number\n" + "override: function (this:Sub): string"); } public void testInheritanceCheck10_1() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() { return 4; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };"); } public void testInheritanceCheck10_2() throws Exception { testTypes( "/** @constructor */function Root() {};" + "/** @return {number} */" + "Root.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo =\n" + "function() {};"); } public void testInheritanceCheck10_3() throws Exception { testTypes( "/** @constructor */function Root() {};" + "/** @return {number} */" + "Root.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {string} */Sub.prototype.foo =\n" + "function() { return \"some string\" };", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Root\n" + "original: function (this:Root): number\n" + "override: function (this:Sub): string"); } public void testInterfaceInheritanceCheck11() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + "function(bar) {};", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Super\n" + "original: function (this:Super, number): undefined\n" + "override: function (this:Sub, string): undefined"); } public void testInheritanceCheck12() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @override */goog.Sub.prototype.foo = \"some string\";"); } public void testInheritanceCheck13() throws Exception { testTypes( "var goog = {};\n" + "/** @constructor\n @extends {goog.Missing} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "Bad type annotation. Unknown type goog.Missing"); } public void testInheritanceCheck14() throws Exception { testTypes( "var goog = {};\n" + "/** @constructor\n @extends {goog.Missing} */\n" + "goog.Super = function() {};\n" + "/** @constructor\n @extends {goog.Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "Bad type annotation. Unknown type goog.Missing"); } // TODO(user): We should support this way of declaring properties as it is // widely used. //public void testInheritanceCheck15() throws Exception { // testTypes( // "/** @constructor */function Super() {};" + // "/** @param {number} bar */Super.prototype.foo;" + // "/** @constructor\n @extends {Super} */function Sub() {};" + // "/** @override\n @param {number} bar */Sub.prototype.foo =\n" + // "function(bar) {};"); //} // public void testInterfacePropertyOverride1() throws Exception { // testTypes( // "/** @interface */function Super() {};" + // "/** @desc description */Super.prototype.foo = function() {};" + // "/** @interface\n @extends {Super} */function Sub() {};" + // "/** @desc description */Sub.prototype.foo = function() {};", // "property foo is already defined by the Super extended interface"); // } // public void testInterfacePropertyOverride2() throws Exception { // testTypes( // "/** @interface */function Root() {};" + // "/** @desc description */Root.prototype.foo = function() {};" + // "/** @interface\n @extends {Root} */function Super() {};" + // "/** @interface\n @extends {Super} */function Sub() {};" + // "/** @desc description */Sub.prototype.foo = function() {};", // "property foo is already defined by the Root extended interface"); // } public void testInterfaceInheritanceCheck1() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @desc description */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on interface Super; use @override to " + "override it"); } public void testInterfaceInheritanceCheck2() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @desc description */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInterfaceInheritanceCheck3() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {number} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @return {number} */Sub.prototype.foo = function() { return 1;};", "property foo already defined on interface Root; use @override to " + "override it"); } public void testInterfaceInheritanceCheck4() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {number} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n * @return {number} */Sub.prototype.foo =\n" + "function() { return 1;};"); } public void testInterfaceInheritanceCheck5() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @return {string} */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };", "mismatch of the foo property type and the type of the property it " + "overrides from interface Super\n" + "original: function (this:Super): string\n" + "override: function (this:Sub): number"); } public void testInterfaceInheritanceCheck6() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {string} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };", "mismatch of the foo property type and the type of the property it " + "overrides from interface Root\n" + "original: function (this:Root): string\n" + "override: function (this:Sub): number"); } public void testInterfaceInheritanceCheck7() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + "function(bar) {};", "mismatch of the foo property type and the type of the property it " + "overrides from interface Super\n" + "original: function (this:Super, number): undefined\n" + "override: function (this:Sub, string): undefined"); } public void testInterfaceInheritanceCheck8() throws Exception { testTypes( "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", new String[] { "Bad type annotation. Unknown type Super", "property foo not defined on any superclass of Sub" }); } public void testInterfacePropertyNotImplemented() throws Exception { testTypes( "/** @interface */function Int() {};" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @constructor\n @implements {Int} */function Foo() {};", "property foo on interface Int is not implemented by type Foo"); } public void testInterfacePropertyNotImplemented2() throws Exception { testTypes( "/** @interface */function Int() {};" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @interface \n @extends {Int} */function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "property foo on interface Int is not implemented by type Foo"); } public void testStubConstructorImplementingInterface() throws Exception { // This does not throw a warning for unimplemented property because Foo is // just a stub. testTypes( // externs "/** @interface */ function Int() {}\n" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @constructor \n @implements {Int} */ var Foo;\n", "", null, false); } public void testObjectLiteral() throws Exception { Node n = parseAndTypeCheck("var a = {m1: 7, m2: 'hello'}"); Node nameNode = n.getFirstChild().getFirstChild(); Node objectNode = nameNode.getFirstChild(); // node extraction assertEquals(Token.NAME, nameNode.getType()); assertEquals(Token.OBJECTLIT, objectNode.getType()); // value's type ObjectType objectType = (ObjectType) objectNode.getJSType(); assertTypeEquals(NUMBER_TYPE, objectType.getPropertyType("m1")); assertTypeEquals(STRING_TYPE, objectType.getPropertyType("m2")); // variable's type assertTypeEquals(objectType, nameNode.getJSType()); } public void testObjectLiteralDeclaration1() throws Exception { testTypes( "var x = {" + "/** @type {boolean} */ abc: true," + "/** @type {number} */ 'def': 0," + "/** @type {string} */ 3: 'fgh'" + "};"); } public void testCallDateConstructorAsFunction() throws Exception { // ECMA-262 15.9.2: When Date is called as a function rather than as a // constructor, it returns a string. Node n = parseAndTypeCheck("Date()"); assertTypeEquals(STRING_TYPE, n.getFirstChild().getFirstChild().getJSType()); } // According to ECMA-262, Error & Array function calls are equivalent to // constructor calls. public void testCallErrorConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Error('x')"); assertTypeEquals(ERROR_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testCallArrayConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Array()"); assertTypeEquals(ARRAY_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testPropertyTypeOfUnionType() throws Exception { testTypes("var a = {};" + "/** @constructor */ a.N = function() {};\n" + "a.N.prototype.p = 1;\n" + "/** @constructor */ a.S = function() {};\n" + "a.S.prototype.p = 'a';\n" + "/** @param {!a.N|!a.S} x\n@return {string} */\n" + "var f = function(x) { return x.p; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } // TODO(user): We should flag these as invalid. This will probably happen // when we make sure the interface is never referenced outside of its // definition. We might want more specific and helpful error messages. //public void testWarningOnInterfacePrototype() throws Exception { // testTypes("/** @interface */ u.T = function() {};\n" + // "/** @return {number} */ u.T.prototype = function() { };", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function() {};\n" + // "/** @return {number} */ u.T.f = function() { return 1;};", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {};\n" + // "/** @return {number} */ T.f = function() { return 1;};", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface3() throws Exception { // testTypes("/** @interface */ u.T = function() {}; u.T.x", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface4() throws Exception { // testTypes("/** @interface */ function T() {}; T.x;", // "cannot reference an interface outside of its definition"); //} public void testAnnotatedPropertyOnInterface1() throws Exception { // For interfaces we must allow function definitions that don't have a // return statement, even though they declare a returned type. testTypes("/** @interface */ u.T = function() {};\n" + "/** @return {number} */ u.T.prototype.f = function() {};"); } public void testAnnotatedPropertyOnInterface2() throws Exception { testTypes("/** @interface */ u.T = function() {};\n" + "/** @return {number} */ u.T.prototype.f = function() { };"); } public void testAnnotatedPropertyOnInterface3() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @return {number} */ T.prototype.f = function() { };"); } public void testAnnotatedPropertyOnInterface4() throws Exception { testTypes( CLOSURE_DEFS + "/** @interface */ function T() {};\n" + "/** @return {number} */ T.prototype.f = goog.abstractMethod;"); } // TODO(user): If we want to support this syntax we have to warn about // missing annotations. //public void testWarnUnannotatedPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {}; u.T.prototype.x;", // "interface property x is not annotated"); //} // //public void testWarnUnannotatedPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {}; T.prototype.x;", // "interface property x is not annotated"); //} public void testWarnUnannotatedPropertyOnInterface5() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @desc x does something */u.T.prototype.x = function() {};"); } public void testWarnUnannotatedPropertyOnInterface6() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @desc x does something */T.prototype.x = function() {};"); } // TODO(user): If we want to support this syntax we have to warn about // the invalid type of the interface member. //public void testWarnDataPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @type {number} */u.T.prototype.x;", // "interface members can only be plain functions"); //} // //public void testWarnDataPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {};\n" + // "/** @type {number} */T.prototype.x;", // "interface members can only be plain functions"); //} public void testWarnDataPropertyOnInterface3() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @type {number} */u.T.prototype.x = 1;", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod"); } public void testWarnDataPropertyOnInterface4() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x = 1;", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod"); } // TODO(user): If we want to support this syntax we should warn about the // mismatching types in the two tests below. //public void testErrorMismatchingPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @param {Number} foo */u.T.prototype.x =\n" + // "/** @param {String} foo */function(foo) {};", // "found : \n" + // "required: "); //} // //public void testErrorMismatchingPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {};\n" + // "/** @return {number} */T.prototype.x =\n" + // "/** @return {string} */function() {};", // "found : \n" + // "required: "); //} // TODO(user): We should warn about this (bar is missing an annotation). We // probably don't want to warn about all missing parameter annotations, but // we should be as strict as possible regarding interfaces. //public void testErrorMismatchingPropertyOnInterface3() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @param {Number} foo */u.T.prototype.x =\n" + // "function(foo, bar) {};", // "found : \n" + // "required: "); //} public void testErrorMismatchingPropertyOnInterface4() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @param {Number} foo */u.T.prototype.x =\n" + "function() {};", "parameter foo does not appear in u.T.prototype.x's parameter list"); } public void testErrorMismatchingPropertyOnInterface5() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x = function() { };", "assignment to property x of T.prototype\n" + "found : function (): undefined\n" + "required: number"); } public void testErrorMismatchingPropertyOnInterface6() throws Exception { testClosureTypesMultipleWarnings( "/** @interface */ function T() {};\n" + "/** @return {number} */T.prototype.x = 1", Lists.newArrayList( "assignment to property x of T.prototype\n" + "found : number\n" + "required: function (this:T): number", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod")); } public void testInterfaceNonEmptyFunction() throws Exception { testTypes("/** @interface */ function T() {};\n" + "T.prototype.x = function() { return 'foo'; }", "interface member functions must have an empty body" ); } public void testDoubleNestedInterface() throws Exception { testTypes("/** @interface */ var I1 = function() {};\n" + "/** @interface */ I1.I2 = function() {};\n" + "/** @interface */ I1.I2.I3 = function() {};\n"); } public void testStaticDataPropertyOnNestedInterface() throws Exception { testTypes("/** @interface */ var I1 = function() {};\n" + "/** @interface */ I1.I2 = function() {};\n" + "/** @type {number} */ I1.I2.x = 1;\n"); } public void testInterfaceInstantiation() throws Exception { testTypes("/** @interface */var f = function(){}; new f", "cannot instantiate non-constructor"); } public void testPrototypeLoop() throws Exception { testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @constructor \n * @extends {T} */var T = function() {};" + "alert((new T).foo);", Lists.newArrayList( "Parse error. Cycle detected in inheritance chain of type T", "Could not resolve type in @extends tag of T")); } public void testDirectPrototypeAssign() throws Exception { // For now, we just ignore @type annotations on the prototype. testTypes( "/** @constructor */ function Foo() {}" + "/** @constructor */ function Bar() {}" + "/** @type {Array} */ Bar.prototype = new Foo()"); } // In all testResolutionViaRegistry* tests, since u is unknown, u.T can only // be resolved via the registry and not via properties. public void testResolutionViaRegistry1() throws Exception { testTypes("/** @constructor */ u.T = function() {};\n" + "/** @type {(number|string)} */ u.T.prototype.a;\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testResolutionViaRegistry2() throws Exception { testTypes( "/** @constructor */ u.T = function() {" + " this.a = 0; };\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testResolutionViaRegistry3() throws Exception { testTypes("/** @constructor */ u.T = function() {};\n" + "/** @type {(number|string)} */ u.T.prototype.a = 0;\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testResolutionViaRegistry4() throws Exception { testTypes("/** @constructor */ u.A = function() {};\n" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.A = function() {}\n;" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.B = function() {};\n" + "var ab = new u.A.B();\n" + "/** @type {!u.A} */ var a = ab;\n" + "/** @type {!u.A.A} */ var aa = ab;\n", "initializing variable\n" + "found : u.A.B\n" + "required: u.A.A"); } public void testResolutionViaRegistry5() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ u.T = function() {}; u.T"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof FunctionType); assertEquals("u.T", ((FunctionType) type).getInstanceType().getReferenceName()); } public void testGatherProperyWithoutAnnotation1() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ var T = function() {};" + "/** @type {!T} */var t; t.x; t;"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); Asserts.assertTypeCollectionEquals( Lists.newArrayList(objectType), registry.getTypesWithProperty("x")); } public void testGatherProperyWithoutAnnotation2() throws Exception { TypeCheckResult ns = parseAndTypeCheckWithScope("/** @type {!Object} */var t; t.x; t;"); Node n = ns.root; JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTypeEquals(type, OBJECT_TYPE); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); Asserts.assertTypeCollectionEquals( Lists.newArrayList(OBJECT_TYPE), registry.getTypesWithProperty("x")); } public void testFunctionMasksVariableBug() throws Exception { testTypes("var x = 4; var f = function x(b) { return b ? 1 : x(true); };", "function x masks variable (IE bug)"); } public void testDfa1() throws Exception { testTypes("var x = null;\n x = 1;\n /** @type number */ var y = x;"); } public void testDfa2() throws Exception { testTypes("function u() {}\n" + "/** @return {number} */ function f() {\nvar x = 'todo';\n" + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); } public void testDfa3() throws Exception { testTypes("function u() {}\n" + "/** @return {number} */ function f() {\n" + "/** @type {number|string} */ var x = 'todo';\n" + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); } public void testDfa4() throws Exception { testTypes("/** @param {Date?} d */ function f(d) {\n" + "if (!d) { return; }\n" + "/** @type {!Date} */ var e = d;\n}"); } public void testDfa5() throws Exception { testTypes("/** @return {string?} */ function u() {return 'a';}\n" + "/** @param {string?} x\n@return {string} */ function f(x) {\n" + "while (!x) { x = u(); }\nreturn x;\n}"); } public void testDfa6() throws Exception { testTypes("/** @return {Object?} */ function u() {return {};}\n" + "/** @param {Object?} x */ function f(x) {\n" + "while (x) { x = u(); if (!x) { x = u(); } }\n}"); } public void testDfa7() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @type {Date?} */ T.prototype.x = null;\n" + "/** @param {!T} t */ function f(t) {\n" + "if (!t.x) { return; }\n" + "/** @type {!Date} */ var e = t.x;\n}"); } public void testDfa8() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @type {number|string} */ T.prototype.x = '';\n" + "function u() {}\n" + "/** @param {!T} t\n@return {number} */ function f(t) {\n" + "if (u()) { t.x = 1; } else { t.x = 2; } return t.x;\n}"); } public void testDfa9() throws Exception { testTypes("function f() {\n/** @type {string?} */var x;\nx = null;\n" + "if (x == null) { return 0; } else { return 1; } }", "condition always evaluates to true\n" + "left : null\n" + "right: null"); } public void testDfa10() throws Exception { testTypes("/** @param {null} x */ function g(x) {}" + "/** @param {string?} x */function f(x) {\n" + "if (!x) { x = ''; }\n" + "if (g(x)) { return 0; } else { return 1; } }", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: null"); } public void testDfa11() throws Exception { testTypes("/** @param {string} opt_x\n@return {string} */\n" + "function f(opt_x) { if (!opt_x) { " + "throw new Error('x cannot be empty'); } return opt_x; }"); } public void testDfa12() throws Exception { testTypes("/** @param {string} x \n * @constructor \n */" + "var Bar = function(x) {};" + "/** @param {string} x */ function g(x) { return true; }" + "/** @param {string|number} opt_x */ " + "function f(opt_x) { " + " if (opt_x) { new Bar(g(opt_x) && 'x'); }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : (number|string)\n" + "required: string"); } public void testDfa13() throws Exception { testTypes( "/**\n" + " * @param {string} x \n" + " * @param {number} y \n" + " * @param {number} z \n" + " */" + "function g(x, y, z) {}" + "function f() { " + " var x = 'a'; g(x, x = 3, x);" + "}"); } public void testTypeInferenceWithCast1() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + "/**@return {number?}*/function g(x) {" + "var y = /**@type {number?}*/(u(x)); return f(y);}"); } public void testTypeInferenceWithCast2() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + "/**@return {number?}*/function g(x) {" + "var y; y = /**@type {number?}*/(u(x)); return f(y);}"); } public void testTypeInferenceWithCast3() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + "/**@return {number}*/function g(x) {" + "return /**@type {number}*/(u(x));}"); } public void testTypeInferenceWithCast4() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + "/**@return {number}*/function g(x) {" + "return /**@type {number}*/(u(x)) && 1;}"); } public void testTypeInferenceWithCast5() throws Exception { testTypes( "/** @param {number} x */ function foo(x) {}" + "/** @param {{length:*}} y */ function bar(y) {" + " /** @type {string} */ y.length;" + " foo(y.length);" + "}", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testTypeInferenceWithClosure1() throws Exception { testTypes( "/** @return {boolean} */" + "function f() {" + " /** @type {?string} */ var x = null;" + " function g() { x = 'y'; } g(); " + " return x == null;" + "}"); } public void testTypeInferenceWithClosure2() throws Exception { testTypes( "/** @return {boolean} */" + "function f() {" + " /** @type {?string} */ var x = null;" + " function g() { x = 'y'; } g(); " + " return x === 3;" + "}", "condition always evaluates to false\n" + "left : (null|string|undefined)\n" + "right: number"); } public void testForwardPropertyReference() throws Exception { testTypes("/** @constructor */ var Foo = function() { this.init(); };" + "/** @return {string} */" + "Foo.prototype.getString = function() {" + " return this.number_;" + "};" + "Foo.prototype.init = function() {" + " /** @type {number} */" + " this.number_ = 3;" + "};", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testNoForwardTypeDeclaration() throws Exception { testTypes( "/** @param {MyType} x */ function f(x) {}", "Bad type annotation. Unknown type MyType"); } public void testNoForwardTypeDeclarationAndNoBraces() throws Exception { testTypes("/** @return The result. */ function f() {}"); } public void testForwardTypeDeclaration1() throws Exception { testClosureTypes( // malformed addDependency calls shouldn't cause a crash "goog.addDependency();" + "goog.addDependency('y', [goog]);" + "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x \n * @return {number} */" + "function f(x) { return 3; }", null); } public void testForwardTypeDeclaration2() throws Exception { String f = "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */ function f(x) { }"; testClosureTypes(f, null); testClosureTypes(f + "f(3);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (MyType|null|undefined)"); } public void testForwardTypeDeclaration3() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */ function f(x) { return x; }" + "/** @constructor */ var MyType = function() {};" + "f(3);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (MyType|null|undefined)"); } public void testDuplicateTypeDef() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Bar = function() {};" + "/** @typedef {number} */ goog.Bar;", "variable goog.Bar redefined with type None, " + "original definition at [testcode]:1 " + "with type function (new:goog.Bar): undefined"); } public void testTypeDef1() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f(3);"); } public void testTypeDef2() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f('3');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testTypeDef3() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ var Bar;" + "/** @param {Bar} x */ function f(x) {}" + "f('3');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testCircularTypeDef() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number|Array.} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f(3); f([3]); f([[3]]);"); } public void testGetTypedPercent1() throws Exception { String js = "var id = function(x) { return x; }\n" + "var id2 = function(x) { return id(x); }"; assertEquals(50.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent2() throws Exception { String js = "var x = {}; x.y = 1;"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent3() throws Exception { String js = "var f = function(x) { x.a = x.b; }"; assertEquals(50.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent4() throws Exception { String js = "var n = {};\n /** @constructor */ n.T = function() {};\n" + "/** @type n.T */ var x = new n.T();"; assertEquals(100.0, getTypedPercent(js), 0.1); } private double getTypedPercent(String js) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); TypeCheck t = makeTypeCheck(); t.processForTesting(null, n); return t.getTypedPercent(); } private ObjectType getInstanceType(Node js1Node) { JSType type = js1Node.getFirstChild().getJSType(); assertNotNull(type); assertTrue(type instanceof FunctionType); FunctionType functionType = (FunctionType) type; assertTrue(functionType.isConstructor()); return functionType.getInstanceType(); } public void testPrototypePropertyReference() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @param {number} a */\n" + "Foo.prototype.bar = function(a){};\n" + "/** @param {Foo} f */\n" + "function baz(f) {\n" + " Foo.prototype.bar.call(f, 3);\n" + "}"); assertEquals(0, compiler.getErrorCount()); assertEquals(0, compiler.getWarningCount()); assertTrue(p.scope.getVar("Foo").getType() instanceof FunctionType); FunctionType fooType = (FunctionType) p.scope.getVar("Foo").getType(); assertEquals("function (this:Foo, number): undefined", fooType.getPrototype().getPropertyType("bar").toString()); } public void testResolvingNamedTypes() throws Exception { String js = "" + "/** @constructor */\n" + "var Foo = function() {}\n" + "/** @param {number} a */\n" + "Foo.prototype.foo = function(a) {\n" + " return this.baz().toString();\n" + "};\n" + "/** @return {Baz} */\n" + "Foo.prototype.baz = function() { return new Baz(); };\n" + "/** @constructor\n" + " * @extends Foo */\n" + "var Bar = function() {};" + "/** @constructor */\n" + "var Baz = function() {};"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testMissingProperty1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "Foo.prototype.baz = function() { this.a = 3; };"); } public void testMissingProperty2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "Foo.prototype.baz = function() { this.b = 3; };", "Property a never defined on Foo"); } public void testMissingProperty3() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "(new Foo).a = 3;"); } public void testMissingProperty4() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "(new Foo).b = 3;", "Property a never defined on Foo"); } public void testMissingProperty5() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "/** @constructor */ function Bar() { this.a = 3; };", "Property a never defined on Foo"); } public void testMissingProperty6() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "/** @constructor \n * @extends {Foo} */ " + "function Bar() { this.a = 3; };"); } public void testMissingProperty7() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { return obj.impossible; }", "Property impossible never defined on Object"); } public void testMissingProperty8() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { return typeof obj.impossible; }"); } public void testMissingProperty9() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { if (obj.impossible) { return true; } }"); } public void testMissingProperty10() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { while (obj.impossible) { return true; } }"); } public void testMissingProperty11() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { for (;obj.impossible;) { return true; } }"); } public void testMissingProperty12() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { do { } while (obj.impossible); }"); } public void testMissingProperty13() throws Exception { testTypes( "var goog = {}; goog.isDef = function(x) { return false; };" + "/** @param {Object} obj */" + "function foo(obj) { return goog.isDef(obj.impossible); }"); } public void testMissingProperty14() throws Exception { testTypes( "var goog = {}; goog.isDef = function(x) { return false; };" + "/** @param {Object} obj */" + "function foo(obj) { return goog.isNull(obj.impossible); }", "Property isNull never defined on goog"); } public void testMissingProperty15() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.foo) { x.foo(); } }"); } public void testMissingProperty16() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { x.foo(); if (x.foo) {} }", "Property foo never defined on Object"); } public void testMissingProperty17() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (typeof x.foo == 'function') { x.foo(); } }"); } public void testMissingProperty18() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.foo instanceof Function) { x.foo(); } }"); } public void testMissingProperty19() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.bar) { if (x.foo) {} } else { x.foo(); } }", "Property foo never defined on Object"); } public void testMissingProperty21() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { x.foo && x.foo(); }"); } public void testMissingProperty22() throws Exception { testTypes( "/** @param {Object} x \n * @return {boolean} */" + "function f(x) { return x.foo ? x.foo() : true; }"); } public void testMissingProperty23() throws Exception { testTypes( "function f(x) { x.impossible(); }", "Property impossible never defined on x"); } public void testMissingProperty24() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MissingType'], []);" + "/** @param {MissingType} x */" + "function f(x) { x.impossible(); }", null); } public void testMissingProperty25() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "Foo.prototype.bar = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "(new FooAlias()).bar();"); } public void testMissingProperty26() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "FooAlias.prototype.bar = function() {};" + "(new Foo()).bar();"); } public void testMissingProperty27() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MissingType'], []);" + "/** @param {?MissingType} x */" + "function f(x) {" + " for (var parent = x; parent; parent = parent.getParent()) {}" + "}", null); } public void testMissingProperty28() throws Exception { testTypes( "function f(obj) {" + " /** @type {*} */ obj.foo;" + " return obj.foo;" + "}"); testTypes( "function f(obj) {" + " /** @type {*} */ obj.foo;" + " return obj.foox;" + "}", "Property foox never defined on obj"); } public void testMissingProperty29() throws Exception { // This used to emit a warning. testTypes( // externs "/** @constructor */ var Foo;" + "Foo.prototype.opera;" + "Foo.prototype.opera.postError;", "", null, false); } public void testDeclaredNativeTypeEquality() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Object() {};"); assertEquals(registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), n.getFirstChild().getJSType()); } public void testUndefinedVar() throws Exception { Node n = parseAndTypeCheck("var undefined;"); assertEquals(registry.getNativeType(JSTypeNative.VOID_TYPE), n.getFirstChild().getFirstChild().getJSType()); } public void testFlowScopeBug1() throws Exception { Node n = parseAndTypeCheck("/** @param {number} a \n" + "* @param {number} b */\n" + "function f(a, b) {\n" + "/** @type number */" + "var i = 0;" + "for (; (i + a) < b; ++i) {}}"); // check the type of the add node for i + f assertEquals(registry.getNativeType(JSTypeNative.NUMBER_TYPE), n.getFirstChild().getLastChild().getLastChild().getFirstChild() .getNext().getFirstChild().getJSType()); } public void testFlowScopeBug2() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Foo() {};\n" + "Foo.prototype.hi = false;" + "function foo(a, b) {\n" + " /** @type Array */" + " var arr;" + " /** @type number */" + " var iter;" + " for (iter = 0; iter < arr.length; ++ iter) {" + " /** @type Foo */" + " var afoo = arr[iter];" + " afoo;" + " }" + "}"); // check the type of afoo when referenced assertTypeEquals(registry.createOptionalType( registry.createNullableType(registry.getType("Foo"))), n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild().getJSType()); } public void testAddSingletonGetter() { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {};\n" + "goog.addSingletonGetter(Foo);"); ObjectType o = (ObjectType) n.getFirstChild().getJSType(); assertEquals("function (): Foo", o.getPropertyType("getInstance").toString()); assertEquals("Foo", o.getPropertyType("instance_").toString()); } public void testTypeCheckStandaloneAST() throws Exception { Node n = compiler.parseTestCode("function Foo() { }"); typeCheck(n); MemoizedScopeCreator scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); Scope topScope = scopeCreator.createScope(n, null); Node second = compiler.parseTestCode("new Foo"); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, second); externAndJsRoot.setIsSyntheticBlock(true); new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry, topScope, scopeCreator, CheckLevel.WARNING, CheckLevel.OFF) .process(null, second); assertEquals(1, compiler.getWarningCount()); assertEquals("cannot instantiate non-constructor", compiler.getWarnings()[0].description); } private void checkObjectType(ObjectType objectType, String propertyName, JSType expectedType) { assertTrue("Expected " + objectType.getReferenceName() + " to have property " + propertyName, objectType.hasProperty(propertyName)); assertTypeEquals("Expected " + objectType.getReferenceName() + "'s property " + propertyName + " to have type " + expectedType, expectedType, objectType.getPropertyType(propertyName)); } private void testTypes(String js) throws Exception { testTypes(js, (String) null); } private void testTypes(String js, String description) throws Exception { testTypes(js, description, false); } private void testTypes(String js, DiagnosticType type) throws Exception { testTypes(js, type.format(), false); } private void testClosureTypes(String js, String description) throws Exception { testClosureTypesMultipleWarnings(js, description == null ? null : Lists.newArrayList(description)); } private void testClosureTypesMultipleWarnings( String js, List descriptions) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); // For processing goog.addDependency for forward typedefs. new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) .process(null, n); CodingConvention convention = compiler.getCodingConvention(); new TypeCheck(compiler, new ClosureReverseAbstractInterpreter( convention, registry).append( new SemanticReverseAbstractInterpreter( convention, registry)) .getFirst(), registry) .processForTesting(null, n); assertEquals(0, compiler.getErrorCount()); if (descriptions == null) { assertEquals( "unexpected warning(s) : " + Joiner.on(", ").join(compiler.getWarnings()), 0, compiler.getWarningCount()); } else { assertEquals(descriptions.size(), compiler.getWarningCount()); Set actualWarningDescriptions = Sets.newHashSet(); for (int i = 0; i < descriptions.size(); i++) { actualWarningDescriptions.add(compiler.getWarnings()[i].description); } assertEquals( Sets.newHashSet(descriptions), actualWarningDescriptions); } } void testTypes(String js, String description, boolean isError) throws Exception { testTypes(DEFAULT_EXTERNS, js, description, isError); } void testTypes(String externs, String js, String description, boolean isError) throws Exception { parseAndTypeCheck(externs, js); JSError[] errors = compiler.getErrors(); if (description != null && isError) { assertTrue("expected an error", errors.length > 0); assertEquals(description, errors[0].description); errors = Arrays.asList(errors).subList(1, errors.length).toArray( new JSError[errors.length - 1]); } if (errors.length > 0) { fail("unexpected error(s):\n" + Joiner.on("\n").join(errors)); } JSError[] warnings = compiler.getWarnings(); if (description != null && !isError) { assertTrue("expected a warning", warnings.length > 0); assertEquals(description, warnings[0].description); warnings = Arrays.asList(warnings).subList(1, warnings.length).toArray( new JSError[warnings.length - 1]); } if (warnings.length > 0) { fail("unexpected warnings(s):\n" + Joiner.on("\n").join(warnings)); } } /** * Parses and type checks the JavaScript code. */ private Node parseAndTypeCheck(String js) { return parseAndTypeCheck(DEFAULT_EXTERNS, js); } private Node parseAndTypeCheck(String externs, String js) { return parseAndTypeCheckWithScope(externs, js).root; } /** * Parses and type checks the JavaScript code and returns the Scope used * whilst type checking. */ private TypeCheckResult parseAndTypeCheckWithScope(String js) { return parseAndTypeCheckWithScope(DEFAULT_EXTERNS, js); } private TypeCheckResult parseAndTypeCheckWithScope( String externs, String js) { compiler.init( Lists.newArrayList(SourceFile.fromCode("[externs]", externs)), Lists.newArrayList(SourceFile.fromCode("[testcode]", js)), compiler.getOptions()); Node n = compiler.getInput(new InputId("[testcode]")).getAstRoot(compiler); Node externsNode = compiler.getInput(new InputId("[externs]")) .getAstRoot(compiler); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); Scope s = makeTypeCheck().processForTesting(externsNode, n); return new TypeCheckResult(n, s); } private Node typeCheck(Node n) { Node externsNode = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); makeTypeCheck().processForTesting(null, n); return n; } private TypeCheck makeTypeCheck() { return new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry); } void testTypes(String js, String[] warnings) throws Exception { Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Node externsNode = new Node(Token.BLOCK); // create a common parent for the externs and source roots new Node(Token.BLOCK, externsNode, n); makeTypeCheck().processForTesting(null, n); assertEquals(0, compiler.getErrorCount()); if (warnings != null) { assertEquals(warnings.length, compiler.getWarningCount()); JSError[] messages = compiler.getWarnings(); for (int i = 0; i < warnings.length && i < compiler.getWarningCount(); i++) { assertEquals(warnings[i], messages[i].description); } } else { assertEquals(0, compiler.getWarningCount()); } } String suppressMissingProperty(String ... props) { String result = "function dummy(x) { "; for (String prop : props) { result += "x." + prop + " = 3;"; } return result + "}"; } private static class TypeCheckResult { private final Node root; private final Scope scope; private TypeCheckResult(Node root, Scope scope) { this.root = root; this.scope = scope; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CallGraphTest.java0000644000175000017500000011152512115204405026371 0ustar apoapo/* * 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.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CallGraph.Callsite; import com.google.javascript.jscomp.CallGraph.Function; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Set; /** * Tests for CallGraph. * * @author dcc@google.com (Devin Coughlin) */ public class CallGraphTest extends CompilerTestCase { private CallGraph currentProcessor; private boolean createForwardCallGraph; private boolean createBackwardCallGraph; @Override protected CompilerPass getProcessor(Compiler compiler) { // We store the new callgraph so it can be tested later currentProcessor = new CallGraph(compiler, createForwardCallGraph, createBackwardCallGraph); return currentProcessor; } static final String SHARED_EXTERNS = "var ExternalFunction = function(a) {}\n" + "var externalnamespace = {}\n" + "externalnamespace.prop = function(){};\n"; public void testGetFunctionForAstNode() { String source = "function A() {};\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); Node functionANode = functionA.getAstNode(); assertEquals(functionA, callgraph.getFunctionForAstNode(functionANode)); } public void testGetAllFunctions() { String source = "function A() {}\n" + "var B = function() {\n" + "(function C(){A()})()\n" + "};\n"; CallGraph callgraph = compileAndRunForward(source); Collection functions = callgraph.getAllFunctions(); // 3 Functions, plus one for the main function assertEquals(4, functions.size()); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); CallGraph.Function functionB = callgraph.getUniqueFunctionWithName("B"); CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); assertEquals("A", NodeUtil.getFunctionName(functionA.getAstNode())); assertEquals("B", NodeUtil.getFunctionName(functionB.getAstNode())); assertEquals("C", NodeUtil.getFunctionName(functionC.getAstNode())); } public void testGetAllFunctionsContainsNormalFunction() { String source = "function A(){}\n"; CallGraph callgraph = compileAndRunForward(source); Collection allFunctions = callgraph.getAllFunctions(); // 2 functions: one for A() and one for the main function assertEquals(2, allFunctions.size()); assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); assertTrue(allFunctions.contains(callgraph.getMainFunction())); } public void testGetAllFunctionsContainsVarAssignedLiteralFunction() { String source = "var A = function(){}\n"; CallGraph callgraph = compileAndRunForward(source); Collection allFunctions = callgraph.getAllFunctions(); // 2 functions: one for A() and one for the global function assertEquals(2, allFunctions.size()); Function functionA = callgraph.getUniqueFunctionWithName("A"); assertTrue(allFunctions.contains(functionA)); assertTrue(allFunctions.contains(callgraph.getMainFunction())); } public void testGetAllFunctionsContainsNamespaceAssignedLiteralFunction() { String source = "var namespace = {};\n" + "namespace.A = function(){};\n"; CallGraph callgraph = compileAndRunForward(source); Collection allFunctions = callgraph.getAllFunctions(); // 2 functions: one for namespace.A() and one for the global function assertEquals(2, allFunctions.size()); assertTrue(allFunctions.contains( callgraph.getUniqueFunctionWithName("namespace.A"))); assertTrue(allFunctions.contains(callgraph.getMainFunction())); } public void testGetAllFunctionsContainsLocalFunction() { String source = "var A = function(){var B = function(){}};\n"; CallGraph callgraph = compileAndRunForward(source); Collection allFunctions = callgraph.getAllFunctions(); // 3 functions: one for A, B, and global function assertEquals(3, allFunctions.size()); assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("B"))); assertTrue(allFunctions.contains(callgraph.getMainFunction())); } public void testGetAllFunctionsContainsAnonymousFunction() { String source = "var A = function(){(function(){})();};\n"; CallGraph callgraph = compileAndRunForward(source); Collection allFunctions = callgraph.getAllFunctions(); // 3 functions: A, anonymous, and global function assertEquals(3, allFunctions.size()); assertTrue(allFunctions.contains(callgraph.getUniqueFunctionWithName("A"))); assertTrue( allFunctions.contains(callgraph.getUniqueFunctionWithName(null))); assertTrue(allFunctions.contains(callgraph.getMainFunction())); } public void testGetCallsiteForAstNode() { String source = "function A() {B()};\n" + "function B(){};\n"; CallGraph callgraph = compileAndRunBackward(source); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); CallGraph.Callsite callToB = functionA.getCallsitesInFunction().iterator().next(); Node callsiteNode = callToB.getAstNode(); assertEquals(callToB, callgraph.getCallsiteForAstNode(callsiteNode)); } public void testFunctionGetCallsites() { String source = "function A() {var x; x()}\n" + "var B = function() {\n" + "(function C(){A()})()\n" + "};\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); Collection callsitesInA = functionA.getCallsitesInFunction(); assertEquals(1, callsitesInA.size()); CallGraph.Callsite firstCallsiteInA = callsitesInA.iterator().next(); Node aTargetExpression = firstCallsiteInA.getAstNode().getFirstChild(); assertEquals(Token.NAME, aTargetExpression.getType()); assertEquals("x", aTargetExpression.getString()); CallGraph.Function functionB = callgraph.getUniqueFunctionWithName("B"); Collection callsitesInB = functionB.getCallsitesInFunction(); assertEquals(1, callsitesInB.size()); CallGraph.Callsite firstCallsiteInB = callsitesInB.iterator().next(); Node bTargetExpression = firstCallsiteInB.getAstNode().getFirstChild(); assertEquals(Token.FUNCTION, bTargetExpression.getType()); assertEquals("C", NodeUtil.getFunctionName(bTargetExpression)); CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); Collection callsitesInC = functionC.getCallsitesInFunction(); assertEquals(1, callsitesInC.size()); CallGraph.Callsite firstCallsiteInC = callsitesInC.iterator().next(); Node cTargetExpression = firstCallsiteInC.getAstNode().getFirstChild(); assertEquals(Token.NAME, aTargetExpression.getType()); assertEquals("A", cTargetExpression.getString()); } public void testFindNewInFunction() { String source = "function A() {var x; new x(1,2)}\n;"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); Collection callsitesInA = functionA.getCallsitesInFunction(); assertEquals(1, callsitesInA.size()); Node callsiteInA = callsitesInA.iterator().next().getAstNode(); assertEquals(Token.NEW, callsiteInA.getType()); Node aTargetExpression = callsiteInA.getFirstChild(); assertEquals(Token.NAME, aTargetExpression.getType()); assertEquals("x", aTargetExpression.getString()); } public void testFindCallsiteTargetGlobalName() { String source = "function A() {}\n" + "function B() {}\n" + "function C() {A()}\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); assertNotNull(functionC); CallGraph.Callsite callsiteInC = functionC.getCallsitesInFunction().iterator().next(); assertNotNull(callsiteInC); Collection targetsOfCallsiteInC = callsiteInC.getPossibleTargets(); assertNotNull(targetsOfCallsiteInC); assertEquals(1, targetsOfCallsiteInC.size()); } public void testFindCallsiteTargetAliasedGlobalProperty() { String source = "var namespace = {};\n" + "namespace.A = function() {};\n" + "function C() {namespace.A()}\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); assertNotNull(functionC); CallGraph.Callsite callsiteInC = functionC.getCallsitesInFunction().iterator().next(); assertNotNull(callsiteInC); Collection targetsOfCallsiteInC = callsiteInC.getPossibleTargets(); assertNotNull(targetsOfCallsiteInC); assertEquals(1, targetsOfCallsiteInC.size()); } public void testGetAllCallsitesContainsMultiple() { String source = "function A() {}\n" + "var B = function() {\n" + "(function (){A()})()\n" + "};\n" + "A();\n" + "B();\n"; CallGraph callgraph = compileAndRunBackward(source); Collection allCallsites = callgraph.getAllCallsites(); assertEquals(4, allCallsites.size()); } public void testGetAllCallsitesContainsGlobalSite() { String source = "function A(){}\n" + "A();\n"; CallGraph callgraph = compileAndRunBackward(source); Collection allCallsites = callgraph.getAllCallsites(); assertEquals(1, allCallsites.size()); Node callsiteNode = allCallsites.iterator().next().getAstNode(); assertEquals(Token.CALL, callsiteNode.getType()); assertEquals("A", callsiteNode.getFirstChild().getString()); } public void testGetAllCallsitesContainsLocalSite() { String source = "function A(){}\n" + "function B(){A();}\n"; CallGraph callgraph = compileAndRunBackward(source); Collection allCallsites = callgraph.getAllCallsites(); assertEquals(1, allCallsites.size()); Node callsiteNode = allCallsites.iterator().next().getAstNode(); assertEquals(Token.CALL, callsiteNode.getType()); assertEquals("A", callsiteNode.getFirstChild().getString()); } public void testGetAllCallsitesContainsLiteralSite() { String source = "function A(){(function(a){})();}\n"; CallGraph callgraph = compileAndRunBackward(source); Collection allCallsites = callgraph.getAllCallsites(); assertEquals(1, allCallsites.size()); Node callsiteNode = allCallsites.iterator().next().getAstNode(); assertEquals(Token.CALL, callsiteNode.getType()); assertEquals(Token.FUNCTION, callsiteNode.getFirstChild().getType()); } public void testGetAllCallsitesContainsConstructorSite() { String source = "function A(){}\n" + "function B(){new A();}\n"; CallGraph callgraph = compileAndRunBackward(source); Collection allCallsites = callgraph.getAllCallsites(); assertEquals(1, allCallsites.size()); Node callsiteNode = allCallsites.iterator().next().getAstNode(); assertEquals(Token.NEW, callsiteNode.getType()); assertEquals("A", callsiteNode.getFirstChild().getString()); } /** * Test getting a backward directed graph on a backward call graph * and propagating over it. */ public void testGetDirectedGraph_backwardOnBackward() { // For this test we create a simple callback that, when applied until a // fixedpoint, computes whether a function is "poisoned" by an extern. // A function is poisoned if it calls an extern or if it calls another // poisoned function. String source = "function A(){};\n" + "function B(){ExternalFunction(6); C(); D();}\n" + "function C(){B(); A();};\n" + "function D(){A();};\n" + "function E(){C()};\n" + "A();\n"; CallGraph callgraph = compileAndRunBackward(source); final Set poisonedFunctions = Sets.newHashSet(); // Set up initial poisoned functions for (Callsite callsite : callgraph.getAllCallsites()) { if (callsite.hasExternTarget()) { poisonedFunctions.add(callsite.getContainingFunction()); } } // Propagate poison from callees to callers EdgeCallback edgeCallback = new EdgeCallback() { @Override public boolean traverseEdge(Function callee, Callsite callsite, Function caller) { boolean changed; if (poisonedFunctions.contains(callee)) { changed = poisonedFunctions.add(caller); // Returns true if added } else { changed = false; } return changed; } }; FixedPointGraphTraversal.newTraversal(edgeCallback) .computeFixedPoint(callgraph.getBackwardDirectedGraph()); // We expect B, C, and E to poisoned. assertEquals(3, poisonedFunctions.size()); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("B"))); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("C"))); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("E"))); } /** * Test getting a backward directed graph on a forward call graph * and propagating over it. */ public void testGetDirectedGraph_backwardOnForward() { // For this test we create a simple callback that, when applied until a // fixedpoint, computes whether a function is "poisoned" by an extern. // A function is poisoned if it calls an extern or if it calls another // poisoned function. String source = "function A(){};\n" + "function B(){ExternalFunction(6); C(); D();}\n" + "function C(){B(); A();};\n" + "function D(){A();};\n" + "function E(){C()};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); final Set poisonedFunctions = Sets.newHashSet(); // Set up initial poisoned functions for (Callsite callsite : callgraph.getAllCallsites()) { if (callsite.hasExternTarget()) { poisonedFunctions.add(callsite.getContainingFunction()); } } // Propagate poison from callees to callers EdgeCallback edgeCallback = new EdgeCallback() { @Override public boolean traverseEdge(Function callee, Callsite callsite, Function caller) { boolean changed; if (poisonedFunctions.contains(callee)) { changed = poisonedFunctions.add(caller); // Returns true if added } else { changed = false; } return changed; } }; FixedPointGraphTraversal.newTraversal(edgeCallback) .computeFixedPoint(callgraph.getBackwardDirectedGraph()); // We expect B, C, and E to poisoned. assertEquals(3, poisonedFunctions.size()); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("B"))); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("C"))); assertTrue(poisonedFunctions.contains( callgraph.getUniqueFunctionWithName("E"))); } /** * Test getting a forward directed graph on a forward call graph * and propagating over it. */ public void testGetDirectedGraph_forwardOnForward() { // For this test we create a simple callback that, when applied until a // fixedpoint, computes whether a function is reachable from an initial // set of "root" nodes. String source = "function A(){B()};\n" + "function B(){C();D()}\n" + "function C(){B()};\n" + "function D(){};\n" + "function E(){C()};\n" + "function X(){Y()};\n" + "function Y(){Z()};\n" + "function Z(){};" + "B();\n"; CallGraph callgraph = compileAndRunForward(source); final Set reachableFunctions = Sets.newHashSet(); // We assume the main function and X are our roots reachableFunctions.add(callgraph.getMainFunction()); reachableFunctions.add(callgraph.getUniqueFunctionWithName("X")); // Propagate reachability from callers to callees EdgeCallback edgeCallback = new EdgeCallback() { @Override public boolean traverseEdge(Function caller, Callsite callsite, Function callee) { boolean changed; if (reachableFunctions.contains(caller)) { changed = reachableFunctions.add(callee); // Returns true if added } else { changed = false; } return changed; } }; FixedPointGraphTraversal.newTraversal(edgeCallback) .computeFixedPoint(callgraph.getForwardDirectedGraph()); // We expect B, C, D, X, Y, Z and the main function should be reachable. // A and E should not be reachable. assertEquals(7, reachableFunctions.size()); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("B"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("C"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("D"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("X"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("Y"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("Z"))); assertTrue(reachableFunctions.contains( callgraph.getMainFunction())); assertFalse(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("A"))); assertFalse(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("E"))); } /** * Test getting a backward directed graph on a forward call graph * and propagating over it. */ public void testGetDirectedGraph_forwardOnBackward() { // For this test we create a simple callback that, when applied until a // fixedpoint, computes whether a function is reachable from an initial // set of "root" nodes. String source = "function A(){B()};\n" + "function B(){C();D()}\n" + "function C(){B()};\n" + "function D(){};\n" + "function E(){C()};\n" + "function X(){Y()};\n" + "function Y(){Z()};\n" + "function Z(){};" + "B();\n"; CallGraph callgraph = compileAndRunBackward(source); final Set reachableFunctions = Sets.newHashSet(); // We assume the main function and X are our roots reachableFunctions.add(callgraph.getMainFunction()); reachableFunctions.add(callgraph.getUniqueFunctionWithName("X")); // Propagate reachability from callers to callees EdgeCallback edgeCallback = new EdgeCallback() { @Override public boolean traverseEdge(Function caller, Callsite callsite, Function callee) { boolean changed; if (reachableFunctions.contains(caller)) { changed = reachableFunctions.add(callee); // Returns true if added } else { changed = false; } return changed; } }; FixedPointGraphTraversal.newTraversal(edgeCallback) .computeFixedPoint(callgraph.getForwardDirectedGraph()); // We expect B, C, D, X, Y, Z and the main function should be reachable. // A and E should not be reachable. assertEquals(7, reachableFunctions.size()); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("B"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("C"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("D"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("X"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("Y"))); assertTrue(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("Z"))); assertTrue(reachableFunctions.contains( callgraph.getMainFunction())); assertFalse(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("A"))); assertFalse(reachableFunctions.contains( callgraph.getUniqueFunctionWithName("E"))); } public void testFunctionIsMain() { String source = "function A(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function mainFunction = callgraph.getMainFunction(); assertTrue(mainFunction.isMain()); assertNotNull(mainFunction.getBodyNode()); assertTrue(mainFunction.getBodyNode().isBlock()); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); assertFalse(functionA.isMain()); } public void testFunctionGetAstNode() { String source = "function A(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function mainFunction = callgraph.getMainFunction(); // Main function's AST node should be the global block assertTrue(mainFunction.getAstNode().isBlock()); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); // Regular function's AST node should be the function for A assertTrue(functionA.getAstNode().isFunction()); assertEquals("A", NodeUtil.getFunctionName(functionA.getAstNode())); } public void testFunctionGetBodyNode() { String source = "function A(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function mainFunction = callgraph.getMainFunction(); // Main function's body node should its AST node assertEquals(mainFunction.getAstNode(), mainFunction.getBodyNode()); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); // Regular function's body node should be the block for A assertTrue(functionA.getBodyNode().isBlock()); assertEquals(NodeUtil.getFunctionBody(functionA.getAstNode()), functionA.getBodyNode()); } public void testFunctionGetName() { String source = "function A(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); CallGraph.Function mainFunction = callgraph.getMainFunction(); // Main function's name should be CallGraph.MAIN_FUNCTION_NAME assertEquals(CallGraph.MAIN_FUNCTION_NAME, mainFunction.getName()); CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); // Regular function's name should be its name assertEquals(NodeUtil.getFunctionName(functionA.getAstNode()), functionA.getName()); } public void testFunctionGetCallsitesInFunction() { String source = "function A(){};\n" + "function B(){A()};\n" + "A();\n" + "B();\n"; CallGraph callgraph = compileAndRunForward(source); // Main function calls A and B CallGraph.Function mainFunction = callgraph.getMainFunction(); List callsiteNamesInMain = getCallsiteTargetNames(mainFunction.getCallsitesInFunction()); assertEquals(2, callsiteNamesInMain.size()); assertTrue(callsiteNamesInMain.contains("A")); assertTrue(callsiteNamesInMain.contains("B")); // A calls no functions CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); assertEquals(0, functionA.getCallsitesInFunction().size()); // B calls A CallGraph.Function functionB = callgraph.getUniqueFunctionWithName("B"); List callsiteNamesInB = getCallsiteTargetNames(functionB.getCallsitesInFunction()); assertEquals(1, callsiteNamesInB.size()); assertTrue(callsiteNamesInMain.contains("A")); } public void testFunctionGetCallsitesInFunction_ignoreInnerFunction() { String source = "function A(){var B = function(){C();}};\n" + "function C(){};\n"; CallGraph callgraph = compileAndRunForward(source); // A calls no functions (and especially not C) CallGraph.Function functionA = callgraph.getUniqueFunctionWithName("A"); assertEquals(0, functionA.getCallsitesInFunction().size()); } public void testFunctionGetCallsitesPossiblyTargetingFunction() { String source = "function A(){B()};\n" + "function B(){C();C();};\n" + "function C(){C()};\n" + "A();\n"; CallGraph callgraph = compileAndRunBackward(source); Function main = callgraph.getMainFunction(); Function functionA = callgraph.getUniqueFunctionWithName("A"); Function functionB = callgraph.getUniqueFunctionWithName("B"); Function functionC = callgraph.getUniqueFunctionWithName("C"); assertEquals(0, main.getCallsitesPossiblyTargetingFunction().size()); Collection callsitesTargetingA = functionA.getCallsitesPossiblyTargetingFunction(); // A is called only from the main function assertEquals(1, callsitesTargetingA.size()); assertEquals(main, callsitesTargetingA.iterator().next().getContainingFunction()); Collection callsitesTargetingB = functionB.getCallsitesPossiblyTargetingFunction(); // B is called only from A assertEquals(1, callsitesTargetingB.size()); assertEquals(functionA, callsitesTargetingB.iterator().next().getContainingFunction()); Collection callsitesTargetingC = functionC.getCallsitesPossiblyTargetingFunction(); // C is called 3 times: twice from B and once from C assertEquals(3, callsitesTargetingC.size()); Collection expectedFunctionsCallingC = Sets.newHashSet(functionB.getCallsitesInFunction()); expectedFunctionsCallingC.addAll(functionC.getCallsitesInFunction()); assertTrue(callsitesTargetingC.containsAll(expectedFunctionsCallingC)); } public void testFunctionGetCallsitesInFunction_newIsCallsite() { String source = "function A(){};\n" + "function C(){new A()};\n"; CallGraph callgraph = compileAndRunForward(source); // The call to new A() in C() should count as a callsite CallGraph.Function functionC = callgraph.getUniqueFunctionWithName("C"); assertEquals(1, functionC.getCallsitesInFunction().size()); } 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();"; compileAndRunForward(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();"; compileAndRunForward(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();"; compileAndRunForward(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"; compileAndRunForward(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"; compileAndRunForward(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"; CallGraph callGraph = compileAndRunForward(source); Function functionA = callGraph.getUniqueFunctionWithName("A"); Function functionB = callGraph.getUniqueFunctionWithName("B"); Function functionC = callGraph.getUniqueFunctionWithName("C"); assertTrue(functionA.isExposedToCallOrApply()); assertTrue(functionB.isExposedToCallOrApply()); assertFalse(functionC.isExposedToCallOrApply()); } public void testCallsiteGetAstNode() { String source = "function A(){B()};\n" + "function B(){};\n"; CallGraph callgraph = compileAndRunForward(source); Function functionA = callgraph.getUniqueFunctionWithName("A"); Callsite callToB = functionA.getCallsitesInFunction().iterator().next(); assertTrue(callToB.getAstNode().isCall()); } public void testCallsiteGetContainingFunction() { String source = "function A(){B()};\n" + "function B(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); Function mainFunction = callgraph.getMainFunction(); Callsite callToA = mainFunction.getCallsitesInFunction().iterator().next(); assertEquals(mainFunction, callToA.getContainingFunction()); Function functionA = callgraph.getUniqueFunctionWithName("A"); Callsite callToB = functionA.getCallsitesInFunction().iterator().next(); assertEquals(functionA, callToB.getContainingFunction()); } public void testCallsiteGetKnownTargets() { String source = "function A(){B()};\n" + "function B(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); Function mainFunction = callgraph.getMainFunction(); Function functionA = callgraph.getUniqueFunctionWithName("A"); Function functionB = callgraph.getUniqueFunctionWithName("B"); Callsite callInMain = mainFunction.getCallsitesInFunction().iterator() .next(); Collection targetsOfCallInMain = callInMain.getPossibleTargets(); assertEquals(1, targetsOfCallInMain.size()); assertTrue(targetsOfCallInMain.contains(functionA)); Callsite callInA = functionA.getCallsitesInFunction().iterator().next(); Collection targetsOfCallInA = callInA.getPossibleTargets(); assertTrue(targetsOfCallInA.contains(functionB)); } public void testCallsiteHasUnknownTarget() { String source = "var A = externalnamespace.prop;\n" + "function B(){A();};\n" + "B();\n"; CallGraph callgraph = compileAndRunForward(source); Function mainFunction = callgraph.getMainFunction(); Function functionB = callgraph.getUniqueFunctionWithName("B"); Callsite callInMain = mainFunction.getCallsitesInFunction().iterator().next(); // B()'s target function is known, and it is functionB assertFalse(callInMain.hasUnknownTarget()); assertEquals("B", callInMain.getAstNode().getFirstChild().getString()); Callsite callInB = functionB.getCallsitesInFunction().iterator().next(); // A() has an unknown target and no known targets assertTrue(callInB.hasUnknownTarget()); assertEquals(0, callInB.getPossibleTargets().size()); } public void testCallsiteHasExternTarget() { String source = "var A = function(){}\n" + "function B(){ExternalFunction(6);};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); Function mainFunction = callgraph.getMainFunction(); Function functionB = callgraph.getUniqueFunctionWithName("B"); Callsite callInMain = mainFunction.getCallsitesInFunction().iterator().next(); // A()'s target function is not an extern assertFalse(callInMain.hasExternTarget()); Callsite callInB = functionB.getCallsitesInFunction().iterator().next(); assertEquals("ExternalFunction", callInB.getAstNode().getFirstChild().getString()); // ExternalFunction(6) is a call to an extern function assertTrue(callInB.hasExternTarget()); assertEquals(0, callInB.getPossibleTargets().size()); } public void testThrowForBackwardOpOnForwardGraph() { String source = "function A(){B()};\n" + "function B(){C();C();};\n" + "function C(){C()};\n" + "A();\n"; CallGraph callgraph = compileAndRunForward(source); Function functionA = callgraph.getUniqueFunctionWithName("A"); UnsupportedOperationException caughtException = null; try { functionA.getCallsitesPossiblyTargetingFunction(); } catch (UnsupportedOperationException e) { caughtException = e; } assertNotNull(caughtException); } public void testThrowForForwardOpOnBackwardGraph() { String source = "function A(){B()};\n" + "function B(){};\n" + "A();\n"; CallGraph callgraph = compileAndRunBackward(source); Function mainFunction = callgraph.getMainFunction(); Callsite callInMain = mainFunction.getCallsitesInFunction().iterator() .next(); try { callInMain.getPossibleTargets(); } catch (UnsupportedOperationException e) { return; } fail(); } /** * Helper function that, given a collection of callsites, returns a * collection of the names of the target expression nodes, e.g. * if the callsites are [A(), B.b()], the collection returned is * ["A", "B"]. * * This makes it easier to test methods that return collections of callsites. * * An exception is thrown if the callsite target is not a simple name * (e.g. "a.bar()"). */ private List getCallsiteTargetNames(Collection callsites) { List result = Lists.newArrayList(); for (Callsite callsite : callsites) { Node targetExpressionNode = callsite.getAstNode().getFirstChild(); if (targetExpressionNode.isName()) { result.add(targetExpressionNode.getString()); } else { throw new IllegalStateException("Called getCallsiteTargetNames() on " + "a complex callsite."); } } return result; } private void assertFunctionAliased(boolean aliased, String name) { Function function = currentProcessor.getUniqueFunctionWithName(name); assertEquals(aliased, function.isAliased()); } private CallGraph compileAndRunBackward(String js) { return compileAndRun(SHARED_EXTERNS, js, false, true); } private CallGraph compileAndRunForward(String js) { return compileAndRun(SHARED_EXTERNS, js, true, false); } private CallGraph compileAndRun(String externs, String js, boolean forward, boolean backward) { createBackwardCallGraph = backward; createForwardCallGraph = forward; testSame(externs, js, null); return currentProcessor; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ClosureCodingConventionTest.java0000644000175000017500000002207512115204405031340 0ustar apoapo/* * Copyright 2007 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.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.CodingConvention.SubclassType; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import junit.framework.TestCase; /** * Test class for {@link GoogleCodingConvention}. */ public class ClosureCodingConventionTest extends TestCase { private ClosureCodingConvention conv = new ClosureCodingConvention(); public void testVarAndOptionalParams() { Node args = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "a"), Node.newString(Token.NAME, "b")); Node optArgs = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "opt_a"), Node.newString(Token.NAME, "opt_b")); assertFalse(conv.isVarArgsParameter(args.getFirstChild())); assertFalse(conv.isVarArgsParameter(args.getLastChild())); assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); assertFalse(conv.isVarArgsParameter(optArgs.getLastChild())); assertFalse(conv.isOptionalParameter(args.getFirstChild())); assertFalse(conv.isOptionalParameter(args.getLastChild())); assertFalse(conv.isOptionalParameter(optArgs.getFirstChild())); assertFalse(conv.isOptionalParameter(optArgs.getLastChild())); } public void testInlineName() { assertFalse(conv.isConstant("a")); assertFalse(conv.isConstant("XYZ123_")); assertFalse(conv.isConstant("ABC")); assertFalse(conv.isConstant("ABCdef")); assertFalse(conv.isConstant("aBC")); assertFalse(conv.isConstant("A")); assertFalse(conv.isConstant("_XYZ123")); assertFalse(conv.isConstant("a$b$XYZ123_")); assertFalse(conv.isConstant("a$b$ABC_DEF")); assertFalse(conv.isConstant("a$b$A")); assertFalse(conv.isConstant("a$b$a")); assertFalse(conv.isConstant("a$b$ABCdef")); assertFalse(conv.isConstant("a$b$aBC")); assertFalse(conv.isConstant("a$b$")); assertFalse(conv.isConstant("$")); } public void testExportedName() { assertFalse(conv.isExported("_a")); assertFalse(conv.isExported("_a_")); assertFalse(conv.isExported("a")); assertFalse(conv.isExported("$super", false)); assertTrue(conv.isExported("$super", true)); assertTrue(conv.isExported("$super")); } public void testPrivateName() { assertFalse(conv.isPrivate("a_")); assertFalse(conv.isPrivate("a")); assertFalse(conv.isPrivate("_a_")); } public void testEnumKey() { assertTrue(conv.isValidEnumKey("A")); assertTrue(conv.isValidEnumKey("123")); assertTrue(conv.isValidEnumKey("FOO_BAR")); assertTrue(conv.isValidEnumKey("a")); assertTrue(conv.isValidEnumKey("someKeyInCamelCase")); assertTrue(conv.isValidEnumKey("_FOO_BAR")); } public void testInheritanceDetection1() { assertNotClassDefining("goog.foo(A, B);"); } public void testInheritanceDetection2() { assertDefinesClasses("goog.inherits(A, B);", "A", "B"); } public void testInheritanceDetection3() { assertDefinesClasses("A.inherits(B);", "A", "B"); } public void testInheritanceDetection4() { assertDefinesClasses("goog.inherits(goog.A, goog.B);", "goog.A", "goog.B"); } public void testInheritanceDetection5() { assertDefinesClasses("goog.A.inherits(goog.B);", "goog.A", "goog.B"); } public void testInheritanceDetection6() { assertNotClassDefining("A.inherits(this.B);"); } public void testInheritanceDetection7() { assertNotClassDefining("this.A.inherits(B);"); } public void testInheritanceDetection8() { assertNotClassDefining("goog.inherits(A, B, C);"); } public void testInheritanceDetection9() { assertDefinesClasses("A.mixin(B.prototype);", "A", "B"); } public void testInheritanceDetection10() { assertDefinesClasses("goog.mixin(A.prototype, B.prototype);", "A", "B"); } public void testInheritanceDetection11() { assertNotClassDefining("A.mixin(B)"); } public void testInheritanceDetection12() { assertNotClassDefining("goog.mixin(A.prototype, B)"); } public void testInheritanceDetection13() { assertNotClassDefining("goog.mixin(A, B)"); } public void testInheritanceDetection14() { assertNotClassDefining("goog$mixin((function(){}).prototype)"); } public void testInheritanceDetectionPostCollapseProperties() { assertDefinesClasses("goog$inherits(A, B);", "A", "B"); assertNotClassDefining("goog$inherits(A);"); } public void testObjectLiteralCast() { assertNotObjectLiteralCast("goog.reflect.object();"); assertNotObjectLiteralCast("goog.reflect.object(A);"); assertNotObjectLiteralCast("goog.reflect.object(1, {});"); assertObjectLiteralCast("goog.reflect.object(A, {});"); } public void testFunctionBind() { assertNotFunctionBind("goog.bind()"); // invalid bind assertFunctionBind("goog.bind(f)"); assertFunctionBind("goog.bind(f, obj)"); assertFunctionBind("goog.bind(f, obj, p1)"); assertNotFunctionBind("goog$bind()"); // invalid bind assertFunctionBind("goog$bind(f)"); assertFunctionBind("goog$bind(f, obj)"); assertFunctionBind("goog$bind(f, obj, p1)"); assertNotFunctionBind("goog.partial()"); // invalid bind assertFunctionBind("goog.partial(f)"); assertFunctionBind("goog.partial(f, obj)"); assertFunctionBind("goog.partial(f, obj, p1)"); assertNotFunctionBind("goog$partial()"); // invalid bind assertFunctionBind("goog$partial(f)"); assertFunctionBind("goog$partial(f, obj)"); assertFunctionBind("goog$partial(f, obj, p1)"); assertFunctionBind("(function(){}).bind()"); assertFunctionBind("(function(){}).bind(obj)"); assertFunctionBind("(function(){}).bind(obj, p1)"); assertNotFunctionBind("Function.prototype.bind.call()"); assertFunctionBind("Function.prototype.bind.call(obj)"); assertFunctionBind("Function.prototype.bind.call(obj, p1)"); } public void testRequire() { assertRequire("goog.require('foo')"); assertNotRequire("goog.require(foo)"); assertNotRequire("goog.require()"); assertNotRequire("foo()"); } public void testApplySubclassRelationship() { JSTypeRegistry registry = new JSTypeRegistry(null); Node nodeA = new Node(Token.FUNCTION); FunctionType ctorA = registry.createConstructorType("A", nodeA, new Node(Token.PARAM_LIST), null, null); Node nodeB = new Node(Token.FUNCTION); FunctionType ctorB = registry.createConstructorType("B", nodeB, new Node(Token.PARAM_LIST), null, null); conv.applySubclassRelationship(ctorA, ctorB, SubclassType.INHERITS); assertTrue(ctorB.getPrototype().hasOwnProperty("constructor")); assertEquals(nodeB, ctorB.getPrototype().getPropertyNode("constructor")); assertTrue(ctorB.hasOwnProperty("superClass_")); assertEquals(nodeB, ctorB.getPropertyNode("superClass_")); } private void assertFunctionBind(String code) { Node n = parseTestCode(code); assertNotNull(conv.describeFunctionBind(n.getFirstChild())); } private void assertNotFunctionBind(String code) { Node n = parseTestCode(code); assertNull(conv.describeFunctionBind(n.getFirstChild())); } private void assertRequire(String code) { Node n = parseTestCode(code); assertNotNull(conv.extractClassNameIfRequire(n.getFirstChild(), n)); } private void assertNotRequire(String code) { Node n = parseTestCode(code); assertNull(conv.extractClassNameIfRequire(n.getFirstChild(), n)); } private void assertNotObjectLiteralCast(String code) { Node n = parseTestCode(code); assertNull(conv.getObjectLiteralCast(n.getFirstChild())); } private void assertObjectLiteralCast(String code) { Node n = parseTestCode(code); assertNotNull(conv.getObjectLiteralCast(n.getFirstChild())); } private void assertNotClassDefining(String code) { Node n = parseTestCode(code); assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); } private void assertDefinesClasses(String code, String subclassName, String superclassName) { Node n = parseTestCode(code); SubclassRelationship classes = conv.getClassesDefinedByCall(n.getFirstChild()); assertNotNull(classes); assertEquals(subclassName, classes.subclassName); assertEquals(superclassName, classes.superclassName); } private Node parseTestCode(String code) { Compiler compiler = new Compiler(); return compiler.parseTestCode(code).getFirstChild(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/parsing/0000755000175000017500000000000012115204405024467 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/parsing/JsDocInfoParserTest.java0000644000175000017500000030652312115204405031176 0ustar apoapo/* * Copyright 2007 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.parsing; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.javascript.jscomp.parsing.Config.LanguageMode; import com.google.javascript.jscomp.testing.TestErrorReporter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.head.CompilerEnvirons; import com.google.javascript.rhino.head.Parser; import com.google.javascript.rhino.head.Token.CommentType; import com.google.javascript.rhino.head.ast.AstRoot; import com.google.javascript.rhino.head.ast.Comment; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.SimpleSourceFile; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; import java.util.Collection; import java.util.List; import java.util.Set; public class JsDocInfoParserTest extends BaseJSTypeTestCase { private Set extraAnnotations; private Set extraSuppressions; private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder = null; @Override public void setUp() throws Exception { super.setUp(); extraAnnotations = Sets.newHashSet( ParserRunner.createConfig(true, LanguageMode.ECMASCRIPT3, false) .annotationNames.keySet()); extraSuppressions = Sets.newHashSet( ParserRunner.createConfig(true, LanguageMode.ECMASCRIPT3, false) .suppressionNames); extraSuppressions.add("x"); extraSuppressions.add("y"); extraSuppressions.add("z"); } public void testParseTypeViaStatic1() throws Exception { Node typeNode = parseType("null"); assertTypeEquals(NULL_TYPE, typeNode); } public void testParseTypeViaStatic2() throws Exception { Node typeNode = parseType("string"); assertTypeEquals(STRING_TYPE, typeNode); } public void testParseTypeViaStatic3() throws Exception { Node typeNode = parseType("!Date"); assertTypeEquals(DATE_TYPE, typeNode); } public void testParseTypeViaStatic4() throws Exception { Node typeNode = parseType("boolean|string"); assertTypeEquals(createUnionType(BOOLEAN_TYPE, STRING_TYPE), typeNode); } public void testParseInvalidTypeViaStatic() throws Exception { Node typeNode = parseType("sometype. */"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, NUMBER_TYPE), info.getType()); } public void testParseTemplatizedType2() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, NUMBER_TYPE), info.getType()); } public void testParseTemplatizedType3() throws Exception { JSDocInfo info = parse("@type !Array.<(number,null)>*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), info.getType()); } public void testParseTemplatizedType4() throws Exception { JSDocInfo info = parse("@type {!Array.<(number|null)>}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), info.getType()); } public void testParseTemplatizedType5() throws Exception { JSDocInfo info = parse("@type {!Array.>}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createUnionType(NULL_TYPE, createTemplatizedType(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)))), info.getType()); } public void testParseTemplatizedType6() throws Exception { JSDocInfo info = parse("@type {!Array.>}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createTemplatizedType(ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE))), info.getType()); } public void testParseTemplatizedType7() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, registry.createFunctionType( createUnionType(DATE_TYPE, NULL_TYPE))), info.getType()); } public void testParseTemplatizedType8() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, registry.createFunctionType(DATE_TYPE)), info.getType()); } public void testParseTemplatizedType9() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createUnionType(DATE_TYPE, NUMBER_TYPE, NULL_TYPE)), info.getType()); } public void testParseTemplatizedType10() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, createUnionType(DATE_TYPE, NUMBER_TYPE, BOOLEAN_TYPE, NULL_TYPE)), info.getType()); } public void testParseTemplatizedType11() throws Exception { JSDocInfo info = parse("@type {!Object.}*/"); assertTypeEquals( createTemplatizedType( OBJECT_TYPE, ImmutableList.of(UNKNOWN_TYPE, NUMBER_TYPE)), info.getType()); assertTemplatizedTypeEquals( registry.getObjectElementKey(), NUMBER_TYPE, info.getType()); } public void testParseTemplatizedType12() throws Exception { JSDocInfo info = parse("@type {!Object.}*/"); assertTypeEquals( createTemplatizedType( OBJECT_TYPE, ImmutableList.of(STRING_TYPE, NUMBER_TYPE)), info.getType()); assertTemplatizedTypeEquals( registry.getObjectElementKey(), NUMBER_TYPE, info.getType()); assertTemplatizedTypeEquals( registry.getObjectIndexKey(), STRING_TYPE, info.getType()); } public void testParseTemplatizedType13() throws Exception { JSDocInfo info = parse("@type !Array. */"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, UNKNOWN_TYPE), info.getType()); } public void testParseUnionType1() throws Exception { JSDocInfo info = parse("@type {(boolean,null)}*/"); assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); } public void testParseUnionType2() throws Exception { JSDocInfo info = parse("@type {boolean|null}*/"); assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); } public void testParseUnionType3() throws Exception { JSDocInfo info = parse("@type {boolean||null}*/"); assertTypeEquals(createUnionType(BOOLEAN_TYPE, NULL_TYPE), info.getType()); } public void testParseUnionType4() throws Exception { JSDocInfo info = parse("@type {(Array.,null)}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType5() throws Exception { JSDocInfo info = parse("@type {(null, Array.)}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType6() throws Exception { JSDocInfo info = parse("@type {Array.|null}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType7() throws Exception { JSDocInfo info = parse("@type {null|Array.}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType8() throws Exception { JSDocInfo info = parse("@type {null||Array.}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType9() throws Exception { JSDocInfo info = parse("@type {Array.||null}*/"); assertTypeEquals(createUnionType( createTemplatizedType( ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE), info.getType()); } public void testParseUnionType10() throws Exception { parse("@type {string|}*/", "Bad type annotation. type not recognized due to syntax error"); } public void testParseUnionType11() throws Exception { parse("@type {(string,)}*/", "Bad type annotation. type not recognized due to syntax error"); } public void testParseUnionType12() throws Exception { parse("@type {()}*/", "Bad type annotation. type not recognized due to syntax error"); } public void testParseUnionType13() throws Exception { testParseType( "(function(this:Date),function(this:String):number)", "Function"); } public void testParseUnionType14() throws Exception { testParseType( "(function(...[function(number):boolean]):number)|" + "function(this:String, string):number", "Function"); } public void testParseUnionType15() throws Exception { testParseType("*|number", "*"); } public void testParseUnionType16() throws Exception { testParseType("number|*", "*"); } public void testParseUnionType17() throws Exception { testParseType("string|number|*", "*"); } public void testParseUnionType18() throws Exception { testParseType("(string,*,number)", "*"); } public void testParseUnionTypeError1() throws Exception { parse("@type {(string,|number)} */", "Bad type annotation. type not recognized due to syntax error"); } public void testParseUnknownType1() throws Exception { testParseType("?"); } public void testParseUnknownType2() throws Exception { testParseType("(?|number)", "?"); } public void testParseUnknownType3() throws Exception { testParseType("(number|?)", "?"); } public void testParseFunctionalType1() throws Exception { testParseType("function (): number"); } public void testParseFunctionalType2() throws Exception { testParseType("function (number, string): boolean"); } public void testParseFunctionalType3() throws Exception { testParseType( "function(this:Array)", "function (this:Array): ?"); } public void testParseFunctionalType4() throws Exception { testParseType("function (...[number]): boolean"); } public void testParseFunctionalType5() throws Exception { testParseType("function (number, ...[string]): boolean"); } public void testParseFunctionalType6() throws Exception { testParseType( "function (this:Date, number): (boolean|number|string)"); } public void testParseFunctionalType7() throws Exception { testParseType("function()", "function (): ?"); } public void testParseFunctionalType8() throws Exception { testParseType( "function(this:Array,...[boolean])", "function (this:Array, ...[boolean]): ?"); } public void testParseFunctionalType9() throws Exception { testParseType( "function(this:Array,!Date,...[boolean?])", "function (this:Array, Date, ...[(boolean|null)]): ?"); } public void testParseFunctionalType10() throws Exception { testParseType( "function(...[Object?]):boolean?", "function (...[(Object|null)]): (boolean|null)"); } public void testParseFunctionalType11() throws Exception { testParseType( "function(...[[number]]):[number?]", "function (...[Array]): Array"); } public void testParseFunctionalType12() throws Exception { testParseType( "function(...)", "function (...[?]): ?"); } public void testParseFunctionalType13() throws Exception { testParseType( "function(...): void", "function (...[?]): undefined"); } public void testParseFunctionalType14() throws Exception { testParseType("function (*, string, number): boolean"); } public void testParseFunctionalType15() throws Exception { testParseType("function (?, string): boolean"); } public void testParseFunctionalType16() throws Exception { testParseType("function (string, ?): ?"); } public void testParseFunctionalType17() throws Exception { testParseType("(function (?): ?|number)"); } public void testParseFunctionalType18() throws Exception { testParseType("function (?): (?|number)", "function (?): ?"); } public void testParseFunctionalType19() throws Exception { testParseType( "function(...[?]): void", "function (...[?]): undefined"); } public void testStructuralConstructor() throws Exception { JSType type = testParseType( "function (new:Object)", "function (new:Object): ?"); assertTrue(type.isConstructor()); assertFalse(type.isNominalConstructor()); } public void testNominalConstructor() throws Exception { ObjectType type = testParseType("Array", "(Array|null)").dereference(); assertTrue(type.getConstructor().isNominalConstructor()); } public void testBug1419535() throws Exception { parse("@type {function(Object, string, *)?} */"); parse("@type {function(Object, string, *)|null} */"); } public void testIssue477() throws Exception { parse("@type function */", "Bad type annotation. missing opening ("); } public void testMalformedThisAnnotation() throws Exception { parse("@this */", "Bad type annotation. type not recognized due to syntax error"); } public void testParseFunctionalTypeError1() throws Exception { parse("@type {function number):string}*/", "Bad type annotation. missing opening ("); } public void testParseFunctionalTypeError2() throws Exception { parse("@type {function( number}*/", "Bad type annotation. missing closing )"); } public void testParseFunctionalTypeError3() throws Exception { parse("@type {function(...[number], string)}*/", "Bad type annotation. variable length argument must be last"); } public void testParseFunctionalTypeError4() throws Exception { parse("@type {function(string, ...[number], boolean):string}*/", "Bad type annotation. variable length argument must be last"); } public void testParseFunctionalTypeError5() throws Exception { parse("@type {function (thi:Array)}*/", "Bad type annotation. missing closing )"); } public void testParseFunctionalTypeError6() throws Exception { resolve(parse("@type {function (this:number)}*/").getType(), "this type must be an object type"); } public void testParseFunctionalTypeError7() throws Exception { parse("@type {function(...[number)}*/", "Bad type annotation. missing closing ]"); } public void testParseFunctionalTypeError8() throws Exception { parse("@type {function(...number])}*/", "Bad type annotation. missing opening ["); } public void testParseFunctionalTypeError9() throws Exception { parse("@type {function (new:Array, this:Object)} */", "Bad type annotation. missing closing )"); } public void testParseFunctionalTypeError10() throws Exception { parse("@type {function (this:Array, new:Object)} */", "Bad type annotation. missing closing )"); } public void testParseFunctionalTypeError11() throws Exception { parse("@type {function (Array, new:Object)} */", "Bad type annotation. missing closing )"); } public void testParseFunctionalTypeError12() throws Exception { resolve(parse("@type {function (new:number)}*/").getType(), "constructed type must be an object type"); } public void testParseArrayType1() throws Exception { testParseType("[number]", "Array"); } public void testParseArrayType2() throws Exception { testParseType("[(number,boolean,[Object?])]", "Array"); } public void testParseArrayType3() throws Exception { testParseType("[[number],[string]]?", "(Array|null)"); } public void testParseArrayTypeError1() throws Exception { parse("@type {[number}*/", "Bad type annotation. missing closing ]"); } public void testParseArrayTypeError2() throws Exception { parse("@type {number]}*/", "Bad type annotation. expected closing }"); } public void testParseArrayTypeError3() throws Exception { parse("@type {[(number,boolean,Object?])]}*/", "Bad type annotation. missing closing )"); } public void testParseArrayTypeError4() throws Exception { parse("@type {(number,boolean,[Object?)]}*/", "Bad type annotation. missing closing ]"); } private JSType testParseType(String type) throws Exception { return testParseType(type, type); } private JSType testParseType( String type, String typeExpected) throws Exception { JSDocInfo info = parse("@type {" + type + "}*/"); assertNotNull(info); assertTrue(info.hasType()); JSType actual = resolve(info.getType()); assertEquals(typeExpected, actual.toString()); return actual; } public void testParseNullableModifiers1() throws Exception { JSDocInfo info = parse("@type {string?}*/"); assertTypeEquals(createNullableType(STRING_TYPE), info.getType()); } public void testParseNullableModifiers2() throws Exception { JSDocInfo info = parse("@type {!Array.}*/"); assertTypeEquals( createTemplatizedType( ARRAY_TYPE, createUnionType(STRING_TYPE, NULL_TYPE)), info.getType()); } public void testParseNullableModifiers3() throws Exception { JSDocInfo info = parse("@type {Array.?}*/"); assertTypeEquals( createNullableType(createTemplatizedType(ARRAY_TYPE, BOOLEAN_TYPE)), info.getType()); } public void testParseNullableModifiers4() throws Exception { JSDocInfo info = parse("@type {(string,boolean)?}*/"); assertTypeEquals( createNullableType(createUnionType(STRING_TYPE, BOOLEAN_TYPE)), info.getType()); } public void testParseNullableModifiers5() throws Exception { JSDocInfo info = parse("@type {(string?,boolean)}*/"); assertTypeEquals( createUnionType(createNullableType(STRING_TYPE), BOOLEAN_TYPE), info.getType()); } public void testParseNullableModifiers6() throws Exception { JSDocInfo info = parse("@type {(string,boolean?)}*/"); assertTypeEquals( createUnionType(STRING_TYPE, createNullableType(BOOLEAN_TYPE)), info.getType()); } public void testParseNullableModifiers7() throws Exception { JSDocInfo info = parse("@type {string?|boolean}*/"); assertTypeEquals( createUnionType(createNullableType(STRING_TYPE), BOOLEAN_TYPE), info.getType()); } public void testParseNullableModifiers8() throws Exception { JSDocInfo info = parse("@type {string|boolean?}*/"); assertTypeEquals( createUnionType(STRING_TYPE, createNullableType(BOOLEAN_TYPE)), info.getType()); } public void testParseNullableModifiers9() throws Exception { JSDocInfo info = parse("@type {foo.Hello.World?}*/"); assertTypeEquals( createNullableType( registry.createNamedType( "foo.Hello.World", null, -1, -1)), info.getType()); } public void testParseOptionalModifier() throws Exception { JSDocInfo info = parse("@type {function(number=)}*/"); assertTypeEquals( registry.createFunctionType( UNKNOWN_TYPE, registry.createOptionalParameters(NUMBER_TYPE)), info.getType()); } public void testParseNewline1() throws Exception { JSDocInfo info = parse("@type {string\n* }\n*/"); assertTypeEquals(STRING_TYPE, info.getType()); } public void testParseNewline2() throws Exception { JSDocInfo info = parse("@type !Array.<\n* number\n* > */"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, NUMBER_TYPE), info.getType()); } public void testParseNewline3() throws Exception { JSDocInfo info = parse("@type !Array.<(number,\n* null)>*/"); assertTypeEquals( createTemplatizedType( ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), info.getType()); } public void testParseNewline4() throws Exception { JSDocInfo info = parse("@type !Array.<(number|\n* null)>*/"); assertTypeEquals( createTemplatizedType( ARRAY_TYPE, createUnionType(NUMBER_TYPE, NULL_TYPE)), info.getType()); } public void testParseNewline5() throws Exception { JSDocInfo info = parse("@type !Array.*/"); assertTypeEquals( createTemplatizedType(ARRAY_TYPE, registry.createFunctionType( createUnionType(DATE_TYPE, NULL_TYPE))), info.getType()); } public void testParseReturnType1() throws Exception { JSDocInfo info = parse("@return {null|string|Array.}*/"); assertTypeEquals( createUnionType(createTemplatizedType(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE, STRING_TYPE), info.getReturnType()); } public void testParseReturnType2() throws Exception { JSDocInfo info = parse("@returns {null|(string,Array.)}*/"); assertTypeEquals( createUnionType(createTemplatizedType(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE, STRING_TYPE), info.getReturnType()); } public void testParseReturnType3() throws Exception { JSDocInfo info = parse("@return {((null||Array.,string),boolean)}*/"); assertTypeEquals( createUnionType(createTemplatizedType(ARRAY_TYPE, BOOLEAN_TYPE), NULL_TYPE, STRING_TYPE, BOOLEAN_TYPE), info.getReturnType()); } public void testParseThisType1() throws Exception { JSDocInfo info = parse("@this {goog.foo.Bar}*/"); assertTypeEquals( registry.createNamedType("goog.foo.Bar", null, -1, -1), info.getThisType()); } public void testParseThisType2() throws Exception { JSDocInfo info = parse("@this goog.foo.Bar*/"); assertTypeEquals( registry.createNamedType("goog.foo.Bar", null, -1, -1), info.getThisType()); } public void testParseThisType3() throws Exception { parse("@type {number}\n@this goog.foo.Bar*/", "Bad type annotation. type annotation incompatible " + "with other annotations"); } public void testParseThisType4() throws Exception { resolve(parse("@this number*/").getThisType(), "@this must specify an object type"); } public void testParseThisType5() throws Exception { parse("@this {Date|Error}*/"); } public void testParseThisType6() throws Exception { resolve(parse("@this {Date|number}*/").getThisType(), "@this must specify an object type"); } public void testParseParam1() throws Exception { JSDocInfo info = parse("@param {number} index*/"); assertEquals(1, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); } public void testParseParam2() throws Exception { JSDocInfo info = parse("@param index*/"); assertEquals(1, info.getParameterCount()); assertEquals(null, info.getParameterType("index")); } public void testParseParam3() throws Exception { JSDocInfo info = parse("@param {number} index useful comments*/"); assertEquals(1, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); } public void testParseParam4() throws Exception { JSDocInfo info = parse("@param index useful comments*/"); assertEquals(1, info.getParameterCount()); assertEquals(null, info.getParameterType("index")); } public void testParseParam5() throws Exception { // Test for multi-line @param. JSDocInfo info = parse("@param {number} \n index */"); assertEquals(1, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); } public void testParseParam6() throws Exception { // Test for multi-line @param. JSDocInfo info = parse("@param {number} \n * index */"); assertEquals(1, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); } public void testParseParam7() throws Exception { // Optional @param JSDocInfo info = parse("@param {number=} index */"); assertTypeEquals( registry.createOptionalType(NUMBER_TYPE), info.getParameterType("index")); } public void testParseParam8() throws Exception { // Var args @param JSDocInfo info = parse("@param {...number} index */"); assertTypeEquals( registry.createOptionalType(NUMBER_TYPE), info.getParameterType("index")); } public void testParseParam9() throws Exception { parse("@param {...number=} index */", "Bad type annotation. expected closing }", "Bad type annotation. expecting a variable name in a @param tag"); } public void testParseParam10() throws Exception { parse("@param {...number index */", "Bad type annotation. expected closing }"); } public void testParseParam11() throws Exception { parse("@param {number= index */", "Bad type annotation. expected closing }"); } public void testParseParam12() throws Exception { JSDocInfo info = parse("@param {...number|string} index */"); assertTypeEquals( registry.createOptionalType( registry.createUnionType(STRING_TYPE, NUMBER_TYPE)), info.getParameterType("index")); } public void testParseParam13() throws Exception { JSDocInfo info = parse("@param {...(number|string)} index */"); assertTypeEquals( registry.createOptionalType( registry.createUnionType(STRING_TYPE, NUMBER_TYPE)), info.getParameterType("index")); } public void testParseParam14() throws Exception { JSDocInfo info = parse("@param {string} [index] */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( registry.createOptionalType(STRING_TYPE), info.getParameterType("index")); } public void testParseParam15() throws Exception { JSDocInfo info = parse("@param {string} [index */", "Bad type annotation. missing closing ]"); assertEquals(1, info.getParameterCount()); assertTypeEquals(STRING_TYPE, info.getParameterType("index")); } public void testParseParam16() throws Exception { JSDocInfo info = parse("@param {string} index] */"); assertEquals(1, info.getParameterCount()); assertTypeEquals(STRING_TYPE, info.getParameterType("index")); } public void testParseParam17() throws Exception { JSDocInfo info = parse("@param {string=} [index] */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( registry.createOptionalType(STRING_TYPE), info.getParameterType("index")); } public void testParseParam18() throws Exception { JSDocInfo info = parse("@param {...string} [index] */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( registry.createOptionalType(STRING_TYPE), info.getParameterType("index")); } public void testParseParam19() throws Exception { JSDocInfo info = parse("@param {...} [index] */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( registry.createOptionalType(UNKNOWN_TYPE), info.getParameterType("index")); assertTrue(info.getParameterType("index").isVarArgs()); } public void testParseParam20() throws Exception { JSDocInfo info = parse("@param {?=} index */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( UNKNOWN_TYPE, info.getParameterType("index")); } public void testParseParam21() throws Exception { JSDocInfo info = parse("@param {...?} index */"); assertEquals(1, info.getParameterCount()); assertTypeEquals( UNKNOWN_TYPE, info.getParameterType("index")); assertTrue(info.getParameterType("index").isVarArgs()); } public void testParseThrows1() throws Exception { JSDocInfo info = parse("@throws {number} Some number */"); assertEquals(1, info.getThrownTypes().size()); assertTypeEquals(NUMBER_TYPE, info.getThrownTypes().get(0)); } public void testParseThrows2() throws Exception { JSDocInfo info = parse("@throws {number} Some number\n " + "*@throws {String} A string */"); assertEquals(2, info.getThrownTypes().size()); assertTypeEquals(NUMBER_TYPE, info.getThrownTypes().get(0)); } public void testParseRecordType1() throws Exception { parseFull("/** @param {{x}} n\n*/"); } public void testParseRecordType2() throws Exception { parseFull("/** @param {{z, y}} n\n*/"); } public void testParseRecordType3() throws Exception { parseFull("/** @param {{z, y, x, q, hello, thisisatest}} n\n*/"); } public void testParseRecordType4() throws Exception { parseFull("/** @param {{a, 'a', 'hello', 2, this, do, while, for}} n\n*/"); } public void testParseRecordType5() throws Exception { parseFull("/** @param {{x : hello}} n\n*/"); } public void testParseRecordType6() throws Exception { parseFull("/** @param {{'x' : hello}} n\n*/"); } public void testParseRecordType7() throws Exception { parseFull("/** @param {{'x' : !hello}} n\n*/"); } public void testParseRecordType8() throws Exception { parseFull("/** @param {{'x' : !hello, y : bar}} n\n*/"); } public void testParseRecordType9() throws Exception { parseFull("/** @param {{'x' : !hello, y : {z : bar, 3 : meh}}} n\n*/"); } public void testParseRecordType10() throws Exception { parseFull("/** @param {{__proto__ : moo}} n\n*/"); } public void testParseRecordType11() throws Exception { parseFull("/** @param {{a : b} n\n*/", "Bad type annotation. expected closing }"); } public void testParseRecordType12() throws Exception { parseFull("/** @param {{!hello : hey}} n\n*/", "Bad type annotation. type not recognized due to syntax error"); } public void testParseRecordType13() throws Exception { parseFull("/** @param {{x}|number} n\n*/"); } public void testParseRecordType14() throws Exception { parseFull("/** @param {{x : y}|number} n\n*/"); } public void testParseRecordType15() throws Exception { parseFull("/** @param {{'x' : y}|number} n\n*/"); } public void testParseRecordType16() throws Exception { parseFull("/** @param {{x, y}|number} n\n*/"); } public void testParseRecordType17() throws Exception { parseFull("/** @param {{x : hello, 'y'}|number} n\n*/"); } public void testParseRecordType18() throws Exception { parseFull("/** @param {number|{x : hello, 'y'}} n\n*/"); } public void testParseRecordType19() throws Exception { parseFull("/** @param {?{x : hello, 'y'}} n\n*/"); } public void testParseRecordType20() throws Exception { parseFull("/** @param {!{x : hello, 'y'}} n\n*/"); } public void testParseRecordType21() throws Exception { parseFull("/** @param {{x : hello, 'y'}|boolean} n\n*/"); } public void testParseRecordType22() throws Exception { parseFull("/** @param {{x : hello, 'y'}|function()} n\n*/"); } public void testParseRecordType23() throws Exception { parseFull("/** @param {{x : function(), 'y'}|function()} n\n*/"); } public void testParseParamError1() throws Exception { parseFull("/** @param\n*/", "Bad type annotation. expecting a variable name in a @param tag"); } public void testParseParamError2() throws Exception { parseFull("/** @param {Number}*/", "Bad type annotation. expecting a variable name in a @param tag"); } public void testParseParamError3() throws Exception { parseFull("/** @param {Number}\n*/", "Bad type annotation. expecting a variable name in a @param tag"); } public void testParseParamError4() throws Exception { parseFull("/** @param {Number}\n* * num */", "Bad type annotation. expecting a variable name in a @param tag"); } public void testParseParamError5() throws Exception { parse("@param {number} x \n * @param {string} x */", "Bad type annotation. duplicate variable name \"x\""); } public void testParseExtends1() throws Exception { assertTypeEquals(STRING_OBJECT_TYPE, parse("@extends String*/").getBaseType()); } public void testParseExtends2() throws Exception { JSDocInfo info = parse("@extends com.google.Foo.Bar.Hello.World*/"); assertTypeEquals( registry.createNamedType( "com.google.Foo.Bar.Hello.World", null, -1, -1), info.getBaseType()); } public void testParseExtendsGenerics() throws Exception { JSDocInfo info = parse("@extends com.google.Foo.Bar.Hello.World.*/"); assertTypeEquals( registry.createNamedType( "com.google.Foo.Bar.Hello.World", null, -1, -1), info.getBaseType()); } public void testParseImplementsGenerics() throws Exception { // For types that are not templatized, <> annotations are ignored. List interfaces = parse("@implements {SomeInterface.<*>} */") .getImplementedInterfaces(); assertEquals(1, interfaces.size()); assertTypeEquals(registry.createNamedType("SomeInterface", null, -1, -1), interfaces.get(0)); } public void testParseExtends4() throws Exception { assertTypeEquals(STRING_OBJECT_TYPE, parse("@extends {String}*/").getBaseType()); } public void testParseExtends5() throws Exception { assertTypeEquals(STRING_OBJECT_TYPE, parse("@extends {String*/", "Bad type annotation. expected closing }").getBaseType()); } public void testParseExtends6() throws Exception { // Multi-line extends assertTypeEquals(STRING_OBJECT_TYPE, parse("@extends \n * {String}*/").getBaseType()); } public void testParseExtendsInvalidName() throws Exception { // This looks bad, but for the time being it should be OK, as // we will not find a type with this name in the JS parsed tree. // If this is fixed in the future, change this test to check for a // warning/error message. assertTypeEquals( registry.createNamedType("some_++#%$%_UglyString", null, -1, -1), parse("@extends {some_++#%$%_UglyString} */").getBaseType()); } public void testParseExtendsNullable1() throws Exception { parse("@extends {Base?} */", "Bad type annotation. expected closing }"); } public void testParseExtendsNullable2() throws Exception { parse("@extends Base? */", "Bad type annotation. expected end of line or comment"); } public void testParseEnum1() throws Exception { assertTypeEquals(NUMBER_TYPE, parse("@enum*/").getEnumParameterType()); } public void testParseEnum2() throws Exception { assertTypeEquals(STRING_TYPE, parse("@enum {string}*/").getEnumParameterType()); } public void testParseEnum3() throws Exception { assertTypeEquals(STRING_TYPE, parse("@enum string*/").getEnumParameterType()); } public void testParseDesc1() throws Exception { assertEquals("hello world!", parse("@desc hello world!*/").getDescription()); } public void testParseDesc2() throws Exception { assertEquals("hello world!", parse("@desc hello world!\n*/").getDescription()); } public void testParseDesc3() throws Exception { assertEquals("", parse("@desc*/").getDescription()); } public void testParseDesc4() throws Exception { assertEquals("", parse("@desc\n*/").getDescription()); } public void testParseDesc5() throws Exception { assertEquals("hello world!", parse("@desc hello\nworld!\n*/").getDescription()); } public void testParseDesc6() throws Exception { assertEquals("hello world!", parse("@desc hello\n* world!\n*/").getDescription()); } public void testParseDesc7() throws Exception { assertEquals("a b c", parse("@desc a\n\nb\nc*/").getDescription()); } public void testParseDesc8() throws Exception { assertEquals("a b c d", parse("@desc a\n *b\n\n *c\n\nd*/").getDescription()); } public void testParseDesc9() throws Exception { String comment = "@desc\n.\n,\n{\n)\n}\n|\n.<\n>\n<\n?\n~\n+\n-\n;\n:\n*/"; assertEquals(". , { ) } | .< > < ? ~ + - ; :", parse(comment).getDescription()); } public void testParseDesc10() throws Exception { String comment = "@desc\n?\n?\n?\n?*/"; assertEquals("? ? ? ?", parse(comment).getDescription()); } public void testParseDesc11() throws Exception { String comment = "@desc :[]*/"; assertEquals(":[]", parse(comment).getDescription()); } public void testParseDesc12() throws Exception { String comment = "@desc\n:\n[\n]\n...*/"; assertEquals(": [ ] ...", parse(comment).getDescription()); } public void testParseMeaning1() throws Exception { assertEquals("tigers", parse("@meaning tigers */").getMeaning()); } public void testParseMeaning2() throws Exception { assertEquals("tigers and lions and bears", parse("@meaning tigers\n * and lions\n * and bears */").getMeaning()); } public void testParseMeaning3() throws Exception { JSDocInfo info = parse("@meaning tigers\n * and lions\n * @desc and bears */"); assertEquals("tigers and lions", info.getMeaning()); assertEquals("and bears", info.getDescription()); } public void testParseMeaning4() throws Exception { parse("@meaning tigers\n * @meaning and lions */", "extra @meaning tag"); } public void testParseLends1() throws Exception { JSDocInfo info = parse("@lends {name} */"); assertEquals("name", info.getLendsName()); } public void testParseLends2() throws Exception { JSDocInfo info = parse("@lends foo.bar */"); assertEquals("foo.bar", info.getLendsName()); } public void testParseLends3() throws Exception { parse("@lends {name */", "Bad type annotation. expected closing }"); } public void testParseLends4() throws Exception { parse("@lends {} */", "Bad type annotation. missing object name in @lends tag"); } public void testParseLends5() throws Exception { parse("@lends } */", "Bad type annotation. missing object name in @lends tag"); } public void testParseLends6() throws Exception { parse("@lends {string} \n * @lends {string} */", "Bad type annotation. @lends tag incompatible with other annotations"); } public void testParseLends7() throws Exception { parse("@type {string} \n * @lends {string} */", "Bad type annotation. @lends tag incompatible with other annotations"); } public void testParsePreserve() throws Exception { Node node = new Node(1); this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); String comment = "@preserve Foo\nBar\n\nBaz*/"; parse(comment); assertEquals(" Foo\nBar\n\nBaz", node.getJSDocInfo().getLicense()); } public void testParseLicense() throws Exception { Node node = new Node(1); this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); String comment = "@license Foo\nBar\n\nBaz*/"; parse(comment); assertEquals(" Foo\nBar\n\nBaz", node.getJSDocInfo().getLicense()); } public void testParseLicenseAscii() throws Exception { Node node = new Node(1); this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); String comment = "@license Foo\n * Bar\n\n Baz*/"; parse(comment); assertEquals(" Foo\n Bar\n\n Baz", node.getJSDocInfo().getLicense()); } public void testParseLicenseWithAnnotation() throws Exception { Node node = new Node(1); this.fileLevelJsDocBuilder = node.getJsDocBuilderForNode(); String comment = "@license Foo \n * @author Charlie Brown */"; parse(comment); assertEquals(" Foo \n @author Charlie Brown ", node.getJSDocInfo().getLicense()); } public void testParseDefine1() throws Exception { assertTypeEquals(STRING_TYPE, parse("@define {string}*/").getType()); } public void testParseDefine2() throws Exception { assertTypeEquals(STRING_TYPE, parse("@define {string*/", "Bad type annotation. expected closing }").getType()); } public void testParseDefine3() throws Exception { JSDocInfo info = parse("@define {boolean}*/"); assertTrue(info.isConstant()); assertTrue(info.isDefine()); assertTypeEquals(BOOLEAN_TYPE, info.getType()); } public void testParseDefine4() throws Exception { assertTypeEquals(NUMBER_TYPE, parse("@define {number}*/").getType()); } public void testParseDefine5() throws Exception { assertTypeEquals(createUnionType(NUMBER_TYPE, BOOLEAN_TYPE), parse("@define {number|boolean}*/").getType()); } public void testParseDefineErrors1() throws Exception { parse("@enum {string}\n @define {string} */", "conflicting @define tag"); } public void testParseDefineErrors2() throws Exception { parse("@define {string}\n @enum {string} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testParseDefineErrors3() throws Exception { parse("@const\n @define {string} */", "conflicting @define tag"); } public void testParseDefineErrors4() throws Exception { parse("@type string \n @define {string} */", "conflicting @define tag"); } public void testParseDefineErrors5() throws Exception { parse("@return {string}\n @define {string} */", "conflicting @define tag"); } public void testParseDefineErrors7() throws Exception { parse("@define {string}\n @const */", "conflicting @const tag"); } public void testParseDefineErrors8() throws Exception { parse("@define {string}\n @type string */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testParseNoCheck1() throws Exception { assertTrue(parse("@notypecheck*/").isNoTypeCheck()); } public void testParseNoCheck2() throws Exception { parse("@notypecheck\n@notypecheck*/", "extra @notypecheck tag"); } public void testParseOverride1() throws Exception { assertTrue(parse("@override*/").isOverride()); } public void testParseOverride2() throws Exception { parse("@override\n@override*/", "Bad type annotation. extra @override/@inheritDoc tag"); } public void testParseInheritDoc1() throws Exception { assertTrue(parse("@inheritDoc*/").isOverride()); } public void testParseInheritDoc2() throws Exception { parse("@override\n@inheritDoc*/", "Bad type annotation. extra @override/@inheritDoc tag"); } public void testParseInheritDoc3() throws Exception { parse("@inheritDoc\n@inheritDoc*/", "Bad type annotation. extra @override/@inheritDoc tag"); } public void testParseNoAlias1() throws Exception { assertTrue(parse("@noalias*/").isNoAlias()); } public void testParseNoAlias2() throws Exception { parse("@noalias\n@noalias*/", "extra @noalias tag"); } public void testParseDeprecated1() throws Exception { assertTrue(parse("@deprecated*/").isDeprecated()); } public void testParseDeprecated2() throws Exception { parse("@deprecated\n@deprecated*/", "extra @deprecated tag"); } public void testParseExport1() throws Exception { assertTrue(parse("@export*/").isExport()); } public void testParseExport2() throws Exception { parse("@export\n@export*/", "extra @export tag"); } public void testParseExpose1() throws Exception { assertTrue(parse("@expose*/").isExpose()); } public void testParseExpose2() throws Exception { parse("@expose\n@expose*/", "extra @expose tag"); } public void testParseExterns1() throws Exception { assertTrue(parseFileOverview("@externs*/").isExterns()); } public void testParseExterns2() throws Exception { parseFileOverview("@externs\n@externs*/", "extra @externs tag"); } public void testParseExterns3() throws Exception { assertNull(parse("@externs*/")); } public void testParseJavaDispatch1() throws Exception { assertTrue(parse("@javadispatch*/").isJavaDispatch()); } public void testParseJavaDispatch2() throws Exception { parse("@javadispatch\n@javadispatch*/", "extra @javadispatch tag"); } public void testParseJavaDispatch3() throws Exception { assertNull(parseFileOverview("@javadispatch*/")); } public void testParseNoCompile1() throws Exception { assertTrue(parseFileOverview("@nocompile*/").isNoCompile()); } public void testParseNoCompile2() throws Exception { parseFileOverview("@nocompile\n@nocompile*/", "extra @nocompile tag"); } public void testBugAnnotation() throws Exception { parse("@bug */"); } public void testDescriptionAnnotation() throws Exception { parse("@description */"); } public void testRegression1() throws Exception { String comment = " * @param {number} index the index of blah\n" + " * @return {boolean} whatever\n" + " * @private\n" + " */"; JSDocInfo info = parse(comment); assertEquals(1, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); assertTypeEquals(BOOLEAN_TYPE, info.getReturnType()); assertEquals(Visibility.PRIVATE, info.getVisibility()); } public void testRegression2() throws Exception { String comment = " * @return {boolean} whatever\n" + " * but important\n" + " *\n" + " * @param {number} index the index of blah\n" + " * some more comments here\n" + " * @param name the name of the guy\n" + " *\n" + " * @protected\n" + " */"; JSDocInfo info = parse(comment); assertEquals(2, info.getParameterCount()); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); assertEquals(null, info.getParameterType("name")); assertTypeEquals(BOOLEAN_TYPE, info.getReturnType()); assertEquals(Visibility.PROTECTED, info.getVisibility()); } public void testRegression3() throws Exception { String comment = " * @param mediaTag this specified whether the @media tag is ....\n" + " *\n" + "\n" + "@public\n" + " *\n" + "\n" + " **********\n" + " * @final\n" + " */"; JSDocInfo info = parse(comment); assertEquals(1, info.getParameterCount()); assertEquals(null, info.getParameterType("mediaTag")); assertEquals(Visibility.PUBLIC, info.getVisibility()); assertTrue(info.isConstant()); } public void testRegression4() throws Exception { String comment = " * @const\n" + " * @hidden\n" + " * @preserveTry\n" + " * @constructor\n" + " */"; JSDocInfo info = parse(comment); assertTrue(info.isConstant()); assertFalse(info.isDefine()); assertTrue(info.isConstructor()); assertTrue(info.isHidden()); assertTrue(info.shouldPreserveTry()); } public void testRegression5() throws Exception { String comment = "@const\n@enum {string}\n@public*/"; JSDocInfo info = parse(comment); assertTrue(info.isConstant()); assertFalse(info.isDefine()); assertTypeEquals(STRING_TYPE, info.getEnumParameterType()); assertEquals(Visibility.PUBLIC, info.getVisibility()); } public void testRegression6() throws Exception { String comment = "@hidden\n@enum\n@public*/"; JSDocInfo info = parse(comment); assertTrue(info.isHidden()); assertTypeEquals(NUMBER_TYPE, info.getEnumParameterType()); assertEquals(Visibility.PUBLIC, info.getVisibility()); } public void testRegression7() throws Exception { String comment = " * @desc description here\n" + " * @param {boolean} flag and some more description\n" + " * nicely formatted\n" + " */"; JSDocInfo info = parse(comment); assertEquals(1, info.getParameterCount()); assertTypeEquals(BOOLEAN_TYPE, info.getParameterType("flag")); assertEquals("description here", info.getDescription()); } public void testRegression8() throws Exception { String comment = " * @name random tag here\n" + " * @desc description here\n" + " *\n" + " * @param {boolean} flag and some more description\n" + " * nicely formatted\n" + " */"; JSDocInfo info = parse(comment); assertEquals(1, info.getParameterCount()); assertTypeEquals(BOOLEAN_TYPE, info.getParameterType("flag")); assertEquals("description here", info.getDescription()); } public void testRegression9() throws Exception { JSDocInfo jsdoc = parse( " * @param {string} p0 blah blah blah\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(1, jsdoc.getParameterCount()); assertTypeEquals(STRING_TYPE, jsdoc.getParameterType("p0")); assertNull(jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); } public void testRegression10() throws Exception { JSDocInfo jsdoc = parse( " * @param {!String} p0 blah blah blah\n" + " * @param {boolean} p1 fobar\n" + " * @return {!Date} jksjkash dshad\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(2, jsdoc.getParameterCount()); assertTypeEquals(STRING_OBJECT_TYPE, jsdoc.getParameterType("p0")); assertTypeEquals(BOOLEAN_TYPE, jsdoc.getParameterType("p1")); assertTypeEquals(DATE_TYPE, jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); } public void testRegression11() throws Exception { JSDocInfo jsdoc = parse( " * @constructor\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); } public void testRegression12() throws Exception { JSDocInfo jsdoc = parse( " * @extends FooBar\n" + " */"); assertTypeEquals(registry.createNamedType("FooBar", null, 0, 0), jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); } public void testRegression13() throws Exception { JSDocInfo jsdoc = parse( " * @type {!RegExp}\n" + " * @protected\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertTypeEquals(REGEXP_TYPE, jsdoc.getType()); assertEquals(Visibility.PROTECTED, jsdoc.getVisibility()); } public void testRegression14() throws Exception { JSDocInfo jsdoc = parse( " * @const\n" + " * @private\n" + " */"); assertNull(jsdoc.getBaseType()); assertTrue(jsdoc.isConstant()); assertNull(jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.PRIVATE, jsdoc.getVisibility()); } public void testRegression15() throws Exception { JSDocInfo jsdoc = parse( " * @desc Hello,\n" + " * World!\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertEquals("Hello, World!", jsdoc.getDescription()); assertNull(jsdoc.getEnumParameterType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertNull(jsdoc.getType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); assertFalse(jsdoc.isExport()); } public void testRegression16() throws Exception { JSDocInfo jsdoc = parse( " Email is plp@foo.bar\n" + " @type {string}\n" + " */"); assertNull(jsdoc.getBaseType()); assertFalse(jsdoc.isConstant()); assertTypeEquals(STRING_TYPE, jsdoc.getType()); assertFalse(jsdoc.isHidden()); assertEquals(0, jsdoc.getParameterCount()); assertNull(jsdoc.getReturnType()); assertEquals(Visibility.INHERITED, jsdoc.getVisibility()); } public void testRegression17() throws Exception { // verifying that if no @desc is present the description is empty assertNull(parse("@private*/").getDescription()); } public void testFullRegression1() throws Exception { parseFull("/** @param (string,number) foo*/function bar(foo){}", "Bad type annotation. expecting a variable name in a @param tag"); } public void testFullRegression2() throws Exception { parseFull("/** @param {string,number) foo*/function bar(foo){}", "Bad type annotation. expected closing }", "Bad type annotation. expecting a variable name in a @param tag"); } public void testFullRegression3() throws Exception { parseFull("/**..\n*/"); } public void testBug907488() throws Exception { parse("@type {number,null} */", "Bad type annotation. expected closing }"); } public void testBug907494() throws Exception { parse("@return {Object,undefined} */", "Bad type annotation. expected closing }"); } public void testBug909468() throws Exception { parse("@extends {(x)}*/", "Bad type annotation. expecting a type name"); } public void testParseInterface() throws Exception { assertTrue(parse("@interface*/").isInterface()); } public void testParseImplicitCast1() throws Exception { assertTrue(parse("@type {string} \n * @implicitCast*/").isImplicitCast()); } public void testParseImplicitCast2() throws Exception { assertFalse(parse("@type {string}*/").isImplicitCast()); } public void testParseDuplicateImplicitCast() throws Exception { parse("@type {string} \n * @implicitCast \n * @implicitCast*/", "Bad type annotation. extra @implicitCast tag"); } public void testParseInterfaceDoubled() throws Exception { parse( "* @interface\n" + "* @interface\n" + "*/", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testParseImplements() throws Exception { List interfaces = parse("@implements {SomeInterface}*/") .getImplementedInterfaces(); assertEquals(1, interfaces.size()); assertTypeEquals(registry.createNamedType("SomeInterface", null, -1, -1), interfaces.get(0)); } public void testParseImplementsTwo() throws Exception { List interfaces = parse( "* @implements {SomeInterface1}\n" + "* @implements {SomeInterface2}\n" + "*/") .getImplementedInterfaces(); assertEquals(2, interfaces.size()); assertTypeEquals(registry.createNamedType("SomeInterface1", null, -1, -1), interfaces.get(0)); assertTypeEquals(registry.createNamedType("SomeInterface2", null, -1, -1), interfaces.get(1)); } public void testParseImplementsSameTwice() throws Exception { parse( "* @implements {Smth}\n" + "* @implements {Smth}\n" + "*/", "Bad type annotation. duplicate @implements tag"); } public void testParseImplementsNoName() throws Exception { parse("* @implements {} */", "Bad type annotation. expecting a type name"); } public void testParseImplementsMissingRC() throws Exception { parse("* @implements {Smth */", "Bad type annotation. expected closing }"); } public void testParseImplementsNullable1() throws Exception { parse("@implements {Base?} */", "Bad type annotation. expected closing }"); } public void testParseImplementsNullable2() throws Exception { parse("@implements Base? */", "Bad type annotation. expected end of line or comment"); } public void testInterfaceExtends() throws Exception { JSDocInfo jsdoc = parse( " * @interface \n" + " * @extends {Extended} */"); assertTrue(jsdoc.isInterface()); assertEquals(1, jsdoc.getExtendedInterfacesCount()); List types = jsdoc.getExtendedInterfaces(); assertTypeEquals(registry.createNamedType("Extended", null, -1, -1), types.get(0)); } public void testInterfaceMultiExtends1() throws Exception { JSDocInfo jsdoc = parse( " * @interface \n" + " * @extends {Extended1} \n" + " * @extends {Extended2} */"); assertTrue(jsdoc.isInterface()); assertNull(jsdoc.getBaseType()); assertEquals(2, jsdoc.getExtendedInterfacesCount()); List types = jsdoc.getExtendedInterfaces(); assertTypeEquals(registry.createNamedType("Extended1", null, -1, -1), types.get(0)); assertTypeEquals(registry.createNamedType("Extended2", null, -1, -1), types.get(1)); } public void testInterfaceMultiExtends2() throws Exception { JSDocInfo jsdoc = parse( " * @extends {Extended1} \n" + " * @interface \n" + " * @extends {Extended2} \n" + " * @extends {Extended3} */"); assertTrue(jsdoc.isInterface()); assertNull(jsdoc.getBaseType()); assertEquals(3, jsdoc.getExtendedInterfacesCount()); List types = jsdoc.getExtendedInterfaces(); assertTypeEquals(registry.createNamedType("Extended1", null, -1, -1), types.get(0)); assertTypeEquals(registry.createNamedType("Extended2", null, -1, -1), types.get(1)); assertTypeEquals(registry.createNamedType("Extended3", null, -1, -1), types.get(2)); } public void testBadClassMultiExtends() throws Exception { parse(" * @extends {Extended1} \n" + " * @constructor \n" + " * @extends {Extended2} */", "Bad type annotation. type annotation incompatible with other " + "annotations"); } public void testBadExtendsWithNullable() throws Exception { JSDocInfo jsdoc = parse("@constructor\n * @extends {Object?} */", "Bad type annotation. expected closing }"); assertTrue(jsdoc.isConstructor()); assertTypeEquals(OBJECT_TYPE, jsdoc.getBaseType()); } public void testBadImplementsWithNullable() throws Exception { JSDocInfo jsdoc = parse("@implements {Disposable?}\n * @constructor */", "Bad type annotation. expected closing }"); assertTrue(jsdoc.isConstructor()); assertTypeEquals(registry.createNamedType("Disposable", null, -1, -1), jsdoc.getImplementedInterfaces().get(0)); } public void testBadTypeDefInterfaceAndConstructor1() throws Exception { JSDocInfo jsdoc = parse("@interface\n@constructor*/", "Bad type annotation. cannot be both an interface and a constructor"); assertTrue(jsdoc.isInterface()); } public void testBadTypeDefInterfaceAndConstructor2() throws Exception { JSDocInfo jsdoc = parse("@constructor\n@interface*/", "Bad type annotation. cannot be both an interface and a constructor"); assertTrue(jsdoc.isConstructor()); } public void testDocumentationParameter() throws Exception { JSDocInfo jsdoc = parse("@param {Number} number42 This is a description.*/", true); assertTrue(jsdoc.hasDescriptionForParameter("number42")); assertEquals("This is a description.", jsdoc.getDescriptionForParameter("number42")); } public void testMultilineDocumentationParameter() throws Exception { JSDocInfo jsdoc = parse("@param {Number} number42 This is a description" + "\n* on multiple \n* lines.*/", true); assertTrue(jsdoc.hasDescriptionForParameter("number42")); assertEquals("This is a description on multiple lines.", jsdoc.getDescriptionForParameter("number42")); } public void testDocumentationMultipleParameter() throws Exception { JSDocInfo jsdoc = parse("@param {Number} number42 This is a description." + "\n* @param {Integer} number87 This is another description.*/" , true); assertTrue(jsdoc.hasDescriptionForParameter("number42")); assertEquals("This is a description.", jsdoc.getDescriptionForParameter("number42")); assertTrue(jsdoc.hasDescriptionForParameter("number87")); assertEquals("This is another description.", jsdoc.getDescriptionForParameter("number87")); } public void testDocumentationMultipleParameter2() throws Exception { JSDocInfo jsdoc = parse("@param {number} delta = 0 results in a redraw\n" + " != 0 ..... */", true); assertTrue(jsdoc.hasDescriptionForParameter("delta")); assertEquals("= 0 results in a redraw != 0 .....", jsdoc.getDescriptionForParameter("delta")); } public void testAuthors() throws Exception { JSDocInfo jsdoc = parse("@param {Number} number42 This is a description." + "\n* @param {Integer} number87 This is another description." + "\n* @author a@google.com (A Person)" + "\n* @author b@google.com (B Person)" + "\n* @author c@google.com (C Person)*/" , true); Collection authors = jsdoc.getAuthors(); assertTrue(authors != null); assertTrue(authors.size() == 3); assertContains(authors, "a@google.com (A Person)"); assertContains(authors, "b@google.com (B Person)"); assertContains(authors, "c@google.com (C Person)"); } public void testSuppress1() throws Exception { JSDocInfo info = parse("@suppress {x} */"); assertEquals(Sets.newHashSet("x"), info.getSuppressions()); } public void testSuppress2() throws Exception { JSDocInfo info = parse("@suppress {x|y|x|z} */"); assertEquals(Sets.newHashSet("x", "y", "z"), info.getSuppressions()); } public void testSuppress3() throws Exception { JSDocInfo info = parse("@suppress {x,y} */"); assertEquals(Sets.newHashSet("x", "y"), info.getSuppressions()); } public void testBadSuppress1() throws Exception { parse("@suppress {} */", "malformed @suppress tag"); } public void testBadSuppress2() throws Exception { parse("@suppress {x|} */", "malformed @suppress tag"); } public void testBadSuppress3() throws Exception { parse("@suppress {|x} */", "malformed @suppress tag"); } public void testBadSuppress4() throws Exception { parse("@suppress {x|y */", "malformed @suppress tag"); } public void testBadSuppress6() throws Exception { parse("@suppress {x} \n * @suppress {y} */", "duplicate @suppress tag"); } public void testBadSuppress7() throws Exception { parse("@suppress {impossible} */", "unknown @suppress parameter: impossible"); } public void testModifies1() throws Exception { JSDocInfo info = parse("@modifies {this} */"); assertEquals(Sets.newHashSet("this"), info.getModifies()); } public void testModifies2() throws Exception { JSDocInfo info = parse("@modifies {arguments} */"); assertEquals(Sets.newHashSet("arguments"), info.getModifies()); } public void testModifies3() throws Exception { JSDocInfo info = parse("@modifies {this|arguments} */"); assertEquals(Sets.newHashSet("this", "arguments"), info.getModifies()); } public void testModifies4() throws Exception { JSDocInfo info = parse("@param {*} x\n * @modifies {x} */"); assertEquals(Sets.newHashSet("x"), info.getModifies()); } public void testModifies5() throws Exception { JSDocInfo info = parse( "@param {*} x\n" + " * @param {*} y\n" + " * @modifies {x} */"); assertEquals(Sets.newHashSet("x"), info.getModifies()); } public void testModifies6() throws Exception { JSDocInfo info = parse( "@param {*} x\n" + " * @param {*} y\n" + " * @modifies {x|y} */"); assertEquals(Sets.newHashSet("x", "y"), info.getModifies()); } public void testBadModifies1() throws Exception { parse("@modifies {} */", "malformed @modifies tag"); } public void testBadModifies2() throws Exception { parse("@modifies {this|} */", "malformed @modifies tag"); } public void testBadModifies3() throws Exception { parse("@modifies {|this} */", "malformed @modifies tag"); } public void testBadModifies4() throws Exception { parse("@modifies {this|arguments */", "malformed @modifies tag"); } public void testBadModifies5() throws Exception { parse("@modifies {this,arguments} */", "malformed @modifies tag"); } public void testBadModifies6() throws Exception { parse("@modifies {this} \n * @modifies {this} */", "conflicting @modifies tag"); } public void testBadModifies7() throws Exception { parse("@modifies {impossible} */", "unknown @modifies parameter: impossible"); } public void testBadModifies8() throws Exception { parse("@modifies {this}\n" + "@nosideeffects */", "conflicting @nosideeffects tag"); } public void testBadModifies9() throws Exception { parse("@nosideeffects\n" + "@modifies {this} */", "conflicting @modifies tag"); } //public void testNoParseFileOverview() throws Exception { // JSDocInfo jsdoc = parseFileOverviewWithoutDoc("@fileoverview Hi mom! */"); // assertNull(jsdoc.getFileOverview()); // assertTrue(jsdoc.hasFileOverview()); //} public void testFileOverviewSingleLine() throws Exception { JSDocInfo jsdoc = parseFileOverview("@fileoverview Hi mom! */"); assertEquals("Hi mom!", jsdoc.getFileOverview()); } public void testFileOverviewMultiLine() throws Exception { JSDocInfo jsdoc = parseFileOverview("@fileoverview Pie is \n * good! */"); assertEquals("Pie is\n good!", jsdoc.getFileOverview()); } public void testFileOverviewDuplicate() throws Exception { parseFileOverview( "@fileoverview Pie \n * @fileoverview Cake */", "extra @fileoverview tag"); } public void testReferences() throws Exception { JSDocInfo jsdoc = parse("@see A cool place!" + "\n* @see The world." + "\n* @see SomeClass#SomeMember" + "\n* @see A boring test case*/" , true); Collection references = jsdoc.getReferences(); assertTrue(references != null); assertTrue(references.size() == 4); assertContains(references, "A cool place!"); assertContains(references, "The world."); assertContains(references, "SomeClass#SomeMember"); assertContains(references, "A boring test case"); } public void testSingleTags() throws Exception { JSDocInfo jsdoc = parse("@version Some old version" + "\n* @deprecated In favor of the new one!" + "\n* @return {SomeType} The most important object :-)*/" , true); assertTrue(jsdoc.isDeprecated()); assertEquals("In favor of the new one!", jsdoc.getDeprecationReason()); assertEquals("Some old version", jsdoc.getVersion()); assertEquals("The most important object :-)", jsdoc.getReturnDescription()); } public void testSingleTagsReordered() throws Exception { JSDocInfo jsdoc = parse("@deprecated In favor of the new one!" + "\n * @return {SomeType} The most important object :-)" + "\n * @version Some old version*/" , true); assertTrue(jsdoc.isDeprecated()); assertEquals("In favor of the new one!", jsdoc.getDeprecationReason()); assertEquals("Some old version", jsdoc.getVersion()); assertEquals("The most important object :-)", jsdoc.getReturnDescription()); } public void testVersionDuplication() throws Exception { parse("* @version Some old version" + "\n* @version Another version*/", true, "conflicting @version tag"); } public void testVersionMissing() throws Exception { parse("* @version */", true, "@version tag missing version information"); } public void testAuthorMissing() throws Exception { parse("* @author */", true, "@author tag missing author"); } public void testSeeMissing() throws Exception { parse("* @see */", true, "@see tag missing description"); } public void testSourceName() throws Exception { JSDocInfo jsdoc = parse("@deprecated */", true); assertEquals("testcode", jsdoc.getAssociatedNode().getSourceFileName()); } public void testParseBlockComment() throws Exception { JSDocInfo jsdoc = parse("this is a nice comment\n " + "* that is multiline \n" + "* @author abc@google.com */", true); assertEquals("this is a nice comment\nthat is multiline", jsdoc.getBlockDescription()); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "author", 2, 2), "abc@google.com", 9, 2, 23); } public void testParseBlockComment2() throws Exception { JSDocInfo jsdoc = parse("this is a nice comment\n " + "* that is *** multiline \n" + "* @author abc@google.com */", true); assertEquals("this is a nice comment\nthat is *** multiline", jsdoc.getBlockDescription()); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "author", 2, 2), "abc@google.com", 9, 2, 23); } public void testParseBlockComment3() throws Exception { JSDocInfo jsdoc = parse("\n " + "* hello world \n" + "* @author abc@google.com */", true); assertEquals("hello world", jsdoc.getBlockDescription()); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "author", 2, 2), "abc@google.com", 9, 2, 23); } public void testParseWithMarkers1() throws Exception { JSDocInfo jsdoc = parse("@author abc@google.com */", true); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "author", 0, 0), "abc@google.com", 7, 0, 21); } public void testParseWithMarkers2() throws Exception { JSDocInfo jsdoc = parse("@param {Foo} somename abc@google.com */", true); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "param", 0, 0), "abc@google.com", 21, 0, 37); } public void testParseWithMarkers3() throws Exception { JSDocInfo jsdoc = parse("@return {Foo} some long \n * multiline" + " \n * description */", true); JSDocInfo.Marker returnDoc = assertAnnotationMarker(jsdoc, "return", 0, 0); assertDocumentationInMarker(returnDoc, "some long multiline description", 13, 2, 15); assertEquals(8, returnDoc.getType().getPositionOnStartLine()); assertEquals(12, returnDoc.getType().getPositionOnEndLine()); } public void testParseWithMarkers4() throws Exception { JSDocInfo jsdoc = parse("@author foobar \n * @param {Foo} somename abc@google.com */", true); assertAnnotationMarker(jsdoc, "author", 0, 0); assertAnnotationMarker(jsdoc, "param", 1, 3); } public void testParseWithMarkers5() throws Exception { JSDocInfo jsdoc = parse("@return some long \n * multiline" + " \n * description */", true); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "return", 0, 0), "some long multiline description", 8, 2, 15); } public void testParseWithMarkers6() throws Exception { JSDocInfo jsdoc = parse("@param x some long \n * multiline" + " \n * description */", true); assertDocumentationInMarker( assertAnnotationMarker(jsdoc, "param", 0, 0), "some long multiline description", 8, 2, 15); } public void testParseWithMarkerNames1() throws Exception { JSDocInfo jsdoc = parse("@param {SomeType} name somedescription */", true); assertNameInMarker( assertAnnotationMarker(jsdoc, "param", 0, 0), "name", 0, 18); } public void testParseWithMarkerNames2() throws Exception { JSDocInfo jsdoc = parse("@param {SomeType} name somedescription \n" + "* @param {AnotherType} anothername des */", true); assertTypeInMarker( assertNameInMarker( assertAnnotationMarker(jsdoc, "param", 0, 0, 0), "name", 0, 18), "SomeType", 0, 7, 0, 16, true); assertTypeInMarker( assertNameInMarker( assertAnnotationMarker(jsdoc, "param", 1, 2, 1), "anothername", 1, 23), "AnotherType", 1, 9, 1, 21, true); } public void testParseWithMarkerNames3() throws Exception { JSDocInfo jsdoc = parse( "@param {Some.Long.Type.\n * Name} name somedescription */", true); assertTypeInMarker( assertNameInMarker( assertAnnotationMarker(jsdoc, "param", 0, 0, 0), "name", 1, 10), "Some.Long.Type.Name", 0, 7, 1, 8, true); } @SuppressWarnings("deprecation") public void testParseWithoutMarkerName() throws Exception { JSDocInfo jsdoc = parse("@author helloworld*/", true); assertNull(assertAnnotationMarker(jsdoc, "author", 0, 0).getName()); } public void testParseWithMarkerType() throws Exception { JSDocInfo jsdoc = parse("@extends {FooBar}*/", true); assertTypeInMarker( assertAnnotationMarker(jsdoc, "extends", 0, 0), "FooBar", 0, 9, 0, 16, true); } public void testParseWithMarkerType2() throws Exception { JSDocInfo jsdoc = parse("@extends FooBar*/", true); assertTypeInMarker( assertAnnotationMarker(jsdoc, "extends", 0, 0), "FooBar", 0, 9, 0, 15, false); } public void testTypeTagConflict1() throws Exception { parse("@constructor \n * @constructor */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict2() throws Exception { parse("@interface \n * @interface */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict3() throws Exception { parse("@constructor \n * @interface */", "Bad type annotation. cannot be both an interface and a constructor"); } public void testTypeTagConflict4() throws Exception { parse("@interface \n * @constructor */", "Bad type annotation. cannot be both an interface and a constructor"); } public void testTypeTagConflict5() throws Exception { parse("@interface \n * @type {string} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict6() throws Exception { parse("@typedef {string} \n * @type {string} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict7() throws Exception { parse("@typedef {string} \n * @constructor */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict8() throws Exception { parse("@typedef {string} \n * @return {boolean} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict9() throws Exception { parse("@enum {string} \n * @return {boolean} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict10() throws Exception { parse("@this {Object} \n * @enum {boolean} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict11() throws Exception { parse("@param {Object} x \n * @type {boolean} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict12() throws Exception { parse("@typedef {boolean} \n * @param {Object} x */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict13() throws Exception { parse("@typedef {boolean} \n * @extends {Object} */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict14() throws Exception { parse("@return x \n * @return y */", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict15() throws Exception { parse("/**\n" + " * @struct\n" + " * @struct\n" + " */\n" + "function StrStr() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict16() throws Exception { parse("/**\n" + " * @struct\n" + " * @interface\n" + " */\n" + "function StrIntf() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict17() throws Exception { parse("/**\n" + " * @interface\n" + " * @struct\n" + " */\n" + "function StrIntf() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict18() throws Exception { parse("/**\n" + " * @dict\n" + " * @dict\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict19() throws Exception { parse("/**\n" + " * @dict\n" + " * @interface\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict20() throws Exception { parse("/**\n" + " * @interface\n" + " * @dict\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict21() throws Exception { parse("/**\n" + " * @private {string}\n" + " * @type {number}\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict22() throws Exception { parse("/**\n" + " * @protected {string}\n" + " * @param {string} x\n" + " */\n" + "function DictDict(x) {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict23() throws Exception { parse("/**\n" + " * @public {string}\n" + " * @return {string} x\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testTypeTagConflict24() throws Exception { parse("/**\n" + " * @const {string}\n" + " * @return {string} x\n" + " */\n" + "function DictDict() {}", "Bad type annotation. " + "type annotation incompatible with other annotations"); } public void testPrivateType() throws Exception { JSDocInfo jsdoc = parse("@private {string} */"); assertTypeEquals(STRING_TYPE, jsdoc.getType()); } public void testProtectedType() throws Exception { JSDocInfo jsdoc = parse("@protected {string} */"); assertTypeEquals(STRING_TYPE, jsdoc.getType()); } public void testPublicType() throws Exception { JSDocInfo jsdoc = parse("@public {string} */"); assertTypeEquals(STRING_TYPE, jsdoc.getType()); } public void testConstType() throws Exception { JSDocInfo jsdoc = parse("@const {string} */"); assertTypeEquals(STRING_TYPE, jsdoc.getType()); } public void testStableIdGeneratorConflict() throws Exception { parse("/**\n" + " * @stableIdGenerator\n" + " * @stableIdGenerator\n" + " */\n" + "function getId() {}", "extra @stableIdGenerator tag"); } public void testParserWithTemplateTypeNameMissing() { parse("@template */", "Bad type annotation. @template tag missing type name"); } public void testParserWithTemplateDuplicated() { parse("@template T\n@template V */", "Bad type annotation. @template tag at most once"); } public void testParserWithTwoTemplates() { parse("@template T,V */"); } public void testParserWithClassTemplateTypeNameMissing() { parse("@classTemplate */", "Bad type annotation. @classTemplate tag missing type name"); } public void testParserWithClassTemplateDuplicated() { parse("@classTemplate T\n@classTemplate V */", "Bad type annotation. @classTemplate tag at most once"); } public void testParserWithTwoClassTemplates() { parse("@classTemplate T,V */"); } public void testParserWithClassTemplatesAndTemplate() { parse("@template T\n@classTemplate T,V */"); } public void testWhitelistedNewAnnotations() { parse("@foobar */", "illegal use of unknown JSDoc tag \"foobar\"; ignoring it"); extraAnnotations.add("foobar"); parse("@foobar */"); } public void testWhitelistedConflictingAnnotation() { extraAnnotations.add("param"); JSDocInfo info = parse("@param {number} index */"); assertTypeEquals(NUMBER_TYPE, info.getParameterType("index")); } public void testNonIdentifierAnnotation() { // Try to whitelist an annotation that is not a valid JS identifier. // It should not work. extraAnnotations.add("123"); parse("@123 */", "illegal use of unknown JSDoc tag \"\"; ignoring it"); } public void testUnsupportedJsDocSyntax1() { JSDocInfo info = parse("@param {string} [accessLevel=\"author\"] The user level */", true); assertEquals(1, info.getParameterCount()); assertTypeEquals( registry.createOptionalType(STRING_TYPE), info.getParameterType("accessLevel")); assertEquals("The user level", info.getDescriptionForParameter("accessLevel")); } public void testUnsupportedJsDocSyntax2() { JSDocInfo info = parse("@param userInfo The user info. \n" + " * @param userInfo.name The name of the user */", true); assertEquals(1, info.getParameterCount()); assertEquals("The user info.", info.getDescriptionForParameter("userInfo")); } public void testWhitelistedAnnotations() { parse( "* @addon \n" + "* @ngInject \n" + "* @augments \n" + "* @base \n" + "* @borrows \n" + "* @bug \n" + "* @class \n" + "* @config \n" + "* @constructs \n" + "* @default \n" + "* @description \n" + "* @event \n" + "* @example \n" + "* @exception \n" + "* @exec \n" + "* @externs \n" + "* @field \n" + "* @function \n" + "* @id \n" + "* @ignore \n" + "* @inner \n" + "* @lends {string} \n" + "* @link \n" + "* @member \n" + "* @memberOf \n" + "* @modName \n" + "* @mods \n" + "* @name \n" + "* @namespace \n" + "* @nocompile \n" + "* @property \n" + "* @requires \n" + "* @since \n" + "* @static \n" + "* @supported */"); } public void testGetOriginalCommentString() throws Exception { String comment = "* @desc This is a comment */"; JSDocInfo info = parse(comment); assertNull(info.getOriginalCommentString()); info = parse(comment, true /* parseDocumentation */); assertEquals(comment, info.getOriginalCommentString()); } public void testParseNgInject1() throws Exception { assertTrue(parse("@ngInject*/").isNgInject()); } public void testParseNgInject2() throws Exception { parse("@ngInject \n@ngInject*/", "extra @ngInject tag"); } public void testTextExtents() { parse("@return {@code foo} bar \n * baz. */", true, "Bad type annotation. type not recognized due to syntax error"); } /** * Asserts that a documentation field exists on the given marker. * * @param description The text of the documentation field expected. * @param startCharno The starting character of the text. * @param endLineno The ending line of the text. * @param endCharno The ending character of the text. * @return The marker, for chaining purposes. */ private JSDocInfo.Marker assertDocumentationInMarker(JSDocInfo.Marker marker, String description, int startCharno, int endLineno, int endCharno) { assertTrue(marker.getDescription() != null); assertEquals(description, marker.getDescription().getItem()); // Match positional information. assertEquals(marker.getAnnotation().getStartLine(), marker.getDescription().getStartLine()); assertEquals(startCharno, marker.getDescription().getPositionOnStartLine()); assertEquals(endLineno, marker.getDescription().getEndLine()); assertEquals(endCharno, marker.getDescription().getPositionOnEndLine()); return marker; } /** * Asserts that a type field exists on the given marker. * * @param typeName The name of the type expected in the type field. * @param startCharno The starting character of the type declaration. * @param hasBrackets Whether the type in the type field is expected * to have brackets. * @return The marker, for chaining purposes. */ private JSDocInfo.Marker assertTypeInMarker( JSDocInfo.Marker marker, String typeName, int startLineno, int startCharno, int endLineno, int endCharno, boolean hasBrackets) { assertTrue(marker.getType() != null); assertTrue(marker.getType().getItem().isString()); // Match the name and brackets information. String foundName = marker.getType().getItem().getString(); assertEquals(typeName, foundName); assertEquals(hasBrackets, marker.getType().hasBrackets()); // Match position information. assertEquals(startCharno, marker.getType().getPositionOnStartLine()); assertEquals(endCharno, marker.getType().getPositionOnEndLine()); assertEquals(startLineno, marker.getType().getStartLine()); assertEquals(endLineno, marker.getType().getEndLine()); return marker; } /** * Asserts that a name field exists on the given marker. * * @param name The name expected in the name field. * @param startCharno The starting character of the text. * @return The marker, for chaining purposes. */ @SuppressWarnings("deprecation") private JSDocInfo.Marker assertNameInMarker(JSDocInfo.Marker marker, String name, int startLine, int startCharno) { assertTrue(marker.getName() != null); assertEquals(name, marker.getName().getItem()); assertEquals(startCharno, marker.getName().getPositionOnStartLine()); assertEquals(startCharno + name.length(), marker.getName().getPositionOnEndLine()); assertEquals(startLine, marker.getName().getStartLine()); assertEquals(startLine, marker.getName().getEndLine()); return marker; } /** * Asserts that an annotation marker of a given annotation name * is found in the given JSDocInfo. * * @param jsdoc The JSDocInfo in which to search for the annotation marker. * @param annotationName The name/type of the annotation for which to * search. Example: "author" for an "@author" annotation. * @param startLineno The expected starting line number of the marker. * @param startCharno The expected character on the starting line. * @return The marker found, for further testing. */ private JSDocInfo.Marker assertAnnotationMarker(JSDocInfo jsdoc, String annotationName, int startLineno, int startCharno) { return assertAnnotationMarker(jsdoc, annotationName, startLineno, startCharno, 0); } /** * Asserts that the index-th annotation marker of a given annotation name * is found in the given JSDocInfo. * * @param jsdoc The JSDocInfo in which to search for the annotation marker. * @param annotationName The name/type of the annotation for which to * search. Example: "author" for an "@author" annotation. * @param startLineno The expected starting line number of the marker. * @param startCharno The expected character on the starting line. * @param index The index of the marker. * @return The marker found, for further testing. */ private JSDocInfo.Marker assertAnnotationMarker(JSDocInfo jsdoc, String annotationName, int startLineno, int startCharno, int index) { Collection markers = jsdoc.getMarkers(); assertTrue(markers.size() > 0); int counter = 0; for (JSDocInfo.Marker marker : markers) { if (marker.getAnnotation() != null) { if (annotationName.equals(marker.getAnnotation().getItem())) { if (counter == index) { assertEquals(startLineno, marker.getAnnotation().getStartLine()); assertEquals(startCharno, marker.getAnnotation().getPositionOnStartLine()); assertEquals(startLineno, marker.getAnnotation().getEndLine()); assertEquals(startCharno + annotationName.length(), marker.getAnnotation().getPositionOnEndLine()); return marker; } counter++; } } } fail("No marker found"); return null; } private void assertContains(Collection collection, T item) { assertTrue(collection.contains(item)); } private void parseFull(String code, String... warnings) { CompilerEnvirons environment = new CompilerEnvirons(); TestErrorReporter testErrorReporter = new TestErrorReporter(null, warnings); environment.setErrorReporter(testErrorReporter); environment.setRecordingComments(true); environment.setRecordingLocalJsDocComments(true); Parser p = new Parser(environment, testErrorReporter); AstRoot script = p.parse(code, null, 0); Config config = new Config(extraAnnotations, extraSuppressions, true, LanguageMode.ECMASCRIPT3, false); for (Comment comment : script.getComments()) { JsDocInfoParser jsdocParser = new JsDocInfoParser( new JsDocTokenStream(comment.getValue().substring(3), comment.getLineno()), comment, null, config, testErrorReporter); jsdocParser.parse(); jsdocParser.retrieveAndResetParsedJSDocInfo(); } assertTrue("some expected warnings were not reported", testErrorReporter.hasEncounteredAllWarnings()); } @SuppressWarnings("unused") private JSDocInfo parseFileOverviewWithoutDoc(String comment, String... warnings) { return parse(comment, false, true, warnings); } private JSDocInfo parseFileOverview(String comment, String... warnings) { return parse(comment, true, true, warnings); } private JSDocInfo parse(String comment, String... warnings) { return parse(comment, false, warnings); } private JSDocInfo parse(String comment, boolean parseDocumentation, String... warnings) { return parse(comment, parseDocumentation, false, warnings); } private JSDocInfo parse(String comment, boolean parseDocumentation, boolean parseFileOverview, String... warnings) { TestErrorReporter errorReporter = new TestErrorReporter(null, warnings); Config config = new Config(extraAnnotations, extraSuppressions, parseDocumentation, LanguageMode.ECMASCRIPT3, false); StaticSourceFile file = new SimpleSourceFile("testcode", false); Node associatedNode = new Node(Token.SCRIPT); associatedNode.setInputId(new InputId(file.getName())); associatedNode.setStaticSourceFile(file); JsDocInfoParser jsdocParser = new JsDocInfoParser( stream(comment), new Comment(0, 0, CommentType.JSDOC, comment), associatedNode, config, errorReporter); if (fileLevelJsDocBuilder != null) { jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); } jsdocParser.parse(); assertTrue("expected warnings were not reported", errorReporter.hasEncounteredAllWarnings()); if (parseFileOverview) { return jsdocParser.getFileOverviewJSDocInfo(); } else { return jsdocParser.retrieveAndResetParsedJSDocInfo(); } } private Node parseType(String typeComment) { return JsDocInfoParser.parseTypeString(typeComment); } private JsDocTokenStream stream(String source) { return new JsDocTokenStream(source, 0); } private void assertTemplatizedTypeEquals(TemplateType key, JSType expected, JSTypeExpression te) { assertEquals( expected, resolve(te).getTemplateTypeMap().getTemplateType(key)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/parsing/JsDocTokenStreamTest.java0000644000175000017500000003377312115204405031366 0ustar apoapo/* * 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.parsing; import static com.google.javascript.jscomp.parsing.JsDocToken.ANNOTATION; import static com.google.javascript.jscomp.parsing.JsDocToken.BANG; import static com.google.javascript.jscomp.parsing.JsDocToken.COLON; import static com.google.javascript.jscomp.parsing.JsDocToken.COMMA; import static com.google.javascript.jscomp.parsing.JsDocToken.ELLIPSIS; import static com.google.javascript.jscomp.parsing.JsDocToken.EOC; import static com.google.javascript.jscomp.parsing.JsDocToken.EOF; import static com.google.javascript.jscomp.parsing.JsDocToken.EOL; import static com.google.javascript.jscomp.parsing.JsDocToken.EQUALS; import static com.google.javascript.jscomp.parsing.JsDocToken.GT; import static com.google.javascript.jscomp.parsing.JsDocToken.LB; import static com.google.javascript.jscomp.parsing.JsDocToken.LC; import static com.google.javascript.jscomp.parsing.JsDocToken.LP; import static com.google.javascript.jscomp.parsing.JsDocToken.LT; import static com.google.javascript.jscomp.parsing.JsDocToken.PIPE; import static com.google.javascript.jscomp.parsing.JsDocToken.QMARK; import static com.google.javascript.jscomp.parsing.JsDocToken.RB; import static com.google.javascript.jscomp.parsing.JsDocToken.RC; import static com.google.javascript.jscomp.parsing.JsDocToken.RP; import static com.google.javascript.jscomp.parsing.JsDocToken.STAR; import static com.google.javascript.jscomp.parsing.JsDocToken.STRING; import com.google.common.collect.ImmutableList; import junit.framework.TestCase; import java.util.List; /** * Tests for {@link JsDocTokenStream}. */ public class JsDocTokenStreamTest extends TestCase { public void testJsDocTokenization1() throws Exception { List tokens = ImmutableList.of( STAR, ANNOTATION, LC, STRING, RC, EOL, STAR, ANNOTATION); List strings = ImmutableList.of("type", "string", "private"); testJSDocTokenStream(" * @type {string}\n * @private", tokens, strings); testJSDocTokenStream(" * @type { string } \n * @private", tokens, strings); testJSDocTokenStream(" * @type { string}\n * @private", tokens, strings); testJSDocTokenStream(" * @type {string }\n * @private", tokens, strings); testJSDocTokenStream(" * @type {string}\n * @private", tokens, strings); testJSDocTokenStream(" * @type {string} \n * @private", tokens, strings); } public void testJsDocTokenization2() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, STRING, LT, STRING, PIPE, STRING, GT, RC); List strings = ImmutableList.of("param", "Array", "string", "null"); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream(" @param {Array.}", tokens, strings); testJSDocTokenStream(" @param {Array.}", tokens, strings); testJSDocTokenStream("@param {Array .}", tokens, strings); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream("@param { Array.}", tokens, strings); testJSDocTokenStream("@param {Array.} ", tokens, strings); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream(" @param { Array .< string |null > } ", tokens, strings); } public void testJsDocTokenization3() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, STRING, LT, STRING, PIPE, STRING, GT, RC); List strings = ImmutableList.of("param", "Array", "string", "null"); testJSDocTokenStream("@param {Array.}", tokens, strings); testJSDocTokenStream("@param {Array.< string || null> }", tokens, strings); testJSDocTokenStream("@param {Array. } ", tokens, strings); testJSDocTokenStream("@param {Array .}", tokens, strings); testJSDocTokenStream("@param {Array.< string||null>}", tokens, strings); testJSDocTokenStream("@param { Array.}", tokens, strings); testJSDocTokenStream(" @param {Array.}", tokens, strings); testJSDocTokenStream("@param { Array. }", tokens, strings); } public void testJsDocTokenization4() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, STRING, LT, LP, STRING, COMMA, STRING, RP, GT, RC, EOF); List strings = ImmutableList.of("param", "Array", "string", "null"); testJSDocTokenStream("@param {Array.<(string,null)>}", tokens, strings); testJSDocTokenStream("@param {Array .<(string,null)> } ", tokens, strings); testJSDocTokenStream(" @param {Array.< ( string,null)>}", tokens, strings); testJSDocTokenStream("@param {Array.<(string , null)>}", tokens, strings); testJSDocTokenStream("@param {Array.<(string, null) > } ", tokens, strings); testJSDocTokenStream("@param { Array .< (string,null)>} ", tokens, strings); } public void testJsDocTokenization5() throws Exception { List tokens = ImmutableList.of(ANNOTATION, STRING, EOC, EOF); List strings = ImmutableList.of("param", "foo.Bar"); testJSDocTokenStream("@param foo.Bar*/", tokens, strings); testJSDocTokenStream(" @param foo.Bar*/", tokens, strings); testJSDocTokenStream(" @param foo.Bar */", tokens, strings); } public void testJsDocTokenization6() throws Exception { List tokens = ImmutableList.of( ANNOTATION, EOL, ANNOTATION, EOL, ANNOTATION, EOC); List strings = ImmutableList.of("hidden", "static", "desc"); testJSDocTokenStream("@hidden\n@static\n@desc*/", tokens, strings); testJSDocTokenStream("@hidden\n @static\n@desc*/", tokens, strings); testJSDocTokenStream("@hidden\n@static\n @desc*/", tokens, strings); testJSDocTokenStream("@hidden\n@static\n@desc */", tokens, strings); testJSDocTokenStream(" @hidden \n@static\n @desc*/", tokens, strings); testJSDocTokenStream("@hidden\n@static \n @desc */", tokens, strings); testJSDocTokenStream("@hidden\n@static\n@desc*/", tokens, strings); testJSDocTokenStream("@hidden \n@static \n @desc*/", tokens, strings); } public void testJsDocTokenization7() throws Exception { List tokens = ImmutableList.of( ELLIPSIS, ELLIPSIS, ELLIPSIS, ELLIPSIS, ELLIPSIS, LT, EOC); List strings = ImmutableList.of(); testJSDocTokenStream("................<*/", tokens, strings); testJSDocTokenStream("............... .<*/", tokens, strings); testJSDocTokenStream("................< */", tokens, strings); testJSDocTokenStream("............... .< */", tokens, strings); testJSDocTokenStream("............... .< */ ", tokens, strings); testJSDocTokenStream(" ............... .< */ ", tokens, strings); } public void testJsDocTokenization8() throws Exception { List tokens = ImmutableList.of( STAR, ANNOTATION, STRING, STRING, STRING, STRING, STRING, STRING, STRING, EOL, EOC); List strings = ImmutableList.of( "param", "foo.Bar", "opt_name", "this", "parameter", "is", "a", "name"); testJSDocTokenStream( " * @param foo.Bar opt_name this parameter is a name\n" + " */", tokens, strings); testJSDocTokenStream( " * @param foo.Bar opt_name this parameter is a name \n" + " */ ", tokens, strings); } public void testJsDocTokenization9() throws Exception { List tokens = ImmutableList.of( STAR, ANNOTATION, STRING, STRING, STRING, STRING, STRING, ANNOTATION, STRING, EOL, EOC); List strings = ImmutableList.of( "param", "foo.Bar", "opt_name", "this", "parameter", "does", "media", "blah"); testJSDocTokenStream( " * @param foo.Bar opt_name this parameter does @media blah\n" + " */", tokens, strings); } public void testJsDocTokenization10() throws Exception { List tokens = ImmutableList.of(STRING, GT, EOC); List strings = ImmutableList.of("Array*/", tokens, strings); } public void testJsDocTokenization11() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, STRING, QMARK, RC, EOC, EOF); List strings = ImmutableList.of("param", "string"); testJSDocTokenStream("@param {string?}*/", tokens, strings); testJSDocTokenStream(" @param {string?}*/", tokens, strings); testJSDocTokenStream("@param { string?}*/", tokens, strings); testJSDocTokenStream("@param {string ?}*/", tokens, strings); testJSDocTokenStream("@param {string ? } */", tokens, strings); testJSDocTokenStream("@param { string ? }*/", tokens, strings); testJSDocTokenStream("@param {string? }*/", tokens, strings); } public void testJsDocTokenization12() throws Exception { List tokens = ImmutableList.of(STRING, ELLIPSIS, EOC); List strings = ImmutableList.of("function"); testJSDocTokenStream("function ...*/", tokens, strings); } public void testJsDocTokenization13() throws Exception { List tokens = ImmutableList.of(ELLIPSIS, LB, STRING, RB, EOC); List strings = ImmutableList.of("number"); testJSDocTokenStream("...[number]*/", tokens, strings); } public void testJsDocTokenization14() throws Exception { // Since ES4 type parsing only requires to parse an ellipsis when it is // followed by a comma (,) we are allowing this case to parse this way. // This is a simplification of the tokenizer, but the extra complexity is // never used. List tokens = ImmutableList.of(STRING, LB, STRING, EOC); List strings = ImmutableList.of("foo", "bar..."); testJSDocTokenStream("foo[ bar...*/", tokens, strings); } public void testJsDocTokenization15() throws Exception { List tokens = ImmutableList.of( STRING, LB, STRING, COMMA, ELLIPSIS, EOC); List strings = ImmutableList.of("foo", "bar"); testJSDocTokenStream("foo[ bar,...*/", tokens, strings); testJSDocTokenStream("foo[ bar ,...*/", tokens, strings); testJSDocTokenStream("foo[bar, ...*/", tokens, strings); testJSDocTokenStream("foo[ bar , ... */", tokens, strings); testJSDocTokenStream("foo [bar,... */", tokens, strings); } public void testJsDocTokenization16() throws Exception { List tokens = ImmutableList.of( STRING, COLON, COLON, COLON, ELLIPSIS, STRING, COLON, STRING, EOC); List strings = ImmutableList.of("foo", "bar", "bar2"); testJSDocTokenStream("foo:::...bar:bar2*/", tokens, strings); } public void testJsDocTokenization17() throws Exception { List tokens = ImmutableList.of(STRING, EOL, EOC); List strings = ImmutableList.of(".."); testJSDocTokenStream("..\n*/", tokens, strings); } public void testJsDocTokenization18() throws Exception { List tokens = ImmutableList.of(STRING, EOL, EOC); List strings = ImmutableList.of("."); testJSDocTokenStream(".\n*/", tokens, strings); } public void testJsDocTokenization19() throws Exception { List tokens = ImmutableList.of(ANNOTATION, LC, STAR, RC, EOC); List strings = ImmutableList.of("type", "*"); testJSDocTokenStream("@type {*}*/", tokens, strings); } public void testJsDocTokenization20() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, BANG, STRING, RC, EOC, EOF); List strings = ImmutableList.of("param", "Object"); testJSDocTokenStream("@param {!Object}*/", tokens, strings); testJSDocTokenStream(" @param {!Object}*/", tokens, strings); testJSDocTokenStream("@param {! Object}*/", tokens, strings); testJSDocTokenStream("@param { !Object}*/", tokens, strings); testJSDocTokenStream("@param {!Object } */", tokens, strings); testJSDocTokenStream("@param { ! Object }*/", tokens, strings); testJSDocTokenStream("@param {!Object }*/", tokens, strings); } public void testJsDocTokenization21() throws Exception { List tokens = ImmutableList.of( ANNOTATION, LC, STRING, EQUALS, RC, EOC, EOF); List strings = ImmutableList.of("param", "Object"); testJSDocTokenStream("@param {Object=}*/", tokens, strings); testJSDocTokenStream(" @param {Object=}*/", tokens, strings); testJSDocTokenStream("@param { Object =}*/", tokens, strings); testJSDocTokenStream("@param { Object=}*/", tokens, strings); testJSDocTokenStream("@param {Object= } */", tokens, strings); testJSDocTokenStream("@param { Object = }*/", tokens, strings); testJSDocTokenStream("@param {Object= }*/", tokens, strings); } private void testJSDocTokenStream(String comment, List tokens, List strings) { JsDocTokenStream stream = new JsDocTokenStream(comment, 0); int stringsIndex = 0; for (JsDocToken token : tokens) { JsDocToken readToken = stream.getJsDocToken(); // token equality if (token != readToken) { assertEquals(token, readToken); } // string equality if (token == ANNOTATION || token == STRING) { assertEquals(strings.get(stringsIndex++), stream.getString()); } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/parsing/IRFactoryTest.java0000644000175000017500000010156012115204405030037 0ustar apoapo/* * 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.parsing; import com.google.common.collect.Sets; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.jscomp.parsing.Config.LanguageMode; import com.google.javascript.jscomp.testing.TestErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.head.CompilerEnvirons; import com.google.javascript.rhino.head.Parser; import com.google.javascript.rhino.head.ast.AstRoot; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; /** * Tests {@link IRFactory}. */ public class IRFactoryTest extends BaseJSTypeTestCase { private LanguageMode mode = LanguageMode.ECMASCRIPT3; @Override protected void setUp() throws Exception { super.setUp(); mode = LanguageMode.ECMASCRIPT3; } public void testStrictScript() throws Exception { assertNull(newParse("").getDirectives()); assertEquals( Sets.newHashSet("use strict"), newParse("'use strict'").getDirectives()); } public void testArrayLiteral2() throws Exception { testNewParser("[a, , b]", "SCRIPT 1 [source_file: FileName.js] [length: 8]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 8]\n" + " ARRAYLIT 1 [source_file: FileName.js] [length: 8]\n" + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + " NAME b 1 [source_file: FileName.js] [length: 1]\n"); } public void testArrayLiteral4() throws Exception { testNewParser("[,,,a,,b]", "SCRIPT 1 [source_file: FileName.js] [length: 9]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 9]\n" + " ARRAYLIT 1 [source_file: FileName.js] [length: 9]\n" + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + " EMPTY 1 [source_file: FileName.js] [length: 1]\n" + " NAME b 1 [source_file: FileName.js] [length: 1]\n"); } public void testObjectLiteral() { newParse("var o = {}"); } public void testObjectLiteral2() { newParse("var o = {a: 1}"); } public void testObjectLiteral3() { newParse("var o = {a: 1, b: 2}"); } public void testObjectLiteral4() { newParse("var o = {1: 'a'}"); } public void testObjectLiteral5() { newParse("var o = {'a': 'a'}"); } public void testObjectLiteral6() { testNewParser("({1: true})", "SCRIPT 1 [source_file: FileName.js] [length: 11]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 10]\n" + " OBJECTLIT 1 [source_file: FileName.js] [length: 9]\n" + " STRING_KEY 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + " TRUE 1 [source_file: FileName.js] [length: 4]\n"); } public void testObjectLiteral7() { mode = LanguageMode.ECMASCRIPT5; testNewParser("({get 1() {}})", "SCRIPT 1 [source_file: FileName.js] [length: 14]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 13]\n" + " OBJECTLIT 1 [source_file: FileName.js] [length: 12]\n" + " GETTER_DEF 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + " FUNCTION 1 [source_file: FileName.js] [length: 6]\n" + " NAME 1 [source_file: FileName.js]\n" + " PARAM_LIST 1 [source_file: FileName.js]\n" + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); } public void testObjectLiteral8() { mode = LanguageMode.ECMASCRIPT5; testNewParser("({set 1(a) {}})", "SCRIPT 1 [source_file: FileName.js] [length: 15]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 14]\n" + " OBJECTLIT 1 [source_file: FileName.js] [length: 13]\n" + " SETTER_DEF 1 1 [quoted: 1] [source_file: FileName.js] [length: 1]\n" + " FUNCTION 1 [source_file: FileName.js] [length: 7]\n" + " NAME 1 [source_file: FileName.js]\n" + " PARAM_LIST 1 [source_file: FileName.js]\n" + " NAME a 1 [source_file: FileName.js] [length: 1]\n" + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); } // The old and new parser produce different results now with labels, and // named breaks and continues, so disable these tests. public void testLabel() { testNewParser("foo: bar", "SCRIPT 1 [source_file: FileName.js] [length: 8]\n" + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + " LABEL_NAME foo 1 [source_file: FileName.js] [length: 4]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 3]\n" + " NAME bar 1 [source_file: FileName.js] [length: 3]\n"); } public void testLabel2() { testNewParser("l: while (f()) { if (g()) { continue l; } }", "SCRIPT 1 [source_file: FileName.js] [length: 43]\n" + " LABEL 1 [source_file: FileName.js] [length: 2]\n" + " LABEL_NAME l 1 [source_file: FileName.js] [length: 2]\n" + " WHILE 1 [source_file: FileName.js] [length: 40]\n" + " CALL 1 [source_file: FileName.js] [length: 3]\n" + " NAME f 1 [source_file: FileName.js] [length: 1]\n" + " BLOCK 1 [source_file: FileName.js] [length: 28]\n" + " IF 1 [source_file: FileName.js] [length: 24]\n" + " CALL 1 [source_file: FileName.js] [length: 3]\n" + " NAME g 1 [source_file: FileName.js] [length: 1]\n" + " BLOCK 1 [source_file: FileName.js] [length: 15]\n" + " CONTINUE 1 [source_file: FileName.js] [length: 11]\n" + " LABEL_NAME l 1 [source_file: FileName.js] [length: 1]\n"); } public void testLabel3() { testNewParser("Foo:Bar:X:{ break Bar; }", "SCRIPT 1 [source_file: FileName.js] [length: 24]\n" + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + " LABEL_NAME Foo 1 [source_file: FileName.js] [length: 4]\n" + " LABEL 1 [source_file: FileName.js] [length: 4]\n" + " LABEL_NAME Bar 1 [source_file: FileName.js] [length: 4]\n" + " LABEL 1 [source_file: FileName.js] [length: 2]\n" + " LABEL_NAME X 1 [source_file: FileName.js] [length: 2]\n" + " BLOCK 1 [source_file: FileName.js] [length: 14]\n" + " BREAK 1 [source_file: FileName.js] [length: 10]\n" + " LABEL_NAME Bar 1 [source_file: FileName.js] [length: 3]\n"); } public void testNegation1() { testNewParser("-a", "SCRIPT 1 [source_file: FileName.js] [length: 2]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 2]\n" + " NEG 1 [source_file: FileName.js] [length: 2]\n" + " NAME a 1 [source_file: FileName.js] [length: 1]\n"); } public void testNegation2() { testNewParser("-2", "SCRIPT 1 [source_file: FileName.js] [length: 2]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 2]\n" + " NUMBER -2.0 1 [source_file: FileName.js] [length: 1]\n"); } public void testNegation3() { testNewParser("1 - -2", "SCRIPT 1 [source_file: FileName.js] [length: 6]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 6]\n" + " SUB 1 [source_file: FileName.js] [length: 6]\n" + " NUMBER 1.0 1 [source_file: FileName.js] [length: 1]\n" + " NUMBER -2.0 1 [source_file: FileName.js] [length: 1]\n"); } public void testGetter() { mode = LanguageMode.ECMASCRIPT5; testNewParser("({get a() {}})", "SCRIPT 1 [source_file: FileName.js] [length: 14]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 13]\n" + " OBJECTLIT 1 [source_file: FileName.js] [length: 12]\n" + " GETTER_DEF a 1 [source_file: FileName.js] [length: 1]\n" + " FUNCTION 1 [source_file: FileName.js] [length: 6]\n" + " NAME 1 [source_file: FileName.js]\n" + " PARAM_LIST 1 [source_file: FileName.js]\n" + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); } public void testSetter() { mode = LanguageMode.ECMASCRIPT5; testNewParser("({set a(x) {}})", "SCRIPT 1 [source_file: FileName.js] [length: 15]\n" + " EXPR_RESULT 1 [source_file: FileName.js] [length: 14]\n" + " OBJECTLIT 1 [source_file: FileName.js] [length: 13]\n" + " SETTER_DEF a 1 [source_file: FileName.js] [length: 1]\n" + " FUNCTION 1 [source_file: FileName.js] [length: 7]\n" + " NAME 1 [source_file: FileName.js]\n" + " PARAM_LIST 1 [source_file: FileName.js]\n" + " NAME x 1 [source_file: FileName.js] [length: 1]\n" + " BLOCK 1 [source_file: FileName.js] [length: 2]\n"); } public void testDelete1() { testNoParseError("delete a.b;"); } public void testDelete2() { testNoParseError("delete a['b'];"); } public void testDelete3() { // This is allowed in ES3 and ES5, but not in ES5/strict. There // is a strict mode check for this. testNoParseError("delete a;"); } public void testDelete4() { testParseError("delete 'x';", "Invalid delete operand. Only properties can be deleted."); } public void testCommentPositions1() { Node root = newParse("/** @param {string} x */function a(x) {};" + "/** @param {string} x */function b(x) {}"); Node a = root.getFirstChild(); Node b = root.getLastChild(); assertMarkerPosition(a, 1, 4); assertMarkerPosition(b, 1, 45); } public void testCommentPositions2() { Node root = newParse( "/* foo \n" + " bar \n" + "*/\n" + "/** @param {string} x */\n" + "function a(x) {};\n" + "\n" + "/* bar \n" + " foo \n" + " foo */\n" + "\n" + "/** @param {string} x */\n" + "function b(x) {};"); assertMarkerPosition(root.getFirstChild(), 4, 4); assertMarkerPosition(root.getFirstChild().getNext().getNext(), 11, 6); } public void testLiteralLocation() { Node root = newParse( "var d =\n" + " \"foo\";\n" + "var e =\n" + " 1;\n" + "var f = \n" + " 1.2;\n" + "var g = \n" + " 2e5;\n" + "var h = \n" + " 'bar';\n"); Node firstStmt = root.getFirstChild(); Node firstLiteral = firstStmt.getFirstChild().getFirstChild(); Node secondStmt = firstStmt.getNext(); Node secondLiteral = secondStmt.getFirstChild().getFirstChild(); Node thirdStmt = secondStmt.getNext(); Node thirdLiteral = thirdStmt.getFirstChild().getFirstChild(); Node fourthStmt = thirdStmt.getNext(); Node fourthLiteral = fourthStmt.getFirstChild().getFirstChild(); Node fifthStmt = fourthStmt.getNext(); Node fifthLiteral = fifthStmt.getFirstChild().getFirstChild(); assertNodePosition(2, 4, firstLiteral); assertNodePosition(4, 4, secondLiteral); assertNodePosition(6, 4, thirdLiteral); assertNodePosition(8, 4, fourthLiteral); assertNodePosition(10, 4, fifthLiteral); } public void testSwitchLocation() { Node root = newParse( "switch (a) {\n" + " //{\n" + " case 1:\n" + " b++;\n" + " case 2:\n" + " default:\n" + " b--;\n" + " }\n"); Node switchStmt = root.getFirstChild(); Node switchVar = switchStmt.getFirstChild(); Node firstCase = switchVar.getNext(); Node caseArg = firstCase.getFirstChild(); Node caseBody = caseArg.getNext(); Node caseExprStmt = caseBody.getFirstChild(); Node incrExpr = caseExprStmt.getFirstChild(); Node incrVar = incrExpr.getFirstChild(); Node secondCase = firstCase.getNext(); Node defaultCase = secondCase.getNext(); assertNodePosition(1, 0, switchStmt); assertNodePosition(1, 8, switchVar); assertNodePosition(3, 3, firstCase); assertNodePosition(3, 8, caseArg); assertNodePosition(3, 3, caseBody); assertNodePosition(4, 5, caseExprStmt); assertNodePosition(4, 5, incrExpr); assertNodePosition(4, 5, incrVar); assertNodePosition(5, 3, secondCase); assertNodePosition(6, 3, defaultCase); } public void testFunctionParamLocation() { Node root = newParse( "function\n" + " foo(a,\n" + " b,\n" + " c)\n" + "{}\n"); Node function = root.getFirstChild(); Node functionName = function.getFirstChild(); Node params = functionName.getNext(); Node param1 = params.getFirstChild(); Node param2 = param1.getNext(); Node param3 = param2.getNext(); Node body = params.getNext(); assertNodePosition(1, 0, function); assertNodePosition(2, 5, functionName); // params corresponds to the LP token. // Can't be on a separate line because of inferred // semicolons. assertNodePosition(2, 8, params); assertNodePosition(2, 9, param1); assertNodePosition(3, 5, param2); assertNodePosition(4, 5, param3); assertNodePosition(5, 0, body); } public void testVarDeclLocation() { Node root = newParse( "var\n" + " a =\n" + " 3\n"); Node varDecl = root.getFirstChild(); Node varName = varDecl.getFirstChild(); Node varExpr = varName.getFirstChild(); assertNodePosition(1, 0, varDecl); assertNodePosition(2, 4, 1, varName); assertNodePosition(3, 4, 1, varExpr); } public void testReturnLocation() { Node root = newParse( "function\n" + " foo(\n" + " a,\n" + " b,\n" + " c) {\n" + " return\n" + " 4;\n" + "}\n"); Node function = root.getFirstChild(); Node functionName = function.getFirstChild(); Node params = functionName.getNext(); Node body = params.getNext(); Node returnStmt = body.getFirstChild(); Node exprStmt = returnStmt.getNext(); Node returnVal = exprStmt.getFirstChild(); assertNodePosition(6, 4, returnStmt); assertNodePosition(7, 4, exprStmt); assertNodePosition(7, 4, returnVal); } public void testLinenoFor() { Node root = newParse( "for(\n" + ";\n" + ";\n" + ") {\n" + "}\n"); Node forNode = root.getFirstChild(); Node initClause= forNode.getFirstChild(); Node condClause = initClause.getNext(); Node incrClause = condClause.getNext(); assertNodePosition(1, 0, forNode); assertNodePosition(2, 0, initClause); assertNodePosition(3, 0, condClause); // TODO(bowdidge) Incorrectly gets charno position when EmptyExpression // has its absolute position on the carriage return. For now, the // line number gets reported correctly (on the next line) but the // character position is -1, so the overall line/char pair in our tree // is -1. assertNodePosition(-1, -1, incrClause); // should be 4 } public void testBinaryExprLocation() { Node root = newParse( "var d = a\n" + " + \n" + " b;\n" + "var\n" + " e =\n" + " a +\n" + " c;\n" + "var f = b\n" + " / c;\n"); Node firstVarDecl = root.getFirstChild(); Node firstVar = firstVarDecl.getFirstChild(); Node firstVarAdd = firstVar.getFirstChild(); Node secondVarDecl = firstVarDecl.getNext(); Node secondVar = secondVarDecl.getFirstChild(); Node secondVarAdd = secondVar.getFirstChild(); Node thirdVarDecl = secondVarDecl.getNext(); Node thirdVar = thirdVarDecl.getFirstChild(); Node thirdVarAdd = thirdVar.getFirstChild(); assertNodePosition(1, 0, firstVarDecl); assertNodePosition(1, 4, firstVar); assertNodePosition(1, 8, firstVarAdd); assertNodePosition(1, 8, firstVarAdd.getFirstChild()); assertNodePosition(3, 4, firstVarAdd.getLastChild()); assertNodePosition(4, 0, secondVarDecl); assertNodePosition(5, 4, secondVar); assertNodePosition(6, 4, secondVarAdd); assertNodePosition(6, 4, secondVarAdd.getFirstChild()); assertNodePosition(7, 4, secondVarAdd.getLastChild()); assertNodePosition(8, 0, thirdVarDecl); assertNodePosition(8, 4, thirdVar); assertNodePosition(8, 8, thirdVarAdd); assertNodePosition(8, 8, thirdVarAdd.getFirstChild()); assertNodePosition(9, 6, thirdVarAdd.getLastChild()); } public void testPrefixLocation() { Node root = newParse( "a++;\n" + "--\n" + "b;\n"); Node firstStmt = root.getFirstChild(); Node secondStmt = firstStmt.getNext(); Node firstOp = firstStmt.getFirstChild(); Node secondOp = secondStmt.getFirstChild(); assertNodePosition(1, 0, firstOp); assertNodePosition(2, 0, secondOp); } public void testIfLocation() { Node root = newParse( "if\n" + " (a == 3)\n" + "{\n" + " b = 0;\n" + "}\n" + " else\n" + "{\n" + " c = 1;\n" + "}\n"); Node ifStmt = root.getFirstChild(); Node eqClause = ifStmt.getFirstChild(); Node thenClause = eqClause.getNext(); Node elseClause = thenClause.getNext(); assertNodePosition(1, 0, ifStmt); assertNodePosition(2, 3, eqClause); assertNodePosition(3, 0, thenClause); assertNodePosition(7, 0, elseClause); } public void testTryLocation() { Node root = newParse( "try {\n" + " var x = 1;\n" + "} catch\n" + " (err)\n" + "{\n" + "} finally {\n" + " var y = 2;\n" + "}\n"); Node tryStmt = root.getFirstChild(); Node tryBlock = tryStmt.getFirstChild(); Node catchBlock = tryBlock.getNext(); Node catchVarBlock = catchBlock.getFirstChild(); Node catchVar = catchVarBlock.getFirstChild(); Node finallyBlock = catchBlock.getNext(); Node finallyStmt = finallyBlock.getFirstChild(); assertNodePosition(1, 0, tryStmt); assertNodePosition(1, 4, tryBlock); assertNodePosition(3, 2, catchVarBlock); assertNodePosition(4, 4, catchVar); assertNodePosition(3, 0, catchBlock); assertNodePosition(6, 10, finallyBlock); assertNodePosition(7, 2, finallyStmt); } public void testHookLocation() { Node root = newParse( "a\n" + "?\n" + "b\n" + ":\n" + "c\n" + ";\n"); Node hookExpr = root.getFirstChild().getFirstChild(); Node condExpr = hookExpr.getFirstChild(); Node thenExpr = condExpr.getNext(); Node elseExpr = thenExpr.getNext(); assertNodePosition(2, 0, hookExpr); assertNodePosition(1, 0, condExpr); assertNodePosition(3, 0, thenExpr); assertNodePosition(5, 0, elseExpr); } public void testLabelLocation() { Node root = newParse( "foo:\n" + "a = 1;\n" + "bar:\n" + "b = 2;\n"); Node firstStmt = root.getFirstChild(); Node secondStmt = firstStmt.getNext(); assertNodePosition(1, 0, firstStmt); assertNodePosition(3, 0, secondStmt); } public void testCompareLocation() { Node root = newParse( "a\n" + "<\n" + "b\n"); Node condClause = root.getFirstChild().getFirstChild(); Node lhs = condClause.getFirstChild(); Node rhs = lhs.getNext(); assertNodePosition(1, 0, condClause); assertNodePosition(1, 0, lhs); assertNodePosition(3, 0, rhs); } public void testEqualityLocation() { Node root = newParse( "a\n" + "==\n" + "b\n"); Node condClause = root.getFirstChild().getFirstChild(); Node lhs = condClause.getFirstChild(); Node rhs = lhs.getNext(); assertNodePosition(1, 0, condClause); assertNodePosition(1, 0, lhs); assertNodePosition(3, 0, rhs); } public void testPlusEqLocation() { Node root = newParse( "a\n" + "+=\n" + "b\n"); Node condClause = root.getFirstChild().getFirstChild(); Node lhs = condClause.getFirstChild(); Node rhs = lhs.getNext(); assertNodePosition(1, 0, condClause); assertNodePosition(1, 0, lhs); assertNodePosition(3, 0, rhs); } public void testCommaLocation() { Node root = newParse( "a,\n" + "b,\n" + "c;\n"); Node statement = root.getFirstChild(); Node comma1 = statement.getFirstChild(); Node comma2 = comma1.getFirstChild(); Node cRef = comma2.getNext(); Node aRef = comma2.getFirstChild(); Node bRef = aRef.getNext(); assertNodePosition(1, 0, comma2); assertNodePosition(1, 0, aRef); assertNodePosition(2, 0, bRef); assertNodePosition(3, 0, cRef); } public void testRegexpLocation() { Node root = newParse( "var path =\n" + "replace(\n" + "/a/g," + "'/');\n"); Node firstVarDecl = root.getFirstChild(); Node firstVar = firstVarDecl.getFirstChild(); Node callNode = firstVar.getFirstChild(); Node fnName = callNode.getFirstChild(); Node regexObject = fnName.getNext(); Node aString = regexObject.getFirstChild(); Node endRegexString = regexObject.getNext(); assertNodePosition(1, 0, firstVarDecl); assertNodePosition(1, 4, 4, firstVar); assertNodePosition(2, 0, 18, callNode); assertNodePosition(2, 0, 7, fnName); assertNodePosition(3, 0, regexObject); assertNodePosition(3, 0, aString); assertNodePosition(3, 5, endRegexString); } public void testNestedOr() { Node root = newParse( "if (a && \n" + " b() || \n" + " /* comment */\n" + " c) {\n" + "}\n" ); Node ifStmt = root.getFirstChild(); Node orClause = ifStmt.getFirstChild(); Node andClause = orClause.getFirstChild(); Node cName = andClause.getNext(); assertNodePosition(1, 0, ifStmt); assertNodePosition(1, 4, orClause); assertNodePosition(1, 4, andClause); assertNodePosition(4, 4, cName); } public void testBitwiseOps() { Node root = newParse( "if (a & \n" + " b() | \n" + " /* comment */\n" + " c) {\n" + "}\n" ); Node ifStmt = root.getFirstChild(); Node bitOr = ifStmt.getFirstChild(); Node bitAnd = bitOr.getFirstChild(); Node cName = bitAnd.getNext(); assertNodePosition(1, 0, ifStmt); assertNodePosition(1, 4, bitOr); assertNodePosition(1, 4, bitAnd); assertNodePosition(4, 4, cName); } public void testObjectLitLocation() { Node root = newParse( "var foo =\n" + "{ \n" + "'A' : 'A', \n" + "'B' : 'B', \n" + "'C' :\n" + " 'C' \n" + "};\n"); Node firstVarDecl = root.getFirstChild(); Node firstVar = firstVarDecl.getFirstChild(); Node firstObjectLit = firstVar.getFirstChild(); Node firstKey = firstObjectLit.getFirstChild(); Node firstValue = firstKey.getFirstChild(); Node secondKey = firstKey.getNext(); Node secondValue = secondKey.getFirstChild(); Node thirdKey = secondKey.getNext(); Node thirdValue = thirdKey.getFirstChild(); assertNodePosition(1, 4, firstVar); assertNodePosition(2, 0, firstObjectLit); assertNodePosition(3, 0, firstKey); assertNodePosition(3, 6, firstValue); assertNodePosition(4, 0, secondKey); assertNodePosition(4, 6, secondValue); assertNodePosition(5, 0, thirdKey); assertNodePosition(6, 4, thirdValue); } public void testTryWithoutCatchLocation() { Node root = newParse( "try {\n" + " var x = 1;\n" + "} finally {\n" + " var y = 2;\n" + "}\n"); Node tryStmt = root.getFirstChild(); Node tryBlock = tryStmt.getFirstChild(); Node catchBlock = tryBlock.getNext(); Node finallyBlock = catchBlock.getNext(); Node finallyStmt = finallyBlock.getFirstChild(); assertNodePosition(1, 0, tryStmt); assertNodePosition(1, 4, tryBlock); assertNodePosition(3, 0, catchBlock); assertNodePosition(3, 10, finallyBlock); assertNodePosition(4, 2, finallyStmt); } public void testTryWithoutFinallyLocation() { Node root = newParse( "try {\n" + " var x = 1;\n" + "} catch (ex) {\n" + " var y = 2;\n" + "}\n"); Node tryStmt = root.getFirstChild(); Node tryBlock = tryStmt.getFirstChild(); Node catchBlock = tryBlock.getNext(); Node catchStmt = catchBlock.getFirstChild(); Node exceptionVar = catchStmt.getFirstChild(); Node exceptionBlock = exceptionVar.getNext(); Node varDecl = exceptionBlock.getFirstChild(); assertNodePosition(1, 0, tryStmt); assertNodePosition(1, 4, tryBlock); assertNodePosition(3, 0, catchBlock); assertNodePosition(3, 2, catchStmt); assertNodePosition(3, 9, exceptionVar); assertNodePosition(3, 13, exceptionBlock); assertNodePosition(4, 2, varDecl); } public void testMultilineEqLocation() { Node root = newParse( "if\n" + " (((a == \n" + " 3) && \n" + " (b == 2)) || \n" + " (c == 1)) {\n" + "}\n"); Node ifStmt = root.getFirstChild(); Node orTest = ifStmt.getFirstChild(); Node andTest = orTest.getFirstChild(); Node cTest = andTest.getNext(); Node aTest = andTest.getFirstChild(); Node bTest = aTest.getNext(); assertNodePosition(1, 0, ifStmt); assertNodePosition(2, 7, orTest); assertNodePosition(2, 7, andTest); assertNodePosition(2, 7, aTest); assertNodePosition(4, 3, bTest); assertNodePosition(5, 2, cTest); } public void testMultilineBitTestLocation() { Node root = newParse( "if (\n" + " ((a \n" + " | 3 \n" + " ) == \n" + " (b \n" + " & 2)) && \n" + " ((a \n" + " ^ 0xffff) \n" + " != \n" + " (c \n" + " << 1))) {\n" + "}\n"); Node ifStmt = root.getFirstChild(); Node andTest = ifStmt.getFirstChild(); Node eqTest = andTest.getFirstChild(); Node notEqTest = eqTest.getNext(); Node bitOrTest = eqTest.getFirstChild(); Node bitAndTest = bitOrTest.getNext(); Node bitXorTest = notEqTest.getFirstChild(); Node bitShiftTest = bitXorTest.getNext(); assertNodePosition(1, 0, ifStmt); assertNodePosition(2, 8, eqTest); assertNodePosition(7, 8, notEqTest); assertNodePosition(2, 8, bitOrTest); assertNodePosition(5, 8, bitAndTest); assertNodePosition(7, 8, bitXorTest); assertNodePosition(10, 8, bitShiftTest); } public void testCallLocation() { Node root = newParse( "a.\n" + "b.\n" + "cccc(1);\n"); Node exprStmt = root.getFirstChild(); Node functionCall = exprStmt.getFirstChild(); Node functionProp = functionCall.getFirstChild(); Node firstNameComponent = functionProp.getFirstChild(); Node lastNameComponent = firstNameComponent.getNext(); Node aNameComponent = firstNameComponent.getFirstChild(); Node bNameComponent = aNameComponent.getNext(); assertNodePosition(1, 0, 13, functionCall); assertNodePosition(1, 0, 10, functionProp); // TODO(bowdidge) New Rhino doesn't keep the position of the dot handy. // New Rhino treats the location of the qualified name as the beginning of // the whole name. assertNodePosition(1, 0, 4, firstNameComponent); assertNodePosition(3, 0, 4, lastNameComponent); assertNodePosition(1, 0, 1, aNameComponent); assertNodePosition(2, 0, 1, bNameComponent); } public void testNewLocation() { Node root = newParse( "new c();\n"); Node exprStmt = root.getFirstChild(); Node newExpr = exprStmt.getFirstChild(); assertNodePosition(1, 0, 7, newExpr); } public void testNewLocationMultiLine() { Node root = newParse( "new \n" + "c();\n"); Node exprStmt = root.getFirstChild(); Node newExpr = exprStmt.getFirstChild(); assertNodePosition(1, 0, 10, newExpr); } public void testLinenoDeclaration() { Node root = newParse( "a.\n" + "b=\n" + "function() {};\n"); Node exprStmt = root.getFirstChild(); Node fnAssignment = exprStmt.getFirstChild(); Node aDotbName = fnAssignment.getFirstChild(); Node aName = aDotbName.getFirstChild(); Node bName = aName.getNext(); Node fnNode = aDotbName.getNext(); Node fnName = fnNode.getFirstChild(); assertNodePosition(1, 0, fnAssignment); // TODO(bowdidge) New Rhino doesn't keep track of the position of the dot. //assertNodePosition(1, 1, aDotbName); assertNodePosition(1, 0, aName); assertNodePosition(2, 0, bName); assertNodePosition(3, 0, fnNode); assertNodePosition(3, 8, fnName); } final String INVALID_ASSIGNMENT_TARGET = "invalid assignment target"; final String INVALID_INCREMENT_TARGET = "invalid increment target"; final String INVALID_DECREMENT_TARGET = "invalid decrement target"; final String INVALID_INC_OPERAND = "Invalid increment operand"; final String INVALID_DEC_OPERAND = "Invalid decrement operand"; public void testAssignmentValidation() { testNoParseError("x=1"); testNoParseError("x.y=1"); testNoParseError("f().y=1"); testParseError("(x||y)=1", INVALID_ASSIGNMENT_TARGET); testParseError("(x?y:z)=1", INVALID_ASSIGNMENT_TARGET); testParseError("f()=1", INVALID_ASSIGNMENT_TARGET); testNoParseError("x+=1"); testNoParseError("x.y+=1"); testNoParseError("f().y+=1"); testParseError("(x||y)+=1", INVALID_ASSIGNMENT_TARGET); testParseError("(x?y:z)+=1", INVALID_ASSIGNMENT_TARGET); testParseError("f()+=1", INVALID_ASSIGNMENT_TARGET); testParseError("f()++", INVALID_INCREMENT_TARGET); testParseError("f()--", INVALID_DECREMENT_TARGET); testParseError("++f()", INVALID_INCREMENT_TARGET); testParseError("--f()", INVALID_DECREMENT_TARGET); } private void testNoParseError(String string) { testParseError(string, (String)null); } private void testParseError(String string, String error) { testParseError(string, error == null ? null : new String[] { error }); } private void testParseError(String string, String[] errors) { newParse(string, new TestErrorReporter(errors, null)); assertTrue("unexpected warnings reported", errorReporter.hasEncounteredAllWarnings()); assertTrue("expected error were not reported", errorReporter.hasEncounteredAllErrors()); } private void assertMarkerPosition(Node n, int lineno, int charno) { int count = 0; for (JSDocInfo.Marker marker : n.getJSDocInfo().getMarkers()) { assertEquals(lineno, marker.getAnnotation().getStartLine()); assertEquals(charno, marker.getAnnotation().getPositionOnStartLine()); count++; } assertEquals(1, count); } private void assertNodePosition(int lineno, int charno, Node n) { assertEquals("Line number", lineno, n.getLineno()); assertEquals("Column position", charno, n.getCharno()); } private void assertNodePosition(int lineno, int charno, int length, Node n) { assertEquals("Line number", lineno, n.getLineno()); assertEquals("Column position", charno, n.getCharno()); assertEquals("Length", length, n.getLength()); } private void testNewParser(String code, String expected) { String actual = newParse(code).toStringTree(); assertEquals(expected, actual); } private Node newParse(String string) { return newParse(string, new TestErrorReporter(null, null)); } private Node newParse(String string, TestErrorReporter errorReporter) { CompilerEnvirons environment = new CompilerEnvirons(); environment.setRecordingComments(true); environment.setRecordingLocalJsDocComments(true); Parser p = new Parser(environment); AstRoot script = p.parse(string, null, 1); Config config = ParserRunner.createConfig(true, mode, false); Node root = IRFactory.transformTree( script, SourceFile.fromCode("FileName.js", string), string, config, errorReporter); return root; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/parsing/ParserTest.java0000644000175000017500000010654712115204405027443 0ustar apoapo/* * Copyright 2007 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.parsing; import com.google.common.collect.ImmutableList; import com.google.javascript.jscomp.parsing.Config.LanguageMode; import com.google.javascript.jscomp.testing.TestErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.head.ScriptRuntime; import com.google.javascript.rhino.jstype.SimpleSourceFile; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; import java.io.IOException; import java.util.List; import java.util.logging.Logger; public class ParserTest extends BaseJSTypeTestCase { private static final String SUSPICIOUS_COMMENT_WARNING = IRFactory.SUSPICIOUS_COMMENT_WARNING; private static final String TRAILING_COMMA_MESSAGE = ScriptRuntime.getMessage0("msg.extra.trailing.comma"); private static final String BAD_PROPERTY_MESSAGE = ScriptRuntime.getMessage0("msg.bad.prop"); private static final String MISSING_GT_MESSAGE = "Bad type annotation. " + com.google.javascript.rhino.ScriptRuntime.getMessage0( "msg.jsdoc.missing.gt"); private static final String MISPLACED_TYPE_ANNOTATION = IRFactory.MISPLACED_TYPE_ANNOTATION; private Config.LanguageMode mode; private boolean isIdeMode = false; @Override protected void setUp() throws Exception { super.setUp(); mode = LanguageMode.ECMASCRIPT3; isIdeMode = false; } public void testLinenoCharnoAssign1() throws Exception { Node assign = parse("a = b").getFirstChild().getFirstChild(); assertEquals(Token.ASSIGN, assign.getType()); assertEquals(1, assign.getLineno()); assertEquals(0, assign.getCharno()); } public void testLinenoCharnoAssign2() throws Exception { Node assign = parse("\n a.g.h.k = 45").getFirstChild().getFirstChild(); assertEquals(Token.ASSIGN, assign.getType()); assertEquals(2, assign.getLineno()); assertEquals(1, assign.getCharno()); } public void testLinenoCharnoCall() throws Exception { Node call = parse("\n foo(123);").getFirstChild().getFirstChild(); assertEquals(Token.CALL, call.getType()); assertEquals(2, call.getLineno()); assertEquals(1, call.getCharno()); } public void testLinenoCharnoGetProp1() throws Exception { Node getprop = parse("\n foo.bar").getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getprop.getType()); assertEquals(2, getprop.getLineno()); assertEquals(1, getprop.getCharno()); Node name = getprop.getFirstChild().getNext(); assertEquals(Token.STRING, name.getType()); assertEquals(2, name.getLineno()); assertEquals(5, name.getCharno()); } public void testLinenoCharnoGetProp2() throws Exception { Node getprop = parse("\n foo.\nbar").getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getprop.getType()); assertEquals(2, getprop.getLineno()); assertEquals(1, getprop.getCharno()); Node name = getprop.getFirstChild().getNext(); assertEquals(Token.STRING, name.getType()); assertEquals(3, name.getLineno()); assertEquals(0, name.getCharno()); } public void testLinenoCharnoGetelem1() throws Exception { Node call = parse("\n foo[123]").getFirstChild().getFirstChild(); assertEquals(Token.GETELEM, call.getType()); assertEquals(2, call.getLineno()); assertEquals(1, call.getCharno()); } public void testLinenoCharnoGetelem2() throws Exception { Node call = parse("\n \n foo()[123]").getFirstChild().getFirstChild(); assertEquals(Token.GETELEM, call.getType()); assertEquals(3, call.getLineno()); assertEquals(1, call.getCharno()); } public void testLinenoCharnoGetelem3() throws Exception { Node call = parse("\n \n (8 + kl)[123]").getFirstChild().getFirstChild(); assertEquals(Token.GETELEM, call.getType()); assertEquals(3, call.getLineno()); assertEquals(2, call.getCharno()); } public void testLinenoCharnoForComparison() throws Exception { Node lt = parse("for (; i < j;){}").getFirstChild().getFirstChild().getNext(); assertEquals(Token.LT, lt.getType()); assertEquals(1, lt.getLineno()); assertEquals(7, lt.getCharno()); } public void testLinenoCharnoHook() throws Exception { Node n = parse("\n a ? 9 : 0").getFirstChild().getFirstChild(); assertEquals(Token.HOOK, n.getType()); assertEquals(2, n.getLineno()); assertEquals(1, n.getCharno()); } public void testLinenoCharnoArrayLiteral() throws Exception { Node n = parse("\n [8, 9]").getFirstChild().getFirstChild(); assertEquals(Token.ARRAYLIT, n.getType()); assertEquals(2, n.getLineno()); assertEquals(2, n.getCharno()); n = n.getFirstChild(); assertEquals(Token.NUMBER, n.getType()); assertEquals(2, n.getLineno()); assertEquals(3, n.getCharno()); n = n.getNext(); assertEquals(Token.NUMBER, n.getType()); assertEquals(2, n.getLineno()); assertEquals(6, n.getCharno()); } public void testLinenoCharnoObjectLiteral() throws Exception { Node n = parse("\n\n var a = {a:0\n,b :1};") .getFirstChild().getFirstChild().getFirstChild(); assertEquals(Token.OBJECTLIT, n.getType()); assertEquals(3, n.getLineno()); assertEquals(9, n.getCharno()); Node key = n.getFirstChild(); assertEquals(Token.STRING_KEY, key.getType()); assertEquals(3, key.getLineno()); assertEquals(10, key.getCharno()); Node value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(3, value.getLineno()); assertEquals(12, value.getCharno()); key = key.getNext(); assertEquals(Token.STRING_KEY, key.getType()); assertEquals(4, key.getLineno()); assertEquals(1, key.getCharno()); value = key.getFirstChild(); assertEquals(Token.NUMBER, value.getType()); assertEquals(4, value.getLineno()); assertEquals(4, value.getCharno()); } public void testLinenoCharnoAdd() throws Exception { testLinenoCharnoBinop("+"); } public void testLinenoCharnoSub() throws Exception { testLinenoCharnoBinop("-"); } public void testLinenoCharnoMul() throws Exception { testLinenoCharnoBinop("*"); } public void testLinenoCharnoDiv() throws Exception { testLinenoCharnoBinop("/"); } public void testLinenoCharnoMod() throws Exception { testLinenoCharnoBinop("%"); } public void testLinenoCharnoShift() throws Exception { testLinenoCharnoBinop("<<"); } public void testLinenoCharnoBinaryAnd() throws Exception { testLinenoCharnoBinop("&"); } public void testLinenoCharnoAnd() throws Exception { testLinenoCharnoBinop("&&"); } public void testLinenoCharnoBinaryOr() throws Exception { testLinenoCharnoBinop("|"); } public void testLinenoCharnoOr() throws Exception { testLinenoCharnoBinop("||"); } public void testLinenoCharnoLt() throws Exception { testLinenoCharnoBinop("<"); } public void testLinenoCharnoLe() throws Exception { testLinenoCharnoBinop("<="); } public void testLinenoCharnoGt() throws Exception { testLinenoCharnoBinop(">"); } public void testLinenoCharnoGe() throws Exception { testLinenoCharnoBinop(">="); } private void testLinenoCharnoBinop(String binop) { Node op = parse("var a = 89 " + binop + " 76").getFirstChild(). getFirstChild().getFirstChild(); assertEquals(1, op.getLineno()); assertEquals(8, op.getCharno()); } public void testJSDocAttachment1() { Node varNode = parse("/** @type number */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment2() { Node varNode = parse("/** @type number */var a,b;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); // First NAME Node nameNode1 = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode1.getType()); assertNull(nameNode1.getJSDocInfo()); // Second NAME Node nameNode2 = nameNode1.getNext(); assertEquals(Token.NAME, nameNode2.getType()); assertNull(nameNode2.getJSDocInfo()); } public void testJSDocAttachment3() { Node assignNode = parse( "/** @type number */goog.FOO = 5;").getFirstChild().getFirstChild(); // ASSIGN assertEquals(Token.ASSIGN, assignNode.getType()); JSDocInfo info = assignNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(NUMBER_TYPE, info.getType()); } public void testJSDocAttachment4() { Node varNode = parse( "var a, /** @define {number} */b = 5;").getFirstChild(); // ASSIGN assertEquals(Token.VAR, varNode.getType()); assertNull(varNode.getJSDocInfo()); // a Node a = varNode.getFirstChild(); assertNull(a.getJSDocInfo()); // b Node b = a.getNext(); JSDocInfo info = b.getJSDocInfo(); assertNotNull(info); assertTrue(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); } public void testJSDocAttachment5() { Node varNode = parse( "var /** @type number */a, /** @define {number} */b = 5;") .getFirstChild(); // ASSIGN assertEquals(Token.VAR, varNode.getType()); assertNull(varNode.getJSDocInfo()); // a Node a = varNode.getFirstChild(); assertNotNull(a.getJSDocInfo()); JSDocInfo info = a.getJSDocInfo(); assertNotNull(info); assertFalse(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); // b Node b = a.getNext(); info = b.getJSDocInfo(); assertNotNull(info); assertTrue(info.isDefine()); assertTypeEquals(NUMBER_TYPE, info.getType()); } /** * Tests that a JSDoc comment in an unexpected place of the code does not * propagate to following code due to {@link JSDocInfo} aggregation. */ public void testJSDocAttachment6() throws Exception { Node functionNode = parse( "var a = /** @param {number} index */5;" + "/** @return boolean */function f(index){}") .getFirstChild().getNext(); assertEquals(Token.FUNCTION, functionNode.getType()); JSDocInfo info = functionNode.getJSDocInfo(); assertNotNull(info); assertFalse(info.hasParameter("index")); assertTrue(info.hasReturnType()); assertTypeEquals(UNKNOWN_TYPE, info.getReturnType()); } public void testJSDocAttachment7() { Node varNode = parse("/** */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment8() { Node varNode = parse("/** x */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment9() { Node varNode = parse("/** \n x */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment10() { Node varNode = parse("/** x\n */var a;").getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment11() { Node varNode = parse("/** @type {{x : number, 'y' : string, z}} */var a;") .getFirstChild(); // VAR assertEquals(Token.VAR, varNode.getType()); JSDocInfo info = varNode.getJSDocInfo(); assertNotNull(info); assertTypeEquals(createRecordTypeBuilder(). addProperty("x", NUMBER_TYPE, null). addProperty("y", STRING_TYPE, null). addProperty("z", UNKNOWN_TYPE, null). build(), info.getType()); // NAME Node nameNode = varNode.getFirstChild(); assertEquals(Token.NAME, nameNode.getType()); assertNull(nameNode.getJSDocInfo()); } public void testJSDocAttachment12() { Node varNode = parse("var a = {/** @type {Object} */ b: c};") .getFirstChild(); Node objectLitNode = varNode.getFirstChild().getFirstChild(); assertEquals(Token.OBJECTLIT, objectLitNode.getType()); assertNotNull(objectLitNode.getFirstChild().getJSDocInfo()); } public void testJSDocAttachment13() { Node varNode = parse("/** foo */ var a;").getFirstChild(); assertNotNull(varNode.getJSDocInfo()); } public void testJSDocAttachment14() { Node varNode = parse("/** */ var a;").getFirstChild(); assertNull(varNode.getJSDocInfo()); } public void testJSDocAttachment15() { Node varNode = parse("/** \n * \n */ var a;").getFirstChild(); assertNull(varNode.getJSDocInfo()); } public void testJSDocAttachment16() { Node exprCall = parse("/** @private */ x(); function f() {};").getFirstChild(); assertEquals(Token.EXPR_RESULT, exprCall.getType()); assertNull(exprCall.getNext().getJSDocInfo()); assertNotNull(exprCall.getFirstChild().getJSDocInfo()); } public void testInlineJSDocAttachment1() { Node fn = parse("function f(/** string */ x) {}").getFirstChild(); assertTrue(fn.isFunction()); JSDocInfo info = fn.getFirstChild().getNext().getFirstChild().getJSDocInfo(); assertNotNull(info); assertTypeEquals(STRING_TYPE, info.getType()); } public void testInlineJSDocAttachment2() { Node fn = parse( "function f(/**\n" + " * {string}\n" + " */ x) {}").getFirstChild(); assertTrue(fn.isFunction()); JSDocInfo info = fn.getFirstChild().getNext().getFirstChild().getJSDocInfo(); assertNotNull(info); assertTypeEquals(STRING_TYPE, info.getType()); } public void testInlineJSDocAttachment3() { parse( "function f(/** @type {string} */ x) {}", "Bad type annotation. type not recognized due to syntax error"); } public void testInlineJSDocAttachment4() { parse( "function f(/**\n" + " * @type {string}\n" + " */ x) {}", "Bad type annotation. type not recognized due to syntax error"); } public void testIncorrectJSDocDoesNotAlterJSParsing1() throws Exception { assertNodeEquality( parse("var a = [1,2]"), parse("/** @type Array. testCases = ImmutableList.of( new ParserResult( "3;", createScript(new Node(Token.EXPR_RESULT, Node.newNumber(3.0)))), new ParserResult( "var a = b;", createScript(new Node(Token.VAR, a))), new ParserResult( "\"hell\\\no\\ world\\\n\\\n!\"", createScript(new Node(Token.EXPR_RESULT, Node.newString(Token.STRING, "hello world!"))))); for (ParserResult testCase : testCases) { assertNodeEquality(testCase.node, parse(testCase.code)); } } private Node createScript(Node n) { Node script = new Node(Token.SCRIPT); script.addChildToBack(n); return script; } public void testTrailingCommaWarning1() { parse("var a = ['foo', 'bar'];"); } public void testTrailingCommaWarning2() { parse("var a = ['foo',,'bar'];"); } public void testTrailingCommaWarning3() { parse("var a = ['foo', 'bar',];", TRAILING_COMMA_MESSAGE); mode = LanguageMode.ECMASCRIPT5; parse("var a = ['foo', 'bar',];"); } public void testTrailingCommaWarning4() { parse("var a = [,];", TRAILING_COMMA_MESSAGE); mode = LanguageMode.ECMASCRIPT5; parse("var a = [,];"); } public void testTrailingCommaWarning5() { parse("var a = {'foo': 'bar'};"); } public void testTrailingCommaWarning6() { parse("var a = {'foo': 'bar',};", TRAILING_COMMA_MESSAGE); mode = LanguageMode.ECMASCRIPT5; parse("var a = {'foo': 'bar',};"); } public void testTrailingCommaWarning7() { parseError("var a = {,};", BAD_PROPERTY_MESSAGE); } public void testSuspiciousBlockCommentWarning1() { parse("/* @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING); } public void testSuspiciousBlockCommentWarning2() { parse("/* \n * @type {number} */ var x = 3;", SUSPICIOUS_COMMENT_WARNING); } public void testCatchClauseForbidden() { parseError("try { } catch (e if true) {}", "Catch clauses are not supported"); } public void testConstForbidden() { parseError("const x = 3;", "Unsupported syntax: CONST"); } public void testDestructuringAssignForbidden() { parseError("var [x, y] = foo();", "destructuring assignment forbidden"); } public void testDestructuringAssignForbidden2() { parseError("var {x, y} = foo();", "missing : after property id"); } public void testDestructuringAssignForbidden3() { parseError("var {x: x, y: y} = foo();", "destructuring assignment forbidden"); } public void testDestructuringAssignForbidden4() { parseError("[x, y] = foo();", "destructuring assignment forbidden", "invalid assignment target"); } public void testLetForbidden() { parseError("function f() { let (x = 3) { alert(x); }; }", "missing ; before statement", "syntax error"); } public void testYieldForbidden() { parseError("function f() { yield 3; }", "missing ; before statement"); } public void testBracelessFunctionForbidden() { parseError("var sq = function(x) x * x;", "missing { before function body"); } public void testGeneratorsForbidden() { parseError("var i = (x for (x in obj));", "Unsupported syntax: GENEXPR"); } public void testGettersForbidden1() { parseError("var x = {get foo() { return 3; }};", IRFactory.GETTER_ERROR_MESSAGE); } public void testGettersForbidden2() { parseError("var x = {get foo bar() { return 3; }};", "invalid property id"); } public void testGettersForbidden3() { parseError("var x = {a getter:function b() { return 3; }};", "missing : after property id", "syntax error"); } public void testGettersForbidden4() { parseError("var x = {\"a\" getter:function b() { return 3; }};", "missing : after property id", "syntax error"); } public void testGettersForbidden5() { parseError("var x = {a: 2, get foo() { return 3; }};", IRFactory.GETTER_ERROR_MESSAGE); } public void testSettersForbidden() { parseError("var x = {set foo() { return 3; }};", IRFactory.SETTER_ERROR_MESSAGE); } public void testSettersForbidden2() { parseError("var x = {a setter:function b() { return 3; }};", "missing : after property id", "syntax error"); } public void testFileOverviewJSDoc1() { Node n = parse("/** @fileoverview Hi mom! */ function Foo() {}"); assertEquals(Token.FUNCTION, n.getFirstChild().getType()); assertTrue(n.getJSDocInfo() != null); assertNull(n.getFirstChild().getJSDocInfo()); assertEquals("Hi mom!", n.getJSDocInfo().getFileOverview()); } public void testFileOverviewJSDocDoesNotHoseParsing() { assertEquals( Token.FUNCTION, parse("/** @fileoverview Hi mom! \n */ function Foo() {}") .getFirstChild().getType()); assertEquals( Token.FUNCTION, parse("/** @fileoverview Hi mom! \n * * * */ function Foo() {}") .getFirstChild().getType()); assertEquals( Token.FUNCTION, parse("/** @fileoverview \n * x */ function Foo() {}") .getFirstChild().getType()); assertEquals( Token.FUNCTION, parse("/** @fileoverview \n * x \n */ function Foo() {}") .getFirstChild().getType()); } public void testFileOverviewJSDoc2() { Node n = parse("/** @fileoverview Hi mom! */ " + "/** @constructor */ function Foo() {}"); assertTrue(n.getJSDocInfo() != null); assertEquals("Hi mom!", n.getJSDocInfo().getFileOverview()); assertTrue(n.getFirstChild().getJSDocInfo() != null); assertFalse(n.getFirstChild().getJSDocInfo().hasFileOverview()); assertTrue(n.getFirstChild().getJSDocInfo().isConstructor()); } public void testObjectLiteralDoc1() { Node n = parse("var x = {/** @type {number} */ 1: 2};"); Node objectLit = n.getFirstChild().getFirstChild().getFirstChild(); assertEquals(Token.OBJECTLIT, objectLit.getType()); Node number = objectLit.getFirstChild(); assertEquals(Token.STRING_KEY, number.getType()); assertNotNull(number.getJSDocInfo()); } public void testDuplicatedParam() { parse("function foo(x, x) {}", "Duplicate parameter name \"x\"."); } public void testGetter() { mode = LanguageMode.ECMASCRIPT3; parseError("var x = {get 1(){}};", IRFactory.GETTER_ERROR_MESSAGE); parseError("var x = {get 'a'(){}};", IRFactory.GETTER_ERROR_MESSAGE); parseError("var x = {get a(){}};", IRFactory.GETTER_ERROR_MESSAGE); mode = LanguageMode.ECMASCRIPT5; parse("var x = {get 1(){}};"); parse("var x = {get 'a'(){}};"); parse("var x = {get a(){}};"); parseError("var x = {get a(b){}};", "getters may not have parameters"); } public void testSetter() { mode = LanguageMode.ECMASCRIPT3; parseError("var x = {set 1(x){}};", IRFactory.SETTER_ERROR_MESSAGE); parseError("var x = {set 'a'(x){}};", IRFactory.SETTER_ERROR_MESSAGE); parseError("var x = {set a(x){}};", IRFactory.SETTER_ERROR_MESSAGE); mode = LanguageMode.ECMASCRIPT5; parse("var x = {set 1(x){}};"); parse("var x = {set 'a'(x){}};"); parse("var x = {set a(x){}};"); parseError("var x = {set a(){}};", "setters must have exactly one parameter"); } public void testLamestWarningEver() { // This used to be a warning. parse("var x = /** @type {undefined} */ (y);"); parse("var x = /** @type {void} */ (y);"); } public void testUnfinishedComment() { parseError("/** this is a comment ", "unterminated comment"); } public void testParseBlockDescription() { Node n = parse("/** This is a variable. */ var x;"); Node var = n.getFirstChild(); assertNotNull(var.getJSDocInfo()); assertEquals("This is a variable.", var.getJSDocInfo().getBlockDescription()); } public void testUnnamedFunctionStatement() { // Statements parseError("function() {};", "unnamed function statement"); parseError("if (true) { function() {}; }", "unnamed function statement"); parse("function f() {};"); // Expressions parse("(function f() {});"); parse("(function () {});"); } public void testReservedKeywords() { mode = LanguageMode.ECMASCRIPT3; parseError("var boolean;", "missing variable name"); parseError("function boolean() {};", "missing ( before function parameters."); parseError("boolean = 1;", "identifier is a reserved word"); parseError("class = 1;", "identifier is a reserved word"); parseError("public = 2;", "identifier is a reserved word"); mode = LanguageMode.ECMASCRIPT5; parse("var boolean;"); parse("function boolean() {};"); parse("boolean = 1;"); parseError("class = 1;", "identifier is a reserved word"); parse("public = 2;"); mode = LanguageMode.ECMASCRIPT5_STRICT; parse("var boolean;"); parse("function boolean() {};"); parse("boolean = 1;"); parseError("class = 1;", "identifier is a reserved word"); parseError("public = 2;", "identifier is a reserved word"); } public void testKeywordsAsProperties() { mode = LanguageMode.ECMASCRIPT3; parseError("var x = {function: 1};", "invalid property id"); parseError("x.function;", "missing name after . operator"); parseError("var x = {get x(){} };", IRFactory.GETTER_ERROR_MESSAGE); parseError("var x = {get function(){} };", "invalid property id"); parseError("var x = {get 'function'(){} };", IRFactory.GETTER_ERROR_MESSAGE); parseError("var x = {get 1(){} };", IRFactory.GETTER_ERROR_MESSAGE); parseError("var x = {set function(a){} };", "invalid property id"); parseError("var x = {set 'function'(a){} };", IRFactory.SETTER_ERROR_MESSAGE); parseError("var x = {set 1(a){} };", IRFactory.SETTER_ERROR_MESSAGE); parseError("var x = {class: 1};", "invalid property id"); parseError("x.class;", "missing name after . operator"); parse("var x = {let: 1};"); parse("x.let;"); parse("var x = {yield: 1};"); parse("x.yield;"); mode = LanguageMode.ECMASCRIPT5; parse("var x = {function: 1};"); parse("x.function;"); parse("var x = {get function(){} };"); parse("var x = {get 'function'(){} };"); parse("var x = {get 1(){} };"); parse("var x = {set function(a){} };"); parse("var x = {set 'function'(a){} };"); parse("var x = {set 1(a){} };"); parse("var x = {class: 1};"); parse("x.class;"); parse("var x = {let: 1};"); parse("x.let;"); parse("var x = {yield: 1};"); parse("x.yield;"); mode = LanguageMode.ECMASCRIPT5_STRICT; parse("var x = {function: 1};"); parse("x.function;"); parse("var x = {get function(){} };"); parse("var x = {get 'function'(){} };"); parse("var x = {get 1(){} };"); parse("var x = {set function(a){} };"); parse("var x = {set 'function'(a){} };"); parse("var x = {set 1(a){} };"); parse("var x = {class: 1};"); parse("x.class;"); parse("var x = {let: 1};"); parse("x.let;"); parse("var x = {yield: 1};"); parse("x.yield;"); } public void testGetPropFunctionName() { parseError("function a.b() {}", "missing ( before function parameters."); parseError("var x = function a.b() {}", "missing ( before function parameters."); } public void testGetPropFunctionNameIdeMode() { // In IDE mode, we try to fix up the tree, but sometimes // this leads to even more errors. isIdeMode = true; parseError("function a.b() {}", "missing ( before function parameters.", "missing formal parameter", "missing ) after formal parameters", "missing { before function body", "syntax error", "missing ; before statement", "missing ; before statement", "missing } after function body", "Unsupported syntax: ERROR", "Unsupported syntax: ERROR"); parseError("var x = function a.b() {}", "missing ( before function parameters.", "missing formal parameter", "missing ) after formal parameters", "missing { before function body", "syntax error", "missing ; before statement", "missing ; before statement", "missing } after function body", "Unsupported syntax: ERROR", "Unsupported syntax: ERROR"); } public void testIdeModePartialTree() { Node partialTree = parseError("function Foo() {} f.", "missing name after . operator"); assertNull(partialTree); isIdeMode = true; partialTree = parseError("function Foo() {} f.", "missing name after . operator"); assertNotNull(partialTree); } public void testForEach() { parseError( "function f(stamp, status) {\n" + " for each ( var curTiming in this.timeLog.timings ) {\n" + " if ( curTiming.callId == stamp ) {\n" + " curTiming.flag = status;\n" + " break;\n" + " }\n" + " }\n" + "};", "unsupported language extension: for each"); } public void testMisplacedTypeAnnotation1() { // misuse with COMMA parse( "var o = {};" + "/** @type {string} */ o.prop1 = 1, o.prop2 = 2;", MISPLACED_TYPE_ANNOTATION); } public void testMisplacedTypeAnnotation2() { // missing parenthese for the cast. parse( "var o = /** @type {string} */ getValue();", MISPLACED_TYPE_ANNOTATION); } public void testMisplacedTypeAnnotation3() { // missing parenthese for the cast. parse( "var o = 1 + /** @type {string} */ value;", MISPLACED_TYPE_ANNOTATION); } public void testMisplacedTypeAnnotation4() { // missing parenthese for the cast. parse( "var o = /** @type {!Array.} */ ['hello', 'you'];", MISPLACED_TYPE_ANNOTATION); } public void testMisplacedTypeAnnotation5() { // missing parenthese for the cast. parse( "var o = (/** @type {!Foo} */ {});", MISPLACED_TYPE_ANNOTATION); } public void testMisplacedTypeAnnotation6() { parse("var o = /** @type {function():string} */ function() {return 'str';}", MISPLACED_TYPE_ANNOTATION); } public void testValidTypeAnnotation1() { parse("/** @type {string} */ var o = 'str';"); parse("var /** @type {string} */ o = 'str', /** @type {number} */ p = 0;"); parse("/** @type {function():string} */ function o() { return 'str'; }"); parse("var o = {}; /** @type {string} */ o.prop = 'str';"); parse("var o = {}; /** @type {string} */ o['prop'] = 'str';"); parse("var o = { /** @type {string} */ prop : 'str' };"); parse("var o = { /** @type {string} */ 'prop' : 'str' };"); parse("var o = { /** @type {string} */ 1 : 'str' };"); } public void testValidTypeAnnotation2() { mode = LanguageMode.ECMASCRIPT5; parse("var o = { /** @type {string} */ get prop() { return 'str' }};"); parse("var o = { /** @type {string} */ set prop(s) {}};"); } public void testValidTypeAnnotation3() { // This one we don't currently support in the type checker but // we would like to. parse("try {} catch (/** @type {Error} */ e) {}"); } /** * Verify that the given code has the given parse errors. * @return If in IDE mode, returns a partial tree. */ private Node parseError(String string, String... errors) { TestErrorReporter testErrorReporter = new TestErrorReporter(errors, null); Node script = null; try { StaticSourceFile file = new SimpleSourceFile("input", false); script = ParserRunner.parse( file, string, ParserRunner.createConfig(isIdeMode, mode, false), testErrorReporter, Logger.getAnonymousLogger()).ast; } catch (IOException e) { throw new RuntimeException(e); } // verifying that all warnings were seen assertTrue(testErrorReporter.hasEncounteredAllErrors()); assertTrue(testErrorReporter.hasEncounteredAllWarnings()); return script; } private Node parse(String string, String... warnings) { TestErrorReporter testErrorReporter = new TestErrorReporter(null, warnings); Node script = null; try { StaticSourceFile file = new SimpleSourceFile("input", false); script = ParserRunner.parse( file, string, ParserRunner.createConfig(true, mode, false), testErrorReporter, Logger.getAnonymousLogger()).ast; } catch (IOException e) { throw new RuntimeException(e); } // verifying that all warnings were seen assertTrue(testErrorReporter.hasEncounteredAllErrors()); assertTrue(testErrorReporter.hasEncounteredAllWarnings()); return script; } private static class ParserResult { private final String code; private final Node node; private ParserResult(String code, Node node) { this.code = code; this.node = node; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TypeValidatorTest.java0000644000175000017500000001173112115204405027321 0ustar apoapo/* * 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 static com.google.javascript.jscomp.TypeValidator.TYPE_MISMATCH_WARNING; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.TypeValidator.TypeMismatch; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import java.util.Collections; import java.util.List; /** * Tests for TypeValidator. * @author nicksantos@google.com (Nick Santos) */ public class TypeValidatorTest extends CompilerTestCase { private Compiler compiler = null; public TypeValidatorTest() { enableTypeCheck(CheckLevel.ERROR); } @Override protected CompilerPass getProcessor(final Compiler compiler) { this.compiler = compiler; return new CompilerPass() { @Override public void process(Node externs, Node n) { // Do nothing: we're in it for the type-checking. } }; } @Override public int getNumRepetitions() { return 1; } public void testBasicMismatch() throws Exception { testSame("/** @param {number} x */ function f(x) {} f('a');", TYPE_MISMATCH_WARNING); assertMismatches(Lists.newArrayList(fromNatives(STRING_TYPE, NUMBER_TYPE))); } public void testFunctionMismatch() throws Exception { testSame( "/** \n" + " * @param {function(string): number} x \n" + " * @return {function(boolean): string} \n" + " */ function f(x) { return x; }", TYPE_MISMATCH_WARNING); JSTypeRegistry registry = compiler.getTypeRegistry(); JSType string = registry.getNativeType(STRING_TYPE); JSType bool = registry.getNativeType(BOOLEAN_TYPE); JSType number = registry.getNativeType(NUMBER_TYPE); JSType firstFunction = registry.createFunctionType(number, string); JSType secondFunction = registry.createFunctionType(string, bool); assertMismatches( Lists.newArrayList( new TypeMismatch(firstFunction, secondFunction, null), fromNatives(STRING_TYPE, BOOLEAN_TYPE), fromNatives(NUMBER_TYPE, STRING_TYPE))); } public void testFunctionMismatch2() throws Exception { testSame( "/** \n" + " * @param {function(string): number} x \n" + " * @return {function(boolean): number} \n" + " */ function f(x) { return x; }", TYPE_MISMATCH_WARNING); JSTypeRegistry registry = compiler.getTypeRegistry(); JSType string = registry.getNativeType(STRING_TYPE); JSType bool = registry.getNativeType(BOOLEAN_TYPE); JSType number = registry.getNativeType(NUMBER_TYPE); JSType firstFunction = registry.createFunctionType(number, string); JSType secondFunction = registry.createFunctionType(number, bool); assertMismatches( Lists.newArrayList( new TypeMismatch(firstFunction, secondFunction, null), fromNatives(STRING_TYPE, BOOLEAN_TYPE))); } public void testNullUndefined() { testSame("/** @param {string} x */ function f(x) {}\n" + "f(/** @type {string|null|undefined} */ ('a'));", TYPE_MISMATCH_WARNING); assertMismatches(Collections.emptyList()); } public void testSubclass() { testSame("/** @constructor */\n" + "function Super() {}\n" + "/**\n" + " * @constructor\n" + " * @extends {Super}\n" + " */\n" + "function Sub() {}\n" + "/** @param {Sub} x */ function f(x) {}\n" + "f(/** @type {Super} */ (new Sub));", TYPE_MISMATCH_WARNING); assertMismatches(Collections.emptyList()); } private TypeMismatch fromNatives(JSTypeNative a, JSTypeNative b) { JSTypeRegistry registry = compiler.getTypeRegistry(); return new TypeMismatch( registry.getNativeType(a), registry.getNativeType(b), null); } private void assertMismatches(List expected) { List actual = Lists.newArrayList( compiler.getTypeValidator().getMismatches()); assertEquals(expected, actual); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/OptimizeReturnsTest.java0000644000175000017500000001730112115204405027714 0ustar apoapo/* * 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.base.Joiner; /** * Tests OptimizeReturns * @author johnlenz@google.com (John Lenz) */ public class OptimizeReturnsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new OptimizeReturns(compiler); } private static final String EXTERNAL_SYMBOLS = "var extern;extern.externalMethod"; public OptimizeReturnsTest() { super(EXTERNAL_SYMBOLS); } @Override protected int getNumRepetitions() { // run pass once. return 1; } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); disableTypeCheck(); } /** * Combine source strings using '\n' as the separator. */ private static String newlineJoin(String ... parts) { return Joiner.on("\n").join(parts); } public void testNoRewriteUsedResult1() throws Exception { String source = newlineJoin( "function a(){return 1}", "var x = a()"); testSame(source); } public void testNoRewriteUsedResult2() throws Exception { String source = newlineJoin( "var a = function(){return 1}", "a(); var b = a()"); testSame(source); } public void testRewriteUnusedResult1() throws Exception { String source = newlineJoin( "function a(){return 1}", "a()"); String expected = newlineJoin( "function a(){return}", "a()"); test(source, expected); } public void testRewriteUnusedResult2() throws Exception { String source = newlineJoin( "var a; a = function(){return 1}", "a()"); String expected = newlineJoin( "var a; a = function(){return}", "a()"); test(source, expected); } public void testRewriteUnusedResult3() throws Exception { String source = newlineJoin( "var a = function(){return 1}", "a()"); String expected = newlineJoin( "var a = function(){return}", "a()"); test(source, expected); } public void testRewriteUnusedResult4a() throws Exception { String source = newlineJoin( "var a = function(){return a()}", "a()"); testSame(source); } public void testRewriteUnusedResult4b() throws Exception { String source = newlineJoin( "var a = function b(){return b()}", "a()"); testSame(source); } public void testRewriteUnusedResult4c() throws Exception { String source = newlineJoin( "function a(){return a()}", "a()"); testSame(source); } public void testRewriteUnusedResult5() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype.foo = function(args) {return args};", "var o = new a;", "o.foo()"); String expected = newlineJoin( "function a(){}", "a.prototype.foo = function(args) {return};", "var o = new a;", "o.foo()"); test(source, expected); } public void testRewriteUnusedResult6() throws Exception { String source = newlineJoin( "function a(){return (g = 1)}", "a()"); String expected = newlineJoin( "function a(){g = 1;return}", "a()"); test(source, expected); } public void testRewriteUnusedResult7a() throws Exception { String source = newlineJoin( "function a() { return 1 }", "function b() { return a() }", "function c() { return b() }", "c();"); String expected = newlineJoin( "function a() { return 1 }", "function b() { return a() }", "function c() { b(); return }", "c();"); test(source, expected); } public void testRewriteUnusedResult7b() throws Exception { String source = newlineJoin( "c();", "function c() { return b() }", "function b() { return a() }", "function a() { return 1 }"); // Iteration 1. String expected = newlineJoin( "c();", "function c() { b(); return }", "function b() { return a() }", "function a() { return 1 }"); test(source, expected); // Iteration 2. source = expected; expected = newlineJoin( "c();", "function c() { b(); return }", "function b() { a(); return }", "function a() { return 1 }"); test(source, expected); // Iteration 3. source = expected; expected = newlineJoin( "c();", "function c() { b(); return }", "function b() { a(); return }", "function a() { return }"); test(source, expected); } public void testRewriteUnusedResult8() throws Exception { String source = newlineJoin( "function a() { return c() }", "function b() { return a() }", "function c() { return b() }", "c();"); testSame(source); } public void testNoRewriteObjLit1() throws Exception { String source = newlineJoin( "var a = {b:function(){return 1;}}", "for(c in a) (a[c])();", "a.b()"); testSame(source); } public void testNoRewriteObjLit2() throws Exception { String source = newlineJoin( "var a = {b:function fn(){return 1;}}", "for(c in a) (a[c])();", "a.b()"); testSame(source); } public void testNoRewriteArrLit() throws Exception { String source = newlineJoin( "var a = [function(){return 1;}]", "(a[0])();"); testSame(source); } public void testPrototypeMethod1() throws Exception { String source = newlineJoin( "function c(){}", "c.prototype.a = function(){return 1}", "var x = new c;", "x.a()"); String result = newlineJoin( "function c(){}", "c.prototype.a = function(){return}", "var x = new c;", "x.a()"); test(source, result); } public void testPrototypeMethod2() throws Exception { String source = newlineJoin( "function c(){}", "c.prototype.a = function(){return 1}", "goog.reflect.object({a: 'v'})", "var x = new c;", "x.a()"); testSame(source); } public void testPrototypeMethod3() throws Exception { String source = newlineJoin( "function c(){}", "c.prototype.a = function(){return 1}", "var x = new c;", "for(var key in goog.reflect.object({a: 'v'})){ x[key](); }", "x.a()"); testSame(source); } public void testPrototypeMethod4() throws Exception { String source = newlineJoin( "function c(){}", "c.prototype.a = function(){return 1}", "var x = new c;", "for(var key in goog.reflect.object({a: 'v'})){ x[key](); }"); testSame(source); } public void testCallOrApply() throws Exception { // TODO(johnlenz): Add support for .call and .apply testSame("function a() {return 1}; a.call(new foo);"); testSame("function a() {return 1}; a.apply(new foo);"); } public void testRewriteUseSiteRemoval() throws Exception { String source = newlineJoin( "function a() { return {\"_id\" : 1} }", "a();"); String expected = newlineJoin( "function a() { return }", "a();"); test(source, expected); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReplaceCssNamesTest.java0000644000175000017500000002554712115204405027554 0ustar apoapo/* * 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 static com.google.javascript.jscomp.ReplaceCssNames.UNEXPECTED_STRING_LITERAL_ERROR; import static com.google.javascript.jscomp.ReplaceCssNames.UNKNOWN_SYMBOL_WARNING; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.javascript.rhino.Node; import java.util.Map; import java.util.Set; /** * Tests for ReplaceCssNames.java. * */ public class ReplaceCssNamesTest extends CompilerTestCase { /** Whether to pass the map of replacements as opposed to null */ boolean useReplacementMap; /** Map of replacements to use during the test. */ Map replacementMap = new ImmutableMap.Builder() .put("active", "a") .put("buttonbar", "b") .put("colorswatch", "c") .put("disabled", "d") .put("elephant", "e") .put("footer", "f") .put("goog", "g") .build(); Map replacementMapFull = new ImmutableMap.Builder() .put("long-prefix", "h") .put("suffix1", "i") .put("unrelated-word", "k") .put("unrelated", "l") .put("long-suffix", "m") .put("long-prefix-suffix1", "h-i") .build(); CssRenamingMap renamingMap; Set whitelist; Map cssNames; public ReplaceCssNamesTest() { } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ReplaceCssNames(compiler, cssNames, whitelist) { @Override protected CssRenamingMap getCssRenamingMap() { return useReplacementMap ? renamingMap : null; } }; } protected CssRenamingMap getPartialMap() { CssRenamingMap map = new CssRenamingMap.ByPart() { @Override public String get(String value) { return replacementMap.get(value); } }; return map; } protected CssRenamingMap getFullMap() { return new CssRenamingMap.ByWhole() { @Override public String get(String value) { return replacementMapFull.get(value); } }; } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); cssNames = Maps.newHashMap(); useReplacementMap = true; renamingMap = getPartialMap(); } @Override protected int getNumRepetitions() { // The first pass strips the goog.getCssName even if a warning is issued, // such that a subsequent pass won't issue a warning. return 1; } public void testDoNotUseReplacementMap() { useReplacementMap = false; test("var x = goog.getCssName('goog-footer-active')", "var x = 'goog-footer-active'"); test("el.className = goog.getCssName('goog-colorswatch-disabled')", "el.className = 'goog-colorswatch-disabled'"); test("setClass(goog.getCssName('active-buttonbar'))", "setClass('active-buttonbar')"); Map expected = new ImmutableMap.Builder() .put("goog", 2) .put("footer", 1) .put("active", 2) .put("colorswatch", 1) .put("disabled", 1) .put("buttonbar", 1) .build(); assertEquals(expected, cssNames); } public void testOneArgWithUnknownStringLiterals() { test("var x = goog.getCssName('unknown')", "var x = 'unknown'", null, UNKNOWN_SYMBOL_WARNING); test("el.className = goog.getCssName('ooo')", "el.className = 'ooo'", null, UNKNOWN_SYMBOL_WARNING); test("setClass(goog.getCssName('ab'))", "setClass('ab')", null, UNKNOWN_SYMBOL_WARNING); } public void testOneArgWithSimpleStringLiterals() { test("var x = goog.getCssName('buttonbar')", "var x = 'b'"); test("el.className = goog.getCssName('colorswatch')", "el.className = 'c'"); test("setClass(goog.getCssName('elephant'))", "setClass('e')"); Map expected = new ImmutableMap.Builder() .put("buttonbar", 1) .put("colorswatch", 1) .put("elephant", 1) .build(); assertEquals(expected, cssNames); } public void testOneArgWithCompositeClassNames() { test("var x = goog.getCssName('goog-footer-active')", "var x = 'g-f-a'"); test("el.className = goog.getCssName('goog-colorswatch-disabled')", "el.className = 'g-c-d'"); test("setClass(goog.getCssName('active-buttonbar'))", "setClass('a-b')"); Map expected = new ImmutableMap.Builder() .put("goog", 2) .put("footer", 1) .put("active", 2) .put("colorswatch", 1) .put("disabled", 1) .put("buttonbar", 1) .build(); assertEquals(expected, cssNames); } public void testOneArgWithCompositeClassNamesFull() { renamingMap = getFullMap(); test("var x = goog.getCssName('long-prefix')", "var x = 'h'"); test("var x = goog.getCssName('long-prefix-suffix1')", "var x = 'h-i'"); test("var x = goog.getCssName('unrelated')", "var x = 'l'"); test("var x = goog.getCssName('unrelated-word')", "var x = 'k'"); } public void testOneArgWithCompositeClassNamesWithUnknownParts() { test("var x = goog.getCssName('goog-header-active')", "var x = 'goog-header-active'", null, UNKNOWN_SYMBOL_WARNING); test("el.className = goog.getCssName('goog-colorswatch-focussed')", "el.className = 'goog-colorswatch-focussed'", null, UNKNOWN_SYMBOL_WARNING); test("setClass(goog.getCssName('inactive-buttonbar'))", "setClass('inactive-buttonbar')", null, UNKNOWN_SYMBOL_WARNING); } public void testTwoArgsWithStringLiterals() { test("var x = goog.getCssName('header', 'active')", null, UNEXPECTED_STRING_LITERAL_ERROR); test("el.className = goog.getCssName('footer', window)", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("setClass(goog.getCssName('buttonbar', 'disabled'))", null, UNEXPECTED_STRING_LITERAL_ERROR); test("setClass(goog.getCssName(goog.getCssName('buttonbar'), 'active'))", null, UNEXPECTED_STRING_LITERAL_ERROR); } public void testTwoArsWithVariableFirstArg() { test("var x = goog.getCssName(baseClass, 'active')", "var x = baseClass + '-a'"); test("el.className = goog.getCssName(this.getClass(), 'disabled')", "el.className = this.getClass() + '-d'"); test("setClass(goog.getCssName(BASE_CLASS, 'disabled'))", "setClass(BASE_CLASS + '-d')"); } public void testTwoArgsWithVariableFirstArgFull() { renamingMap = getFullMap(); test("var x = goog.getCssName(baseClass, 'long-suffix')", "var x = baseClass + '-m'"); } public void testZeroArguments() { test("goog.getCssName()", null, ReplaceCssNames.INVALID_NUM_ARGUMENTS_ERROR); } public void testManyArguments() { test("goog.getCssName('a', 'b', 'c')", null, ReplaceCssNames.INVALID_NUM_ARGUMENTS_ERROR); test("goog.getCssName('a', 'b', 'c', 'd')", null, ReplaceCssNames.INVALID_NUM_ARGUMENTS_ERROR); test("goog.getCssName('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')", null, ReplaceCssNames.INVALID_NUM_ARGUMENTS_ERROR); } public void testNonStringArgument() { test("goog.getCssName(window);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(555);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName([]);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName({});", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(null);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(undefined);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, window);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, 555);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, []);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, {});", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, null);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName(baseClass, undefined);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); test("goog.getCssName('foo', 3);", null, ReplaceCssNames.STRING_LITERAL_EXPECTED_ERROR); } public void testNoSymbolMapStripsCallAndDoesntIssueWarnings() { String input = "[goog.getCssName('test'), goog.getCssName(base, 'active')]"; Compiler compiler = new Compiler(); ErrorManager errorMan = new BasicErrorManager() { @Override protected void printSummary() {} @Override public void println(CheckLevel level, JSError error) {} }; compiler.setErrorManager(errorMan); Node root = compiler.parseTestCode(input); useReplacementMap = false; ReplaceCssNames replacer = new ReplaceCssNames(compiler, null, null); replacer.process(null, root); assertEquals("[\"test\",base+\"-active\"]", compiler.toSource(root)); assertEquals("There should be no errors", 0, errorMan.getErrorCount()); assertEquals("There should be no warnings", 0, errorMan.getWarningCount()); } public void testWhitelistByPart() { whitelist = ImmutableSet.of("goog", "elephant"); test("var x = goog.getCssName('goog')", "var x = 'goog'"); test("var x = goog.getCssName('elephant')", "var x = 'elephant'"); // Whitelisting happens before splitting, not after. test("var x = goog.getCssName('goog-elephant')", "var x = 'g-e'"); } public void testWhitelistByWhole() { whitelist = ImmutableSet.of("long-prefix"); renamingMap = getFullMap(); test("var x = goog.getCssName('long-prefix')", "var x = 'long-prefix'"); } public void testWhitelistWithDashes() { whitelist = ImmutableSet.of("goog-elephant"); test("var x = goog.getCssName('goog')", "var x = 'g'"); test("var x = goog.getCssName('elephant')", "var x = 'e'"); test("var x = goog.getCssName('goog-elephant')", "var x = 'goog-elephant'"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckAccessControlsTest.java0000644000175000017500000007260312115204405030422 0ustar apoapo/* * 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 static com.google.javascript.jscomp.CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS; import static com.google.javascript.jscomp.CheckAccessControls.BAD_PRIVATE_PROPERTY_ACCESS; import static com.google.javascript.jscomp.CheckAccessControls.BAD_PROTECTED_PROPERTY_ACCESS; import static com.google.javascript.jscomp.CheckAccessControls.CONST_PROPERTY_DELETED; import static com.google.javascript.jscomp.CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_CLASS; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_CLASS_REASON; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_NAME; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_NAME_REASON; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_PROP; import static com.google.javascript.jscomp.CheckAccessControls.DEPRECATED_PROP_REASON; import static com.google.javascript.jscomp.CheckAccessControls.EXTEND_FINAL_CLASS; import static com.google.javascript.jscomp.CheckAccessControls.PRIVATE_OVERRIDE; import static com.google.javascript.jscomp.CheckAccessControls.VISIBILITY_MISMATCH; /** * Tests for {@link CheckAccessControls}. * * @author nicksantos@google.com (Nick Santos) */ public class CheckAccessControlsTest extends CompilerTestCase { public CheckAccessControlsTest() { super(CompilerTypeTestCase.DEFAULT_EXTERNS); parseTypeInfo = true; enableTypeCheck(CheckLevel.WARNING); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CheckAccessControls(compiler); } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.setWarningLevel(DiagnosticGroups.ACCESS_CONTROLS, CheckLevel.ERROR); options.setWarningLevel(DiagnosticGroups.CONSTANT_PROPERTY, CheckLevel.ERROR); return options; } /** * Tests that the given JavaScript code has a @deprecated marker * somewhere in it which raises an error. Also tests that the * deprecated marker works with a message. The JavaScript should * have a JsDoc of the form "@deprecated %s\n". * * @param js The JavaScript code to parse and test. * @param reason A simple deprecation reason string, used for testing * the addition of a deprecation reason to the @deprecated tag. * @param error The deprecation error expected when no reason is given. * @param errorWithMessage The deprecation error expected when a reason * message is given. */ private void testDep(String js, String reason, DiagnosticType error, DiagnosticType errorWithMessage) { // Test without a reason. test(String.format(js, ""), null, error); // Test with a reason. test(String.format(js, reason), null, errorWithMessage, null, reason); } public void testDeprecatedFunction() { testDep("/** @deprecated %s */ function f() {} function g() { f(); }", "Some Reason", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testWarningOnDeprecatedConstVariable() { testDep("/** @deprecated %s */ var f = 4; function g() { alert(f); }", "Another reason", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testThatNumbersArentDeprecated() { testSame("/** @deprecated */ var f = 4; var h = 3; " + "function g() { alert(h); }"); } public void testDeprecatedFunctionVariable() { testDep("/** @deprecated %s */ var f = function() {}; " + "function g() { f(); }", "I like g...", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testNoWarningInGlobalScope() { testSame("var goog = {}; goog.makeSingleton = function(x) {};" + "/** @deprecated */ function f() {} goog.makeSingleton(f);"); } public void testNoWarningInGlobalScopeForCall() { testDep("/** @deprecated %s */ function f() {} f();", "Some global scope", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testNoWarningInDeprecatedFunction() { testSame("/** @deprecated */ function f() {} " + "/** @deprecated */ function g() { f(); }"); } public void testWarningInNormalClass() { testDep("/** @deprecated %s */ function f() {}" + "/** @constructor */ var Foo = function() {}; " + "Foo.prototype.bar = function() { f(); }", "FooBar", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testWarningForProperty1() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.bar = 3;" + "Foo.prototype.baz = function() { alert((new Foo()).bar); };", "A property is bad", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForProperty2() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.bar = 3;" + "Foo.prototype.baz = function() { alert(this.bar); };", "Zee prop, it is deprecated!", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForDeprecatedClass() { testDep("/** @constructor \n* @deprecated %s */ function Foo() {} " + "function f() { new Foo(); }", "Use the class 'Bar'", DEPRECATED_CLASS, DEPRECATED_CLASS_REASON); } public void testNoWarningForDeprecatedClassInstance() { testSame("/** @constructor \n * @deprecated */ function Foo() {} " + "/** @param {Foo} x */ function f(x) { return x; }"); } public void testWarningForDeprecatedSuperClass() { testDep("/** @constructor \n * @deprecated %s */ function Foo() {} " + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "function f() { new SubFoo(); }", "Superclass to the rescue!", DEPRECATED_CLASS, DEPRECATED_CLASS_REASON); } public void testWarningForDeprecatedSuperClass2() { testDep("/** @constructor \n * @deprecated %s */ function Foo() {} " + "var namespace = {}; " + "/** @constructor \n * @extends {Foo} */ " + "namespace.SubFoo = function() {}; " + "function f() { new namespace.SubFoo(); }", "Its only weakness is Kryptoclass", DEPRECATED_CLASS, DEPRECATED_CLASS_REASON); } public void testWarningForPrototypeProperty() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.bar = 3;" + "Foo.prototype.baz = function() { alert(Foo.prototype.bar); };", "It is now in production, use that model...", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testNoWarningForNumbers() { testSame("/** @constructor */ function Foo() {}" + "/** @deprecated */ Foo.prototype.bar = 3;" + "Foo.prototype.baz = function() { alert(3); };"); } public void testWarningForMethod1() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.bar = function() {};" + "Foo.prototype.baz = function() { this.bar(); };", "There is a madness to this method", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForMethod2() { testDep("/** @constructor */ function Foo() {} " + "/** @deprecated %s */ Foo.prototype.bar; " + "Foo.prototype.baz = function() { this.bar(); };", "Stop the ringing!", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testNoWarningInDeprecatedClass() { testSame("/** @deprecated */ function f() {} " + "/** @constructor \n * @deprecated */ " + "var Foo = function() {}; " + "Foo.prototype.bar = function() { f(); }"); } public void testNoWarningInDeprecatedClass2() { testSame("/** @deprecated */ function f() {} " + "/** @constructor \n * @deprecated */ " + "var Foo = function() {}; " + "Foo.bar = function() { f(); }"); } public void testNoWarningInDeprecatedStaticMethod() { testSame("/** @deprecated */ function f() {} " + "/** @constructor */ " + "var Foo = function() {}; " + "/** @deprecated */ Foo.bar = function() { f(); }"); } public void testWarningInStaticMethod() { testDep("/** @deprecated %s */ function f() {} " + "/** @constructor */ " + "var Foo = function() {}; " + "Foo.bar = function() { f(); }", "crazy!", DEPRECATED_NAME, DEPRECATED_NAME_REASON); } public void testDeprecatedObjLitKey() { testDep("var f = {}; /** @deprecated %s */ f.foo = 3; " + "function g() { return f.foo; }", "It is literally not used anymore", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForSubclassMethod() { testDep("/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @deprecated %s */ SubFoo.prototype.bar = function() {};" + "function f() { (new SubFoo()).bar(); };", "I have a parent class!", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForSuperClassWithDeprecatedSubclassMethod() { testSame("/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @deprecated \n * @override */ SubFoo.prototype.bar = " + "function() {};" + "function f() { (new Foo()).bar(); };"); } public void testWarningForSuperclassMethod() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.bar = function() {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "SubFoo.prototype.bar = function() {};" + "function f() { (new SubFoo()).bar(); };", "I have a child class!", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForSuperclassMethod2() { testDep("/** @constructor */ function Foo() {}" + "/** @deprecated %s \n* @protected */" + "Foo.prototype.bar = function() {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @protected */SubFoo.prototype.bar = function() {};" + "function f() { (new SubFoo()).bar(); };", "I have another child class...", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForBind() { testDep("/** @deprecated %s */ Function.prototype.bind = function() {};" + "(function() {}).bind();", "I'm bound to this method...", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testWarningForDeprecatedClassInGlobalScope() { testDep("/** @constructor \n * @deprecated %s */ var Foo = function() {};" + "new Foo();", "I'm a very worldly object!", DEPRECATED_CLASS, DEPRECATED_CLASS_REASON); } public void testNoWarningForPrototypeCopying() { testSame("/** @constructor */ var Foo = function() {};" + "Foo.prototype.bar = function() {};" + "/** @deprecated */ Foo.prototype.baz = Foo.prototype.bar;" + "(new Foo()).bar();"); } public void testNoWarningOnDeprecatedPrototype() { // This used to cause an NPE. testSame("/** @constructor */ var Foo = function() {};" + "/** @deprecated */ Foo.prototype = {};" + "Foo.prototype.bar = function() {};"); } public void testPrivateAccessForNames() { testSame("/** @private */ function foo_() {}; foo_();"); test(new String[] { "/** @private */ function foo_() {};", "foo_();" }, null, BAD_PRIVATE_GLOBAL_ACCESS); } public void testPrivateAccessForProperties1() { testSame("/** @constructor */ function Foo() {}" + "/** @private */ Foo.prototype.bar_ = function() {};" + "Foo.prototype.baz = function() { this.bar_(); }; (new Foo).bar_();"); } public void testPrivateAccessForProperties2() { testSame(new String[] { "/** @constructor */ function Foo() {}", "/** @private */ Foo.prototype.bar_ = function() {};" + "Foo.prototype.baz = function() { this.bar_(); }; (new Foo).bar_();" }); } public void testPrivateAccessForProperties3() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @private */ Foo.prototype.bar_ = function() {}; (new Foo).bar_();", "Foo.prototype.baz = function() { this.bar_(); };" }); } public void testPrivateAccessForProperties4() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @private */ Foo.prototype.bar_ = function() {};", "Foo.prototype['baz'] = function() { (new Foo()).bar_(); };" }); } public void testNoPrivateAccessForProperties1() { test(new String[] { "/** @constructor */ function Foo() {} (new Foo).bar_();", "/** @private */ Foo.prototype.bar_ = function() {};" + "Foo.prototype.baz = function() { this.bar_(); };" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties2() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};" + "Foo.prototype.baz = function() { this.bar_(); };", "(new Foo).bar_();" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties3() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};", "/** @constructor */ function OtherFoo() { (new Foo).bar_(); }" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties4() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() { this.bar_(); }" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties5() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.baz = function() { this.bar_(); }" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties6() { // Overriding a private property with a non-private property // in a different file causes problems. test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.bar_ = function() {};" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties7() { // It's OK to override a private property with a non-private property // in the same file, but you'll get yelled at when you try to use it. test(new String[] { "/** @constructor */ function Foo() {} " + "/** @private */ Foo.prototype.bar_ = function() {};" + "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "SubFoo.prototype.bar_ = function() {};", "SubFoo.prototype.baz = function() { this.bar_(); }" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testNoPrivateAccessForProperties8() { test(new String[] { "/** @constructor */ function Foo() { /** @private */ this.bar_ = 3; }", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() { /** @private */ this.bar_ = 3; };" }, null, PRIVATE_OVERRIDE); } public void testProtectedAccessForProperties1() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @protected */ Foo.prototype.bar = function() {};" + "(new Foo).bar();", "Foo.prototype.baz = function() { this.bar(); };" }); } public void testProtectedAccessForProperties2() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @protected */ Foo.prototype.bar = function() {};" + "(new Foo).bar();", "/** @constructor \n * @extends {Foo} */" + "function SubFoo() { this.bar(); }" }); } public void testProtectedAccessForProperties3() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @protected */ Foo.prototype.bar = function() {};" + "(new Foo).bar();", "/** @constructor \n * @extends {Foo} */" + "function SubFoo() { }" + "SubFoo.baz = function() { (new Foo).bar(); }" }); } public void testProtectedAccessForProperties4() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @protected */ Foo.bar = function() {};", "/** @constructor \n * @extends {Foo} */" + "function SubFoo() { Foo.bar(); }" }); } public void testProtectedAccessForProperties5() { testSame(new String[] { "/** @constructor */ function Foo() {}" + "/** @protected */ Foo.prototype.bar = function() {};" + "(new Foo).bar();", "/** @constructor \n * @extends {Foo} */" + "var SubFoo = function() { this.bar(); }" }); } public void testProtectedAccessForProperties6() { testSame(new String[] { "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @protected */ goog.Foo.prototype.bar = function() {};", "/** @constructor \n * @extends {goog.Foo} */" + "goog.SubFoo = function() { this.bar(); };" }); } public void testNoProtectedAccessForProperties1() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @protected */ Foo.prototype.bar = function() {};", "(new Foo).bar();" }, null, BAD_PROTECTED_PROPERTY_ACCESS); } public void testNoProtectedAccessForProperties2() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @protected */ Foo.prototype.bar = function() {};", "/** @constructor */ function OtherFoo() { (new Foo).bar(); }" }, null, BAD_PROTECTED_PROPERTY_ACCESS); } public void testNoProtectedAccessForProperties3() { test(new String[] { "/** @constructor */ function Foo() {} " + "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @protected */ SubFoo.prototype.bar = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubberFoo() { (new SubFoo).bar(); }" }, null, BAD_PROTECTED_PROPERTY_ACCESS); } public void testNoProtectedAccessForProperties4() { test(new String[] { "/** @constructor */ function Foo() { (new SubFoo).bar(); } ", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @protected */ SubFoo.prototype.bar = function() {};", }, null, BAD_PROTECTED_PROPERTY_ACCESS); } public void testNoProtectedAccessForProperties5() { test(new String[] { "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @protected */ goog.Foo.prototype.bar = function() {};", "/** @constructor */" + "goog.NotASubFoo = function() { (new goog.Foo).bar(); };" }, null, BAD_PROTECTED_PROPERTY_ACCESS); } public void testNoExceptionsWithBadConstructors1() { testSame(new String[] { "function Foo() { (new SubFoo).bar(); } " + "/** @constructor */ function SubFoo() {}" + "/** @protected */ SubFoo.prototype.bar = function() {};" }); } public void testNoExceptionsWithBadConstructors2() { testSame(new String[] { "/** @constructor */ function Foo() {} " + "Foo.prototype.bar = function() {};" + "/** @constructor */" + "function SubFoo() {}" + "/** @protected */ " + "SubFoo.prototype.bar = function() { (new Foo).bar(); };" }); } public void testGoodOverrideOfProtectedProperty() { testSame(new String[] { "/** @constructor */ function Foo() { } " + "/** @protected */ Foo.prototype.bar = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @inheritDoc */ SubFoo.prototype.bar = function() {};", }); } public void testBadOverrideOfProtectedProperty() { test(new String[] { "/** @constructor */ function Foo() { } " + "/** @protected */ Foo.prototype.bar = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @private */ SubFoo.prototype.bar = function() {};", }, null, VISIBILITY_MISMATCH); } public void testBadOverrideOfPrivateProperty() { test(new String[] { "/** @constructor */ function Foo() { } " + "/** @private */ Foo.prototype.bar = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @protected */ SubFoo.prototype.bar = function() {};", }, null, PRIVATE_OVERRIDE); testSame(new String[] { "/** @constructor */ function Foo() { } " + "/** @private */ Foo.prototype.bar = function() {};", "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {}" + "/** @override \n *@suppress{visibility} */\n" + " SubFoo.prototype.bar = function() {};", }); } public void testAccessOfStaticMethodOnPrivateConstructor() { testSame(new String[] { "/** @constructor \n * @private */ function Foo() { } " + "Foo.create = function() { return new Foo(); };", "Foo.create()", }); } public void testAccessOfStaticMethodOnPrivateQualifiedConstructor() { testSame(new String[] { "var goog = {};" + "/** @constructor \n * @private */ goog.Foo = function() { }; " + "goog.Foo.create = function() { return new goog.Foo(); };", "goog.Foo.create()", }); } public void testInstanceofOfPrivateConstructor() { testSame(new String[] { "var goog = {};" + "/** @constructor \n * @private */ goog.Foo = function() { }; " + "goog.Foo.create = function() { return new goog.Foo(); };", "goog instanceof goog.Foo", }); } public void testOkAssignmentOfDeprecatedProperty() { testSame( "/** @constructor */ function Foo() {" + " /** @deprecated */ this.bar = 3;" + "}"); } public void testBadReadOfDeprecatedProperty() { testDep( "/** @constructor */ function Foo() {" + " /** @deprecated %s */ this.bar = 3;" + " this.baz = this.bar;" + "}", "GRR", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testAutoboxedDeprecatedProperty() { test( "", // no externs "/** @constructor */ function String() {}" + "/** @deprecated %s */ String.prototype.length;" + "function f() { return 'x'.length; }", "GRR", DEPRECATED_PROP_REASON, null); } public void testAutoboxedPrivateProperty() { test( "/** @constructor */ function String() {}" + "/** @private */ String.prototype.length;", // externs "function f() { return 'x'.length; }", "", // output BAD_PRIVATE_PROPERTY_ACCESS, null); } public void testNullableDeprecatedProperty() { testDep( "/** @constructor */ function Foo() {}" + "/** @deprecated %s */ Foo.prototype.length;" + "/** @param {?Foo} x */ function f(x) { return x.length; }", "GRR", DEPRECATED_PROP, DEPRECATED_PROP_REASON); } public void testNullablePrivateProperty() { test(new String[] { "/** @constructor */ function Foo() {}" + "/** @private */ Foo.prototype.length;", "/** @param {?Foo} x */ function f(x) { return x.length; }" }, null, BAD_PRIVATE_PROPERTY_ACCESS); } public void testConstantProperty1() { test("/** @constructor */ function A() {" + "/** @const */ this.bar = 3;}" + "/** @constructor */ function B() {" + "/** @const */ this.bar = 3;this.bar += 4;}", null, CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty2() { test("/** @constructor */ function Foo() {}" + "/** @const */ Foo.prototype.prop = 2;" + "var foo = new Foo();" + "foo.prop = 3;", null , CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty3() { testSame("var o = { /** @const */ x: 1 };" + "o.x = 2;"); } public void testConstantProperty4() { test("/** @constructor */ function cat(name) {}" + "/** @const */ cat.test = 1;" + "cat.test *= 2;", null, CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty5() { test("/** @constructor */ function Foo() { this.prop = 1;}" + "/** @const */ Foo.prototype.prop;" + "Foo.prototype.prop = 2", null , CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty6() { test("/** @constructor */ function Foo() { this.prop = 1;}" + "/** @const */ Foo.prototype.prop = 2;", null , CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty7() { testSame("/** @constructor */ function Foo() {} " + "Foo.prototype.bar_ = function() {};" + "/** @constructor \n * @extends {Foo} */ " + "function SubFoo() {};" + "/** @const */ /** @override */ SubFoo.prototype.bar_ = function() {};" + "SubFoo.prototype.baz = function() { this.bar_(); }"); } public void testConstantProperty8() { testSame("var o = { /** @const */ x: 1 };" + "var y = o.x;"); } public void testConstantProperty9() { testSame("/** @constructor */ function A() {" + "/** @const */ this.bar = 3;}" + "/** @constructor */ function B() {" + "this.bar = 4;}"); } public void testConstantProperty10() { testSame("/** @constructor */ function Foo() { this.prop = 1;}" + "/** @const */ Foo.prototype.prop;"); } public void testConstantProperty11() { test("/** @constructor */ function Foo() {}" + "/** @const */ Foo.prototype.bar;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() { this.bar = 5; this.bar = 6; }", null , CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty12() { testSame("/** @constructor */ function Foo() {}" + "/** @const */ Foo.prototype.bar;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() { this.bar = 5; }" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo2() { this.bar = 5; }"); } public void testConstantProperty13() { test("/** @constructor */ function Foo() {}" + "/** @const */ Foo.prototype.bar;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() { this.bar = 5; }" + "/**\n" + " * @constructor\n" + " * @extends {SubFoo}\n" + " */ function SubSubFoo() { this.bar = 5; }", null , CONST_PROPERTY_REASSIGNED_VALUE); } public void testConstantProperty14() { test("/** @constructor */ function Foo() {" + "/** @const */ this.bar = 3; delete this.bar; }", null, CONST_PROPERTY_DELETED); } public void testSuppressConstantProperty() { testSame("/** @constructor */ function A() {" + "/** @const */ this.bar = 3;}" + "/**\n" + " * @suppress {constantProperty}\n" + " * @constructor\n" + " */ function B() {" + "/** @const */ this.bar = 3;this.bar += 4;}"); } public void testSuppressConstantProperty2() { testSame("/** @constructor */ function A() {" + "/** @const */ this.bar = 3;}" + "/**\n" + " * @suppress {const}\n" + " * @constructor\n" + " */ function B() {" + "/** @const */ this.bar = 3;this.bar += 4;}"); } public void testFinalClassCannotBeSubclassed() { test( "/**\n" + " * @constructor\n" + " * @const\n" + " */ Foo = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n*" + " */ Bar = function() {};", null, EXTEND_FINAL_CLASS); test( "/**\n" + " * @constructor\n" + " * @const\n" + " */ function Foo() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n*" + " */ function Bar() {};", null, EXTEND_FINAL_CLASS); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DevirtualizePrototypeMethodsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DevirtualizePrototypeMethodsTest.j0000644000175000017500000006060512115204405031767 0ustar apoapo/* * 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.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import java.util.List; /** * Tests for {@link DevirtualizePrototypeMethods} * */ public class DevirtualizePrototypeMethodsTest extends CompilerTestCase { private static final String EXTERNAL_SYMBOLS = "var extern;extern.externalMethod"; private final List typeInformation; public DevirtualizePrototypeMethodsTest() { super(EXTERNAL_SYMBOLS); typeInformation = Lists.newArrayList(); } @Override protected int getNumRepetitions() { // run pass once. return 1; } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); disableTypeCheck(); } /** * Combine source strings using '\n' as the separator. */ private static String newlineJoin(String ... parts) { return Joiner.on("\n").join(parts); } /** * Combine source strings using ';' as the separator. */ private static String semicolonJoin(String ... parts) { return Joiner.on(";").join(parts); } /** * Inputs for prototype method tests. */ private static class RewritePrototypeMethodTestInput { static final String INPUT = newlineJoin( "/** @constructor */", "function a(){ this.x = 3; }", "/** @return {number} */", "a.prototype.foo = function() {return this.x};", "/** @param {number} p\n@return {number} */", "a.prototype.bar = function(p) {return this.x};", "a.prototype.baz = function() {};", "var o = new a;", "o.foo();", "o.bar(2);", "o.baz()"); static final String EXPECTED = newlineJoin( "function a(){ this.x = 3; }", "var JSCompiler_StaticMethods_foo = ", "function(JSCompiler_StaticMethods_foo$self) {", " return JSCompiler_StaticMethods_foo$self.x", "};", "var JSCompiler_StaticMethods_bar = ", "function(JSCompiler_StaticMethods_bar$self, p) {", " return JSCompiler_StaticMethods_bar$self.x", "};", "var JSCompiler_StaticMethods_baz = ", "function(JSCompiler_StaticMethods_baz$self) {", "};", "var o = new a;", "JSCompiler_StaticMethods_foo(o);", "JSCompiler_StaticMethods_bar(o, 2);", "JSCompiler_StaticMethods_baz(o)"); static final List EXPECTED_TYPE_CHECKING_OFF = ImmutableList.of( "FUNCTION a = null", "NAME JSCompiler_StaticMethods_foo$self = null", "FUNCTION JSCompiler_StaticMethods_foo = null", "NAME JSCompiler_StaticMethods_bar$self = null", "FUNCTION JSCompiler_StaticMethods_bar = null", "FUNCTION JSCompiler_StaticMethods_baz = null", "NEW a = null", "CALL JSCompiler_StaticMethods_foo = null", "CALL JSCompiler_StaticMethods_bar = null", "CALL JSCompiler_StaticMethods_baz = null"); static final List EXPECTED_TYPE_CHECKING_ON = ImmutableList.of( "FUNCTION a = function (new:a): undefined", "NAME JSCompiler_StaticMethods_foo$self = a", "FUNCTION JSCompiler_StaticMethods_foo = function (a): number", "NAME JSCompiler_StaticMethods_bar$self = a", "FUNCTION JSCompiler_StaticMethods_bar = function (a, number): number", "FUNCTION JSCompiler_StaticMethods_baz = function (a): undefined", "NEW a = a", "CALL JSCompiler_StaticMethods_foo = number", "CALL JSCompiler_StaticMethods_bar = number", "CALL JSCompiler_StaticMethods_baz = undefined"); private RewritePrototypeMethodTestInput() {} } public void testRewritePrototypeMethods1() throws Exception { // type checking off disableTypeCheck(); checkTypes(RewritePrototypeMethodTestInput.INPUT, RewritePrototypeMethodTestInput.EXPECTED, RewritePrototypeMethodTestInput.EXPECTED_TYPE_CHECKING_OFF); } public void testRewritePrototypeMethods2() throws Exception { // type checking on enableTypeCheck(CheckLevel.ERROR); checkTypes(RewritePrototypeMethodTestInput.INPUT, RewritePrototypeMethodTestInput.EXPECTED, RewritePrototypeMethodTestInput.EXPECTED_TYPE_CHECKING_ON); } public void testRewriteChained() throws Exception { String source = newlineJoin( "A.prototype.foo = function(){return this.b};", "B.prototype.bar = function(){};", "o.foo().bar()"); String expected = newlineJoin( "var JSCompiler_StaticMethods_foo = ", "function(JSCompiler_StaticMethods_foo$self) {", " return JSCompiler_StaticMethods_foo$self.b", "};", "var JSCompiler_StaticMethods_bar = ", "function(JSCompiler_StaticMethods_bar$self) {", "};", "JSCompiler_StaticMethods_bar(JSCompiler_StaticMethods_foo(o))"); test(source, expected); } /** * Inputs for declaration used as an r-value tests. */ private static class NoRewriteDeclarationUsedAsRValue { static final String DECL = "a.prototype.foo = function() {}"; static final String CALL = "o.foo()"; private NoRewriteDeclarationUsedAsRValue() {} } public void testRewriteDeclIsExpressionStatement() throws Exception { test(semicolonJoin(NoRewriteDeclarationUsedAsRValue.DECL, NoRewriteDeclarationUsedAsRValue.CALL), "var JSCompiler_StaticMethods_foo =" + "function(JSCompiler_StaticMethods_foo$self) {};" + "JSCompiler_StaticMethods_foo(o)"); } public void testNoRewriteDeclUsedAsAssignmentRhs() throws Exception { testSame(semicolonJoin("var c = " + NoRewriteDeclarationUsedAsRValue.DECL, NoRewriteDeclarationUsedAsRValue.CALL)); } public void testNoRewriteDeclUsedAsCallArgument() throws Exception { testSame(semicolonJoin("f(" + NoRewriteDeclarationUsedAsRValue.DECL + ")", NoRewriteDeclarationUsedAsRValue.CALL)); } /** * Inputs for restrict-to-global-scope tests. */ private static class NoRewriteIfNotInGlobalScopeTestInput { static final String INPUT = newlineJoin( "function a(){}", "a.prototype.foo = function() {return this.x};", "var o = new a;", "o.foo()"); private NoRewriteIfNotInGlobalScopeTestInput() {} } public void testRewriteInGlobalScope() throws Exception { String expected = newlineJoin( "function a(){}", "var JSCompiler_StaticMethods_foo = ", "function(JSCompiler_StaticMethods_foo$self) {", " return JSCompiler_StaticMethods_foo$self.x", "};", "var o = new a;", "JSCompiler_StaticMethods_foo(o);"); test(NoRewriteIfNotInGlobalScopeTestInput.INPUT, expected); } public void testNoRewriteIfNotInGlobalScope1() throws Exception { testSame("if(true){" + NoRewriteIfNotInGlobalScopeTestInput.INPUT + "}"); } public void testNoRewriteIfNotInGlobalScope2() throws Exception { testSame("function enclosingFunction() {" + NoRewriteIfNotInGlobalScopeTestInput.INPUT + "}"); } public void testNoRewriteNamespaceFunctions() throws Exception { String source = newlineJoin( "function a(){}", "a.foo = function() {return this.x};", "a.foo()"); testSame(source); } /** * Inputs for multiple definition tests. */ private static class NoRewriteMultipleDefinitionTestInput { static final String TEMPLATE = ".prototype.foo = function() {}"; static final String SOURCE_A = "a" + TEMPLATE; static final String SOURCE_B = "b" + TEMPLATE; static final String CALL = "o.foo()"; static final String SINGLE_DEFINITION_EXPECTED = "var JSCompiler_StaticMethods_foo = " + " function(JSCompiler_StaticMethods_foo$self) {};" + "JSCompiler_StaticMethods_foo(o)"; private NoRewriteMultipleDefinitionTestInput() {} } public void testRewriteSingleDefinition1() throws Exception { test(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, NoRewriteMultipleDefinitionTestInput.CALL), NoRewriteMultipleDefinitionTestInput.SINGLE_DEFINITION_EXPECTED); } public void testRewriteSingleDefinition2() throws Exception { test(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_B, NoRewriteMultipleDefinitionTestInput.CALL), NoRewriteMultipleDefinitionTestInput.SINGLE_DEFINITION_EXPECTED); } public void testNoRewriteMultipleDefinition1() throws Exception { testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, NoRewriteMultipleDefinitionTestInput.SOURCE_A, NoRewriteMultipleDefinitionTestInput.CALL)); } public void testNoRewriteMultipleDefinition2() throws Exception { testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_B, NoRewriteMultipleDefinitionTestInput.SOURCE_B, NoRewriteMultipleDefinitionTestInput.CALL)); } public void testNoRewriteMultipleDefinition3() throws Exception { testSame(semicolonJoin(NoRewriteMultipleDefinitionTestInput.SOURCE_A, NoRewriteMultipleDefinitionTestInput.SOURCE_B, NoRewriteMultipleDefinitionTestInput.CALL)); } /** * Inputs for object literal tests. */ private static class NoRewritePrototypeObjectLiteralsTestInput { static final String REGULAR = "b.prototype.foo = function() {}"; static final String OBJ_LIT = "a.prototype = {foo : function() {}}"; static final String CALL = "o.foo()"; private NoRewritePrototypeObjectLiteralsTestInput() {} } public void testRewritePrototypeNoObjectLiterals() throws Exception { test(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.REGULAR, NoRewritePrototypeObjectLiteralsTestInput.CALL), "var JSCompiler_StaticMethods_foo = " + "function(JSCompiler_StaticMethods_foo$self) {};" + "JSCompiler_StaticMethods_foo(o)"); } public void testRewritePrototypeObjectLiterals1() throws Exception { test(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.OBJ_LIT, NoRewritePrototypeObjectLiteralsTestInput.CALL), "a.prototype={};" + "var JSCompiler_StaticMethods_foo=" + "function(JSCompiler_StaticMethods_foo$self){};" + "JSCompiler_StaticMethods_foo(o)"); } public void testNoRewritePrototypeObjectLiterals2() throws Exception { testSame(semicolonJoin(NoRewritePrototypeObjectLiteralsTestInput.OBJ_LIT, NoRewritePrototypeObjectLiteralsTestInput.REGULAR, NoRewritePrototypeObjectLiteralsTestInput.CALL)); } public void testNoRewriteExternalMethods1() throws Exception { testSame("a.externalMethod()"); } public void testNoRewriteExternalMethods2() throws Exception { testSame("A.prototype.externalMethod = function(){}; o.externalMethod()"); } public void testNoRewriteCodingConvention() throws Exception { // no rename, leading _ indicates exported symbol testSame("a.prototype._foo = function() {};"); } public void testRewriteNoVarArgs() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype.foo = function(args) {return args};", "var o = new a;", "o.foo()"); String expected = newlineJoin( "function a(){}", "var JSCompiler_StaticMethods_foo = ", " function(JSCompiler_StaticMethods_foo$self, args) {return args};", "var o = new a;", "JSCompiler_StaticMethods_foo(o)"); test(source, expected); } public void testNoRewriteVarArgs() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype.foo = function(var_args) {return arguments};", "var o = new a;", "o.foo()"); testSame(source); } /** * Inputs for invalidating reference tests. */ private static class NoRewriteNonCallReferenceTestInput { static final String BASE = newlineJoin( "function a(){}", "a.prototype.foo = function() {return this.x};", "var o = new a;"); private NoRewriteNonCallReferenceTestInput() {} } public void testRewriteCallReference() throws Exception { String expected = newlineJoin( "function a(){}", "var JSCompiler_StaticMethods_foo = ", "function(JSCompiler_StaticMethods_foo$self) {", " return JSCompiler_StaticMethods_foo$self.x", "};", "var o = new a;", "JSCompiler_StaticMethods_foo(o);"); test(NoRewriteNonCallReferenceTestInput.BASE + "o.foo()", expected); } public void testNoRewriteNoReferences() throws Exception { testSame(NoRewriteNonCallReferenceTestInput.BASE); } public void testNoRewriteNonCallReference() throws Exception { testSame(NoRewriteNonCallReferenceTestInput.BASE + "o.foo && o.foo()"); } /** * Inputs for nested definition tests. */ private static class NoRewriteNestedFunctionTestInput { static final String PREFIX = "a.prototype.foo = function() {"; static final String SUFFIX = "o.foo()"; static final String INNER = "a.prototype.bar = function() {}; o.bar()"; static final String EXPECTED_PREFIX = "var JSCompiler_StaticMethods_foo=" + "function(JSCompiler_StaticMethods_foo$self){"; static final String EXPECTED_SUFFIX = "JSCompiler_StaticMethods_foo(o)"; private NoRewriteNestedFunctionTestInput() {} } public void testRewriteNoNestedFunction() throws Exception { test(semicolonJoin( NoRewriteNestedFunctionTestInput.PREFIX + "}", NoRewriteNestedFunctionTestInput.SUFFIX, NoRewriteNestedFunctionTestInput.INNER), semicolonJoin( NoRewriteNestedFunctionTestInput.EXPECTED_PREFIX + "}", NoRewriteNestedFunctionTestInput.EXPECTED_SUFFIX, "var JSCompiler_StaticMethods_bar=" + "function(JSCompiler_StaticMethods_bar$self){}", "JSCompiler_StaticMethods_bar(o)")); } public void testNoRewriteNestedFunction() throws Exception { test(NoRewriteNestedFunctionTestInput.PREFIX + NoRewriteNestedFunctionTestInput.INNER + "};" + NoRewriteNestedFunctionTestInput.SUFFIX, NoRewriteNestedFunctionTestInput.EXPECTED_PREFIX + NoRewriteNestedFunctionTestInput.INNER + "};" + NoRewriteNestedFunctionTestInput.EXPECTED_SUFFIX); } public void testRewriteImplementedMethod() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype.foo = function(args) {return args};", "var o = new a;", "o.foo()"); String expected = newlineJoin( "function a(){}", "var JSCompiler_StaticMethods_foo = ", " function(JSCompiler_StaticMethods_foo$self, args) {return args};", "var o = new a;", "JSCompiler_StaticMethods_foo(o)"); test(source, expected); } public void testRewriteImplementedMethod2() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype['foo'] = function(args) {return args};", "var o = new a;", "o.foo()"); testSame(source); } public void testRewriteImplementedMethod3() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype.foo = function(args) {return args};", "var o = new a;", "o['foo']"); testSame(source); } public void testRewriteImplementedMethod4() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype['foo'] = function(args) {return args};", "var o = new a;", "o['foo']"); testSame(source); } public void testRewriteImplementedMethodInObj() throws Exception { String source = newlineJoin( "function a(){}", "a.prototype = {foo: function(args) {return args}};", "var o = new a;", "o.foo()"); test(source, "function a(){}" + "a.prototype={};" + "var JSCompiler_StaticMethods_foo=" + "function(JSCompiler_StaticMethods_foo$self,args){return args};" + "var o=new a;" + "JSCompiler_StaticMethods_foo(o)"); } public void testNoRewriteGet1() throws Exception { // Getters and setter require special handling. String source = newlineJoin( "function a(){}", "a.prototype = {get foo(){return f}};", "var o = new a;", "o.foo()"); testSame(source); } public void testNoRewriteGet2() throws Exception { // Getters and setter require special handling. String source = newlineJoin( "function a(){}", "a.prototype = {get foo(){return 1}};", "var o = new a;", "o.foo"); testSame(source); } public void testNoRewriteSet1() throws Exception { // Getters and setter require special handling. String source = newlineJoin( "function a(){}", "a.prototype = {set foo(a){}};", "var o = new a;", "o.foo()"); testSame(source); } public void testNoRewriteSet2() throws Exception { // Getters and setter require special handling. String source = newlineJoin( "function a(){}", "a.prototype = {set foo(a){}};", "var o = new a;", "o.foo = 1"); testSame(source); } public void testNoRewriteNotImplementedMethod() throws Exception { testSame(newlineJoin("function a(){}", "var o = new a;", "o.foo()")); } public void testWrapper() { testSame("(function() {})()"); } private static class ModuleTestInput { static final String DEFINITION = "a.prototype.foo = function() {}"; static final String USE = "x.foo()"; static final String REWRITTEN_DEFINITION = "var JSCompiler_StaticMethods_foo=" + "function(JSCompiler_StaticMethods_foo$self){}"; static final String REWRITTEN_USE = "JSCompiler_StaticMethods_foo(x)"; private ModuleTestInput() {} } public void testRewriteSameModule1() throws Exception { JSModule[] modules = createModuleStar( // m1 semicolonJoin(ModuleTestInput.DEFINITION, ModuleTestInput.USE), // m2 ""); test(modules, new String[] { // m1 semicolonJoin(ModuleTestInput.REWRITTEN_DEFINITION, ModuleTestInput.REWRITTEN_USE), // m2 "", }); } public void testRewriteSameModule2() throws Exception { JSModule[] modules = createModuleStar( // m1 "", // m2 semicolonJoin(ModuleTestInput.DEFINITION, ModuleTestInput.USE)); test(modules, new String[] { // m1 "", // m2 semicolonJoin(ModuleTestInput.REWRITTEN_DEFINITION, ModuleTestInput.REWRITTEN_USE) }); } public void testRewriteSameModule3() throws Exception { JSModule[] modules = createModuleStar( // m1 semicolonJoin(ModuleTestInput.USE, ModuleTestInput.DEFINITION), // m2 ""); test(modules, new String[] { // m1 semicolonJoin(ModuleTestInput.REWRITTEN_USE, ModuleTestInput.REWRITTEN_DEFINITION), // m2 "" }); } public void testRewriteDefinitionBeforeUse() throws Exception { JSModule[] modules = createModuleStar( // m1 ModuleTestInput.DEFINITION, // m2 ModuleTestInput.USE); test(modules, new String[] { // m1 ModuleTestInput.REWRITTEN_DEFINITION, // m2 ModuleTestInput.REWRITTEN_USE }); } public void testNoRewriteUseBeforeDefinition() throws Exception { JSModule[] modules = createModuleStar( // m1 ModuleTestInput.USE, // m2 ModuleTestInput.DEFINITION); testSame(modules); } /** * Verifies that the compiler pass's output matches the expected * output, and that nodes are annotated with the expected jstype * information. */ private void checkTypes(String source, String expected, List expectedTypes) { typeInformation.clear(); test(source, expected); assertEquals(expectedTypes, typeInformation); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new TypeInformationGatherer( compiler, new DevirtualizePrototypeMethods(compiler), typeInformation); } /** * Wrapper that gathers function, call, and self variable type strings after * the pass under test runs. For use to test passes that modify JSType * annotations. */ private static class TypeInformationGatherer implements CompilerPass { private final Compiler compiler; private final CompilerPass passUnderTest; private final List typeInformation; TypeInformationGatherer(Compiler compiler, CompilerPass passUnderTest, List typeInformation) { this.compiler = compiler; this.passUnderTest = passUnderTest; this.typeInformation = typeInformation; } @Override public void process(Node externs, Node root) { passUnderTest.process(externs, root); NodeTraversal.traverse(compiler, externs, new GatherCallback()); NodeTraversal.traverse(compiler, root, new GatherCallback()); } public String getNameString(Node n) { int type = n.getType(); if (type == Token.NAME) { return n.getString(); } else if (type == Token.GETPROP) { String left = getNameString(n.getFirstChild()); if (left == null) { return null; } return left + "." + n.getLastChild().getString(); } else if (type == Token.GETELEM) { String left = getNameString(n.getFirstChild()); if (left == null) { return null; } return left + "[" + n.getLastChild().getString() + "]"; } else if (type == Token.THIS) { return "this"; } else if (type == Token.FUNCTION){ return "{ANON FUNCTION}"; } else { // I wonder if we should just die on this. return null; } } private class GatherCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Node nameNode = null; if (node.isFunction()) { if (parent.isName()) { nameNode = parent; } else if (parent.isAssign()) { nameNode = parent.getFirstChild(); } else { nameNode = node.getFirstChild(); } } else if (node.isCall() || node.isNew()) { nameNode = node.getFirstChild(); } if (nameNode != null) { JSType type = node.getJSType(); typeInformation.add( Joiner.on("").join( Token.name(node.getType()), " ", getNameString(nameNode), " = ", (type != null) ? type.toString() : "null")); } if (node.isGetProp()) { Node child = node.getFirstChild(); if (child.isName() && child.getString().endsWith("$self")) { JSType type = child.getJSType(); typeInformation.add( Joiner.on("").join( Token.name(child.getType()), " ", child.getString(), " = ", (type != null) ? type.toString() : "null")); } } } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckPathsBetweenNodesTest.java0000644000175000017500000002635712115204405031064 0ustar apoapo/* * 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.base.Predicate; import com.google.common.base.Predicates; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import junit.framework.TestCase; /** * Tests for {@link CheckPathsBetweenNodes}. * */ public class CheckPathsBetweenNodesTest extends TestCase { /** * Predicate satisfied by strings with a given prefix. */ private static class PrefixPredicate implements Predicate { String prefix; PrefixPredicate(String prefix) { this.prefix = prefix; } @Override public boolean apply(String input) { return input.startsWith(prefix); } } private static final Predicate FALSE = Predicates.alwaysFalse(); private static final Predicate> ALL_EDGE = Predicates.alwaysTrue(); private static final Predicate> NO_EDGE = Predicates.alwaysFalse(); /** Tests straight-line graphs. */ public void testSimple() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.connect("a", "-", "b"); g.connect("b", "-", "c"); g.connect("c", "-", "d"); g.connect("a", "x", "d"); // Simple case: the sole path from a to d has a matching node. assertGood(createTest(g, "a", "d", Predicates.equalTo("b"), edgeIs("-"))); //Test two edge cases where satisfying node is the first and last node on // the path. assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), edgeIs("-"))); assertGood(createTest(g, "a", "d", Predicates.equalTo("d"), edgeIs("-"))); // Traverse no edges, so no paths. assertGood(createTest(g, "a", "d", FALSE, NO_EDGE)); // No path with matching edges contains b. assertBad(createTest(g, "a", "d", Predicates.equalTo("b"), edgeIs("x"))); } /** * Tests a graph where some paths between the nodes are valid and others * are invalid. */ public void testSomeValidPaths() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.createDirectedGraphNode("e"); g.connect("a", "1", "b"); g.connect("b", "2", "c"); g.connect("b", "3", "e"); g.connect("e", "4", "d"); g.connect("c", "5", "d"); assertBad(createTest(g, "a", "d", Predicates.equalTo("c"), ALL_EDGE)); assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); } /** Tests a graph with many valid paths. */ public void testManyValidPaths() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c1"); g.createDirectedGraphNode("c2"); g.createDirectedGraphNode("c3"); g.createDirectedGraphNode("d"); g.connect("a", "-", "b"); g.connect("b", "-", "c1"); g.connect("b", "-", "c2"); g.connect("c2", "-", "d"); g.connect("c1", "-", "d"); g.connect("a", "-", "c3"); g.connect("c3", "-", "d"); assertGood(createTest(g, "a", "d", new PrefixPredicate("c"), ALL_EDGE)); } /** Tests a graph with some cycles. */ public void testCycles1() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.createDirectedGraphNode("e"); g.createDirectedGraphNode("f"); g.connect("a", "-", "b"); g.connect("b", "-", "c"); g.connect("c", "-", "d"); g.connect("d", "-", "e"); g.connect("e", "-", "f"); g.connect("f", "-", "b"); assertGood(createTest(g, "a", "e", Predicates.equalTo("b"), ALL_EDGE)); assertGood(createTest(g, "a", "e", Predicates.equalTo("c"), ALL_EDGE)); assertGood(createTest(g, "a", "e", Predicates.equalTo("d"), ALL_EDGE)); assertGood(createTest(g, "a", "e", Predicates.equalTo("e"), ALL_EDGE)); assertBad(createTest(g, "a", "e", Predicates.equalTo("f"), ALL_EDGE)); } /** * Tests another graph with cycles. The topology of this graph was inspired * by a control flow graph that was being incorrectly analyzed by an early * version of CheckPathsBetweenNodes. */ public void testCycles2() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.connect("a", "-", "b"); g.connect("b", "-", "c"); g.connect("c", "-", "b"); g.connect("b", "-", "d"); assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE)); assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); } /** * Tests another graph with cycles. The topology of this graph was inspired * by a control flow graph that was being incorrectly analyzed by an early * version of CheckPathsBetweenNodes. */ public void testCycles3() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.connect("a", "-", "b"); g.connect("b", "-", "c"); g.connect("c", "-", "b"); g.connect("b", "-", "d"); g.connect("c", "-", "d"); assertGood(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE)); assertBad(createTest(g, "a", "d", Predicates.equalTo("z"), ALL_EDGE)); } /** * Much of the tests are done by testing all paths. We quickly verified * that some paths are indeed correct for the some path case. */ public void testSomePath1() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.createDirectedGraphNode("d"); g.connect("a", "-", "b"); g.connect("a", "-", "c"); g.connect("b", "-", "d"); g.connect("c", "-", "d"); assertTrue(createTest(g, "a", "d", Predicates.equalTo("b"), ALL_EDGE) .somePathsSatisfyPredicate()); assertTrue(createTest(g, "a", "d", Predicates.equalTo("c"), ALL_EDGE) .somePathsSatisfyPredicate()); assertTrue(createTest(g, "a", "d", Predicates.equalTo("a"), ALL_EDGE) .somePathsSatisfyPredicate()); assertTrue(createTest(g, "a", "d", Predicates.equalTo("d"), ALL_EDGE) .somePathsSatisfyPredicate()); assertFalse(createTest(g, "a", "d", Predicates.equalTo("NONE"), ALL_EDGE) .somePathsSatisfyPredicate()); } public void testSomePath2() { // No Paths between nodes, by definition, always false. DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); assertFalse(createTest(g, "a", "b", Predicates.equalTo("b"), ALL_EDGE) .somePathsSatisfyPredicate()); assertFalse(createTest(g, "a", "b", Predicates.equalTo("d"), ALL_EDGE) .somePathsSatisfyPredicate()); assertTrue(createTest(g, "a", "b", Predicates.equalTo("a"), ALL_EDGE) .somePathsSatisfyPredicate()); } public void testSomePathRevisiting() { DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("1"); g.createDirectedGraphNode("2a"); g.createDirectedGraphNode("2b"); g.createDirectedGraphNode("3"); g.createDirectedGraphNode("4a"); g.createDirectedGraphNode("4b"); g.createDirectedGraphNode("5"); g.connect("1", "-", "2a"); g.connect("1", "-", "2b"); g.connect("2a", "-", "3"); g.connect("2b", "-", "3"); g.connect("3", "-", "4a"); g.connect("3", "-", "4b"); g.connect("4a", "-", "5"); g.connect("4b", "-", "5"); CountingPredicate p = new CountingPredicate(Predicates.equalTo("4a")); assertTrue(createTest(g, "1", "5", p, ALL_EDGE) .somePathsSatisfyPredicate()); // Make sure we are not doing more traversals than we have to. assertEquals(4, p.count); } public void testNonInclusive() { // No Paths between nodes, by definition, always false. DiGraph g = LinkedDirectedGraph.create(); g.createDirectedGraphNode("a"); g.createDirectedGraphNode("b"); g.createDirectedGraphNode("c"); g.connect("a", "-", "b"); g.connect("b", "-", "c"); assertFalse(createNonInclusiveTest(g, "a", "b", Predicates.equalTo("a"), ALL_EDGE).somePathsSatisfyPredicate()); assertFalse(createNonInclusiveTest(g, "a", "b", Predicates.equalTo("b"), ALL_EDGE).somePathsSatisfyPredicate()); assertTrue(createNonInclusiveTest(g, "a", "c", Predicates.equalTo("b"), ALL_EDGE).somePathsSatisfyPredicate()); } private static void assertGood(CheckPathsBetweenNodes test) { assertTrue(test.allPathsSatisfyPredicate()); } private static void assertBad(CheckPathsBetweenNodes test) { assertFalse(test.allPathsSatisfyPredicate()); } private static CheckPathsBetweenNodes createTest( DiGraph graph, String entry, String exit, Predicate nodePredicate, Predicate> edgePredicate) { return new CheckPathsBetweenNodes(graph, graph.getDirectedGraphNode(entry), graph.getDirectedGraphNode(exit), nodePredicate, edgePredicate); } private static CheckPathsBetweenNodes createNonInclusiveTest( DiGraph graph, String entry, String exit, Predicate nodePredicate, Predicate> edgePredicate) { return new CheckPathsBetweenNodes(graph, graph.getDirectedGraphNode(entry), graph.getDirectedGraphNode(exit), nodePredicate, edgePredicate, false); } private static Predicate> edgeIs(final Object val) { return new Predicate>() { @Override public boolean apply(DiGraphEdge input) { return input.getValue().equals(val); } }; } private static class CountingPredicate implements Predicate { private int count = 0; private final Predicate delegate; private CountingPredicate(Predicate delegate) { this.delegate = delegate; } @Override public boolean apply(T input) { count ++; return delegate.apply(input); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TypeCheckTest.java0000644000175000017500000142647612115204405026432 0ustar apoapo/* * Copyright 2006 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.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.testing.Asserts; import java.util.Arrays; import java.util.List; import java.util.Set; /** * Tests {@link TypeCheck}. * */ public class TypeCheckTest extends CompilerTypeTestCase { private CheckLevel reportMissingOverrides = CheckLevel.WARNING; @Override public void setUp() throws Exception { super.setUp(); reportMissingOverrides = CheckLevel.WARNING; } public void testInitialTypingScope() { Scope s = new TypedScopeCreator(compiler, CodingConventions.getDefault()).createInitialScope( new Node(Token.BLOCK)); assertTypeEquals(ARRAY_FUNCTION_TYPE, s.getVar("Array").getType()); assertTypeEquals(BOOLEAN_OBJECT_FUNCTION_TYPE, s.getVar("Boolean").getType()); assertTypeEquals(DATE_FUNCTION_TYPE, s.getVar("Date").getType()); assertTypeEquals(ERROR_FUNCTION_TYPE, s.getVar("Error").getType()); assertTypeEquals(EVAL_ERROR_FUNCTION_TYPE, s.getVar("EvalError").getType()); assertTypeEquals(NUMBER_OBJECT_FUNCTION_TYPE, s.getVar("Number").getType()); assertTypeEquals(OBJECT_FUNCTION_TYPE, s.getVar("Object").getType()); assertTypeEquals(RANGE_ERROR_FUNCTION_TYPE, s.getVar("RangeError").getType()); assertTypeEquals(REFERENCE_ERROR_FUNCTION_TYPE, s.getVar("ReferenceError").getType()); assertTypeEquals(REGEXP_FUNCTION_TYPE, s.getVar("RegExp").getType()); assertTypeEquals(STRING_OBJECT_FUNCTION_TYPE, s.getVar("String").getType()); assertTypeEquals(SYNTAX_ERROR_FUNCTION_TYPE, s.getVar("SyntaxError").getType()); assertTypeEquals(TYPE_ERROR_FUNCTION_TYPE, s.getVar("TypeError").getType()); assertTypeEquals(URI_ERROR_FUNCTION_TYPE, s.getVar("URIError").getType()); } public void testPrivateType() throws Exception { testTypes( "/** @private {number} */ var x = false;", "initializing variable\n" + "found : boolean\n" + "required: number"); } public void testTypeCheck1() throws Exception { testTypes("/**@return {void}*/function foo(){ if (foo()) return; }"); } public void testTypeCheck2() throws Exception { testTypes("/**@return {void}*/function foo(){ var x=foo(); x--; }", "increment/decrement\n" + "found : undefined\n" + "required: number"); } public void testTypeCheck4() throws Exception { testTypes("/**@return {void}*/function foo(){ !foo(); }"); } public void testTypeCheck5() throws Exception { testTypes("/**@return {void}*/function foo(){ var a = +foo(); }", "sign operator\n" + "found : undefined\n" + "required: number"); } public void testTypeCheck6() throws Exception { testTypes( "/**@return {void}*/function foo(){" + "/** @type {undefined|number} */var a;if (a == foo())return;}"); } public void testTypeCheck8() throws Exception { testTypes("/**@return {void}*/function foo(){do {} while (foo());}"); } public void testTypeCheck9() throws Exception { testTypes("/**@return {void}*/function foo(){while (foo());}"); } public void testTypeCheck10() throws Exception { testTypes("/**@return {void}*/function foo(){for (;foo(););}"); } public void testTypeCheck11() throws Exception { testTypes("/**@type !Number */var a;" + "/**@type !String */var b;" + "a = b;", "assignment\n" + "found : String\n" + "required: Number"); } public void testTypeCheck12() throws Exception { testTypes("/**@return {!Object}*/function foo(){var a = 3^foo();}", "bad right operand to bitwise operator\n" + "found : Object\n" + "required: (boolean|null|number|string|undefined)"); } public void testTypeCheck13() throws Exception { testTypes("/**@type {!Number|!String}*/var i; i=/xx/;", "assignment\n" + "found : RegExp\n" + "required: (Number|String)"); } public void testTypeCheck14() throws Exception { testTypes("/**@param opt_a*/function foo(opt_a){}"); } public void testTypeCheck15() throws Exception { testTypes("/**@type {Number|null} */var x;x=null;x=10;", "assignment\n" + "found : number\n" + "required: (Number|null)"); } public void testTypeCheck16() throws Exception { testTypes("/**@type {Number|null} */var x='';", "initializing variable\n" + "found : string\n" + "required: (Number|null)"); } public void testTypeCheck17() throws Exception { testTypes("/**@return {Number}\n@param {Number} opt_foo */\n" + "function a(opt_foo){\nreturn /**@type {Number}*/(opt_foo);\n}"); } public void testTypeCheck18() throws Exception { testTypes("/**@return {RegExp}\n*/\n function a(){return new RegExp();}"); } public void testTypeCheck19() throws Exception { testTypes("/**@return {Array}\n*/\n function a(){return new Array();}"); } public void testTypeCheck20() throws Exception { testTypes("/**@return {Date}\n*/\n function a(){return new Date();}"); } public void testTypeCheckBasicDowncast() throws Exception { testTypes("/** @constructor */function foo() {}\n" + "/** @type {Object} */ var bar = new foo();\n"); } public void testTypeCheckNoDowncastToNumber() throws Exception { testTypes("/** @constructor */function foo() {}\n" + "/** @type {!Number} */ var bar = new foo();\n", "initializing variable\n" + "found : foo\n" + "required: Number"); } public void testTypeCheck21() throws Exception { testTypes("/** @type Array. */var foo;"); } public void testTypeCheck22() throws Exception { testTypes("/** @param {Element|Object} p */\nfunction foo(p){}\n" + "/** @constructor */function Element(){}\n" + "/** @type {Element|Object} */var v;\n" + "foo(v);\n"); } public void testTypeCheck23() throws Exception { testTypes("/** @type {(Object,Null)} */var foo; foo = null;"); } public void testTypeCheck24() throws Exception { testTypes("/** @constructor */function MyType(){}\n" + "/** @type {(MyType,Null)} */var foo; foo = null;"); } public void testTypeCheckDefaultExterns() throws Exception { testTypes("/** @param {string} x */ function f(x) {}" + "f([].length);" , "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testTypeCheckCustomExterns() throws Exception { testTypes( DEFAULT_EXTERNS + "/** @type {boolean} */ Array.prototype.oogabooga;", "/** @param {string} x */ function f(x) {}" + "f([].oogabooga);" , "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: string", false); } public void testTypeCheckCustomExterns2() throws Exception { testTypes( DEFAULT_EXTERNS + "/** @enum {string} */ var Enum = {FOO: 1, BAR: 1};", "/** @param {Enum} x */ function f(x) {} f(Enum.FOO); f(true);", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: Enum.", false); } public void testTemplatizedArray1() throws Exception { testTypes("/** @param {!Array.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedArray2() throws Exception { testTypes("/** @param {!Array.>} a\n" + "* @return {number}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : Array.\n" + "required: number"); } public void testTemplatizedArray3() throws Exception { testTypes("/** @param {!Array.} a\n" + "* @return {number}\n" + "*/ var f = function(a) { a[1] = 0; return a[0]; };"); } public void testTemplatizedArray4() throws Exception { testTypes("/** @param {!Array.} a\n" + "*/ var f = function(a) { a[0] = 'a'; };", "assignment\n" + "found : string\n" + "required: number"); } public void testTemplatizedArray5() throws Exception { testTypes("/** @param {!Array.<*>} a\n" + "*/ var f = function(a) { a[0] = 'a'; };"); } public void testTemplatizedArray6() throws Exception { testTypes("/** @param {!Array.<*>} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : *\n" + "required: string"); } public void testTemplatizedArray7() throws Exception { testTypes("/** @param {?Array.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject1() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a[0]; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject2() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testTemplatizedObject3() throws Exception { testTypes("/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "restricted index type\n" + "found : string\n" + "required: number"); } public void testTemplatizedObject4() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {!Object.} a\n" + "* @return {string}\n" + "*/ var f = function(a) { return a['x']; };", "restricted index type\n" + "found : string\n" + "required: E."); } public void testTemplatizedObject5() throws Exception { testTypes("/** @constructor */ function F() {" + " /** @type {Object.} */ this.numbers = {};" + "}" + "(new F()).numbers['ten'] = '10';", "restricted index type\n" + "found : string\n" + "required: number"); } public void testUnionOfFunctionAndType() throws Exception { testTypes("/** @type {null|(function(Number):void)} */ var a;" + "/** @type {(function(Number):void)|null} */ var b = null; a = b;"); } public void testOptionalParameterComparedToUndefined() throws Exception { testTypes("/**@param opt_a {Number}*/function foo(opt_a)" + "{if (opt_a==undefined) var b = 3;}"); } public void testOptionalAllType() throws Exception { testTypes("/** @param {*} opt_x */function f(opt_x) { return opt_x }\n" + "/** @type {*} */var y;\n" + "f(y);"); } public void testOptionalUnknownNamedType() throws Exception { testTypes("/** @param {!T} opt_x\n@return {undefined} */\n" + "function f(opt_x) { return opt_x; }\n" + "/** @constructor */var T = function() {};", "inconsistent return type\n" + "found : (T|undefined)\n" + "required: undefined"); } public void testOptionalArgFunctionParam() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a()};"); } public void testOptionalArgFunctionParam2() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a(3)};"); } public void testOptionalArgFunctionParam3() throws Exception { testTypes("/** @param {function(number=)} a */" + "function f(a) {a(undefined)};"); } public void testOptionalArgFunctionParam4() throws Exception { String expectedWarning = "Function a: called with 2 argument(s). " + "Function requires at least 0 argument(s) and no more than 1 " + "argument(s)."; testTypes("/** @param {function(number=)} a */function f(a) {a(3,4)};", expectedWarning, false); } public void testOptionalArgFunctionParamError() throws Exception { String expectedWarning = "Bad type annotation. variable length argument must be last"; testTypes("/** @param {function(...[number], number=)} a */" + "function f(a) {};", expectedWarning, false); } public void testOptionalNullableArgFunctionParam() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a()};"); } public void testOptionalNullableArgFunctionParam2() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a(null)};"); } public void testOptionalNullableArgFunctionParam3() throws Exception { testTypes("/** @param {function(?number=)} a */" + "function f(a) {a(3)};"); } public void testOptionalArgFunctionReturn() throws Exception { testTypes("/** @return {function(number=)} */" + "function f() { return function(opt_x) { }; };" + "f()()"); } public void testOptionalArgFunctionReturn2() throws Exception { testTypes("/** @return {function(Object=)} */" + "function f() { return function(opt_x) { }; };" + "f()({})"); } public void testBooleanType() throws Exception { testTypes("/**@type {boolean} */var x = 1 < 2;"); } public void testBooleanReduction1() throws Exception { testTypes("/**@type {string} */var x; x = null || \"a\";"); } public void testBooleanReduction2() throws Exception { // It's important for the type system to recognize that in no case // can the boolean expression evaluate to a boolean value. testTypes("/** @param {string} s\n @return {string} */" + "(function(s) { return ((s == 'a') && s) || 'b'; })"); } public void testBooleanReduction3() throws Exception { testTypes("/** @param {string} s\n @return {string?} */" + "(function(s) { return s && null && 3; })"); } public void testBooleanReduction4() throws Exception { testTypes("/** @param {Object} x\n @return {Object} */" + "(function(x) { return null || x || null ; })"); } public void testBooleanReduction5() throws Exception { testTypes("/**\n" + "* @param {Array|string} x\n" + "* @return {string?}\n" + "*/\n" + "var f = function(x) {\n" + "if (!x || typeof x == 'string') {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testBooleanReduction6() throws Exception { testTypes("/**\n" + "* @param {Array|string|null} x\n" + "* @return {string?}\n" + "*/\n" + "var f = function(x) {\n" + "if (!(x && typeof x != 'string')) {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testBooleanReduction7() throws Exception { testTypes("/** @constructor */var T = function() {};\n" + "/**\n" + "* @param {Array|T} x\n" + "* @return {null}\n" + "*/\n" + "var f = function(x) {\n" + "if (!x) {\n" + "return x;\n" + "}\n" + "return null;\n" + "};"); } public void testNullAnd() throws Exception { testTypes("/** @type null */var x;\n" + "/** @type number */var r = x && x;", "initializing variable\n" + "found : null\n" + "required: number"); } public void testNullOr() throws Exception { testTypes("/** @type null */var x;\n" + "/** @type number */var r = x || x;", "initializing variable\n" + "found : null\n" + "required: number"); } public void testBooleanPreservation1() throws Exception { testTypes("/**@type {string} */var x = \"a\";" + "x = ((x == \"a\") && x) || x == \"b\";", "assignment\n" + "found : (boolean|string)\n" + "required: string"); } public void testBooleanPreservation2() throws Exception { testTypes("/**@type {string} */var x = \"a\"; x = (x == \"a\") || x;", "assignment\n" + "found : (boolean|string)\n" + "required: string"); } public void testBooleanPreservation3() throws Exception { testTypes("/** @param {Function?} x\n @return {boolean?} */" + "function f(x) { return x && x == \"a\"; }", "condition always evaluates to false\n" + "left : Function\n" + "right: string"); } public void testBooleanPreservation4() throws Exception { testTypes("/** @param {Function?|boolean} x\n @return {boolean} */" + "function f(x) { return x && x == \"a\"; }", "inconsistent return type\n" + "found : (boolean|null)\n" + "required: boolean"); } public void testTypeOfReduction1() throws Exception { testTypes("/** @param {string|number} x\n @return {string} */ " + "function f(x) { return typeof x == 'number' ? String(x) : x; }"); } public void testTypeOfReduction2() throws Exception { testTypes("/** @param {string|number} x\n @return {string} */ " + "function f(x) { return typeof x != 'string' ? String(x) : x; }"); } public void testTypeOfReduction3() throws Exception { testTypes("/** @param {number|null} x\n @return {number} */ " + "function f(x) { return typeof x == 'object' ? 1 : x; }"); } public void testTypeOfReduction4() throws Exception { testTypes("/** @param {Object|undefined} x\n @return {Object} */ " + "function f(x) { return typeof x == 'undefined' ? {} : x; }"); } public void testTypeOfReduction5() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {!E|number} x\n @return {string} */ " + "function f(x) { return typeof x != 'number' ? x : 'a'; }"); } public void testTypeOfReduction6() throws Exception { testTypes("/** @param {number|string} x\n@return {string} */\n" + "function f(x) {\n" + "return typeof x == 'string' && x.length == 3 ? x : 'a';\n" + "}"); } public void testTypeOfReduction7() throws Exception { testTypes("/** @return {string} */var f = function(x) { " + "return typeof x == 'number' ? x : 'a'; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testTypeOfReduction8() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {number|string} x\n@return {string} */\n" + "function f(x) {\n" + "return goog.isString(x) && x.length == 3 ? x : 'a';\n" + "}", null); } public void testTypeOfReduction9() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {!Array|string} x\n@return {string} */\n" + "function f(x) {\n" + "return goog.isArray(x) ? 'a' : x;\n" + "}", null); } public void testTypeOfReduction10() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {Array|string} x\n@return {Array} */\n" + "function f(x) {\n" + "return goog.isArray(x) ? x : [];\n" + "}", null); } public void testTypeOfReduction11() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {Array|string} x\n@return {Array} */\n" + "function f(x) {\n" + "return goog.isObject(x) ? x : [];\n" + "}", null); } public void testTypeOfReduction12() throws Exception { testTypes("/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {E|Array} x\n @return {Array} */ " + "function f(x) { return typeof x == 'object' ? x : []; }"); } public void testTypeOfReduction13() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @enum {string} */ var E = {A: 'a', B: 'b'};\n" + "/** @param {E|Array} x\n@return {Array} */ " + "function f(x) { return goog.isObject(x) ? x : []; }", null); } public void testTypeOfReduction14() throws Exception { // Don't do type inference on GETELEMs. testClosureTypes( CLOSURE_DEFS + "function f(x) { " + " return goog.isString(arguments[0]) ? arguments[0] : 0;" + "}", null); } public void testTypeOfReduction15() throws Exception { // Don't do type inference on GETELEMs. testClosureTypes( CLOSURE_DEFS + "function f(x) { " + " return typeof arguments[0] == 'string' ? arguments[0] : 0;" + "}", null); } public void testTypeOfReduction16() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @interface */ function I() {}\n" + "/**\n" + " * @param {*} x\n" + " * @return {I}\n" + " */\n" + "function f(x) { " + " if(goog.isObject(x)) {" + " return /** @type {I} */(x);" + " }" + " return null;" + "}", null); } public void testQualifiedNameReduction1() throws Exception { testTypes("var x = {}; /** @type {string?} */ x.a = 'a';\n" + "/** @return {string} */ var f = function() {\n" + "return x.a ? x.a : 'a'; }"); } public void testQualifiedNameReduction2() throws Exception { testTypes("/** @param {string?} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return this.a ? this.a : 'a'; }"); } public void testQualifiedNameReduction3() throws Exception { testTypes("/** @param {string|Array} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return typeof this.a == 'string' ? this.a : 'a'; }"); } public void testQualifiedNameReduction4() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {string|Array} a\n@constructor */ var T = " + "function(a) {this.a = a};\n" + "/** @return {string} */ T.prototype.f = function() {\n" + "return goog.isString(this.a) ? this.a : 'a'; }", null); } public void testQualifiedNameReduction5a() throws Exception { testTypes("var x = {/** @type {string} */ a:'b' };\n" + "/** @return {string} */ var f = function() {\n" + "return x.a; }"); } public void testQualifiedNameReduction5b() throws Exception { testTypes( "var x = {/** @type {number} */ a:12 };\n" + "/** @return {string} */\n" + "var f = function() {\n" + " return x.a;\n" + "}", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testQualifiedNameReduction5c() throws Exception { testTypes( "/** @return {string} */ var f = function() {\n" + "var x = {/** @type {number} */ a:0 };\n" + "return (x.a) ? (x.a) : 'a'; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testQualifiedNameReduction6() throws Exception { testTypes( "/** @return {string} */ var f = function() {\n" + "var x = {/** @return {string?} */ get a() {return 'a'}};\n" + "return x.a ? x.a : 'a'; }"); } public void testQualifiedNameReduction7() throws Exception { testTypes( "/** @return {string} */ var f = function() {\n" + "var x = {/** @return {number} */ get a() {return 12}};\n" + "return x.a; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testQualifiedNameReduction7a() throws Exception { // It would be nice to find a way to make this an error. testTypes( "/** @return {string} */ var f = function() {\n" + "var x = {get a() {return 12}};\n" + "return x.a; }"); } public void testQualifiedNameReduction8() throws Exception { testTypes( "/** @return {string} */ var f = function() {\n" + "var x = {get a() {return 'a'}};\n" + "return x.a ? x.a : 'a'; }"); } public void testQualifiedNameReduction9() throws Exception { testTypes( "/** @return {string} */ var f = function() {\n" + "var x = { /** @param {string} b */ set a(b) {}};\n" + "return x.a ? x.a : 'a'; }"); } public void testQualifiedNameReduction10() throws Exception { // TODO(johnlenz): separate setter property types from getter property // types. testTypes( "/** @return {string} */ var f = function() {\n" + "var x = { /** @param {number} b */ set a(b) {}};\n" + "return x.a ? x.a : 'a'; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testObjLitDef1a() throws Exception { testTypes( "var x = {/** @type {number} */ a:12 };\n" + "x.a = 'a';", "assignment to property a of x\n" + "found : string\n" + "required: number"); } public void testObjLitDef1b() throws Exception { testTypes( "function f(){" + "var x = {/** @type {number} */ a:12 };\n" + "x.a = 'a';" + "};\n" + "f();", "assignment to property a of x\n" + "found : string\n" + "required: number"); } public void testObjLitDef2a() throws Exception { testTypes( "var x = {/** @param {number} b */ set a(b){} };\n" + "x.a = 'a';", "assignment to property a of x\n" + "found : string\n" + "required: number"); } public void testObjLitDef2b() throws Exception { testTypes( "function f(){" + "var x = {/** @param {number} b */ set a(b){} };\n" + "x.a = 'a';" + "};\n" + "f();", "assignment to property a of x\n" + "found : string\n" + "required: number"); } public void testObjLitDef3a() throws Exception { testTypes( "/** @type {string} */ var y;\n" + "var x = {/** @return {number} */ get a(){} };\n" + "y = x.a;", "assignment\n" + "found : number\n" + "required: string"); } public void testObjLitDef3b() throws Exception { testTypes( "/** @type {string} */ var y;\n" + "function f(){" + "var x = {/** @return {number} */ get a(){} };\n" + "y = x.a;" + "};\n" + "f();", "assignment\n" + "found : number\n" + "required: string"); } public void testObjLitDef4() throws Exception { testTypes( "var x = {" + "/** @return {number} */ a:12 };\n", "assignment to property a of {a: function (): number}\n" + "found : number\n" + "required: function (): number"); } public void testObjLitDef5() throws Exception { testTypes( "var x = {};\n" + "/** @return {number} */ x.a = 12;\n", "assignment to property a of x\n" + "found : number\n" + "required: function (): number"); } public void testObjLitDef6() throws Exception { testTypes("var lit = /** @struct */ { 'x': 1 };", "Illegal key, the object literal is a struct"); } public void testObjLitDef7() throws Exception { testTypes("var lit = /** @dict */ { x: 1 };", "Illegal key, the object literal is a dict"); } public void testInstanceOfReduction1() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {T|string} x\n@return {T} */\n" + "var f = function(x) {\n" + "if (x instanceof T) { return x; } else { return new T(); }\n" + "};"); } public void testInstanceOfReduction2() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {!T|string} x\n@return {string} */\n" + "var f = function(x) {\n" + "if (x instanceof T) { return ''; } else { return x; }\n" + "};"); } public void testUndeclaredGlobalProperty1() throws Exception { testTypes("/** @const */ var x = {}; x.y = null;" + "function f(a) { x.y = a; }" + "/** @param {string} a */ function g(a) { }" + "function h() { g(x.y); }"); } public void testUndeclaredGlobalProperty2() throws Exception { testTypes("/** @const */ var x = {}; x.y = null;" + "function f() { x.y = 3; }" + "/** @param {string} a */ function g(a) { }" + "function h() { g(x.y); }", "actual parameter 1 of g does not match formal parameter\n" + "found : (null|number)\n" + "required: string"); } public void testLocallyInferredGlobalProperty1() throws Exception { // We used to have a bug where x.y.z leaked from f into h. testTypes( "/** @constructor */ function F() {}" + "/** @type {number} */ F.prototype.z;" + "/** @const */ var x = {}; /** @type {F} */ x.y;" + "function f() { x.y.z = 'abc'; }" + "/** @param {number} x */ function g(x) {}" + "function h() { g(x.y.z); }", "assignment to property z of F\n" + "found : string\n" + "required: number"); } public void testPropertyInferredPropagation() throws Exception { testTypes("/** @return {Object} */function f() { return {}; }\n" + "function g() { var x = f(); if (x.p) x.a = 'a'; else x.a = 'b'; }\n" + "function h() { var x = f(); x.a = false; }"); } public void testPropertyInference1() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference2() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "F.prototype.baz = function() { this.x_ = null; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference3() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "F.prototype.baz = function() { this.x_ = 3; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : (boolean|number)\n" + "required: string"); } public void testPropertyInference4() throws Exception { testTypes( "/** @constructor */ function F() { }" + "F.prototype.x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testPropertyInference5() throws Exception { testTypes( "/** @constructor */ function F() { }" + "F.prototype.baz = function() { this.x_ = 3; };" + "/** @return {string} */" + "F.prototype.bar = function() { if (this.x_) return this.x_; };"); } public void testPropertyInference6() throws Exception { testTypes( "/** @constructor */ function F() { }" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };"); } public void testPropertyInference7() throws Exception { testTypes( "/** @constructor */ function F() { this.x_ = true; }" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testPropertyInference8() throws Exception { testTypes( "/** @constructor */ function F() { " + " /** @type {string} */ this.x_ = 'x';" + "}" + "(new F).x_ = 3;" + "/** @return {string} */" + "F.prototype.bar = function() { return this.x_; };", "assignment to property x_ of F\n" + "found : number\n" + "required: string"); } public void testPropertyInference9() throws Exception { testTypes( "/** @constructor */ function A() {}" + "/** @return {function(): ?} */ function f() { " + " return function() {};" + "}" + "var g = f();" + "/** @type {number} */ g.prototype.bar_ = null;", "assignment\n" + "found : null\n" + "required: number"); } public void testPropertyInference10() throws Exception { // NOTE(nicksantos): There used to be a bug where a property // on the prototype of one structural function would leak onto // the prototype of other variables with the same structural // function type. testTypes( "/** @constructor */ function A() {}" + "/** @return {function(): ?} */ function f() { " + " return function() {};" + "}" + "var g = f();" + "/** @type {number} */ g.prototype.bar_ = 1;" + "var h = f();" + "/** @type {string} */ h.prototype.bar_ = 1;", "assignment\n" + "found : number\n" + "required: string"); } public void testNoPersistentTypeInferenceForObjectProperties() throws Exception { testTypes("/** @param {Object} o\n@param {string} x */\n" + "function s1(o,x) { o.x = x; }\n" + "/** @param {Object} o\n@return {string} */\n" + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + "/** @param {Object} o\n@param {number} x */\n" + "function s2(o,x) { o.x = x; }\n" + "/** @param {Object} o\n@return {number} */\n" + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); } public void testNoPersistentTypeInferenceForFunctionProperties() throws Exception { testTypes("/** @param {Function} o\n@param {string} x */\n" + "function s1(o,x) { o.x = x; }\n" + "/** @param {Function} o\n@return {string} */\n" + "function g1(o) { return typeof o.x == 'undefined' ? '' : o.x; }\n" + "/** @param {Function} o\n@param {number} x */\n" + "function s2(o,x) { o.x = x; }\n" + "/** @param {Function} o\n@return {number} */\n" + "function g2(o) { return typeof o.x == 'undefined' ? 0 : o.x; }"); } public void testObjectPropertyTypeInferredInLocalScope1() throws Exception { testTypes("/** @param {!Object} o\n@return {string} */\n" + "function f(o) { o.x = 1; return o.x; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testObjectPropertyTypeInferredInLocalScope2() throws Exception { testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + "function f(o, x) { o.x = 'a';\nif (x) {o.x = x;}\nreturn o.x; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testObjectPropertyTypeInferredInLocalScope3() throws Exception { testTypes("/**@param {!Object} o\n@param {number?} x\n@return {string}*/" + "function f(o, x) { if (x) {o.x = x;} else {o.x = 'a';}\nreturn o.x; }", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty1() throws Exception { testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + "/** @type {number} */ T.prototype.x = 0;", "assignment to property x of T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty2() throws Exception { testTypes("/** @constructor */var T = function() { this.x = ''; };\n" + "/** @type {number} */ T.prototype.x;", "assignment to property x of T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty3() throws Exception { testTypes("/** @type {Object} */ var n = {};\n" + "/** @constructor */ n.T = function() { this.x = ''; };\n" + "/** @type {number} */ n.T.prototype.x = 0;", "assignment to property x of n.T\n" + "found : string\n" + "required: number"); } public void testMismatchingOverridingInferredPropertyBeforeDeclaredProperty4() throws Exception { testTypes("var n = {};\n" + "/** @constructor */ n.T = function() { this.x = ''; };\n" + "/** @type {number} */ n.T.prototype.x = 0;", "assignment to property x of n.T\n" + "found : string\n" + "required: number"); } public void testPropertyUsedBeforeDefinition1() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @return {string} */" + "T.prototype.f = function() { return this.g(); };\n" + "/** @return {number} */ T.prototype.g = function() { return 1; };\n", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testPropertyUsedBeforeDefinition2() throws Exception { testTypes("var n = {};\n" + "/** @constructor */ n.T = function() {};\n" + "/** @return {string} */" + "n.T.prototype.f = function() { return this.g(); };\n" + "/** @return {number} */ n.T.prototype.g = function() { return 1; };\n", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testAdd1() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 'abc'+foo();}"); } public void testAdd2() throws Exception { testTypes("/**@return {void}*/function foo(){var a = foo()+4;}"); } public void testAdd3() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {string} */ var b = 'b';" + "/** @type {string} */ var c = a + b;"); } public void testAdd4() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {string} */ var b = 'b';" + "/** @type {string} */ var c = a + b;"); } public void testAdd5() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {number} */ var b = 5;" + "/** @type {string} */ var c = a + b;"); } public void testAdd6() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {number} */ var b = 5;" + "/** @type {number} */ var c = a + b;"); } public void testAdd7() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {string} */ var b = 'b';" + "/** @type {number} */ var c = a + b;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd8() throws Exception { testTypes("/** @type {string} */ var a = 'a';" + "/** @type {number} */ var b = 5;" + "/** @type {number} */ var c = a + b;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd9() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {number} */ var b = 5;" + "/** @type {string} */ var c = a + b;", "initializing variable\n" + "found : number\n" + "required: string"); } public void testAdd10() throws Exception { // d.e.f will have unknown type. testTypes( suppressMissingProperty("e", "f") + "/** @type {number} */ var a = 5;" + "/** @type {string} */ var c = a + d.e.f;"); } public void testAdd11() throws Exception { // d.e.f will have unknown type. testTypes( suppressMissingProperty("e", "f") + "/** @type {number} */ var a = 5;" + "/** @type {number} */ var c = a + d.e.f;"); } public void testAdd12() throws Exception { testTypes("/** @return {(number,string)} */ function a() { return 5; }" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a() + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd13() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @return {(number,string)} */ function b() { return 5; }" + "/** @type {boolean} */ var c = a + b();", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd14() throws Exception { testTypes("/** @type {(null,string)} */ var a = null;" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd15() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @return {(number,string)} */ function b() { return 5; }" + "/** @type {boolean} */ var c = a + b();", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd16() throws Exception { testTypes("/** @type {(undefined,string)} */ var a = undefined;" + "/** @type {number} */ var b = 5;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd17() throws Exception { testTypes("/** @type {number} */ var a = 5;" + "/** @type {(undefined,string)} */ var b = undefined;" + "/** @type {boolean} */ var c = a + b;", "initializing variable\n" + "found : (number|string)\n" + "required: boolean"); } public void testAdd18() throws Exception { testTypes("function f() {};" + "/** @type {string} */ var a = 'a';" + "/** @type {number} */ var c = a + f();", "initializing variable\n" + "found : string\n" + "required: number"); } public void testAdd19() throws Exception { testTypes("/** @param {number} opt_x\n@param {number} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testAdd20() throws Exception { testTypes("/** @param {!Number} opt_x\n@param {!Number} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testAdd21() throws Exception { testTypes("/** @param {Number|Boolean} opt_x\n" + "@param {number|boolean} opt_y\n" + "@return {number} */ function f(opt_x, opt_y) {" + "return opt_x + opt_y;}"); } public void testNumericComparison1() throws Exception { testTypes("/**@param {number} a*/ function f(a) {return a < 3;}"); } public void testNumericComparison2() throws Exception { testTypes("/**@param {!Object} a*/ function f(a) {return a < 3;}", "left side of numeric comparison\n" + "found : Object\n" + "required: number"); } public void testNumericComparison3() throws Exception { testTypes("/**@param {string} a*/ function f(a) {return a < 3;}"); } public void testNumericComparison4() throws Exception { testTypes("/**@param {(number,undefined)} a*/ " + "function f(a) {return a < 3;}"); } public void testNumericComparison5() throws Exception { testTypes("/**@param {*} a*/ function f(a) {return a < 3;}", "left side of numeric comparison\n" + "found : *\n" + "required: number"); } public void testNumericComparison6() throws Exception { testTypes("/**@return {void} */ function foo() { if (3 >= foo()) return; }", "right side of numeric comparison\n" + "found : undefined\n" + "required: number"); } public void testStringComparison1() throws Exception { testTypes("/**@param {string} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison2() throws Exception { testTypes("/**@param {Object} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison3() throws Exception { testTypes("/**@param {number} a*/ function f(a) {return a < 'x';}"); } public void testStringComparison4() throws Exception { testTypes("/**@param {string|undefined} a*/ " + "function f(a) {return a < 'x';}"); } public void testStringComparison5() throws Exception { testTypes("/**@param {*} a*/ " + "function f(a) {return a < 'x';}"); } public void testStringComparison6() throws Exception { testTypes("/**@return {void} */ " + "function foo() { if ('a' >= foo()) return; }", "right side of comparison\n" + "found : undefined\n" + "required: string"); } public void testValueOfComparison1() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.valueOf = function() { return 1; };" + "/**@param {!O} a\n@param {!O} b*/ function f(a,b) { return a < b; }"); } public void testValueOfComparison2() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.valueOf = function() { return 1; };" + "/**@param {!O} a\n@param {number} b*/" + "function f(a,b) { return a < b; }"); } public void testValueOfComparison3() throws Exception { testTypes("/** @constructor */function O() {};" + "/**@override*/O.prototype.toString = function() { return 'o'; };" + "/**@param {!O} a\n@param {string} b*/" + "function f(a,b) { return a < b; }"); } public void testGenericRelationalExpression() throws Exception { testTypes("/**@param {*} a\n@param {*} b*/ " + "function f(a,b) {return a < b;}"); } public void testInstanceof1() throws Exception { testTypes("function foo(){" + "if (bar instanceof 3)return;}", "instanceof requires an object\n" + "found : number\n" + "required: Object"); } public void testInstanceof2() throws Exception { testTypes("/**@return {void}*/function foo(){" + "if (foo() instanceof Object)return;}", "deterministic instanceof yields false\n" + "found : undefined\n" + "required: NoObject"); } public void testInstanceof3() throws Exception { testTypes("/**@return {*} */function foo(){" + "if (foo() instanceof Object)return;}"); } public void testInstanceof4() throws Exception { testTypes("/**@return {(Object|number)} */function foo(){" + "if (foo() instanceof Object)return 3;}"); } public void testInstanceof5() throws Exception { // No warning for unknown types. testTypes("/** @return {?} */ function foo(){" + "if (foo() instanceof Object)return;}"); } public void testInstanceof6() throws Exception { testTypes("/**@return {(Array|number)} */function foo(){" + "if (foo() instanceof Object)return 3;}"); } public void testInstanceOfReduction3() throws Exception { testTypes( "/** \n" + " * @param {Object} x \n" + " * @param {Function} y \n" + " * @return {boolean} \n" + " */\n" + "var f = function(x, y) {\n" + " return x instanceof y;\n" + "};"); } public void testScoping1() throws Exception { testTypes( "/**@param {string} a*/function foo(a){" + " /**@param {Array|string} a*/function bar(a){" + " if (a instanceof Array)return;" + " }" + "}"); } public void testScoping2() throws Exception { testTypes( "/** @type number */ var a;" + "function Foo() {" + " /** @type string */ var a;" + "}"); } public void testScoping3() throws Exception { testTypes("\n\n/** @type{Number}*/var b;\n/** @type{!String} */var b;", "variable b redefined with type String, original " + "definition at [testcode]:3 with type (Number|null)"); } public void testScoping4() throws Exception { testTypes("/** @type{Number}*/var b; if (true) /** @type{!String} */var b;", "variable b redefined with type String, original " + "definition at [testcode]:1 with type (Number|null)"); } public void testScoping5() throws Exception { // multiple definitions are not checked by the type checker but by a // subsequent pass testTypes("if (true) var b; var b;"); } public void testScoping6() throws Exception { // multiple definitions are not checked by the type checker but by a // subsequent pass testTypes("if (true) var b; if (true) var b;"); } public void testScoping7() throws Exception { testTypes("/** @constructor */function A() {" + " /** @type !A */this.a = null;" + "}", "assignment to property a of A\n" + "found : null\n" + "required: A"); } public void testScoping8() throws Exception { testTypes("/** @constructor */function A() {}" + "/** @constructor */function B() {" + " /** @type !A */this.a = null;" + "}", "assignment to property a of B\n" + "found : null\n" + "required: A"); } public void testScoping9() throws Exception { testTypes("/** @constructor */function B() {" + " /** @type !A */this.a = null;" + "}" + "/** @constructor */function A() {}", "assignment to property a of B\n" + "found : null\n" + "required: A"); } public void testScoping10() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = function b(){};"); // a declared, b is not assertTrue(p.scope.isDeclared("a", false)); assertFalse(p.scope.isDeclared("b", false)); // checking that a has the correct assigned type assertEquals("function (): undefined", p.scope.getVar("a").getType().toString()); } public void testScoping11() throws Exception { // named function expressions create a binding in their body only // the return is wrong but the assignment is OK since the type of b is ? testTypes( "/** @return {number} */var a = function b(){ return b };", "inconsistent return type\n" + "found : function (): number\n" + "required: number"); } public void testScoping12() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @type {number} */ F.prototype.bar = 3;" + "/** @param {!F} f */ function g(f) {" + " /** @return {string} */" + " function h() {" + " return f.bar;" + " }" + "}", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testFunctionArguments1() throws Exception { testFunctionType( "/** @param {number} a\n@return {string} */" + "function f(a) {}", "function (number): string"); } public void testFunctionArguments2() throws Exception { testFunctionType( "/** @param {number} opt_a\n@return {string} */" + "function f(opt_a) {}", "function (number=): string"); } public void testFunctionArguments3() throws Exception { testFunctionType( "/** @param {number} b\n@return {string} */" + "function f(a,b) {}", "function (?, number): string"); } public void testFunctionArguments4() throws Exception { testFunctionType( "/** @param {number} opt_a\n@return {string} */" + "function f(a,opt_a) {}", "function (?, number=): string"); } public void testFunctionArguments5() throws Exception { testTypes( "function a(opt_a,a) {}", "optional arguments must be at the end"); } public void testFunctionArguments6() throws Exception { testTypes( "function a(var_args,a) {}", "variable length argument must be last"); } public void testFunctionArguments7() throws Exception { testTypes( "/** @param {number} opt_a\n@return {string} */" + "function a(a,opt_a,var_args) {}"); } public void testFunctionArguments8() throws Exception { testTypes( "function a(a,opt_a,var_args,b) {}", "variable length argument must be last"); } public void testFunctionArguments9() throws Exception { // testing that only one error is reported testTypes( "function a(a,opt_a,var_args,b,c) {}", "variable length argument must be last"); } public void testFunctionArguments10() throws Exception { // testing that only one error is reported testTypes( "function a(a,opt_a,b,c) {}", "optional arguments must be at the end"); } public void testFunctionArguments11() throws Exception { testTypes( "function a(a,opt_a,b,c,var_args,d) {}", "optional arguments must be at the end"); } public void testFunctionArguments12() throws Exception { testTypes("/** @param foo {String} */function bar(baz){}", "parameter foo does not appear in bar's parameter list"); } public void testFunctionArguments13() throws Exception { // verifying that the argument type have non-inferable types testTypes( "/** @return {boolean} */ function u() { return true; }" + "/** @param {boolean} b\n@return {?boolean} */" + "function f(b) { if (u()) { b = null; } return b; }", "assignment\n" + "found : null\n" + "required: boolean"); } public void testFunctionArguments14() throws Exception { testTypes( "/**\n" + " * @param {string} x\n" + " * @param {number} opt_y\n" + " * @param {boolean} var_args\n" + " */ function f(x, opt_y, var_args) {}" + "f('3'); f('3', 2); f('3', 2, true); f('3', 2, true, false);"); } public void testFunctionArguments15() throws Exception { testTypes( "/** @param {?function(*)} f */" + "function g(f) { f(1, 2); }", "Function f: called with 2 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testFunctionArguments16() throws Exception { testTypes( "/** @param {...number} var_args */" + "function g(var_args) {} g(1, true);", "actual parameter 2 of g does not match formal parameter\n" + "found : boolean\n" + "required: (number|undefined)"); } public void testFunctionArguments17() throws Exception { testClosureTypesMultipleWarnings( "/** @param {booool|string} x */" + "function f(x) { g(x) }" + "/** @param {number} x */" + "function g(x) {}", Lists.newArrayList( "Bad type annotation. Unknown type booool", "actual parameter 1 of g does not match formal parameter\n" + "found : (booool|null|string)\n" + "required: number")); } public void testFunctionArguments18() throws Exception { testTypes( "function f(x) {}" + "f(/** @param {number} y */ (function() {}));", "parameter y does not appear in 's parameter list"); } public void testPrintFunctionName1() throws Exception { // Ensures that the function name is pretty. testTypes( "var goog = {}; goog.run = function(f) {};" + "goog.run();", "Function goog.run: called with 0 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testPrintFunctionName2() throws Exception { testTypes( "/** @constructor */ var Foo = function() {}; " + "Foo.prototype.run = function(f) {};" + "(new Foo).run();", "Function Foo.prototype.run: called with 0 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testFunctionInference1() throws Exception { testFunctionType( "function f(a) {}", "function (?): undefined"); } public void testFunctionInference2() throws Exception { testFunctionType( "function f(a,b) {}", "function (?, ?): undefined"); } public void testFunctionInference3() throws Exception { testFunctionType( "function f(var_args) {}", "function (...[?]): undefined"); } public void testFunctionInference4() throws Exception { testFunctionType( "function f(a,b,c,var_args) {}", "function (?, ?, ?, ...[?]): undefined"); } public void testFunctionInference5() throws Exception { testFunctionType( "/** @this Date\n@return {string} */function f(a) {}", "function (this:Date, ?): string"); } public void testFunctionInference6() throws Exception { testFunctionType( "/** @this Date\n@return {string} */function f(opt_a) {}", "function (this:Date, ?=): string"); } public void testFunctionInference7() throws Exception { testFunctionType( "/** @this Date */function f(a,b,c,var_args) {}", "function (this:Date, ?, ?, ?, ...[?]): undefined"); } public void testFunctionInference8() throws Exception { testFunctionType( "function f() {}", "function (): undefined"); } public void testFunctionInference9() throws Exception { testFunctionType( "var f = function() {};", "function (): undefined"); } public void testFunctionInference10() throws Exception { testFunctionType( "/** @this Date\n@param {boolean} b\n@return {string} */" + "var f = function(a,b) {};", "function (this:Date, ?, boolean): string"); } public void testFunctionInference11() throws Exception { testFunctionType( "var goog = {};" + "/** @return {number}*/goog.f = function(){};", "goog.f", "function (): number"); } public void testFunctionInference12() throws Exception { testFunctionType( "var goog = {};" + "goog.f = function(){};", "goog.f", "function (): undefined"); } public void testFunctionInference13() throws Exception { testFunctionType( "var goog = {};" + "/** @constructor */ goog.Foo = function(){};" + "/** @param {!goog.Foo} f */function eatFoo(f){};", "eatFoo", "function (goog.Foo): undefined"); } public void testFunctionInference14() throws Exception { testFunctionType( "var goog = {};" + "/** @constructor */ goog.Foo = function(){};" + "/** @return {!goog.Foo} */function eatFoo(){ return new goog.Foo; };", "eatFoo", "function (): goog.Foo"); } public void testFunctionInference15() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "f.prototype.foo = function(){};", "f.prototype.foo", "function (this:f): undefined"); } public void testFunctionInference16() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "f.prototype.foo = function(){};", "(new f).foo", "function (this:f): undefined"); } public void testFunctionInference17() throws Exception { testFunctionType( "/** @constructor */ function f() {}" + "function abstractMethod() {}" + "/** @param {number} x */ f.prototype.foo = abstractMethod;", "(new f).foo", "function (this:f, number): ?"); } public void testFunctionInference18() throws Exception { testFunctionType( "var goog = {};" + "/** @this {Date} */ goog.eatWithDate;", "goog.eatWithDate", "function (this:Date): ?"); } public void testFunctionInference19() throws Exception { testFunctionType( "/** @param {string} x */ var f;", "f", "function (string): ?"); } public void testFunctionInference20() throws Exception { testFunctionType( "/** @this {Date} */ var f;", "f", "function (this:Date): ?"); } public void testFunctionInference21() throws Exception { testTypes( "var f = function() { throw 'x' };" + "/** @return {boolean} */ var g = f;"); testFunctionType( "var f = function() { throw 'x' };", "f", "function (): ?"); } public void testFunctionInference22() throws Exception { testTypes( "/** @type {!Function} */ var f = function() { g(this); };" + "/** @param {boolean} x */ var g = function(x) {};"); } public void testFunctionInference23() throws Exception { // We want to make sure that 'prop' isn't declared on all objects. testTypes( "/** @type {!Function} */ var f = function() {\n" + " /** @type {number} */ this.prop = 3;\n" + "};" + "/**\n" + " * @param {Object} x\n" + " * @return {string}\n" + " */ var g = function(x) { return x.prop; };"); } public void testInnerFunction1() throws Exception { testTypes( "function f() {" + " /** @type {number} */ var x = 3;\n" + " function g() { x = null; }" + " return x;" + "}", "assignment\n" + "found : null\n" + "required: number"); } public void testInnerFunction2() throws Exception { testTypes( "/** @return {number} */\n" + "function f() {" + " var x = null;\n" + " function g() { x = 3; }" + " g();" + " return x;" + "}", "inconsistent return type\n" + "found : (null|number)\n" + "required: number"); } public void testInnerFunction3() throws Exception { testTypes( "var x = null;" + "/** @return {number} */\n" + "function f() {" + " x = 3;\n" + " /** @return {number} */\n" + " function g() { x = true; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testInnerFunction4() throws Exception { testTypes( "var x = null;" + "/** @return {number} */\n" + "function f() {" + " x = '3';\n" + " /** @return {number} */\n" + " function g() { x = 3; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testInnerFunction5() throws Exception { testTypes( "/** @return {number} */\n" + "function f() {" + " var x = 3;\n" + " /** @return {number} */" + " function g() { var x = 3;x = true; return x; }" + " return x;" + "}", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testInnerFunction6() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " var x = 0 || function() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction7() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " /** @type {number|function()} */" + " var x = 0 || function() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction8() throws Exception { testClosureTypes( CLOSURE_DEFS + "function f() {" + " function x() {};\n" + " function g() { if (goog.isFunction(x)) { x(1); } }" + " g();" + "}", "Function x: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testInnerFunction9() throws Exception { testTypes( "function f() {" + " var x = 3;\n" + " function g() { x = null; };\n" + " function h() { return x == null; }" + " return h();" + "}"); } public void testInnerFunction10() throws Exception { testTypes( "function f() {" + " /** @type {?number} */ var x = null;" + " /** @return {string} */" + " function g() {" + " if (!x) {" + " x = 1;" + " }" + " return x;" + " }" + "}", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testInnerFunction11() throws Exception { // TODO(nicksantos): This is actually bad inference, because // h sets x to null. We should fix this, but for now we do it // this way so that we don't break existing binaries. We will // need to change TypeInference#isUnflowable to fix this. testTypes( "function f() {" + " /** @type {?number} */ var x = null;" + " /** @return {number} */" + " function g() {" + " x = 1;" + " h();" + " return x;" + " }" + " function h() {" + " x = null;" + " }" + "}"); } public void testAbstractMethodHandling1() throws Exception { testTypes( "/** @type {Function} */ var abstractFn = function() {};" + "abstractFn(1);"); } public void testAbstractMethodHandling2() throws Exception { testTypes( "var abstractFn = function() {};" + "abstractFn(1);", "Function abstractFn: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testAbstractMethodHandling3() throws Exception { testTypes( "var goog = {};" + "/** @type {Function} */ goog.abstractFn = function() {};" + "goog.abstractFn(1);"); } public void testAbstractMethodHandling4() throws Exception { testTypes( "var goog = {};" + "goog.abstractFn = function() {};" + "goog.abstractFn(1);", "Function goog.abstractFn: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testAbstractMethodHandling5() throws Exception { testTypes( "/** @type {!Function} */ var abstractFn = function() {};" + "/** @param {number} x */ var f = abstractFn;" + "f('x');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testAbstractMethodHandling6() throws Exception { testTypes( "var goog = {};" + "/** @type {Function} */ goog.abstractFn = function() {};" + "/** @param {number} x */ goog.f = abstractFn;" + "goog.f('x');", "actual parameter 1 of goog.f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testMethodInference1() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @return {number} */ F.prototype.foo = function() { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference2() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.F = function() {};" + "/** @return {number} */ goog.F.prototype.foo = " + " function() { return 3; };" + "/** @constructor \n * @extends {goog.F} */ " + "goog.G = function() {};" + "/** @override */ goog.G.prototype.foo = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference3() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {boolean} x \n * @return {number} */ " + "F.prototype.foo = function(x) { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(x) { return x; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference4() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {boolean} x \n * @return {number} */ " + "F.prototype.foo = function(x) { return 3; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(y) { return y; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testMethodInference5() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {number} x \n * @return {string} */ " + "F.prototype.foo = function(x) { return 'x'; };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @type {number} */ G.prototype.num = 3;" + "/** @override */ " + "G.prototype.foo = function(y) { return this.num + y; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testMethodInference6() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @param {number} x */ F.prototype.foo = function(x) { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function() { };" + "(new G()).foo(1);"); } public void testMethodInference7() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ G.prototype.foo = function(x, y) { };", "mismatch of the foo property type and the type of the property " + "it overrides from superclass F\n" + "original: function (this:F): undefined\n" + "override: function (this:G, ?, ?): undefined"); } public void testMethodInference8() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(opt_b, var_args) { };" + "(new G()).foo(1, 2, 3);"); } public void testMethodInference9() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.foo = function() { };" + "/** @constructor \n * @extends {F} */ " + "function G() {}" + "/** @override */ " + "G.prototype.foo = function(var_args, opt_b) { };", "variable length argument must be last"); } public void testStaticMethodDeclaration1() throws Exception { testTypes( "/** @constructor */ function F() { F.foo(true); }" + "/** @param {number} x */ F.foo = function(x) {};", "actual parameter 1 of F.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testStaticMethodDeclaration2() throws Exception { testTypes( "var goog = goog || {}; function f() { goog.foo(true); }" + "/** @param {number} x */ goog.foo = function(x) {};", "actual parameter 1 of goog.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testStaticMethodDeclaration3() throws Exception { testTypes( "var goog = goog || {}; function f() { goog.foo(true); }" + "goog.foo = function() {};", "Function goog.foo: called with 1 argument(s). Function requires " + "at least 0 argument(s) and no more than 0 argument(s)."); } public void testDuplicateStaticMethodDecl1() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {number} x */ goog.foo = function(x) {};" + "/** @param {number} x */ goog.foo = function(x) {};", "variable goog.foo redefined with type function (number): undefined, " + "original definition at [testcode]:1 " + "with type function (number): undefined"); } public void testDuplicateStaticMethodDecl2() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {number} x */ goog.foo = function(x) {};" + "/** @param {number} x \n * @suppress {duplicate} */ " + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl3() throws Exception { testTypes( "var goog = goog || {};" + "goog.foo = function(x) {};" + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl4() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Function} */ goog.foo = function(x) {};" + "goog.foo = function(x) {};"); } public void testDuplicateStaticMethodDecl5() throws Exception { testTypes( "var goog = goog || {};" + "goog.foo = function(x) {};" + "/** @return {undefined} */ goog.foo = function(x) {};", "variable goog.foo redefined with type function (?): undefined, " + "original definition at [testcode]:1 with type " + "function (?): undefined"); } public void testDuplicateStaticMethodDecl6() throws Exception { // Make sure the CAST node doesn't interfere with the @suppress // annotation. testTypes( "var goog = goog || {};" + "goog.foo = function(x) {};" + "/**\n" + " * @suppress {duplicate}\n" + " * @return {undefined}\n" + " */\n" + "goog.foo = " + " /** @type {!Function} */ (function(x) {});"); } public void testDuplicateStaticPropertyDecl1() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Foo} */ goog.foo;" + "/** @type {Foo} */ goog.foo;" + "/** @constructor */ function Foo() {}"); } public void testDuplicateStaticPropertyDecl2() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {Foo} */ goog.foo;" + "/** @type {Foo} \n * @suppress {duplicate} */ goog.foo;" + "/** @constructor */ function Foo() {}"); } public void testDuplicateStaticPropertyDecl3() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string} */ goog.foo;" + "/** @constructor */ function Foo() {}", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo"); } public void testDuplicateStaticPropertyDecl4() throws Exception { testClosureTypesMultipleWarnings( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string} */ goog.foo = 'x';" + "/** @constructor */ function Foo() {}", Lists.newArrayList( "assignment to property foo of goog\n" + "found : string\n" + "required: Foo", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo")); } public void testDuplicateStaticPropertyDecl5() throws Exception { testClosureTypesMultipleWarnings( "var goog = goog || {};" + "/** @type {!Foo} */ goog.foo;" + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';" + "/** @constructor */ function Foo() {}", Lists.newArrayList( "assignment to property foo of goog\n" + "found : string\n" + "required: Foo", "variable goog.foo redefined with type string, " + "original definition at [testcode]:1 with type Foo")); } public void testDuplicateStaticPropertyDecl6() throws Exception { testTypes( "var goog = goog || {};" + "/** @type {string} */ goog.foo = 'y';" + "/** @type {string}\n * @suppress {duplicate} */ goog.foo = 'x';"); } public void testDuplicateStaticPropertyDecl7() throws Exception { testTypes( "var goog = goog || {};" + "/** @param {string} x */ goog.foo;" + "/** @type {function(string)} */ goog.foo;"); } public void testDuplicateStaticPropertyDecl8() throws Exception { testTypes( "var goog = goog || {};" + "/** @return {EventCopy} */ goog.foo;" + "/** @constructor */ function EventCopy() {}" + "/** @return {EventCopy} */ goog.foo;"); } public void testDuplicateStaticPropertyDecl9() throws Exception { testTypes( "var goog = goog || {};" + "/** @return {EventCopy} */ goog.foo;" + "/** @return {EventCopy} */ goog.foo;" + "/** @constructor */ function EventCopy() {}"); } public void testDuplicateStaticPropertyDec20() throws Exception { testTypes( "/**\n" + " * @fileoverview\n" + " * @suppress {duplicate}\n" + " */" + "var goog = goog || {};" + "/** @type {string} */ goog.foo = 'y';" + "/** @type {string} */ goog.foo = 'x';"); } public void testDuplicateLocalVarDecl() throws Exception { testClosureTypesMultipleWarnings( "/** @param {number} x */\n" + "function f(x) { /** @type {string} */ var x = ''; }", Lists.newArrayList( "variable x redefined with type string, original definition" + " at [testcode]:2 with type number", "initializing variable\n" + "found : string\n" + "required: number")); } public void testDuplicateInstanceMethod1() throws Exception { // If there's no jsdoc on the methods, then we treat them like // any other inferred properties. testTypes( "/** @constructor */ function F() {}" + "F.prototype.bar = function() {};" + "F.prototype.bar = function() {};"); } public void testDuplicateInstanceMethod2() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** jsdoc */ F.prototype.bar = function() {};" + "/** jsdoc */ F.prototype.bar = function() {};", "variable F.prototype.bar redefined with type " + "function (this:F): undefined, original definition at " + "[testcode]:1 with type function (this:F): undefined"); } public void testDuplicateInstanceMethod3() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.bar = function() {};" + "/** jsdoc */ F.prototype.bar = function() {};", "variable F.prototype.bar redefined with type " + "function (this:F): undefined, original definition at " + "[testcode]:1 with type function (this:F): undefined"); } public void testDuplicateInstanceMethod4() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** jsdoc */ F.prototype.bar = function() {};" + "F.prototype.bar = function() {};"); } public void testDuplicateInstanceMethod5() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** jsdoc \n * @return {number} */ F.prototype.bar = function() {" + " return 3;" + "};" + "/** jsdoc \n * @suppress {duplicate} */ " + "F.prototype.bar = function() { return ''; };", "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testDuplicateInstanceMethod6() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** jsdoc \n * @return {number} */ F.prototype.bar = function() {" + " return 3;" + "};" + "/** jsdoc \n * @return {string} * \n @suppress {duplicate} */ " + "F.prototype.bar = function() { return ''; };", "assignment to property bar of F.prototype\n" + "found : function (this:F): string\n" + "required: function (this:F): number"); } public void testStubFunctionDeclaration1() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "/** @param {number} x \n * @param {string} y \n" + " * @return {number} */ f.prototype.foo;", "(new f).foo", "function (this:f, number, string): number"); } public void testStubFunctionDeclaration2() throws Exception { testExternFunctionType( // externs "/** @constructor */ function f() {};" + "/** @constructor \n * @extends {f} */ f.subclass;", "f.subclass", "function (new:f.subclass): ?"); } public void testStubFunctionDeclaration3() throws Exception { testFunctionType( "/** @constructor */ function f() {};" + "/** @return {undefined} */ f.foo;", "f.foo", "function (): undefined"); } public void testStubFunctionDeclaration4() throws Exception { testFunctionType( "/** @constructor */ function f() { " + " /** @return {number} */ this.foo;" + "}", "(new f).foo", "function (this:f): number"); } public void testStubFunctionDeclaration5() throws Exception { testFunctionType( "/** @constructor */ function f() { " + " /** @type {Function} */ this.foo;" + "}", "(new f).foo", createNullableType(U2U_CONSTRUCTOR_TYPE).toString()); } public void testStubFunctionDeclaration6() throws Exception { testFunctionType( "/** @constructor */ function f() {} " + "/** @type {Function} */ f.prototype.foo;", "(new f).foo", createNullableType(U2U_CONSTRUCTOR_TYPE).toString()); } public void testStubFunctionDeclaration7() throws Exception { testFunctionType( "/** @constructor */ function f() {} " + "/** @type {Function} */ f.prototype.foo = function() {};", "(new f).foo", createNullableType(U2U_CONSTRUCTOR_TYPE).toString()); } public void testStubFunctionDeclaration8() throws Exception { testFunctionType( "/** @type {Function} */ var f = function() {}; ", "f", createNullableType(U2U_CONSTRUCTOR_TYPE).toString()); } public void testStubFunctionDeclaration9() throws Exception { testFunctionType( "/** @type {function():number} */ var f; ", "f", "function (): number"); } public void testStubFunctionDeclaration10() throws Exception { testFunctionType( "/** @type {function(number):number} */ var f = function(x) {};", "f", "function (number): number"); } public void testNestedFunctionInference1() throws Exception { String nestedAssignOfFooAndBar = "/** @constructor */ function f() {};" + "f.prototype.foo = f.prototype.bar = function(){};"; testFunctionType(nestedAssignOfFooAndBar, "(new f).bar", "function (this:f): undefined"); } /** * Tests the type of a function definition. The function defined by * {@code functionDef} should be named {@code "f"}. */ private void testFunctionType(String functionDef, String functionType) throws Exception { testFunctionType(functionDef, "f", functionType); } /** * Tests the type of a function definition. The function defined by * {@code functionDef} should be named {@code functionName}. */ private void testFunctionType(String functionDef, String functionName, String functionType) throws Exception { // using the variable initialization check to verify the function's type testTypes( functionDef + "/** @type number */var a=" + functionName + ";", "initializing variable\n" + "found : " + functionType + "\n" + "required: number"); } /** * Tests the type of a function definition in externs. * The function defined by {@code functionDef} should be * named {@code functionName}. */ private void testExternFunctionType(String functionDef, String functionName, String functionType) throws Exception { testTypes( functionDef, "/** @type number */var a=" + functionName + ";", "initializing variable\n" + "found : " + functionType + "\n" + "required: number", false); } public void testTypeRedefinition() throws Exception { testClosureTypesMultipleWarnings("a={};/**@enum {string}*/ a.A = {ZOR:'b'};" + "/** @constructor */ a.A = function() {}", Lists.newArrayList( "variable a.A redefined with type function (new:a.A): undefined, " + "original definition at [testcode]:1 with type enum{a.A}", "assignment to property A of a\n" + "found : function (new:a.A): undefined\n" + "required: enum{a.A}")); } public void testIn1() throws Exception { testTypes("'foo' in Object"); } public void testIn2() throws Exception { testTypes("3 in Object"); } public void testIn3() throws Exception { testTypes("undefined in Object"); } public void testIn4() throws Exception { testTypes("Date in Object", "left side of 'in'\n" + "found : function (new:Date, ?=, ?=, ?=, ?=, ?=, ?=, ?=): string\n" + "required: string"); } public void testIn5() throws Exception { testTypes("'x' in null", "'in' requires an object\n" + "found : null\n" + "required: Object"); } public void testIn6() throws Exception { testTypes( "/** @param {number} x */" + "function g(x) {}" + "g(1 in {});", "actual parameter 1 of g does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testIn7() throws Exception { // Make sure we do inference in the 'in' expression. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " return g(x.foo) in {};" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testForIn1() throws Exception { testTypes( "/** @param {boolean} x */ function f(x) {}" + "for (var k in {}) {" + " f(k);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: boolean"); } public void testForIn2() throws Exception { testTypes( "/** @param {boolean} x */ function f(x) {}" + "/** @enum {string} */ var E = {FOO: 'bar'};" + "/** @type {Object.} */ var obj = {};" + "var k = null;" + "for (k in obj) {" + " f(k);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : E.\n" + "required: boolean"); } public void testForIn3() throws Exception { testTypes( "/** @param {boolean} x */ function f(x) {}" + "/** @type {Object.} */ var obj = {};" + "for (var k in obj) {" + " f(obj[k]);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: boolean"); } public void testForIn4() throws Exception { testTypes( "/** @param {boolean} x */ function f(x) {}" + "/** @enum {string} */ var E = {FOO: 'bar'};" + "/** @type {Object.} */ var obj = {};" + "for (var k in obj) {" + " f(obj[k]);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : (Array|null)\n" + "required: boolean"); } public void testForIn5() throws Exception { testTypes( "/** @param {boolean} x */ function f(x) {}" + "/** @constructor */ var E = function(){};" + "/** @type {Object.} */ var obj = {};" + "for (var k in obj) {" + " f(k);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: boolean"); } // TODO(nicksantos): change this to something that makes sense. // public void testComparison1() throws Exception { // testTypes("/**@type null */var a;" + // "/**@type !Date */var b;" + // "if (a==b) {}", // "condition always evaluates to false\n" + // "left : null\n" + // "right: Date"); // } public void testComparison2() throws Exception { testTypes("/**@type number*/var a;" + "/**@type !Date */var b;" + "if (a!==b) {}", "condition always evaluates to true\n" + "left : number\n" + "right: Date"); } public void testComparison3() throws Exception { // Since null == undefined in JavaScript, this code is reasonable. testTypes("/** @type {(Object,undefined)} */var a;" + "var b = a == null"); } public void testComparison4() throws Exception { testTypes("/** @type {(!Object,undefined)} */var a;" + "/** @type {!Object} */var b;" + "var c = a == b"); } public void testComparison5() throws Exception { testTypes("/** @type null */var a;" + "/** @type null */var b;" + "a == b", "condition always evaluates to true\n" + "left : null\n" + "right: null"); } public void testComparison6() throws Exception { testTypes("/** @type null */var a;" + "/** @type null */var b;" + "a != b", "condition always evaluates to false\n" + "left : null\n" + "right: null"); } public void testComparison7() throws Exception { testTypes("var a;" + "var b;" + "a == b", "condition always evaluates to true\n" + "left : undefined\n" + "right: undefined"); } public void testComparison8() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] == null || a[1] == undefined"); } public void testComparison9() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] == null", "condition always evaluates to true\n" + "left : undefined\n" + "right: null"); } public void testComparison10() throws Exception { testTypes("/** @type {Array.} */ var a = [];" + "a[0] === null"); } public void testComparison11() throws Exception { testTypes( "(function(){}) == 'x'", "condition always evaluates to false\n" + "left : function (): undefined\n" + "right: string"); } public void testComparison12() throws Exception { testTypes( "(function(){}) == 3", "condition always evaluates to false\n" + "left : function (): undefined\n" + "right: number"); } public void testComparison13() throws Exception { testTypes( "(function(){}) == false", "condition always evaluates to false\n" + "left : function (): undefined\n" + "right: boolean"); } public void testComparison14() throws Exception { testTypes("/** @type {function((Array|string), Object): number} */" + "function f(x, y) { return x === y; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testComparison15() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @constructor */ function F() {}" + "/**\n" + " * @param {number} x\n" + " * @constructor\n" + " * @extends {F}\n" + " */\n" + "function G(x) {}\n" + "goog.inherits(G, F);\n" + "/**\n" + " * @param {number} x\n" + " * @constructor\n" + " * @extends {G}\n" + " */\n" + "function H(x) {}\n" + "goog.inherits(H, G);\n" + "/** @param {G} x */" + "function f(x) { return x.constructor === H; }", null); } public void testDeleteOperator1() throws Exception { testTypes( "var x = {};" + "/** @return {string} */ function f() { return delete x['a']; }", "inconsistent return type\n" + "found : boolean\n" + "required: string"); } public void testDeleteOperator2() throws Exception { testTypes( "var obj = {};" + "/** \n" + " * @param {string} x\n" + " * @return {Object} */ function f(x) { return obj; }" + "/** @param {?number} x */ function g(x) {" + " if (x) { delete f(x)['a']; }" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testEnumStaticMethod1() throws Exception { testTypes( "/** @enum */ var Foo = {AAA: 1};" + "/** @param {number} x */ Foo.method = function(x) {};" + "Foo.method(true);", "actual parameter 1 of Foo.method does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testEnumStaticMethod2() throws Exception { testTypes( "/** @enum */ var Foo = {AAA: 1};" + "/** @param {number} x */ Foo.method = function(x) {};" + "function f() { Foo.method(true); }", "actual parameter 1 of Foo.method does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testEnum1() throws Exception { testTypes("/**@enum*/var a={BB:1,CC:2};\n" + "/**@type {a}*/var d;d=a.BB;"); } public void testEnum2() throws Exception { testTypes("/**@enum*/var a={b:1}", "enum key b must be a syntactic constant"); } public void testEnum3() throws Exception { testTypes("/**@enum*/var a={BB:1,BB:2}", "variable a.BB redefined with type a., " + "original definition at [testcode]:1 with type a."); } public void testEnum4() throws Exception { testTypes("/**@enum*/var a={BB:'string'}", "assignment to property BB of enum{a}\n" + "found : string\n" + "required: number"); } public void testEnum5() throws Exception { testTypes("/**@enum {String}*/var a={BB:'string'}", "assignment to property BB of enum{a}\n" + "found : string\n" + "required: (String|null)"); } public void testEnum6() throws Exception { testTypes("/**@enum*/var a={BB:1,CC:2};\n/**@type {!Array}*/var d;d=a.BB;", "assignment\n" + "found : a.\n" + "required: Array"); } public void testEnum7() throws Exception { testTypes("/** @enum */var a={AA:1,BB:2,CC:3};" + "/** @type a */var b=a.D;", "element D does not exist on this enum"); } public void testEnum8() throws Exception { testClosureTypesMultipleWarnings("/** @enum */var a=8;", Lists.newArrayList( "enum initializer must be an object literal or an enum", "initializing variable\n" + "found : number\n" + "required: enum{a}")); } public void testEnum9() throws Exception { testClosureTypesMultipleWarnings( "var goog = {};" + "/** @enum */goog.a=8;", Lists.newArrayList( "assignment to property a of goog\n" + "found : number\n" + "required: enum{goog.a}", "enum initializer must be an object literal or an enum")); } public void testEnum10() throws Exception { testTypes( "/** @enum {number} */" + "goog.K = { A : 3 };"); } public void testEnum11() throws Exception { testTypes( "/** @enum {number} */" + "goog.K = { 502 : 3 };"); } public void testEnum12() throws Exception { testTypes( "/** @enum {number} */ var a = {};" + "/** @enum */ var b = a;"); } public void testEnum13() throws Exception { testTypes( "/** @enum {number} */ var a = {};" + "/** @enum {string} */ var b = a;", "incompatible enum element types\n" + "found : number\n" + "required: string"); } public void testEnum14() throws Exception { testTypes( "/** @enum {number} */ var a = {FOO:5};" + "/** @enum */ var b = a;" + "var c = b.FOO;"); } public void testEnum15() throws Exception { testTypes( "/** @enum {number} */ var a = {FOO:5};" + "/** @enum */ var b = a;" + "var c = b.BAR;", "element BAR does not exist on this enum"); } public void testEnum16() throws Exception { testTypes("var goog = {};" + "/**@enum*/goog .a={BB:1,BB:2}", "variable goog.a.BB redefined with type goog.a., " + "original definition at [testcode]:1 with type goog.a."); } public void testEnum17() throws Exception { testTypes("var goog = {};" + "/**@enum*/goog.a={BB:'string'}", "assignment to property BB of enum{goog.a}\n" + "found : string\n" + "required: number"); } public void testEnum18() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {!E} x\n@return {number} */\n" + "var f = function(x) { return x; };"); } public void testEnum19() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {number} x\n@return {!E} */\n" + "var f = function(x) { return x; };", "inconsistent return type\n" + "found : number\n" + "required: E."); } public void testEnum20() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2}; var x = []; x[E.A] = 0;"); } public void testEnum21() throws Exception { Node n = parseAndTypeCheck( "/** @enum {string} */ var E = {A : 'a', B : 'b'};\n" + "/** @param {!E} x\n@return {!E} */ function f(x) { return x; }"); Node nodeX = n.getLastChild().getLastChild().getLastChild().getLastChild(); JSType typeE = nodeX.getJSType(); assertFalse(typeE.isObject()); assertFalse(typeE.isNullable()); } public void testEnum22() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum23() throws Exception { testTypes("/**@enum*/ var E = {A: 1, B: 2};" + "/** @param {E} x \n* @return {string} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.\n" + "required: string"); } public void testEnum24() throws Exception { testTypes("/**@enum {Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}", "inconsistent return type\n" + "found : E.<(Object|null)>\n" + "required: Object"); } public void testEnum25() throws Exception { testTypes("/**@enum {!Object} */ var E = {A: {}};" + "/** @param {E} x \n* @return {!Object} */ function f(x) {return x}"); } public void testEnum26() throws Exception { testTypes("var a = {}; /**@enum*/ a.B = {A: 1, B: 2};" + "/** @param {a.B} x \n* @return {number} */ function f(x) {return x}"); } public void testEnum27() throws Exception { // x is unknown testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "function f(x) { return A == x; }"); } public void testEnum28() throws Exception { // x is unknown testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "function f(x) { return A.B == x; }"); } public void testEnum29() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {number} */ function f() { return A; }", "inconsistent return type\n" + "found : enum{A}\n" + "required: number"); } public void testEnum30() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {number} */ function f() { return A.B; }"); } public void testEnum31() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {A} */ function f() { return A; }", "inconsistent return type\n" + "found : enum{A}\n" + "required: A."); } public void testEnum32() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @return {A} */ function f() { return A.B; }"); } public void testEnum34() throws Exception { testTypes("/** @enum */ var A = {B: 1, C: 2}; " + "/** @param {number} x */ function f(x) { return x == A.B; }"); } public void testEnum35() throws Exception { testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + "/** @return {a.b} */ function f() { return a.b.C; }"); } public void testEnum36() throws Exception { testTypes("var a = a || {}; /** @enum */ a.b = {C: 1, D: 2};" + "/** @return {!a.b} */ function f() { return 1; }", "inconsistent return type\n" + "found : number\n" + "required: a.b."); } public void testEnum37() throws Exception { testTypes( "var goog = goog || {};" + "/** @enum {number} */ goog.a = {};" + "/** @enum */ var b = goog.a;"); } public void testEnum38() throws Exception { testTypes( "/** @enum {MyEnum} */ var MyEnum = {};" + "/** @param {MyEnum} x */ function f(x) {}", "Parse error. Cycle detected in inheritance chain " + "of type MyEnum"); } public void testEnum39() throws Exception { testTypes( "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + "/** @param {MyEnum} x \n * @return {number} */" + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testEnum40() throws Exception { testTypes( "/** @enum {Number} */ var MyEnum = {FOO: new Number(1)};" + "/** @param {number} x \n * @return {number} */" + "function f(x) { return x == MyEnum.FOO && MyEnum.FOO == x; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testEnum41() throws Exception { testTypes( "/** @enum {number} */ var MyEnum = {/** @const */ FOO: 1};" + "/** @return {string} */" + "function f() { return MyEnum.FOO; }", "inconsistent return type\n" + "found : MyEnum.\n" + "required: string"); } public void testEnum42() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @enum {Object} */ var MyEnum = {FOO: {newProperty: 1, b: 2}};" + "f(MyEnum.FOO.newProperty);"); } public void testAliasedEnum1() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {MyEnum} x */ function f(x) {} f(MyEnum.FOO);"); } public void testAliasedEnum2() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {YourEnum} x */ function f(x) {} f(MyEnum.FOO);"); } public void testAliasedEnum3() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {MyEnum} x */ function f(x) {} f(YourEnum.FOO);"); } public void testAliasedEnum4() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {YourEnum} x */ function f(x) {} f(YourEnum.FOO);"); } public void testAliasedEnum5() throws Exception { testTypes( "/** @enum */ var YourEnum = {FOO: 3};" + "/** @enum */ var MyEnum = YourEnum;" + "/** @param {string} x */ function f(x) {} f(MyEnum.FOO);", "actual parameter 1 of f does not match formal parameter\n" + "found : YourEnum.\n" + "required: string"); } public void testBackwardsEnumUse1() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var MyEnum = {FOO: 'x'};"); } public void testBackwardsEnumUse2() throws Exception { testTypes( "/** @return {number} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var MyEnum = {FOO: 'x'};", "inconsistent return type\n" + "found : MyEnum.\n" + "required: number"); } public void testBackwardsEnumUse3() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;"); } public void testBackwardsEnumUse4() throws Exception { testTypes( "/** @return {number} */ function f() { return MyEnum.FOO; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;", "inconsistent return type\n" + "found : YourEnum.\n" + "required: number"); } public void testBackwardsEnumUse5() throws Exception { testTypes( "/** @return {string} */ function f() { return MyEnum.BAR; }" + "/** @enum {string} */ var YourEnum = {FOO: 'x'};" + "/** @enum {string} */ var MyEnum = YourEnum;", "element BAR does not exist on this enum"); } public void testBackwardsTypedefUse2() throws Exception { testTypes( "/** @this {MyTypedef} */ function f() {}" + "/** @typedef {!(Date|Array)} */ var MyTypedef;"); } public void testBackwardsTypedefUse4() throws Exception { testTypes( "/** @return {MyTypedef} */ function f() { return null; }" + "/** @typedef {string} */ var MyTypedef;", "inconsistent return type\n" + "found : null\n" + "required: string"); } public void testBackwardsTypedefUse6() throws Exception { testTypes( "/** @return {goog.MyTypedef} */ function f() { return null; }" + "var goog = {};" + "/** @typedef {string} */ goog.MyTypedef;", "inconsistent return type\n" + "found : null\n" + "required: string"); } public void testBackwardsTypedefUse7() throws Exception { testTypes( "/** @return {goog.MyTypedef} */ function f() { return null; }" + "var goog = {};" + "/** @typedef {Object} */ goog.MyTypedef;"); } public void testBackwardsTypedefUse8() throws Exception { // Technically, this isn't quite right, because the JS runtime // will coerce null -> the global object. But we'll punt on that for now. testTypes( "/** @param {!Array} x */ function g(x) {}" + "/** @this {goog.MyTypedef} */ function f() { g(this); }" + "var goog = {};" + "/** @typedef {(Array|null|undefined)} */ goog.MyTypedef;"); } public void testBackwardsTypedefUse9() throws Exception { testTypes( "/** @param {!Array} x */ function g(x) {}" + "/** @this {goog.MyTypedef} */ function f() { g(this); }" + "var goog = {};" + "/** @typedef {(Error|null|undefined)} */ goog.MyTypedef;", "actual parameter 1 of g does not match formal parameter\n" + "found : Error\n" + "required: Array"); } public void testBackwardsTypedefUse10() throws Exception { testTypes( "/** @param {goog.MyEnum} x */ function g(x) {}" + "var goog = {};" + "/** @enum {goog.MyTypedef} */ goog.MyEnum = {FOO: 1};" + "/** @typedef {number} */ goog.MyTypedef;" + "g(1);", "actual parameter 1 of g does not match formal parameter\n" + "found : number\n" + "required: goog.MyEnum."); } public void testBackwardsConstructor1() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = function(x) {};", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testBackwardsConstructor2() throws Exception { testTypes( "function f() { (new Foo(true)); }" + "/** \n * @constructor \n * @param {number} x */" + "var YourFoo = function(x) {};" + "/** \n * @constructor \n * @param {number} x */" + "var Foo = YourFoo;", "actual parameter 1 of Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testMinimalConstructorAnnotation() throws Exception { testTypes("/** @constructor */function Foo(){}"); } public void testGoodExtends1() throws Exception { // A minimal @extends example testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends2() throws Exception { testTypes("/** @constructor\n * @extends base */function derived() {}\n" + "/** @constructor */function base() {}\n"); } public void testGoodExtends3() throws Exception { testTypes("/** @constructor\n * @extends {Object} */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n"); } public void testGoodExtends4() throws Exception { // Ensure that @extends actually sets the base type of a constructor // correctly. Because this isn't part of the human-readable Function // definition, we need to crawl the prototype chain (eww). Node n = parseAndTypeCheck( "var goog = {};\n" + "/** @constructor */goog.Base = function(){};\n" + "/** @constructor\n" + " * @extends {goog.Base} */goog.Derived = function(){};\n"); Node subTypeName = n.getLastChild().getLastChild().getFirstChild(); assertEquals("goog.Derived", subTypeName.getQualifiedName()); FunctionType subCtorType = (FunctionType) subTypeName.getNext().getJSType(); assertEquals("goog.Derived", subCtorType.getInstanceType().toString()); JSType superType = subCtorType.getPrototype().getImplicitPrototype(); assertEquals("goog.Base", superType.toString()); } public void testGoodExtends5() throws Exception { // we allow for the extends annotation to be placed first testTypes("/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n"); } public void testGoodExtends6() throws Exception { testFunctionType( CLOSURE_DEFS + "/** @constructor */function base() {}\n" + "/** @return {number} */ " + " base.prototype.foo = function() { return 1; };\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "goog.inherits(derived, base);", "derived.superClass_.foo", "function (this:base): number"); } public void testGoodExtends7() throws Exception { testFunctionType( "Function.prototype.inherits = function(x) {};" + "/** @constructor */function base() {}\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "derived.inherits(base);", "(new derived).constructor", "function (new:derived, ...[?]): ?"); } public void testGoodExtends8() throws Exception { testTypes("/** @constructor \n @extends {Base} */ function Sub() {}" + "/** @return {number} */ function f() { return (new Sub()).foo; }" + "/** @constructor */ function Base() {}" + "/** @type {boolean} */ Base.prototype.foo = true;", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testGoodExtends9() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "Super.prototype.foo = function() {};" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "/** @override */ Sub.prototype.foo = function() {};"); } public void testGoodExtends10() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "/** @return {Super} */ function foo() { return new Sub(); }"); } public void testGoodExtends11() throws Exception { testTypes( "/** @constructor */ function Super() {}" + "/** @param {boolean} x */ Super.prototype.foo = function(x) {};" + "/** @constructor \n * @extends {Super} */ function Sub() {}" + "Sub.prototype = new Super();" + "(new Sub()).foo(0);", "actual parameter 1 of Super.prototype.foo " + "does not match formal parameter\n" + "found : number\n" + "required: boolean"); } public void testGoodExtends12() throws Exception { testTypes( "/** @constructor \n * @extends {Super} */ function Sub() {}" + "/** @constructor \n * @extends {Sub} */ function Sub2() {}" + "/** @constructor */ function Super() {}" + "/** @param {Super} x */ function foo(x) {}" + "foo(new Sub2());"); } public void testGoodExtends13() throws Exception { testTypes( "/** @constructor \n * @extends {B} */ function C() {}" + "/** @constructor \n * @extends {D} */ function E() {}" + "/** @constructor \n * @extends {C} */ function D() {}" + "/** @constructor \n * @extends {A} */ function B() {}" + "/** @constructor */ function A() {}" + "/** @param {number} x */ function f(x) {} f(new E());", "actual parameter 1 of f does not match formal parameter\n" + "found : E\n" + "required: number"); } public void testGoodExtends14() throws Exception { testTypes( CLOSURE_DEFS + "/** @param {Function} f */ function g(f) {" + " /** @constructor */ function NewType() {};" + " goog.inherits(NewType, f);" + " (new NewType());" + "}"); } public void testGoodExtends15() throws Exception { testTypes( CLOSURE_DEFS + "/** @constructor */ function OldType() {}" + "/** @param {?function(new:OldType)} f */ function g(f) {" + " /**\n" + " * @constructor\n" + " * @extends {OldType}\n" + " */\n" + " function NewType() {};" + " goog.inherits(NewType, f);" + " NewType.prototype.method = function() {" + " NewType.superClass_.foo.call(this);" + " };" + "}", "Property foo never defined on OldType.prototype"); } public void testGoodExtends16() throws Exception { testTypes( CLOSURE_DEFS + "/** @param {Function} f */ function g(f) {" + " /** @constructor */ function NewType() {};" + " goog.inherits(f, NewType);" + " (new NewType());" + "}"); } public void testGoodExtends17() throws Exception { testFunctionType( "Function.prototype.inherits = function(x) {};" + "/** @constructor */function base() {}\n" + "/** @param {number} x */ base.prototype.bar = function(x) {};\n" + "/** @extends {base}\n * @constructor */function derived() {}\n" + "derived.inherits(base);", "(new derived).constructor.prototype.bar", "function (this:base, number): undefined"); } public void testBadExtends1() throws Exception { testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {not_base} */function derived() {}\n", "Bad type annotation. Unknown type not_base"); } public void testBadExtends2() throws Exception { testTypes("/** @constructor */function base() {\n" + "/** @type {!Number}*/\n" + "this.baseMember = new Number(4);\n" + "}\n" + "/** @constructor\n" + " * @extends {base} */function derived() {}\n" + "/** @param {!String} x*/\n" + "function foo(x){ }\n" + "/** @type {!derived}*/var y;\n" + "foo(y.baseMember);\n", "actual parameter 1 of foo does not match formal parameter\n" + "found : Number\n" + "required: String"); } public void testBadExtends3() throws Exception { testTypes("/** @extends {Object} */function base() {}", "@extends used without @constructor or @interface for base"); } public void testBadExtends4() throws Exception { // If there's a subclass of a class with a bad extends, // we only want to warn about the first one. testTypes( "/** @constructor \n * @extends {bad} */ function Sub() {}" + "/** @constructor \n * @extends {Sub} */ function Sub2() {}" + "/** @param {Sub} x */ function foo(x) {}" + "foo(new Sub2());", "Bad type annotation. Unknown type bad"); } public void testLateExtends() throws Exception { testTypes( CLOSURE_DEFS + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.foo = function() {};\n" + "/** @constructor */function Bar() {}\n" + "goog.inherits(Foo, Bar);\n", "Missing @extends tag on type Foo"); } public void testSuperclassMatch() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);\n"); } public void testSuperclassMatchWithMixin() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor */ var Baz = function() {};\n" + "/** @constructor \n @extends Foo */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.mixin = function(y){};" + "Bar.inherits(Foo);\n" + "Bar.mixin(Baz);\n"); } public void testSuperclassMismatch1() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function() {};\n" + "/** @constructor \n @extends Object */ var Bar = function() {};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);\n", "Missing @extends tag on type Bar"); } public void testSuperclassMismatch2() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes("/** @constructor */ var Foo = function(){};\n" + "/** @constructor */ var Bar = function(){};\n" + "Bar.inherits = function(x){};" + "Bar.inherits(Foo);", "Missing @extends tag on type Bar"); } public void testSuperClassDefinedAfterSubClass1() throws Exception { testTypes( "/** @constructor \n * @extends {Base} */ function A() {}" + "/** @constructor \n * @extends {Base} */ function B() {}" + "/** @constructor */ function Base() {}" + "/** @param {A|B} x \n * @return {B|A} */ " + "function foo(x) { return x; }"); } public void testSuperClassDefinedAfterSubClass2() throws Exception { testTypes( "/** @constructor \n * @extends {Base} */ function A() {}" + "/** @constructor \n * @extends {Base} */ function B() {}" + "/** @param {A|B} x \n * @return {B|A} */ " + "function foo(x) { return x; }" + "/** @constructor */ function Base() {}"); } public void testDirectPrototypeAssignment1() throws Exception { testTypes( "/** @constructor */ function Base() {}" + "Base.prototype.foo = 3;" + "/** @constructor \n * @extends {Base} */ function A() {}" + "A.prototype = new Base();" + "/** @return {string} */ function foo() { return (new A).foo; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testDirectPrototypeAssignment2() throws Exception { // This ensures that we don't attach property 'foo' onto the Base // instance object. testTypes( "/** @constructor */ function Base() {}" + "/** @constructor \n * @extends {Base} */ function A() {}" + "A.prototype = new Base();" + "A.prototype.foo = 3;" + "/** @return {string} */ function foo() { return (new Base).foo; }"); } public void testDirectPrototypeAssignment3() throws Exception { // This verifies that the compiler doesn't crash if the user // overwrites the prototype of a global variable in a local scope. testTypes( "/** @constructor */ var MainWidgetCreator = function() {};" + "/** @param {Function} ctor */" + "function createMainWidget(ctor) {" + " /** @constructor */ function tempCtor() {};" + " tempCtor.prototype = ctor.prototype;" + " MainWidgetCreator.superClass_ = ctor.prototype;" + " MainWidgetCreator.prototype = new tempCtor();" + "}"); } public void testGoodImplements1() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n * @constructor */function f() {}"); } public void testGoodImplements2() throws Exception { testTypes("/** @interface */function Base1() {}\n" + "/** @interface */function Base2() {}\n" + "/** @constructor\n" + " * @implements {Base1}\n" + " * @implements {Base2}\n" + " */ function derived() {}"); } public void testGoodImplements3() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @constructor \n @implements {Disposable} */function f() {}"); } public void testGoodImplements4() throws Exception { testTypes("var goog = {};" + "/** @type {!Function} */" + "goog.abstractMethod = function() {};" + "/** @interface */\n" + "goog.Disposable = goog.abstractMethod;" + "goog.Disposable.prototype.dispose = goog.abstractMethod;" + "/** @implements {goog.Disposable}\n * @constructor */" + "goog.SubDisposable = function() {};" + "/** @inheritDoc */ " + "goog.SubDisposable.prototype.dispose = function() {};"); } public void testGoodImplements5() throws Exception { testTypes( "/** @interface */\n" + "goog.Disposable = function() {};" + "/** @type {Function} */" + "goog.Disposable.prototype.dispose = function() {};" + "/** @implements {goog.Disposable}\n * @constructor */" + "goog.SubDisposable = function() {};" + "/** @param {number} key \n @override */ " + "goog.SubDisposable.prototype.dispose = function(key) {};"); } public void testGoodImplements6() throws Exception { testTypes( "var myNullFunction = function() {};" + "/** @interface */\n" + "goog.Disposable = function() {};" + "/** @return {number} */" + "goog.Disposable.prototype.dispose = myNullFunction;" + "/** @implements {goog.Disposable}\n * @constructor */" + "goog.SubDisposable = function() {};" + "/** @return {number} \n @override */ " + "goog.SubDisposable.prototype.dispose = function() { return 0; };"); } public void testGoodImplements7() throws Exception { testTypes( "var myNullFunction = function() {};" + "/** @interface */\n" + "goog.Disposable = function() {};" + "/** @return {number} */" + "goog.Disposable.prototype.dispose = function() {};" + "/** @implements {goog.Disposable}\n * @constructor */" + "goog.SubDisposable = function() {};" + "/** @return {number} \n @override */ " + "goog.SubDisposable.prototype.dispose = function() { return 0; };"); } public void testBadImplements1() throws Exception { testTypes("/** @interface */function Base1() {}\n" + "/** @interface */function Base2() {}\n" + "/** @constructor\n" + " * @implements {nonExistent}\n" + " * @implements {Base2}\n" + " */ function derived() {}", "Bad type annotation. Unknown type nonExistent"); } public void testBadImplements2() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n */function f() {}", "@implements used without @constructor for f"); } public void testBadImplements3() throws Exception { testTypes( "var goog = {};" + "/** @type {!Function} */ goog.abstractMethod = function(){};" + "/** @interface */ var Disposable = goog.abstractMethod;" + "Disposable.prototype.method = goog.abstractMethod;" + "/** @implements {Disposable}\n * @constructor */function f() {}", "property method on interface Disposable is not implemented by type f"); } public void testBadImplements4() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @implements {Disposable}\n * @interface */function f() {}", "f cannot implement this type; an interface can only extend, " + "but not implement interfaces"); } public void testBadImplements5() throws Exception { testTypes("/** @interface */function Disposable() {}\n" + "/** @type {number} */ Disposable.prototype.bar = function() {};", "assignment to property bar of Disposable.prototype\n" + "found : function (): undefined\n" + "required: number"); } public void testBadImplements6() throws Exception { testClosureTypesMultipleWarnings( "/** @interface */function Disposable() {}\n" + "/** @type {function()} */ Disposable.prototype.bar = 3;", Lists.newArrayList( "assignment to property bar of Disposable.prototype\n" + "found : number\n" + "required: function (): ?", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod")); } public void testConstructorClassTemplate() throws Exception { testTypes("/** @constructor \n @classTemplate S,T */ function A() {}\n"); } // TODO(johnlenz): Check for bad usage public void disable_testBadClassTemplate() throws Exception { testTypes("/** @classTemplate T */function A() {}", "Bad type annotation. Unknown type nonExistent"); } public void testInterfaceExtends() throws Exception { testTypes("/** @interface */function A() {}\n" + "/** @interface \n * @extends {A} */function B() {}\n" + "/** @constructor\n" + " * @implements {B}\n" + " */ function derived() {}"); } public void testBadInterfaceExtends1() throws Exception { testTypes("/** @interface \n * @extends {nonExistent} */function A() {}", "Bad type annotation. Unknown type nonExistent"); } public void testBadInterfaceExtendsNonExistentInterfaces() throws Exception { String js = "/** @interface \n" + " * @extends {nonExistent1} \n" + " * @extends {nonExistent2} \n" + " */function A() {}"; String[] expectedWarnings = { "Bad type annotation. Unknown type nonExistent1", "Bad type annotation. Unknown type nonExistent2" }; testTypes(js, expectedWarnings); } public void testBadInterfaceExtends2() throws Exception { testTypes("/** @constructor */function A() {}\n" + "/** @interface \n * @extends {A} */function B() {}", "B cannot extend this type; interfaces can only extend interfaces"); } public void testBadInterfaceExtends3() throws Exception { testTypes("/** @interface */function A() {}\n" + "/** @constructor \n * @extends {A} */function B() {}", "B cannot extend this type; constructors can only extend constructors"); } public void testBadInterfaceExtends4() throws Exception { // TODO(user): This should be detected as an error. Even if we enforce // that A cannot be used in the assignment, we should still detect the // inheritance chain as invalid. testTypes("/** @interface */function A() {}\n" + "/** @constructor */function B() {}\n" + "B.prototype = A;"); } public void testBadInterfaceExtends5() throws Exception { // TODO(user): This should be detected as an error. Even if we enforce // that A cannot be used in the assignment, we should still detect the // inheritance chain as invalid. testTypes("/** @constructor */function A() {}\n" + "/** @interface */function B() {}\n" + "B.prototype = A;"); } public void testBadImplementsAConstructor() throws Exception { testTypes("/** @constructor */function A() {}\n" + "/** @constructor \n * @implements {A} */function B() {}", "can only implement interfaces"); } public void testBadImplementsNonInterfaceType() throws Exception { testTypes("/** @constructor \n * @implements {Boolean} */function B() {}", "can only implement interfaces"); } public void testBadImplementsNonObjectType() throws Exception { testTypes("/** @constructor \n * @implements {string} */function S() {}", "can only implement interfaces"); } public void testInterfaceAssignment1() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {!I} */var i = t;"); } public void testInterfaceAssignment2() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor */var T = function() {};\n" + "var t = new T();\n" + "/** @type {!I} */var i = t;", "initializing variable\n" + "found : T\n" + "required: I"); } public void testInterfaceAssignment3() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {I|number} */var i = t;"); } public void testInterfaceAssignment4() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1} */var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1|I2} */var i = t;"); } public void testInterfaceAssignment5() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1}\n@implements {I2}*/" + "var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1} */var i1 = t;\n" + "/** @type {I2} */var i2 = t;\n"); } public void testInterfaceAssignment6() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I1} */var T = function() {};\n" + "/** @type {!I1} */var i1 = new T();\n" + "/** @type {!I2} */var i2 = i1;\n", "initializing variable\n" + "found : I1\n" + "required: I2"); } public void testInterfaceAssignment7() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface\n@extends {I1}*/var I2 = function() {};\n" + "/** @constructor\n@implements {I2}*/var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1} */var i1 = t;\n" + "/** @type {I2} */var i2 = t;\n" + "i1 = i2;\n"); } public void testInterfaceAssignment8() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @type {I} */var i;\n" + "/** @type {Object} */var o = i;\n" + "new Object().prototype = i.prototype;"); } public void testInterfaceAssignment9() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @return {I?} */function f() { return null; }\n" + "/** @type {!I} */var i = f();\n", "initializing variable\n" + "found : (I|null)\n" + "required: I"); } public void testInterfaceAssignment10() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor\n@implements {I2} */var T = function() {};\n" + "/** @return {!I1|!I2} */function f() { return new T(); }\n" + "/** @type {!I1} */var i1 = f();\n", "initializing variable\n" + "found : (I1|I2)\n" + "required: I1"); } public void testInterfaceAssignment11() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */var I2 = function() {};\n" + "/** @constructor */var T = function() {};\n" + "/** @return {!I1|!I2|!T} */function f() { return new T(); }\n" + "/** @type {!I1} */var i1 = f();\n", "initializing variable\n" + "found : (I1|I2|T)\n" + "required: I1"); } public void testInterfaceAssignment12() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements{I}*/var T1 = function() {};\n" + "/** @constructor\n@extends {T1}*/var T2 = function() {};\n" + "/** @return {I} */function f() { return new T2(); }"); } public void testInterfaceAssignment13() throws Exception { testTypes("/** @interface */var I = function() {};\n" + "/** @constructor\n@implements {I}*/var T = function() {};\n" + "/** @constructor */function Super() {};\n" + "/** @return {I} */Super.prototype.foo = " + "function() { return new T(); };\n" + "/** @constructor\n@extends {Super} */function Sub() {}\n" + "/** @override\n@return {T} */Sub.prototype.foo = " + "function() { return new T(); };\n"); } public void testGetprop1() throws Exception { testTypes("/** @return {void}*/function foo(){foo().bar;}", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testGetprop2() throws Exception { testTypes("var x = null; x.alert();", "No properties on this expression\n" + "found : null\n" + "required: Object"); } public void testGetprop3() throws Exception { testTypes( "/** @constructor */ " + "function Foo() { /** @type {?Object} */ this.x = null; }" + "Foo.prototype.initX = function() { this.x = {foo: 1}; };" + "Foo.prototype.bar = function() {" + " if (this.x == null) { this.initX(); alert(this.x.foo); }" + "};"); } public void testGetprop4() throws Exception { testTypes("var x = null; x.prop = 3;", "No properties on this expression\n" + "found : null\n" + "required: Object"); } public void testSetprop1() throws Exception { // Create property on struct in the constructor testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() { this.x = 123; }"); } public void testSetprop2() throws Exception { // Create property on struct outside the constructor testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "(new Foo()).x = 123;", "Cannot add a property to a struct instance " + "after it is constructed."); } public void testSetprop3() throws Exception { // Create property on struct outside the constructor testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "(function() { (new Foo()).x = 123; })();", "Cannot add a property to a struct instance " + "after it is constructed."); } public void testSetprop4() throws Exception { // Assign to existing property of struct outside the constructor testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() { this.x = 123; }\n" + "(new Foo()).x = \"asdf\";"); } public void testSetprop5() throws Exception { // Create a property on union that includes a struct testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "(true ? new Foo() : {}).x = 123;", "Cannot add a property to a struct instance " + "after it is constructed."); } public void testSetprop6() throws Exception { // Create property on struct in another constructor testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "/**\n" + " * @constructor\n" + " * @param{Foo} f\n" + " */\n" + "function Bar(f) { f.x = 123; }", "Cannot add a property to a struct instance " + "after it is constructed."); } public void testSetprop7() throws Exception { //Bug b/c we require THIS when creating properties on structs for simplicity testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {\n" + " var t = this;\n" + " t.x = 123;\n" + "}", "Cannot add a property to a struct instance " + "after it is constructed."); } public void testSetprop8() throws Exception { // Create property on struct using DEC testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "(new Foo()).x--;", new String[] { "Property x never defined on Foo", "Cannot add a property to a struct instance " + "after it is constructed." }); } public void testSetprop9() throws Exception { // Create property on struct using ASSIGN_ADD testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "(new Foo()).x += 123;", new String[] { "Property x never defined on Foo", "Cannot add a property to a struct instance " + "after it is constructed." }); } public void testSetprop10() throws Exception { // Create property on object literal that is a struct testTypes("/** \n" + " * @constructor \n" + " * @struct \n" + " */ \n" + "function Square(side) { \n" + " this.side = side; \n" + "} \n" + "Square.prototype = /** @struct */ {\n" + " area: function() { return this.side * this.side; }\n" + "};\n" + "Square.prototype.id = function(x) { return x; };"); } public void testSetprop11() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "/** @constructor */\n" + "function Bar() {}\n" + "Bar.prototype = new Foo();\n" + "Bar.prototype.someprop = 123;"); } public void testSetprop12() throws Exception { // Create property on a constructor of structs (which isn't itself a struct) testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "Foo.someprop = 123;"); } public void testGetpropDict1() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1(){ this['prop'] = 123; }" + "/** @param{Dict1} x */" + "function takesDict(x) { return x.prop; }", "Cannot do '.' access on a dict"); } public void testGetpropDict2() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1(){ this['prop'] = 123; }" + "/**\n" + " * @constructor\n" + " * @extends {Dict1}\n" + " */" + "function Dict1kid(){ this['prop'] = 123; }" + "/** @param{Dict1kid} x */" + "function takesDict(x) { return x.prop; }", "Cannot do '.' access on a dict"); } public void testGetpropDict3() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1() { this['prop'] = 123; }" + "/** @constructor */" + "function NonDict() { this.prop = 321; }" + "/** @param{(NonDict|Dict1)} x */" + "function takesDict(x) { return x.prop; }", "Cannot do '.' access on a dict"); } public void testGetpropDict4() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1() { this['prop'] = 123; }" + "/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1() { this.prop = 123; }" + "/** @param{(Struct1|Dict1)} x */" + "function takesNothing(x) { return x.prop; }", "Cannot do '.' access on a dict"); } public void testGetpropDict5() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1(){ this.prop = 123; }", "Cannot do '.' access on a dict"); } public void testGetpropDict6() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @dict\n" + " */\n" + "function Foo() {}\n" + "function Bar() {}\n" + "Bar.prototype = new Foo();\n" + "Bar.prototype.someprop = 123;\n", "Cannot do '.' access on a dict"); } public void testGetpropDict7() throws Exception { testTypes("(/** @dict */ {'x': 123}).x = 321;", "Cannot do '.' access on a dict"); } public void testGetelemStruct1() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1(){ this.prop = 123; }" + "/** @param{Struct1} x */" + "function takesStruct(x) {" + " var z = x;" + " return z['prop'];" + "}", "Cannot do '[]' access on a struct"); } public void testGetelemStruct2() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1(){ this.prop = 123; }" + "/**\n" + " * @constructor\n" + " * @extends {Struct1}" + " */" + "function Struct1kid(){ this.prop = 123; }" + "/** @param{Struct1kid} x */" + "function takesStruct2(x) { return x['prop']; }", "Cannot do '[]' access on a struct"); } public void testGetelemStruct3() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1(){ this.prop = 123; }" + "/**\n" + " * @constructor\n" + " * @extends {Struct1}\n" + " */" + "function Struct1kid(){ this.prop = 123; }" + "var x = (new Struct1kid())['prop'];", "Cannot do '[]' access on a struct"); } public void testGetelemStruct4() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1() { this.prop = 123; }" + "/** @constructor */" + "function NonStruct() { this.prop = 321; }" + "/** @param{(NonStruct|Struct1)} x */" + "function takesStruct(x) { return x['prop']; }", "Cannot do '[]' access on a struct"); } public void testGetelemStruct5() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Struct1() { this.prop = 123; }" + "/**\n" + " * @constructor\n" + " * @dict\n" + " */" + "function Dict1() { this['prop'] = 123; }" + "/** @param{(Struct1|Dict1)} x */" + "function takesNothing(x) { return x['prop']; }", "Cannot do '[]' access on a struct"); } public void testGetelemStruct6() throws Exception { // By casting Bar to Foo, the illegal bracket access is not detected testTypes("/** @interface */ function Foo(){}\n" + "/**\n" + " * @constructor\n" + " * @struct\n" + " * @implements {Foo}\n" + " */" + "function Bar(){ this.x = 123; }\n" + "var z = /** @type {Foo} */(new Bar)['x'];"); } public void testGetelemStruct7() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Foo() {}\n" + "/** @constructor */\n" + "function Bar() {}\n" + "Bar.prototype = new Foo();\n" + "Bar.prototype['someprop'] = 123;\n", "Cannot do '[]' access on a struct"); } public void testInOnStruct() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Foo() {}\n" + "if ('prop' in (new Foo())) {}", "Cannot use the IN operator with structs"); } public void testForinOnStruct() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */" + "function Foo() {}\n" + "for (var prop in (new Foo())) {}", "Cannot use the IN operator with structs"); } public void testArrayAccess1() throws Exception { testTypes("var a = []; var b = a['hi'];"); } public void testArrayAccess2() throws Exception { testTypes("var a = []; var b = a[[1,2]];", "array access\n" + "found : Array\n" + "required: number"); } public void testArrayAccess3() throws Exception { testTypes("var bar = [];" + "/** @return {void} */function baz(){};" + "var foo = bar[baz()];", "array access\n" + "found : undefined\n" + "required: number"); } public void testArrayAccess4() throws Exception { testTypes("/**@return {!Array}*/function foo(){};var bar = foo()[foo()];", "array access\n" + "found : Array\n" + "required: number"); } public void testArrayAccess6() throws Exception { testTypes("var bar = null[1];", "only arrays or objects can be accessed\n" + "found : null\n" + "required: Object"); } public void testArrayAccess7() throws Exception { testTypes("var bar = void 0; bar[0];", "only arrays or objects can be accessed\n" + "found : undefined\n" + "required: Object"); } public void testArrayAccess8() throws Exception { // Verifies that we don't emit two warnings, because // the var has been dereferenced after the first one. testTypes("var bar = void 0; bar[0]; bar[1];", "only arrays or objects can be accessed\n" + "found : undefined\n" + "required: Object"); } public void testArrayAccess9() throws Exception { testTypes("/** @return {?Array} */ function f() { return []; }" + "f()[{}]", "array access\n" + "found : {}\n" + "required: number"); } public void testPropAccess() throws Exception { testTypes("/** @param {*} x */var f = function(x) {\n" + "var o = String(x);\n" + "if (typeof o['a'] != 'undefined') { return o['a']; }\n" + "return null;\n" + "};"); } public void testPropAccess2() throws Exception { testTypes("var bar = void 0; bar.baz;", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testPropAccess3() throws Exception { // Verifies that we don't emit two warnings, because // the var has been dereferenced after the first one. testTypes("var bar = void 0; bar.baz; bar.bax;", "No properties on this expression\n" + "found : undefined\n" + "required: Object"); } public void testPropAccess4() throws Exception { testTypes("/** @param {*} x */ function f(x) { return x['hi']; }"); } public void testSwitchCase1() throws Exception { testTypes("/**@type number*/var a;" + "/**@type string*/var b;" + "switch(a){case b:;}", "case expression doesn't match switch\n" + "found : string\n" + "required: number"); } public void testSwitchCase2() throws Exception { testTypes("var a = null; switch (typeof a) { case 'foo': }"); } public void testVar1() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @type {(string,null)} */var a = null"); assertTypeEquals(createUnionType(STRING_TYPE, NULL_TYPE), p.scope.getVar("a").getType()); } public void testVar2() throws Exception { testTypes("/** @type {Function} */ var a = function(){}"); } public void testVar3() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = 3;"); assertTypeEquals(NUMBER_TYPE, p.scope.getVar("a").getType()); } public void testVar4() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var a = 3; a = 'string';"); assertTypeEquals(createUnionType(STRING_TYPE, NUMBER_TYPE), p.scope.getVar("a").getType()); } public void testVar5() throws Exception { testTypes("var goog = {};" + "/** @type string */goog.foo = 'hello';" + "/** @type number */var a = goog.foo;", "initializing variable\n" + "found : string\n" + "required: number"); } public void testVar6() throws Exception { testTypes( "function f() {" + " return function() {" + " /** @type {!Date} */" + " var a = 7;" + " };" + "}", "initializing variable\n" + "found : number\n" + "required: Date"); } public void testVar7() throws Exception { testTypes("/** @type number */var a, b;", "declaration of multiple variables with shared type information"); } public void testVar8() throws Exception { testTypes("var a, b;"); } public void testVar9() throws Exception { testTypes("/** @enum */var a;", "enum initializer must be an object literal or an enum"); } public void testVar10() throws Exception { testTypes("/** @type !Number */var foo = 'abc';", "initializing variable\n" + "found : string\n" + "required: Number"); } public void testVar11() throws Exception { testTypes("var /** @type !Date */foo = 'abc';", "initializing variable\n" + "found : string\n" + "required: Date"); } public void testVar12() throws Exception { testTypes("var /** @type !Date */foo = 'abc', " + "/** @type !RegExp */bar = 5;", new String[] { "initializing variable\n" + "found : string\n" + "required: Date", "initializing variable\n" + "found : number\n" + "required: RegExp"}); } public void testVar13() throws Exception { // this caused an NPE testTypes("var /** @type number */a,a;"); } public void testVar14() throws Exception { testTypes("/** @return {number} */ function f() { var x; return x; }", "inconsistent return type\n" + "found : undefined\n" + "required: number"); } public void testVar15() throws Exception { testTypes("/** @return {number} */" + "function f() { var x = x || {}; return x; }", "inconsistent return type\n" + "found : {}\n" + "required: number"); } public void testAssign1() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 'hello';", "assignment to property foo of goog\n" + "found : string\n" + "required: number"); } public void testAssign2() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 3;" + "goog.foo = 'hello';", "assignment to property foo of goog\n" + "found : string\n" + "required: number"); } public void testAssign3() throws Exception { testTypes("var goog = {};" + "/** @type number */goog.foo = 3;" + "goog.foo = 4;"); } public void testAssign4() throws Exception { testTypes("var goog = {};" + "goog.foo = 3;" + "goog.foo = 'hello';"); } public void testAssignInference() throws Exception { testTypes( "/**" + " * @param {Array} x" + " * @return {number}" + " */" + "function f(x) {" + " var y = null;" + " y = x[0];" + " if (y == null) { return 4; } else { return 6; }" + "}"); } public void testOr1() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "a + b || undefined;"); } public void testOr2() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "/** @type number */var c = a + b || undefined;", "initializing variable\n" + "found : (number|undefined)\n" + "required: number"); } public void testOr3() throws Exception { testTypes("/** @type {(number, undefined)} */var a;" + "/** @type number */var c = a || 3;"); } /** * Test that type inference continues with the right side, * when no short-circuiting is possible. * See bugid 1205387 for more details. */ public void testOr4() throws Exception { testTypes("/**@type {number} */var x;x=null || \"a\";", "assignment\n" + "found : string\n" + "required: number"); } /** * @see #testOr4() */ public void testOr5() throws Exception { testTypes("/**@type {number} */var x;x=undefined || \"a\";", "assignment\n" + "found : string\n" + "required: number"); } public void testAnd1() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "a + b && undefined;"); } public void testAnd2() throws Exception { testTypes("/** @type number */var a;" + "/** @type number */var b;" + "/** @type number */var c = a + b && undefined;", "initializing variable\n" + "found : (number|undefined)\n" + "required: number"); } public void testAnd3() throws Exception { testTypes("/** @type {(!Array, undefined)} */var a;" + "/** @type number */var c = a && undefined;", "initializing variable\n" + "found : undefined\n" + "required: number"); } public void testAnd4() throws Exception { testTypes("/** @param {number} x */function f(x){};\n" + "/** @type null */var x; /** @type {number?} */var y;\n" + "if (x && y) { f(y) }"); } public void testAnd5() throws Exception { testTypes("/** @param {number} x\n@param {string} y*/function f(x,y){};\n" + "/** @type {number?} */var x; /** @type {string?} */var y;\n" + "if (x && y) { f(x, y) }"); } public void testAnd6() throws Exception { testTypes("/** @param {number} x */function f(x){};\n" + "/** @type {number|undefined} */var x;\n" + "if (x && f(x)) { f(x) }"); } public void testAnd7() throws Exception { // TODO(user): a deterministic warning should be generated for this // case since x && x is always false. The implementation of this requires // a more precise handling of a null value within a variable's type. // Currently, a null value defaults to ? which passes every check. testTypes("/** @type null */var x; if (x && x) {}"); } public void testHook() throws Exception { testTypes("/**@return {void}*/function foo(){ var x=foo()?a:b; }"); } public void testHookRestrictsType1() throws Exception { testTypes("/** @return {(string,null)} */" + "function f() { return null;}" + "/** @type {(string,null)} */ var a = f();" + "/** @type string */" + "var b = a ? a : 'default';"); } public void testHookRestrictsType2() throws Exception { testTypes("/** @type {String} */" + "var a = null;" + "/** @type null */" + "var b = a ? null : a;"); } public void testHookRestrictsType3() throws Exception { testTypes("/** @type {String} */" + "var a;" + "/** @type null */" + "var b = (!a) ? a : null;"); } public void testHookRestrictsType4() throws Exception { testTypes("/** @type {(boolean,undefined)} */" + "var a;" + "/** @type boolean */" + "var b = a != null ? a : true;"); } public void testHookRestrictsType5() throws Exception { testTypes("/** @type {(boolean,undefined)} */" + "var a;" + "/** @type {(undefined)} */" + "var b = a == null ? a : undefined;"); } public void testHookRestrictsType6() throws Exception { testTypes("/** @type {(number,null,undefined)} */" + "var a;" + "/** @type {number} */" + "var b = a == null ? 5 : a;"); } public void testHookRestrictsType7() throws Exception { testTypes("/** @type {(number,null,undefined)} */" + "var a;" + "/** @type {number} */" + "var b = a == undefined ? 5 : a;"); } public void testWhileRestrictsType1() throws Exception { testTypes("/** @param {null} x */ function g(x) {}" + "/** @param {number?} x */\n" + "function f(x) {\n" + "while (x) {\n" + "if (g(x)) { x = 1; }\n" + "x = x-1;\n}\n}", "actual parameter 1 of g does not match formal parameter\n" + "found : number\n" + "required: null"); } public void testWhileRestrictsType2() throws Exception { testTypes("/** @param {number?} x\n@return {number}*/\n" + "function f(x) {\n/** @type {number} */var y = 0;" + "while (x) {\n" + "y = x;\n" + "x = x-1;\n}\n" + "return y;}"); } public void testHigherOrderFunctions1() throws Exception { testTypes( "/** @type {function(number)} */var f;" + "f(true);", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testHigherOrderFunctions2() throws Exception { testTypes( "/** @type {function():!Date} */var f;" + "/** @type boolean */var a = f();", "initializing variable\n" + "found : Date\n" + "required: boolean"); } public void testHigherOrderFunctions3() throws Exception { testTypes( "/** @type {function(this:Error):Date} */var f; new f", "cannot instantiate non-constructor"); } public void testHigherOrderFunctions4() throws Exception { testTypes( "/** @type {function(this:Error,...[number]):Date} */var f; new f", "cannot instantiate non-constructor"); } public void testHigherOrderFunctions5() throws Exception { testTypes( "/** @param {number} x */ function g(x) {}" + "/** @type {function(new:Error,...[number]):Date} */ var f;" + "g(new f());", "actual parameter 1 of g does not match formal parameter\n" + "found : Error\n" + "required: number"); } public void testConstructorAlias1() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @type {number} */ Foo.prototype.bar = 3;" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {string} */ function foo() { " + " return (new FooAlias()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias2() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @type {number} */ FooAlias.prototype.bar = 3;" + "/** @return {string} */ function foo() { " + " return (new Foo()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias3() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @type {number} */ Foo.prototype.bar = 3;" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {string} */ function foo() { " + " return (new FooAlias()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias4() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "var FooAlias = Foo;" + "/** @type {number} */ FooAlias.prototype.bar = 3;" + "/** @return {string} */ function foo() { " + " return (new Foo()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorAlias5() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {FooAlias} */ function foo() { " + " return new Foo(); }"); } public void testConstructorAlias6() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {Foo} */ function foo() { " + " return new FooAlias(); }"); } public void testConstructorAlias7() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @constructor */ goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias8() throws Exception { testTypes( "var goog = {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.Foo = function(x) {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(1); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias9() throws Exception { testTypes( "var goog = {};" + "/**\n * @param {number} x \n * @constructor */ " + "goog.Foo = function(x) {};" + "/** @constructor */ goog.FooAlias = goog.Foo;" + "/** @return {number} */ function foo() { " + " return new goog.FooAlias(1); }", "inconsistent return type\n" + "found : goog.Foo\n" + "required: number"); } public void testConstructorAlias10() throws Exception { testTypes( "/**\n * @param {number} x \n * @constructor */ " + "var Foo = function(x) {};" + "/** @constructor */ var FooAlias = Foo;" + "/** @return {number} */ function foo() { " + " return new FooAlias(1); }", "inconsistent return type\n" + "found : Foo\n" + "required: number"); } public void testClosure1() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|undefined} */var a;" + "/** @type string */" + "var b = goog.isDef(a) ? a : 'default';", null); } public void testClosure2() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string?} */var a;" + "/** @type string */" + "var b = goog.isNull(a) ? 'default' : a;", null); } public void testClosure3() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null|undefined} */var a;" + "/** @type string */" + "var b = goog.isDefAndNotNull(a) ? a : 'default';", null); } public void testClosure4() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|undefined} */var a;" + "/** @type string */" + "var b = !goog.isDef(a) ? 'default' : a;", null); } public void testClosure5() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string?} */var a;" + "/** @type string */" + "var b = !goog.isNull(a) ? a : 'default';", null); } public void testClosure6() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null|undefined} */var a;" + "/** @type string */" + "var b = !goog.isDefAndNotNull(a) ? 'default' : a;", null); } public void testClosure7() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string|null|undefined} */ var a = foo();" + "/** @type {number} */" + "var b = goog.asserts.assert(a);", "initializing variable\n" + "found : string\n" + "required: number"); } public void testReturn1() throws Exception { testTypes("/**@return {void}*/function foo(){ return 3; }", "inconsistent return type\n" + "found : number\n" + "required: undefined"); } public void testReturn2() throws Exception { testTypes("/**@return {!Number}*/function foo(){ return; }", "inconsistent return type\n" + "found : undefined\n" + "required: Number"); } public void testReturn3() throws Exception { testTypes("/**@return {!Number}*/function foo(){ return 'abc'; }", "inconsistent return type\n" + "found : string\n" + "required: Number"); } public void testReturn4() throws Exception { testTypes("/**@return {!Number}\n*/\n function a(){return new Array();}", "inconsistent return type\n" + "found : Array\n" + "required: Number"); } public void testReturn5() throws Exception { testTypes("/** @param {number} n\n" + "@constructor */function n(n){return};"); } public void testReturn6() throws Exception { testTypes( "/** @param {number} opt_a\n@return {string} */" + "function a(opt_a) { return opt_a }", "inconsistent return type\n" + "found : (number|undefined)\n" + "required: string"); } public void testReturn7() throws Exception { testTypes("/** @constructor */var A = function() {};\n" + "/** @constructor */var B = function() {};\n" + "/** @return {!B} */A.f = function() { return 1; };", "inconsistent return type\n" + "found : number\n" + "required: B"); } public void testReturn8() throws Exception { testTypes("/** @constructor */var A = function() {};\n" + "/** @constructor */var B = function() {};\n" + "/** @return {!B} */A.prototype.f = function() { return 1; };", "inconsistent return type\n" + "found : number\n" + "required: B"); } public void testInferredReturn1() throws Exception { testTypes( "function f() {} /** @param {number} x */ function g(x) {}" + "g(f());", "actual parameter 1 of g does not match formal parameter\n" + "found : undefined\n" + "required: number"); } public void testInferredReturn2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {}; " + "/** @param {number} x */ function g(x) {}" + "g((new Foo()).bar());", "actual parameter 1 of g does not match formal parameter\n" + "found : undefined\n" + "required: number"); } public void testInferredReturn3() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {}; " + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {number} \n * @override */ " + "SubFoo.prototype.bar = function() { return 3; }; ", "mismatch of the bar property type and the type of the property " + "it overrides from superclass Foo\n" + "original: function (this:Foo): undefined\n" + "override: function (this:SubFoo): number"); } public void testInferredReturn4() throws Exception { // By design, this throws a warning. if you want global x to be // defined to some other type of function, then you need to declare it // as a greater type. testTypes( "var x = function() {};" + "x = /** @type {function(): number} */ (function() { return 3; });", "assignment\n" + "found : function (): number\n" + "required: function (): undefined"); } public void testInferredReturn5() throws Exception { // If x is local, then the function type is not declared. testTypes( "/** @return {string} */" + "function f() {" + " var x = function() {};" + " x = /** @type {function(): number} */ (function() { return 3; });" + " return x();" + "}", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testInferredReturn6() throws Exception { testTypes( "/** @return {string} */" + "function f() {" + " var x = function() {};" + " if (f()) " + " x = /** @type {function(): number} */ " + " (function() { return 3; });" + " return x();" + "}", "inconsistent return type\n" + "found : (number|undefined)\n" + "required: string"); } public void testInferredReturn7() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = function(x) {};" + "Foo.prototype.bar = function(x) { return 3; };", "inconsistent return type\n" + "found : number\n" + "required: undefined"); } public void testInferredReturn8() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @param {number} x */ SubFoo.prototype.bar = " + " function(x) { return 3; }", "inconsistent return type\n" + "found : number\n" + "required: undefined"); } public void testInferredParam1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = function(x) {};" + "/** @param {string} x */ function f(x) {}" + "Foo.prototype.bar = function(y) { f(y); };", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testInferredParam2() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {void} */ SubFoo.prototype.bar = " + " function(x) { f(x); }", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testInferredParam3() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "/** @param {number=} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {void} */ SubFoo.prototype.bar = " + " function(x) { f(x); }; (new SubFoo()).bar();", "actual parameter 1 of f does not match formal parameter\n" + "found : (number|undefined)\n" + "required: string"); } public void testInferredParam4() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "/** @param {...number} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {void} */ SubFoo.prototype.bar = " + " function(x) { f(x); }; (new SubFoo()).bar();", "actual parameter 1 of f does not match formal parameter\n" + "found : (number|undefined)\n" + "required: string"); } public void testInferredParam5() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "/** @param {...number} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @param {number=} x \n * @param {...number} y */ " + "SubFoo.prototype.bar = " + " function(x, y) { f(x); }; (new SubFoo()).bar();", "actual parameter 1 of f does not match formal parameter\n" + "found : (number|undefined)\n" + "required: string"); } public void testInferredParam6() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes( "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "/** @param {number=} x */ Foo.prototype.bar = function(x) {};" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @param {number=} x \n * @param {number=} y */ " + "SubFoo.prototype.bar = " + " function(x, y) { f(y); };", "actual parameter 1 of f does not match formal parameter\n" + "found : (number|undefined)\n" + "required: string"); } public void testInferredParam7() throws Exception { testTypes( "/** @param {string} x */ function f(x) {}" + "var bar = /** @type {function(number=,number=)} */ (" + " function(x, y) { f(y); });", "actual parameter 1 of f does not match formal parameter\n" + "found : (number|undefined)\n" + "required: string"); } public void testOverriddenParams1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {...?} var_args */" + "Foo.prototype.bar = function(var_args) {};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @param {number} x\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function(x) {};"); } public void testOverriddenParams2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {function(...[?])} */" + "Foo.prototype.bar = function(var_args) {};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @type {function(number)}\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function(x) {};"); } public void testOverriddenParams3() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {...number} var_args */" + "Foo.prototype.bar = function(var_args) { };" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @param {number} x\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function(x) {};", "mismatch of the bar property type and the type of the " + "property it overrides from superclass Foo\n" + "original: function (this:Foo, ...[number]): undefined\n" + "override: function (this:SubFoo, number): undefined"); } public void testOverriddenParams4() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {function(...[number])} */" + "Foo.prototype.bar = function(var_args) {};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @type {function(number)}\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function(x) {};", "mismatch of the bar property type and the type of the " + "property it overrides from superclass Foo\n" + "original: function (...[number]): ?\n" + "override: function (number): ?"); } public void testOverriddenParams5() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */" + "Foo.prototype.bar = function(x) { };" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function() {};" + "(new SubFoo()).bar();"); } public void testOverriddenParams6() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */" + "Foo.prototype.bar = function(x) { };" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = function() {};" + "(new SubFoo()).bar(true);", "actual parameter 1 of SubFoo.prototype.bar " + "does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testOverriddenReturn1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @return {Object} */ Foo.prototype.bar = " + " function() { return {}; };" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {SubFoo}\n * @override */ SubFoo.prototype.bar = " + " function() { return new Foo(); }", "inconsistent return type\n" + "found : Foo\n" + "required: (SubFoo|null)"); } public void testOverriddenReturn2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @return {SubFoo} */ Foo.prototype.bar = " + " function() { return new SubFoo(); };" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "/** @return {Foo} x\n * @override */ SubFoo.prototype.bar = " + " function() { return new SubFoo(); }", "mismatch of the bar property type and the type of the " + "property it overrides from superclass Foo\n" + "original: function (this:Foo): (SubFoo|null)\n" + "override: function (this:SubFoo): (Foo|null)"); } public void testThis1() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){};" + "/** @return {number} */" + "goog.A.prototype.n = function() { return this };", "inconsistent return type\n" + "found : goog.A\n" + "required: number"); } public void testOverriddenProperty1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {Object} */" + "Foo.prototype.bar = {};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @type {Array}\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = [];"); } public void testOverriddenProperty2() throws Exception { testTypes( "/** @constructor */ function Foo() {" + " /** @type {Object} */" + " this.bar = {};" + "}" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/**\n" + " * @type {Array}\n" + " * @override\n" + " */" + "SubFoo.prototype.bar = [];"); } public void testOverriddenProperty3() throws Exception { testTypes( "/** @constructor */ function Foo() {" + "}" + "/** @type {string} */ Foo.prototype.data;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/** @type {string|Object} \n @override */ " + "SubFoo.prototype.data = null;", "mismatch of the data property type and the type " + "of the property it overrides from superclass Foo\n" + "original: string\n" + "override: (Object|null|string)"); } public void testOverriddenProperty4() throws Exception { // These properties aren't declared, so there should be no warning. testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = null;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "SubFoo.prototype.bar = 3;"); } public void testOverriddenProperty5() throws Exception { // An override should be OK if the superclass property wasn't declared. testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = null;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/** @override */ SubFoo.prototype.bar = 3;"); } public void testOverriddenProperty6() throws Exception { // The override keyword shouldn't be neccessary if the subclass property // is inferred. testTypes( "/** @constructor */ function Foo() {}" + "/** @type {?number} */ Foo.prototype.bar = null;" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "SubFoo.prototype.bar = 3;"); } public void testThis2() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " this.foo = null;" + "};" + "/** @return {number} */" + "goog.A.prototype.n = function() { return this.foo };", "inconsistent return type\n" + "found : null\n" + "required: number"); } public void testThis3() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " this.foo = null;" + " this.foo = 5;" + "};"); } public void testThis4() throws Exception { testTypes("var goog = {};" + "/** @constructor */goog.A = function(){" + " /** @type {string?} */this.foo = null;" + "};" + "/** @return {number} */goog.A.prototype.n = function() {" + " return this.foo };", "inconsistent return type\n" + "found : (null|string)\n" + "required: number"); } public void testThis5() throws Exception { testTypes("/** @this Date\n@return {number}*/function h() { return this }", "inconsistent return type\n" + "found : Date\n" + "required: number"); } public void testThis6() throws Exception { testTypes("var goog = {};" + "/** @constructor\n@return {!Date} */" + "goog.A = function(){ return this };", "inconsistent return type\n" + "found : goog.A\n" + "required: Date"); } public void testThis7() throws Exception { testTypes("/** @constructor */function A(){};" + "/** @return {number} */A.prototype.n = function() { return this };", "inconsistent return type\n" + "found : A\n" + "required: number"); } public void testThis8() throws Exception { testTypes("/** @constructor */function A(){" + " /** @type {string?} */this.foo = null;" + "};" + "/** @return {number} */A.prototype.n = function() {" + " return this.foo };", "inconsistent return type\n" + "found : (null|string)\n" + "required: number"); } public void testThis9() throws Exception { // In A.bar, the type of {@code this} is unknown. testTypes("/** @constructor */function A(){};" + "A.prototype.foo = 3;" + "/** @return {string} */ A.bar = function() { return this.foo; };"); } public void testThis10() throws Exception { // In A.bar, the type of {@code this} is inferred from the @this tag. testTypes("/** @constructor */function A(){};" + "A.prototype.foo = 3;" + "/** @this {A}\n@return {string} */" + "A.bar = function() { return this.foo; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testThis11() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @constructor */ function Ctor() {" + " /** @this {Date} */" + " this.method = function() {" + " f(this);" + " };" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : Date\n" + "required: number"); } public void testThis12() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @constructor */ function Ctor() {}" + "Ctor.prototype['method'] = function() {" + " f(this);" + "}", "actual parameter 1 of f does not match formal parameter\n" + "found : Ctor\n" + "required: number"); } public void testThis13() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @constructor */ function Ctor() {}" + "Ctor.prototype = {" + " method: function() {" + " f(this);" + " }" + "};", "actual parameter 1 of f does not match formal parameter\n" + "found : Ctor\n" + "required: number"); } public void testThis14() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(this.Object);", "actual parameter 1 of f does not match formal parameter\n" + "found : function (new:Object, *=): ?\n" + "required: number"); } public void testThisTypeOfFunction1() throws Exception { testTypes( "/** @type {function(this:Object)} */ function f() {}" + "f();"); } public void testThisTypeOfFunction2() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @type {function(this:F)} */ function f() {}" + "f();", "\"function (this:F): ?\" must be called with a \"this\" type"); } public void testThisTypeOfFunction3() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.bar = function() {};" + "var f = (new F()).bar; f();", "\"function (this:F): undefined\" must be called with a \"this\" type"); } public void testThisTypeOfFunction4() throws Exception { testTypes( "/** @constructor */ function F() {}" + "F.prototype.moveTo = function(x, y) {};" + "F.prototype.lineTo = function(x, y) {};" + "function demo() {" + " var path = new F();" + " var points = [[1,1], [2,2]];" + " for (var i = 0; i < points.length; i++) {" + " (i == 0 ? path.moveTo : path.lineTo)(" + " points[i][0], points[i][1]);" + " }" + "}", "\"function (this:F, ?, ?): undefined\" " + "must be called with a \"this\" type"); } public void testGlobalThis1() throws Exception { testTypes("/** @constructor */ function Window() {}" + "/** @param {string} msg */ " + "Window.prototype.alert = function(msg) {};" + "this.alert(3);", "actual parameter 1 of Window.prototype.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis2() throws Exception { // this.alert = 3 doesn't count as a declaration, so this isn't a warning. testTypes("/** @constructor */ function Bindow() {}" + "/** @param {string} msg */ " + "Bindow.prototype.alert = function(msg) {};" + "this.alert = 3;" + "(new Bindow()).alert(this.alert)"); } public void testGlobalThis2b() throws Exception { testTypes("/** @constructor */ function Bindow() {}" + "/** @param {string} msg */ " + "Bindow.prototype.alert = function(msg) {};" + "/** @return {number} */ this.alert = function() { return 3; };" + "(new Bindow()).alert(this.alert())", "actual parameter 1 of Bindow.prototype.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis3() throws Exception { testTypes( "/** @param {string} msg */ " + "function alert(msg) {};" + "this.alert(3);", "actual parameter 1 of global this.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis4() throws Exception { testTypes( "/** @param {string} msg */ " + "var alert = function(msg) {};" + "this.alert(3);", "actual parameter 1 of global this.alert " + "does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testGlobalThis5() throws Exception { testTypes( "function f() {" + " /** @param {string} msg */ " + " var alert = function(msg) {};" + "}" + "this.alert(3);", "Property alert never defined on global this"); } public void testGlobalThis6() throws Exception { testTypes( "/** @param {string} msg */ " + "var alert = function(msg) {};" + "var x = 3;" + "x = 'msg';" + "this.alert(this.x);"); } public void testGlobalThis7() throws Exception { testTypes( "/** @constructor */ function Window() {}" + "/** @param {Window} msg */ " + "var foo = function(msg) {};" + "foo(this);"); } public void testGlobalThis8() throws Exception { testTypes( "/** @constructor */ function Window() {}" + "/** @param {number} msg */ " + "var foo = function(msg) {};" + "foo(this);", "actual parameter 1 of foo does not match formal parameter\n" + "found : global this\n" + "required: number"); } public void testGlobalThis9() throws Exception { testTypes( // Window is not marked as a constructor, so the // inheritance doesn't happen. "function Window() {}" + "Window.prototype.alert = function() {};" + "this.alert();", "Property alert never defined on global this"); } public void testControlFlowRestrictsType1() throws Exception { testTypes("/** @return {String?} */ function f() { return null; }" + "/** @type {String?} */ var a = f();" + "/** @type String */ var b = new String('foo');" + "/** @type null */ var c = null;" + "if (a) {" + " b = a;" + "} else {" + " c = a;" + "}"); } public void testControlFlowRestrictsType2() throws Exception { testTypes("/** @return {(string,null)} */ function f() { return null; }" + "/** @type {(string,null)} */ var a = f();" + "/** @type string */ var b = 'foo';" + "/** @type null */ var c = null;" + "if (a) {" + " b = a;" + "} else {" + " c = a;" + "}", "assignment\n" + "found : (null|string)\n" + "required: null"); } public void testControlFlowRestrictsType3() throws Exception { testTypes("/** @type {(string,void)} */" + "var a;" + "/** @type string */" + "var b = 'foo';" + "if (a) {" + " b = a;" + "}"); } public void testControlFlowRestrictsType4() throws Exception { testTypes("/** @param {string} a */ function f(a){}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);"); } public void testControlFlowRestrictsType5() throws Exception { testTypes("/** @param {undefined} a */ function f(a){}" + "/** @type {(!Array,undefined)} */ var a;" + "a || f(a);"); } public void testControlFlowRestrictsType6() throws Exception { testTypes("/** @param {undefined} x */ function f(x) {}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: undefined"); } public void testControlFlowRestrictsType7() throws Exception { testTypes("/** @param {undefined} x */ function f(x) {}" + "/** @type {(string,undefined)} */ var a;" + "a && f(a);", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: undefined"); } public void testControlFlowRestrictsType8() throws Exception { testTypes("/** @param {undefined} a */ function f(a){}" + "/** @type {(!Array,undefined)} */ var a;" + "if (a || f(a)) {}"); } public void testControlFlowRestrictsType9() throws Exception { testTypes("/** @param {number?} x\n * @return {number}*/\n" + "var f = function(x) {\n" + "if (!x || x == 1) { return 1; } else { return x; }\n" + "};"); } public void testControlFlowRestrictsType10() throws Exception { // We should correctly infer that y will be (null|{}) because // the loop wraps around. testTypes("/** @param {number} x */ function f(x) {}" + "function g() {" + " var y = null;" + " for (var i = 0; i < 10; i++) {" + " f(y);" + " if (y != null) {" + " // y is None the first time it goes through this branch\n" + " } else {" + " y = {};" + " }" + " }" + "};", "actual parameter 1 of f does not match formal parameter\n" + "found : (null|{})\n" + "required: number"); } public void testControlFlowRestrictsType11() throws Exception { testTypes("/** @param {boolean} x */ function f(x) {}" + "function g() {" + " var y = null;" + " if (y != null) {" + " for (var i = 0; i < 10; i++) {" + " f(y);" + " }" + " }" + "};", "condition always evaluates to false\n" + "left : null\n" + "right: null"); } public void testSwitchCase3() throws Exception { testTypes("/** @type String */" + "var a = new String('foo');" + "switch (a) { case 'A': }"); } public void testSwitchCase4() throws Exception { testTypes("/** @type {(string,Null)} */" + "var a = 'foo';" + "switch (a) { case 'A':break; case null:break; }"); } public void testSwitchCase5() throws Exception { testTypes("/** @type {(String,Null)} */" + "var a = new String('foo');" + "switch (a) { case 'A':break; case null:break; }"); } public void testSwitchCase6() throws Exception { testTypes("/** @type {(Number,Null)} */" + "var a = new Number(5);" + "switch (a) { case 5:break; case null:break; }"); } public void testSwitchCase7() throws Exception { // This really tests the inference inside the case. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " switch (3) { case g(x.foo): return 3; }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testSwitchCase8() throws Exception { // This really tests the inference inside the switch clause. testTypes( "/**\n" + " * @param {number} x\n" + " * @return {number}\n" + " */\n" + "function g(x) { return 5; }" + "function f() {" + " var x = {};" + " x.foo = '3';" + " switch (g(x.foo)) { case 3: return 3; }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testNoTypeCheck1() throws Exception { testTypes("/** @notypecheck */function foo() { new 4 }"); } public void testNoTypeCheck2() throws Exception { testTypes("/** @notypecheck */var foo = function() { new 4 }"); } public void testNoTypeCheck3() throws Exception { testTypes("/** @notypecheck */var foo = function bar() { new 4 }"); } public void testNoTypeCheck4() throws Exception { testTypes("var foo;" + "/** @notypecheck */foo = function() { new 4 }"); } public void testNoTypeCheck5() throws Exception { testTypes("var foo;" + "foo = /** @notypecheck */function() { new 4 }"); } public void testNoTypeCheck6() throws Exception { testTypes("var foo;" + "/** @notypecheck */foo = function bar() { new 4 }"); } public void testNoTypeCheck7() throws Exception { testTypes("var foo;" + "foo = /** @notypecheck */function bar() { new 4 }"); } public void testNoTypeCheck8() throws Exception { testTypes("/** @fileoverview \n * @notypecheck */ var foo;" + "var bar = 3; /** @param {string} x */ function f(x) {} f(bar);"); } public void testNoTypeCheck9() throws Exception { testTypes("/** @notypecheck */ function g() { }" + " /** @type {string} */ var a = 1", "initializing variable\n" + "found : number\n" + "required: string" ); } public void testNoTypeCheck10() throws Exception { testTypes("/** @notypecheck */ function g() { }" + " function h() {/** @type {string} */ var a = 1}", "initializing variable\n" + "found : number\n" + "required: string" ); } public void testNoTypeCheck11() throws Exception { testTypes("/** @notypecheck */ function g() { }" + "/** @notypecheck */ function h() {/** @type {string} */ var a = 1}" ); } public void testNoTypeCheck12() throws Exception { testTypes("/** @notypecheck */ function g() { }" + "function h() {/** @type {string}\n * @notypecheck\n*/ var a = 1}" ); } public void testNoTypeCheck13() throws Exception { testTypes("/** @notypecheck */ function g() { }" + "function h() {/** @type {string}\n * @notypecheck\n*/ var a = 1;" + "/** @type {string}*/ var b = 1}", "initializing variable\n" + "found : number\n" + "required: string" ); } public void testNoTypeCheck14() throws Exception { testTypes("/** @fileoverview \n * @notypecheck */ function g() { }" + "g(1,2,3)"); } public void testImplicitCast() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;", "(new Element).innerHTML = new Array();", null, false); } public void testImplicitCastSubclassAccess() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "/** @constructor \n @extends Element */" + "function DIVElement() {};", "(new DIVElement).innerHTML = new Array();", null, false); } public void testImplicitCastNotInExterns() throws Exception { testTypes("/** @constructor */ function Element() {};\n" + "/** @type {string}\n" + " * @implicitCast */" + "Element.prototype.innerHTML;" + "(new Element).innerHTML = new Array();", new String[] { "Illegal annotation on innerHTML. @implicitCast may only be " + "used in externs.", "assignment to property innerHTML of Element\n" + "found : Array\n" + "required: string"}); } public void testNumberNode() throws Exception { Node n = typeCheck(Node.newNumber(0)); assertTypeEquals(NUMBER_TYPE, n.getJSType()); } public void testStringNode() throws Exception { Node n = typeCheck(Node.newString("hello")); assertTypeEquals(STRING_TYPE, n.getJSType()); } public void testBooleanNodeTrue() throws Exception { Node trueNode = typeCheck(new Node(Token.TRUE)); assertTypeEquals(BOOLEAN_TYPE, trueNode.getJSType()); } public void testBooleanNodeFalse() throws Exception { Node falseNode = typeCheck(new Node(Token.FALSE)); assertTypeEquals(BOOLEAN_TYPE, falseNode.getJSType()); } public void testUndefinedNode() throws Exception { Node p = new Node(Token.ADD); Node n = Node.newString(Token.NAME, "undefined"); p.addChildToBack(n); p.addChildToBack(Node.newNumber(5)); typeCheck(p); assertTypeEquals(VOID_TYPE, n.getJSType()); } public void testNumberAutoboxing() throws Exception { testTypes("/** @type Number */var a = 4;", "initializing variable\n" + "found : number\n" + "required: (Number|null)"); } public void testNumberUnboxing() throws Exception { testTypes("/** @type number */var a = new Number(4);", "initializing variable\n" + "found : Number\n" + "required: number"); } public void testStringAutoboxing() throws Exception { testTypes("/** @type String */var a = 'hello';", "initializing variable\n" + "found : string\n" + "required: (String|null)"); } public void testStringUnboxing() throws Exception { testTypes("/** @type string */var a = new String('hello');", "initializing variable\n" + "found : String\n" + "required: string"); } public void testBooleanAutoboxing() throws Exception { testTypes("/** @type Boolean */var a = true;", "initializing variable\n" + "found : boolean\n" + "required: (Boolean|null)"); } public void testBooleanUnboxing() throws Exception { testTypes("/** @type boolean */var a = new Boolean(false);", "initializing variable\n" + "found : Boolean\n" + "required: boolean"); } public void testIIFE1() throws Exception { testTypes( "var namespace = {};" + "/** @type {number} */ namespace.prop = 3;" + "(function(ns) {" + " ns.prop = true;" + "})(namespace);", "assignment to property prop of ns\n" + "found : boolean\n" + "required: number"); } public void testIIFE2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "(function(ctor) {" + " /** @type {boolean} */ ctor.prop = true;" + "})(Foo);" + "/** @return {number} */ function f() { return Foo.prop; }", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testIIFE3() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "(function(ctor) {" + " /** @type {boolean} */ ctor.prop = true;" + "})(Foo);" + "/** @param {number} x */ function f(x) {}" + "f(Foo.prop);", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testIIFE4() throws Exception { testTypes( "/** @const */ var namespace = {};" + "(function(ns) {" + " /**\n" + " * @constructor\n" + " * @param {number} x\n" + " */\n" + " ns.Ctor = function(x) {};" + "})(namespace);" + "new namespace.Ctor(true);", "actual parameter 1 of namespace.Ctor " + "does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testIIFE5() throws Exception { // TODO(nicksantos): This behavior is currently incorrect. // To handle this case properly, we'll need to change how we handle // type resolution. testTypes( "/** @const */ var namespace = {};" + "(function(ns) {" + " /**\n" + " * @constructor\n" + " */\n" + " ns.Ctor = function() {};" + " /** @type {boolean} */ ns.Ctor.prototype.bar = true;" + "})(namespace);" + "/** @param {namespace.Ctor} x\n" + " * @return {number} */ function f(x) { return x.bar; }", "Bad type annotation. Unknown type namespace.Ctor"); } public void testNotIIFE1() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @param {...?} x */ function g(x) {}" + "g(function(y) { f(y); }, true);"); } public void testIssue61() throws Exception { testTypes( "var ns = {};" + "(function() {" + " /** @param {string} b */" + " ns.a = function(b) {};" + "})();" + "function d() {" + " ns.a(123);" + "}", "actual parameter 1 of ns.a does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testIssue61b() throws Exception { testTypes( "var ns = {};" + "(function() {" + " /** @param {string} b */" + " ns.a = function(b) {};" + "})();" + "ns.a(123);", "actual parameter 1 of ns.a does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testIssue86() throws Exception { testTypes( "/** @interface */ function I() {}" + "/** @return {number} */ I.prototype.get = function(){};" + "/** @constructor \n * @implements {I} */ function F() {}" + "/** @override */ F.prototype.get = function() { return true; };", "inconsistent return type\n" + "found : boolean\n" + "required: number"); } public void testIssue124() throws Exception { testTypes( "var t = null;" + "function test() {" + " if (t != null) { t = null; }" + " t = 1;" + "}"); } public void testIssue124b() throws Exception { testTypes( "var t = null;" + "function test() {" + " if (t != null) { t = null; }" + " t = undefined;" + "}", "condition always evaluates to false\n" + "left : (null|undefined)\n" + "right: null"); } public void testIssue259() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @constructor */" + "var Clock = function() {" + " /** @constructor */" + " this.Date = function() {};" + " f(new this.Date());" + "};", "actual parameter 1 of f does not match formal parameter\n" + "found : this.Date\n" + "required: number"); } public void testIssue301() throws Exception { testTypes( "Array.indexOf = function() {};" + "var s = 'hello';" + "alert(s.toLowerCase.indexOf('1'));", "Property indexOf never defined on String.prototype.toLowerCase"); } public void testIssue368() throws Exception { testTypes( "/** @constructor */ function Foo(){}" + "/**\n" + " * @param {number} one\n" + " * @param {string} two\n" + " */\n" + "Foo.prototype.add = function(one, two) {};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar(){}" + "/** @override */\n" + "Bar.prototype.add = function(ignored) {};" + "(new Bar()).add(1, 2);", "actual parameter 2 of Bar.prototype.add does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testIssue380() throws Exception { testTypes( "/** @type { function(string): {innerHTML: string} } */\n" + "document.getElementById;\n" + "var list = /** @type {!Array.} */ ['hello', 'you'];\n" + "list.push('?');\n" + "document.getElementById('node').innerHTML = list.toString();", // Parse warning, but still applied. "Type annotations are not allowed here. " + "Are you missing parentheses?"); } public void testIssue483() throws Exception { testTypes( "/** @constructor */ function C() {" + " /** @type {?Array} */ this.a = [];" + "}" + "C.prototype.f = function() {" + " if (this.a.length > 0) {" + " g(this.a);" + " }" + "};" + "/** @param {number} a */ function g(a) {}", "actual parameter 1 of g does not match formal parameter\n" + "found : Array\n" + "required: number"); } public void testIssue537a() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype = {method: function() {}};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {" + " Foo.call(this);" + " if (this.baz()) this.method(1);" + "}" + "Bar.prototype = {" + " baz: function() {" + " return true;" + " }" + "};" + "Bar.prototype.__proto__ = Foo.prototype;", "Function Foo.prototype.method: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testIssue537b() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype = {method: function() {}};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {" + " Foo.call(this);" + " if (this.baz(1)) this.method();" + "}" + "Bar.prototype = {" + " baz: function() {" + " return true;" + " }" + "};" + "Bar.prototype.__proto__ = Foo.prototype;", "Function Bar.prototype.baz: called with 1 argument(s). " + "Function requires at least 0 argument(s) " + "and no more than 0 argument(s)."); } public void testIssue537c() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {" + " Foo.call(this);" + " if (this.baz2()) alert(1);" + "}" + "Bar.prototype = {" + " baz: function() {" + " return true;" + " }" + "};" + "Bar.prototype.__proto__ = Foo.prototype;", "Property baz2 never defined on Bar"); } public void testIssue537d() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype = {" + " /** @return {Bar} */ x: function() { new Bar(); }," + " /** @return {Foo} */ y: function() { new Bar(); }" + "};" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {" + " this.xy = 3;" + "}" + "/** @return {Bar} */ function f() { return new Bar(); }" + "/** @return {Foo} */ function g() { return new Bar(); }" + "Bar.prototype = {" + " /** @return {Bar} */ x: function() { new Bar(); }," + " /** @return {Foo} */ y: function() { new Bar(); }" + "};" + "Bar.prototype.__proto__ = Foo.prototype;"); } public void testIssue586() throws Exception { testTypes( "/** @constructor */" + "var MyClass = function() {};" + "/** @param {boolean} success */" + "MyClass.prototype.fn = function(success) {};" + "MyClass.prototype.test = function() {" + " this.fn();" + " this.fn = function() {};" + "};", "Function MyClass.prototype.fn: called with 0 argument(s). " + "Function requires at least 1 argument(s) " + "and no more than 1 argument(s)."); } public void testIssue635() throws Exception { // TODO(nicksantos): Make this emit a warning, because of the 'this' type. testTypes( "/** @constructor */" + "function F() {}" + "F.prototype.bar = function() { this.baz(); };" + "F.prototype.baz = function() {};" + "/** @constructor */" + "function G() {}" + "G.prototype.bar = F.prototype.bar;"); } public void testIssue635b() throws Exception { testTypes( "/** @constructor */" + "function F() {}" + "/** @constructor */" + "function G() {}" + "/** @type {function(new:G)} */ var x = F;", "initializing variable\n" + "found : function (new:F): undefined\n" + "required: function (new:G): ?"); } public void testIssue669() throws Exception { testTypes( "/** @return {{prop1: (Object|undefined)}} */" + "function f(a) {" + " var results;" + " if (a) {" + " results = {};" + " results.prop1 = {a: 3};" + " } else {" + " results = {prop2: 3};" + " }" + " return results;" + "}"); } public void testIssue688() throws Exception { testTypes( "/** @const */ var SOME_DEFAULT =\n" + " /** @type {TwoNumbers} */ ({first: 1, second: 2});\n" + "/**\n" + "* Class defining an interface with two numbers.\n" + "* @interface\n" + "*/\n" + "function TwoNumbers() {}\n" + "/** @type number */\n" + "TwoNumbers.prototype.first;\n" + "/** @type number */\n" + "TwoNumbers.prototype.second;\n" + "/** @return {number} */ function f() { return SOME_DEFAULT; }", "inconsistent return type\n" + "found : (TwoNumbers|null)\n" + "required: number"); } public void testIssue700() throws Exception { testTypes( "/**\n" + " * @param {{text: string}} opt_data\n" + " * @return {string}\n" + " */\n" + "function temp1(opt_data) {\n" + " return opt_data.text;\n" + "}\n" + "\n" + "/**\n" + " * @param {{activity: (boolean|number|string|null|Object)}} opt_data\n" + " * @return {string}\n" + " */\n" + "function temp2(opt_data) {\n" + " /** @notypecheck */\n" + " function __inner() {\n" + " return temp1(opt_data.activity);\n" + " }\n" + " return __inner();\n" + "}\n" + "\n" + "/**\n" + " * @param {{n: number, text: string, b: boolean}} opt_data\n" + " * @return {string}\n" + " */\n" + "function temp3(opt_data) {\n" + " return 'n: ' + opt_data.n + ', t: ' + opt_data.text + '.';\n" + "}\n" + "\n" + "function callee() {\n" + " var output = temp3({\n" + " n: 0,\n" + " text: 'a string',\n" + " b: true\n" + " })\n" + " alert(output);\n" + "}\n" + "\n" + "callee();"); } public void testIssue725() throws Exception { testTypes( "/** @typedef {{name: string}} */ var RecordType1;" + "/** @typedef {{name2: string}} */ var RecordType2;" + "/** @param {RecordType1} rec */ function f(rec) {" + " alert(rec.name2);" + "}", "Property name2 never defined on rec"); } public void testIssue726() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = function(x) {};" + "/** @return {!Function} */ " + "Foo.prototype.getDeferredBar = function() { " + " var self = this;" + " return function() {" + " self.bar(true);" + " };" + "};", "actual parameter 1 of Foo.prototype.bar does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testIssue765() throws Exception { testTypes( "/** @constructor */" + "var AnotherType = function (parent) {" + " /** @param {string} stringParameter Description... */" + " this.doSomething = function (stringParameter) {};" + "};" + "/** @constructor */" + "var YetAnotherType = function () {" + " this.field = new AnotherType(self);" + " this.testfun=function(stringdata) {" + " this.field.doSomething(null);" + " };" + "};", "actual parameter 1 of AnotherType.doSomething " + "does not match formal parameter\n" + "found : null\n" + "required: string"); } public void testIssue783() throws Exception { testTypes( "/** @constructor */" + "var Type = function () {" + " /** @type {Type} */" + " this.me_ = this;" + "};" + "Type.prototype.doIt = function() {" + " var me = this.me_;" + " for (var i = 0; i < me.unknownProp; i++) {}" + "};", "Property unknownProp never defined on Type"); } public void testIssue791() throws Exception { testTypes( "/** @param {{func: function()}} obj */" + "function test1(obj) {}" + "var fnStruc1 = {};" + "fnStruc1.func = function() {};" + "test1(fnStruc1);"); } public void testIssue810() throws Exception { testTypes( "/** @constructor */" + "var Type = function () {" + "};" + "Type.prototype.doIt = function(obj) {" + " this.prop = obj.unknownProp;" + "};", "Property unknownProp never defined on obj"); } /** * Tests that the || operator is type checked correctly, that is of * the type of the first argument or of the second argument. See * bugid 592170 for more details. */ public void testBug592170() throws Exception { testTypes( "/** @param {Function} opt_f ... */" + "function foo(opt_f) {" + " /** @type {Function} */" + " return opt_f || function () {};" + "}", "Type annotations are not allowed here. Are you missing parentheses?"); } /** * Tests that undefined can be compared shallowly to a value of type * (number,undefined) regardless of the side on which the undefined * value is. */ public void testBug901455() throws Exception { testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + "var b = undefined === a()"); testTypes("/** @return {(number,undefined)} */ function a() { return 3; }" + "var b = a() === undefined"); } /** * Tests that the match method of strings returns nullable arrays. */ public void testBug908701() throws Exception { testTypes("/** @type {String} */var s = new String('foo');" + "var b = s.match(/a/) != null;"); } /** * Tests that named types play nicely with subtyping. */ public void testBug908625() throws Exception { testTypes("/** @constructor */function A(){}" + "/** @constructor\n * @extends A */function B(){}" + "/** @param {B} b" + "\n @return {(A,undefined)} */function foo(b){return b}"); } /** * Tests that assigning two untyped functions to a variable whose type is * inferred and calling this variable is legal. */ public void testBug911118() throws Exception { // verifying the type assigned to function expressions assigned variables Scope s = parseAndTypeCheckWithScope("var a = function(){};").scope; JSType type = s.getVar("a").getType(); assertEquals("function (): undefined", type.toString()); // verifying the bug example testTypes("function nullFunction() {};" + "var foo = nullFunction;" + "foo = function() {};" + "foo();"); } public void testBug909000() throws Exception { testTypes("/** @constructor */function A(){}\n" + "/** @param {!A} a\n" + "@return {boolean}*/\n" + "function y(a) { return a }", "inconsistent return type\n" + "found : A\n" + "required: boolean"); } public void testBug930117() throws Exception { testTypes( "/** @param {boolean} x */function f(x){}" + "f(null);", "actual parameter 1 of f does not match formal parameter\n" + "found : null\n" + "required: boolean"); } public void testBug1484445() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {number?} */ Foo.prototype.bar = null;" + "/** @type {number?} */ Foo.prototype.baz = null;" + "/** @param {Foo} foo */" + "function f(foo) {" + " while (true) {" + " if (foo.bar == null && foo.baz == null) {" + " foo.bar;" + " }" + " }" + "}"); } public void testBug1859535() throws Exception { testTypes( "/**\n" + " * @param {Function} childCtor Child class.\n" + " * @param {Function} parentCtor Parent class.\n" + " */" + "var inherits = function(childCtor, parentCtor) {" + " /** @constructor */" + " function tempCtor() {};" + " tempCtor.prototype = parentCtor.prototype;" + " childCtor.superClass_ = parentCtor.prototype;" + " childCtor.prototype = new tempCtor();" + " /** @override */ childCtor.prototype.constructor = childCtor;" + "};" + "/**" + " * @param {Function} constructor\n" + " * @param {Object} var_args\n" + " * @return {Object}\n" + " */" + "var factory = function(constructor, var_args) {" + " /** @constructor */" + " var tempCtor = function() {};" + " tempCtor.prototype = constructor.prototype;" + " var obj = new tempCtor();" + " constructor.apply(obj, arguments);" + " return obj;" + "};"); } public void testBug1940591() throws Exception { testTypes( "/** @type {Object} */" + "var a = {};\n" + "/** @type {number} */\n" + "a.name = 0;\n" + "/**\n" + " * @param {Function} x anything.\n" + " */\n" + "a.g = function(x) { x.name = 'a'; }"); } public void testBug1942972() throws Exception { testTypes( "var google = {\n" + " gears: {\n" + " factory: {},\n" + " workerPool: {}\n" + " }\n" + "};\n" + "\n" + "google.gears = {factory: {}};\n"); } public void testBug1943776() throws Exception { testTypes( "/** @return {{foo: Array}} */" + "function bar() {" + " return {foo: []};" + "}"); } public void testBug1987544() throws Exception { testTypes( "/** @param {string} x */ function foo(x) {}" + "var duration;" + "if (true && !(duration = 3)) {" + " foo(duration);" + "}", "actual parameter 1 of foo does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testBug1940769() throws Exception { testTypes( "/** @return {!Object} */ " + "function proto(obj) { return obj.prototype; }" + "/** @constructor */ function Map() {}" + "/**\n" + " * @constructor\n" + " * @extends {Map}\n" + " */" + "function Map2() { Map.call(this); };" + "Map2.prototype = proto(Map);"); } public void testBug2335992() throws Exception { testTypes( "/** @return {*} */ function f() { return 3; }" + "var x = f();" + "/** @type {string} */" + "x.y = 3;", "assignment\n" + "found : number\n" + "required: string"); } public void testBug2341812() throws Exception { testTypes( "/** @interface */" + "function EventTarget() {}" + "/** @constructor \n * @implements {EventTarget} */" + "function Node() {}" + "/** @type {number} */ Node.prototype.index;" + "/** @param {EventTarget} x \n * @return {string} */" + "function foo(x) { return x.index; }"); } public void testBug7701884() throws Exception { testTypes( "/**\n" + " * @param {Array.} x\n" + " * @param {function(T)} y\n" + " * @template T\n" + " */\n" + "var forEach = function(x, y) {\n" + " for (var i = 0; i < x.length; i++) y(x[i]);\n" + "};" + "/** @param {number} x */" + "function f(x) {}" + "/** @param {?} x */" + "function h(x) {" + " var top = null;" + " forEach(x, function(z) { top = z; });" + " if (top) f(top);" + "}"); } public void testBug8017789() throws Exception { testTypes( "/** @param {(map|function())} isResult */" + "var f = function(isResult) {" + " while (true)" + " isResult['t'];" + "};" + "/** @typedef {Object.} */" + "var map;"); } public void testTypedefBeforeUse() throws Exception { testTypes( "/** @typedef {Object.} */" + "var map;" + "/** @param {(map|function())} isResult */" + "var f = function(isResult) {" + " while (true)" + " isResult['t'];" + "};"); } public void testScopedConstructors1() throws Exception { testTypes( "function foo1() { " + " /** @constructor */ function Bar() { " + " /** @type {number} */ this.x = 3;" + " }" + "}" + "function foo2() { " + " /** @constructor */ function Bar() { " + " /** @type {string} */ this.x = 'y';" + " }" + " /** " + " * @param {Bar} b\n" + " * @return {number}\n" + " */" + " function baz(b) { return b.x; }" + "}", "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testScopedConstructors2() throws Exception { testTypes( "/** @param {Function} f */" + "function foo1(f) {" + " /** @param {Function} g */" + " f.prototype.bar = function(g) {};" + "}"); } public void testQualifiedNameInference1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @type {number?} */ Foo.prototype.bar = null;" + "/** @type {number?} */ Foo.prototype.baz = null;" + "/** @param {Foo} foo */" + "function f(foo) {" + " while (true) {" + " if (!foo.baz) break; " + " foo.bar = null;" + " }" + // Tests a bug where this condition always evaluated to true. " return foo.bar == null;" + "}"); } public void testQualifiedNameInference2() throws Exception { testTypes( "var x = {};" + "x.y = c;" + "function f(a, b) {" + " if (a) {" + " if (b) " + " x.y = 2;" + " else " + " x.y = 1;" + " }" + " return x.y == null;" + "}"); } public void testQualifiedNameInference3() throws Exception { testTypes( "var x = {};" + "x.y = c;" + "function f(a, b) {" + " if (a) {" + " if (b) " + " x.y = 2;" + " else " + " x.y = 1;" + " }" + " return x.y == null;" + "} function g() { x.y = null; }"); } public void testQualifiedNameInference4() throws Exception { testTypes( "/** @param {string} x */ function f(x) {}\n" + "/**\n" + " * @param {?string} x \n" + " * @constructor\n" + " */" + "function Foo(x) { this.x_ = x; }\n" + "Foo.prototype.bar = function() {" + " if (this.x_) { f(this.x_); }" + "};"); } public void testQualifiedNameInference5() throws Exception { testTypes( "var ns = {}; " + "(function() { " + " /** @param {number} x */ ns.foo = function(x) {}; })();" + "(function() { ns.foo(true); })();", "actual parameter 1 of ns.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testQualifiedNameInference6() throws Exception { testTypes( "/** @const */ var ns = {}; " + "/** @param {number} x */ ns.foo = function(x) {};" + "(function() { " + " ns.foo = function(x) {};" + " ns.foo(true); " + "})();", "actual parameter 1 of ns.foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testQualifiedNameInference7() throws Exception { testTypes( "var ns = {}; " + "(function() { " + " /** @constructor \n * @param {number} x */ " + " ns.Foo = function(x) {};" + " /** @param {ns.Foo} x */ function f(x) {}" + " f(new ns.Foo(true));" + "})();", "actual parameter 1 of ns.Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testQualifiedNameInference8() throws Exception { // We may need to reshuffle name resolution order so that the @param // type resolves correctly. testClosureTypesMultipleWarnings( "var ns = {}; " + "(function() { " + " /** @constructor \n * @param {number} x */ " + " ns.Foo = function(x) {};" + "})();" + "/** @param {ns.Foo} x */ function f(x) {}" + "f(new ns.Foo(true));", Lists.newArrayList( "Bad type annotation. Unknown type ns.Foo", "actual parameter 1 of ns.Foo does not match formal parameter\n" + "found : boolean\n" + "required: number")); } public void testQualifiedNameInference9() throws Exception { testTypes( "var ns = {}; " + "ns.ns2 = {}; " + "(function() { " + " /** @constructor \n * @param {number} x */ " + " ns.ns2.Foo = function(x) {};" + " /** @param {ns.ns2.Foo} x */ function f(x) {}" + " f(new ns.ns2.Foo(true));" + "})();", "actual parameter 1 of ns.ns2.Foo does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testQualifiedNameInference10() throws Exception { testTypes( "var ns = {}; " + "ns.ns2 = {}; " + "(function() { " + " /** @interface */ " + " ns.ns2.Foo = function() {};" + " /** @constructor \n * @implements {ns.ns2.Foo} */ " + " function F() {}" + " (new F());" + "})();"); } public void testQualifiedNameInference11() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "function f() {" + " var x = new Foo();" + " x.onload = function() {" + " x.onload = null;" + " };" + "}"); } public void testQualifiedNameInference12() throws Exception { // We should be able to tell that the two 'this' properties // are different. testTypes( "/** @param {function(this:Object)} x */ function f(x) {}" + "/** @constructor */ function Foo() {" + " /** @type {number} */ this.bar = 3;" + " f(function() { this.bar = true; });" + "}"); } public void testQualifiedNameInference13() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "function f(z) {" + " var x = new Foo();" + " if (z) {" + " x.onload = function() {};" + " } else {" + " x.onload = null;" + " };" + "}"); } public void testSheqRefinedScope() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */function A() {}\n" + "/** @constructor \n @extends A */ function B() {}\n" + "/** @return {number} */\n" + "B.prototype.p = function() { return 1; }\n" + "/** @param {A} a\n @param {B} b */\n" + "function f(a, b) {\n" + " b.p();\n" + " if (a === b) {\n" + " b.p();\n" + " }\n" + "}"); Node nodeC = n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild(); JSType typeC = nodeC.getJSType(); assertTrue(typeC.isNumber()); Node nodeB = nodeC.getFirstChild().getFirstChild(); JSType typeB = nodeB.getJSType(); assertEquals("B", typeB.toString()); } public void testAssignToUntypedVariable() throws Exception { Node n = parseAndTypeCheck("var z; z = 1;"); Node assign = n.getLastChild().getFirstChild(); Node node = assign.getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertEquals("number", node.getJSType().toString()); } public void testAssignToUntypedProperty() throws Exception { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 1;" + "(new Foo).a;"); Node node = n.getLastChild().getFirstChild(); assertFalse(node.getJSType().isUnknownType()); assertTrue(node.getJSType().isNumber()); } public void testNew1() throws Exception { testTypes("new 4", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew2() throws Exception { testTypes("var Math = {}; new Math()", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew3() throws Exception { testTypes("new Date()"); } public void testNew4() throws Exception { testTypes("/** @constructor */function A(){}; new A();"); } public void testNew5() throws Exception { testTypes("function A(){}; new A();", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew6() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @constructor */function A(){};" + "var a = new A();"); JSType aType = p.scope.getVar("a").getType(); assertTrue(aType instanceof ObjectType); ObjectType aObjectType = (ObjectType) aType; assertEquals("A", aObjectType.getConstructor().getReferenceName()); } public void testNew7() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "if (opt_constructor) { new opt_constructor; }" + "}"); } public void testNew8() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "new opt_constructor;" + "}"); } public void testNew9() throws Exception { testTypes("/** @param {Function} opt_constructor */" + "function foo(opt_constructor) {" + "new (opt_constructor || Array);" + "}"); } public void testNew10() throws Exception { testTypes("var goog = {};" + "/** @param {Function} opt_constructor */" + "goog.Foo = function (opt_constructor) {" + "new (opt_constructor || Array);" + "}"); } public void testNew11() throws Exception { testTypes("/** @param {Function} c1 */" + "function f(c1) {" + " var c2 = function(){};" + " c1.prototype = new c2;" + "}", TypeCheck.NOT_A_CONSTRUCTOR); } public void testNew12() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("var a = new Array();"); Var a = p.scope.getVar("a"); assertTypeEquals(ARRAY_TYPE, a.getType()); } public void testNew13() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "/** @constructor */function FooBar(){};" + "var a = new FooBar();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("FooBar", a.getType().toString()); } public void testNew14() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "/** @constructor */var FooBar = function(){};" + "var a = new FooBar();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("FooBar", a.getType().toString()); } public void testNew15() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "/** @constructor */goog.A = function(){};" + "var a = new goog.A();"); Var a = p.scope.getVar("a"); assertTrue(a.getType() instanceof ObjectType); assertEquals("goog.A", a.getType().toString()); } public void testNew16() throws Exception { testTypes( "/** \n" + " * @param {string} x \n" + " * @constructor \n" + " */" + "function Foo(x) {}" + "function g() { new Foo(1); }", "actual parameter 1 of Foo does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testNew17() throws Exception { testTypes("var goog = {}; goog.x = 3; new goog.x", "cannot instantiate non-constructor"); } public void testNew18() throws Exception { testTypes("var goog = {};" + "/** @constructor */ goog.F = function() {};" + "/** @constructor */ goog.G = goog.F;"); } public void testName1() throws Exception { assertTypeEquals(VOID_TYPE, testNameNode("undefined")); } public void testName2() throws Exception { assertTypeEquals(OBJECT_FUNCTION_TYPE, testNameNode("Object")); } public void testName3() throws Exception { assertTypeEquals(ARRAY_FUNCTION_TYPE, testNameNode("Array")); } public void testName4() throws Exception { assertTypeEquals(DATE_FUNCTION_TYPE, testNameNode("Date")); } public void testName5() throws Exception { assertTypeEquals(REGEXP_FUNCTION_TYPE, testNameNode("RegExp")); } /** * Type checks a NAME node and retrieve its type. */ private JSType testNameNode(String name) { Node node = Node.newString(Token.NAME, name); Node parent = new Node(Token.SCRIPT, node); parent.setInputId(new InputId("code")); Node externs = new Node(Token.SCRIPT); externs.setInputId(new InputId("externs")); Node externAndJsRoot = new Node(Token.BLOCK, externs, parent); externAndJsRoot.setIsSyntheticBlock(true); makeTypeCheck().processForTesting(null, parent); return node.getJSType(); } public void testBitOperation1() throws Exception { testTypes("/**@return {void}*/function foo(){ ~foo(); }", "operator ~ cannot be applied to undefined"); } public void testBitOperation2() throws Exception { testTypes("/**@return {void}*/function foo(){var a = foo()<<3;}", "operator << cannot be applied to undefined"); } public void testBitOperation3() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 3<>>3;}", "operator >>> cannot be applied to undefined"); } public void testBitOperation5() throws Exception { testTypes("/**@return {void}*/function foo(){var a = 3>>>foo();}", "operator >>> cannot be applied to undefined"); } public void testBitOperation6() throws Exception { testTypes("/**@return {!Object}*/function foo(){var a = foo()&3;}", "bad left operand to bitwise operator\n" + "found : Object\n" + "required: (boolean|null|number|string|undefined)"); } public void testBitOperation7() throws Exception { testTypes("var x = null; x |= undefined; x &= 3; x ^= '3'; x |= true;"); } public void testBitOperation8() throws Exception { testTypes("var x = void 0; x |= new Number(3);"); } public void testBitOperation9() throws Exception { testTypes("var x = void 0; x |= {};", "bad right operand to bitwise operator\n" + "found : {}\n" + "required: (boolean|null|number|string|undefined)"); } public void testCall1() throws Exception { testTypes("3();", "number expressions are not callable"); } public void testCall2() throws Exception { testTypes("/** @param {!Number} foo*/function bar(foo){ bar('abc'); }", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: Number"); } public void testCall3() throws Exception { // We are checking that an unresolved named type can successfully // meet with a functional type to produce a callable type. testTypes("/** @type {Function|undefined} */var opt_f;" + "/** @type {some.unknown.type} */var f1;" + "var f2 = opt_f || f1;" + "f2();", "Bad type annotation. Unknown type some.unknown.type"); } public void testCall4() throws Exception { testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ bar('abc'); }", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall5() throws Exception { testTypes("/**@param {!RegExp} a*/var foo = function bar(a){ foo('abc'); }", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall6() throws Exception { testTypes("/** @param {!Number} foo*/function bar(foo){}" + "bar('abc');", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: Number"); } public void testCall7() throws Exception { testTypes("/** @param {!RegExp} a*/var foo = function bar(a){};" + "foo('abc');", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: RegExp"); } public void testCall8() throws Exception { testTypes("/** @type {Function|number} */var f;f();", "(Function|number) expressions are " + "not callable"); } public void testCall9() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @param {!goog.Foo} a */ var bar = function(a){};" + "bar('abc');", "actual parameter 1 of bar does not match formal parameter\n" + "found : string\n" + "required: goog.Foo"); } public void testCall10() throws Exception { testTypes("/** @type {Function} */var f;f();"); } public void testCall11() throws Exception { testTypes("var f = new Function(); f();"); } public void testFunctionCall1() throws Exception { testTypes( "/** @param {number} x */ var foo = function(x) {};" + "foo.call(null, 3);"); } public void testFunctionCall2() throws Exception { testTypes( "/** @param {number} x */ var foo = function(x) {};" + "foo.call(null, 'bar');", "actual parameter 2 of foo.call does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testFunctionCall3() throws Exception { testTypes( "/** @param {number} x \n * @constructor */ " + "var Foo = function(x) { this.bar.call(null, x); };" + "/** @type {function(number)} */ Foo.prototype.bar;"); } public void testFunctionCall4() throws Exception { testTypes( "/** @param {string} x \n * @constructor */ " + "var Foo = function(x) { this.bar.call(null, x); };" + "/** @type {function(number)} */ Foo.prototype.bar;", "actual parameter 2 of this.bar.call " + "does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testFunctionCall5() throws Exception { testTypes( "/** @param {Function} handler \n * @constructor */ " + "var Foo = function(handler) { handler.call(this, x); };"); } public void testFunctionCall6() throws Exception { testTypes( "/** @param {Function} handler \n * @constructor */ " + "var Foo = function(handler) { handler.apply(this, x); };"); } public void testFunctionCall7() throws Exception { testTypes( "/** @param {Function} handler \n * @param {Object} opt_context */ " + "var Foo = function(handler, opt_context) { " + " handler.call(opt_context, x);" + "};"); } public void testFunctionCall8() throws Exception { testTypes( "/** @param {Function} handler \n * @param {Object} opt_context */ " + "var Foo = function(handler, opt_context) { " + " handler.apply(opt_context, x);" + "};"); } public void testFunctionBind1() throws Exception { testTypes( "/** @type {function(string, number): boolean} */" + "function f(x, y) { return true; }" + "f.bind(null, 3);", "actual parameter 2 of f.bind does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testFunctionBind2() throws Exception { testTypes( "/** @type {function(number): boolean} */" + "function f(x) { return true; }" + "f(f.bind(null, 3)());", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testFunctionBind3() throws Exception { testTypes( "/** @type {function(number, string): boolean} */" + "function f(x, y) { return true; }" + "f.bind(null, 3)(true);", "actual parameter 1 of function does not match formal parameter\n" + "found : boolean\n" + "required: string"); } public void testFunctionBind4() throws Exception { testTypes( "/** @param {...number} x */" + "function f(x) {}" + "f.bind(null, 3, 3, 3)(true);", "actual parameter 1 of function does not match formal parameter\n" + "found : boolean\n" + "required: (number|undefined)"); } public void testFunctionBind5() throws Exception { testTypes( "/** @param {...number} x */" + "function f(x) {}" + "f.bind(null, true)(3, 3, 3);", "actual parameter 2 of f.bind does not match formal parameter\n" + "found : boolean\n" + "required: (number|undefined)"); } public void testGoogBind1() throws Exception { testClosureTypes( "var goog = {}; goog.bind = function(var_args) {};" + "/** @type {function(number): boolean} */" + "function f(x, y) { return true; }" + "f(goog.bind(f, null, 'x')());", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testGoogBind2() throws Exception { // TODO(nicksantos): We do not currently type-check the arguments // of the goog.bind. testClosureTypes( "var goog = {}; goog.bind = function(var_args) {};" + "/** @type {function(boolean): boolean} */" + "function f(x, y) { return true; }" + "f(goog.bind(f, null, 'x')());", null); } public void testCast2() throws Exception { // can upcast to a base type. testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n @extends {base} */function derived() {}\n" + "/** @type {base} */ var baz = new derived();\n"); } public void testCast3() throws Exception { // cannot downcast testTypes("/** @constructor */function base() {}\n" + "/** @constructor @extends {base} */function derived() {}\n" + "/** @type {!derived} */ var baz = new base();\n", "initializing variable\n" + "found : base\n" + "required: derived"); } public void testCast3a() throws Exception { // cannot downcast testTypes("/** @constructor */function Base() {}\n" + "/** @constructor @extends {Base} */function Derived() {}\n" + "var baseInstance = new Base();" + "/** @type {!Derived} */ var baz = baseInstance;\n", "initializing variable\n" + "found : Base\n" + "required: Derived"); } public void testCast4() throws Exception { // downcast must be explicit testTypes("/** @constructor */function base() {}\n" + "/** @constructor\n * @extends {base} */function derived() {}\n" + "/** @type {!derived} */ var baz = " + "/** @type {!derived} */(new base());\n"); } public void testCast5() throws Exception { // cannot explicitly cast to an unrelated type testTypes("/** @constructor */function foo() {}\n" + "/** @constructor */function bar() {}\n" + "var baz = /** @type {!foo} */(new bar);\n", "invalid cast - must be a subtype or supertype\n" + "from: bar\n" + "to : foo"); } public void testCast5a() throws Exception { // cannot explicitly cast to an unrelated type testTypes("/** @constructor */function foo() {}\n" + "/** @constructor */function bar() {}\n" + "var barInstance = new bar;\n" + "var baz = /** @type {!foo} */(barInstance);\n", "invalid cast - must be a subtype or supertype\n" + "from: bar\n" + "to : foo"); } public void testCast6() throws Exception { // can explicitly cast to a subtype or supertype testTypes("/** @constructor */function foo() {}\n" + "/** @constructor \n @extends foo */function bar() {}\n" + "var baz = /** @type {!bar} */(new bar);\n" + "var baz = /** @type {!foo} */(new foo);\n" + "var baz = /** @type {bar} */(new bar);\n" + "var baz = /** @type {foo} */(new foo);\n" + "var baz = /** @type {!foo} */(new bar);\n" + "var baz = /** @type {!bar} */(new foo);\n" + "var baz = /** @type {foo} */(new bar);\n" + "var baz = /** @type {bar} */(new foo);\n"); } public void testCast7() throws Exception { testTypes("var x = /** @type {foo} */ (new Object());", "Bad type annotation. Unknown type foo"); } public void testCast8() throws Exception { testTypes("function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast9() throws Exception { testTypes("var foo = {};" + "function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast10() throws Exception { testTypes("var foo = function() {};" + "function f() { return /** @type {foo} */ (new Object()); }", "Bad type annotation. Unknown type foo"); } public void testCast11() throws Exception { testTypes("var goog = {}; goog.foo = {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast12() throws Exception { testTypes("var goog = {}; goog.foo = function() {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast13() throws Exception { // Test to make sure that the forward-declaration still allows for // a warning. testClosureTypes("var goog = {}; " + "goog.addDependency('zzz.js', ['goog.foo'], []);" + "goog.foo = function() {};" + "function f() { return /** @type {goog.foo} */ (new Object()); }", "Bad type annotation. Unknown type goog.foo"); } public void testCast14() throws Exception { // Test to make sure that the forward-declaration still prevents // some warnings. testClosureTypes("var goog = {}; " + "goog.addDependency('zzz.js', ['goog.bar'], []);" + "function f() { return /** @type {goog.bar} */ (new Object()); }", null); } public void testCast15() throws Exception { // This fixes a bug where a type cast on an object literal // would cause a run-time cast exception if the node was visited // more than once. // // Some code assumes that an object literal must have a object type, // while because of the cast, it could have any type (including // a union). testTypes( "for (var i = 0; i < 10; i++) {" + "var x = /** @type {Object|number} */ ({foo: 3});" + "/** @param {number} x */ function f(x) {}" + "f(x.foo);" + "f([].foo);" + "}", "Property foo never defined on Array"); } public void testCast16() throws Exception { // A type cast should not invalidate the checks on the members testTypes( "for (var i = 0; i < 10; i++) {" + "var x = /** @type {Object|number} */ (" + " {/** @type {string} */ foo: 3});" + "}", "assignment to property foo of Object\n" + "found : number\n" + "required: string"); } public void testCast17a() throws Exception { // Mostly verifying that rhino actually understands these JsDocs. testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = /** @type {Foo} */ (y)"); testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = (/** @type {Foo} */ y)"); } public void testCast17b() throws Exception { // Mostly verifying that rhino actually understands these JsDocs. testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = /** @type {Foo} */ ({})"); } public void testCast18() throws Exception { // Mostly verifying that legacy annotations are applied // despite the parser warning. testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = (/** @type {Foo} */ {})", "Type annotations are not allowed here. " + "Are you missing parentheses?"); // Not really encourage because of possible ambiguity but it works. testTypes("/** @constructor */ function Foo() {} \n" + "/** @type {Foo} */ var x = /** @type {Foo} */ {}", "Type annotations are not allowed here. " + "Are you missing parentheses?"); } public void testCast19() throws Exception { testTypes( "var x = 'string';\n" + "/** @type {number} */\n" + "var y = /** @type {number} */(x);", "invalid cast - must be a subtype or supertype\n" + "from: string\n" + "to : number"); } public void testCast20() throws Exception { testTypes( "/** @enum {boolean|null} */\n" + "var X = {" + " AA: true," + " BB: false," + " CC: null" + "};\n" + "var y = /** @type {X} */(true);"); } public void testCast21() throws Exception { testTypes( "/** @enum {boolean|null} */\n" + "var X = {" + " AA: true," + " BB: false," + " CC: null" + "};\n" + "var value = true;\n" + "var y = /** @type {X} */(value);"); } public void testCast22() throws Exception { testTypes( "var x = null;\n" + "var y = /** @type {number} */(x);", "invalid cast - must be a subtype or supertype\n" + "from: null\n" + "to : number"); } public void testCast23() throws Exception { testTypes( "var x = null;\n" + "var y = /** @type {Number} */(x);"); } public void testCast24() throws Exception { testTypes( "var x = undefined;\n" + "var y = /** @type {number} */(x);", "invalid cast - must be a subtype or supertype\n" + "from: undefined\n" + "to : number"); } public void testCast25() throws Exception { testTypes( "var x = undefined;\n" + "var y = /** @type {number|undefined} */(x);"); } public void testCast26() throws Exception { testTypes( "function fn(dir) {\n" + " var node = dir ? 1 : 2;\n" + " fn(/** @type {number} */ (node));\n" + "}"); } public void testCast27() throws Exception { // C doesn't implement I but a subtype might. testTypes( "/** @interface */ function I() {}\n" + "/** @constructor */ function C() {}\n" + "var x = new C();\n" + "var y = /** @type {I} */(x);"); } public void testCast27a() throws Exception { // C doesn't implement I but a subtype might. testTypes( "/** @interface */ function I() {}\n" + "/** @constructor */ function C() {}\n" + "/** @type {C} */ var x ;\n" + "var y = /** @type {I} */(x);"); } public void testCast28() throws Exception { // C doesn't implement I but a subtype might. testTypes( "/** @interface */ function I() {}\n" + "/** @constructor */ function C() {}\n" + "/** @type {!I} */ var x;\n" + "var y = /** @type {C} */(x);"); } public void testCast28a() throws Exception { // C doesn't implement I but a subtype might. testTypes( "/** @interface */ function I() {}\n" + "/** @constructor */ function C() {}\n" + "/** @type {I} */ var x;\n" + "var y = /** @type {C} */(x);"); } public void testCast29a() throws Exception { // C doesn't implement the record type but a subtype might. testTypes( "/** @constructor */ function C() {}\n" + "var x = new C();\n" + "var y = /** @type {{remoteJids: Array, sessionId: string}} */(x);"); } public void testCast29b() throws Exception { // C doesn't implement the record type but a subtype might. testTypes( "/** @constructor */ function C() {}\n" + "/** @type {C} */ var x;\n" + "var y = /** @type {{prop1: Array, prop2: string}} */(x);"); } public void testCast29c() throws Exception { // C doesn't implement the record type but a subtype might. testTypes( "/** @constructor */ function C() {}\n" + "/** @type {{remoteJids: Array, sessionId: string}} */ var x ;\n" + "var y = /** @type {C} */(x);"); } public void testCast30() throws Exception { // Should be able to cast to a looser return type testTypes( "/** @constructor */ function C() {}\n" + "/** @type {function():string} */ var x ;\n" + "var y = /** @type {function():?} */(x);"); } public void testCast31() throws Exception { // Should be able to cast to a tighter parameter type testTypes( "/** @constructor */ function C() {}\n" + "/** @type {function(*)} */ var x ;\n" + "var y = /** @type {function(string)} */(x);"); } public void testCast32() throws Exception { testTypes( "/** @constructor */ function C() {}\n" + "/** @type {Object} */ var x ;\n" + "var y = /** @type {null|{length:number}} */(x);"); } public void testCast33() throws Exception { // null and void should be assignable to any type that accepts one or the // other or both. testTypes( "/** @constructor */ function C() {}\n" + "/** @type {null|undefined} */ var x ;\n" + "var y = /** @type {string?|undefined} */(x);"); testTypes( "/** @constructor */ function C() {}\n" + "/** @type {null|undefined} */ var x ;\n" + "var y = /** @type {string|undefined} */(x);"); testTypes( "/** @constructor */ function C() {}\n" + "/** @type {null|undefined} */ var x ;\n" + "var y = /** @type {string?} */(x);"); testTypes( "/** @constructor */ function C() {}\n" + "/** @type {null|undefined} */ var x ;\n" + "var y = /** @type {null} */(x);"); } public void testCast34a() throws Exception { testTypes( "/** @constructor */ function C() {}\n" + "/** @type {Object} */ var x ;\n" + "var y = /** @type {Function} */(x);"); } public void testCast34b() throws Exception { testTypes( "/** @constructor */ function C() {}\n" + "/** @type {Function} */ var x ;\n" + "var y = /** @type {Object} */(x);"); } public void testNestedCasts() throws Exception { testTypes("/** @constructor */var T = function() {};\n" + "/** @constructor */var V = function() {};\n" + "/**\n" + "* @param {boolean} b\n" + "* @return {T|V}\n" + "*/\n" + "function f(b) { return b ? new T() : new V(); }\n" + "/**\n" + "* @param {boolean} b\n" + "* @return {boolean|undefined}\n" + "*/\n" + "function g(b) { return b ? true : undefined; }\n" + "/** @return {T} */\n" + "function h() {\n" + "return /** @type {T} */ (f(/** @type {boolean} */ (g(true))));\n" + "}"); } public void testNativeCast1() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(String(true));", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testNativeCast2() throws Exception { testTypes( "/** @param {string} x */ function f(x) {}" + "f(Number(true));", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testNativeCast3() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(Boolean(''));", "actual parameter 1 of f does not match formal parameter\n" + "found : boolean\n" + "required: number"); } public void testNativeCast4() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "f(Error(''));", "actual parameter 1 of f does not match formal parameter\n" + "found : Error\n" + "required: number"); } public void testBadConstructorCall() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo();", "Constructor function (new:Foo): undefined should be called " + "with the \"new\" keyword"); } public void testTypeof() throws Exception { testTypes("/**@return {void}*/function foo(){ var a = typeof foo(); }"); } public void testTypeof2() throws Exception { testTypes("function f(){ if (typeof 123 == 'numbr') return 321; }", "unknown type: numbr"); } public void testTypeof3() throws Exception { testTypes("function f() {" + "return (typeof 123 == 'number' ||" + "typeof 123 == 'string' ||" + "typeof 123 == 'boolean' ||" + "typeof 123 == 'undefined' ||" + "typeof 123 == 'function' ||" + "typeof 123 == 'object' ||" + "typeof 123 == 'unknown'); }"); } public void testConstructorType1() throws Exception { testTypes("/**@constructor*/function Foo(){}" + "/**@type{!Foo}*/var f = new Date();", "initializing variable\n" + "found : Date\n" + "required: Foo"); } public void testConstructorType2() throws Exception { testTypes("/**@constructor*/function Foo(){\n" + "/**@type{Number}*/this.bar = new Number(5);\n" + "}\n" + "/**@type{Foo}*/var f = new Foo();\n" + "/**@type{Number}*/var n = f.bar;"); } public void testConstructorType3() throws Exception { // Reverse the declaration order so that we know that Foo is getting set // even on an out-of-order declaration sequence. testTypes("/**@type{Foo}*/var f = new Foo();\n" + "/**@type{Number}*/var n = f.bar;" + "/**@constructor*/function Foo(){\n" + "/**@type{Number}*/this.bar = new Number(5);\n" + "}\n"); } public void testConstructorType4() throws Exception { testTypes("/**@constructor*/function Foo(){\n" + "/**@type{!Number}*/this.bar = new Number(5);\n" + "}\n" + "/**@type{!Foo}*/var f = new Foo();\n" + "/**@type{!String}*/var n = f.bar;", "initializing variable\n" + "found : Number\n" + "required: String"); } public void testConstructorType5() throws Exception { testTypes("/**@constructor*/function Foo(){}\n" + "if (Foo){}\n"); } public void testConstructorType6() throws Exception { testTypes("/** @constructor */\n" + "function bar() {}\n" + "function _foo() {\n" + " /** @param {bar} x */\n" + " function f(x) {}\n" + "}"); } public void testConstructorType7() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("/** @constructor */function A(){};"); JSType type = p.scope.getVar("A").getType(); assertTrue(type instanceof FunctionType); FunctionType fType = (FunctionType) type; assertEquals("A", fType.getReferenceName()); } public void testConstructorType8() throws Exception { testTypes( "var ns = {};" + "ns.create = function() { return function() {}; };" + "/** @constructor */ ns.Foo = ns.create();" + "ns.Foo.prototype = {x: 0, y: 0};" + "/**\n" + " * @param {ns.Foo} foo\n" + " * @return {string}\n" + " */\n" + "function f(foo) {" + " return foo.x;" + "}", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testConstructorType9() throws Exception { testTypes( "var ns = {};" + "ns.create = function() { return function() {}; };" + "ns.extend = function(x) { return x; };" + "/** @constructor */ ns.Foo = ns.create();" + "ns.Foo.prototype = ns.extend({x: 0, y: 0});" + "/**\n" + " * @param {ns.Foo} foo\n" + " * @return {string}\n" + " */\n" + "function f(foo) {" + " return foo.x;" + "}"); } public void testConstructorType10() throws Exception { testTypes("/** @constructor */" + "function NonStr() {}" + "/**\n" + " * @constructor\n" + " * @struct\n" + " * @extends{NonStr}\n" + " */" + "function NonStrKid() {}", "NonStrKid cannot extend this type; " + "structs can only extend structs"); } public void testConstructorType11() throws Exception { testTypes("/** @constructor */" + "function NonDict() {}" + "/**\n" + " * @constructor\n" + " * @dict\n" + " * @extends{NonDict}\n" + " */" + "function NonDictKid() {}", "NonDictKid cannot extend this type; " + "dicts can only extend dicts"); } public void testConstructorType12() throws Exception { testTypes("/**\n" + " * @constructor\n" + " * @struct\n" + " */\n" + "function Bar() {}\n" + "Bar.prototype = {};\n", "Bar cannot extend this type; " + "structs can only extend structs"); } public void testBadStruct() throws Exception { testTypes("/** @struct */function Struct1() {}", "@struct used without @constructor for Struct1"); } public void testBadDict() throws Exception { testTypes("/** @dict */function Dict1() {}", "@dict used without @constructor for Dict1"); } public void testAnonymousPrototype1() throws Exception { testTypes( "var ns = {};" + "/** @constructor */ ns.Foo = function() {" + " this.bar(3, 5);" + "};" + "ns.Foo.prototype = {" + " bar: function(x) {}" + "};", "Function ns.Foo.prototype.bar: called with 2 argument(s). " + "Function requires at least 1 argument(s) and no more " + "than 1 argument(s)."); } public void testAnonymousPrototype2() throws Exception { testTypes( "/** @interface */ var Foo = function() {};" + "Foo.prototype = {" + " foo: function(x) {}" + "};" + "/**\n" + " * @constructor\n" + " * @implements {Foo}\n" + " */ var Bar = function() {};", "property foo on interface Foo is not implemented by type Bar"); } public void testAnonymousType1() throws Exception { testTypes("function f() { return {}; }" + "/** @constructor */\n" + "f().bar = function() {};"); } public void testAnonymousType2() throws Exception { testTypes("function f() { return {}; }" + "/** @interface */\n" + "f().bar = function() {};"); } public void testAnonymousType3() throws Exception { testTypes("function f() { return {}; }" + "/** @enum */\n" + "f().bar = {FOO: 1};"); } public void testBang1() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return x; }", "inconsistent return type\n" + "found : (Object|null)\n" + "required: Object"); } public void testBang2() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return x ? x : new Object(); }"); } public void testBang3() throws Exception { testTypes("/** @param {Object} x\n@return {!Object} */\n" + "function f(x) { return /** @type {!Object} */ (x); }"); } public void testBang4() throws Exception { testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + "function f(x, y) {\n" + "if (typeof x != 'undefined') { return x == y; }\n" + "else { return x != y; }\n}"); } public void testBang5() throws Exception { testTypes("/**@param {Object} x\n@param {Object} y\n@return {boolean}*/\n" + "function f(x, y) { return !!x && x == y; }"); } public void testBang6() throws Exception { testTypes("/** @param {Object?} x\n@return {Object} */\n" + "function f(x) { return x; }"); } public void testBang7() throws Exception { testTypes("/**@param {(Object,string,null)} x\n" + "@return {(Object,string)}*/function f(x) { return x; }"); } public void testDefinePropertyOnNullableObject1() throws Exception { testTypes("/** @type {Object} */ var n = {};\n" + "/** @type {number} */ n.x = 1;\n" + "/** @return {boolean} */function f() { return n.x; }", "inconsistent return type\n" + "found : number\n" + "required: boolean"); } public void testDefinePropertyOnNullableObject2() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @param {T} t\n@return {boolean} */function f(t) {\n" + "t.x = 1; return t.x; }", "inconsistent return type\n" + "found : number\n" + "required: boolean"); } public void testUnknownConstructorInstanceType1() throws Exception { testTypes("/** @return {Array} */ function g(f) { return new f(); }"); } public void testUnknownConstructorInstanceType2() throws Exception { testTypes("function g(f) { return /** @type Array */(new f()); }"); } public void testUnknownConstructorInstanceType3() throws Exception { testTypes("function g(f) { var x = new f(); x.a = 1; return x; }"); } public void testUnknownPrototypeChain() throws Exception { testTypes("/**\n" + "* @param {Object} co\n" + " * @return {Object}\n" + " */\n" + "function inst(co) {\n" + " /** @constructor */\n" + " var c = function() {};\n" + " c.prototype = co.prototype;\n" + " return new c;\n" + "}"); } public void testNamespacedConstructor() throws Exception { Node root = parseAndTypeCheck( "var goog = {};" + "/** @constructor */ goog.MyClass = function() {};" + "/** @return {!goog.MyClass} */ " + "function foo() { return new goog.MyClass(); }"); JSType typeOfFoo = root.getLastChild().getJSType(); assert(typeOfFoo instanceof FunctionType); JSType retType = ((FunctionType) typeOfFoo).getReturnType(); assert(retType instanceof ObjectType); assertEquals("goog.MyClass", ((ObjectType) retType).getReferenceName()); } public void testComplexNamespace() throws Exception { String js = "var goog = {};" + "goog.foo = {};" + "goog.foo.bar = 5;"; TypeCheckResult p = parseAndTypeCheckWithScope(js); // goog type in the scope JSType googScopeType = p.scope.getVar("goog").getType(); assertTrue(googScopeType instanceof ObjectType); assertTrue("foo property not present on goog type", ((ObjectType) googScopeType).hasProperty("foo")); assertFalse("bar property present on goog type", ((ObjectType) googScopeType).hasProperty("bar")); // goog type on the VAR node Node varNode = p.root.getFirstChild(); assertEquals(Token.VAR, varNode.getType()); JSType googNodeType = varNode.getFirstChild().getJSType(); assertTrue(googNodeType instanceof ObjectType); // goog scope type and goog type on VAR node must be the same assertTrue(googScopeType == googNodeType); // goog type on the left of the GETPROP node (under fist ASSIGN) Node getpropFoo1 = varNode.getNext().getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getpropFoo1.getType()); assertEquals("goog", getpropFoo1.getFirstChild().getString()); JSType googGetpropFoo1Type = getpropFoo1.getFirstChild().getJSType(); assertTrue(googGetpropFoo1Type instanceof ObjectType); // still the same type as the one on the variable assertTrue(googGetpropFoo1Type == googScopeType); // the foo property should be defined on goog JSType googFooType = ((ObjectType) googScopeType).getPropertyType("foo"); assertTrue(googFooType instanceof ObjectType); // goog type on the left of the GETPROP lower level node // (under second ASSIGN) Node getpropFoo2 = varNode.getNext().getNext() .getFirstChild().getFirstChild().getFirstChild(); assertEquals(Token.GETPROP, getpropFoo2.getType()); assertEquals("goog", getpropFoo2.getFirstChild().getString()); JSType googGetpropFoo2Type = getpropFoo2.getFirstChild().getJSType(); assertTrue(googGetpropFoo2Type instanceof ObjectType); // still the same type as the one on the variable assertTrue(googGetpropFoo2Type == googScopeType); // goog.foo type on the left of the top-level GETPROP node // (under second ASSIGN) JSType googFooGetprop2Type = getpropFoo2.getJSType(); assertTrue("goog.foo incorrectly annotated in goog.foo.bar selection", googFooGetprop2Type instanceof ObjectType); ObjectType googFooGetprop2ObjectType = (ObjectType) googFooGetprop2Type; assertFalse("foo property present on goog.foo type", googFooGetprop2ObjectType.hasProperty("foo")); assertTrue("bar property not present on goog.foo type", googFooGetprop2ObjectType.hasProperty("bar")); assertTypeEquals("bar property on goog.foo type incorrectly inferred", NUMBER_TYPE, googFooGetprop2ObjectType.getPropertyType("bar")); } public void testAddingMethodsUsingPrototypeIdiomSimpleNamespace() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype.m1 = 5"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 1, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); } public void testAddingMethodsUsingPrototypeIdiomComplexNamespace1() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "goog.A = /** @constructor */function() {};" + "/** @type number */goog.A.prototype.m1 = 5"); testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); } public void testAddingMethodsUsingPrototypeIdiomComplexNamespace2() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope( "var goog = {};" + "/** @constructor */goog.A = function() {};" + "/** @type number */goog.A.prototype.m1 = 5"); testAddingMethodsUsingPrototypeIdiomComplexNamespace(p); } private void testAddingMethodsUsingPrototypeIdiomComplexNamespace( TypeCheckResult p) { ObjectType goog = (ObjectType) p.scope.getVar("goog").getType(); assertEquals(NATIVE_PROPERTIES_COUNT + 1, goog.getPropertiesCount()); JSType googA = goog.getPropertyType("A"); assertNotNull(googA); assertTrue(googA instanceof FunctionType); FunctionType googAFunction = (FunctionType) googA; ObjectType classA = googAFunction.getInstanceType(); assertEquals(NATIVE_PROPERTIES_COUNT + 1, classA.getPropertiesCount()); checkObjectType(classA, "m1", NUMBER_TYPE); } public void testAddingMethodsPrototypeIdiomAndObjectLiteralSimpleNamespace() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true}"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 2, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); checkObjectType(instanceType, "m2", BOOLEAN_TYPE); } public void testDontAddMethodsIfNoConstructor() throws Exception { Node js1Node = parseAndTypeCheck( "function A() {}" + "A.prototype = {m1: 5, m2: true}"); JSType functionAType = js1Node.getFirstChild().getJSType(); assertEquals("function (): undefined", functionAType.toString()); assertTypeEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m1")); assertTypeEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m2")); } public void testFunctionAssignement() throws Exception { testTypes("/**" + "* @param {string} ph0" + "* @param {string} ph1" + "* @return {string}" + "*/" + "function MSG_CALENDAR_ACCESS_ERROR(ph0, ph1) {return ''}" + "/** @type {Function} */" + "var MSG_CALENDAR_ADD_ERROR = MSG_CALENDAR_ACCESS_ERROR;"); } public void testAddMethodsPrototypeTwoWays() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true};" + "A.prototype.m3 = 'third property!';"); ObjectType instanceType = getInstanceType(js1Node); assertEquals("A", instanceType.toString()); assertEquals(NATIVE_PROPERTIES_COUNT + 3, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); checkObjectType(instanceType, "m2", BOOLEAN_TYPE); checkObjectType(instanceType, "m3", STRING_TYPE); } public void testPrototypePropertyTypes() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {\n" + " /** @type string */ this.m1;\n" + " /** @type Object? */ this.m2 = {};\n" + " /** @type boolean */ this.m3;\n" + "}\n" + "/** @type string */ A.prototype.m4;\n" + "/** @type number */ A.prototype.m5 = 0;\n" + "/** @type boolean */ A.prototype.m6;\n"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 6, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", STRING_TYPE); checkObjectType(instanceType, "m2", createUnionType(OBJECT_TYPE, NULL_TYPE)); checkObjectType(instanceType, "m3", BOOLEAN_TYPE); checkObjectType(instanceType, "m4", STRING_TYPE); checkObjectType(instanceType, "m5", NUMBER_TYPE); checkObjectType(instanceType, "m6", BOOLEAN_TYPE); } public void testValueTypeBuiltInPrototypePropertyType() throws Exception { Node node = parseAndTypeCheck("\"x\".charAt(0)"); assertTypeEquals(STRING_TYPE, node.getFirstChild().getFirstChild().getJSType()); } public void testDeclareBuiltInConstructor() throws Exception { // Built-in prototype properties should be accessible // even if the built-in constructor is declared. Node node = parseAndTypeCheck( "/** @constructor */ var String = function(opt_str) {};\n" + "(new String(\"x\")).charAt(0)"); assertTypeEquals(STRING_TYPE, node.getLastChild().getFirstChild().getJSType()); } public void testExtendBuiltInType1() throws Exception { String externs = "/** @constructor */ var String = function(opt_str) {};\n" + "/**\n" + "* @param {number} start\n" + "* @param {number} opt_length\n" + "* @return {string}\n" + "*/\n" + "String.prototype.substr = function(start, opt_length) {};\n"; Node n1 = parseAndTypeCheck(externs + "(new String(\"x\")).substr(0,1);"); assertTypeEquals(STRING_TYPE, n1.getLastChild().getFirstChild().getJSType()); } public void testExtendBuiltInType2() throws Exception { String externs = "/** @constructor */ var String = function(opt_str) {};\n" + "/**\n" + "* @param {number} start\n" + "* @param {number} opt_length\n" + "* @return {string}\n" + "*/\n" + "String.prototype.substr = function(start, opt_length) {};\n"; Node n2 = parseAndTypeCheck(externs + "\"x\".substr(0,1);"); assertTypeEquals(STRING_TYPE, n2.getLastChild().getFirstChild().getJSType()); } public void testExtendFunction1() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(new Function()).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertTypeEquals(NUMBER_TYPE, type); } public void testExtendFunction2() throws Exception { Node n = parseAndTypeCheck("/**@return {number}*/Function.prototype.f = " + "function() { return 1; };\n" + "(function() {}).f();"); JSType type = n.getLastChild().getLastChild().getJSType(); assertTypeEquals(NUMBER_TYPE, type); } public void testInheritanceCheck1() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};"); } public void testInheritanceCheck2() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "property foo not defined on any superclass of Sub"); } public void testInheritanceCheck3() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on superclass Super; " + "use @override to override it"); } public void testInheritanceCheck4() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInheritanceCheck5() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() {};" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on superclass Root; " + "use @override to override it"); } public void testInheritanceCheck6() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() {};" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInheritanceCheck7() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "goog.Sub.prototype.foo = 5;"); } public void testInheritanceCheck8() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @override */goog.Sub.prototype.foo = 5;"); } public void testInheritanceCheck9_1() throws Exception { testTypes( "/** @constructor */function Super() {};" + "Super.prototype.foo = function() { return 3; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };"); } public void testInheritanceCheck9_2() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @return {number} */" + "Super.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo =\n" + "function() {};"); } public void testInheritanceCheck9_3() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @return {number} */" + "Super.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {string} */Sub.prototype.foo =\n" + "function() { return \"some string\" };", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Super\n" + "original: function (this:Super): number\n" + "override: function (this:Sub): string"); } public void testInheritanceCheck10_1() throws Exception { testTypes( "/** @constructor */function Root() {};" + "Root.prototype.foo = function() { return 3; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };"); } public void testInheritanceCheck10_2() throws Exception { testTypes( "/** @constructor */function Root() {};" + "/** @return {number} */" + "Root.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo =\n" + "function() {};"); } public void testInheritanceCheck10_3() throws Exception { testTypes( "/** @constructor */function Root() {};" + "/** @return {number} */" + "Root.prototype.foo = function() { return 1; };" + "/** @constructor\n @extends {Root} */function Super() {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @return {string} */Sub.prototype.foo =\n" + "function() { return \"some string\" };", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Root\n" + "original: function (this:Root): number\n" + "override: function (this:Sub): string"); } public void testInterfaceInheritanceCheck11() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + "function(bar) {};", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Super\n" + "original: function (this:Super, number): undefined\n" + "override: function (this:Sub, string): undefined"); } public void testInheritanceCheck12() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @override */goog.Sub.prototype.foo = \"some string\";"); } public void testInheritanceCheck13() throws Exception { testTypes( "var goog = {};\n" + "/** @constructor\n @extends {goog.Missing} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "Bad type annotation. Unknown type goog.Missing"); } public void testInheritanceCheck14() throws Exception { testClosureTypes( "var goog = {};\n" + "/** @constructor\n @extends {goog.Missing} */\n" + "goog.Super = function() {};\n" + "/** @constructor\n @extends {goog.Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", "Bad type annotation. Unknown type goog.Missing"); } public void testInheritanceCheck15() throws Exception { testTypes( "/** @constructor */function Super() {};" + "/** @param {number} bar */Super.prototype.foo;" + "/** @constructor\n @extends {Super} */function Sub() {};" + "/** @override\n @param {number} bar */Sub.prototype.foo =\n" + "function(bar) {};"); } public void testInheritanceCheck16() throws Exception { testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "/** @type {number} */ goog.Super.prototype.foo = 3;" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @type {number} */ goog.Sub.prototype.foo = 5;", "property foo already defined on superclass goog.Super; " + "use @override to override it"); } public void testInheritanceCheck17() throws Exception { // Make sure this warning still works, even when there's no // @override tag. reportMissingOverrides = CheckLevel.OFF; testTypes( "var goog = {};" + "/** @constructor */goog.Super = function() {};" + "/** @param {number} x */ goog.Super.prototype.foo = function(x) {};" + "/** @constructor\n @extends {goog.Super} */goog.Sub = function() {};" + "/** @param {string} x */ goog.Sub.prototype.foo = function(x) {};", "mismatch of the foo property type and the type of the property it " + "overrides from superclass goog.Super\n" + "original: function (this:goog.Super, number): undefined\n" + "override: function (this:goog.Sub, string): undefined"); } public void testInterfacePropertyOverride1() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @desc description */Super.prototype.foo = function() {};" + "/** @interface\n @extends {Super} */function Sub() {};" + "/** @desc description */Sub.prototype.foo = function() {};"); } public void testInterfacePropertyOverride2() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @desc description */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @interface\n @extends {Super} */function Sub() {};" + "/** @desc description */Sub.prototype.foo = function() {};"); } public void testInterfaceInheritanceCheck1() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @desc description */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "Sub.prototype.foo = function() {};", "property foo already defined on interface Super; use @override to " + "override it"); } public void testInterfaceInheritanceCheck2() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @desc description */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};"); } public void testInterfaceInheritanceCheck3() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {number} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @return {number} */Sub.prototype.foo = function() { return 1;};", "property foo already defined on interface Root; use @override to " + "override it"); } public void testInterfaceInheritanceCheck4() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {number} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n * @return {number} */Sub.prototype.foo =\n" + "function() { return 1;};"); } public void testInterfaceInheritanceCheck5() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @return {string} */Super.prototype.foo = function() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };", "mismatch of the foo property type and the type of the property it " + "overrides from interface Super\n" + "original: function (this:Super): string\n" + "override: function (this:Sub): number"); } public void testInterfaceInheritanceCheck6() throws Exception { testTypes( "/** @interface */function Root() {};" + "/** @return {string} */Root.prototype.foo = function() {};" + "/** @interface\n @extends {Root} */function Super() {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @return {number} */Sub.prototype.foo =\n" + "function() { return 1; };", "mismatch of the foo property type and the type of the property it " + "overrides from interface Root\n" + "original: function (this:Root): string\n" + "override: function (this:Sub): number"); } public void testInterfaceInheritanceCheck7() throws Exception { testTypes( "/** @interface */function Super() {};" + "/** @param {number} bar */Super.prototype.foo = function(bar) {};" + "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + "function(bar) {};", "mismatch of the foo property type and the type of the property it " + "overrides from interface Super\n" + "original: function (this:Super, number): undefined\n" + "override: function (this:Sub, string): undefined"); } public void testInterfaceInheritanceCheck8() throws Exception { testTypes( "/** @constructor\n @implements {Super} */function Sub() {};" + "/** @override */Sub.prototype.foo = function() {};", new String[] { "Bad type annotation. Unknown type Super", "property foo not defined on any superclass of Sub" }); } public void testInterfaceInheritanceCheck9() throws Exception { testTypes( "/** @interface */ function I() {}" + "/** @return {number} */ I.prototype.bar = function() {};" + "/** @constructor */ function F() {}" + "/** @return {number} */ F.prototype.bar = function() {return 3; };" + "/** @return {number} */ F.prototype.foo = function() {return 3; };" + "/** @constructor \n * @extends {F} \n * @implements {I} */ " + "function G() {}" + "/** @return {string} */ function f() { return new G().bar(); }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testInterfaceInheritanceCheck10() throws Exception { testTypes( "/** @interface */ function I() {}" + "/** @return {number} */ I.prototype.bar = function() {};" + "/** @constructor */ function F() {}" + "/** @return {number} */ F.prototype.foo = function() {return 3; };" + "/** @constructor \n * @extends {F} \n * @implements {I} */ " + "function G() {}" + "/** @return {number} \n * @override */ " + "G.prototype.bar = G.prototype.foo;" + "/** @return {string} */ function f() { return new G().bar(); }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testInterfaceInheritanceCheck12() throws Exception { testTypes( "/** @interface */ function I() {};\n" + "/** @type {string} */ I.prototype.foobar;\n" + "/** \n * @constructor \n * @implements {I} */\n" + "function C() {\n" + "/** \n * @type {number} */ this.foobar = 2;};\n" + "/** @type {I} */ \n var test = new C(); alert(test.foobar);", "mismatch of the foobar property type and the type of the property" + " it overrides from interface I\n" + "original: string\n" + "override: number"); } public void testInterfaceInheritanceCheck13() throws Exception { testTypes( "function abstractMethod() {};\n" + "/** @interface */var base = function() {};\n" + "/** @extends {base} \n @interface */ var Int = function() {}\n" + "/** @type {{bar : !Function}} */ var x; \n" + "/** @type {!Function} */ base.prototype.bar = abstractMethod; \n" + "/** @type {Int} */ var foo;\n" + "foo.bar();"); } public void testInterfacePropertyNotImplemented() throws Exception { testTypes( "/** @interface */function Int() {};" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @constructor\n @implements {Int} */function Foo() {};", "property foo on interface Int is not implemented by type Foo"); } public void testInterfacePropertyNotImplemented2() throws Exception { testTypes( "/** @interface */function Int() {};" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @interface \n @extends {Int} */function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "property foo on interface Int is not implemented by type Foo"); } public void testStubConstructorImplementingInterface() throws Exception { // This does not throw a warning for unimplemented property because Foo is // just a stub. testTypes( // externs "/** @interface */ function Int() {}\n" + "/** @desc description */Int.prototype.foo = function() {};" + "/** @constructor \n @implements {Int} */ var Foo;\n", "", null, false); } public void testObjectLiteral() throws Exception { Node n = parseAndTypeCheck("var a = {m1: 7, m2: 'hello'}"); Node nameNode = n.getFirstChild().getFirstChild(); Node objectNode = nameNode.getFirstChild(); // node extraction assertEquals(Token.NAME, nameNode.getType()); assertEquals(Token.OBJECTLIT, objectNode.getType()); // value's type ObjectType objectType = (ObjectType) objectNode.getJSType(); assertTypeEquals(NUMBER_TYPE, objectType.getPropertyType("m1")); assertTypeEquals(STRING_TYPE, objectType.getPropertyType("m2")); // variable's type assertTypeEquals(objectType, nameNode.getJSType()); } public void testObjectLiteralDeclaration1() throws Exception { testTypes( "var x = {" + "/** @type {boolean} */ abc: true," + "/** @type {number} */ 'def': 0," + "/** @type {string} */ 3: 'fgh'" + "};"); } public void testObjectLiteralDeclaration2() throws Exception { testTypes( "var x = {" + " /** @type {boolean} */ abc: true" + "};" + "x.abc = 0;", "assignment to property abc of x\n" + "found : number\n" + "required: boolean"); } public void testObjectLiteralDeclaration3() throws Exception { testTypes( "/** @param {{foo: !Function}} x */ function f(x) {}" + "f({foo: function() {}});"); } public void testObjectLiteralDeclaration4() throws Exception { testClosureTypes( "var x = {" + " /** @param {boolean} x */ abc: function(x) {}" + "};" + "/**\n" + " * @param {string} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};", "assignment to property abc of x\n" + "found : function (string): undefined\n" + "required: function (boolean): undefined"); // TODO(user): suppress {duplicate} currently also silence the // redefining type error in the TypeValidator. Maybe it needs // a new suppress name instead? } public void testObjectLiteralDeclaration5() throws Exception { testTypes( "var x = {" + " /** @param {boolean} x */ abc: function(x) {}" + "};" + "/**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};"); } public void testObjectLiteralDeclaration6() throws Exception { testTypes( "var x = {};" + "/**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */ x.abc = function(x) {};" + "x = {" + " /**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */" + " abc: function(x) {}" + "};"); } public void testObjectLiteralDeclaration7() throws Exception { testTypes( "var x = {};" + "/**\n" + " * @type {function(boolean): undefined}\n" + " */ x.abc = function(x) {};" + "x = {" + " /**\n" + " * @param {boolean} x\n" + " * @suppress {duplicate}\n" + " */" + " abc: function(x) {}" + "};"); } public void testCallDateConstructorAsFunction() throws Exception { // ECMA-262 15.9.2: When Date is called as a function rather than as a // constructor, it returns a string. Node n = parseAndTypeCheck("Date()"); assertTypeEquals(STRING_TYPE, n.getFirstChild().getFirstChild().getJSType()); } // According to ECMA-262, Error & Array function calls are equivalent to // constructor calls. public void testCallErrorConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Error('x')"); assertTypeEquals(ERROR_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testCallArrayConstructorAsFunction() throws Exception { Node n = parseAndTypeCheck("Array()"); assertTypeEquals(ARRAY_TYPE, n.getFirstChild().getFirstChild().getJSType()); } public void testPropertyTypeOfUnionType() throws Exception { testTypes("var a = {};" + "/** @constructor */ a.N = function() {};\n" + "a.N.prototype.p = 1;\n" + "/** @constructor */ a.S = function() {};\n" + "a.S.prototype.p = 'a';\n" + "/** @param {!a.N|!a.S} x\n@return {string} */\n" + "var f = function(x) { return x.p; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } // TODO(user): We should flag these as invalid. This will probably happen // when we make sure the interface is never referenced outside of its // definition. We might want more specific and helpful error messages. //public void testWarningOnInterfacePrototype() throws Exception { // testTypes("/** @interface */ u.T = function() {};\n" + // "/** @return {number} */ u.T.prototype = function() { };", // "e of its definition"); //} // //public void testBadPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function() {};\n" + // "/** @return {number} */ u.T.f = function() { return 1;};", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {};\n" + // "/** @return {number} */ T.f = function() { return 1;};", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface3() throws Exception { // testTypes("/** @interface */ u.T = function() {}; u.T.x", // "cannot reference an interface outside of its definition"); //} // //public void testBadPropertyOnInterface4() throws Exception { // testTypes("/** @interface */ function T() {}; T.x;", // "cannot reference an interface outside of its definition"); //} public void testAnnotatedPropertyOnInterface1() throws Exception { // For interfaces we must allow function definitions that don't have a // return statement, even though they declare a returned type. testTypes("/** @interface */ u.T = function() {};\n" + "/** @return {number} */ u.T.prototype.f = function() {};"); } public void testAnnotatedPropertyOnInterface2() throws Exception { testTypes("/** @interface */ u.T = function() {};\n" + "/** @return {number} */ u.T.prototype.f = function() { };"); } public void testAnnotatedPropertyOnInterface3() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @return {number} */ T.prototype.f = function() { };"); } public void testAnnotatedPropertyOnInterface4() throws Exception { testTypes( CLOSURE_DEFS + "/** @interface */ function T() {};\n" + "/** @return {number} */ T.prototype.f = goog.abstractMethod;"); } // TODO(user): If we want to support this syntax we have to warn about // missing annotations. //public void testWarnUnannotatedPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {}; u.T.prototype.x;", // "interface property x is not annotated"); //} // //public void testWarnUnannotatedPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {}; T.prototype.x;", // "interface property x is not annotated"); //} public void testWarnUnannotatedPropertyOnInterface5() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @desc x does something */u.T.prototype.x = function() {};"); } public void testWarnUnannotatedPropertyOnInterface6() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @desc x does something */T.prototype.x = function() {};"); } // TODO(user): If we want to support this syntax we have to warn about // the invalid type of the interface member. //public void testWarnDataPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @type {number} */u.T.prototype.x;", // "interface members can only be plain functions"); //} public void testDataPropertyOnInterface1() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x;"); } public void testDataPropertyOnInterface2() throws Exception { reportMissingOverrides = CheckLevel.OFF; testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x;\n" + "/** @constructor \n" + " * @implements {T} \n" + " */\n" + "function C() {}\n" + "C.prototype.x = 'foo';", "mismatch of the x property type and the type of the property it " + "overrides from interface T\n" + "original: number\n" + "override: string"); } public void testDataPropertyOnInterface3() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x;\n" + "/** @constructor \n" + " * @implements {T} \n" + " */\n" + "function C() {}\n" + "/** @override */\n" + "C.prototype.x = 'foo';", "mismatch of the x property type and the type of the property it " + "overrides from interface T\n" + "original: number\n" + "override: string"); } public void testDataPropertyOnInterface4() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x;\n" + "/** @constructor \n" + " * @implements {T} \n" + " */\n" + "function C() { /** @type {string} */ \n this.x = 'foo'; }\n", "mismatch of the x property type and the type of the property it " + "overrides from interface T\n" + "original: number\n" + "override: string"); } public void testWarnDataPropertyOnInterface3() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @type {number} */u.T.prototype.x = 1;", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod"); } public void testWarnDataPropertyOnInterface4() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x = 1;", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod"); } // TODO(user): If we want to support this syntax we should warn about the // mismatching types in the two tests below. //public void testErrorMismatchingPropertyOnInterface1() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @param {Number} foo */u.T.prototype.x =\n" + // "/** @param {String} foo */function(foo) {};", // "found : \n" + // "required: "); //} // //public void testErrorMismatchingPropertyOnInterface2() throws Exception { // testTypes("/** @interface */ function T() {};\n" + // "/** @return {number} */T.prototype.x =\n" + // "/** @return {string} */function() {};", // "found : \n" + // "required: "); //} // TODO(user): We should warn about this (bar is missing an annotation). We // probably don't want to warn about all missing parameter annotations, but // we should be as strict as possible regarding interfaces. //public void testErrorMismatchingPropertyOnInterface3() throws Exception { // testTypes("/** @interface */ u.T = function () {};\n" + // "/** @param {Number} foo */u.T.prototype.x =\n" + // "function(foo, bar) {};", // "found : \n" + // "required: "); //} public void testErrorMismatchingPropertyOnInterface4() throws Exception { testTypes("/** @interface */ u.T = function () {};\n" + "/** @param {Number} foo */u.T.prototype.x =\n" + "function() {};", "parameter foo does not appear in u.T.prototype.x's parameter list"); } public void testErrorMismatchingPropertyOnInterface5() throws Exception { testTypes("/** @interface */ function T() {};\n" + "/** @type {number} */T.prototype.x = function() { };", "assignment to property x of T.prototype\n" + "found : function (): undefined\n" + "required: number"); } public void testErrorMismatchingPropertyOnInterface6() throws Exception { testClosureTypesMultipleWarnings( "/** @interface */ function T() {};\n" + "/** @return {number} */T.prototype.x = 1", Lists.newArrayList( "assignment to property x of T.prototype\n" + "found : number\n" + "required: function (this:T): number", "interface members can only be empty property declarations, " + "empty functions, or goog.abstractMethod")); } public void testInterfaceNonEmptyFunction() throws Exception { testTypes("/** @interface */ function T() {};\n" + "T.prototype.x = function() { return 'foo'; }", "interface member functions must have an empty body" ); } public void testDoubleNestedInterface() throws Exception { testTypes("/** @interface */ var I1 = function() {};\n" + "/** @interface */ I1.I2 = function() {};\n" + "/** @interface */ I1.I2.I3 = function() {};\n"); } public void testStaticDataPropertyOnNestedInterface() throws Exception { testTypes("/** @interface */ var I1 = function() {};\n" + "/** @interface */ I1.I2 = function() {};\n" + "/** @type {number} */ I1.I2.x = 1;\n"); } public void testInterfaceInstantiation() throws Exception { testTypes("/** @interface */var f = function(){}; new f", "cannot instantiate non-constructor"); } public void testPrototypeLoop() throws Exception { testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @constructor \n * @extends {T} */var T = function() {};" + "alert((new T).foo);", Lists.newArrayList( "Parse error. Cycle detected in inheritance chain of type T", "Could not resolve type in @extends tag of T")); } public void testImplementsLoop() throws Exception { testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @constructor \n * @implements {T} */var T = function() {};" + "alert((new T).foo);", Lists.newArrayList( "Parse error. Cycle detected in inheritance chain of type T")); } public void testImplementsExtendsLoop() throws Exception { testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @constructor \n * @implements {F} */var G = function() {};" + "/** @constructor \n * @extends {G} */var F = function() {};" + "alert((new F).foo);", Lists.newArrayList( "Parse error. Cycle detected in inheritance chain of type F")); } public void testInterfaceExtendsLoop() throws Exception { // TODO(user): This should give a cycle in inheritance graph error, // not a cannot resolve error. testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @interface \n * @extends {F} */var G = function() {};" + "/** @interface \n * @extends {G} */var F = function() {};", Lists.newArrayList( "Could not resolve type in @extends tag of G")); } public void testConversionFromInterfaceToRecursiveConstructor() throws Exception { testClosureTypesMultipleWarnings( suppressMissingProperty("foo") + "/** @interface */ var OtherType = function() {}\n" + "/** @implements {MyType} \n * @constructor */\n" + "var MyType = function() {}\n" + "/** @type {MyType} */\n" + "var x = /** @type {!OtherType} */ (new Object());", Lists.newArrayList( "Parse error. Cycle detected in inheritance chain of type MyType", "initializing variable\n" + "found : OtherType\n" + "required: (MyType|null)")); } public void testDirectPrototypeAssign() throws Exception { // For now, we just ignore @type annotations on the prototype. testTypes( "/** @constructor */ function Foo() {}" + "/** @constructor */ function Bar() {}" + "/** @type {Array} */ Bar.prototype = new Foo()"); } // In all testResolutionViaRegistry* tests, since u is unknown, u.T can only // be resolved via the registry and not via properties. public void testResolutionViaRegistry1() throws Exception { testTypes("/** @constructor */ u.T = function() {};\n" + "/** @type {(number|string)} */ u.T.prototype.a;\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testResolutionViaRegistry2() throws Exception { testTypes( "/** @constructor */ u.T = function() {" + " this.a = 0; };\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testResolutionViaRegistry3() throws Exception { testTypes("/** @constructor */ u.T = function() {};\n" + "/** @type {(number|string)} */ u.T.prototype.a = 0;\n" + "/**\n" + "* @param {u.T} t\n" + "* @return {string}\n" + "*/\n" + "var f = function(t) { return t.a; };", "inconsistent return type\n" + "found : (number|string)\n" + "required: string"); } public void testResolutionViaRegistry4() throws Exception { testTypes("/** @constructor */ u.A = function() {};\n" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.A = function() {}\n;" + "/**\n* @constructor\n* @extends {u.A}\n*/\nu.A.B = function() {};\n" + "var ab = new u.A.B();\n" + "/** @type {!u.A} */ var a = ab;\n" + "/** @type {!u.A.A} */ var aa = ab;\n", "initializing variable\n" + "found : u.A.B\n" + "required: u.A.A"); } public void testResolutionViaRegistry5() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ u.T = function() {}; u.T"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof FunctionType); assertEquals("u.T", ((FunctionType) type).getInstanceType().getReferenceName()); } public void testGatherProperyWithoutAnnotation1() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ var T = function() {};" + "/** @type {!T} */var t; t.x; t;"); JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); Asserts.assertTypeCollectionEquals( Lists.newArrayList(objectType), registry.getTypesWithProperty("x")); } public void testGatherProperyWithoutAnnotation2() throws Exception { TypeCheckResult ns = parseAndTypeCheckWithScope("/** @type {!Object} */var t; t.x; t;"); Node n = ns.root; JSType type = n.getLastChild().getLastChild().getJSType(); assertFalse(type.isUnknownType()); assertTypeEquals(type, OBJECT_TYPE); assertTrue(type instanceof ObjectType); ObjectType objectType = (ObjectType) type; assertFalse(objectType.hasProperty("x")); Asserts.assertTypeCollectionEquals( Lists.newArrayList(OBJECT_TYPE), registry.getTypesWithProperty("x")); } public void testFunctionMasksVariableBug() throws Exception { testTypes("var x = 4; var f = function x(b) { return b ? 1 : x(true); };", "function x masks variable (IE bug)"); } public void testDfa1() throws Exception { testTypes("var x = null;\n x = 1;\n /** @type number */ var y = x;"); } public void testDfa2() throws Exception { testTypes("function u() {}\n" + "/** @return {number} */ function f() {\nvar x = 'todo';\n" + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); } public void testDfa3() throws Exception { testTypes("function u() {}\n" + "/** @return {number} */ function f() {\n" + "/** @type {number|string} */ var x = 'todo';\n" + "if (u()) { x = 1; } else { x = 2; } return x;\n}"); } public void testDfa4() throws Exception { testTypes("/** @param {Date?} d */ function f(d) {\n" + "if (!d) { return; }\n" + "/** @type {!Date} */ var e = d;\n}"); } public void testDfa5() throws Exception { testTypes("/** @return {string?} */ function u() {return 'a';}\n" + "/** @param {string?} x\n@return {string} */ function f(x) {\n" + "while (!x) { x = u(); }\nreturn x;\n}"); } public void testDfa6() throws Exception { testTypes("/** @return {Object?} */ function u() {return {};}\n" + "/** @param {Object?} x */ function f(x) {\n" + "while (x) { x = u(); if (!x) { x = u(); } }\n}"); } public void testDfa7() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @type {Date?} */ T.prototype.x = null;\n" + "/** @param {!T} t */ function f(t) {\n" + "if (!t.x) { return; }\n" + "/** @type {!Date} */ var e = t.x;\n}"); } public void testDfa8() throws Exception { testTypes("/** @constructor */ var T = function() {};\n" + "/** @type {number|string} */ T.prototype.x = '';\n" + "function u() {}\n" + "/** @param {!T} t\n@return {number} */ function f(t) {\n" + "if (u()) { t.x = 1; } else { t.x = 2; } return t.x;\n}"); } public void testDfa9() throws Exception { testTypes("function f() {\n/** @type {string?} */var x;\nx = null;\n" + "if (x == null) { return 0; } else { return 1; } }", "condition always evaluates to true\n" + "left : null\n" + "right: null"); } public void testDfa10() throws Exception { testTypes("/** @param {null} x */ function g(x) {}" + "/** @param {string?} x */function f(x) {\n" + "if (!x) { x = ''; }\n" + "if (g(x)) { return 0; } else { return 1; } }", "actual parameter 1 of g does not match formal parameter\n" + "found : string\n" + "required: null"); } public void testDfa11() throws Exception { testTypes("/** @param {string} opt_x\n@return {string} */\n" + "function f(opt_x) { if (!opt_x) { " + "throw new Error('x cannot be empty'); } return opt_x; }"); } public void testDfa12() throws Exception { testTypes("/** @param {string} x \n * @constructor \n */" + "var Bar = function(x) {};" + "/** @param {string} x */ function g(x) { return true; }" + "/** @param {string|number} opt_x */ " + "function f(opt_x) { " + " if (opt_x) { new Bar(g(opt_x) && 'x'); }" + "}", "actual parameter 1 of g does not match formal parameter\n" + "found : (number|string)\n" + "required: string"); } public void testDfa13() throws Exception { testTypes( "/**\n" + " * @param {string} x \n" + " * @param {number} y \n" + " * @param {number} z \n" + " */" + "function g(x, y, z) {}" + "function f() { " + " var x = 'a'; g(x, x = 3, x);" + "}"); } public void testTypeInferenceWithCast1() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + "/**@return {number?}*/function g(x) {" + "var y = /**@type {number?}*/(u(x)); return f(y);}"); } public void testTypeInferenceWithCast2() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return null;}" + "/**@param {number?} x\n@return {number?}*/function f(x) {return x;}" + "/**@return {number?}*/function g(x) {" + "var y; y = /**@type {number?}*/(u(x)); return f(y);}"); } public void testTypeInferenceWithCast3() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + "/**@return {number}*/function g(x) {" + "return /**@type {number}*/(u(x));}"); } public void testTypeInferenceWithCast4() throws Exception { testTypes( "/**@return {(number,null,undefined)}*/function u(x) {return 1;}" + "/**@return {number}*/function g(x) {" + "return /**@type {number}*/(u(x)) && 1;}"); } public void testTypeInferenceWithCast5() throws Exception { testTypes( "/** @param {number} x */ function foo(x) {}" + "/** @param {{length:*}} y */ function bar(y) {" + " /** @type {string} */ y.length;" + " foo(y.length);" + "}", "actual parameter 1 of foo does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testTypeInferenceWithClosure1() throws Exception { testTypes( "/** @return {boolean} */" + "function f() {" + " /** @type {?string} */ var x = null;" + " function g() { x = 'y'; } g(); " + " return x == null;" + "}"); } public void testTypeInferenceWithClosure2() throws Exception { testTypes( "/** @return {boolean} */" + "function f() {" + " /** @type {?string} */ var x = null;" + " function g() { x = 'y'; } g(); " + " return x === 3;" + "}", "condition always evaluates to false\n" + "left : (null|string)\n" + "right: number"); } public void testTypeInferenceWithNoEntry1() throws Exception { testTypes( "/** @param {number} x */ function f(x) {}" + "/** @constructor */ function Foo() {}" + "Foo.prototype.init = function() {" + " /** @type {?{baz: number}} */ this.bar = {baz: 3};" + "};" + "/**\n" + " * @extends {Foo}\n" + " * @constructor\n" + " */" + "function SubFoo() {}" + "/** Method */" + "SubFoo.prototype.method = function() {" + " for (var i = 0; i < 10; i++) {" + " f(this.bar);" + " f(this.bar.baz);" + " }" + "};", "actual parameter 1 of f does not match formal parameter\n" + "found : (null|{baz: number})\n" + "required: number"); } public void testTypeInferenceWithNoEntry2() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @param {number} x */ function f(x) {}" + "/** @param {!Object} x */ function g(x) {}" + "/** @constructor */ function Foo() {}" + "Foo.prototype.init = function() {" + " /** @type {?{baz: number}} */ this.bar = {baz: 3};" + "};" + "/**\n" + " * @extends {Foo}\n" + " * @constructor\n" + " */" + "function SubFoo() {}" + "/** Method */" + "SubFoo.prototype.method = function() {" + " for (var i = 0; i < 10; i++) {" + " f(this.bar);" + " goog.asserts.assert(this.bar);" + " g(this.bar);" + " }" + "};", "actual parameter 1 of f does not match formal parameter\n" + "found : (null|{baz: number})\n" + "required: number"); } public void testForwardPropertyReference() throws Exception { testTypes("/** @constructor */ var Foo = function() { this.init(); };" + "/** @return {string} */" + "Foo.prototype.getString = function() {" + " return this.number_;" + "};" + "Foo.prototype.init = function() {" + " /** @type {number} */" + " this.number_ = 3;" + "};", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testNoForwardTypeDeclaration() throws Exception { testTypes( "/** @param {MyType} x */ function f(x) {}", "Bad type annotation. Unknown type MyType"); } public void testNoForwardTypeDeclarationAndNoBraces() throws Exception { testTypes("/** @return The result. */ function f() {}"); } public void testForwardTypeDeclaration1() throws Exception { testClosureTypes( // malformed addDependency calls shouldn't cause a crash "goog.addDependency();" + "goog.addDependency('y', [goog]);" + "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x \n * @return {number} */" + "function f(x) { return 3; }", null); } public void testForwardTypeDeclaration2() throws Exception { String f = "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */ function f(x) { }"; testClosureTypes(f, null); testClosureTypes(f + "f(3);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (MyType|null)"); } public void testForwardTypeDeclaration3() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */ function f(x) { return x; }" + "/** @constructor */ var MyType = function() {};" + "f(3);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (MyType|null)"); } public void testForwardTypeDeclaration4() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */ function f(x) { return x; }" + "/** @constructor */ var MyType = function() {};" + "f(new MyType());", null); } public void testForwardTypeDeclaration5() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/**\n" + " * @constructor\n" + " * @extends {MyType}\n" + " */ var YourType = function() {};" + "/** @override */ YourType.prototype.method = function() {};", "Could not resolve type in @extends tag of YourType"); } public void testForwardTypeDeclaration6() throws Exception { testClosureTypesMultipleWarnings( "goog.addDependency('zzz.js', ['MyType'], []);" + "/**\n" + " * @constructor\n" + " * @implements {MyType}\n" + " */ var YourType = function() {};" + "/** @override */ YourType.prototype.method = function() {};", Lists.newArrayList( "Could not resolve type in @implements tag of YourType", "property method not defined on any superclass of YourType")); } public void testForwardTypeDeclaration7() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType=} x */" + "function f(x) { return x == undefined; }", null); } public void testForwardTypeDeclaration8() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */" + "function f(x) { return x.name == undefined; }", null); } public void testForwardTypeDeclaration9() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType} x */" + "function f(x) { x.name = 'Bob'; }", null); } public void testForwardTypeDeclaration10() throws Exception { String f = "goog.addDependency('zzz.js', ['MyType'], []);" + "/** @param {MyType|number} x */ function f(x) { }"; testClosureTypes(f, null); testClosureTypes(f + "f(3);", null); testClosureTypes(f + "f('3');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: (MyType|null|number)"); } public void testForwardTypeDeclaration12() throws Exception { // We assume that {Function} types can produce anything, and don't // want to type-check them. testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/**\n" + " * @param {!Function} ctor\n" + " * @return {MyType}\n" + " */\n" + "function f(ctor) { return new ctor(); }", null); } public void testForwardTypeDeclaration13() throws Exception { // Some projects use {Function} registries to register constructors // that aren't in their binaries. We want to make sure we can pass these // around, but still do other checks on them. testClosureTypes( "goog.addDependency('zzz.js', ['MyType'], []);" + "/**\n" + " * @param {!Function} ctor\n" + " * @return {MyType}\n" + " */\n" + "function f(ctor) { return (new ctor()).impossibleProp; }", "Property impossibleProp never defined on ?"); } public void testDuplicateTypeDef() throws Exception { testTypes( "var goog = {};" + "/** @constructor */ goog.Bar = function() {};" + "/** @typedef {number} */ goog.Bar;", "variable goog.Bar redefined with type None, " + "original definition at [testcode]:1 " + "with type function (new:goog.Bar): undefined"); } public void testTypeDef1() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f(3);"); } public void testTypeDef2() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f('3');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testTypeDef3() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number} */ var Bar;" + "/** @param {Bar} x */ function f(x) {}" + "f('3');", "actual parameter 1 of f does not match formal parameter\n" + "found : string\n" + "required: number"); } public void testTypeDef4() throws Exception { testTypes( "/** @constructor */ function A() {}" + "/** @constructor */ function B() {}" + "/** @typedef {(A|B)} */ var AB;" + "/** @param {AB} x */ function f(x) {}" + "f(new A()); f(new B()); f(1);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (A|B|null)"); } public void testTypeDef5() throws Exception { // Notice that the error message is slightly different than // the one for testTypeDef4, even though they should be the same. // This is an implementation detail necessary for NamedTypes work out // OK, and it should change if NamedTypes ever go away. testTypes( "/** @param {AB} x */ function f(x) {}" + "/** @constructor */ function A() {}" + "/** @constructor */ function B() {}" + "/** @typedef {(A|B)} */ var AB;" + "f(new A()); f(new B()); f(1);", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: (A|B|null)"); } public void testCircularTypeDef() throws Exception { testTypes( "var goog = {};" + "/** @typedef {number|Array.} */ goog.Bar;" + "/** @param {goog.Bar} x */ function f(x) {}" + "f(3); f([3]); f([[3]]);"); } public void testGetTypedPercent1() throws Exception { String js = "var id = function(x) { return x; }\n" + "var id2 = function(x) { return id(x); }"; assertEquals(50.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent2() throws Exception { String js = "var x = {}; x.y = 1;"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent3() throws Exception { String js = "var f = function(x) { x.a = x.b; }"; assertEquals(50.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent4() throws Exception { String js = "var n = {};\n /** @constructor */ n.T = function() {};\n" + "/** @type n.T */ var x = new n.T();"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent5() throws Exception { String js = "/** @enum {number} */ keys = {A: 1,B: 2,C: 3};"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testGetTypedPercent6() throws Exception { String js = "a = {TRUE: 1, FALSE: 0};"; assertEquals(100.0, getTypedPercent(js), 0.1); } private double getTypedPercent(String js) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); TypeCheck t = makeTypeCheck(); t.processForTesting(null, n); return t.getTypedPercent(); } private ObjectType getInstanceType(Node js1Node) { JSType type = js1Node.getFirstChild().getJSType(); assertNotNull(type); assertTrue(type instanceof FunctionType); FunctionType functionType = (FunctionType) type; assertTrue(functionType.isConstructor()); return functionType.getInstanceType(); } public void testPrototypePropertyReference() throws Exception { TypeCheckResult p = parseAndTypeCheckWithScope("" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @param {number} a */\n" + "Foo.prototype.bar = function(a){};\n" + "/** @param {Foo} f */\n" + "function baz(f) {\n" + " Foo.prototype.bar.call(f, 3);\n" + "}"); assertEquals(0, compiler.getErrorCount()); assertEquals(0, compiler.getWarningCount()); assertTrue(p.scope.getVar("Foo").getType() instanceof FunctionType); FunctionType fooType = (FunctionType) p.scope.getVar("Foo").getType(); assertEquals("function (this:Foo, number): undefined", fooType.getPrototype().getPropertyType("bar").toString()); } public void testResolvingNamedTypes() throws Exception { String js = "" + "/** @constructor */\n" + "var Foo = function() {}\n" + "/** @param {number} a */\n" + "Foo.prototype.foo = function(a) {\n" + " return this.baz().toString();\n" + "};\n" + "/** @return {Baz} */\n" + "Foo.prototype.baz = function() { return new Baz(); };\n" + "/** @constructor\n" + " * @extends Foo */\n" + "var Bar = function() {};" + "/** @constructor */\n" + "var Baz = function() {};"; assertEquals(100.0, getTypedPercent(js), 0.1); } public void testMissingProperty1() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "Foo.prototype.baz = function() { this.a = 3; };"); } public void testMissingProperty2() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "Foo.prototype.baz = function() { this.b = 3; };", "Property a never defined on Foo"); } public void testMissingProperty3() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "(new Foo).a = 3;"); } public void testMissingProperty4() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "(new Foo).b = 3;", "Property a never defined on Foo"); } public void testMissingProperty5() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "/** @constructor */ function Bar() { this.a = 3; };", "Property a never defined on Foo"); } public void testMissingProperty6() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "/** @constructor \n * @extends {Foo} */ " + "function Bar() { this.a = 3; };"); } public void testMissingProperty7() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { return obj.impossible; }", "Property impossible never defined on Object"); } public void testMissingProperty8() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { return typeof obj.impossible; }"); } public void testMissingProperty9() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { if (obj.impossible) { return true; } }"); } public void testMissingProperty10() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { while (obj.impossible) { return true; } }"); } public void testMissingProperty11() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { for (;obj.impossible;) { return true; } }"); } public void testMissingProperty12() throws Exception { testTypes( "/** @param {Object} obj */" + "function foo(obj) { do { } while (obj.impossible); }"); } public void testMissingProperty13() throws Exception { testTypes( "var goog = {}; goog.isDef = function(x) { return false; };" + "/** @param {Object} obj */" + "function foo(obj) { return goog.isDef(obj.impossible); }"); } public void testMissingProperty14() throws Exception { testTypes( "var goog = {}; goog.isDef = function(x) { return false; };" + "/** @param {Object} obj */" + "function foo(obj) { return goog.isNull(obj.impossible); }", "Property isNull never defined on goog"); } public void testMissingProperty15() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.foo) { x.foo(); } }"); } public void testMissingProperty16() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { x.foo(); if (x.foo) {} }", "Property foo never defined on Object"); } public void testMissingProperty17() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (typeof x.foo == 'function') { x.foo(); } }"); } public void testMissingProperty18() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.foo instanceof Function) { x.foo(); } }"); } public void testMissingProperty19() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.bar) { if (x.foo) {} } else { x.foo(); } }", "Property foo never defined on Object"); } public void testMissingProperty20() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { if (x.foo) { } else { x.foo(); } }", "Property foo never defined on Object"); } public void testMissingProperty21() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { x.foo && x.foo(); }"); } public void testMissingProperty22() throws Exception { testTypes( "/** @param {Object} x \n * @return {boolean} */" + "function f(x) { return x.foo ? x.foo() : true; }"); } public void testMissingProperty23() throws Exception { testTypes( "function f(x) { x.impossible(); }", "Property impossible never defined on x"); } public void testMissingProperty24() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MissingType'], []);" + "/** @param {MissingType} x */" + "function f(x) { x.impossible(); }", null); } public void testMissingProperty25() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "Foo.prototype.bar = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "(new FooAlias()).bar();"); } public void testMissingProperty26() throws Exception { testTypes( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;" + "FooAlias.prototype.bar = function() {};" + "(new Foo()).bar();"); } public void testMissingProperty27() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MissingType'], []);" + "/** @param {?MissingType} x */" + "function f(x) {" + " for (var parent = x; parent; parent = parent.getParent()) {}" + "}", null); } public void testMissingProperty28() throws Exception { testTypes( "function f(obj) {" + " /** @type {*} */ obj.foo;" + " return obj.foo;" + "}"); testTypes( "function f(obj) {" + " /** @type {*} */ obj.foo;" + " return obj.foox;" + "}", "Property foox never defined on obj"); } public void testMissingProperty29() throws Exception { // This used to emit a warning. testTypes( // externs "/** @constructor */ var Foo;" + "Foo.prototype.opera;" + "Foo.prototype.opera.postError;", "", null, false); } public void testMissingProperty30() throws Exception { testTypes( "/** @return {*} */" + "function f() {" + " return {};" + "}" + "f().a = 3;" + "/** @param {Object} y */ function g(y) { return y.a; }"); } public void testMissingProperty31() throws Exception { testTypes( "/** @return {Array|number} */" + "function f() {" + " return [];" + "}" + "f().a = 3;" + "/** @param {Array} y */ function g(y) { return y.a; }"); } public void testMissingProperty32() throws Exception { testTypes( "/** @return {Array|number} */" + "function f() {" + " return [];" + "}" + "f().a = 3;" + "/** @param {Date} y */ function g(y) { return y.a; }", "Property a never defined on Date"); } public void testMissingProperty33() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { !x.foo || x.foo(); }"); } public void testMissingProperty34() throws Exception { testTypes( "/** @fileoverview \n * @suppress {missingProperties} */" + "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() { return this.a; };" + "Foo.prototype.baz = function() { this.b = 3; };"); } public void testMissingProperty35() throws Exception { // Bar has specialProp defined, so Bar|Baz may have specialProp defined. testTypes( "/** @constructor */ function Foo() {}" + "/** @constructor */ function Bar() {}" + "/** @constructor */ function Baz() {}" + "/** @param {Foo|Bar} x */ function f(x) { x.specialProp = 1; }" + "/** @param {Bar|Baz} x */ function g(x) { return x.specialProp; }"); } public void testMissingProperty36() throws Exception { // Foo has baz defined, and SubFoo has bar defined, so some objects with // bar may have baz. testTypes( "/** @constructor */ function Foo() {}" + "Foo.prototype.baz = 0;" + "/** @constructor \n * @extends {Foo} */ function SubFoo() {}" + "SubFoo.prototype.bar = 0;" + "/** @param {{bar: number}} x */ function f(x) { return x.baz; }"); } public void testMissingProperty37() throws Exception { // This used to emit a missing property warning because we couldn't // determine that the inf(Foo, {isVisible:boolean}) == SubFoo. testTypes( "/** @param {{isVisible: boolean}} x */ function f(x){" + " x.isVisible = false;" + "}" + "/** @constructor */ function Foo() {}" + "/**\n" + " * @constructor \n" + " * @extends {Foo}\n" + " */ function SubFoo() {}" + "/** @type {boolean} */ SubFoo.prototype.isVisible = true;" + "/**\n" + " * @param {Foo} x\n" + " * @return {boolean}\n" + " */\n" + "function g(x) { return x.isVisible; }"); } public void testMissingProperty38() throws Exception { testTypes( "/** @constructor */ function Foo() {}" + "/** @constructor */ function Bar() {}" + "/** @return {Foo|Bar} */ function f() { return new Foo(); }" + "f().missing;", "Property missing never defined on (Bar|Foo|null)"); } public void testMissingProperty39() throws Exception { testTypes( "/** @return {string|number} */ function f() { return 3; }" + "f().length;"); } public void testMissingProperty40() throws Exception { testClosureTypes( "goog.addDependency('zzz.js', ['MissingType'], []);" + "/** @param {(Array|MissingType)} x */" + "function f(x) { x.impossible(); }", null); } public void testMissingProperty41() throws Exception { testTypes( "/** @param {(Array|Date)} x */" + "function f(x) { if (x.impossible) x.impossible(); }"); } public void testMissingProperty42() throws Exception { testTypes( "/** @param {Object} x */" + "function f(x) { " + " if (typeof x.impossible == 'undefined') throw Error();" + " return x.impossible;" + "}"); } public void testMissingProperty43() throws Exception { testTypes( "function f(x) { " + " return /** @type {number} */ (x.impossible) && 1;" + "}"); } public void testReflectObject1() throws Exception { testClosureTypes( "var goog = {}; goog.reflect = {}; " + "goog.reflect.object = function(x, y){};" + "/** @constructor */ function A() {}" + "goog.reflect.object(A, {x: 3});", null); } public void testReflectObject2() throws Exception { testClosureTypes( "var goog = {}; goog.reflect = {}; " + "goog.reflect.object = function(x, y){};" + "/** @param {string} x */ function f(x) {}" + "/** @constructor */ function A() {}" + "goog.reflect.object(A, {x: f(1 + 1)});", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testLends1() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends */ ({bar: 1}));", "Bad type annotation. missing object name in @lends tag"); } public void testLends2() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {Foob} */ ({bar: 1}));", "Variable Foob not declared before @lends annotation."); } public void testLends3() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, {bar: 1});" + "alert(Foo.bar);", "Property bar never defined on Foo"); } public void testLends4() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {Foo} */ ({bar: 1}));" + "alert(Foo.bar);"); } public void testLends5() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, {bar: 1});" + "alert((new Foo()).bar);", "Property bar never defined on Foo"); } public void testLends6() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {Foo.prototype} */ ({bar: 1}));" + "alert((new Foo()).bar);"); } public void testLends7() throws Exception { testTypes( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {Foo.prototype|Foo} */ ({bar: 1}));", "Bad type annotation. expected closing }"); } public void testLends8() throws Exception { testTypes( "function extend(x, y) {}" + "/** @type {number} */ var Foo = 3;" + "extend(Foo, /** @lends {Foo} */ ({bar: 1}));", "May only lend properties to object types. Foo has type number."); } public void testLends9() throws Exception { testClosureTypesMultipleWarnings( "function extend(x, y) {}" + "/** @constructor */ function Foo() {}" + "extend(Foo, /** @lends {!Foo} */ ({bar: 1}));", Lists.newArrayList( "Bad type annotation. expected closing }", "Bad type annotation. missing object name in @lends tag")); } public void testLends10() throws Exception { testTypes( "function defineClass(x) { return function() {}; } " + "/** @constructor */" + "var Foo = defineClass(" + " /** @lends {Foo.prototype} */ ({/** @type {number} */ bar: 1}));" + "/** @return {string} */ function f() { return (new Foo()).bar; }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testLends11() throws Exception { testTypes( "function defineClass(x, y) { return function() {}; } " + "/** @constructor */" + "var Foo = function() {};" + "/** @return {*} */ Foo.prototype.bar = function() { return 3; };" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "var SubFoo = defineClass(Foo, " + " /** @lends {SubFoo.prototype} */ ({\n" + " /** @return {number} */ bar: function() { return 3; }}));" + "/** @return {string} */ function f() { return (new SubFoo()).bar(); }", "inconsistent return type\n" + "found : number\n" + "required: string"); } public void testDeclaredNativeTypeEquality() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Object() {};"); assertTypeEquals(registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), n.getFirstChild().getJSType()); } public void testUndefinedVar() throws Exception { Node n = parseAndTypeCheck("var undefined;"); assertTypeEquals(registry.getNativeType(JSTypeNative.VOID_TYPE), n.getFirstChild().getFirstChild().getJSType()); } public void testFlowScopeBug1() throws Exception { Node n = parseAndTypeCheck("/** @param {number} a \n" + "* @param {number} b */\n" + "function f(a, b) {\n" + "/** @type number */" + "var i = 0;" + "for (; (i + a) < b; ++i) {}}"); // check the type of the add node for i + f assertTypeEquals(registry.getNativeType(JSTypeNative.NUMBER_TYPE), n.getFirstChild().getLastChild().getLastChild().getFirstChild() .getNext().getFirstChild().getJSType()); } public void testFlowScopeBug2() throws Exception { Node n = parseAndTypeCheck("/** @constructor */ function Foo() {};\n" + "Foo.prototype.hi = false;" + "function foo(a, b) {\n" + " /** @type Array */" + " var arr;" + " /** @type number */" + " var iter;" + " for (iter = 0; iter < arr.length; ++ iter) {" + " /** @type Foo */" + " var afoo = arr[iter];" + " afoo;" + " }" + "}"); // check the type of afoo when referenced assertTypeEquals(registry.createNullableType(registry.getType("Foo")), n.getLastChild().getLastChild().getLastChild().getLastChild() .getLastChild().getLastChild().getJSType()); } public void testAddSingletonGetter() { Node n = parseAndTypeCheck( "/** @constructor */ function Foo() {};\n" + "goog.addSingletonGetter(Foo);"); ObjectType o = (ObjectType) n.getFirstChild().getJSType(); assertEquals("function (): Foo", o.getPropertyType("getInstance").toString()); assertEquals("Foo", o.getPropertyType("instance_").toString()); } public void testTypeCheckStandaloneAST() throws Exception { Node n = compiler.parseTestCode("function Foo() { }"); typeCheck(n); MemoizedScopeCreator scopeCreator = new MemoizedScopeCreator( new TypedScopeCreator(compiler)); Scope topScope = scopeCreator.createScope(n, null); Node second = compiler.parseTestCode("new Foo"); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, second); externAndJsRoot.setIsSyntheticBlock(true); new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry, topScope, scopeCreator, CheckLevel.WARNING, CheckLevel.OFF) .process(null, second); assertEquals(1, compiler.getWarningCount()); assertEquals("cannot instantiate non-constructor", compiler.getWarnings()[0].description); } public void testUpdateParameterTypeOnClosure() throws Exception { testTypes( "/**\n" + "* @constructor\n" + "* @param {*=} opt_value\n" + "* @return {?}\n" + "*/\n" + "function Object(opt_value) {}\n" + "/**\n" + "* @constructor\n" + "* @param {...*} var_args\n" + "*/\n" + "function Function(var_args) {}\n" + "/**\n" + "* @type {Function}\n" + "*/\n" + // The line below sets JSDocInfo on Object so that the type of the // argument to function f has JSDoc through its prototype chain. "Object.prototype.constructor = function() {};\n", "/**\n" + "* @param {function(): boolean} fn\n" + "*/\n" + "function f(fn) {}\n" + "f(function(g) { });\n", null, false); } public void testTemplatedThisType1() throws Exception { testTypes( "/** @constructor */\n" + "function Foo() {}\n" + "/**\n" + " * @this {T}\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "Foo.prototype.method = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {}\n" + "var g = new Bar().method();\n" + "/**\n" + " * @param {number} a\n" + " */\n" + "function compute(a) {};\n" + "compute(g);\n", "actual parameter 1 of compute does not match formal parameter\n" + "found : Bar\n" + "required: number"); } public void testTemplatedThisType2() throws Exception { testTypes( "/**\n" + " * @this {Array.|{length:number}}\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "Array.prototype.method = function() {};\n" + "(function(){\n" + " Array.prototype.method.call(arguments);" + "})();"); } public void testTemplateType1() throws Exception { testTypes( "/**\n" + "* @param {T} x\n" + "* @param {T} y\n" + "* @param {function(this:T, ...)} z\n" + "* @template T\n" + "*/\n" + "function f(x, y, z) {}\n" + "f(this, this, function() { this });"); } public void testTemplateType2() throws Exception { // "this" types need to be coerced for ES3 style function or left // allow for ES5-strict methods. testTypes( "/**\n" + "* @param {T} x\n" + "* @param {function(this:T, ...)} y\n" + "* @template T\n" + "*/\n" + "function f(x, y) {}\n" + "f(0, function() {});"); } public void testTemplateType3() throws Exception { testTypes( "/**" + " * @param {T} v\n" + " * @param {function(T)} f\n" + " * @template T\n" + " */\n" + "function call(v, f) { f.call(null, v); }" + "/** @type {string} */ var s;" + "call(3, function(x) {" + " x = true;" + " s = x;" + "});", "assignment\n" + "found : boolean\n" + "required: string"); } public void testTemplateType4() throws Exception { testTypes( "/**" + " * @param {...T} p\n" + " * @return {T} \n" + " * @template T\n" + " */\n" + "function fn(p) { return p; }\n" + "/** @type {!Object} */ var x;" + "x = fn(3, null);", "assignment\n" + "found : (null|number)\n" + "required: Object"); } public void testTemplateType5() throws Exception { compiler.getOptions().setCodingConvention(new GoogleCodingConvention()); testTypes( "var CGI_PARAM_RETRY_COUNT = 'rc';" + "" + "/**" + " * @param {...T} p\n" + " * @return {T} \n" + " * @template T\n" + " */\n" + "function fn(p) { return p; }\n" + "/** @type {!Object} */ var x;" + "" + "/** @return {void} */\n" + "function aScope() {\n" + " x = fn(CGI_PARAM_RETRY_COUNT, 1);\n" + "}", "assignment\n" + "found : (number|string)\n" + "required: Object"); } public void testTemplateType6() throws Exception { testTypes( "/**" + " * @param {Array.} arr \n" + " * @param {?function(T)} f \n" + " * @return {T} \n" + " * @template T\n" + " */\n" + "function fn(arr, f) { return arr[0]; }\n" + "/** @param {Array.} arr */ function g(arr) {" + " /** @type {!Object} */ var x = fn.call(null, arr, null);" + "}", "initializing variable\n" + "found : number\n" + "required: Object"); } public void testTemplateType7() throws Exception { // TODO(johnlenz): As the @this type for Array.prototype.push includes // "{length:number}" (and this includes "Array.") we don't // get a type warning here. Consider special-casing array methods. testTypes( "/** @type {!Array.} */\n" + "var query = [];\n" + "query.push(1);\n"); } public void testTemplateType8() throws Exception { testTypes( "/** @constructor \n" + " * @classTemplate S,T\n" + " */\n" + "function Bar() {}\n" + "/**" + " * @param {Bar.} bar \n" + " * @return {T} \n" + " * @template T\n" + " */\n" + "function fn(bar) {}\n" + "/** @param {Bar.} bar */ function g(bar) {" + " /** @type {!Object} */ var x = fn(bar);" + "}", "initializing variable\n" + "found : number\n" + "required: Object"); } public void disable_testBadTemplateType4() throws Exception { // TODO(johnlenz): Add a check for useless of template types. // Unless there are at least two references to a Template type in // a definition it isn't useful. testTypes( "/**\n" + "* @template T\n" + "*/\n" + "function f() {}\n" + "f();", FunctionTypeBuilder.TEMPLATE_TYPE_EXPECTED.format()); } public void disable_testBadTemplateType5() throws Exception { // TODO(johnlenz): Add a check for useless of template types. // Unless there are at least two references to a Template type in // a definition it isn't useful. testTypes( "/**\n" + "* @template T\n" + "* @return {T}\n" + "*/\n" + "function f() {}\n" + "f();", FunctionTypeBuilder.TEMPLATE_TYPE_EXPECTED.format()); } public void disable_testFunctionLiteralUndefinedThisArgument() throws Exception { // TODO(johnlenz): this was a weird error. We should add a general // restriction on what is accepted for T. Something like: // "@template T of {Object|string}" or some such. testTypes("" + "/**\n" + " * @param {function(this:T, ...)?} fn\n" + " * @param {?T} opt_obj\n" + " * @template T\n" + " */\n" + "function baz(fn, opt_obj) {}\n" + "baz(function() { this; });", "Function literal argument refers to undefined this argument"); } public void testFunctionLiteralDefinedThisArgument() throws Exception { testTypes("" + "/**\n" + " * @param {function(this:T, ...)?} fn\n" + " * @param {?T} opt_obj\n" + " * @template T\n" + " */\n" + "function baz(fn, opt_obj) {}\n" + "baz(function() { this; }, {});"); } public void testFunctionLiteralDefinedThisArgument2() throws Exception { testTypes("" + "/** @param {string} x */ function f(x) {}" + "/**\n" + " * @param {?function(this:T, ...)} fn\n" + " * @param {T=} opt_obj\n" + " * @template T\n" + " */\n" + "function baz(fn, opt_obj) {}\n" + "function g() { baz(function() { f(this.length); }, []); }", "actual parameter 1 of f does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testFunctionLiteralUnreadNullThisArgument() throws Exception { testTypes("" + "/**\n" + " * @param {function(this:T, ...)?} fn\n" + " * @param {?T} opt_obj\n" + " * @template T\n" + " */\n" + "function baz(fn, opt_obj) {}\n" + "baz(function() {}, null);"); } public void testUnionTemplateThisType() throws Exception { testTypes( "/** @constructor */ function F() {}" + "/** @return {F|Array} */ function g() { return []; }" + "/** @param {F} x */ function h(x) { }" + "/**\n" + "* @param {T} x\n" + "* @param {function(this:T, ...)} y\n" + "* @template T\n" + "*/\n" + "function f(x, y) {}\n" + "f(g(), function() { h(this); });", "actual parameter 1 of h does not match formal parameter\n" + "found : (Array|F|null)\n" + "required: (F|null)"); } public void testActiveXObject() throws Exception { testTypes( "/** @type {Object} */ var x = new ActiveXObject();" + "/** @type { {impossibleProperty} } */ var y = new ActiveXObject();"); } public void testRecordType1() throws Exception { testTypes( "/** @param {{prop: number}} x */" + "function f(x) {}" + "f({});", "actual parameter 1 of f does not match formal parameter\n" + "found : {prop: (number|undefined)}\n" + "required: {prop: number}"); } public void testRecordType2() throws Exception { testTypes( "/** @param {{prop: (number|undefined)}} x */" + "function f(x) {}" + "f({});"); } public void testRecordType3() throws Exception { testTypes( "/** @param {{prop: number}} x */" + "function f(x) {}" + "f({prop: 'x'});", "actual parameter 1 of f does not match formal parameter\n" + "found : {prop: (number|string)}\n" + "required: {prop: number}"); } public void testRecordType4() throws Exception { // Notice that we do not do flow-based inference on the object type: // We don't try to prove that x.prop may not be string until x // gets passed to g. testClosureTypesMultipleWarnings( "/** @param {{prop: (number|undefined)}} x */" + "function f(x) {}" + "/** @param {{prop: (string|undefined)}} x */" + "function g(x) {}" + "var x = {}; f(x); g(x);", Lists.newArrayList( "actual parameter 1 of f does not match formal parameter\n" + "found : {prop: (number|string|undefined)}\n" + "required: {prop: (number|undefined)}", "actual parameter 1 of g does not match formal parameter\n" + "found : {prop: (number|string|undefined)}\n" + "required: {prop: (string|undefined)}")); } public void testRecordType5() throws Exception { testTypes( "/** @param {{prop: (number|undefined)}} x */" + "function f(x) {}" + "/** @param {{otherProp: (string|undefined)}} x */" + "function g(x) {}" + "var x = {}; f(x); g(x);"); } public void testRecordType6() throws Exception { testTypes( "/** @return {{prop: (number|undefined)}} x */" + "function f() { return {}; }"); } public void testRecordType7() throws Exception { testTypes( "/** @return {{prop: (number|undefined)}} x */" + "function f() { var x = {}; g(x); return x; }" + "/** @param {number} x */" + "function g(x) {}", "actual parameter 1 of g does not match formal parameter\n" + "found : {prop: (number|undefined)}\n" + "required: number"); } public void testRecordType8() throws Exception { testTypes( "/** @return {{prop: (number|string)}} x */" + "function f() { var x = {prop: 3}; g(x.prop); return x; }" + "/** @param {string} x */" + "function g(x) {}", "actual parameter 1 of g does not match formal parameter\n" + "found : number\n" + "required: string"); } public void testDuplicateRecordFields1() throws Exception { testTypes("/**" + "* @param {{x:string, x:number}} a" + "*/" + "function f(a) {};", "Parse error. Duplicate record field x"); } public void testDuplicateRecordFields2() throws Exception { testTypes("/**" + "* @param {{name:string,number:x,number:y}} a" + " */" + "function f(a) {};", new String[] {"Bad type annotation. Unknown type x", "Parse error. Duplicate record field number", "Bad type annotation. Unknown type y"}); } public void testMultipleExtendsInterface1() throws Exception { testTypes("/** @interface */ function base1() {}\n" + "/** @interface */ function base2() {}\n" + "/** @interface\n" + "* @extends {base1}\n" + "* @extends {base2}\n" + "*/\n" + "function derived() {}"); } public void testMultipleExtendsInterface2() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @desc description */Int0.prototype.foo = function() {};" + "/** @interface \n @extends {Int0} \n @extends {Int1} */" + "function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "property foo on interface Int0 is not implemented by type Foo"); } public void testMultipleExtendsInterface3() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @desc description */Int1.prototype.foo = function() {};" + "/** @interface \n @extends {Int0} \n @extends {Int1} */" + "function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "property foo on interface Int1 is not implemented by type Foo"); } public void testMultipleExtendsInterface4() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @interface \n @extends {Int0} \n @extends {Int1} \n" + " @extends {number} */" + "function Int2() {};" + "/** @constructor\n @implements {Int2} */function Foo() {};", "Int2 @extends non-object type number"); } public void testMultipleExtendsInterface5() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @constructor */function Int1() {};" + "/** @desc description @ return {string} x */" + "/** @interface \n @extends {Int0} \n @extends {Int1} */" + "function Int2() {};", "Int2 cannot extend this type; interfaces can only extend interfaces"); } public void testMultipleExtendsInterface6() throws Exception { testTypes( "/** @interface */function Super1() {};" + "/** @interface */function Super2() {};" + "/** @param {number} bar */Super2.prototype.foo = function(bar) {};" + "/** @interface\n @extends {Super1}\n " + "@extends {Super2} */function Sub() {};" + "/** @override\n @param {string} bar */Sub.prototype.foo =\n" + "function(bar) {};", "mismatch of the foo property type and the type of the property it " + "overrides from superclass Super2\n" + "original: function (this:Super2, number): undefined\n" + "override: function (this:Sub, string): undefined"); } public void testMultipleExtendsInterfaceAssignment() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */ var I2 = function() {}\n" + "/** @interface\n@extends {I1}\n@extends {I2}*/" + "var I3 = function() {};\n" + "/** @constructor\n@implements {I3}*/var T = function() {};\n" + "var t = new T();\n" + "/** @type {I1} */var i1 = t;\n" + "/** @type {I2} */var i2 = t;\n" + "/** @type {I3} */var i3 = t;\n" + "i1 = i3;\n" + "i2 = i3;\n"); } public void testMultipleExtendsInterfaceParamPass() throws Exception { testTypes("/** @interface */var I1 = function() {};\n" + "/** @interface */ var I2 = function() {}\n" + "/** @interface\n@extends {I1}\n@extends {I2}*/" + "var I3 = function() {};\n" + "/** @constructor\n@implements {I3}*/var T = function() {};\n" + "var t = new T();\n" + "/** @param x I1 \n@param y I2\n@param z I3*/function foo(x,y,z){};\n" + "foo(t,t,t)\n"); } public void testBadMultipleExtendsClass() throws Exception { testTypes("/** @constructor */ function base1() {}\n" + "/** @constructor */ function base2() {}\n" + "/** @constructor\n" + "* @extends {base1}\n" + "* @extends {base2}\n" + "*/\n" + "function derived() {}", "Bad type annotation. type annotation incompatible " + "with other annotations"); } public void testInterfaceExtendsResolution() throws Exception { testTypes("/** @interface \n @extends {A} */ function B() {};\n" + "/** @constructor \n @implements {B} */ function C() {};\n" + "/** @interface */ function A() {};"); } public void testPropertyCanBeDefinedInObject() throws Exception { testTypes("/** @interface */ function I() {};" + "I.prototype.bar = function() {};" + "/** @type {Object} */ var foo;" + "foo.bar();"); } private void checkObjectType(ObjectType objectType, String propertyName, JSType expectedType) { assertTrue("Expected " + objectType.getReferenceName() + " to have property " + propertyName, objectType.hasProperty(propertyName)); assertTypeEquals("Expected " + objectType.getReferenceName() + "'s property " + propertyName + " to have type " + expectedType, expectedType, objectType.getPropertyType(propertyName)); } public void testExtendedInterfacePropertiesCompatibility1() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @interface \n @extends {Int0} \n @extends {Int1} */" + "function Int2() {};", "Interface Int2 has a property foo with incompatible types in its " + "super interfaces Int0 and Int1"); } public void testExtendedInterfacePropertiesCompatibility2() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @interface */function Int2() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @type {Object} */" + "Int2.prototype.foo;" + "/** @interface \n @extends {Int0} \n @extends {Int1} \n" + "@extends {Int2}*/" + "function Int3() {};", new String[] { "Interface Int3 has a property foo with incompatible types in " + "its super interfaces Int0 and Int1", "Interface Int3 has a property foo with incompatible types in " + "its super interfaces Int1 and Int2" }); } public void testExtendedInterfacePropertiesCompatibility3() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @interface \n @extends {Int1} */ function Int2() {};" + "/** @interface \n @extends {Int0} \n @extends {Int2} */" + "function Int3() {};", "Interface Int3 has a property foo with incompatible types in its " + "super interfaces Int0 and Int1"); } public void testExtendedInterfacePropertiesCompatibility4() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface \n @extends {Int0} */ function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @interface */function Int2() {};" + "/** @interface \n @extends {Int2} */ function Int3() {};" + "/** @type {string} */" + "Int2.prototype.foo;" + "/** @interface \n @extends {Int1} \n @extends {Int3} */" + "function Int4() {};", "Interface Int4 has a property foo with incompatible types in its " + "super interfaces Int0 and Int2"); } public void testExtendedInterfacePropertiesCompatibility5() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @interface \n @extends {Int1} */ function Int2() {};" + "/** @interface \n @extends {Int0} \n @extends {Int2} */" + "function Int3() {};" + "/** @interface */function Int4() {};" + "/** @type {number} */" + "Int4.prototype.foo;" + "/** @interface \n @extends {Int3} \n @extends {Int4} */" + "function Int5() {};", new String[] { "Interface Int3 has a property foo with incompatible types in its" + " super interfaces Int0 and Int1", "Interface Int5 has a property foo with incompatible types in its" + " super interfaces Int1 and Int4"}); } public void testExtendedInterfacePropertiesCompatibility6() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @interface \n @extends {Int1} */ function Int2() {};" + "/** @interface \n @extends {Int0} \n @extends {Int2} */" + "function Int3() {};" + "/** @interface */function Int4() {};" + "/** @type {string} */" + "Int4.prototype.foo;" + "/** @interface \n @extends {Int3} \n @extends {Int4} */" + "function Int5() {};", "Interface Int3 has a property foo with incompatible types in its" + " super interfaces Int0 and Int1"); } public void testExtendedInterfacePropertiesCompatibility7() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.foo;" + "/** @interface \n @extends {Int1} */ function Int2() {};" + "/** @interface \n @extends {Int0} \n @extends {Int2} */" + "function Int3() {};" + "/** @interface */function Int4() {};" + "/** @type {Object} */" + "Int4.prototype.foo;" + "/** @interface \n @extends {Int3} \n @extends {Int4} */" + "function Int5() {};", new String[] { "Interface Int3 has a property foo with incompatible types in its" + " super interfaces Int0 and Int1", "Interface Int5 has a property foo with incompatible types in its" + " super interfaces Int1 and Int4"}); } public void testExtendedInterfacePropertiesCompatibility8() throws Exception { testTypes( "/** @interface */function Int0() {};" + "/** @interface */function Int1() {};" + "/** @type {number} */" + "Int0.prototype.foo;" + "/** @type {string} */" + "Int1.prototype.bar;" + "/** @interface \n @extends {Int1} */ function Int2() {};" + "/** @interface \n @extends {Int0} \n @extends {Int2} */" + "function Int3() {};" + "/** @interface */function Int4() {};" + "/** @type {Object} */" + "Int4.prototype.foo;" + "/** @type {Null} */" + "Int4.prototype.bar;" + "/** @interface \n @extends {Int3} \n @extends {Int4} */" + "function Int5() {};", new String[] { "Interface Int5 has a property bar with incompatible types in its" + " super interfaces Int1 and Int4", "Interface Int5 has a property foo with incompatible types in its" + " super interfaces Int0 and Int4"}); } public void testGenerics1() throws Exception { String fnDecl = "/** \n" + " * @param {T} x \n" + " * @param {function(T):T} y \n" + " * @template T\n" + " */ \n" + "function f(x,y) { return y(x); }\n"; testTypes( fnDecl + "/** @type {string} */" + "var out;" + "/** @type {string} */" + "var result = f('hi', function(x){ out = x; return x; });"); testTypes( fnDecl + "/** @type {string} */" + "var out;" + "var result = f(0, function(x){ out = x; return x; });", "assignment\n" + "found : number\n" + "required: string"); testTypes( fnDecl + "var out;" + "/** @type {string} */" + "var result = f(0, function(x){ out = x; return x; });", "assignment\n" + "found : number\n" + "required: string"); } public void testFilter0() throws Exception { testTypes( "/**\n" + " * @param {T} arr\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "var filter = function(arr){};\n" + "/** @type {!Array.} */" + "var arr;\n" + "/** @type {!Array.} */" + "var result = filter(arr);"); } public void testFilter1() throws Exception { testTypes( "/**\n" + " * @param {!Array.} arr\n" + " * @return {!Array.}\n" + " * @template T\n" + " */\n" + "var filter = function(arr){};\n" + "/** @type {!Array.} */" + "var arr;\n" + "/** @type {!Array.} */" + "var result = filter(arr);"); } public void testFilter2() throws Exception { testTypes( "/**\n" + " * @param {!Array.} arr\n" + " * @return {!Array.}\n" + " * @template T\n" + " */\n" + "var filter = function(arr){};\n" + "/** @type {!Array.} */" + "var arr;\n" + "/** @type {!Array.} */" + "var result = filter(arr);", "initializing variable\n" + "found : Array.\n" + "required: Array."); } public void testFilter3() throws Exception { testTypes( "/**\n" + " * @param {Array.} arr\n" + " * @return {Array.}\n" + " * @template T\n" + " */\n" + "var filter = function(arr){};\n" + "/** @type {Array.} */" + "var arr;\n" + "/** @type {Array.} */" + "var result = filter(arr);", "initializing variable\n" + "found : (Array.|null)\n" + "required: (Array.|null)"); } public void testBackwardsInferenceGoogArrayFilter1() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {Array.} */" + "var arr;\n" + "/** @type {!Array.} */" + "var result = goog.array.filter(" + " arr," + " function(item,index,src) {return false;});", "initializing variable\n" + "found : Array.\n" + "required: Array."); } public void testBackwardsInferenceGoogArrayFilter2() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {number} */" + "var out;" + "/** @type {Array.} */" + "var arr;\n" + "var out4 = goog.array.filter(" + " arr," + " function(item,index,src) {out = item;});", "assignment\n" + "found : string\n" + "required: number"); } public void testBackwardsInferenceGoogArrayFilter3() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string} */" + "var out;" + "/** @type {Array.} */ var arr;\n" + "var result = goog.array.filter(" + " arr," + " function(item,index,src) {out = index;});", "assignment\n" + "found : number\n" + "required: string"); } public void testBackwardsInferenceGoogArrayFilter4() throws Exception { testClosureTypes( CLOSURE_DEFS + "/** @type {string} */" + "var out;" + "/** @type {Array.} */ var arr;\n" + "var out4 = goog.array.filter(" + " arr," + " function(item,index,srcArr) {out = srcArr;});", "assignment\n" + "found : (null|{length: number})\n" + "required: string"); } public void testCatchExpression1() throws Exception { testTypes( "function fn() {" + " /** @type {number} */" + " var out = 0;" + " try {\n" + " foo();\n" + " } catch (/** @type {string} */ e) {\n" + " out = e;" + " }" + "}\n", "assignment\n" + "found : string\n" + "required: number"); } public void testCatchExpression2() throws Exception { testTypes( "function fn() {" + " /** @type {number} */" + " var out = 0;" + " /** @type {string} */" + " var e;" + " try {\n" + " foo();\n" + " } catch (e) {\n" + " out = e;" + " }" + "}\n"); } public void testTemplatized1() throws Exception { testTypes( "/** @type {!Array.} */" + "var arr1 = [];\n" + "/** @type {!Array.} */" + "var arr2 = [];\n" + "arr1 = arr2;", "assignment\n" + "found : Array.\n" + "required: Array."); } public void testTemplatized2() throws Exception { testTypes( "/** @type {!Array.} */" + "var arr1 = /** @type {!Array.} */([]);\n", "initializing variable\n" + "found : Array.\n" + "required: Array."); } public void testTemplatized3() throws Exception { testTypes( "/** @type {Array.} */" + "var arr1 = /** @type {!Array.} */([]);\n", "initializing variable\n" + "found : Array.\n" + "required: (Array.|null)"); } public void testTemplatized4() throws Exception { testTypes( "/** @type {Array.} */" + "var arr1 = [];\n" + "/** @type {Array.} */" + "var arr2 = arr1;\n", "initializing variable\n" + "found : (Array.|null)\n" + "required: (Array.|null)"); } public void testTemplatized5() throws Exception { testTypes( "/**\n" + " * @param {Object.} obj\n" + " * @return {boolean|undefined}\n" + " * @template T\n" + " */\n" + "var some = function(obj) {" + " for (var key in obj) if (obj[key]) return true;" + "};" + "/** @return {!Array} */ function f() { return []; }" + "/** @return {!Array.} */ function g() { return []; }" + "some(f());\n" + "some(g());\n"); } public void testTemplatizedTypeSubtypes2() throws Exception { JSType arrayOfNumber = createTemplatizedType( ARRAY_TYPE, NUMBER_TYPE); JSType arrayOfString = createTemplatizedType( ARRAY_TYPE, STRING_TYPE); assertFalse(arrayOfString.isSubtype(createUnionType(arrayOfNumber, NULL_VOID))); } private void testTypes(String js) throws Exception { testTypes(js, (String) null); } private void testTypes(String js, String description) throws Exception { testTypes(js, description, false); } private void testTypes(String js, DiagnosticType type) throws Exception { testTypes(js, type.format(), false); } private void testClosureTypes(String js, String description) throws Exception { testClosureTypesMultipleWarnings(js, description == null ? null : Lists.newArrayList(description)); } private void testClosureTypesMultipleWarnings( String js, List descriptions) throws Exception { Node n = compiler.parseTestCode(js); Node externs = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); // For processing goog.addDependency for forward typedefs. new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) .process(null, n); CodingConvention convention = compiler.getCodingConvention(); new TypeCheck(compiler, new ClosureReverseAbstractInterpreter( convention, registry).append( new SemanticReverseAbstractInterpreter( convention, registry)) .getFirst(), registry) .processForTesting(null, n); assertEquals( "unexpected error(s) : " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); if (descriptions == null) { assertEquals( "unexpected warning(s) : " + Joiner.on(", ").join(compiler.getWarnings()), 0, compiler.getWarningCount()); } else { assertEquals( "unexpected warning(s) : " + Joiner.on(", ").join(compiler.getWarnings()), descriptions.size(), compiler.getWarningCount()); Set actualWarningDescriptions = Sets.newHashSet(); for (int i = 0; i < descriptions.size(); i++) { actualWarningDescriptions.add(compiler.getWarnings()[i].description); } assertEquals( Sets.newHashSet(descriptions), actualWarningDescriptions); } } void testTypes(String js, String description, boolean isError) throws Exception { testTypes(DEFAULT_EXTERNS, js, description, isError); } void testTypes(String externs, String js, String description, boolean isError) throws Exception { parseAndTypeCheck(externs, js); JSError[] errors = compiler.getErrors(); if (description != null && isError) { assertTrue("expected an error", errors.length > 0); assertEquals(description, errors[0].description); errors = Arrays.asList(errors).subList(1, errors.length).toArray( new JSError[errors.length - 1]); } if (errors.length > 0) { fail("unexpected error(s):\n" + Joiner.on("\n").join(errors)); } JSError[] warnings = compiler.getWarnings(); if (description != null && !isError) { assertTrue("expected a warning", warnings.length > 0); assertEquals(description, warnings[0].description); warnings = Arrays.asList(warnings).subList(1, warnings.length).toArray( new JSError[warnings.length - 1]); } if (warnings.length > 0) { fail("unexpected warnings(s):\n" + Joiner.on("\n").join(warnings)); } } /** * Parses and type checks the JavaScript code. */ private Node parseAndTypeCheck(String js) { return parseAndTypeCheck(DEFAULT_EXTERNS, js); } private Node parseAndTypeCheck(String externs, String js) { return parseAndTypeCheckWithScope(externs, js).root; } /** * Parses and type checks the JavaScript code and returns the Scope used * whilst type checking. */ private TypeCheckResult parseAndTypeCheckWithScope(String js) { return parseAndTypeCheckWithScope(DEFAULT_EXTERNS, js); } private TypeCheckResult parseAndTypeCheckWithScope( String externs, String js) { compiler.init( Lists.newArrayList(SourceFile.fromCode("[externs]", externs)), Lists.newArrayList(SourceFile.fromCode("[testcode]", js)), compiler.getOptions()); Node n = compiler.getInput(new InputId("[testcode]")).getAstRoot(compiler); Node externsNode = compiler.getInput(new InputId("[externs]")) .getAstRoot(compiler); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); Scope s = makeTypeCheck().processForTesting(externsNode, n); return new TypeCheckResult(n, s); } private Node typeCheck(Node n) { Node externsNode = new Node(Token.BLOCK); Node externAndJsRoot = new Node(Token.BLOCK, externsNode, n); externAndJsRoot.setIsSyntheticBlock(true); makeTypeCheck().processForTesting(null, n); return n; } private TypeCheck makeTypeCheck() { return new TypeCheck( compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), registry), registry, reportMissingOverrides, CheckLevel.OFF); } void testTypes(String js, String[] warnings) throws Exception { Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Node externsNode = new Node(Token.BLOCK); // create a parent node for the extern and source blocks new Node(Token.BLOCK, externsNode, n); makeTypeCheck().processForTesting(null, n); assertEquals(0, compiler.getErrorCount()); if (warnings != null) { assertEquals(warnings.length, compiler.getWarningCount()); JSError[] messages = compiler.getWarnings(); for (int i = 0; i < warnings.length && i < compiler.getWarningCount(); i++) { assertEquals(warnings[i], messages[i].description); } } else { assertEquals(0, compiler.getWarningCount()); } } String suppressMissingProperty(String ... props) { String result = "function dummy(x) { "; for (String prop : props) { result += "x." + prop + " = 3;"; } return result + "}"; } private static class TypeCheckResult { private final Node root; private final Scope scope; private TypeCheckResult(Node root, Scope scope) { this.root = root; this.scope = scope; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/OptimizeParametersTest.java0000644000175000017500000004631412115204405030363 0ustar apoapo/* * 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; /** * Tests for {@link OptimizeParameters} * */ public class OptimizeParametersTest extends CompilerTestCase { @Override public CompilerPass getProcessor(Compiler compiler) { return new OptimizeParameters(compiler); } @Override public void setUp() { super.enableNormalize(); super.enableLineNumberCheck(false); } public void testNoRemoval() { testSame("function foo(p1) { } foo(1); foo(2)"); testSame("function foo(p1) { } foo(1,2); foo(3,4)"); } public void testSimpleRemoval() { test("function foo(p1) { } foo(); foo()", "function foo() {var p1;} foo(); foo()"); test("function foo(p1) { } foo(1); foo(1)", "function foo() {var p1 = 1;} foo(); foo()"); test("function foo(p1) { } foo(1,2); foo(1,4)", "function foo() {var p1 = 1;} foo(2); foo(4)"); } public void testNotAFunction() { testSame("var x = 1; x; x = 2"); } public void testRemoveOneOptionalNamedFunction() { test("function foo(p1) { } foo()", "function foo() {var p1} foo()"); } public void testDifferentScopes() { test("function f(a, b) {} f(1, 2); f(1, 3); " + "function h() {function g(a) {} g(4); g(5);} f(1, 2);", "function f(b) {var a = 1} f(2); f(3); " + "function h() {function g(a) {} g(4); g(5);} f(2);"); } public void testOptimizeOnlyImmutableValues() { test("function foo(a) {}; foo(undefined);", "function foo() {var a = undefined}; foo()"); test("function foo(a) {}; foo(null);", "function foo() {var a = null}; foo()"); test("function foo(a) {}; foo(1);", "function foo() {var a = 1}; foo()"); test("function foo(a) {}; foo('abc');", "function foo() {var a = 'abc'}; foo()"); test("var foo = function(a) {}; foo(undefined);", "var foo = function() {var a = undefined}; foo()"); test("var foo = function(a) {}; foo(null);", "var foo = function() {var a = null}; foo()"); test("var foo = function(a) {}; foo(1);", "var foo = function() {var a = 1}; foo()"); test("var foo = function(a) {}; foo('abc');", "var foo = function() {var a = 'abc'}; foo()"); } public void testRemoveOneOptionalVarAssignment() { test("var foo = function (p1) { }; foo()", "var foo = function () {var p1}; foo()"); } public void testDoOptimizeCall() { testSame("var foo = function () {}; foo(); foo.call();"); // TODO(johnlenz): support foo.call testSame("var foo = function () {}; foo(); foo.call(this);"); testSame("var foo = function (a, b) {}; foo(1); foo.call(this, 1);"); testSame("var foo = function () {}; foo(); foo.call(null);"); testSame("var foo = function (a, b) {}; foo(1); foo.call(null, 1);"); testSame("var foo = function () {}; foo.call();"); // TODO(johnlenz): support foo.call testSame("var foo = function () {}; foo.call(this);"); testSame("var foo = function (a, b) {}; foo.call(this, 1);"); testSame("var foo = function () {}; foo.call(null);"); testSame("var foo = function (a, b) {}; foo.call(null, 1);"); } public void testDoOptimizeApply() { testSame("var foo = function () {}; foo(); foo.apply();"); testSame("var foo = function () {}; foo(); foo.apply(this);"); testSame("var foo = function (a, b) {}; foo(1); foo.apply(this, 1);"); testSame("var foo = function () {}; foo(); foo.apply(null);"); testSame("var foo = function (a, b) {}; foo(1); foo.apply(null, []);"); testSame("var foo = function () {}; foo.apply();"); testSame("var foo = function () {}; foo.apply(this);"); testSame("var foo = function (a, b) {}; foo.apply(this, 1);"); testSame("var foo = function () {}; foo.apply(null);"); testSame("var foo = function (a, b) {}; foo.apply(null, []);"); } public void testRemoveOneOptionalExpressionAssign() { // TODO(johnlenz): There are two definitions of "foo" here, ignore the // one that can't be called. testSame("var foo; foo = function (p1) { }; foo()"); } public void testRemoveOneOptionalOneRequired() { test("function foo(p1, p2) { } foo(1); foo(2)", "function foo(p1) {var p2} foo(1); foo(2)"); } public void testRemoveOneOptionalMultipleCalls() { test( "function foo(p1, p2) { } foo(1); foo(2); foo()", "function foo(p1) {var p2} foo(1); foo(2); foo()"); } public void testRemoveOneOptionalMultiplePossibleDefinition() { // TODO(johnlenz): Support multiple valid definitions. String src = "var goog = {};" + "goog.foo = function (p1, p2) { };" + "goog.foo = function (q1, q2) { };" + "goog.foo = function (r1, r2) { };" + "goog.foo(1); goog.foo(2); goog.foo()"; testSame(src); } public void testRemoveTwoOptionalMultiplePossibleDefinition() { // TODO(johnlenz): Support multiple valid definitions. String src = "var goog = {};" + "goog.foo = function (p1, p2, p3, p4) { };" + "goog.foo = function (q1, q2, q3, q4) { };" + "goog.foo = function (r1, r2, r3, r4) { };" + "goog.foo(1,0); goog.foo(2,1); goog.foo()"; testSame(src); } public void testConstructorOptArgsNotRemoved() { String src = "/** @constructor */" + "var goog = function(){};" + "goog.prototype.foo = function(a,b) {};" + "goog.prototype.bar = function(a) {};" + "goog.bar.inherits(goog.foo);" + "new goog.foo(2,3);" + "new goog.foo(1,2);"; testSame(src); } public void testMultipleUnknown() { // TODO(johnlenz): Support multiple definitions. String src = "var goog1 = {};" + "goog1.foo = function () { };" + "var goog2 = {};" + "goog2.foo = function (p1) { };" + "var x = getGoog();" + "x.foo()"; testSame(src); } public void testSingleUnknown() { String src = "var goog2 = {};" + "goog2.foo = function (p1) { };" + "var x = getGoog();" + "x.foo()"; String expected = "var goog2 = {};" + "goog2.foo = function () { var p1 };" + "var x = getGoog();" + "x.foo()"; test(src, expected); } public void testRemoveVarArg() { test("function foo(p1, var_args) { } foo(1); foo(2)", "function foo(p1) { var var_args } foo(1); foo(2)"); } public void testAliasMethodsDontGetOptimize() { String src = "var foo = function(a, b) {};" + "var goog = {};" + "goog.foo = foo;" + "goog.prototype.bar = goog.foo;" + "new goog().bar(1,2);" + "foo(2);"; testSame(src); } public void testAliasMethodsDontGetOptimize2() { String src = "var foo = function(a, b) {};" + "var bar = foo;" + "foo(1);" + "bar(2,3);"; testSame(src); } public void testAliasMethodsDontGetOptimize3() { String src = "var array = {};" + "array[0] = function(a, b) {};" + "var foo = array[0];" + // foo should be marked as aliased. "foo(1);"; testSame(src); } public void testAliasMethodsDontGetOptimize4() { // Don't change the call to baz as it has been aliased. test( "function foo(bar) {};" + "baz = function(a) {};" + "baz(1);" + "foo(baz);", "function foo() {var bar = baz};" + "baz = function(a) {};" + "baz(1);" + "foo();"); } public void testMethodsDefinedInArraysDontGetOptimized() { String src = "var array = [true, function (a) {}];" + "array[1](1)"; testSame(src); } public void testMethodsDefinedInObjectDontGetOptimized() { String src = "var object = { foo: function bar() {} };" + "object.foo(1)"; testSame(src); src = "var object = { foo: function bar() {} };" + "object['foo'](1)"; testSame(src); } public void testRemoveConstantArgument() { // Remove only one parameter test("function foo(p1, p2) {}; foo(1,2); foo(2,2);", "function foo(p1) {var p2 = 2}; foo(1); foo(2)"); // Remove nothing testSame("function foo(p1, p2) {}; foo(1); foo(2,3);"); // Remove middle parameter test("function foo(a,b,c){}; foo(1, 2, 3); foo(1, 2, 4); foo(2, 2, 3)", "function foo(a,c){var b=2}; foo(1, 3); foo(1, 4); foo(2, 3)"); // Number are equals test("function foo(a) {}; foo(1); foo(1.0);", "function foo() {var a = 1;}; foo(); foo();"); // A more OO test String src = "/** @constructor */" + "function Person(){}; Person.prototype.run = function(a, b) {};" + "Person.run(1, 'a'); Person.run(2, 'a')"; String expected = "function Person(){}; Person.prototype.run = " + "function(a) {var b = 'a'};" + "Person.run(1); Person.run(2)"; test(src, expected); } public void testCanDeleteArgumentsAtAnyPosition() { // Argument removed in middle and end String src = "function foo(a,b,c,d,e) {};" + "foo(1,2,3,4,5);" + "foo(2,2,4,4,5);"; String expected = "function foo(a,c) {var b=2; var d=4; var e=5;};" + "foo(1,3);" + "foo(2,4);"; test(src, expected); } public void testNoOptimizationForExternsFunctions() { testSame("function _foo(x, y, z){}; _foo(1);"); } public void testNoOptimizationForGoogExportSymbol() { testSame("goog.exportSymbol('foo', foo);" + "function foo(x, y, z){}; foo(1);"); } public void testNoArgumentRemovalNonEqualNodes() { testSame("function foo(a){}; foo('bar'); foo('baz');"); testSame("function foo(a){}; foo(1.0); foo(2.0);"); testSame("function foo(a){}; foo(true); foo(false);"); testSame("var a = 1, b = 2; function foo(a){}; foo(a); foo(b);"); testSame("function foo(a){}; foo(/&/g); foo(/ extractMessages(String... js) { try { String sourceCode = Joiner.on("\n").join(js); return new JsMessageExtractor(null, RELAX) .extractMessages(SourceFile.fromCode("testcode", sourceCode)); } catch (IOException e) { fail(e.getMessage()); return null; } } private JsMessage extractMessage(String... js) { Collection messages = extractMessages(js); assertEquals(1, messages.size()); return messages.iterator().next(); } public void testSyntaxError1() { try { extractMessage("if (true) {}}"); fail("Expected exception"); } catch (RuntimeException e) { assertTrue(e.getMessage().contains("JSCompiler errors\n")); assertTrue(e.getMessage().contains( "testcode:1: ERROR - Parse error. syntax error\n")); assertTrue(e.getMessage().contains("if (true) {}}\n")); } } public void testSyntaxError2() { try { extractMessage("", "if (true) {}}"); fail("Expected exception"); } catch (RuntimeException e) { assertTrue( e.getMessage(), e.getMessage().contains("JSCompiler errors\n")); assertTrue( e.getMessage(), e.getMessage().contains( "testcode:2: ERROR - Parse error. syntax error\n")); assertTrue( e.getMessage(), e.getMessage().contains("if (true) {}}\n")); } } public void testExtractNewStyleMessage1() { // A simple message with no description. assertEquals( new JsMessage.Builder("MSG_SILLY") .appendStringPart("silly test message") .build(), extractMessage("var MSG_SILLY = goog.getMsg('silly test message');")); } public void testExtractNewStyleMessage2() { // A message with placeholders and meta data. assertEquals( new JsMessage.Builder("MSG_WELCOME") .appendStringPart("Hi ") .appendPlaceholderReference("userName") .appendStringPart("! Welcome to ") .appendPlaceholderReference("product") .appendStringPart(".") .setDesc("The welcome message.") .setIsHidden(true) .build(), extractMessage( "/**", " * @desc The welcome", " * message.", " *", " * @hidden", " */", "var MSG_WELCOME = goog.getMsg(", " 'Hi {$userName}! Welcome to {$product}.',", " {userName: someUserName, product: getProductName()});")); } public void testExtractOldStyleMessage1() { // Description before the message. assertEquals( new JsMessage.Builder("MSG_SILLY") .appendStringPart("silly test message") .setDesc("Description.") .build(), extractMessage( "var MSG_SILLY_HELP = 'Description.';", "var MSG_SILLY = 'silly test message';")); } public void testExtractOldStyleMessage2() { // Description after the message, broken into parts. assertEquals( new JsMessage.Builder("MSG_SILLY") .appendStringPart("silly test message") .setDesc("Description.") .build(), extractMessage( "var MSG_SILLY = 'silly test message';", "var MSG_SILLY_HELP = 'Descrip' + 'tion.';")); } public void testExtractOldStyleMessage3() { // Function-style message with two placeholders and no description. assertEquals( new JsMessage.Builder("MSG_SILLY") .appendPlaceholderReference("one") .appendStringPart(", ") .appendPlaceholderReference("two") .appendStringPart(", buckle my shoe") .build(), extractMessage( "var MSG_SILLY = function(one, two) {", " return one + ', ' + two + ', buckle my shoe';", "};")); } public void testExtractMixedMessages() { // Several mixed-style messages in succession, one containing newlines. Iterator msgs = extractMessages( "var MSG_MONEY = function(amount) {", " return 'You owe $' + amount +", " ' to the credit card company.';", "};", "var MSG_TIME = goog.getMsg('You need to finish your work in ' +", " '{$duration} hours.', {'duration': d});", "var MSG_NAG = 'Clean your room.\\n\\nWash your clothes.';", "var MSG_NAG_HELP = 'Just some ' +", " 'nags.';").iterator(); assertEquals( new JsMessage.Builder("MSG_MONEY") .appendStringPart("You owe $") .appendPlaceholderReference("amount") .appendStringPart(" to the credit card company.") .build(), msgs.next()); assertEquals( new JsMessage.Builder("MSG_TIME") .appendStringPart("You need to finish your work in ") .appendPlaceholderReference("duration") .appendStringPart(" hours.") .build(), msgs.next()); assertEquals( new JsMessage.Builder("MSG_NAG") .appendStringPart("Clean your room.\n\nWash your clothes.") .setDesc("Just some nags.") .build(), msgs.next()); } public void testDuplicateUnnamedVariables() { // Make sure that duplicate unnamed variables don't get swallowed when using // a Google-specific ID generator. Collection msgs = extractMessages( "function a() {", " var MSG_UNNAMED_2 = goog.getMsg('foo');", "}", "function b() {", " var MSG_UNNAMED_2 = goog.getMsg('bar');", "}"); assertEquals(2, msgs.size()); final Iterator iter = msgs.iterator(); assertEquals("foo", iter.next().toString()); assertEquals("bar", iter.next().toString()); } public void testMeaningAnnotation() { List msgs = Lists.newArrayList( extractMessages( "var MSG_UNNAMED_1 = goog.getMsg('foo');", "var MSG_UNNAMED_2 = goog.getMsg('foo');")); assertEquals(2, msgs.size()); assertTrue(msgs.get(0).getId().equals(msgs.get(1).getId())); assertEquals(msgs.get(0), msgs.get(1)); msgs = Lists.newArrayList( extractMessages( "var MSG_UNNAMED_1 = goog.getMsg('foo');", "/** @meaning bar */ var MSG_UNNAMED_2 = goog.getMsg('foo');")); assertEquals(2, msgs.size()); assertFalse(msgs.get(0).getId().equals(msgs.get(1).getId())); } private void assertEquals(JsMessage expected, JsMessage actual) { assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getKey(), actual.getKey()); assertEquals(expected.parts(), actual.parts()); assertEquals(expected.placeholders(), actual.placeholders()); assertEquals(expected.getDesc(), actual.getDesc()); assertEquals(expected.isHidden(), actual.isHidden()); assertEquals(expected.getMeaning(), actual.getMeaning()); } } ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ObjectPropertyStringPreprocessTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ObjectPropertyStringPreprocessTest0000644000175000017500000000431512115204405032022 0ustar apoapo/* * 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; /** * Tests for {@link ObjectPropertyStringPreprocess} * */ public class ObjectPropertyStringPreprocessTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new ObjectPropertyStringPreprocess(compiler); } @Override protected int getNumRepetitions() { return 1; } @Override protected void setUp() { super.allowExternsChanges(true); } public void testDeclaration() { test("goog.testing.ObjectPropertyString = function() {}", "JSCompiler_ObjectPropertyString = function() {}"); } public void testFooBar() { test("new goog.testing.ObjectPropertyString(foo, 'bar')", "new JSCompiler_ObjectPropertyString(goog.global, foo.bar)"); } public void testFooPrototypeBar() { test("new goog.testing.ObjectPropertyString(foo.prototype, 'bar')", "new JSCompiler_ObjectPropertyString(goog.global, " + "foo.prototype.bar)"); } public void testInvalidNumArgumentsError() { testSame(new String[] {"new goog.testing.ObjectPropertyString()"}, ObjectPropertyStringPreprocess.INVALID_NUM_ARGUMENTS_ERROR); } public void testQualifedNameExpectedError() { testSame( new String[] { "new goog.testing.ObjectPropertyString(foo[a], 'bar')" }, ObjectPropertyStringPreprocess.QUALIFIED_NAME_EXPECTED_ERROR); } public void testStringLiteralExpectedError() { testSame(new String[] {"new goog.testing.ObjectPropertyString(foo, bar)"}, ObjectPropertyStringPreprocess.STRING_LITERAL_EXPECTED_ERROR); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeIntegrationTest.java0000644000175000017500000002760712115204405030510 0ustar apoapo/* * Copyright 2004 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; /** * Tests for the interaction between multiple peephole passes. */ public class PeepholeIntegrationTest extends CompilerTestCase { private boolean late = true; // TODO(user): Remove this when we no longer need to do string comparison. private PeepholeIntegrationTest(boolean compareAsTree) { super("", compareAsTree); } public PeepholeIntegrationTest() { super(""); } @Override public void setUp() throws Exception { super.setUp(); this.late = false; enableLineNumberCheck(true); // TODO(nicksantos): Turn this on. There are some normalizations // that cause weirdness here. disableNormalize(); } @Override public CompilerPass getProcessor(final Compiler compiler) { PeepholeOptimizationsPass peepholePass = new PeepholeOptimizationsPass(compiler, new PeepholeSubstituteAlternateSyntax(late), new PeepholeRemoveDeadCode(), new PeepholeFoldConstants(late) ); return peepholePass; } @Override protected int getNumRepetitions() { // Reduce this to 2 if we get better expression evaluators. return 4; } private void foldSame(String js) { testSame(js); } private void fold(String js, String expected) { test(js, expected); } // TODO(user): This is same as fold() except it uses string comparison. Any // test that needs tell us where a folding is constructing an invalid AST. private void assertResultString(String js, String expected) { PeepholeIntegrationTest scTest = new PeepholeIntegrationTest(false); scTest.disableNormalize(); scTest.test(js, expected); } public void testTrueFalse() { late = false; foldSame("x = true"); foldSame("x = false"); fold("x = !1", "x = false"); fold("x = !0", "x = true"); late = true; fold("x = true", "x = !0"); fold("x = false", "x = !1"); foldSame("x = !1"); foldSame("x = !0"); } /** Check that removing blocks with 1 child works */ public void testFoldOneChildBlocksIntegration() { fold("function f(){switch(foo()){default:{break}}}", "function f(){foo()}"); fold("function f(){switch(x){default:{break}}}", "function f(){}"); fold("function f(){switch(x){default:x;case 1:return 2}}", "function f(){switch(x){default:case 1:return 2}}"); // ensure that block folding does not break hook ifs fold("if(x){if(true){foo();foo()}else{bar();bar()}}", "if(x){foo();foo()}"); fold("if(x){if(false){foo();foo()}else{bar();bar()}}", "if(x){bar();bar()}"); // Cases where the then clause has no side effects. fold("if(x()){}", "x()"); fold("if(x()){} else {x()}", "x()||x()"); fold("if(x){}", ""); // Even the condition has no side effect. fold("if(a()){A()} else if (b()) {} else {C()}", "a()?A():b()||C()"); fold("if(a()){} else if (b()) {} else {C()}", "a()||b()||C()"); fold("if(a()){A()} else if (b()) {} else if (c()) {} else{D()}", "a()?A():b()||c()||D()"); fold("if(a()){} else if (b()) {} else if (c()) {} else{D()}", "a()||b()||c()||D()"); fold("if(a()){A()} else if (b()) {} else if (c()) {} else{}", "a()?A():b()||c()"); // Verify that non-global scope works. fold("function foo(){if(x()){}}", "function foo(){x()}"); } public void testFoldOneChildBlocksStringCompare() { // The expected parse tree has a BLOCK structure around the true branch. assertResultString("if(x){if(y){var x;}}else{var z;}", "if(x){if(y)var x}else var z"); } /** Test a particularly hairy edge case. */ public void testNecessaryDanglingElse() { // The extra block is added by CodeGenerator. The logic to avoid ambiguous // else clauses used to be in FoldConstants, so the test is here for // legacy reasons. assertResultString( "if(x)if(y){y();z()}else;else x()", "if(x){if(y){y();z()}}else x()"); } /** Try to minimize returns */ public void testFoldReturnsIntegration() { // if-then-else duplicate statement removal handles this case: fold("function f(){if(x)return;else return}", "function f(){}"); } public void testBug1059649() { // ensure that folding blocks with a single var node doesn't explode fold("if(x){var y=3;}var z=5", "if(x)var y=3;var z=5"); // With normalization, we no longer have this case. foldSame("if(x){var y=3;}else{var y=4;}var z=5"); fold("while(x){var y=3;}var z=5", "while(x)var y=3;var z=5"); fold("for(var i=0;i<10;i++){var y=3;}var z=5", "for(var i=0;i<10;i++)var y=3;var z=5"); fold("for(var i in x){var y=3;}var z=5", "for(var i in x)var y=3;var z=5"); fold("do{var y=3;}while(x);var z=5", "do var y=3;while(x);var z=5"); } public void testHookIfIntegration() { fold("if (false){ x = 1; } else if (cond) { x = 2; } else { x = 3; }", "x=cond?2:3"); fold("x?void 0:y()", "x||y()"); fold("!x?void 0:y()", "(!x)||y()"); fold("x?y():void 0", "x&&y()"); } public void testRemoveDuplicateStatementsIntegration() { fold("function z() {if (a) { return true }" + "else if (b) { return true }" + "else { return true }}", "function z() {return true;}"); fold("function z() {if (a()) { return true }" + "else if (b()) { return true }" + "else { return true }}", "function z() {a()||b();return true;}"); } public void testFoldLogicalOpIntegration() { test("if(x && true) z()", "x&&z()"); test("if(x && false) z()", ""); fold("if(x || 3) z()", "z()"); fold("if(x || false) z()", "x&&z()"); test("if(x==y && false) z()", ""); // TODO(user): This can be further optimized. fold("if(y() || x || 3) z()", "(y()||1)&&z()"); } public void testFoldBitwiseOpStringCompareIntegration() { assertResultString("while(-1 | 0){}", "while(1);"); } public void testVarLiftingIntegration() { fold("if(true);else var a;", "var a"); fold("if(false) foo();else var a;", "var a"); fold("if(true)var a;else;", "var a"); fold("if(false)var a;else;", "var a"); fold("if(false)var a,b;", "var b; var a"); fold("if(false){var a;var a;}", "var a"); fold("if(false)var a=function(){var b};", "var a"); fold("if(a)if(false)var a;else var b;", "var a;if(a)var b"); } public void testBug1438784() throws Exception { fold("for(var i=0;i<10;i++)if(x)x.y;", "for(var i=0;i<10;i++);"); } public void testFoldUselessWhileIntegration() { fold("while(!true) { foo() }", ""); fold("while(!false) foo() ", "while(1) foo()"); fold("while(!void 0) foo()", "while(1) foo()"); // Make sure proper empty nodes are inserted. fold("if(foo())while(false){foo()}else bar()", "foo()||bar()"); } public void testFoldUselessForIntegration() { fold("for(;!true;) { foo() }", ""); fold("for(;void 0;) { foo() }", ""); fold("for(;undefined;) { foo() }", ""); fold("for(;1;) foo()", "for(;;) foo()"); fold("for(;!void 0;) foo()", "for(;;) foo()"); // Make sure proper empty nodes are inserted. fold("if(foo())for(;false;){foo()}else bar()", "foo()||bar()"); } public void testFoldUselessDoIntegration() { test("do { foo() } while(!true);", "foo()"); fold("do { foo() } while(void 0);", "foo()"); fold("do { foo() } while(undefined);", "foo()"); fold("do { foo() } while(!void 0);", "do { foo() } while(1);"); // Make sure proper empty nodes are inserted. test("if(foo())do {foo()} while(false) else bar()", "foo()?foo():bar()"); } public void testMinimizeWhileConstantConditionIntegration() { fold("while(!false) foo()", "while(1) foo()"); fold("while(202) foo()", "while(1) foo()"); fold("while(Infinity) foo()", "while(1) foo()"); fold("while('text') foo()", "while(1) foo()"); fold("while([]) foo()", "while(1) foo()"); fold("while({}) foo()", "while(1) foo()"); fold("while(/./) foo()", "while(1) foo()"); } public void testMinimizeExpr() { test("!!true", ""); fold("!!x()", "x()"); test("!(!x()&&!y())", "x()||y()"); fold("x()||!!y()", "x()||y()"); /* This is similar to the !!true case */ fold("!!x()&&y()", "x()&&y()"); } public void testBug1509085() { PeepholeIntegrationTest oneRepetitiontest = new PeepholeIntegrationTest() { @Override protected int getNumRepetitions() { return 1; } }; oneRepetitiontest.test("x ? x() : void 0", "x&&x();"); oneRepetitiontest.foldSame("y = x ? x() : void 0"); } public void testBugIssue3() { foldSame("function foo() {" + " if(sections.length != 1) children[i] = 0;" + " else var selectedid = children[i]" + "}"); } public void testBugIssue43() { foldSame("function foo() {" + " if (a) { var b = 1; } else { a.b = 1; }" + "}"); } public void testFoldNegativeBug() { fold("while(-3){};", "while(1);"); } public void testNoNormalizeLabeledExpr() { enableNormalize(true); foldSame("var x; foo:{x = 3;}"); foldSame("var x; foo:x = 3;"); } public void testShortCircuit1() { test("1 && a()", "a()"); } public void testShortCircuit2() { test("1 && a() && 2", "a()"); } public void testShortCircuit3() { test("a() && 1 && 2", "a()"); } public void testShortCircuit4() { test("a() && (1 && b())", "a() && b()"); test("a() && 1 && b()", "a() && b()"); test("(a() && 1) && b()", "a() && b()"); } public void testMinimizeExprCondition() { fold("(x || true) && y()", "y()"); fold("(x || false) && y()", "x&&y()"); fold("(x && true) && y()", "x && y()"); fold("(x && false) && y()", ""); fold("a = x || false ? b : c", "a=x?b:c"); fold("do {x()} while((x && false) && y())", "x()"); } public void testTrueFalseFolding() { late = true; fold("x = true", "x = !0"); fold("x = false", "x = !1"); fold("x = !3", "x = !1"); fold("x = true && !0", "x = !0"); fold("x = !!!!!!!!!!!!3", "x = !0"); fold("if(!3){x()}", ""); fold("if(!!3){x()}", "x()"); } public void testCommaSplitingConstantCondition() { late = false; fold("(b=0,b=1);if(b)x=b;", "b=0;b=1;x=b;"); fold("(b=0,b=1);if(b)x=b;", "b=0;b=1;x=b;"); } public void testAvoidCommaSplitting() { late = false; fold("x(),y(),z()", "x();y();z()"); late = true; foldSame("x(),y(),z()"); } public void testObjectLiteral() { test("({})", ""); test("({a:1})", ""); test("({a:foo()})", "foo()"); test("({'a':foo()})", "foo()"); } public void testArrayLiteral() { test("([])", ""); test("([1])", ""); test("([a])", ""); test("([foo()])", "foo()"); } public void testFoldIfs1() { fold("function f() {if (x) return 1; else if (y) return 1;}", "function f() {if (x||y) return 1;}"); fold("function f() {if (x) return 1; else {if (y) return 1; else foo();}}", "function f() {if (x||y) return 1; foo();}"); } public void testFoldIfs2() { fold("function f() {if (x) { a(); } else if (y) { a() }}", "function f() {x?a():y&&a();}"); } public void testFoldHook2() { fold("function f(a) {if (!a) return a; else return a;}", "function f(a) {return a}"); } public void disable_testFoldHook1() { fold("function f(a) {return (!a)?a:a;}", "function f(a) {return a}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GatherRawExportsTest.java0000644000175000017500000000600512115204405030001 0ustar apoapo/* * 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.Sets; import java.util.Set; /** * Tests for {@link GatherRawExports}. * * @author johnlenz@google.com (John Lenz) */ public class GatherRawExportsTest extends CompilerTestCase { private static final String EXTERNS = "var window;"; private GatherRawExports last; public GatherRawExportsTest() { super(EXTERNS); super.enableNormalize(); } @Override protected CompilerPass getProcessor(Compiler compiler) { last = new GatherRawExports(compiler); return last; } public void testExportsFound1() { assertExported("var a"); } public void testExportsFound2() { assertExported("window['a']", "a"); } public void testExportsFound3() { assertExported("window.a", "a"); } public void testExportsFound4() { assertExported("this['a']", "a"); } public void testExportsFound5() { assertExported("this.a", "a"); } public void testExportsFound6() { assertExported("function f() { this['a'] }"); } public void testExportsFound7() { assertExported("function f() { this.a }"); } public void testExportsFound8() { assertExported("window['foo']", "foo"); } public void testExportsFound9() { assertExported("window['a'] = 1;", "a"); } public void testExportsFound10() { assertExported("window['a']['b']['c'] = 1;", "a"); } public void testExportsFound11() { assertExported("if (window['a'] = 1) alert(x);", "a"); } public void testExportsFound12() { assertExported("function foo() { window['a'] = 1; }", "a"); } public void testExportsFound13() { assertExported("function foo() {var window; window['a'] = 1; }"); } public void testExportsFound14() { assertExported("var a={window:{}}; a.window['b']"); } public void testExportsFound15() { assertExported("window.window['b']", "window"); } public void testExportsFound16() { // It would be nice to handle this case, hopefully inlining will take care // of it for us. assertExported("var a = window; a['b']"); } public void testExportOnTopFound1() { assertExported("top['a']", "a"); } public void testExportOntopFound2() { assertExported("top.a", "a"); } private void assertExported(String js, String ... names) { Set setNames = Sets.newHashSet(names); testSame(js); assertTrue(last.getExportedVariableNames().equals(setNames)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InferJSDocInfoTest.java0000644000175000017500000001624312115204405027277 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Deque; /** * Tests for {@link InferJSDocInfo}. * @author nicksantos@google.com (Nick Santos) */ // TODO(nicksantos): A lot of this code is duplicated from // TypedScopeCreatorTest. We should create a common test harness for // assertions about type information. public class InferJSDocInfoTest extends CompilerTestCase { private Scope globalScope; @Override public int getNumRepetitions() { return 1; } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.ideMode = true; return options; } private final Callback callback = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { Scope s = t.getScope(); if (s.isGlobal()) { globalScope = s; } } }; @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { MemoizedScopeCreator scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); Scope topScope = scopeCreator.createScope(root.getParent(), null); (new TypeInferencePass( compiler, compiler.getReverseAbstractInterpreter(), topScope, scopeCreator)).process(externs, root); NodeTraversal t = new NodeTraversal( compiler, callback, scopeCreator); t.traverseRoots(Lists.newArrayList(externs, root)); (new InferJSDocInfo(compiler)).process(externs, root); } }; } public void testNativeCtor() { testSame( "/** Object. \n * @param {*=} x \n * @constructor */ " + "function Object(x) {};", "var x = new Object();" + "/** Another object. */ var y = new Object();", null); assertEquals( "Object.", findGlobalNameType("x").getJSDocInfo().getBlockDescription()); assertEquals( "Object.", findGlobalNameType("y").getJSDocInfo().getBlockDescription()); assertEquals( "Object.", globalScope.getVar("y").getType().getJSDocInfo().getBlockDescription()); } public void testStructuralFunctions() { testSame( "/** Object. \n * @param {*=} x \n * @constructor */ " + "function Object(x) {};", "/** Function. \n * @param {*} x */ " + "function fn(x) {};" + "var goog = {};" + "/** Another object. \n * @type {Object} */ goog.x = new Object();" + "/** Another function. \n * @param {number} x */ goog.y = fn;", null); assertEquals( "(Object|null)", globalScope.getVar("goog.x").getType().toString()); assertEquals( "Object.", globalScope.getVar("goog.x").getType().restrictByNotNullOrUndefined() .getJSDocInfo().getBlockDescription()); assertEquals( "Another function.", globalScope.getVar("goog.y").getType() .getJSDocInfo().getBlockDescription()); } public void testInstanceObject() { // Asserts that no property gets attached to the instance object. testSame( "/** @constructor */ function Foo() {}" + "var f = new Foo();" + "/** @type {number} */ f.bar = 4;"); ObjectType type = (ObjectType) globalScope.getVar("f").getType(); assertEquals("Foo", type.toString()); assertFalse(type.hasProperty("bar")); assertNull(type.getOwnPropertyJSDocInfo("bar")); } public void testInterface() { testSame( "/** An interface. \n * @interface */ function Foo() {}" + "var f = new Foo();" + "/** @type {number} */ f.bar = 4;"); ObjectType type = (ObjectType) globalScope.getVar("Foo").getType(); assertEquals( "An interface.", type.getJSDocInfo().getBlockDescription()); } public void testNamespacedCtor() { testSame( "var goog = {};" + "/** Hello! \n * @constructor */ goog.Foo = function() {};" + "goog.Foo.bar = goog.Foo;" + "/** Bye! \n * @param {string=} opt_x */" + "goog.Foo.prototype.baz = goog.Foo;" + "/** Blargh */ var x = new goog.Foo();"); assertEquals( "Hello!", findGlobalNameType("x").getJSDocInfo().getBlockDescription()); assertEquals( "Hello!", findGlobalNameType("goog.Foo").getJSDocInfo().getBlockDescription()); assertEquals( "Hello!", findGlobalNameType( "goog.Foo.bar").getJSDocInfo().getBlockDescription()); assertEquals( "Hello!", findGlobalNameType( "goog.Foo.prototype.baz").getJSDocInfo().getBlockDescription()); ObjectType proto = (ObjectType) findGlobalNameType("goog.Foo.prototype"); assertEquals( "Bye!", proto.getPropertyType("baz").getJSDocInfo().getBlockDescription()); } public void testAbstractMethod() { testSame( "/** Abstract method. \n * @type {!Function} */ var abstractMethod;" + "/** @constructor */ function Foo() {}" + "/** Block description. \n * @param {number} x */" + "Foo.prototype.bar = abstractMethod;"); FunctionType abstractMethod = (FunctionType) findGlobalNameType("abstractMethod"); assertNull(abstractMethod.getJSDocInfo()); FunctionType ctor = (FunctionType) findGlobalNameType("Foo"); ObjectType proto = ctor.getInstanceType().getImplicitPrototype(); FunctionType method = (FunctionType) proto.getPropertyType("bar"); assertEquals( "Block description.", method.getJSDocInfo().getBlockDescription()); assertEquals( "Block description.", proto.getOwnPropertyJSDocInfo("bar").getBlockDescription()); } private JSType findGlobalNameType(String name) { return findNameType(name, globalScope); } private JSType findNameType(String name, Scope scope) { Node root = scope.getRootNode(); Deque queue = Lists.newLinkedList(); queue.push(root); while (!queue.isEmpty()) { Node current = queue.pop(); if (name.equals(current.getQualifiedName()) && current.getJSType() != null) { return current.getJSType(); } for (Node child : current.children()) { queue.push(child); } } return null; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineCostEstimatorTest.java0000644000175000017500000000431112115204405030465 0ustar apoapo/* * 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.javascript.rhino.Node; import junit.framework.TestCase; /** * Unit test for {@link InlineCostEstimator}. * @author johnlenz@google.com (John Lenz) */ public class InlineCostEstimatorTest extends TestCase { static Node parse(String js) { Compiler compiler = new Compiler(); Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); return n; } static String minimize(String js) { CompilerOptions options = new CompilerOptions(); options.setLineLengthThreshold(Integer.MAX_VALUE); return new CodePrinter.Builder(parse(js)). setCompilerOptions(options). build(); } static long cost(String js) { return InlineCostEstimator.getCost(parse(js)); } public void testCost() { checkCost("1", "1"); checkCost("true", "1"); checkCost("false", "1"); checkCost("a", "xx"); checkCost("a + b", "xx+xx"); checkCost("foo()", "xx()"); checkCost("foo(a,b)", "xx(xx,xx)"); checkCost("10 + foo(a,b)", "0+xx(xx,xx)"); checkCost("1 + foo(a,b)", "1+xx(xx,xx)"); checkCost("a ? 1 : 0", "xx?1:0"); checkCost("a.b", "xx.xx"); checkCost("new Obj()", "new xx"); checkCost("function a() {return \"monkey\"}", "function xx(){return\"monkey\"}"); } private void checkCost(String source, String example) { // The example string should have been minified already. assertEquals(minimize(example), example); // cost estimate should be the same as the length of the example string. assertEquals(example.length(), cost(source)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReorderConstantExpressionTest.java0000644000175000017500000000655612115204405031737 0ustar apoapo/* * Copyright 2011 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; /** * Unit test for {@ReorderConstantExpression} * */ public class ReorderConstantExpressionTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new PeepholeOptimizationsPass(compiler, new ReorderConstantExpression()); } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); disableTypeCheck(); } public void testSymmetricOperations() throws Exception { set1Tests("=="); set2Tests("=="); set3Tests("=="); set1Tests("!="); set2Tests("!="); set3Tests("!="); set1Tests("==="); set2Tests("==="); set3Tests("==="); set1Tests("!=="); set2Tests("!=="); set3Tests("!=="); set1Tests("*"); set2Tests("*"); set3Tests("*"); } public void testRelationalOperations() throws Exception { set1Tests(">", "<"); set3Tests(">"); set1Tests("<", ">"); set3Tests("<"); set1Tests(">=", "<="); set3Tests(">="); set1Tests("<=", ">="); set3Tests("<="); } private void set1Tests(String op) throws Exception { set1Tests(op, op); } /** * This set has a mutable on the right and an Immutable on the left. * Applies for relational and symmetric operations. */ private void set1Tests(String op1, String op2) throws Exception { test("a " + op1 + " 0", "0 " + op2 + " a"); test("a " + op1 + " '0'", "'0' " + op2 + " a"); test("a " + op1 + " ''", "'' " + op2 + " a"); test("a " + op1 + " -1.0", "-1.0 " + op2 + " a"); test("function f(a){a " + op1 + " 0}", "function f(a){0 " + op2 + " a}"); test("f() " + op1 + " 0", "0 " + op2 + " f()"); test("(a + b) " + op1 + " 0", "0 " + op2 + " (a + b)"); test("(a + 1) " + op1 + " 0", "0 " + op2 + " (a + 1)"); test("x++ " + op1 + " 0", "0 " + op2 + " x++"); test("x = 0; function f(){x++; return x}; f() " + op1 + " 0", "x = 0; function f(){x++; return x}; 0 " + op2 + " f()"); } /** * This set has a mutable on the right and an Immutable on the left. * Applies only for symmetric operations. */ private void set2Tests(String op) throws Exception { test("a " + op + " NaN", "NaN " + op + " a"); test("a " + op + " Infinity", "Infinity " + op + " a"); testSame("NaN " + op + " a"); testSame("Infinity " + op + " a"); } /** * This set has an the immutable on the left already, or both non-immutable. */ private void set3Tests(String op) throws Exception { testSame("0 " + op + " a"); testSame("'0' " + op + " a"); testSame("'' " + op + " a"); testSame("-1.0 " + op + " a"); testSame("-1.0 " + op + " a"); testSame("0 " + op + " 1"); testSame("a " + op + " b"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SyntacticScopeCreatorTest.java0000644000175000017500000001104712115204405031005 0ustar apoapo/* * Copyright 2007 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.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; /** * Tests for {@link SyntacticScopeCreator}. * */ public class SyntacticScopeCreatorTest extends TestCase { /** * Helper to create a top-level scope from a JavaScript string */ private static Scope getScope(String js) { Compiler compiler = new Compiler(); Node root = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Scope scope = new SyntacticScopeCreator(compiler).createScope(root, null); return scope; } /** * Helper to traverse the tree creating the Scope object everywhere. */ private static void testScopes(String js, int errorCount) { Compiler compiler = new Compiler(); Node root = compiler.parseTestCode(js); NodeTraversal.traverse( compiler, root, new NodeTraversal.AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { t.getScope(); } }); assertEquals(errorCount, compiler.getErrorCount()); } public void testFunctionScope() { Scope scope = getScope("function foo() {}\n" + "var x = function bar(a1) {};" + "[function bar2() { var y; }];" + "if (true) { function z() {} }" ); assertTrue(scope.isDeclared("foo", false)); assertTrue(scope.isDeclared("x", false)); assertTrue(scope.isDeclared("z", false)); // The following should not be declared in this scope assertFalse(scope.isDeclared("a1", false)); assertFalse(scope.isDeclared("bar", false)); assertFalse(scope.isDeclared("bar2", false)); assertFalse(scope.isDeclared("y", false)); assertFalse(scope.isDeclared("", false)); } public void testScopeRootNode() { String js = "function foo() {\n" + " var x = 10;" + "}"; Compiler compiler = new Compiler(); Node root = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Scope globalScope = new SyntacticScopeCreator(compiler).createScope(root, null); assertEquals(root, globalScope.getRootNode()); Node fooNode = root.getFirstChild(); assertEquals(Token.FUNCTION, fooNode.getType()); Scope fooScope = new SyntacticScopeCreator(compiler).createScope(fooNode, null); assertEquals(fooNode, fooScope.getRootNode()); assertTrue(fooScope.isDeclared("x", false)); } public void testRedeclaration1() { String js = "var a; var a;"; int errors = createGlobalScopeHelper(js); assertEquals(1, errors); } public void testRedeclaration2() { String js = "var a; /** @suppress {duplicate} */ var a;"; int errors = createGlobalScopeHelper(js); assertEquals(0, errors); } public void testRedeclaration3() { String js = " /** @suppress {duplicate} */ var a; var a; "; int errors = createGlobalScopeHelper(js); assertEquals(0, errors); } public void testFunctionScopeArguments() { // A var declaration doesn't mask arguments testScopes("function f() {var arguments}", 0); testScopes("var f = function arguments() {}", 1); testScopes("var f = function (arguments) {}", 1); testScopes("function f() {try {} catch(arguments) {}}", 1); } /** * Parse the supplied JS and create the global SyntaticScope object. * @return The error count. */ private int createGlobalScopeHelper(String js) { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.checkSymbols = true; compiler.initOptions(options); Node root = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); Scope globalScope = new SyntacticScopeCreator(compiler).createScope(root, null); assertEquals(root, globalScope.getRootNode()); return compiler.getErrorCount(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RenamePropertiesTest.java0000644000175000017500000003625712115204405030030 0ustar apoapo/* * Copyright 2005 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; /** * {@link RenameProperties} tests. * */ public class RenamePropertiesTest extends CompilerTestCase { private static final String EXTERNS = "var window;" + "prop.toString;" + "var google = { gears: { factory: {}, workerPool: {} } };"; private RenameProperties renameProperties; private static boolean generatePseudoNames = false; private static boolean useAffinity = false; private VariableMap prevUsedPropertyMap = null; public RenamePropertiesTest() { super(EXTERNS); enableNormalize(); } @Override protected void tearDown() throws Exception { super.tearDown(); prevUsedPropertyMap = null; useAffinity = false; } @Override protected int getNumRepetitions() { // The RenameProperties pass should only be run once over a parse tree. return 1; } public void testPrototypeProperties() { test("Bar.prototype.getA = function(){}; bar.getA();" + "Bar.prototype.getB = function(){};", "Bar.prototype.a = function(){}; bar.a();" + "Bar.prototype.b = function(){}"); } public void testPrototypePropertiesAsObjLitKeys1() { test("Bar.prototype = {2: function(){}, getA: function(){}}; bar[2]();", "Bar.prototype = {2: function(){}, a: function(){}}; bar[2]();"); } public void testPrototypePropertiesAsObjLitKeys2() { testSame("Bar.prototype = {get 2(){}}; bar[2];"); testSame("Bar.prototype = {get 'a'(){}}; bar['a'];"); test("Bar.prototype = {get getA(){}}; bar.getA;", "Bar.prototype = {get a(){}}; bar.a;"); } public void testPrototypePropertiesAsObjLitKeys3() { testSame("Bar.prototype = {set 2(x){}}; bar[2];"); testSame("Bar.prototype = {set 'a'(x){}}; bar['a'];"); test("Bar.prototype = {set getA(x){}}; bar.getA;", "Bar.prototype = {set a(x){}}; bar.a;"); } public void testMixedQuotedAndUnquotedObjLitKeys1() { test("Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();", "Bar = {a: function(){}, 'getB': function(){}}; bar.a();"); } public void testMixedQuotedAndUnquotedObjLitKeys2() { test("Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();", "Bar = {a: function(){}, 'getB': function(){}}; bar.a();"); } public void testQuotedPrototypeProperty() { testSame("Bar.prototype['getA'] = function(){}; bar['getA']();"); } public void testOverlappingOriginalAndGeneratedNames() { test("Bar.prototype = {b: function(){}, a: function(){}}; bar.b();", "Bar.prototype = {a: function(){}, b: function(){}}; bar.a();"); } public void testRenamePropertiesWithLeadingUnderscores() { test("Bar.prototype = {_getA: function(){}, _b: 0}; bar._getA();", "Bar.prototype = {a: function(){}, b: 0}; bar.a();"); } public void testPropertyAddedToObject() { test("var foo = {}; foo.prop = '';", "var foo = {}; foo.a = '';"); } public void testPropertyAddedToFunction() { test("var foo = function(){}; foo.prop = '';", "var foo = function(){}; foo.a = '';"); } public void testPropertyOfObjectOfUnknownType() { test("var foo = x(); foo.prop = '';", "var foo = x(); foo.a = '';"); } public void testSetPropertyOfThis() { test("this.prop = 'bar'", "this.a = 'bar'"); } public void testReadPropertyOfThis() { test("f(this.prop);", "f(this.a);"); } public void testObjectLiteralInLocalScope() { test("function x() { var foo = {prop1: 'bar', prop2: 'baz'}; }", "function x() { var foo = {a: 'bar', b: 'baz'}; }"); } public void testIncorrectAttemptToAccessQuotedProperty() { // The correct way to call the quoted 'getFoo' method is: bar['getFoo'](). test("Bar.prototype = {'B': 0, 'getFoo': function(){}}; bar.getFoo();", "Bar.prototype = {'B': 0, 'getFoo': function(){}}; bar.a();"); } public void testSetQuotedPropertyOfThis() { testSame("this['prop'] = 'bar';"); } public void testExternedPropertyName() { test("Bar.prototype = {toString: function(){}, foo: 0}; bar.toString();", "Bar.prototype = {toString: function(){}, a: 0}; bar.toString();"); } public void testExternedPropertyNameDefinedByObjectLiteral() { test("function x() { var foo = google.gears.factory; }", "function x() { var foo = google.gears.factory; }"); } public void testAvoidingConflictsBetweenQuotedAndUnquotedPropertyNames() { test("Bar.prototype.foo = function(){}; Bar.prototype['a'] = 0; bar.foo();", "Bar.prototype.b = function(){}; Bar.prototype['a'] = 0; bar.b();"); } public void testSamePropertyNameQuotedAndUnquoted() { test("Bar.prototype.prop = function(){}; y = {'prop': 0};", "Bar.prototype.a = function(){}; y = {'prop': 0};"); } public void testStaticAndInstanceMethodWithSameName() { test("Bar = function(){}; Bar.getA = function(){}; " + "Bar.prototype.getA = function(){}; Bar.getA(); bar.getA();", "Bar = function(){}; Bar.a = function(){}; " + "Bar.prototype.a = function(){}; Bar.a(); bar.a();"); } public void testRenamePropertiesFunctionCall1() { test("var foo = {myProp: 0}; f(foo[JSCompiler_renameProperty('myProp')]);", "var foo = {a: 0}; f(foo['a']);"); } public void testRenamePropertiesFunctionCall2() { test("var foo = {myProp: 0}; " + "f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " + "foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;", "var foo = {a: 0}; f('b.a.c'); " + "foo.a = 1; foo.d = 2; foo.e = 3;"); } public void testRemoveRenameFunctionStubs1() { test("function JSCompiler_renameProperty(x) { return x; }", ""); } public void testRemoveRenameFunctionStubs2() { test("function JSCompiler_renameProperty(x) { return x; }" + "var foo = {myProp: 0}; f(foo[JSCompiler_renameProperty('myProp')]);", "var foo = {a: 0}; f(foo['a']);"); } public void testGeneratePseudoNames() { generatePseudoNames = true; test("var foo={}; foo.bar=1; foo['abc']=2", "var foo={}; foo.$bar$=1; foo['abc']=2"); generatePseudoNames = false; } public void testModules() { String module1Js = "function Bar(){} Bar.prototype.getA=function(x){};" + "var foo;foo.getA(foo);foo.doo=foo;foo.bloo=foo;"; String module2Js = "function Far(){} Far.prototype.getB=function(x){};" + "var too;too.getB(too);too.woo=too;too.bloo=too;"; String module3Js = "function Car(){} Car.prototype.getC=function(x){};" + "var noo;noo.getC(noo);noo.zoo=noo;noo.cloo=noo;"; JSModule module1 = new JSModule("m1"); module1.add(SourceFile.fromCode("input1", module1Js)); JSModule module2 = new JSModule("m2"); module2.add(SourceFile.fromCode("input2", module2Js)); JSModule module3 = new JSModule("m3"); module3.add(SourceFile.fromCode("input3", module3Js)); JSModule[] modules = new JSModule[] { module1, module2, module3 }; Compiler compiler = compileModules("", modules); Result result = compiler.getResult(); assertTrue(result.success); assertEquals("function Bar(){}Bar.prototype.b=function(x){};" + "var foo;foo.b(foo);foo.f=foo;foo.a=foo;", compiler.toSource(module1)); assertEquals("function Far(){}Far.prototype.c=function(x){};" + "var too;too.c(too);too.g=too;too.a=too;", compiler.toSource(module2)); // Note that properties that occur most often globally get the earliest // names. The "getC" property, which doesn't occur until module 3, is // renamed to an earlier name in the alphabet than "woo", which appears // in module 2, because "getC" occurs more total times across all modules. // Might be better to give early modules the shortest names, but this is // how the pass currently works. assertEquals("function Car(){}Car.prototype.d=function(x){};" + "var noo;noo.d(noo);noo.h=noo;noo.e=noo;", compiler.toSource(module3)); } public void testPropertyAffinity() { // 'y' gets to be 'b' because it appears with z often. // Other wise, 'x' gets to be 'b' because of alphabetical ordering. useAffinity = true; test("var foo={};foo.x=1;foo.y=2;foo.z=3;" + "function f1() { foo.z; foo.z; foo.z; foo.y}" + "function f2() { foo.x}", "var foo={};foo.c=1;foo.b=2;foo.a=3;" + "function f1() { foo.a; foo.a; foo.a; foo.b}" + "function f2() { foo.c}"); test("var foo={};foo.x=1;foo.y=2;foo.z=3;" + "function f1() { foo.z; foo.z; foo.z; foo.y}" + "function f2() { foo.z; foo.z; foo.z; foo.x}", "var foo={};foo.b=1;foo.c=2;foo.a=3;" + "function f1() { foo.a; foo.a; foo.a; foo.c}" + "function f2() { foo.a; foo.a; foo.a; foo.b}"); } public void testPropertyAffinityOff() { useAffinity = false; test("var foo={};foo.x=1;foo.y=2;foo.z=3;" + "function f1() { foo.z; foo.z; foo.z; foo.y}" + "function f2() { foo.x}", "var foo={};foo.b=1;foo.c=2;foo.a=3;" + "function f1() { foo.a; foo.a; foo.a; foo.c}" + "function f2() { foo.b}"); test("var foo={};foo.x=1;foo.y=2;foo.z=3;" + "function f1() { foo.z; foo.z; foo.z; foo.y}" + "function f2() { foo.z; foo.z; foo.z; foo.x}", "var foo={};foo.b=1;foo.c=2;foo.a=3;" + "function f1() { foo.a; foo.a; foo.a; foo.c}" + "function f2() { foo.a; foo.a; foo.a; foo.b}"); } public void testPrototypePropertiesStable() { testStableRenaming( "Bar.prototype.getA = function(){}; bar.getA();" + "Bar.prototype.getB = function(){};", "Bar.prototype.a = function(){}; bar.a();" + "Bar.prototype.b = function(){}", "Bar.prototype.get = function(){}; bar.get();" + "Bar.prototype.getA = function(){}; bar.getA();" + "Bar.prototype.getB = function(){};", "Bar.prototype.c = function(){}; bar.c();" + "Bar.prototype.a = function(){}; bar.a();" + "Bar.prototype.b = function(){}"); } public void testPrototypePropertiesAsObjLitKeysStable() { testStableRenaming( "Bar.prototype = {2: function(){}, getA: function(){}}; bar[2]();", "Bar.prototype = {2: function(){}, a: function(){}}; bar[2]();", "Bar.prototype = {getB: function(){},getA: function(){}}; bar.getB();", "Bar.prototype = {b: function(){},a: function(){}}; bar.b();"); } public void testMixedQuotedAndUnquotedObjLitKeysStable() { testStableRenaming( "Bar = {getA: function(){}, 'getB': function(){}}; bar.getA();", "Bar = {a: function(){}, 'getB': function(){}}; bar.a();", "Bar = {get: function(){}, getA: function(){}, 'getB': function(){}};" + "bar.getA();bar.get();", "Bar = {b: function(){}, a: function(){}, 'getB': function(){}};" + "bar.a();bar.b();"); } public void testOverlappingOriginalAndGeneratedNamesStable() { testStableRenaming( "Bar.prototype = {b: function(){}, a: function(){}}; bar.b();", "Bar.prototype = {a: function(){}, b: function(){}}; bar.a();", "Bar.prototype = {c: function(){}, b: function(){}, a: function(){}};" + "bar.b();", "Bar.prototype = {c: function(){}, a: function(){}, b: function(){}};" + "bar.a();"); } public void testStableWithTrickyExternsChanges() { test("Bar.prototype = {b: function(){}, a: function(){}}; bar.b();", "Bar.prototype = {a: function(){}, b: function(){}}; bar.a();"); prevUsedPropertyMap = renameProperties.getPropertyMap(); String externs = EXTERNS + "prop.b;"; test(externs, "Bar.prototype = {new_f: function(){}, b: function(){}, " + "a: function(){}};bar.b();", "Bar.prototype = {c:function(){}, b:function(){}, a:function(){}};" + "bar.b();", null, null); } public void testRenamePropertiesWithLeadingUnderscoresStable() { testStableRenaming( "Bar.prototype = {_getA: function(){}, _b: 0}; bar._getA();", "Bar.prototype = {a: function(){}, b: 0}; bar.a();", "Bar.prototype = {_getA: function(){}, _c: 1, _b: 0}; bar._getA();", "Bar.prototype = {a: function(){}, c: 1, b: 0}; bar.a();"); } public void testPropertyAddedToObjectStable() { testStableRenaming("var foo = {}; foo.prop = '';", "var foo = {}; foo.a = '';", "var foo = {}; foo.prop = ''; foo.a='';", "var foo = {}; foo.a = ''; foo.b='';"); } public void testAvoidingConflictsBetQuotedAndUnquotedPropertyNamesStable() { testStableRenaming( "Bar.prototype.foo = function(){}; Bar.prototype['b'] = 0; bar.foo();", "Bar.prototype.a = function(){}; Bar.prototype['b'] = 0; bar.a();", "Bar.prototype.foo = function(){}; Bar.prototype['a'] = 0; bar.foo();", "Bar.prototype.b = function(){}; Bar.prototype['a'] = 0; bar.b();"); } public void testRenamePropertiesFunctionCallStable() { testStableRenaming( "var foo = {myProp: 0}; " + "f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " + "foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;", "var foo = {a: 0}; f('b.a.c'); " + "foo.a = 1; foo.d = 2; foo.e = 3;", "var bar = {newProp: 0}; var foo = {myProp: 0}; " + "f(JSCompiler_renameProperty('otherProp.myProp.someProp')); " + "foo.myProp = 1; foo.theirProp = 2; foo.yourProp = 3;", "var bar = {f: 0}; var foo = {a: 0}; f('b.a.c'); " + "foo.a = 1; foo.d = 2; foo.e = 3;"); } private void testStableRenaming(String input1, String expected1, String input2, String expected2) { test(input1, expected1); prevUsedPropertyMap = renameProperties.getPropertyMap(); test(input2, expected2); } private Compiler compileModules(String externs, JSModule[] modules) { SourceFile externsInput = SourceFile.fromCode("externs", externs); CompilerOptions options = new CompilerOptions(); options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; Compiler compiler = new Compiler(); compiler.compileModules( ImmutableList.of(externsInput), Lists.newArrayList(modules), options); return compiler; } @Override public CompilerPass getProcessor(Compiler compiler) { return renameProperties = new RenameProperties(compiler, useAffinity, generatePseudoNames, prevUsedPropertyMap); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RecordFunctionInformationTest.java0000644000175000017500000001504612115204405031667 0ustar apoapo/* * 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.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import junit.framework.TestCase; /** * Tests for {@link RecordFunctionInformation} * */ public class RecordFunctionInformationTest extends TestCase { public void testFunction() { String g = "function g(){}"; String fAndG = "function f(){" + g + "}"; String js = "var h=" + fAndG + ";h()"; FunctionInformationMap.Builder expected = FunctionInformationMap.newBuilder(); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(0) .setSourceName("testcode") .setLineNumber(1) .setModuleName("") .setSize(g.length()) .setName("f::g") .setCompiledSource(g).build()); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(1) .setSourceName("testcode") .setLineNumber(1) .setModuleName("") .setSize(fAndG.length()) .setName("f") .setCompiledSource(fAndG).build()); expected.addModule( FunctionInformationMap.Module.newBuilder() .setName("") .setCompiledSource(js + ";").build()); test(js, expected.build()); } public void testModule() { String g = "function g(){}"; String fAndG = "function f(){" + g + "}"; String m0_js = "var h=" + fAndG + ";h()"; String sum = "function(a,b){return a+b}"; String m1_js = "var x=" + sum + "(1,2)"; FunctionInformationMap.Builder expected = FunctionInformationMap.newBuilder(); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(0) .setSourceName("i0") .setLineNumber(1) .setModuleName("m0") .setSize(g.length()) .setName("f::g") .setCompiledSource(g).build()); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(1) .setSourceName("i0") .setLineNumber(1) .setModuleName("m0") .setSize(fAndG.length()) .setName("f") .setCompiledSource(fAndG).build()); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(2) .setSourceName("i1") .setLineNumber(1) .setModuleName("m1") .setSize(sum.length()) .setName("") .setCompiledSource(sum).build()); expected.addModule( FunctionInformationMap.Module.newBuilder() .setName("m0") .setCompiledSource(m0_js + ";").build()); expected.addModule( FunctionInformationMap.Module.newBuilder() .setName("m1") .setCompiledSource(m1_js + ";").build()); test(CompilerTestCase.createModules(m0_js, m1_js), expected.build()); } public void testMotionPreservesOriginalSourceName() { String f = "function f(){}"; String g = "function g(){}"; String m0_before = f + g; String m1_before = ""; JSModule[] modules = CompilerTestCase.createModules(m0_before, m1_before); Compiler compiler = compilerFor(modules); Node root = root(compiler); Node externsRoot = externs(root); Node mainRoot = main(root); String m0_after = f; String m1_after = g; Node nodeG = mainRoot.getFirstChild().getLastChild(); mainRoot.getFirstChild().removeChild(nodeG); mainRoot.getLastChild().addChildrenToBack(nodeG.cloneTree()); FunctionInformationMap.Builder expected = FunctionInformationMap.newBuilder(); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(0) .setSourceName("i0") .setLineNumber(1) .setModuleName("m0") .setSize(g.length()) .setName("f") .setCompiledSource(f).build()); expected.addEntry( FunctionInformationMap.Entry.newBuilder() .setId(1) .setSourceName("i0") .setLineNumber(1) .setModuleName("m1") .setSize(g.length()) .setName("g") .setCompiledSource(g).build()); expected.addModule( FunctionInformationMap.Module.newBuilder() .setName("m0") .setCompiledSource(m0_after + ";").build()); expected.addModule( FunctionInformationMap.Module.newBuilder() .setName("m1") .setCompiledSource(m1_after + ";").build()); test(compiler, externsRoot, mainRoot, expected.build()); } private void test(String js, FunctionInformationMap expected) { Compiler compiler = new Compiler(); compiler.init(ImmutableList.of(SourceFile.fromCode("externs", "")), ImmutableList.of(SourceFile.fromCode("testcode", js)), new CompilerOptions()); test(compiler, expected); } private void test(JSModule[] modules, FunctionInformationMap expected) { test(compilerFor(modules), expected); } private void test(Compiler compiler, FunctionInformationMap expected) { Node root = root(compiler); test(compiler, externs(root), main(root), expected); } private void test(Compiler compiler, Node externsRoot, Node mainRoot, FunctionInformationMap expected) { FunctionNames functionNames = new FunctionNames(compiler); functionNames.process(externsRoot, mainRoot); RecordFunctionInformation processor = new RecordFunctionInformation(compiler, functionNames); processor.process(externsRoot, mainRoot); FunctionInformationMap result = processor.getMap(); assertEquals(expected, result); } private Compiler compilerFor(JSModule[] modules) { Compiler compiler = new Compiler(); compiler.initModules( ImmutableList.of(SourceFile.fromCode("externs", "")), Lists.newArrayList(modules), new CompilerOptions()); return compiler; } private Node root(Compiler compiler) { return compiler.parseInputs(); } private Node externs(Node root) { return root.getFirstChild(); } private Node main(Node root) { return root.getFirstChild().getNext(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ControlStructureCheckTest.java0000644000175000017500000001001712115204405031025 0ustar apoapo/* * 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; /** * Test for the control structure verification. * */ public class ControlStructureCheckTest extends CompilerTestCase { // Rhino parse error message text final String UNLABELED_BREAK = "unlabelled break must be inside loop or switch"; final String UNEXPECTED_CONTINUE = "continue must be inside loop"; final String UNEXPECTED_LABLED_CONTINUE = "continue can only use labeles of iteration statements"; final String UNDEFINED_LABEL = "undefined label"; @Override public CompilerPass getProcessor(Compiler compiler) { return new ControlStructureCheck(compiler); } public void testWhile() { assertNoError("while(1) { break; }"); } public void testNextedWhile() { assertNoError("while(1) { while(1) { break; } }"); } public void testBreak() { assertInvalidBreak("break;"); } public void testContinue() { assertInvalidContinue("continue;"); } public void testBreakCrossFunction() { assertInvalidBreak("while(1) { function f() { break; } }"); } public void testBreakCrossFunctionInFor() { assertInvalidBreak("while(1) {for(var f = function () { break; };;) {}}"); } public void testContinueToSwitch() { assertInvalidContinue("switch(1) {case(1): continue; }"); } public void testContinueToSwitchWithNoCases() { assertNoError("switch(1){}"); } public void testContinueToSwitchWithTwoCases() { assertInvalidContinue("switch(1){case(1):break;case(2):continue;}"); } public void testContinueToSwitchWithDefault() { assertInvalidContinue("switch(1){case(1):break;case(2):default:continue;}"); } public void testContinueToLabelSwitch() { assertInvalidLabeledContinue( "while(1) {a: switch(1) {case(1): continue a; }}"); } public void testContinueOutsideSwitch() { assertNoError("b: while(1) { a: switch(1) { case(1): continue b; } }"); } public void testContinueNotCrossFunction1() { assertNoError("a:switch(1){case(1):function f(){a:while(1){continue a;}}}"); } public void testContinueNotCrossFunction2() { assertUndefinedLabel( "a:switch(1){case(1):function f(){while(1){continue a;}}}"); } public void testUseOfWith1() { testSame("with(a){}", ControlStructureCheck.USE_OF_WITH); } public void testUseOfWith2() { testSame("/** @suppress {with} */" + "with(a){}"); } public void testUseOfWith3() { testSame( "function f(expr, context) {\n" + " try {\n" + " /** @suppress{with} */ with (context) {\n" + " return eval('[' + expr + '][0]');\n" + " }\n" + " } catch (e) {\n" + " return null;\n" + " }\n" + "};\n"); } private void assertNoError(String js) { testSame(js); } private void assertInvalidBreak(String js) { testParseError(js, UNLABELED_BREAK); } private void assertInvalidContinue(String js) { testParseError(js, UNEXPECTED_CONTINUE); } private void assertInvalidLabeledContinue(String js) { testParseError(js, UNEXPECTED_LABLED_CONTINUE); } private void assertUndefinedLabel(String js) { testParseError(js, UNDEFINED_LABEL); } private void testParseError(String js, String errorText) { Compiler compiler = new Compiler(); compiler.parseTestCode(js); assertTrue(compiler.getErrorCount() == 1); String msg = compiler.getErrors()[0].toString(); assertTrue(msg.contains(errorText)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExternExportsPassTest.java0000644000175000017500000004777712115204405030236 0ustar apoapo/* * 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.base.Joiner; import com.google.common.collect.Lists; import junit.framework.TestCase; import java.util.List; /** * Tests for {@link ExternExportsPass}. * */ public class ExternExportsPassTest extends TestCase { private boolean runCheckTypes = true; /** * ExternExportsPass relies on type information to emit JSDoc annotations for * exported externs. However, the user can disable type checking and still * ask for externs to be exported. Set this flag to enable or disable checking * of types during a test. */ private void setRunCheckTypes(boolean shouldRunCheckTypes) { runCheckTypes = shouldRunCheckTypes; } @Override public void setUp() throws Exception { super.setUp(); setRunCheckTypes(true); } public void testExportSymbol() throws Exception { compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + "goog.exportSymbol('foobar', a.b.c)", "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "var foobar = function(d, e, f) {\n};\n"); } public void testExportSymbolDefinedInVar() throws Exception { compileAndCheck("var a = function(d, e, f) {};" + "goog.exportSymbol('foobar', a)", "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "var foobar = function(d, e, f) {\n};\n"); } public void testExportProperty() throws Exception { compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + "goog.exportProperty(a.b, 'cprop', a.b.c)", "var a;\n" + "a.b;\n" + "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "a.b.cprop = function(d, e, f) {\n};\n"); } public void testExportMultiple() throws Exception { compileAndCheck("var a = {}; a.b = function(p1) {}; " + "a.b.c = function(d, e, f) {};" + "a.b.prototype.c = function(g, h, i) {};" + "goog.exportSymbol('a.b', a.b);" + "goog.exportProperty(a.b, 'c', a.b.c);" + "goog.exportProperty(a.b.prototype, 'c', a.b.prototype.c);", "var a;\n" + "/**\n" + " * @param {?} p1\n" + " * @return {undefined}\n" + " */\n" + "a.b = function(p1) {\n};\n" + "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "a.b.c = function(d, e, f) {\n};\n" + "/**\n" + " * @param {?} g\n" + " * @param {?} h\n" + " * @param {?} i\n" + " * @return {undefined}\n" + " */\n" + "a.b.prototype.c = function(g, h, i) {\n};\n"); } public void testExportMultiple2() throws Exception { compileAndCheck("var a = {}; a.b = function(p1) {}; " + "a.b.c = function(d, e, f) {};" + "a.b.prototype.c = function(g, h, i) {};" + "goog.exportSymbol('hello', a);" + "goog.exportProperty(a.b, 'c', a.b.c);" + "goog.exportProperty(a.b.prototype, 'c', a.b.prototype.c);", "/** @type {{b: function (?): undefined}} */\n" + "var hello = {};\n" + "hello.b;\n" + "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "hello.b.c = function(d, e, f) {\n};\n" + "/**\n" + " * @param {?} g\n" + " * @param {?} h\n" + " * @param {?} i\n" + " * @return {undefined}\n" + " */\n" + "hello.b.prototype.c = function(g, h, i) {\n};\n"); } public void testExportMultiple3() throws Exception { compileAndCheck("var a = {}; a.b = function(p1) {}; " + "a.b.c = function(d, e, f) {};" + "a.b.prototype.c = function(g, h, i) {};" + "goog.exportSymbol('prefix', a.b);" + "goog.exportProperty(a.b, 'c', a.b.c);", "/**\n" + " * @param {?} p1\n" + " * @return {undefined}\n" + " */\n" + "var prefix = function(p1) {\n};\n" + "/**\n" + " * @param {?} d\n" + " * @param {?} e\n" + " * @param {?} f\n" + " * @return {undefined}\n" + " */\n" + "prefix.c = function(d, e, f) {\n};\n"); } public void testExportNonStaticSymbol() throws Exception { compileAndCheck("var a = {}; a.b = {}; var d = {}; a.b.c = d;" + "goog.exportSymbol('foobar', a.b.c)", "var foobar;\n"); } public void testExportNonStaticSymbol2() throws Exception { compileAndCheck("var a = {}; a.b = {}; var d = null; a.b.c = d;" + "goog.exportSymbol('foobar', a.b.c())", "var foobar;\n"); } public void testExportNonexistentProperty() throws Exception { compileAndCheck("var a = {}; a.b = {}; a.b.c = function(d, e, f) {};" + "goog.exportProperty(a.b, 'none', a.b.none)", "var a;\n" + "a.b;\n" + "a.b.none;\n"); } public void testExportSymbolWithTypeAnnotation() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {string} param1\n" + " * @param {number} param2\n" + " * @return {string}\n" + " */\n" + "internalName = function(param1, param2) {" + "return param1 + param2;" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {string} param1\n" + " * @param {number} param2\n" + " * @return {string}\n" + " */\n" + "var externalName = function(param1, param2) {\n};\n"); } public void testExportSymbolWithTemplateAnnotation() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {T} param1\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "internalName = function(param1) {" + "return param1;" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {T} param1\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "var externalName = function(param1) {\n};\n"); } public void testExportSymbolWithMultipleTemplateAnnotation() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {K} param1\n" + " * @return {V}\n" + " * @template K,V\n" + " */\n" + "internalName = function(param1) {" + "return /** @type {V} */ (param1);" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {K} param1\n" + " * @return {V}\n" + " * @template K,V\n" + " */\n" + "var externalName = function(param1) {\n};\n"); } public void testExportSymbolWithoutTypeCheck() { // ExternExportsPass should not emit annotations // if there is no type information available. setRunCheckTypes(false); compileAndCheck("var internalName;\n" + "/**\n" + " * @param {string} param1\n" + " * @param {number} param2\n" + " * @return {string}\n" + " */\n" + "internalName = function(param1, param2) {" + "return param1 + param2;" + "};" + "goog.exportSymbol('externalName', internalName)", "var externalName = function(param1, param2) {\n};\n"); } public void testExportSymbolWithConstructor() { compileAndCheck("var internalName;\n" + "/**\n" + " * @constructor\n" + " */\n" + "internalName = function() {" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @return {undefined}\n" + " * @constructor\n" + " */\n" + "var externalName = function() {\n};\n"); } public void testExportSymbolWithConstructorWithoutTypeCheck() { // For now, skipping type checking should prevent generating // annotations of any kind, so, e.g., @constructor is not preserved. // This is probably not ideal, but since JSDocInfo for functions is attached // to JSTypes and not Nodes (and no JSTypes are created when checkTypes // is false), we don't really have a choice. setRunCheckTypes(false); compileAndCheck("var internalName;\n" + "/**\n" + " * @constructor\n" + " */\n" + "internalName = function() {" + "};" + "goog.exportSymbol('externalName', internalName)", "var externalName = function() {\n};\n"); } public void testExportFunctionWithOptionalArguments1() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {number=} a\n" + " */\n" + "internalName = function(a) {" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {number=} a\n" + " * @return {undefined}\n" + " */\n" + "var externalName = function(a) {\n};\n"); } public void testExportFunctionWithOptionalArguments2() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {number=} a\n" + " */\n" + "internalName = function(a) {" + " return 6;\n" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {number=} a\n" + " * @return {?}\n" + " */\n" + "var externalName = function(a) {\n};\n"); } public void testExportFunctionWithOptionalArguments3() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {number=} a\n" + " */\n" + "internalName = function(a) {" + " return a;\n" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {number=} a\n" + " * @return {?}\n" + " */\n" + "var externalName = function(a) {\n};\n"); } public void testExportFunctionWithVariableArguments() { compileAndCheck("var internalName;\n" + "/**\n" + " * @param {...number} a\n" + " * @return {number}\n" + " */\n" + "internalName = function(a) {" + " return 6;\n" + "};" + "goog.exportSymbol('externalName', internalName)", "/**\n" + " * @param {...number} a\n" + " * @return {number}\n" + " */\n" + "var externalName = function(a) {\n};\n"); } /** * Enums are not currently handled. */ public void testExportEnum() { // We don't care what the values of the object properties are. // They're ignored by the type checker, and even if they weren't, it'd // be incomputable to get them correct in all cases // (think complex objects). compileAndCheck( "/** @enum {string}\n @export */ var E = {A:8, B:9};" + "goog.exportSymbol('E', E);", "/** @enum {string} */\n" + "var E = {A:1, B:2};\n"); } /** If we export a property with "prototype" as a path component, there * is no need to emit the initializer for prototype because every namespace * has one automatically. */ public void testExportDontEmitPrototypePathPrefix() { compileAndCheck( "/**\n" + " * @constructor\n" + " */\n" + "var Foo = function() {};" + "/**\n" + " * @return {number}\n" + " */\n" + "Foo.prototype.m = function() {return 6;};\n" + "goog.exportSymbol('Foo', Foo);\n" + "goog.exportProperty(Foo.prototype, 'm', Foo.prototype.m);", "/**\n" + " * @return {undefined}\n" + " * @constructor\n" + " */\n" + "var Foo = function() {\n};\n" + "/**\n" + " * @return {number}\n" + " */\n" + "Foo.prototype.m = function() {\n};\n" ); } /** * Test the workflow of creating an externs file for a library * via the export pass and then using that externs file in a client. * * There should be no warnings in the client if the library includes * type information for the exported functions and the client uses them * correctly. */ public void testUseExportsAsExterns() { String librarySource = "/**\n" + " * @param {number} a\n" + " * @constructor\n" + " */\n" + "var InternalName = function(a) {" + "};" + "goog.exportSymbol('ExternalName', InternalName)"; String clientSource = "var a = new ExternalName(6);\n" + "/**\n" + " * @param {ExternalName} x\n" + " */\n" + "var b = function(x) {};"; Result libraryCompileResult = compileAndExportExterns(librarySource); assertEquals(0, libraryCompileResult.warnings.length); assertEquals(0, libraryCompileResult.errors.length); String generatedExterns = libraryCompileResult.externExport; Result clientCompileResult = compileAndExportExterns(clientSource, generatedExterns); assertEquals(0, clientCompileResult.warnings.length); assertEquals(0, clientCompileResult.errors.length); } public void testWarnOnExportFunctionWithUnknownReturnType() { String librarySource = "var InternalName = function() {" + " return 6;" + "};" + "goog.exportSymbol('ExternalName', InternalName)"; Result libraryCompileResult = compileAndExportExterns(librarySource); assertEquals(1, libraryCompileResult.warnings.length); assertEquals(0, libraryCompileResult.errors.length); } public void testDontWarnOnExportConstructorWithUnknownReturnType() { String librarySource = "/**\n" + " * @constructor\n" + " */\n " + "var InternalName = function() {" + "};" + "goog.exportSymbol('ExternalName', InternalName)"; Result libraryCompileResult = compileAndExportExterns(librarySource); assertEquals(0, libraryCompileResult.warnings.length); assertEquals(0, libraryCompileResult.errors.length); } public void testTypedef() { compileAndCheck( "/** @typedef {{x: number, y: number}} */ var Coord;\n" + "/**\n" + " * @param {Coord} a\n" + " * @export\n" + " */\n" + "var fn = function(a) {};" + "goog.exportSymbol('fn', fn);", "/**\n" + " * @param {{x: number, y: number}} a\n" + " * @return {undefined}\n" + " */\n" + "var fn = function(a) {\n};\n"); } public void testExportParamWithNull() throws Exception { compileAndCheck( "/** @param {string|null=} d */\n" + "var f = function(d) {};\n" + "goog.exportSymbol('foobar', f)\n", "/**\n" + " * @param {(null|string)=} d\n" + " * @return {undefined}\n" + " */\n" + "var foobar = function(d) {\n" + "};\n"); } public void testExportConstructor() throws Exception { compileAndCheck("/** @constructor */ var a = function() {};" + "goog.exportSymbol('foobar', a)", "/**\n" + " * @return {undefined}\n" + " * @constructor\n" + " */\n" + "var foobar = function() {\n};\n"); } private void compileAndCheck(String js, String expected) { Result result = compileAndExportExterns(js); assertEquals(expected, result.externExport); } public void testWarnOnExportFunctionWithUnknownParameterTypes() { /* This source is missing types for the b and c parameters */ String librarySource = "/**\n" + " * @param {number} a\n" + " * @return {number}" + " */\n " + "var InternalName = function(a,b,c) {" + " return 6;" + "};" + "goog.exportSymbol('ExternalName', InternalName)"; Result libraryCompileResult = compileAndExportExterns(librarySource); assertEquals(2, libraryCompileResult.warnings.length); assertEquals(0, libraryCompileResult.errors.length); } private Result compileAndExportExterns(String js) { return compileAndExportExterns(js, ""); } /** * Compiles the passed in JavaScript with the passed in externs and returns * the new externs exported by the this pass. * * @param js the source to be compiled * @param externs the externs the {@code js} source needs * @return the externs generated from {@code js} */ private Result compileAndExportExterns(String js, String externs) { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.externExportsPath = "externs.js"; // Turn on IDE mode to get rid of optimizations. options.ideMode = true; /* Check types so we can make sure our exported externs have * type information. */ options.checkSymbols = true; options.checkTypes = runCheckTypes; List inputs = Lists.newArrayList( SourceFile.fromCode("testcode", "var goog = {};" + "goog.exportSymbol = function(a, b) {}; " + "goog.exportProperty = function(a, b, c) {}; " + js)); List externFiles = Lists.newArrayList( SourceFile.fromCode("externs", externs)); Result result = compiler.compile(externFiles, inputs, options); if (!result.success) { String msg = "Errors:"; msg += Joiner.on("\n").join(result.errors); assertTrue(msg, result.success); } return result; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FunctionRewriterTest.java0000644000175000017500000001475412115204405030053 0ustar apoapo/* * 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; /** * Tests for {@link FunctionRewriter} * */ public class FunctionRewriterTest extends CompilerTestCase { private static final String RETURNARG_HELPER = "function JSCompiler_returnArg(JSCompiler_returnArg_value){" + " return function() { return JSCompiler_returnArg_value }" + "}"; private static final String GET_HELPER = "function JSCompiler_get(JSCompiler_get_name){" + " return function() { return this[JSCompiler_get_name] }" + "}"; private static final String SET_HELPER = "function JSCompiler_set(JSCompiler_set_name) {" + " return function(JSCompiler_set_value){" + " this[JSCompiler_set_name]=JSCompiler_set_value" + " }" + "}"; private static final String EMPTY_HELPER = "function JSCompiler_emptyFn() {" + " return function(){}" + "}"; private static final String IDENTITY_HELPER = "function JSCompiler_identityFn() {" + " return function(JSCompiler_identityFn_value) {" + " return JSCompiler_identityFn_value" + " }" + "}"; @Override protected void setUp() { super.enableLineNumberCheck(false); } @Override protected FunctionRewriter getProcessor(Compiler compiler) { return new FunctionRewriter(compiler); } @Override protected int getNumRepetitions() { // Pass reaches steady state after just 1 iteration return 1; } public void testReplaceReturnConst1() { String source = "a.prototype.foo = function() {return \"foobar\"}"; checkCompilesToSame(source, 3); checkCompilesTo(source, RETURNARG_HELPER, "a.prototype.foo = JSCompiler_returnArg(\"foobar\")", 4); } public void testReplaceReturnConst2() { checkCompilesToSame("a.prototype.foo = function() {return foobar}", 10); } public void testReplaceReturnConst3() { String source = "a.prototype.foo = function() {return void 0;}"; checkCompilesToSame(source, 3); checkCompilesTo(source, RETURNARG_HELPER, "a.prototype.foo = JSCompiler_returnArg(void 0)", 4); } public void testReplaceGetter1() { String source = "a.prototype.foo = function() {return this.foo_}"; checkCompilesToSame(source, 3); checkCompilesTo(source, GET_HELPER, "a.prototype.foo = JSCompiler_get(\"foo_\")", 4); } public void testReplaceGetter2() { checkCompilesToSame("a.prototype.foo = function() {return}", 10); } public void testReplaceSetter1() { String source = "a.prototype.foo = function(v) {this.foo_ = v}"; checkCompilesToSame(source, 4); checkCompilesTo(source, SET_HELPER, "a.prototype.foo = JSCompiler_set(\"foo_\")", 5); } public void testReplaceSetter2() { String source = "a.prototype.foo = function(v, v2) {this.foo_ = v}"; checkCompilesToSame(source, 3); checkCompilesTo(source, SET_HELPER, "a.prototype.foo = JSCompiler_set(\"foo_\")", 4); } public void testReplaceSetter3() { checkCompilesToSame("a.prototype.foo = function() {this.foo_ = v}", 10); } public void testReplaceSetter4() { checkCompilesToSame( "a.prototype.foo = function(v, v2) {this.foo_ = v2}", 10); } public void testReplaceEmptyFunction1() { String source = "a.prototype.foo = function() {}"; checkCompilesToSame(source, 4); checkCompilesTo(source, EMPTY_HELPER, "a.prototype.foo = JSCompiler_emptyFn()", 5); } public void testReplaceEmptyFunction2() { checkCompilesToSame("function foo() {}", 10); } public void testReplaceEmptyFunction3() { String source = "var foo = function() {}"; checkCompilesToSame(source, 4); checkCompilesTo(source, EMPTY_HELPER, "var foo = JSCompiler_emptyFn()", 5); } public void testReplaceIdentityFunction1() { String source = "a.prototype.foo = function(a) {return a}"; checkCompilesToSame(source, 2); checkCompilesTo(source, IDENTITY_HELPER, "a.prototype.foo = JSCompiler_identityFn()", 3); } public void testReplaceIdentityFunction2() { checkCompilesToSame("a.prototype.foo = function(a) {return a + 1}", 10); } public void testIssue538() { checkCompilesToSame( "/** @constructor */\n" + "WebInspector.Setting = function() {}\n" + "WebInspector.Setting.prototype = {\n" + " get name0(){return this._name;},\n" + " get name1(){return this._name;},\n" + " get name2(){return this._name;},\n" + " get name3(){return this._name;},\n" + " get name4(){return this._name;},\n" + " get name5(){return this._name;},\n" + " get name6(){return this._name;},\n" + " get name7(){return this._name;},\n" + " get name8(){return this._name;},\n" + " get name9(){return this._name;},\n" + "}", 1); } private void checkCompilesTo(String src, String expectedHdr, String expectedBody, int repetitions) { StringBuilder srcBuffer = new StringBuilder(); StringBuilder expectedBuffer = new StringBuilder(); expectedBuffer.append(expectedHdr); for (int idx = 0; idx < repetitions; idx++) { if (idx != 0) { srcBuffer.append(";"); expectedBuffer.append(";"); } srcBuffer.append(src); expectedBuffer.append(expectedBody); } test(srcBuffer.toString(), expectedBuffer.toString()); } private void checkCompilesToSame(String src, int repetitions) { checkCompilesTo(src, "", src, repetitions); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CleanupPassesTest.java0000644000175000017500000000272112115204405027277 0ustar apoapo/* * Copyright 2012 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 junit.framework.TestCase; import java.util.List; /** * @author tylerg@google.com (Tyler Goodwin) */ public class CleanupPassesTest extends TestCase { private final AbstractCompiler compiler = new Compiler(); private final CompilerOptions options = new CompilerOptions(); public void testCleanupPassOrder() { CleanupPasses config = new CleanupPasses(options); List checks = config.getChecks(); assertTrue("Pass 0 should be a FieldCleanupPass", checks.get(0).create(compiler) instanceof FieldCleanupPass); } public void testNoOptimizations() { CompilerOptions options = new CompilerOptions(); CleanupPasses config = new CleanupPasses(options); assertTrue("Cleanup Passes unexpectedly contain optimization passes", config.getOptimizations().isEmpty()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/OptimizeArgumentsArrayTest.java0000644000175000017500000001367012115204405031223 0ustar apoapo/* * 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; /** * Unit tests for {@link OptimizeArgumentsArray}. * */ public class OptimizeArgumentsArrayTest extends CompilerTestCase { public OptimizeArgumentsArrayTest() { /* * arguments is a builtin variable of the javascript language and * OptimizeArgumentsArray does not make any attempt to resolve it. However, * I am leaving "var arguments" in the externs to emulate the current * behavior we have for JS compilation where var arguments in defined in * externs/es3.js as extern. */ super("var arguments, alert" /* Externs */); } @Override public void setUp() { super.enableLineNumberCheck(false); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new OptimizeArgumentsArray(compiler, "p"); } public void testSimple() { test("function foo() { alert(arguments[0]); }", "function foo(p0) { alert(p0); }"); } public void testNoVarArgs() { testSame("function f(a,b,c) { alert(a + b + c) }"); test("function f(a,b,c) { alert(arguments[0]) }", "function f(a,b,c) { alert(a) }"); } public void testMissingVarArgs() { testSame("function f() { alert(arguments[x]) }"); } public void testArgumentRefOnNamedParameter() { test("function f(a,b) { alert(arguments[0]) }", "function f(a,b) { alert(a) }"); } public void testTwoVarArgs() { test("function foo(a) { alert(arguments[1] + arguments[2]); }", "function foo(a, p0, p1) { alert(p0 + p1); }"); } public void testTwoFourArgsTwoUsed() { test("function foo() { alert(arguments[0] + arguments[3]); }", "function foo(p0, p1, p2, p3) { alert(p0 + p3); }"); } public void testOneRequired() { test("function foo(req0, var_args) { alert(req0 + arguments[1]); }", "function foo(req0, var_args) { alert(req0 + var_args); }"); } public void testTwoRequiredSixthVarArgReferenced() { test("function foo(r0, r1, var_args) {alert(r0 + r1 + arguments[5]);}", "function foo(r0, r1, var_args, p0, p1, p2) { alert(r0 + r1 + p2); }"); } public void testTwoRequiredOneOptionalFifthVarArgReferenced() { test("function foo(r0, r1, opt_1)" + " {alert(r0 + r1 + opt_1 + arguments[4]);}", "function foo(r0, r1, opt_1, p0, p1)" + " {alert(r0 + r1 + opt_1 + p1); }"); } public void testTwoRequiredTwoOptionalSixthVarArgReferenced() { test("function foo(r0, r1, opt_1, opt_2)" + " {alert(r0 + r1 + opt_1 + opt_2 + arguments[5]);}", "function foo(r0, r1, opt_1, opt_2, p0, p1)" + " {alert(r0 + r1 + opt_1 + opt_2 + p1); }"); } public void testInnerFunctions() { test("function f() { function b( ) { arguments[0] }}", "function f() { function b(p0) { p0 }}"); test("function f( ) { function b() { } arguments[0] }", "function f(p0) { function b() { } p0 }"); test("function f( ) { arguments[0]; function b( ) { arguments[0] }}", "function f(p1) { p1; function b(p0) { p0 }}"); } public void testInnerFunctionsWithNamedArgumentInInnerFunction() { test("function f() { function b(x ) { arguments[1] }}", "function f() { function b(x,p0) { p0 }}"); test("function f( ) { function b(x) { } arguments[0] }", "function f(p0) { function b(x) { } p0 }"); test("function f( ) { arguments[0]; function b(x ) { arguments[1] }}", "function f(p1) { p1; function b(x,p0) { p0 }}"); } public void testInnerFunctionsWithNamedArgumentInOutterFunction() { test("function f(x) { function b( ) { arguments[0] }}", "function f(x) { function b(p0) { p0 }}"); test("function f(x ) { function b() { } arguments[1] }", "function f(x,p0) { function b() { } p0 }"); test("function f(x ) { arguments[1]; function b( ) { arguments[0] }}", "function f(x,p1) { p1; function b(p0) { p0 }}"); } public void testInnerFunctionsWithNamedArgumentInInnerAndOutterFunction() { test("function f(x) { function b(x ) { arguments[1] }}", "function f(x) { function b(x,p0) { p0 }}"); test("function f(x ) { function b(x) { } arguments[1] }", "function f(x,p0) { function b(x) { } p0 }"); test("function f(x ) { arguments[1]; function b(x ) { arguments[1] }}", "function f(x,p1) { p1; function b(x,p0) { p0 }}"); } public void testInnerFunctionsAfterArguments() { // This caused a bug earlier due to incorrect push and pop of the arguments // access stack. test("function f( ) { arguments[0]; function b() { function c() { }} }", "function f(p0) { p0; function b() { function c() { }} }"); } public void testNoOptimizationWhenGetProp() { testSame("function f() { arguments[0]; arguments.size }"); } public void testNoOptimizationWhenIndexIsNotNumberConstant() { testSame("function f() { arguments[0]; arguments['callee'].length}"); testSame("function f() { arguments[0]; arguments.callee.length}"); testSame( "function f() { arguments[0]; var x = 'callee'; arguments[x].length}"); } public void testNoOptimizationWhenArgumentIsUsedAsFunctionCall() { testSame("function f() {arguments[0]()}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckProvidesTest.java0000644000175000017500000000604412115204405027264 0ustar apoapo/* * 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 static com.google.javascript.jscomp.CheckProvides.MISSING_PROVIDE_WARNING; import com.google.javascript.jscomp.CheckLevel; /** * Tests for {@link CheckProvides}. * */ public class CheckProvidesTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new CheckProvides(compiler, CheckLevel.WARNING); } public void testIrrelevant() { testSame("var str = 'g4';"); } public void testHarmlessProcedural() { testSame("goog.provide('X'); /** @constructor */ function X(){};"); } public void testHarmless() { String js = "goog.provide('X'); /** @constructor */ X = function(){};"; testSame(js); } public void testNoProvideInnerClass() { testSame( "goog.provide('X');\n" + "/** @constructor */ function X(){};" + "/** @constructor */ X.Y = function(){};"); } public void testMissingGoogProvide(){ String[] js = new String[]{"/** @constructor */ X = function(){};"}; String warning = "missing goog.provide('X')"; test(js, js, null, MISSING_PROVIDE_WARNING, warning); } public void testMissingGoogProvideWithNamespace(){ String[] js = new String[]{"goog = {}; " + "/** @constructor */ goog.X = function(){};"}; String warning = "missing goog.provide('goog.X')"; test(js, js, null, MISSING_PROVIDE_WARNING, warning); } public void testGoogProvideInWrongFileShouldCreateWarning(){ String bad = "/** @constructor */ X = function(){};"; String good = "goog.provide('X'); goog.provide('Y');" + "/** @constructor */ X = function(){};" + "/** @constructor */ Y = function(){};"; String[] js = new String[] {good, bad}; String warning = "missing goog.provide('X')"; test(js, js, null, MISSING_PROVIDE_WARNING, warning); } public void testGoogProvideMissingConstructorIsOkForNow(){ // TODO(user) to prevent orphan goog.provide calls, the pass would have to // account for enums, static functions and constants testSame(new String[]{"goog.provide('Y'); X = function(){};"}); } public void testIgnorePrivateConstructor() { String js = "/** @constructor*/ X_ = function(){};"; testSame(js); } public void testIgnorePrivatelyAnnotatedConstructor() { testSame("/** @private\n@constructor */ X = function(){};"); testSame("/** @constructor\n@private */ X = function(){};"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/StatementFusionTest.java0000644000175000017500000000572112115204405027664 0ustar apoapo/* * Copyright 2011 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; /** * Unit tests for {@link StatementFusion}. * */ public class StatementFusionTest extends CompilerTestCase { @Override public void setUp() throws Exception { super.setUp(); enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { PeepholeOptimizationsPass peepholePass = new PeepholeOptimizationsPass(compiler, new StatementFusion()); return peepholePass; } public void testNothingToDo() { fuseSame(""); fuseSame("a"); fuseSame("a()"); fuseSame("if(a()){}"); } public void testFoldBlockWithStatements() { fuse("a;b;c", "a,b,c"); fuse("a();b();c();", "a(),b(),c()"); fuse("a(),b();c(),d()", "a(),b(),c(),d()"); fuse("a();b(),c(),d()", "a(),b(),c(),d()"); fuse("a(),b(),c();d()", "a(),b(),c(),d()"); } public void testFoldBlockIntoIf() { fuse("a;b;c;if(x){}", "if(a,b,c,x){}"); fuse("a;b;c;if(x,y){}else{}", "if(a,b,c,x,y){}else{}"); fuse("a;b;c;if(x,y){}", "if(a,b,c,x,y){}"); fuse("a;b;c;if(x,y,z){}", "if(a,b,c,x,y,z){}"); // Can't fuse if there are statements after the IF. fuseSame("a();if(a()){}a()"); } public void testFoldBlockReturn() { fuse("a;b;c;return x", "return a,b,c,x"); fuse("a;b;c;return x+y", "return a,b,c,x+y"); // DeadAssignmentElimination would have cleaned it up anyways. fuseSame("a;b;c;return x;a;b;c"); } public void testFoldBlockThrow() { fuse("a;b;c;throw x", "throw a,b,c,x"); fuse("a;b;c;throw x+y", "throw a,b,c,x+y"); fuseSame("a;b;c;throw x;a;b;c"); } public void testFoldSwitch() { fuse("a;b;c;switch(x){}", "switch(a,b,c,x){}"); } public void testFuseIntoForIn() { fuse("a;b;c;for(x in y){}", "for(x in a,b,c,y){}"); fuseSame("a();for(var x = b() in y){}"); } public void testNoFuseIntoWhile() { fuseSame("a;b;c;while(x){}"); } public void testNoFuseIntoDo() { fuseSame("a;b;c;do{}while(x)"); } public void testNoGlobalSchopeChanges() { testSame("a,b,c"); } public void testNoFunctionBlockChanges() { testSame("function foo() { a,b,c }"); } private void fuse(String before, String after) { test("function F(){if(CONDITION){" + before + "}}", "function F(){if(CONDITION){" + after + "}}"); } private void fuseSame(String code) { fuse(code, code); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckRequiresForConstructorsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckRequiresForConstructorsTest.j0000644000175000017500000002136312115204405031701 0ustar apoapo/* * 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 static com.google.javascript.jscomp.CheckRequiresForConstructors.MISSING_REQUIRE_WARNING; import com.google.common.collect.ImmutableList; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.Result; /** * Tests for {@link CheckRequiresForConstructors}. * */ public class CheckRequiresForConstructorsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new CheckRequiresForConstructors(compiler, CheckLevel.WARNING); } public void testPassWithNoNewNodes() { String js = "var str = 'g4'; /* does not use new */"; testSame(js); } public void testPassWithOneNew() { String js = "var goog = {};" + "goog.require('foo.bar.goo'); var bar = new foo.bar.goo();"; testSame(js); } public void testPassWithOneNewOuterClass() { String js = "var goog = {};" + "goog.require('goog.foo.Bar'); var bar = new goog.foo.Bar.Baz();"; testSame(js); } public void testPassWithOneNewOuterClassWithUpperPrefix() { String js = "var goog = {};" + "goog.require('goog.foo.IDBar'); var bar = new goog.foo.IDBar.Baz();"; testSame(js); } public void testFailWithOneNew() { String[] js = new String[] {"var foo = {}; var bar = new foo.bar();"}; String warning = "'foo.bar' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testPassWithTwoNewNodes() { String js = "var goog = {};" + "goog.require('goog.foo.Bar');goog.require('goog.foo.Baz');" + "var str = new goog.foo.Bar('g4'), num = new goog.foo.Baz(5); "; testSame(js); } public void testPassWithNestedNewNodes() { String js = "var goog = {}; goog.require('goog.foo.Bar'); " + "var str = new goog.foo.Bar(new goog.foo.Bar('5')); "; testSame(js); } public void testFailWithNestedNewNodes() { String[] js = new String[] {"var goog = {}; goog.require('goog.foo.Bar'); " + "var str = new goog.foo.Bar(new goog.foo.Baz('5')); "}; String warning = "'goog.foo.Baz' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testPassWithLocalFunctions() { String js = "/** @constructor */ function tempCtor() {}; var foo = new tempCtor();"; testSame(js); } public void testPassWithLocalVariables() { String js = "/** @constructor */ var nodeCreator = function() {};" + "var newNode = new nodeCreator();"; testSame(js); } public void testFailWithLocalVariableInMoreThanOneFile() { // there should be a warning for the 2nd script because it is only declared // in the 1st script String localVar = "/** @constructor */ function tempCtor() {}" + "function baz(){" + " /** @constructor */ function tempCtor() {}; " + "var foo = new tempCtor();}"; String[] js = new String[] {localVar, " var foo = new tempCtor();"}; String warning = "'tempCtor' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testNewNodesMetaTraditionalFunctionForm() { // the class in this script creates an instance of itself // there should be no warning because the class should not have to // goog.require itself . String js = "/** @constructor */ function Bar(){}; " + "Bar.prototype.bar = function(){ return new Bar();};"; testSame(js); } public void testNewNodesMeta() { String js = "var goog = {};" + "/** @constructor */goog.ui.Option = function(){};" + "goog.ui.Option.optionDecorator = function(){" + " return new goog.ui.Option(); };"; testSame(js); } public void testShouldWarnWhenInstantiatingObjectsDefinedInGlobalScope() { // there should be a warning for the 2nd script because // Bar was declared in the 1st file, not the 2nd String good = "/** @constructor */ function Bar(){}; " + "Bar.prototype.bar = function(){return new Bar();};"; String bad = "/** @constructor */ function Foo(){ var bar = new Bar();}"; String[] js = new String[] {good, bad}; String warning = "'Bar' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testShouldWarnWhenInstantiatingGlobalClassesFromGlobalScope() { // there should be a warning for the 2nd script because Baz // was declared in the first file, not the 2nd String good = "/** @constructor */ function Baz(){}; " + "Baz.prototype.bar = function(){return new Baz();};"; String bad = "var baz = new Baz()"; String[] js = new String[] {good, bad}; String warning = "'Baz' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testIgnoresNativeObject() { String externs = "/** @constructor */ function String(val) {}"; String js = "var str = new String('4');"; test(externs, js, js, null, null); } public void testNewNodesWithMoreThanOneFile() { // Bar is created, and goog.require()ed, but in different files. String[] js = new String[] { "var goog = {};" + "/** @constructor */ function Bar() {}" + "goog.require('Bar');", "var bar = new Bar();"}; String warning = "'Bar' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testPassWithoutWarningsAndMultipleFiles() { String[] js = new String[] { "var goog = {};" + "goog.require('Foo'); var foo = new Foo();", "goog.require('Bar'); var bar = new Bar();"}; testSame(js); } public void testFailWithWarningsAndMultipleFiles() { /* goog.require is in the code base, but not in the correct file */ String[] js = new String[] { "var goog = {};" + "/** @constructor */ function Bar() {}" + "goog.require('Bar');", "var bar = new Bar();"}; String warning = "'Bar' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } public void testCanStillCallNumberWithoutNewOperator() { String externs = "/** @constructor */ function Number(opt_value) {}"; String js = "var n = Number('42');"; test(externs, js, js, null, null); js = "var n = Number();"; test(externs, js, js, null, null); } public void testRequiresAreCaughtBeforeProcessed() { String js = "var foo = {}; var bar = new foo.bar.goo();"; SourceFile input = SourceFile.fromCode("foo.js", js); Compiler compiler = new Compiler(); CompilerOptions opts = new CompilerOptions(); opts.checkRequires = CheckLevel.WARNING; opts.closurePass = true; Result result = compiler.compile(ImmutableList.of(), ImmutableList.of(input), opts); JSError[] warnings = result.warnings; assertNotNull(warnings); assertTrue(warnings.length > 0); String expectation = "'foo.bar.goo' used but not goog.require'd"; for (JSError warning : warnings) { if (expectation.equals(warning.description)) { return; } } fail("Could not find the following warning:" + expectation); } public void testNoWarningsForThisConstructor() { String js = "var goog = {};" + "/** @constructor */goog.Foo = function() {};" + "goog.Foo.bar = function() {" + " return new this.constructor; " + "};"; testSame(js); } public void testBug2062487() { testSame( "var goog = {};" + "/** @constructor */goog.Foo = function() {" + " /** @constructor */ this.x_ = function() {};" + " this.y_ = new this.x_();" + "};"); } public void testIgnoreDuplicateWarningsForSingleClasses(){ // no use telling them the same thing twice String[] js = new String[]{ "var goog = {};" + "/** @constructor */goog.Foo = function() {};" + "goog.Foo.bar = function(){" + " var first = new goog.Forgot();" + " var second = new goog.Forgot();" + "};"}; String warning = "'goog.Forgot' used but not goog.require'd"; test(js, js, null, MISSING_REQUIRE_WARNING, warning); } } ././@LongLink0000644000000000000000000000015600000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarationsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations0000644000175000017500000002023512115204405031772 0ustar apoapo/* * 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.javascript.jscomp.ExtractPrototypeMemberDeclarations.Pattern; /** * Tests for {@link ExtractPrototypeMemberDeclarations}. * */ public class ExtractPrototypeMemberDeclarationsTest extends CompilerTestCase { private static final String TMP = "JSCompiler_prototypeAlias"; private Pattern pattern = Pattern.USE_GLOBAL_TEMP; @Override protected void setUp() { super.enableLineNumberCheck(true); enableNormalize(); pattern = Pattern.USE_GLOBAL_TEMP; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ExtractPrototypeMemberDeclarations(compiler, pattern); } public void testNotEnoughPrototypeToExtract() { // switch statement with stuff after "return" for (int i = 0; i < 7; i++) { testSame(generatePrototypeDeclarations("x", i)); } } public void testExtractingSingleClassPrototype() { extract(generatePrototypeDeclarations("x", 7), loadPrototype("x") + generateExtractedDeclarations(7)); } public void testExtractingTwoClassPrototype() { extract( generatePrototypeDeclarations("x", 6) + generatePrototypeDeclarations("y", 6), loadPrototype("x") + generateExtractedDeclarations(6) + loadPrototype("y") + generateExtractedDeclarations(6)); } public void testExtractingTwoClassPrototypeInDifferentBlocks() { extract( generatePrototypeDeclarations("x", 6) + "if (foo()) {" + generatePrototypeDeclarations("y", 6) + "}", loadPrototype("x") + generateExtractedDeclarations(6) + "if (foo()) {" + loadPrototype("y") + generateExtractedDeclarations(6) + "}"); } public void testNoMemberDeclarations() { testSame( "x.prototype = {}; x.prototype = {}; x.prototype = {};" + "x.prototype = {}; x.prototype = {}; x.prototype = {};" + "x.prototype = {}; x.prototype = {}; x.prototype = {};"); } public void testExtractingPrototypeWithQName() { extract( generatePrototypeDeclarations("com.google.javascript.jscomp.x", 7), loadPrototype("com.google.javascript.jscomp.x") + generateExtractedDeclarations(7)); } public void testInterweaved() { testSame( "x.prototype.a=1; y.prototype.a=1;" + "x.prototype.b=1; y.prototype.b=1;" + "x.prototype.c=1; y.prototype.c=1;" + "x.prototype.d=1; y.prototype.d=1;" + "x.prototype.e=1; y.prototype.e=1;" + "x.prototype.f=1; y.prototype.f=1;"); } public void testExtractingPrototypeWithNestedMembers() { extract( "x.prototype.y.a = 1;" + "x.prototype.y.b = 1;" + "x.prototype.y.c = 1;" + "x.prototype.y.d = 1;" + "x.prototype.y.e = 1;" + "x.prototype.y.f = 1;" + "x.prototype.y.g = 1;", loadPrototype("x") + TMP + ".y.a = 1;" + TMP + ".y.b = 1;" + TMP + ".y.c = 1;" + TMP + ".y.d = 1;" + TMP + ".y.e = 1;" + TMP + ".y.f = 1;" + TMP + ".y.g = 1;"); } public void testWithDevirtualization() { extract( "x.prototype.a = 1;" + "x.prototype.b = 1;" + "function devirtualize1() { }" + "x.prototype.c = 1;" + "x.prototype.d = 1;" + "x.prototype.e = 1;" + "x.prototype.f = 1;" + "x.prototype.g = 1;", loadPrototype("x") + TMP + ".a = 1;" + TMP + ".b = 1;" + "function devirtualize1() { }" + TMP + ".c = 1;" + TMP + ".d = 1;" + TMP + ".e = 1;" + TMP + ".f = 1;" + TMP + ".g = 1;"); extract( "x.prototype.a = 1;" + "x.prototype.b = 1;" + "function devirtualize1() { }" + "x.prototype.c = 1;" + "x.prototype.d = 1;" + "function devirtualize2() { }" + "x.prototype.e = 1;" + "x.prototype.f = 1;" + "function devirtualize3() { }" + "x.prototype.g = 1;", loadPrototype("x") + TMP + ".a = 1;" + TMP + ".b = 1;" + "function devirtualize1() { }" + TMP + ".c = 1;" + TMP + ".d = 1;" + "function devirtualize2() { }" + TMP + ".e = 1;" + TMP + ".f = 1;" + "function devirtualize3() { }" + TMP + ".g = 1;"); } public void testAnonSimple() { pattern = Pattern.USE_ANON_FUNCTION; extract( generatePrototypeDeclarations("x", 3), generateExtractedDeclarations(3) + loadPrototype("x")); testSame(generatePrototypeDeclarations("x", 1)); testSame(generatePrototypeDeclarations("x", 2)); extract( generatePrototypeDeclarations("x", 7), generateExtractedDeclarations(7) + loadPrototype("x")); } public void testAnonWithDevirtualization() { pattern = Pattern.USE_ANON_FUNCTION; extract( "x.prototype.a = 1;" + "x.prototype.b = 1;" + "function devirtualize() { }" + "x.prototype.c = 1;", "(function(" + TMP + "){" + TMP + ".a = 1;" + TMP + ".b = 1;" + TMP + ".c = 1;" + loadPrototype("x") + "function devirtualize() { }"); extract( "x.prototype.a = 1;" + "function devirtualize1() { }" + "x.prototype.b = 1;" + "function devirtualize2() { }" + "x.prototype.c = 1;" + "function devirtualize3() { }", "(function(" + TMP + "){" + TMP + ".a = 1;" + TMP + ".b = 1;" + TMP + ".c = 1;" + loadPrototype("x") + "function devirtualize1() { }" + "function devirtualize2() { }" + "function devirtualize3() { }"); } public void testAnonWithSideFx() { pattern = Pattern.USE_ANON_FUNCTION; testSame( "function foo() {};" + "foo.prototype.a1 = 1;" + "bar();;" + "foo.prototype.a2 = 2;" + "bar();;" + "foo.prototype.a3 = 3;" + "bar();;" + "foo.prototype.a4 = 4;" + "bar();;" + "foo.prototype.a5 = 5;" + "bar();;" + "foo.prototype.a6 = 6;" + "bar();;" + "foo.prototype.a7 = 7;" + "bar();"); } public String loadPrototype(String qName) { if (pattern == Pattern.USE_GLOBAL_TEMP) { return TMP + " = " + qName + ".prototype;"; } else { return "})(" + qName + ".prototype);"; } } public void extract(String src, String expected) { if (pattern == Pattern.USE_GLOBAL_TEMP) { test(src, "var " + TMP + ";" + expected); } else { test(src, expected); } } public String generatePrototypeDeclarations(String className, int num) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < num; i++) { char member = (char) ('a' + i); builder.append(generatePrototypeDeclaration( className, "" + member, "" + member)); } return builder.toString(); } public String generatePrototypeDeclaration(String className, String member, String value) { return className + ".prototype." + member + " = " + value + ";"; } public String generateExtractedDeclarations(int num) { StringBuilder builder = new StringBuilder(); if (pattern == Pattern.USE_ANON_FUNCTION) { builder.append("(function(" + TMP + "){"); } for (int i = 0; i < num; i++) { char member = (char) ('a' + i); builder.append(generateExtractedDeclaration("" + member, "" + member)); } return builder.toString(); } public String generateExtractedDeclaration(String member, String value) { return TMP + "." + member + " = " + value + ";"; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MustBeReachingVariableDefTest.java0000644000175000017500000001472712115204405031467 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import junit.framework.TestCase; /** * Tests for {@link MustBeReachingVariableDef}. * */ public class MustBeReachingVariableDefTest extends TestCase { private MustBeReachingVariableDef defUse = null; private Node def = null; private Node use = null; public static final String EXTERNS = "var goog = {}"; public void testStraightLine() { assertMatch("D:var x=1; U: x"); assertMatch("var x; D:x=1; U: x"); assertNotMatch("D:var x=1; x = 2; U: x"); assertMatch("var x=1; D:x=2; U: x"); assertNotMatch("U:x; D:var x = 1"); assertNotMatch("D:var x; U:x; x=1"); assertNotMatch("D:var x; U:x; x=1; x"); assertMatch("D: var x = 1; var y = 2; y; U:x"); } public void testIf() { assertNotMatch("var x; if(a){ D:x=1 } else { x=2 }; U:x"); assertNotMatch("var x; if(a){ x=1 } else { D:x=2 }; U:x"); assertMatch("D:var x=1; if(a){ U:x } else { x };"); assertMatch("D:var x=1; if(a){ x } else { U:x };"); assertNotMatch("var x; if(a) { D: x = 1 }; U:x;"); } public void testLoops() { assertNotMatch("var x=0; while(a){ D:x=1 }; U:x"); assertNotMatch("var x=0; for(;;) { D:x=1 }; U:x"); assertMatch("D:var x=1; while(a) { U:x }"); assertMatch("D:var x=1; for(;;) { U:x }"); } public void testConditional() { assertMatch("var x=0,y; D:(x=1)&&y; U:x"); assertNotMatch("var x=0,y; D:y&&(x=1); U:x"); } public void testUseAndDefInSameInstruction() { assertMatch("D:var x=0; U:x=1,x"); assertMatch("D:var x=0; U:x,x=1"); } public void testAssignmentInExpressions() { assertMatch("var x=0; D:foo(bar(x=1)); U:x"); assertMatch("var x=0; D:foo(bar + (x = 1)); U:x"); } public void testHook() { assertNotMatch("var x=0; D:foo() ? x=1 : bar(); U:x"); assertNotMatch("var x=0; D:foo() ? x=1 : x=2; U:x"); } public void testExpressionVariableReassignment() { assertMatch("var a,b; D: var x = a + b; U:x"); assertNotMatch("var a,b,c; D: var x = a + b; a = 1; U:x"); assertNotMatch("var a,b,c; D: var x = a + b; f(b = 1); U:x"); assertMatch("var a,b,c; D: var x = a + b; c = 1; U:x"); // Even if the sub-expression is change conditionally assertNotMatch("var a,b,c; D: var x = a + b; c ? a = 1 : 0; U:x"); } public void testMergeDefinitions() { assertNotMatch("var x,y; D: y = x + x; if(x) { x = 1 }; U:y"); } public void testMergesWithOneDefinition() { assertNotMatch( "var x,y; while(y) { if (y) { print(x) } else { D: x = 1 } } U:x"); } public void testRedefinitionUsingItself() { assertMatch("var x = 1; D: x = x + 1; U:x;"); assertNotMatch("var x = 1; D: x = x + 1; x = 1; U:x;"); } public void testMultipleDefinitionsWithDependence() { assertMatch("var x, a, b; D: x = a, x = b; U: x"); assertMatch("var x, a, b; D: x = a, x = b; a = 1; U: x"); assertNotMatch("var x, a, b; D: x = a, x = b; b = 1; U: x"); } public void testExterns() { assertNotMatch("D: goog = {}; U: goog"); } public void testAssignmentOp() { assertMatch("var x = 0; D: x += 1; U: x"); assertMatch("var x = 0; D: x *= 1; U: x"); assertNotMatch("D: var x = 0; x += 1; U: x"); } public void testIncAndDec() { assertMatch("var x; D: x++; U: x"); assertMatch("var x; D: x--; U: x"); } public void testFunctionParams1() { computeDefUse("if (param2) { D: param1 = 1; U: param1 }"); assertSame(def, defUse.getDefNode("param1", use)); } public void testFunctionParams2() { computeDefUse("if (param2) { D: param1 = 1} U: param1"); assertNotSame(def, defUse.getDefNode("param1", use)); } public void testArgumentsObjectModifications() { computeDefUse("D: param1 = 1; arguments[0] = 2; U: param1"); assertNotSame(def, defUse.getDefNode("param1", use)); } public void testArgumentsObjectEscaped() { computeDefUse("D: param1 = 1; var x = arguments; x[0] = 2; U: param1"); assertNotSame(def, defUse.getDefNode("param1", use)); } public void testArgumentsObjectEscapedDependents() { assertNotMatch("param1=1; var x; D:x=param1; var y=arguments; U:x"); } /** * The use of x at U: is the definition of x at D:. */ private void assertMatch(String src) { computeDefUse(src); assertSame(def, defUse.getDefNode("x", use)); } /** * The use of x at U: is not the definition of x at D:. */ private void assertNotMatch(String src) { computeDefUse(src); assertNotSame(def, defUse.getDefNode("x", use)); } /** * Computes reaching definition on given source. */ private void computeDefUse(String src) { Compiler compiler = new Compiler(); src = "function _FUNCTION(param1, param2){" + src + "}"; Node root = compiler.parseTestCode(src).getFirstChild(); assertEquals(0, compiler.getErrorCount()); Scope scope = new SyntacticScopeCreator(compiler).createScope(root, null); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, root); ControlFlowGraph cfg = cfa.getCfg(); defUse = new MustBeReachingVariableDef(cfg, scope, compiler); defUse.analyze(); def = null; use = null; new NodeTraversal(compiler,new LabelFinder()).traverse(root); assertNotNull("Code should have an instruction labeled D", def); assertNotNull("Code should have an instruction labeled U", use); } /** * Finds the D: and U: label and store which node they point to. */ private class LabelFinder extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isLabel()) { if (n.getFirstChild().getString().equals("D")) { def = n.getLastChild(); } else if (n.getFirstChild().getString().equals("U")) { use = n.getLastChild(); } } } } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CollapseAnonymousFunctionsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CollapseAnonymousFunctionsTest.jav0000644000175000017500000000735212115204405031741 0ustar apoapo/* * 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; /** * Tests for {@link CollapseAnonymousFunctions} * */ public class CollapseAnonymousFunctionsTest extends CompilerTestCase { public CollapseAnonymousFunctionsTest() { this.enableNormalize(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CollapseAnonymousFunctions(compiler); } public void testGlobalScope() { test("var f = function(){}", "function f(){}"); } public void testLocalScope1() { test("function f(){ var x = function(){}; x() }", "function f(){ function x(){} x() }"); } public void testLocalScope2() { test("function f(){ var x = function(){}; return x }", "function f(){ function x(){} return x }"); } public void testVarNotImmediatelyBelowScriptOrBlock1() { testSame("if (x) var f = function(){}"); } public void testVarNotImmediatelyBelowScriptOrBlock2() { testSame("var x = 1;" + "if (x == 1) {" + " var f = function () { alert('b')}" + "} else {" + " f = function() { alert('c')}" + "}" + "f();"); } public void testVarNotImmediatelyBelowScriptOrBlock3() { testSame("var x = 1; if (x) {var f = function(){return x}; f(); x--;}"); } public void testMultipleVar() { test("var f = function(){}; var g = f", "function f(){} var g = f"); } public void testMultipleVar2() { test("var f = function(){}; var g = f; var h = function(){}", "function f(){}var g = f;function h(){}"); } public void testBothScopes() { test("var x = function() { var y = function(){} }", "function x() { function y(){} }"); } public void testLocalScopeOnly1() { test("if (x) var f = function(){ var g = function(){} }", "if (x) var f = function(){ function g(){} }"); } public void testLocalScopeOnly2() { test("if (x) var f = function(){ var g = function(){} };", "if (x) var f = function(){ function g(){} }"); } public void testReturn() { test("var f = function(x){return 2*x}; var g = f(2);", "function f(x){return 2*x} var g = f(2)"); } public void testAlert() { test("var x = 1; var f = function(){alert(x)};", "var x = 1; function f(){alert(x)}"); } public void testRecursiveInternal1() { testSame("var f = function foo() { foo() }"); } public void testRecursiveInternal2() { testSame("var f = function foo() { function g(){foo()} g() }"); } public void testRecursiveExternal1() { test("var f = function foo() { f() }", "function f() { f() }"); } public void testRecursiveExternal2() { test("var f = function foo() { function g(){f()} g() }", "function f() { function g(){f()} g() }"); } public void testConstantFunction1() { test("var FOO = function(){};FOO()", "function FOO(){}FOO()"); } public void testInnerFunction1() { test( "function f() { " + " var x = 3; var y = function() { return 4; }; return x + y();" + "}", "function f() { " + " function y() { return 4; } var x = 3; return x + y();" + "}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FixedPointGraphTraversalTest.java0000644000175000017500000001270512115204405031453 0ustar apoapo/* * 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.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.jscomp.graph.DiGraph; import junit.framework.TestCase; /** * Test for FixedPointGraphTraversal. * @author nicksantos@google.com (Nick Santos) */ public class FixedPointGraphTraversalTest extends TestCase { // The maximum value of a counter that counts as a "change" // to the state of the graph, for the purposes of fixed-point // computation. private int maxChange = 0; private class Counter { int value = 0; } private class CounterIncrementer implements EdgeCallback { @Override public boolean traverseEdge(Counter source, String e, Counter dest) { dest.value++; return dest.value <= maxChange; } } private DiGraph graph; private Counter A, B, C, D, E; private CounterIncrementer callback = new CounterIncrementer(); private FixedPointGraphTraversal traversal = new FixedPointGraphTraversal(callback); // Create a new graph of the following form: // // A // / \ // | B // / \ / // C D // \ / // E // // with all edges pointing downwards, and an "up-edge" from E to D. @Override public void setUp() { A = new Counter(); B = new Counter(); C = new Counter(); D = new Counter(); E = new Counter(); graph = LinkedDirectedGraph.create(); graph.createDirectedGraphNode(A); graph.createDirectedGraphNode(B); graph.createDirectedGraphNode(C); graph.createDirectedGraphNode(D); graph.createDirectedGraphNode(E); graph.connect(A, "->", B); graph.connect(A, "->", C); graph.connect(A, "->", D); graph.connect(B, "->", D); graph.connect(C, "->", E); graph.connect(D, "->", E); graph.connect(E, "->", D); } public void testGraph1() { maxChange = 0; traversal.computeFixedPoint(graph, A); assertEquals(0, A.value); assertEquals(1, B.value); assertEquals(1, C.value); assertEquals(1, D.value); assertEquals(0, E.value); } public void testGraph2() { maxChange = 0; traversal.computeFixedPoint(graph, D); assertEquals(0, A.value); assertEquals(0, B.value); assertEquals(0, C.value); assertEquals(0, D.value); assertEquals(1, E.value); } public void testGraph3() { maxChange = 1; traversal.computeFixedPoint(graph, A); assertEquals(0, A.value); assertEquals(1, B.value); assertEquals(1, C.value); assertEquals(3, D.value); assertEquals(2, E.value); } public void testGraph4() { maxChange = 1; traversal.computeFixedPoint(graph, D); assertEquals(0, A.value); assertEquals(0, B.value); assertEquals(0, C.value); assertEquals(1, D.value); assertEquals(2, E.value); } public void testGraph5() { maxChange = 5; traversal.computeFixedPoint(graph, A); assertEquals(0, A.value); assertEquals(1, B.value); assertEquals(1, C.value); assertEquals(6, D.value); assertEquals(5, E.value); } public void testGraph6() { maxChange = 5; traversal.computeFixedPoint(graph, B); assertEquals(0, A.value); assertEquals(0, B.value); assertEquals(0, C.value); assertEquals(6, D.value); assertEquals(5, E.value); } public void testGraph8() { maxChange = 2; traversal.computeFixedPoint(graph, A); try { traversal = new FixedPointGraphTraversal( new EdgeCallback() { @Override public boolean traverseEdge(Counter source, String e, Counter dest) { return true; } }); traversal.computeFixedPoint(graph, A); fail("Expecting Error: " + FixedPointGraphTraversal.NON_HALTING_ERROR_MSG); } catch (IllegalStateException e) { assertEquals(e.getMessage(), FixedPointGraphTraversal.NON_HALTING_ERROR_MSG); } } public void testGraph9() { maxChange = 0; // when the graph traversal is done for the whole graph, we're actually // counting the number of "in" edges for each node. traversal.computeFixedPoint(graph); assertEquals(0, A.value); assertEquals(1, B.value); assertEquals(1, C.value); assertEquals(3, D.value); assertEquals(2, E.value); } public void testGraph10() { // Test a graph with self-edges. maxChange = 5; A = new Counter(); B = new Counter(); graph = LinkedDirectedGraph.create(); graph.createDirectedGraphNode(A); graph.createDirectedGraphNode(B); graph.connect(A, "->", A); graph.connect(A, "->", B); traversal.computeFixedPoint(graph); assertEquals(6, A.value); assertEquals(6, B.value); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ShadowVariablesTest.java0000644000175000017500000002606112115204405027612 0ustar apoapo/* * Copyright 2011 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; /** * Unit tests for {@link ShadowVariables}. * * */ public class ShadowVariablesTest extends CompilerTestCase{ // Use pseudo names to make test easier to read. private boolean generatePseudoNames = false; private RenameVars pass = null; @Override protected CompilerPass getProcessor(Compiler compiler) { pass = new RenameVars( compiler, "", false, false, generatePseudoNames, true, null, null, null); return pass; } @Override protected int getNumRepetitions() { return 1; } @Override protected void setUp() throws Exception { super.setUp(); generatePseudoNames = false; } @Override protected void tearDown() throws Exception { super.tearDown(); pass = null; } public void testShadowSimple1() { test("function foo(x) { return function (y) {} }", "function b(a) { return function (a) {} }"); generatePseudoNames = true; test("function foo ( x ) { return function ( y ) {} }", "function $foo$$($x$$) { return function ($x$$) {} }"); } public void testShadowSimple2() { test("function foo(x,y) { return function (y,z) {} }", "function c(a,b) { return function (a,b) {} }"); generatePseudoNames = true; test("function foo ( x , y ) { return function ( y , z ) {} }", "function $foo$$($x$$,$y$$) { return function ($x$$,$y$$) {} }"); } /** * If we have a choice, pick out the most used variable to shadow. */ public void testShadowMostUsedVar() { generatePseudoNames = true; test("function foo () {var x ; var y ; y ; y ; y ; x ;" + " return function ( k ) {} }", "function $foo$$() {var $x$$; var $y$$; $y$$;$y$$;$y$$;$x$$;" + " return function ($y$$) {} }"); } public void testNoShadowReferencedVariables() { generatePseudoNames = true; test("function f1 () { var x ; x ; x ; x ;" + " return function f2 ( y ) {" + " return function f3 () { x } }}", "function $f1$$() { var $x$$;$x$$;$x$$;$x$$;" + " return function $f2$$($y$$) {" + " return function $f3$$() {$x$$} }}"); } public void testNoShadowGlobalVariables() { generatePseudoNames = true; test("var x ; x ; function foo () { return function ( y ) {}}", "var $x$$; $x$$; function $foo$$() { return function ($y$$) {}}"); } public void testShadowBleedInFunctionName() { generatePseudoNames = true; test("function foo () { function b ( y ) { y } b ; b ;}", "function $foo$$() { function $b$$($b$$) {$b$$} $b$$; $b$$;}"); } public void testNoShadowLessPopularName() { generatePseudoNames = true; // We make sure that y doesn't pick x as a shadow and remains to be renamed // to 'a'. // If we do shadow y with whatever x renames to (say b) we will // get 4 b's and 7 a's while currently we get 3 b's and 8 a's. // I believe this arrangement will always be better for gzipping. test("function f1 ( x ) {" + " function f2 ( y ) {} x ; x ;}" + "function f3 ( i ) {" + " var k ; var j ; j ; j ; j ; j ; j ; j ;}", "function $f1$$($x$$) {" + " function $f2$$($y$$) {} $x$$;$x$$;}" + "function $f3$$($i$$) {" + " var $k$$; var $j$$;$j$$;$j$$;$j$$;$j$$;$j$$;$j$$;}"); } public void testShadowFunctionName() { generatePseudoNames = true; test("var g = function() {" + " var x ; return function(){function y (){}}}", "var $g$$ = function() {" + " var $x$$; return function(){function $x$$(){}}}"); } public void testShadowLotsOfScopes1() { generatePseudoNames = true; test("var g = function( x ) { return function() { return function() {" + " return function() { var y }}}}", "var $g$$ = function($x$$) { return function() { return function() {" + " return function() { var $x$$ }}}}"); } public void testShadowLotsOfScopes2() { generatePseudoNames = true; // 'y' doesn't have a candidate to shadow due to upward referencing. test("var g = function( x ) { return function( y ) " + " {return function() {return function() { x }}}}", "var $g$$ = function($x$$) { return function($y$$) " + " {return function() {return function() { $x$$ }}}}"); test("var g = function( x ) { return function() " + " {return function( y ) {return function() { x }}}}", "var $g$$ = function($x$$) { return function() " + " {return function($y$$) {return function() { $x$$ }}}}"); test("var g = function( x ) { return function() " + " {return function() {return function( y ) { x }}}}", "var $g$$ = function($x$$) { return function() " + " {return function() {return function($y$$) { $x$$ }}}}"); } public void testShadowLotsOfScopes3() { generatePseudoNames = true; // 'y' doesn't have a candidate to shadow due to upward referencing. test("var g = function( x ) { return function() " + " {return function() {return function() { x }; var y }}}", "var $g$$ = function($x$$) { return function() " + " {return function() {return function() { $x$$ }; var $y$$}}}"); test("var g = function( x ) { return function() " + " {return function() {return function() { x }}; var y }}", "var $g$$ = function($x$$) { return function() " + " {return function() {return function() { $x$$ }}; var $y$$}}"); test("var g = function( x ) { return function() " + " {return function() {return function() { x }}}; var y }", "var $g$$ = function($x$$) { return function() " + " {return function() {return function() { $x$$ }}}; var $y$$}"); } public void testShadowLotsOfScopes4() { // Make sure we do get the optimal shadowing scheme where test("var g = function(x) { return function() { return function() {" + " return function(){return function(){};var m};var n};var o}}", "var b = function(a) { return function() { return function() {" + " return function(){return function(){};var a};var a};var a}}"); } public void testShadowLotsOfScopes5() { generatePseudoNames = true; test("var g = function( x ) {" + " return function() { return function() {" + " return function() { return function() {" + " x }; o };var n };var o };var p }", "var $g$$ = function($x$$) {" + " return function() { return function() {" + " return function() { return function() {" + " $x$$};$o$$};var $p$$};var $o$$};var $p$$}"); test("var g = function( x ) {" + " return function() { return function() {" + " return function() { return function() {" + " x }; p };var n };var o };var p }", "var $g$$ = function($x$$) {" + " return function() { return function() {" + " return function() { return function() {" + " $x$$};$p$$};var $o$$};var $o$$};var $p$$}"); } public void testShadowWithShadowAlready() { test("var g = function(x) { return function() { return function() {" + " return function(){return function(){x}};var p};var o};var p}", "var c = function(b) { return function() { return function() {" + " return function(){return function(){b}};var a};var a};var a}"); test("var g = function(x) { return function() { return function() {" + " return function(){return function(){x};p};var p};var o};var p}", "var c = function(b) { return function() { return function() {" + " return function(){return function(){b};a};var a};var a};var a}"); } public void testShadowBug1() { generatePseudoNames = true; test("function f ( x ) { return function( y ) {" + " return function( x ) { x + y ; }}}", "function $f$$($x$$) { return function($y$$) {" + " return function($x$$) { $x$$ + $y$$; }}}"); } public void testOptimal() { // A test for a case that wasn't optimal in a single pass algorithm. test("function f(x) { function g(y) { function h(x) {}}}", "function c(a) { function b(a) { function b(a) {}}}"); } public void testSharingAcrossInnerScopes() { test("function f() {var f=function g(){g()}; var x=function y(){y()}}", "function c() {var d=function a(){a()}; var e=function b(){b()}}"); test("function f(x) { return x ? function(y){} : function(z) {} }", "function b(a) { return a ? function(a){} : function(a) {} }"); } public void testExportedLocal1() { test("function f(a) { a();a();a(); return function($super){} }", "function b(a) { a();a();a(); return function($super){} }"); } public void testExportedLocal2() { test("function f($super) { $super();$super(); return function(a){} }", "function a($super) { $super();$super(); return function(b){} }"); } public void testRenameMapHasNoDuplicates() { test("function foo(x) { return function (y) {} }", "function b(a) { return function (a) {} }"); VariableMap vm = pass.getVariableMap(); try { vm.getNewNameToOriginalNameMap(); } catch (java.lang.IllegalArgumentException unexpected) { fail("Invalid VariableMap generated: " + vm.getOriginalNameToNewNameMap().toString()); } } public void testBug4172539() { // All the planets must line up. When we look at the 2nd inner function, // y can shadow x, also m can shadow x as well. Now all that is left for // n to shadow is 'y'. Now because y has already shadowed x, the pseudo // name maps has already updated y gets $x$$. This mean n will be updated // with "$x$$" in the name map which is incorrect. That is the reason // why we can't update the pseudo name map on-the-fly. generatePseudoNames = true; test("function f(x) {" + " x;x;x;" + " return function (y) { y; x };" + " return function (y) {" + " y;" + " return function (m, n) {" + " m;m;m;" + " };" + " };" + "}", "function $f$$($x$$) {" + " $x$$;$x$$;$x$$;" + " return function ($y$$) { $y$$; $x$$ };" + " return function ($x$$) {" + " $x$$;" + " return function ($x$$, $y$$) {" + " $x$$;$x$$;$x$$;" + " };" + " };" + "}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReplaceMessagesTest.java0000644000175000017500000003147312115204405027602 0ustar apoapo/* * 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 static com.google.javascript.jscomp.JsMessage.Style.RELAX; import static com.google.javascript.jscomp.JsMessageVisitor.MESSAGE_TREE_MALFORMED; import com.google.common.collect.Maps; import com.google.javascript.jscomp.JsMessage.Style; import java.util.Map; /** * Test which checks that replacer works correctly. * */ public class ReplaceMessagesTest extends CompilerTestCase { private Map messages; private Style style = RELAX; private boolean strictReplacement; @Override protected CompilerPass getProcessor(Compiler compiler) { return new ReplaceMessages(compiler, new SimpleMessageBundle(), false, style, strictReplacement); } @Override protected int getNumRepetitions() { // No longer valid on the second run. return 1; } @Override protected void setUp() { messages = Maps.newHashMap(); strictReplacement = false; style = RELAX; } public void testReplaceSimpleMessage() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("Hi\nthere") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');", "var MSG_A=\"Hi\\nthere\""); } public void testNameReplacement() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); test("/** @desc d */\n" + "var MSG_B=goog.getMsg('asdf {$measly}', {measly: x});", "var MSG_B=\"One \"+ (x +\" ph\" )"); } public void testGetPropReplacement() { registerMessage(new JsMessage.Builder("MSG_C") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_C = goog.getMsg('${$amount}', {amount: a.b.amount});", "var MSG_C=a.b.amount"); } public void testFunctionCallReplacement() { registerMessage(new JsMessage.Builder("MSG_D") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_D = goog.getMsg('${$amount}', {amount: getAmt()});", "var MSG_D=getAmt()"); } public void testMethodCallReplacement() { registerMessage(new JsMessage.Builder("MSG_E") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_E = goog.getMsg('${$amount}', {amount: obj.getAmt()});", "var MSG_E=obj.getAmt()"); } public void testHookReplacement() { registerMessage(new JsMessage.Builder("MSG_F") .appendStringPart("#") .appendPlaceholderReference("amount") .appendStringPart(".") .build()); test("/** @desc d */\n" + "var MSG_F = goog.getMsg('${$amount}', {amount: (a ? b : c)});", "var MSG_F=\"#\"+((a?b:c)+\".\")"); } public void testAddReplacement() { registerMessage(new JsMessage.Builder("MSG_G") .appendPlaceholderReference("amount") .build()); test("/** @desc d */\n" + "var MSG_G = goog.getMsg('${$amount}', {amount: x + ''});", "var MSG_G=x+\"\""); } public void testPlaceholderValueReferencedTwice() { registerMessage(new JsMessage.Builder("MSG_H") .appendPlaceholderReference("dick") .appendStringPart(", ") .appendPlaceholderReference("dick") .appendStringPart(" and ") .appendPlaceholderReference("jane") .build()); test("/** @desc d */\n" + "var MSG_H = goog.getMsg('{$dick}{$jane}', {jane: x, dick: y});", "var MSG_H=y+(\", \"+(y+(\" and \"+x)))"); } public void testPlaceholderNameInLowerCamelCase() { registerMessage(new JsMessage.Builder("MSG_I") .appendStringPart("Sum: $") .appendPlaceholderReference("amtEarned") .build()); test("/** @desc d */\n" + "var MSG_I = goog.getMsg('${$amtEarned}', {amtEarned: x});", "var MSG_I=\"Sum: $\"+x"); } public void testQualifiedMessageName() { registerMessage(new JsMessage.Builder("MSG_J") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); test("/** @desc d */\n" + "a.b.c.MSG_J = goog.getMsg('asdf {$measly}', {measly: x});", "a.b.c.MSG_J=\"One \"+(x+\" ph\")"); } public void testSimpleMessageReplacementMissing() { style = Style.LEGACY; test("/** @desc d */\n" + "var MSG_E = 'd*6a0@z>t';", "var MSG_E = 'd*6a0@z>t'"); } public void testSimpleMessageReplacementMissingWithNewStyle() { test("/** @desc d */\n" + "var MSG_E = goog.getMsg('missing');", "var MSG_E = 'missing'"); } public void testStrictModeAndMessageReplacementAbsentInBundle() { strictReplacement = true; test("var MSG_E = 'Hello';", "var MSG_E = 'Hello';", ReplaceMessages.BUNDLE_DOES_NOT_HAVE_THE_MESSAGE); } public void testStrictModeAndMessageReplacementAbsentInNonEmptyBundle() { registerMessage(new JsMessage.Builder("MSG_J") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); strictReplacement = true; test("var MSG_E = 'Hello';", "var MSG_E = 'Hello';", ReplaceMessages.BUNDLE_DOES_NOT_HAVE_THE_MESSAGE); } public void testFunctionReplacementMissing() { style = Style.LEGACY; test("var MSG_F = function() {return 'asdf'};", "var MSG_F = function() {return\"asdf\"}"); } public void testFunctionWithParamReplacementMissing() { style = Style.LEGACY; test( "var MSG_G = function(measly) {return 'asdf' + measly};", "var MSG_G=function(measly){return\"asdf\"+measly}"); } public void testPlaceholderNameInLowerUnderscoreCase() { test( "var MSG_J = goog.getMsg('${$amt_earned}', {amt_earned: x});", "var MSG_J = goog.getMsg('${$amt_earned}', {amt_earned: x});", MESSAGE_TREE_MALFORMED); } public void testBadPlaceholderReferenceInReplacement() { registerMessage(new JsMessage.Builder("MSG_K") .appendPlaceholderReference("amount") .build()); test( "var MSG_K = goog.getMsg('Hi {$jane}', {jane: x});", "var MSG_K = goog.getMsg('Hi {$jane}', {jane: x});", MESSAGE_TREE_MALFORMED); } public void testLegacyStyleNoPlaceholdersVarSyntax() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("Hi\nthere") .build()); style = Style.LEGACY; test("var MSG_A = 'd*6a0@z>t';", "var MSG_A=\"Hi\\nthere\""); } public void testLegacyStyleNoPlaceholdersFunctionSyntax() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("Hi\nthere") .build()); style = Style.LEGACY; test("var MSG_B = function() {return 'asdf'};", "var MSG_B=function(){return\"Hi\\nthere\"}"); } public void testLegacyStyleOnePlaceholder() { registerMessage(new JsMessage.Builder("MSG_C") .appendStringPart("One ") .appendPlaceholderReference("measly") .appendStringPart(" ph") .build()); style = Style.LEGACY; test( "var MSG_C = function(measly) {return 'asdf' + measly};", "var MSG_C=function(measly){return\"One \"+(measly+\" ph\")}"); } public void testLegacyStyleTwoPlaceholders() { registerMessage(new JsMessage.Builder("MSG_D") .appendPlaceholderReference("dick") .appendStringPart(" and ") .appendPlaceholderReference("jane") .build()); style = Style.LEGACY; test( "var MSG_D = function(jane, dick) {return jane + dick};", "var MSG_D=function(jane,dick){return dick+(\" and \"+jane)}"); } public void testLegacyStylePlaceholderNameInLowerCamelCase() { registerMessage(new JsMessage.Builder("MSG_E") .appendStringPart("Sum: $") .appendPlaceholderReference("amtEarned") .build()); style = Style.LEGACY; test( "var MSG_E = function(amtEarned) {return amtEarned + 'x'};", "var MSG_E=function(amtEarned){return\"Sum: $\"+amtEarned}"); } public void testLegacyStylePlaceholderNameInLowerUnderscoreCase() { registerMessage(new JsMessage.Builder("MSG_F") .appendStringPart("Sum: $") .appendPlaceholderReference("amt_earned") .build()); // Placeholder named in lower-underscore case (discouraged nowadays) style = Style.LEGACY; test( "var MSG_F = function(amt_earned) {return amt_earned + 'x'};", "var MSG_F=function(amt_earned){return\"Sum: $\"+amt_earned}"); } public void testLegacyStyleBadPlaceholderReferenceInReplacemen() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("Ola, ") .appendPlaceholderReference("chimp") .build()); test("var MSG_B = function(chump) {return chump + 'x'};", "var MSG_B = function(chump) {return chump + 'x'};", JsMessageVisitor.MESSAGE_TREE_MALFORMED); } public void testTranslatedPlaceHolderMissMatch() { registerMessage(new JsMessage.Builder("MSG_A") .appendPlaceholderReference("a") .appendStringPart("!") .build()); test("var MSG_A = goog.getMsg('{$a}');", "var MSG_A = goog.getMsg('{$a}');", MESSAGE_TREE_MALFORMED); } public void testBadFallbackSyntax1() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(MSG_A);", null, JsMessageVisitor.BAD_FALLBACK_SYNTAX); } public void testBadFallbackSyntax2() { test("var x = goog.getMsgWithFallback('abc', 'bcd');", null, JsMessageVisitor.BAD_FALLBACK_SYNTAX); } public void testBadFallbackSyntax3() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(MSG_A, y);", null, JsMessageVisitor.FALLBACK_ARG_ERROR); } public void testBadFallbackSyntax4() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('asdf');" + "var x = goog.getMsgWithFallback(y, MSG_A);", null, JsMessageVisitor.FALLBACK_ARG_ERROR); } public void testUseFallback() { registerMessage(new JsMessage.Builder("MSG_B") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'translated';" + "var x = MSG_B;"); } public void testFallbackEmptyBundle() { test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } public void testNoUseFallback() { registerMessage(new JsMessage.Builder("MSG_A") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'translated';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } public void testNoUseFallback2() { registerMessage(new JsMessage.Builder("MSG_C") .appendStringPart("translated") .build()); test("/** @desc d */\n" + "var MSG_A = goog.getMsg('msg A');" + "/** @desc d */\n" + "var MSG_B = goog.getMsg('msg B');" + "var x = goog.getMsgWithFallback(MSG_A, MSG_B);", "var MSG_A = 'msg A';" + "var MSG_B = 'msg B';" + "var x = MSG_A;"); } private void registerMessage(JsMessage message) { messages.put(message.getKey(), message); } private class SimpleMessageBundle implements MessageBundle { @Override public JsMessage getMessage(String id) { return messages.get(id); } @Override public Iterable getAllMessages() { return messages.values(); } @Override public JsMessage.IdGenerator idGenerator() { return null; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveUnusedNamesTest.java0000644000175000017500000001110612115204405030133 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.Node; /** * Tests for {@link RemoveUnusedPrototypeProperties}. * * @author nicksantos@google.com (Nick Santos) */ public class RemoveUnusedNamesTest extends CompilerTestCase { private static final String EXTERNS = "/** @constructor */\n " + "function IFoo() { } \n" + "IFoo.prototype.bar; \n" + "/** @constructor */\n " + "var mExtern; \n" + "mExtern.bExtern; \n" + "mExtern['cExtern']; \n"; public RemoveUnusedNamesTest() { super(EXTERNS); } private boolean canRemoveExterns = false; @Override protected int getNumRepetitions() { return 1; } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { new TypeCheck(compiler, new SemanticReverseAbstractInterpreter( compiler.getCodingConvention(), compiler.getTypeRegistry()), compiler.getTypeRegistry(), CheckLevel.ERROR, CheckLevel.ERROR).processForTesting(externs, root); new RemoveUnusedNames( compiler, canRemoveExterns).process(externs, root); // Use to remove side-effect-free artifacts that are left over. new UnreachableCodeElimination(compiler, true).process(externs, root); } }; } @Override public void setUp() { canRemoveExterns = false; } public void testAnalyzeUnusedPrototypeProperties() { // Basic removal for prototype properties test("/** @constructor */ \n" + "function e(){} \n" + "e.prototype.a = function(){};" + "e.prototype.b = function(){};" + "var x = new e; x.a()", "function e(){}" + " e.prototype.a = function(){};" + "var x = new e; x.a()"); } public void testAnalyzeUnusedPrototypeProperties2() { // TODO(user): Prototype literal not yet supported. // Basic removal for prototype replacement //test("/** @constructor */ \n" + // "function e(){} \n" + // "e.prototype = {a: function(){}, b: function(){}};" + // "var x=new e; x.a()", // // "function e(){}" + // "e.prototype = {a: function(){}};" + // "var x = new e; x.a()"); } public void testAnalyzeUnusedPrototypeProperties3() { // Even if bExtern is out there somewhere. The type system tells us they // are unrelated. test("/** @constructor */ \n" + "function e(){} \n" + "e.prototype.a = function(){};" + "e.prototype.bExtern = function(){};" + "var x = new e;x.a()", "function e(){}" + "e.prototype.a = function(){};" + //"e.prototype.bExtern = function(){};" + "var x = new e; x.a()"); // TODO(user): Prototype literal not yet supported. //test("/** @constructor */ \n" + // "function e(){} \n" + // "e.prototype = {a: function(){}, bExtern: function(){}};" + // "var x = new e; x.a()", // "function e(){}" + // "e.prototype = {a: function(){}, bExtern: function(){}};" + // "var x = new e; x.a()"); } public void testAliasing() { // TODO(user): Not fully supported. } public void testStatement() { test("/**\n" + " * @fileoverview\n" + " * @notypecheck\n" + " */ \n" + "/** @constructor */" + "function e(){}" + "var x = e.prototype.method1 = function(){};" + "var y = new e; x()", "function e(){}" + "var x = function(){};" + "var y = new e; x()"); } public void testExportedMethodsByNamingConvention() { // TODO(user): Not fully supported. } public void testExportedMethodsByNamingConventionAlwaysExported() { // TODO(user): Not fully supported. } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveTryCatchTest.java0000644000175000017500000000450012115204405027425 0ustar apoapo/* * Copyright 2006 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; public class RemoveTryCatchTest extends CompilerTestCase { public RemoveTryCatchTest() { // do not compare as tree because we use synthetic blocks super("", false); } @Override public CompilerPass getProcessor(Compiler compiler) { return new RemoveTryCatch(compiler); } @Override public int getNumRepetitions() { // Use only one repetition because test code contains JSDoc comments. return 1; } public void testRemoveTryCatch() { test("try{var a=1;}catch(ex){var b=2;}", "var b;var a=1"); test("try{var a=1;var b=2}catch(ex){var c=3;var d=4;}", "var d;var c;var a=1;var b=2"); test("try{var a=1;var b=2}catch(ex){}", "var a=1;var b=2"); } public void testRemoveTryFinally() { test("try{var a=1;}finally{var c=3;}", "var a=1;var c=3"); test("try{var a=1;var b=2}finally{var e=5;var f=6;}", "var a=1;var b=2;var e=5;var f=6"); } public void testRemoveTryCatchFinally() { test("try{var a=1;}catch(ex){var b=2;}finally{var c=3;}", "var b;var a=1;var c=3"); test("try{var a=1;var b=2}catch(ex){var c=3;var d=4;}finally{var e=5;" + "var f=6;}", "var d;var c;var a=1;var b=2;var e=5;var f=6"); } public void testPreserveTryBlockContainingReturnStatement() { testSame("function f(){var a;try{a=1;return}finally{a=2}}"); } public void testPreserveAnnotatedTryBlock() { test("/** @preserveTry */try{var a=1;}catch(ex){var b=2;}", "try{var a=1}catch(ex){var b=2}"); } public void testIfTryFinally() { test("if(x)try{y}finally{z}", "if(x){y;z}"); } public void testIfTryCatch() { test("if(x)try{y;z}catch(e){}", "if(x){y;z}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MemoizedScopeCreatorTest.java0000644000175000017500000000417712115204405030623 0ustar apoapo/* * 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.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; /** * A dopey test for {@link MemoizedScopeCreator}. This is mostly here * just so it's easy to write more tests if this becomes more complicated. * * @author nicksantos@google.com (Nick Santos) */ public class MemoizedScopeCreatorTest extends TestCase { public void testMemoization() throws Exception { Node trueNode = new Node(Token.TRUE); Node falseNode = new Node(Token.FALSE); // Wow, is there really a circular dependency between JSCompiler and // SyntacticScopeCreator? Compiler compiler = new Compiler(); compiler.initOptions(new CompilerOptions()); ScopeCreator creator = new MemoizedScopeCreator( new SyntacticScopeCreator(compiler)); Scope scopeA = creator.createScope(trueNode, null); assertSame(scopeA, creator.createScope(trueNode, null)); assertNotSame(scopeA, creator.createScope(falseNode, null)); } public void testPreconditionCheck() throws Exception { Compiler compiler = new Compiler(); compiler.initOptions(new CompilerOptions()); Node trueNode = new Node(Token.TRUE); ScopeCreator creator = new MemoizedScopeCreator( new SyntacticScopeCreator(compiler)); Scope scopeA = creator.createScope(trueNode, null); boolean handled = false; try { creator.createScope(trueNode, scopeA); } catch (IllegalStateException e) { handled = true; } assertTrue(handled); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MarkNoSideEffectCallsTest.java0000644000175000017500000002600412115204405030621 0ustar apoapo/* * 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 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.emptyList()); testMarkCalls("function f(){} f()", Collections.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.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.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.emptyList()); testMarkCalls("/**@nosideeffects*/function f(){}", "f = function(){};" + "f()", Collections.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.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.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.emptyList()); } public void testAnnotationInExterns() throws Exception { testMarkCalls("externSef1()", Collections.emptyList()); testMarkCalls("externSef2()", Collections.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.emptyList()); testMarkCalls("externObj.sef2()", Collections.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.emptyList()); // extern has side effects. testMarkCalls("var obj = {};" + "/**@nosideeffects*/obj.sef1 = function(){};", "obj.sef1()", Collections.emptyList()); // override in source also has side effects. testMarkCalls("var obj = {}; obj.nsef1 = function(){}; obj.nsef1()", Collections.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.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.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 expected) { testMarkCalls("", source, expected); } void testMarkCalls( String extraExterns, String source, List 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()); } } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NodeIteratorsTest.java0000644000175000017500000001335712115204405027322 0ustar apoapo/* * 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.Lists; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.jscomp.NodeIterators.FunctionlessLocalScope; import com.google.javascript.jscomp.NodeIterators.LocalVarMotion; import java.util.Iterator; import java.util.List; import junit.framework.TestCase; /** * Tests for NodeIterators. * @author nicksantos@google.com (Nick Santos) */ public class NodeIteratorsTest extends TestCase { // In each test, we find the declaration of "X" in the local scope, // construct a list of all nodes where X is guaranteed to retain its // original value, and compare those nodes against an expected list of // tokens. public void testBasic() { testVarMotionWithCode("var X = 3;", Token.VAR, Token.SCRIPT); } public void testNamedFunction() { testVarMotionWithCode("var X = 3; function f() {}", Token.VAR, Token.SCRIPT); } public void testNamedFunction2() { testVarMotionWithCode("var X = 3; function f() {} var Y;", Token.VAR, Token.NAME, Token.VAR, Token.SCRIPT); } public void testFunctionExpression() { testVarMotionWithCode("var X = 3, Y = function() {}; 3;", Token.NAME, Token.VAR, Token.NUMBER, Token.EXPR_RESULT, Token.SCRIPT); } public void testFunctionExpression2() { testVarMotionWithCode("var X = 3; var Y = function() {}; 3;", Token.VAR, Token.NAME, Token.VAR, Token.NUMBER, Token.EXPR_RESULT, Token.SCRIPT); } public void testHaltAtVarRef() { testVarMotionWithCode("var X, Y = 3; var Z = X;", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltAtVarRef2() { testVarMotionWithCode("var X, Y = 3; (function() {})(3, X);", Token.NUMBER, Token.NAME, Token.VAR, Token.NUMBER, Token.NAME); } public void testHaltAtVarRef3() { testVarMotionWithCode("var X, Y = 3; X;", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltAtSideEffects() { testVarMotionWithCode("var X, Y = 3; var Z = B(3);", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME, Token.NUMBER); } public void testHaltAtSideEffects2() { testVarMotionWithCode("var A = 1, X = A, Y = 3; delete A;", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltAtSideEffects3() { testVarMotionWithCode("var A = 1, X = A, Y = 3; A++;", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltAtSideEffects4() { testVarMotionWithCode("var A = 1, X = A, Y = 3; A--;", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltAtSideEffects5() { testVarMotionWithCode("var A = 1, X = A, Y = 3; A = 'a';", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME, Token.STRING); } public void testNoHaltReadWhenValueIsImmutable() { testVarMotionWithCode("var X = 1, Y = 3; alert();", Token.NUMBER, Token.NAME, Token.VAR, Token.NAME); } public void testHaltReadWhenValueHasSideEffects() { testVarMotionWithCode("var X = f(), Y = 3; alert();", Token.NUMBER, Token.NAME, Token.VAR); } public void testCatchBlock() { testVarMotionWithCode("var X = 1; try { 4; } catch (X) {}", Token.VAR, Token.NUMBER, Token.EXPR_RESULT, Token.BLOCK); } public void testIfBranch() { testVarMotionWithCode("var X = foo(); if (X) {}", Token.VAR, Token.NAME); } /** * Parses the given code, finds the variable X in the global scope, and runs * the VarMotion iterator. Asserts that the iteration order matches the * tokens given. */ private void testVarMotionWithCode(String code, int ... expectedTokens) { List expectedList = Lists.newArrayList(); for (int token : expectedTokens) { expectedList.add(token); } testVarMotionWithCode(code, expectedList); } /** * @see #testVarMotionWithCode(String, int ...) */ private void testVarMotionWithCode(String code, List expectedTokens) { List ancestors = Lists.newArrayList(); // Add an empty node to the beginning of the code and start there. Node root = (new Compiler()).parseTestCode(";" + code); for (Node n = root; n != null; n = n.getFirstChild()) { ancestors.add(0, n); } FunctionlessLocalScope searchIt = new FunctionlessLocalScope( ancestors.toArray(new Node[ancestors.size()])); boolean found = false; while (searchIt.hasNext()) { Node n = searchIt.next(); if (n.isName() && searchIt.currentParent().isVar() && n.getString().equals("X")) { found = true; break; } } assertTrue("Variable X not found! " + root.toStringTree(), found); List currentAncestors = searchIt.currentAncestors(); assert(currentAncestors.size() >= 3); Iterator moveIt = LocalVarMotion.forVar( currentAncestors.get(0), currentAncestors.get(1), currentAncestors.get(2)); List actualTokens = Lists.newArrayList(); while (moveIt.hasNext()) { actualTokens.add(moveIt.next().getType()); } assertEquals(expectedTokens, actualTokens); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReplaceStringsTest.java0000644000175000017500000003231312115204405027456 0ustar apoapo/* * 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.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.javascript.jscomp.ReplaceStrings.Result; import com.google.javascript.rhino.Node; import java.util.Collections; import java.util.List; import java.util.Set; /** * Tests for {@link ReplaceStrings}. * */ public class ReplaceStringsTest extends CompilerTestCase { private ReplaceStrings pass; private Set reserved; private VariableMap previous; private final static String EXTERNS = "var goog = {};\n" + "goog.debug = {};\n" + "/** @constructor */\n" + "goog.debug.Trace = function() {};\n" + "goog.debug.Trace.startTracer = function (var_args) {};\n" + "/** @constructor */\n" + "goog.debug.Logger = function() {};\n" + "goog.debug.Logger.prototype.info = function(msg, opt_ex) {};\n" + "/**\n" + " * @param {string} name\n" + " * @return {!goog.debug.Logger}\n" + " */\n" + "goog.debug.Logger.getLogger = function(name){};\n"; public ReplaceStringsTest() { super(EXTERNS, true); enableNormalize(); } @Override protected CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); options.setWarningLevel( DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF); return options; } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(false); super.enableTypeCheck(CheckLevel.OFF); reserved = Collections.emptySet(); previous = null; } @Override public CompilerPass getProcessor(final Compiler compiler) { List names = Lists.newArrayList( "Error(?)", "goog.debug.Trace.startTracer(*)", "goog.debug.Logger.getLogger(?)", "goog.debug.Logger.prototype.info(?)" ); pass = new ReplaceStrings(compiler, "`", names, reserved, previous); return new CompilerPass() { @Override public void process(Node externs, Node js) { new CollapseProperties(compiler, true, true).process(externs, js); pass.process(externs, js); } }; } @Override public int getNumRepetitions() { // This compiler pass is not idempotent and should only be run over a // parse tree once. return 1; } public void testStable1() { previous = VariableMap.fromMap(ImmutableMap.of("previous","xyz")); testDebugStrings( "Error('xyz');", "Error('previous');", (new String[] { "previous", "xyz" })); reserved = ImmutableSet.of("a", "b", "previous"); testDebugStrings( "Error('xyz');", "Error('c');", (new String[] { "c", "xyz" })); } public void testStable2() { // Two things happen here: // 1) a previously used name "a" is not used for another string, "b" is // chosen instead. // 2) a previously used name "a" is dropped from the output map if // it isn't used. previous = VariableMap.fromMap(ImmutableMap.of("a","unused")); testDebugStrings( "Error('xyz');", "Error('b');", (new String[] { "b", "xyz" })); } public void testThrowError1() { testDebugStrings( "throw Error('xyz');", "throw Error('a');", (new String[] { "a", "xyz" })); previous = VariableMap.fromMap(ImmutableMap.of("previous","xyz")); testDebugStrings( "throw Error('xyz');", "throw Error('previous');", (new String[] { "previous", "xyz" })); } public void testThrowError2() { testDebugStrings( "throw Error('x' +\n 'yz');", "throw Error('a');", (new String[] { "a", "xyz" })); } public void testThrowError3() { testDebugStrings( "throw Error('Unhandled mail' + ' search type ' + type);", "throw Error('a' + '`' + type);", (new String[] { "a", "Unhandled mail search type `" })); } public void testThrowError4() { testDebugStrings( "/** @constructor */\n" + "var A = function() {};\n" + "A.prototype.m = function(child) {\n" + " if (this.haveChild(child)) {\n" + " throw Error('Node: ' + this.getDataPath() +\n" + " ' already has a child named ' + child);\n" + " } else if (child.parentNode) {\n" + " throw Error('Node: ' + child.getDataPath() +\n" + " ' already has a parent');\n" + " }\n" + " child.parentNode = this;\n" + "};", "var A = function(){};\n" + "A.prototype.m = function(child) {\n" + " if (this.haveChild(child)) {\n" + " throw Error('a' + '`' + this.getDataPath() + '`' + child);\n" + " } else if (child.parentNode) {\n" + " throw Error('b' + '`' + child.getDataPath());\n" + " }\n" + " child.parentNode = this;\n" + "};", (new String[] { "a", "Node: ` already has a child named `", "b", "Node: ` already has a parent", })); } public void testThrowNonStringError() { // No replacement is done when an error is neither a string literal nor // a string concatenation expression. testDebugStrings( "throw Error(x('abc'));", "throw Error(x('abc'));", (new String[] { })); } public void testThrowConstStringError() { testDebugStrings( "var AA = 'uvw', AB = 'xyz'; throw Error(AB);", "var AA = 'uvw', AB = 'xyz'; throw Error('a');", (new String [] { "a", "xyz" })); } public void testThrowNewError1() { testDebugStrings( "throw new Error('abc');", "throw new Error('a');", (new String[] { "a", "abc" })); } public void testThrowNewError2() { testDebugStrings( "throw new Error();", "throw new Error();", new String[] {}); } public void testStartTracer1() { testDebugStrings( "goog.debug.Trace.startTracer('HistoryManager.updateHistory');", "goog.debug.Trace.startTracer('a');", (new String[] { "a", "HistoryManager.updateHistory" })); } public void testStartTracer2() { testDebugStrings( "goog$debug$Trace.startTracer('HistoryManager', 'updateHistory');", "goog$debug$Trace.startTracer('a', 'b');", (new String[] { "a", "HistoryManager", "b", "updateHistory" })); } public void testStartTracer3() { testDebugStrings( "goog$debug$Trace.startTracer('ThreadlistView',\n" + " 'Updating ' + array.length + ' rows');", "goog$debug$Trace.startTracer('a', 'b' + '`' + array.length);", new String[] { "a", "ThreadlistView", "b", "Updating ` rows" }); } public void testStartTracer4() { testDebugStrings( "goog.debug.Trace.startTracer(s, 'HistoryManager.updateHistory');", "goog.debug.Trace.startTracer(s, 'a');", (new String[] { "a", "HistoryManager.updateHistory" })); } public void testLoggerInitialization() { testDebugStrings( "goog$debug$Logger$getLogger('my.app.Application');", "goog$debug$Logger$getLogger('a');", (new String[] { "a", "my.app.Application" })); } public void testLoggerOnObject1() { testDebugStrings( "var x = {};" + "x.logger_ = goog.debug.Logger.getLogger('foo');" + "x.logger_.info('Some message');", "var x$logger_ = goog.debug.Logger.getLogger('a');" + "x$logger_.info('b');", new String[] { "a", "foo", "b", "Some message"}); } // Non-matching "info" property. public void testLoggerOnObject2() { test( "var x = {};" + "x.info = function(a) {};" + "x.info('Some message');", "var x$info = function(a) {};" + "x$info('Some message');"); } // Non-matching "info" prototype property. public void testLoggerOnObject3a() { testSame( "/** @constructor */\n" + "var x = function() {};\n" + "x.prototype.info = function(a) {};" + "(new x).info('Some message');"); } // Non-matching "info" prototype property. public void testLoggerOnObject3b() { testSame( "/** @constructor */\n" + "var x = function() {};\n" + "x.prototype.info = function(a) {};" + "var y = (new x); this.info('Some message');"); } // Non-matching "info" property on "NoObject" type. public void testLoggerOnObject4() { testSame("(new x).info('Some message');"); } // Non-matching "info" property on "UnknownObject" type. public void testLoggerOnObject5() { testSame("my$Thing.logger_.info('Some message');"); } public void testLoggerOnVar() { testDebugStrings( "var logger = goog.debug.Logger.getLogger('foo');" + "logger.info('Some message');", "var logger = goog.debug.Logger.getLogger('a');" + "logger.info('b');", new String[] { "a", "foo", "b", "Some message"}); } public void testLoggerOnThis() { testDebugStrings( "function f() {" + " this.logger_ = goog.debug.Logger.getLogger('foo');" + " this.logger_.info('Some message');" + "}", "function f() {" + " this.logger_ = goog.debug.Logger.getLogger('a');" + " this.logger_.info('b');" + "}", new String[] { "a", "foo", "b", "Some message"}); } public void testRepeatedErrorString1() { testDebugStrings( "Error('abc');Error('def');Error('abc');", "Error('a');Error('b');Error('a');", (new String[] { "a", "abc", "b", "def" })); } public void testRepeatedErrorString2() { testDebugStrings( "Error('a:' + u + ', b:' + v); Error('a:' + x + ', b:' + y);", "Error('a' + '`' + u + '`' + v); Error('a' + '`' + x + '`' + y);", (new String[] { "a", "a:`, b:`" })); } public void testRepeatedErrorString3() { testDebugStrings( "var AB = 'b'; throw Error(AB); throw Error(AB);", "var AB = 'b'; throw Error('a'); throw Error('a');", (new String[] { "a", "b" })); } public void testRepeatedTracerString() { testDebugStrings( "goog$debug$Trace.startTracer('A', 'B', 'A');", "goog$debug$Trace.startTracer('a', 'b', 'a');", (new String[] { "a", "A", "b", "B" })); } public void testRepeatedLoggerString() { testDebugStrings( "goog$debug$Logger$getLogger('goog.net.XhrTransport');" + "goog$debug$Logger$getLogger('my.app.Application');" + "goog$debug$Logger$getLogger('my.app.Application');", "goog$debug$Logger$getLogger('a');" + "goog$debug$Logger$getLogger('b');" + "goog$debug$Logger$getLogger('b');", new String[] { "a", "goog.net.XhrTransport","b", "my.app.Application" }); } public void testRepeatedStringsWithDifferentMethods() { test( "throw Error('A');" + "goog$debug$Trace.startTracer('B', 'A');" + "goog$debug$Logger$getLogger('C');" + "goog$debug$Logger$getLogger('B');" + "goog$debug$Logger$getLogger('A');" + "throw Error('D');" + "throw Error('C');" + "throw Error('B');" + "throw Error('A');", "throw Error('a');" + "goog$debug$Trace.startTracer('b', 'a');" + "goog$debug$Logger$getLogger('c');" + "goog$debug$Logger$getLogger('b');" + "goog$debug$Logger$getLogger('a');" + "throw Error('d');" + "throw Error('c');" + "throw Error('b');" + "throw Error('a');"); } public void testReserved() { testDebugStrings( "throw Error('xyz');", "throw Error('a');", (new String[] { "a", "xyz" })); reserved = ImmutableSet.of("a", "b", "c"); testDebugStrings( "throw Error('xyz');", "throw Error('d');", (new String[] { "d", "xyz" })); } private void testDebugStrings(String js, String expected, String[] substitutedStrings) { // Verify that the strings are substituted correctly in the JS code. test(js, expected); List results = pass.getResult(); assertTrue(substitutedStrings.length % 2 == 0); assertEquals(substitutedStrings.length/2, results.size()); // Verify that substituted strings are decoded correctly. for (int i = 0; i < substitutedStrings.length; i += 2) { Result result = results.get(i/2); String original = substitutedStrings[i + 1]; assertEquals(original, result.original); String replacement = substitutedStrings[i]; assertEquals(replacement, result.replacement); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReplaceIdGeneratorsTest.java0000644000175000017500000002557612115204405030430 0ustar apoapo/* * 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; /** * Tests for {@link ReplaceIdGenerators}. * */ public class ReplaceIdGeneratorsTest extends CompilerTestCase { private boolean generatePseudoNames = false; private ReplaceIdGenerators lastPass = null; private String previousMappings = null; @Override protected CompilerPass getProcessor(final Compiler compiler) { lastPass = new ReplaceIdGenerators( compiler, new ImmutableSet.Builder() .add("goog.events.getUniqueId") .add("goog.place.getUniqueId") .build(), generatePseudoNames, previousMappings); return lastPass; } @Override protected void setUp() throws Exception { super.setUp(); generatePseudoNames = false; previousMappings = null; } @Override protected int getNumRepetitions() { return 1; } public void testBackwardCompat() { test("foo.bar = goog.events.getUniqueId('foo_bar')", "foo.bar = 'a'", "foo.bar = 'foo_bar$0'"); } public void testSerialization1() { testMap("var x = goog.events.getUniqueId('xxx');\n" + "var y = goog.events.getUniqueId('yyy');\n", "var x = 'a';\n" + "var y = 'b';\n", "[goog.events.getUniqueId]\n" + "\n" + "a:testcode:1:32\n" + "b:testcode:2:32\n" + "\n"); } public void testSerialization2() { testMap("/** @consistentIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1')", "id = function() {};" + "f1 = 'a';" + "f1 = 'a'", "[id]\n" + "\n" + "a:f1\n" + "\n"); } public void testReusePreviousSerialization1() { previousMappings = "[goog.events.getUniqueId]\n" + "\n" + "previous1:testcode:1:32\n" + "previous2:testcode:2:32\n" + "\n" + "[goog.place.getUniqueId]\n" + "\n" + "\n"; testMap("var x = goog.events.getUniqueId('xxx');\n" + "var y = goog.events.getUniqueId('yyy');\n", "var x = 'previous1';\n" + "var y = 'previous2';\n", "[goog.events.getUniqueId]\n" + "\n" + "previous1:testcode:1:32\n" + "previous2:testcode:2:32\n" + "\n"); } public void testReusePreviousSerialization2() { previousMappings = "[goog.events.getUniqueId]\n" + "\n" + "a:testcode:1:32\n" + "b:testcode:2:32\n" + "\n" + "[goog.place.getUniqueId]\n" + "\n" + "\n"; testMap( "var x = goog.events.getUniqueId('xxx');\n" + "\n" + // new line to change location "var y = goog.events.getUniqueId('yyy');\n", "var x = 'a';\n" + "var y = 'c';\n", "[goog.events.getUniqueId]\n" + "\n" + "a:testcode:1:32\n" + "c:testcode:3:32\n" + "\n"); } public void testReusePreviousSerializationConsistent1() { previousMappings = "[id]\n" + "\n" + "a:f1\n" + "\n"; testMap( "/** @consistentIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1')", "id = function() {};" + "f1 = 'a';" + "f1 = 'a'", "[id]\n" + "\n" + "a:f1\n" + "\n"); } public void testSimple() { test("/** @idGenerator */ foo.getUniqueId = function() {};" + "foo.bar = foo.getUniqueId('foo_bar')", "foo.getUniqueId = function() {};" + "foo.bar = 'a'", "foo.getUniqueId = function() {};" + "foo.bar = 'foo_bar$0'"); test("/** @idGenerator */ goog.events.getUniqueId = function() {};" + "foo1 = goog.events.getUniqueId('foo1');" + "foo1 = goog.events.getUniqueId('foo1');", "goog.events.getUniqueId = function() {};" + "foo1 = 'a';" + "foo1 = 'b';", "goog.events.getUniqueId = function() {};" + "foo1 = 'foo1$0';" + "foo1 = 'foo1$1';"); } public void testSimpleConsistent() { test("/** @consistentIdGenerator */ id = function() {};" + "foo.bar = id('foo_bar')", "id = function() {};" + "foo.bar = 'a'", "id = function() {};" + "foo.bar = 'foo_bar$0'"); test("/** @consistentIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1')", "id = function() {};" + "f1 = 'a';" + "f1 = 'a'", "id = function() {};" + "f1 = 'f1$0';" + "f1 = 'f1$0'"); test("/** @consistentIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1');" + "f1 = id('f1')", "id = function() {};" + "f1 = 'a';" + "f1 = 'a';" + "f1 = 'a'", "id = function() {};" + "f1 = 'f1$0';" + "f1 = 'f1$0';" + "f1 = 'f1$0'"); } public void testSimpleStable() { testNonPseudoSupportingGenerator( "/** @stableIdGenerator */ id = function() {};" + "foo.bar = id('foo_bar')", "id = function() {};" + "foo.bar = '125lGg'"); testNonPseudoSupportingGenerator( "/** @stableIdGenerator */ id = function() {};" + "f1 = id('f1');" + "f1 = id('f1')", "id = function() {};" + "f1 = 'AAAMiw';" + "f1 = 'AAAMiw'"); } public void testVar() { test("/** @consistentIdGenerator */ var id = function() {};" + "foo.bar = id('foo_bar')", "var id = function() {};" + "foo.bar = 'a'", "var id = function() {};" + "foo.bar = 'foo_bar$0'"); testNonPseudoSupportingGenerator( "/** @stableIdGenerator */ var id = function() {};" + "foo.bar = id('foo_bar')", "var id = function() {};" + "foo.bar = '125lGg'"); } public void testObjLit() { test("/** @consistentIdGenerator */ get.id = function() {};" + "foo.bar = {a: get.id('foo_bar')}", "get.id = function() {};" + "foo.bar = {a: 'a'}", "get.id = function() {};" + "foo.bar = {a: 'foo_bar$0'}"); testNonPseudoSupportingGenerator( "/** @stableIdGenerator */ get.id = function() {};" + "foo.bar = {a: get.id('foo_bar')}", "get.id = function() {};" + "foo.bar = {a: '125lGg'}"); } public void testTwoGenerators() { test("/** @idGenerator */ var id1 = function() {};" + "/** @idGenerator */ var id2 = function() {};" + "f1 = id1('1');" + "f2 = id1('1');" + "f3 = id2('1');" + "f4 = id2('1');", "var id1 = function() {};" + "var id2 = function() {};" + "f1 = 'a';" + "f2 = 'b';" + "f3 = 'a';" + "f4 = 'b';", "var id1 = function() {};" + "var id2 = function() {};" + "f1 = '1$0';" + "f2 = '1$1';" + "f3 = '1$0';" + "f4 = '1$1';"); } public void testMixedGenerators() { test("/** @idGenerator */ var id1 = function() {};" + "/** @consistentIdGenerator */ var id2 = function() {};" + "/** @stableIdGenerator */ var id3 = function() {};" + "f1 = id1('1');" + "f2 = id1('1');" + "f3 = id2('1');" + "f4 = id2('1');" + "f5 = id3('1');" + "f6 = id3('1');", "var id1 = function() {};" + "var id2 = function() {};" + "var id3 = function() {};" + "f1 = 'a';" + "f2 = 'b';" + "f3 = 'a';" + "f4 = 'a';" + "f5 = 'AAAAMQ';" + "f6 = 'AAAAMQ';", "var id1 = function() {};" + "var id2 = function() {};" + "var id3 = function() {};" + "f1 = '1$0';" + "f2 = '1$1';" + "f3 = '1$0';" + "f4 = '1$0';" + "f5 = 'AAAAMQ';" + "f6 = 'AAAAMQ';"); } public void testLocalCall() { testSame(new String[] {"/** @idGenerator */ var id = function() {}; " + "function Foo() { id('foo'); }"}, ReplaceIdGenerators.NON_GLOBAL_ID_GENERATOR_CALL); } public void testConditionalCall() { testSame(new String[] {"/** @idGenerator */ var id = function() {}; " + "if(x) id('foo');"}, ReplaceIdGenerators.CONDITIONAL_ID_GENERATOR_CALL); test("/** @consistentIdGenerator */ var id = function() {};" + "function fb() {foo.bar = id('foo_bar')}", "var id = function() {};" + "function fb() {foo.bar = 'a'}", "var id = function() {};" + "function fb() {foo.bar = 'foo_bar$0'}"); testNonPseudoSupportingGenerator( "/** @stableIdGenerator */ var id = function() {};" + "function fb() {foo.bar = id('foo_bar')}", "var id = function() {};" + "function fb() {foo.bar = '125lGg'}"); } public void testConflictingIdGenerator() { testSame(new String[] {"/** @idGenerator \n @consistentIdGenerator \n*/" + "var id = function() {}; "}, ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE); testSame(new String[] {"/** @stableIdGenerator \n @idGenerator \n*/" + "var id = function() {}; "}, ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE); testSame(new String[] {"/** @stableIdGenerator \n @consistentIdGenerator \n*/" + "var id = function() {}; "}, ReplaceIdGenerators.CONFLICTING_GENERATOR_TYPE); test("/** @consistentIdGenerator */ var id = function() {};" + "if (x) {foo.bar = id('foo_bar')}", "var id = function() {};" + "if (x) {foo.bar = 'a'}", "var id = function() {};" + "if (x) {foo.bar = 'foo_bar$0'}"); } private void testMap(String code, String expected, String expectedMap) { test(code, expected); assertEquals(expectedMap, lastPass.getSerializedIdMappings()); } private void test(String code, String expected, String expectedPseudo) { generatePseudoNames = false; test(code, expected); generatePseudoNames = true; test(code, expectedPseudo); } private void testNonPseudoSupportingGenerator(String code, String expected) { generatePseudoNames = false; test(code, expected); generatePseudoNames = true; test(code, expected); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DefaultCodingConventionTest.java0000644000175000017500000001301712115204405031304 0ustar apoapo/* * 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.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; /** * Test class for the default {@link CodingConvention}. */ public class DefaultCodingConventionTest extends TestCase { private CodingConvention conv = CodingConventions.getDefault(); public void testVarAndOptionalParams() { Node args = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "a"), Node.newString(Token.NAME, "b")); Node optArgs = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "opt_a"), Node.newString(Token.NAME, "opt_b")); assertFalse(conv.isVarArgsParameter(args.getFirstChild())); assertFalse(conv.isVarArgsParameter(args.getLastChild())); assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); assertFalse(conv.isVarArgsParameter(optArgs.getLastChild())); assertFalse(conv.isOptionalParameter(args.getFirstChild())); assertFalse(conv.isOptionalParameter(args.getLastChild())); assertFalse(conv.isOptionalParameter(optArgs.getFirstChild())); assertFalse(conv.isOptionalParameter(optArgs.getLastChild())); } public void testInlineName() { assertFalse(conv.isConstant("a")); assertFalse(conv.isConstant("XYZ123_")); assertFalse(conv.isConstant("ABC")); assertFalse(conv.isConstant("ABCdef")); assertFalse(conv.isConstant("aBC")); assertFalse(conv.isConstant("A")); assertFalse(conv.isConstant("_XYZ123")); assertFalse(conv.isConstant("a$b$XYZ123_")); assertFalse(conv.isConstant("a$b$ABC_DEF")); assertFalse(conv.isConstant("a$b$A")); assertFalse(conv.isConstant("a$b$a")); assertFalse(conv.isConstant("a$b$ABCdef")); assertFalse(conv.isConstant("a$b$aBC")); assertFalse(conv.isConstant("a$b$")); assertFalse(conv.isConstant("$")); } public void testExportedName() { assertFalse(conv.isExported("_a")); assertFalse(conv.isExported("_a_")); assertFalse(conv.isExported("a")); assertFalse(conv.isExported("$super", false)); assertTrue(conv.isExported("$super", true)); assertTrue(conv.isExported("$super")); } public void testPrivateName() { assertFalse(conv.isPrivate("a_")); assertFalse(conv.isPrivate("a")); assertFalse(conv.isPrivate("_a_")); } public void testEnumKey() { assertTrue(conv.isValidEnumKey("A")); assertTrue(conv.isValidEnumKey("123")); assertTrue(conv.isValidEnumKey("FOO_BAR")); assertTrue(conv.isValidEnumKey("a")); assertTrue(conv.isValidEnumKey("someKeyInCamelCase")); assertTrue(conv.isValidEnumKey("_FOO_BAR")); } public void testInheritanceDetection1() { assertNotClassDefining("goog.foo(A, B);"); } public void testInheritanceDetection2() { assertNotClassDefining("goog.inherits(A, B);"); } public void testInheritanceDetection3() { assertNotClassDefining("A.inherits(B);"); } public void testInheritanceDetection4() { assertNotClassDefining("goog.inherits(goog.A, goog.B);"); } public void testInheritanceDetection5() { assertNotClassDefining("goog.A.inherits(goog.B);"); } public void testInheritanceDetection6() { assertNotClassDefining("A.inherits(this.B);"); } public void testInheritanceDetection7() { assertNotClassDefining("this.A.inherits(B);"); } public void testInheritanceDetection8() { assertNotClassDefining("goog.inherits(A, B, C);"); } public void testInheritanceDetection9() { assertNotClassDefining("A.mixin(B.prototype);"); } public void testInheritanceDetection10() { assertNotClassDefining("goog.mixin(A.prototype, B.prototype);"); } public void testInheritanceDetectionPostCollapseProperties() { assertNotClassDefining("goog$inherits(A, B);"); assertNotClassDefining("goog$inherits(A);"); } public void testFunctionBind() { assertNotFunctionBind("goog.bind(f)"); assertNotFunctionBind("goog$bind(f)"); assertNotFunctionBind("goog.partial(f)"); assertNotFunctionBind("goog$partial(f)"); assertFunctionBind("(function(){}).bind()"); assertFunctionBind("(function(){}).bind(obj)"); assertFunctionBind("(function(){}).bind(obj, p1)"); assertNotFunctionBind("Function.prototype.bind.call()"); assertFunctionBind("Function.prototype.bind.call(obj)"); assertFunctionBind("Function.prototype.bind.call(obj, p1)"); } private void assertFunctionBind(String code) { Node n = parseTestCode(code); assertNotNull(conv.describeFunctionBind(n.getFirstChild())); } private void assertNotFunctionBind(String code) { Node n = parseTestCode(code); assertNull(conv.describeFunctionBind(n.getFirstChild())); } private void assertNotClassDefining(String code) { Node n = parseTestCode(code); assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); } private Node parseTestCode(String code) { Compiler compiler = new Compiler(); return compiler.parseTestCode(code).getFirstChild(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/0000755000175000017500000000000012115204405023757 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/JsFunctionParserTest.java0000644000175000017500000001344012115204405030723 0ustar apoapo/* * 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.deps; import com.google.common.collect.Lists; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.PrintStreamErrorManager; import com.google.javascript.jscomp.deps.JsFunctionParser.SymbolInfo; import junit.framework.TestCase; import java.util.Collection; import java.util.Iterator; /** * Tests for {@link JsFunctionParser} * * @author agrieve@google.com (Andrew Grieve) * @author ielashi@google.com (Islam El-Ashi) */ public class JsFunctionParserTest extends TestCase { private static final String SRC_PATH = "a"; private JsFunctionParser parser; private ErrorManager errorManager; private Collection functions = Lists.newArrayList( "goog.require", "goog.provide"); @Override public void setUp() { errorManager = new PrintStreamErrorManager(System.err); parser = new JsFunctionParser(functions, errorManager); parser.setShortcutMode(true); } /** * Tests: * -Parsing of comments, * -Parsing of different styles of quotes, * -Correct recording of what was parsed. */ public void testParseFile() { final String CONTENTS = "/*" + "goog.provide('no1');*//*\n" + "goog.provide('no2');\n" + "*/goog.provide('yes1');\n" + "/* blah */goog.provide(\"yes2\")/* blah*/\n" + "goog.require('yes3'); // goog.provide('no3');\n" + "// goog.provide('no4');\n" + "goog.require(\"" + "bar.data.SuperstarAddStarThreadActionRequestDelegate\"); " + "//no new line at EOF"; Collection symbols = parser.parseFile(SRC_PATH, CONTENTS); Iterator i = symbols.iterator(); SymbolInfo symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes1"); assertEquals(symbolInfo.functionName, "goog.provide"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes2"); assertEquals(symbolInfo.functionName, "goog.provide"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes3"); assertEquals(symbolInfo.functionName, "goog.require"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "bar.data.SuperstarAddStarThreadActionRequestDelegate"); assertEquals(symbolInfo.functionName, "goog.require"); assertEquals(symbols.size(), 4); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testMultiplePerLine() { final String CONTENTS = "goog.provide('yes1');goog.provide('yes2');/*" + "goog.provide('no1');*/goog.provide('yes3');//goog.provide('no2');"; Collection symbols = parser.parseFile(SRC_PATH, CONTENTS); Iterator i = symbols.iterator(); SymbolInfo symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes1"); assertEquals(symbolInfo.functionName, "goog.provide"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes2"); assertEquals(symbolInfo.functionName, "goog.provide"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes3"); assertEquals(symbolInfo.functionName, "goog.provide"); assertEquals(symbols.size(), 3); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testShortcutMode1() { // For efficiency reasons, we stop reading after the ctor. final String CONTENTS = " // hi ! \n /* this is a comment */ " + "goog.provide('yes1');\n /* and another comment */ \n" + "goog.provide('yes2'); // include this\n" + "function foo() {}\n" + "goog.provide('no1');"; Collection symbols = parser.parseFile(SRC_PATH, CONTENTS); Iterator i = symbols.iterator(); SymbolInfo symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes1"); assertEquals(symbolInfo.functionName, "goog.provide"); symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes2"); assertEquals(symbolInfo.functionName, "goog.provide"); assertEquals(symbols.size(), 2); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testShortcutMode2() { final String CONTENTS = "/** goog.provide('no1'); \n" + " * goog.provide('no2');\n */\n" + "goog.provide('yes1');\n"; Collection symbols = parser.parseFile(SRC_PATH, CONTENTS); Iterator i = symbols.iterator(); SymbolInfo symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes1"); assertEquals(symbolInfo.functionName, "goog.provide"); assertEquals(symbols.size(), 1); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testShortcutMode3() { final String CONTENTS = "/**\n" + " * goog.provide('no1');\n */\n" + "goog.provide('yes1');\n"; Collection symbols = parser.parseFile(SRC_PATH, CONTENTS); Iterator i = symbols.iterator(); SymbolInfo symbolInfo = i.next(); assertEquals(symbolInfo.symbol, "yes1"); assertEquals(symbolInfo.functionName, "goog.provide"); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/SortedDependenciesTest.java0000644000175000017500000001362612115204405031241 0ustar apoapo/* * 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.deps; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; import junit.framework.TestCase; import java.util.List; /** * Tests for {@link SortedDependencies} * @author nicksantos@google.com (Nick Santos) */ public class SortedDependenciesTest extends TestCase { public void testSort() throws Exception { SimpleDependencyInfo a = new SimpleDependencyInfo( "a", "a", provides(), requires("b", "c")); SimpleDependencyInfo b = new SimpleDependencyInfo( "b", "b", provides("b"), requires("d")); SimpleDependencyInfo c = new SimpleDependencyInfo( "c", "c", provides("c"), requires("d")); SimpleDependencyInfo d = new SimpleDependencyInfo( "d", "d", provides("d"), requires()); SimpleDependencyInfo e = new SimpleDependencyInfo( "e", "e", provides("e"), requires()); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(a, b, c, d)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, b, c, a)); assertSortedInputs( ImmutableList.of(d, c, b, a), ImmutableList.of(d, c, b, a)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, a, b, c)); assertSortedDeps( ImmutableList.of(d, b, c, a), ImmutableList.of(d, b, c, a), ImmutableList.of(a)); assertSortedDeps( ImmutableList.of(d, c), ImmutableList.of(d, c, b, a), ImmutableList.of(c)); assertSortedDeps( ImmutableList.of(d), ImmutableList.of(d, c, b, a), ImmutableList.of(d)); try { assertSortedDeps( ImmutableList.of(), ImmutableList.of(a, b, c, d), ImmutableList.of(e)); fail("Expected an exception"); } catch (IllegalArgumentException expected) {} } public void testSort2() throws Exception { SimpleDependencyInfo ab = new SimpleDependencyInfo( "ab", "ab", provides("a", "b"), requires("d", "f")); SimpleDependencyInfo c = new SimpleDependencyInfo( "c", "c", provides("c"), requires("h")); SimpleDependencyInfo d = new SimpleDependencyInfo( "d", "d", provides("d"), requires("e", "f")); SimpleDependencyInfo ef = new SimpleDependencyInfo( "ef", "ef", provides("e", "f"), requires("g", "c")); SimpleDependencyInfo g = new SimpleDependencyInfo( "g", "g", provides("g"), requires()); SimpleDependencyInfo hi = new SimpleDependencyInfo( "hi", "hi", provides("h", "i"), requires()); assertSortedInputs( ImmutableList.of(g, hi, c, ef, d, ab), ImmutableList.of(ab, c, d, ef, g, hi)); assertSortedDeps( ImmutableList.of(g), ImmutableList.of(ab, c, d, ef, g, hi), ImmutableList.of(g)); assertSortedDeps( ImmutableList.of(g, hi, c, ef, d), ImmutableList.of(ab, c, d, ef, g, hi), ImmutableList.of(d, hi)); } public void testSort3() { SimpleDependencyInfo a = new SimpleDependencyInfo( "a", "a", provides("a"), requires("c")); SimpleDependencyInfo b = new SimpleDependencyInfo( "b", "b", provides("b"), requires("a")); SimpleDependencyInfo c = new SimpleDependencyInfo( "c", "c", provides("c"), requires("b")); try { new SortedDependencies( Lists.newArrayList(a, b, c)); fail("expected exception"); } catch (CircularDependencyException e) { assertEquals("a -> a", e.getMessage()); } } public void testSort4() throws Exception { // Check the degenerate case. SimpleDependencyInfo a = new SimpleDependencyInfo( "a", "a", provides("a"), requires("a")); assertSortedDeps( ImmutableList.of(a), ImmutableList.of(a), ImmutableList.of(a)); } public void testSort5() throws Exception { SimpleDependencyInfo a = new SimpleDependencyInfo( "a", "a", provides("a"), requires()); SimpleDependencyInfo b = new SimpleDependencyInfo( "b", "b", provides("b"), requires()); SimpleDependencyInfo c = new SimpleDependencyInfo( "c", "c", provides("c"), requires()); assertSortedInputs( ImmutableList.of(a, b, c), ImmutableList.of(a, b, c)); assertSortedInputs( ImmutableList.of(c, b, a), ImmutableList.of(c, b, a)); } private void assertSortedInputs( List expected, List shuffled) throws Exception { SortedDependencies sorted = new SortedDependencies(shuffled); assertEquals(expected, sorted.getSortedList()); } private void assertSortedDeps( List expected, List shuffled, List roots) throws Exception { SortedDependencies sorted = new SortedDependencies(shuffled); assertEquals(expected, sorted.getSortedDependenciesOf(roots)); } private List requires(String ... strings) { return Lists.newArrayList(strings); } private List provides(String ... strings) { return Lists.newArrayList(strings); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/JsFileParserTest.java0000644000175000017500000001421312115204405030014 0ustar apoapo/* * 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.deps; import com.google.common.collect.ImmutableList; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.PrintStreamErrorManager; import junit.framework.TestCase; import java.util.Collections; /** * Tests for {@link JsFileParser}. * * @author agrieve@google.com (Andrew Grieve) */ public class JsFileParserTest extends TestCase { JsFileParser parser; private ErrorManager errorManager; private static final String SRC_PATH = "a"; private static final String CLOSURE_PATH = "b"; @Override public void setUp() { errorManager = new PrintStreamErrorManager(System.err); parser = new JsFileParser(errorManager); parser.setShortcutMode(true); } /** * Tests: * -Parsing of comments, * -Parsing of different styles of quotes, * -Correct recording of what was parsed. */ public void testParseFile() { String contents = "/*" + "goog.provide('no1');*//*\n" + "goog.provide('no2');\n" + "*/goog.provide('yes1');\n" + "/* blah */goog.provide(\"yes2\")/* blah*/\n" + "goog.require('yes3'); // goog.provide('no3');\n" + "// goog.provide('no4');\n" + "goog.require(\"bar.data.SuperstarAddStarThreadActionRequestDelegate\"); " + "//no new line at EOF"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2"), ImmutableList.of("yes3", "bar.data.SuperstarAddStarThreadActionRequestDelegate")); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testMultiplePerLine() { String contents = "goog.provide('yes1');goog.provide('yes2');/*" + "goog.provide('no1');*/goog.provide('yes3');//goog.provide('no2');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2", "yes3"), Collections.emptyList()); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode1() { // For efficiency reasons, we stop reading after the ctor. String contents = " // hi ! \n /* this is a comment */ " + "goog.provide('yes1');\n /* and another comment */ \n" + "goog.provide('yes2'); // include this\n" + "function foo() {}\n" + "goog.provide('no1');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2"), Collections.emptyList()); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode2() { String contents = "/** goog.provide('no1'); \n" + " * goog.provide('no2');\n */\n" + "goog.provide('yes1');\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), Collections.emptyList()); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode3() { String contents = "/**\n" + " * goog.provide('no1');\n */\n" + "goog.provide('yes1');\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), Collections.emptyList()); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog1() { String contents = "/**\n" + " * the first constant in base.js\n" + " */\n" + "var COMPILED = false;\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("goog"), Collections.emptyList()); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog2() { String contents = "goog.require('bar');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of(), ImmutableList.of("goog", "bar")); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog3() { // This guy is pretending to provide goog, but he really doesn't. String contents = "goog.provide('x');\n" + "/**\n" + " * the first constant in base.js\n" + " */\n" + "var COMPILED = false;\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("x"), ImmutableList.of("goog")); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog4() { String contents = "goog.addDependency('foo', [], []);\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of(), ImmutableList.of("goog")); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } /** Asserts the deps match without errors */ private void assertDeps(DependencyInfo expected, DependencyInfo actual) { assertEquals(expected, actual); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/JsFileLineParserTest.java0000644000175000017500000000476012115204405030632 0ustar apoapo/* * 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.deps; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.PrintStreamErrorManager; import junit.framework.TestCase; import java.io.StringReader; /** * Tests for {@link JsFileLineParser}. * * @author nicksantos@google.com (Nick Santos) */ public class JsFileLineParserTest extends TestCase { TestParser parser; private ErrorManager errorManager; @Override public void setUp() { errorManager = new PrintStreamErrorManager(System.err); parser = new TestParser(errorManager); } public void testSingleLine1() { assertStrip("2", "// 1\n2"); } public void testSingleLine2() { assertStrip("2 ", "// 1\n2 // 3 // 4 \n"); } public void testMultiLine1() { assertStrip("1", "/* hi */\n1"); } public void testMultiLine2() { assertStrip("123", "1/* hi */2\n3"); } public void testMultiLine3() { assertStrip("14", "1/* hi 2\n3*/4"); } public void testMultiLine4() { assertStrip("15", "1/* hi x\ny\nz*/5"); } public void testMultiLine5() { assertStrip("1234", "1/* hi */2/**/3/*\n/** bye */4"); } public void testMultiLine6() { assertStrip("12", "1/*** hi *** 3 **/2"); } public void testMixedLine1() { assertStrip("14", "1// /** 2 **/ 3\n4"); } public void testMixedLine2() { assertStrip("1 34", "1/** // 2 **/ 3\n4"); } private void assertStrip(String expected, String input) { parser.doParse("file", new StringReader(input)); assertEquals(expected, parser.toString()); } private static class TestParser extends JsFileLineParser { StringBuilder sb = new StringBuilder(); TestParser(ErrorManager errorManager) { super(errorManager); } @Override boolean parseLine(String line) { sb.append(line); return true; } @Override public String toString() { return sb.toString(); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/deps/DepsFileParserTest.java0000644000175000017500000001025012115204405030330 0ustar apoapo/* * 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.deps; import com.google.common.collect.ImmutableList; import com.google.javascript.jscomp.deps.DependencyInfo; import com.google.javascript.jscomp.deps.DepsFileParser; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.PrintStreamErrorManager; import junit.framework.TestCase; import java.util.Collections; import java.util.List; /** * Tests for {@link DepsFileParser}. * * @author agrieve@google.com (Andrew Grieve) */ public class DepsFileParserTest extends TestCase { private DepsFileParser parser; private ErrorManager errorManager; private static final String SRC_PATH = "/path/1.js"; private final List EMPTY = Collections.emptyList(); @Override public void setUp() { errorManager = new PrintStreamErrorManager(System.err); parser = new DepsFileParser(errorManager); parser.setShortcutMode(true); } /** * Tests: * -Parsing of comments, * -Parsing of different styles of quotes, * -Parsing of empty arrays, * -Parsing of non-empty arrays, * -Correct recording of what was parsed. */ public void testGoodParse() { final String CONTENTS = "/*" + "goog.addDependency('no1', [], []);*//*\n" + "goog.addDependency('no2', [ ], [ ]);\n" + "*/goog.addDependency('yes1', [], []);\n" + "/* blah */goog.addDependency(\"yes2\", [], [])/* blah*/\n" + "goog.addDependency('yes3', ['a','b'], ['c']); // goog.addDependency('no3', [], []);\n" + "// goog.addDependency('no4', [], []);\n" + "goog.addDependency(\"yes4\", [], [ \"a\",'b' , 'c' ]); //no new line at EOF"; List result = parser.parseFile(SRC_PATH, CONTENTS); ImmutableList EXPECTED = ImmutableList.of( new SimpleDependencyInfo("yes1", SRC_PATH, EMPTY, EMPTY), new SimpleDependencyInfo("yes2", SRC_PATH, EMPTY, EMPTY), new SimpleDependencyInfo( "yes3", SRC_PATH, ImmutableList.of("a", "b"), ImmutableList.of("c")), new SimpleDependencyInfo( "yes4", SRC_PATH, EMPTY, ImmutableList.of("a", "b", "c")) ); assertEquals(EXPECTED, result); assertEquals(0, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testTooFewArgs() { parser.parseFile(SRC_PATH, "goog.addDependency('a', []);"); assertEquals(1, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testTooManyArgs() { parser.parseFile(SRC_PATH, "goog.addDependency('a', [], [], []);"); assertEquals(1, errorManager.getErrorCount()); assertEquals(0, errorManager.getWarningCount()); } public void testShortcutMode() { List result = parser.parseFile(SRC_PATH, "goog.addDependency('yes1', [], []); \n" + "foo();\n" + "goog.addDependency('no1', [], []);"); ImmutableList EXPECTED = ImmutableList.of( new SimpleDependencyInfo("yes1", SRC_PATH, EMPTY, EMPTY)); assertEquals(EXPECTED, result); } public void testNoShortcutMode() { parser.setShortcutMode(false); List result = parser.parseFile(SRC_PATH, "goog.addDependency('yes1', [], []); \n" + "foo();\n" + "goog.addDependency('yes2', [], []);"); ImmutableList EXPECTED = ImmutableList.of( new SimpleDependencyInfo("yes1", SRC_PATH, EMPTY, EMPTY), new SimpleDependencyInfo("yes2", SRC_PATH, EMPTY, EMPTY)); assertEquals(EXPECTED, result); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InstrumentFunctionsTest.java0000644000175000017500000002136212115204405030574 0ustar apoapo/* * 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.ImmutableList; import com.google.javascript.rhino.Node; import java.io.StringReader; import java.util.List; /** * Tests for {@link InstrumentFunctions} * */ public class InstrumentFunctionsTest extends CompilerTestCase { private String instrumentationPb; public InstrumentFunctionsTest() { this.instrumentationPb = null; } @Override protected void setUp() { super.enableLineNumberCheck(false); this.instrumentationPb = null; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new NameAndInstrumentFunctions(compiler); } @Override protected int getNumRepetitions() { // This pass is not idempotent. return 1; } public void testInstrument() { final String kPreamble = "var $$toRemoveDefinition1, $$notToRemove;\n" + "var $$toRemoveDefinition2, $$toRemoveDefinition3;\n"; // build instrumentation template and init code strings for use in // tests below. List initCodeList = ImmutableList.of( "var $$Table = [];", "function $$TestDefine(id) {", " $$Table[id] = 0;", "};", "function $$TestInstrument(id) {", " $$Table[id]++;", "};"); StringBuilder initCodeBuilder = new StringBuilder(); StringBuilder pbBuilder = new StringBuilder(); for (String line : initCodeList) { initCodeBuilder.append(line).append("\n"); pbBuilder.append("init: \"").append(line).append("\"\n"); } pbBuilder.append("report_call: \"$$testInstrument\"") .append("report_defined: \"$$testDefine\"") .append("declaration_to_remove: \"$$toRemoveDefinition1\"") .append("declaration_to_remove: \"$$toRemoveDefinition2\"") .append("declaration_to_remove: \"$$toRemoveDefinition3\""); final String initCode = initCodeBuilder.toString(); this.instrumentationPb = pbBuilder.toString(); // Test basic instrumentation test("function a(){b}", initCode + "$$testDefine(0);" + "function a(){$$testInstrument(0);b}"); // Test declaration_to_remove test(kPreamble + "function a(){b}", initCode + "$$testDefine(0);" + "var $$notToRemove;" + "function a(){$$testInstrument(0);b}"); // Test object literal declarations test(kPreamble + "var a = { b: function(){c} }", initCode + "var $$notToRemove;" + "$$testDefine(0);" + "var a = { b: function(){$$testInstrument(0);c} }"); // Test multiple object literal declarations test(kPreamble + "var a = { b: function(){c}, d: function(){e} }", initCode + "var $$notToRemove;" + "$$testDefine(0);" + "$$testDefine(1);" + "var a={b:function(){$$testInstrument(0);c}," + "d:function(){$$testInstrument(1);e}}"); // Test recursive object literal declarations test(kPreamble + "var a = { b: { f: function(){c} }, d: function(){e} }", initCode + "var $$notToRemove;" + "$$testDefine(0);" + "$$testDefine(1);" + "var a={b:{f:function(){$$testInstrument(0);c}}," + "d:function(){$$testInstrument(1);e}}"); } public void testEmpty() { this.instrumentationPb = ""; test("function a(){b}", "function a(){b}"); } public void testAppNameSetter() { this.instrumentationPb = "app_name_setter: \"setAppName\""; test("function a(){b}", "setAppName(\"testfile.js\");function a(){b}"); } public void testInit() { this.instrumentationPb = "init: \"var foo = 0;\"\n" + "init: \"function f(){g();}\"\n"; test("function a(){b}", "var foo = 0;function f(){g()}function a(){b}"); } public void testDeclare() { this.instrumentationPb = "report_defined: \"$$testDefine\""; test("function a(){b}", "$$testDefine(0);function a(){b}"); } public void testCall() { this.instrumentationPb = "report_call: \"$$testCall\""; test("function a(){b}", "function a(){$$testCall(0);b}"); } public void testNested() { this.instrumentationPb = "report_call: \"$$testCall\"\n" + "report_defined: \"$$testDefine\""; test("function a(){ function b(){}}", "$$testDefine(1);$$testDefine(0);" + "function a(){$$testCall(1);function b(){$$testCall(0)}}"); } public void testExitPaths() { this.instrumentationPb = "report_exit: \"$$testExit\""; test("function a(){return}", "function a(){return $$testExit(0)}"); test("function b(){return 5}", "function b(){return $$testExit(0, 5)}"); test("function a(){if(2 != 3){return}else{return 5}}", "function a(){if(2!=3){return $$testExit(0)}" + "else{return $$testExit(0,5)}}"); test("function a(){if(2 != 3){return}else{return 5}}b()", "function a(){if(2!=3){return $$testExit(0)}" + "else{return $$testExit(0,5)}}b()"); test("function a(){if(2 != 3){return}else{return 5}}", "function a(){if(2!=3){return $$testExit(0)}" + "else{return $$testExit(0,5)}}"); } public void testExitNoReturn() { this.instrumentationPb = "report_exit: \"$$testExit\""; test("function a(){}", "function a(){$$testExit(0);}"); test("function a(){b()}", "function a(){b();$$testExit(0);}"); } public void testPartialExitPaths() { this.instrumentationPb = "report_exit: \"$$testExit\""; test("function a(){if (2 != 3) {return}}", "function a(){if (2 != 3){return $$testExit(0)}$$testExit(0)}"); } public void testExitTry() { this.instrumentationPb = "report_exit: \"$$testExit\""; test("function a(){try{return}catch(err){}}", "function a(){try{return $$testExit(0)}catch(err){}$$testExit(0)}"); test("function a(){try{}catch(err){return}}", "function a(){try{}catch(err){return $$testExit(0)}$$testExit(0)}"); test("function a(){try{return}finally{}}", "function a(){try{return $$testExit(0)}finally{}$$testExit(0)}"); test("function a(){try{return}catch(err){}finally{}}", "function a(){try{return $$testExit(0)}catch(err){}finally{}" + "$$testExit(0)}"); test("function a(){try{return 1}catch(err){return 2}}", "function a(){try{return $$testExit(0, 1)}" + "catch(err){return $$testExit(0,2)}}"); test("function a(){try{return 1}catch(err){return 2}finally{}}", "function a(){try{return $$testExit(0, 1)}" + "catch(err){return $$testExit(0,2)}" + "finally{}$$testExit(0)}"); test("function a(){try{return 1}catch(err){return 2}finally{return}}", "function a(){try{return $$testExit(0, 1)}" + "catch(err){return $$testExit(0,2)}finally{return $$testExit(0)}}"); test("function a(){try{}catch(err){}finally{return}}", "function a(){try{}catch(err){}finally{return $$testExit(0)}}"); } public void testNestedExit() { this.instrumentationPb = "report_exit: \"$$testExit\"\n" + "report_defined: \"$$testDefine\""; test("function a(){ return function(){ return c;}}", "$$testDefine(1);function a(){$$testDefine(0);" + "return $$testExit(1, function(){return $$testExit(0, c);});}"); } public void testProtobuffParseFail() { this.instrumentationPb = "not an ascii pb\n"; test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR); } public void testInitJsParseFail() { this.instrumentationPb = "init: \"= assignWithNoLhs();\""; test("function a(){b}", "", RhinoErrorReporter.PARSE_ERROR); } private class NameAndInstrumentFunctions implements CompilerPass { private final Compiler compiler; NameAndInstrumentFunctions(Compiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { FunctionNames functionNames = new FunctionNames(compiler); functionNames.process(externs, root); InstrumentFunctions instrumentation = new InstrumentFunctions(compiler, functionNames, "test init code", "testfile.js", new StringReader(instrumentationPb)); instrumentation.process(externs, root); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeRemoveDeadCodeTest.java0000644000175000017500000005015612115204405031026 0ustar apoapo/* * Copyright 2004 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.rhino.Node; /** * Tests for PeepholeRemoveDeadCodeTest in isolation. Tests for the interaction * of multiple peephole passes are in PeepholeIntegrationTest. */ public class PeepholeRemoveDeadCodeTest extends CompilerTestCase { private static final String MATH = "/** @const */ var Math = {};" + "/** @nosideeffects */ Math.random = function(){};" + "/** @nosideeffects */ Math.sin = function(){};"; public PeepholeRemoveDeadCodeTest() { super(MATH); } @Override public void setUp() throws Exception { super.setUp(); enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { SimpleDefinitionFinder definitionFinder = new SimpleDefinitionFinder(compiler); definitionFinder.process(externs, root); new PureFunctionIdentifier(compiler, definitionFinder) .process(externs, root); PeepholeOptimizationsPass peepholePass = new PeepholeOptimizationsPass( compiler, new PeepholeRemoveDeadCode()); peepholePass.process(externs, root); } }; } @Override protected int getNumRepetitions() { // Reduce this to 2 if we get better expression evaluators. return 2; } private void foldSame(String js) { testSame(js); } private void fold(String js, String expected) { test(js, expected); } public void testFoldBlock() { fold("{{foo()}}", "foo()"); fold("{foo();{}}", "foo()"); fold("{{foo()}{}}", "foo()"); fold("{{foo()}{bar()}}", "foo();bar()"); fold("{if(false)foo(); {bar()}}", "bar()"); fold("{if(false)if(false)if(false)foo(); {bar()}}", "bar()"); fold("{'hi'}", ""); fold("{x==3}", ""); fold("{ (function(){x++}) }", ""); fold("function f(){return;}", "function f(){return;}"); fold("function f(){return 3;}", "function f(){return 3}"); fold("function f(){if(x)return; x=3; return; }", "function f(){if(x)return; x=3; return; }"); fold("{x=3;;;y=2;;;}", "x=3;y=2"); // Cases to test for empty block. fold("while(x()){x}", "while(x());"); fold("while(x()){x()}", "while(x())x()"); fold("for(x=0;x<100;x++){x}", "for(x=0;x<100;x++);"); fold("for(x in y){x}", "for(x in y);"); } /** Try to remove spurious blocks with multiple children */ public void testFoldBlocksWithManyChildren() { fold("function f() { if (false) {} }", "function f(){}"); fold("function f() { { if (false) {} if (true) {} {} } }", "function f(){}"); fold("{var x; var y; var z; function f() { { var a; { var b; } } } }", "var x;var y;var z;function f(){var a;var b}"); } public void testIf() { fold("if (1){ x=1; } else { x = 2;}", "x=1"); fold("if (false){ x = 1; } else { x = 2; }", "x=2"); fold("if (undefined){ x = 1; } else { x = 2; }", "x=2"); fold("if (null){ x = 1; } else { x = 2; }", "x=2"); fold("if (void 0){ x = 1; } else { x = 2; }", "x=2"); fold("if (void foo()){ x = 1; } else { x = 2; }", "foo();x=2"); fold("if (false){ x = 1; } else if (true) { x = 3; } else { x = 2; }", "x=3"); fold("if (x){ x = 1; } else if (false) { x = 3; }", "if(x)x=1"); } public void testHook() { fold("true ? a() : b()", "a()"); fold("false ? a() : b()", "b()"); fold("a() ? b() : true", "a() && b()"); fold("a() ? true : b()", "a() || b()"); fold("(a = true) ? b() : c()", "a = true, b()"); fold("(a = false) ? b() : c()", "a = false, c()"); fold("do {f()} while((a = true) ? b() : c())", "do {f()} while((a = true) , b())"); fold("do {f()} while((a = false) ? b() : c())", "do {f()} while((a = false) , c())"); fold("var x = (true) ? 1 : 0", "var x=1"); fold("var y = (true) ? ((false) ? 12 : (cond ? 1 : 2)) : 13", "var y=cond?1:2"); foldSame("var z=x?void 0:y()"); foldSame("z=x?void 0:y()"); foldSame("z*=x?void 0:y()"); foldSame("var z=x?y():void 0"); foldSame("(w?x:void 0).y=z"); foldSame("(w?x:void 0).y+=z"); fold("y = (x ? void 0 : void 0)", "y = void 0"); fold("y = (x ? f() : f())", "y = f()"); } public void testConstantConditionWithSideEffect1() { fold("if (b=true) x=1;", "b=true;x=1"); fold("if (b=/ab/) x=1;", "b=/ab/;x=1"); fold("if (b=/ab/){ x=1; } else { x=2; }", "b=/ab/;x=1"); fold("var b;b=/ab/;if(b)x=1;", "var b;b=/ab/;x=1"); foldSame("var b;b=f();if(b)x=1;"); fold("var b=/ab/;if(b)x=1;", "var b=/ab/;x=1"); foldSame("var b=f();if(b)x=1;"); foldSame("b=b++;if(b)x=b;"); fold("(b=0,b=1);if(b)x=b;", "b=0,b=1;if(b)x=b;"); fold("b=1;if(foo,b)x=b;","b=1;x=b;"); foldSame("b=1;if(foo=1,b)x=b;"); } public void testConstantConditionWithSideEffect2() { fold("(b=true)?x=1:x=2;", "b=true,x=1"); fold("(b=false)?x=1:x=2;", "b=false,x=2"); fold("if (b=/ab/) x=1;", "b=/ab/;x=1"); fold("var b;b=/ab/;(b)?x=1:x=2;", "var b;b=/ab/;x=1"); foldSame("var b;b=f();(b)?x=1:x=2;"); fold("var b=/ab/;(b)?x=1:x=2;", "var b=/ab/;x=1"); foldSame("var b=f();(b)?x=1:x=2;"); } public void testVarLifting() { fold("if(true)var a", "var a"); fold("if(false)var a", "var a"); // More var lifting tests in PeepholeIntegrationTests } public void testFoldUselessWhile() { fold("while(false) { foo() }", ""); fold("while(void 0) { foo() }", ""); fold("while(undefined) { foo() }", ""); foldSame("while(true) foo()"); fold("while(false) { var a = 0; }", "var a"); // Make sure it plays nice with minimizing fold("while(false) { foo(); continue }", ""); fold("while(0) { foo() }", ""); } public void testFoldUselessFor() { fold("for(;false;) { foo() }", ""); fold("for(;void 0;) { foo() }", ""); fold("for(;undefined;) { foo() }", ""); fold("for(;true;) foo() ", "for(;;) foo() "); foldSame("for(;;) foo()"); fold("for(;false;) { var a = 0; }", "var a"); // Make sure it plays nice with minimizing fold("for(;false;) { foo(); continue }", ""); } public void testFoldUselessDo() { fold("do { foo() } while(false);", "foo()"); fold("do { foo() } while(void 0);", "foo()"); fold("do { foo() } while(undefined);", "foo()"); fold("do { foo() } while(true);", "do { foo() } while(true);"); fold("do { var a = 0; } while(false);", "var a=0"); fold("do { var a = 0; } while(!{a:foo()});", "var a=0;foo()"); // Can't fold with break or continues. foldSame("do { foo(); continue; } while(0)"); foldSame("do { foo(); break; } while(0)"); } public void testMinimizeWhileConstantCondition() { fold("while(true) foo()", "while(true) foo()"); fold("while(0) foo()", ""); fold("while(0.0) foo()", ""); fold("while(NaN) foo()", ""); fold("while(null) foo()", ""); fold("while(undefined) foo()", ""); fold("while('') foo()", ""); } public void testFoldConstantCommaExpressions() { fold("if (true, false) {foo()}", ""); fold("if (false, true) {foo()}", "foo()"); fold("true, foo()", "foo()"); fold("(1 + 2 + ''), foo()", "foo()"); } public void testRemoveUselessOps() { // There are four place where expression results are discarded: // - a top-level expression EXPR_RESULT // - the LHS of a COMMA // - the FOR init expression // - the FOR increment expression // Known side-effect free functions calls are removed. fold("Math.random()", ""); fold("Math.random(f() + g())", "f(),g();"); fold("Math.random(f(),g(),h())", "f(),g(),h();"); // Calls to functions with unknown side-effects are are left. foldSame("f();"); foldSame("(function () { f(); })();"); // We know that this function has no side effects because of the // PureFunctionIdentifier. fold("(function () {})();", ""); // Uncalled function expressions are removed fold("(function () {});", ""); fold("(function f() {});", ""); // ... including any code they contain. fold("(function () {foo();});", ""); // Useless operators are removed. fold("+f()", "f()"); fold("a=(+f(),g())", "a=(f(),g())"); fold("a=(true,g())", "a=g()"); fold("f(),true", "f()"); fold("f() + g()", "f(),g()"); fold("for(;;+f()){}", "for(;;f()){}"); fold("for(+f();;g()){}", "for(f();;g()){}"); fold("for(;;Math.random(f(),g(),h())){}", "for(;;f(),g(),h()){}"); // The optimization cascades into conditional expressions: fold("g() && +f()", "g() && f()"); fold("g() || +f()", "g() || f()"); fold("x ? g() : +f()", "x ? g() : f()"); fold("+x()", "x()"); fold("+x() * 2", "x()"); fold("-(+x() * 2)", "x()"); fold("2 -(+x() * 2)", "x()"); fold("x().foo", "x()"); foldSame("x().foo()"); foldSame("x++"); foldSame("++x"); foldSame("x--"); foldSame("--x"); foldSame("x = 2"); foldSame("x *= 2"); // Sanity check, other expression are left alone. foldSame("function f() {}"); foldSame("var x;"); } public void testOptimizeSwitch() { fold("switch(a){}", ""); fold("switch(foo()){}", "foo()"); fold("switch(a){default:}", ""); fold("switch(a){default:break;}", ""); fold("switch(a){default:var b;break;}", "var b"); fold("switch(a){case 1: default:}", ""); fold("switch(a){default: case 1:}", ""); fold("switch(a){default: break; case 1:break;}", ""); fold("switch(a){default: var b; break; case 1: var c; break;}", "var c; var b;"); // Can't remove cases if a default exists. foldSame("function f() {switch(a){default: return; case 1: break;}}"); foldSame("function f() {switch(a){case 1: foo();}}"); foldSame("function f() {switch(a){case 3: case 2: case 1: foo();}}"); fold("function f() {switch(a){case 2: case 1: default: foo();}}", "function f() {switch(a){default: foo();}}"); fold("switch(a){case 1: default:break; case 2: foo()}", "switch(a){case 2: foo()}"); foldSame("switch(a){case 1: goo(); default:break; case 2: foo()}"); // TODO(johnlenz): merge the useless "case 2" foldSame("switch(a){case 1: goo(); case 2:break; case 3: foo()}"); // Can't remove unused code with a "var" in it. fold("switch(1){case 2: var x=0;}", "var x;"); fold("switch ('repeated') {\n" + "case 'repeated':\n" + " foo();\n" + " break;\n" + "case 'repeated':\n" + " var x=0;\n" + " break;\n" + "}", "var x; {foo();}"); // Can't remove cases if something useful is done. foldSame("switch(a){case 1: var c =2; break;}"); foldSame("function f() {switch(a){case 1: return;}}"); foldSame("x:switch(a){case 1: break x;}"); fold("switch ('foo') {\n" + "case 'foo':\n" + " foo();\n" + " break;\n" + "case 'bar':\n" + " bar();\n" + " break;\n" + "}", "{foo();}"); fold("switch ('noMatch') {\n" + "case 'foo':\n" + " foo();\n" + " break;\n" + "case 'bar':\n" + " bar();\n" + " break;\n" + "}", ""); foldSame("switch ('fallThru') {\n" + "case 'fallThru':\n" + " if (foo(123) > 0) {\n" + " foobar(1);\n" + " break;\n" + " }\n" + " foobar(2);\n" + "case 'bar':\n" + " bar();\n" + "}"); foldSame("switch ('fallThru') {\n" + "case 'fallThru':\n" + " foo();\n" + "case 'bar':\n" + " bar();\n" + "}"); foldSame("switch ('hasDefaultCase') {\n" + "case 'foo':\n" + " foo();\n" + " break;\n" + "default:\n" + " bar();\n" + " break;\n" + "}"); fold("switch ('repeated') {\n" + "case 'repeated':\n" + " foo();\n" + " break;\n" + "case 'repeated':\n" + " bar();\n" + " break;\n" + "}", "{foo();}"); fold("switch ('foo') {\n" + "case 'bar':\n" + " bar();\n" + " break;\n" + "case notConstant:\n" + " foobar();\n" + " break;\n" + "case 'foo':\n" + " foo();\n" + " break;\n" + "}", "switch ('foo') {\n" + "case notConstant:\n" + " foobar();\n" + " break;\n" + "case 'foo':\n" + " foo();\n" + " break;\n" + "}"); fold("switch (1) {\n" + "case 1:\n" + " foo();\n" + " break;\n" + "case 2:\n" + " bar();\n" + " break;\n" + "}", "{foo();}"); fold("switch (1) {\n" + "case 1.1:\n" + " foo();\n" + " break;\n" + "case 2:\n" + " bar();\n" + " break;\n" + "}", ""); foldSame("switch (0) {\n" + "case NaN:\n" + " foobar();\n" + " break;\n" + "case -0.0:\n" + " foo();\n" + " break;\n" + "case 2:\n" + " bar();\n" + " break;\n" + "}"); foldSame("switch ('\\v') {\n" + "case '\\u000B':\n" + " foo();\n" + "}"); foldSame("switch ('empty') {\n" + "case 'empty':\n" + "case 'foo':\n" + " foo();\n" + "}"); } public void testRemoveNumber() { test("3", ""); } public void testRemoveVarGet1() { test("a", ""); } public void testRemoveVarGet2() { test("var a = 1;a", "var a = 1"); } public void testRemoveNamespaceGet1() { test("var a = {};a.b", "var a = {}"); } public void testRemoveNamespaceGet2() { test("var a = {};a.b=1;a.b", "var a = {};a.b=1"); } public void testRemovePrototypeGet1() { test("var a = {};a.prototype.b", "var a = {}"); } public void testRemovePrototypeGet2() { test("var a = {};a.prototype.b = 1;a.prototype.b", "var a = {};a.prototype.b = 1"); } public void testRemoveAdd1() { test("1 + 2", ""); } public void testNoRemoveVar1() { testSame("var a = 1"); } public void testNoRemoveVar2() { testSame("var a = 1, b = 2"); } public void testNoRemoveAssign1() { testSame("a = 1"); } public void testNoRemoveAssign2() { testSame("a = b = 1"); } public void testNoRemoveAssign3() { test("1 + (a = 2)", "a = 2"); } public void testNoRemoveAssign4() { testSame("x.a = 1"); } public void testNoRemoveAssign5() { testSame("x.a = x.b = 1"); } public void testNoRemoveAssign6() { test("1 + (x.a = 2)", "x.a = 2"); } public void testNoRemoveCall1() { testSame("a()"); } public void testNoRemoveCall2() { test("a()+b()", "a(),b()"); } public void testNoRemoveCall3() { testSame("a() && b()"); } public void testNoRemoveCall4() { testSame("a() || b()"); } public void testNoRemoveCall5() { test("a() || 1", "a()"); } public void testNoRemoveCall6() { testSame("1 || a()"); } public void testNoRemoveThrow1() { testSame("function f(){throw a()}"); } public void testNoRemoveThrow2() { testSame("function f(){throw a}"); } public void testNoRemoveThrow3() { testSame("function f(){throw 10}"); } public void testRemoveInControlStructure1() { test("if(x()) 1", "x()"); } public void testRemoveInControlStructure2() { test("while(2) 1", "while(2);"); } public void testRemoveInControlStructure3() { test("for(1;2;3) 4", "for(;;);"); } public void testHook1() { test("1 ? 2 : 3", ""); } public void testHook2() { test("x ? a() : 3", "x && a()"); } public void testHook3() { test("x ? 2 : a()", "x || a()"); } public void testHook4() { testSame("x ? a() : b()"); } public void testHook5() { test("a() ? 1 : 2", "a()"); } public void testHook6() { test("a() ? b() : 2", "a() && b()"); } // TODO(johnlenz): Consider adding a post optimization pass to // convert OR into HOOK to save parentheses when the operator // precedents would require them. public void testHook7() { test("a() ? 1 : b()", "a() || b()"); } public void testHook8() { testSame("a() ? b() : c()"); } public void testShortCircuit1() { testSame("1 && a()"); } public void testShortCircuit2() { test("1 && a() && 2", "1 && a()"); } public void testShortCircuit3() { test("a() && 1 && 2", "a()"); } public void testShortCircuit4() { testSame("a() && 1 && b()"); } public void testComplex1() { test("1 && a() + b() + c()", "1 && (a(), b(), c())"); } public void testComplex2() { test("1 && (a() ? b() : 1)", "1 && a() && b()"); } public void testComplex3() { test("1 && (a() ? b() : 1 + c())", "1 && (a() ? b() : c())"); } public void testComplex4() { test("1 && (a() ? 1 : 1 + c())", "1 && (a() || c())"); } public void testComplex5() { // can't simplify LHS of short circuit statements with side effects testSame("(a() ? 1 : 1 + c()) && foo()"); } public void testNoRemoveFunctionDeclaration1() { testSame("function foo(){}"); } public void testNoRemoveFunctionDeclaration2() { testSame("var foo = function (){}"); } public void testNoSimplifyFunctionArgs1() { testSame("f(1 + 2, 3 + g())"); } public void testNoSimplifyFunctionArgs2() { testSame("1 && f(1 + 2, 3 + g())"); } public void testNoSimplifyFunctionArgs3() { testSame("1 && foo(a() ? b() : 1 + c())"); } public void testNoRemoveInherits1() { testSame("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a)"); } public void testNoRemoveInherits2() { test("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a) + 1", "var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a)"); } public void testNoRemoveInherits3() { testSame("this.a = {}; var b = {}; b.inherits(a);"); } public void testNoRemoveInherits4() { test("this.a = {}; var b = {}; b.inherits(a) + 1;", "this.a = {}; var b = {}; b.inherits(a)"); } public void testRemoveFromLabel1() { test("LBL: void 0", "LBL: {}"); } public void testRemoveFromLabel2() { test("LBL: foo() + 1 + bar()", "LBL: foo(),bar()"); } public void testCall1() { test("Math.sin(0);", ""); } public void testCall2() { test("1 + Math.sin(0);", ""); } public void testNew1() { test("new Date;", ""); } public void testNew2() { test("1 + new Date;", ""); } public void testFoldAssign() { test("x=x", ""); testSame("x=xy"); testSame("x=x + 1"); testSame("x.a=x.a"); test("var y=(x=x)", "var y=x"); test("y=1 + (x=x)", "y=1 + x"); } public void testTryCatchFinally() { testSame("try {foo()} catch (e) {bar()}"); testSame("try { try {foo()} catch (e) {bar()}} catch (x) {bar()}"); test("try {var x = 1} finally {}", "var x = 1;"); testSame("try {var x = 1} finally {x()}"); test("function f() { return; try{var x = 1}finally{} }", "function f() { return; var x = 1; }"); test("try {} finally {x()}", "x()"); test("try {} catch (e) { bar()} finally {x()}", "x()"); test("try {} catch (e) { bar()}", ""); test("try {} catch (e) { var a = 0; } finally {x()}", "var a; x()"); test("try {} catch (e) {}", ""); test("try {} finally {}", ""); test("try {} catch (e) {} finally {}", ""); } public void testObjectLiteral() { test("({})", ""); test("({a:1})", ""); test("({a:foo()})", "foo()"); test("({'a':foo()})", "foo()"); } public void testArrayLiteral() { test("([])", ""); test("([1])", ""); test("([a])", ""); test("([foo()])", "foo()"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ChainCallsTest.java0000644000175000017500000000655712115204405026545 0ustar apoapo/* * 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; /** * Tests for {@link ChainCalls} * */ public class ChainCallsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new ChainCalls(compiler); } @Override protected int getNumRepetitions() { return 1; } public void testUnchainedCalls() { test( "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "var f = new Foo();\n" + "f.bar();\n" + "f.bar();\n", "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "var f = new Foo();\n" + "f.bar().bar();\n"); } public void testSecondCallReturnNotThis() { test( "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "Foo.prototype.baz = function() {};\n" + "var f = new Foo();\n" + "f.bar();\n" + "f.baz();\n", "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "Foo.prototype.baz = function() {};\n" + "var f = new Foo();\n" + "f.bar().baz();\n"); } public void testDifferentInstance() { testSame( "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "new Foo().bar();\n" + "new Foo().bar();\n"); } public void testSubclass() { testSame( "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.bar = function() { return this; };\n" + "/** @constructor\n@extends {Foo} */ function Baz() {}\n" + "Baz.prototype.bar = function() {};\n" + "(/** @type {Foo} */ new Baz()).bar();\n" + "(/** @type {Foo} */ new Baz()).bar();\n"); } public void testSimpleDefinitionFinder() { String defs = "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = function() { return this; };" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = function() {};"; testSame( defs + "var o = new Foo; o.a(); o.a();"); testSame( defs + "var o = new Bar; o.a(); o.a();"); } public void testSimpleDefinitionFinder2() { String defs = "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = function() { return this; };" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = function() { return this; };"; testSame( defs + "var o = new Foo; o.a().a();"); testSame( defs + "var o = new Bar; o.a().a();"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/XtbMessageBundleTest.java0000644000175000017500000000521112115204405027722 0ustar apoapo/* * Copyright 2006 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.base.Charsets.UTF_8; import junit.framework.TestCase; import java.io.ByteArrayInputStream; import java.io.InputStream; /** * Tests {@link XtbMessageBundle}. * */ public class XtbMessageBundleTest extends TestCase { private final static String PROJECT_ID = "TestProject"; private static final String XTB = "\n" + "\n" + "\n" + "descargar\n" + "Se han\nignorado" + " conversaciones.\n" + "Si," + " puede hacer" + " clic" + " para utilizar.Esperamos" + " poder ampliar.\n" + "\n" + ""; public void test() { InputStream stream = new ByteArrayInputStream(XTB.getBytes(UTF_8)); XtbMessageBundle bundle = new XtbMessageBundle( stream, PROJECT_ID); JsMessage message = bundle.getMessage("7639678437384034548"); assertEquals("descargar", message.toString()); message = bundle.getMessage("2398375912250604550"); assertEquals("Se han\nignorado {$num} conversaciones.", message.toString()); message = bundle.getMessage("6323937743550839320"); assertEquals("{$pStart}Si, puede {$linkStart_1_3}hacer " + "clic{$linkEnd_1_3} para utilizar.{$pEnd}{$pStart}Esperamos " + "poder ampliar.{$pEnd}", message.toString()); message = bundle.getMessage("3945720239421293834"); assertEquals("", message.toString()); assertFalse(message.parts().isEmpty()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CodePrinterTest.java0000644000175000017500000014625212115204405026757 0ustar apoapo/* * Copyright 2004 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.base.Joiner; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.List; public class CodePrinterTest extends TestCase { private boolean trustedStrings = true; private Compiler lastCompiler = null; @Override public void setUp() { trustedStrings = true; lastCompiler = null; } Node parse(String js) { return parse(js, false); } Node parse(String js, boolean checkTypes) { Compiler compiler = new Compiler(); lastCompiler = compiler; CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); // Allow getters and setters. options.setLanguageIn(LanguageMode.ECMASCRIPT5); compiler.initOptions(options); Node n = compiler.parseTestCode(js); if (checkTypes) { DefaultPassConfig passConfig = new DefaultPassConfig(null); CompilerPass typeResolver = passConfig.resolveTypes.create(compiler); Node externs = new Node(Token.SCRIPT); externs.setInputId(new InputId("externs")); Node externAndJsRoot = new Node(Token.BLOCK, externs, n); externAndJsRoot.setIsSyntheticBlock(true); typeResolver.process(externs, n); CompilerPass inferTypes = passConfig.inferTypes.create(compiler); inferTypes.process(externs, n); } checkUnexpectedErrorsOrWarnings(compiler, 0); return n; } private static void checkUnexpectedErrorsOrWarnings( Compiler compiler, int expected) { int actual = compiler.getErrors().length + compiler.getWarnings().length; if (actual != expected) { String msg = ""; for (JSError err : compiler.getErrors()) { msg += "Error:" + err.toString() + "\n"; } for (JSError err : compiler.getWarnings()) { msg += "Warning:" + err.toString() + "\n"; } assertEquals("Unexpected warnings or errors.\n " + msg, expected, actual); } } String parsePrint(String js, boolean prettyprint, int lineThreshold) { CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); options.setPrettyPrint(prettyprint); options.setLineLengthThreshold(lineThreshold); return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) .build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold) { CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); options.setPrettyPrint(prettyprint); options.setLineLengthThreshold(lineThreshold); options.setLineBreak(lineBreak); return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) .build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, boolean preferLineBreakAtEof, int lineThreshold) { CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); options.setPrettyPrint(prettyprint); options.setLineLengthThreshold(lineThreshold); options.setPreferLineBreakAtEndOfFile(preferLineBreakAtEof); options.setLineBreak(lineBreak); return new CodePrinter.Builder(parse(js)).setCompilerOptions(options) .build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold, boolean outputTypes) { Node node = parse(js, true); CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); options.setPrettyPrint(prettyprint); options.setLineLengthThreshold(lineThreshold); options.setLineBreak(lineBreak); return new CodePrinter.Builder(node).setCompilerOptions(options) .setOutputTypes(outputTypes) .setTypeRegistry(lastCompiler.getTypeRegistry()) .build(); } String parsePrint(String js, boolean prettyprint, boolean lineBreak, int lineThreshold, boolean outputTypes, boolean tagAsStrict) { Node node = parse(js, true); CompilerOptions options = new CompilerOptions(); options.setTrustedStrings(trustedStrings); options.setPrettyPrint(prettyprint); options.setLineLengthThreshold(lineThreshold); options.setLineBreak(lineBreak); return new CodePrinter.Builder(node).setCompilerOptions(options) .setOutputTypes(outputTypes) .setTypeRegistry(lastCompiler.getTypeRegistry()) .setTagAsStrict(tagAsStrict) .build(); } String printNode(Node n) { CompilerOptions options = new CompilerOptions(); options.setLineLengthThreshold(CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD); return new CodePrinter.Builder(n).setCompilerOptions(options).build(); } void assertPrintNode(String expectedJs, Node ast) { assertEquals(expectedJs, printNode(ast)); } public void testPrint() { assertPrint("10 + a + b", "10+a+b"); assertPrint("10 + (30*50)", "10+30*50"); assertPrint("with(x) { x + 3; }", "with(x)x+3"); assertPrint("\"aa'a\"", "\"aa'a\""); assertPrint("\"aa\\\"a\"", "'aa\"a'"); assertPrint("function foo()\n{return 10;}", "function foo(){return 10}"); assertPrint("a instanceof b", "a instanceof b"); assertPrint("typeof(a)", "typeof a"); assertPrint( "var foo = x ? { a : 1 } : {a: 3, b:4, \"default\": 5, \"foo-bar\": 6}", "var foo=x?{a:1}:{a:3,b:4,\"default\":5,\"foo-bar\":6}"); // Safari: needs ';' at the end of a throw statement assertPrint("function foo(){throw 'error';}", "function foo(){throw\"error\";}"); // Safari 3 needs a "{" around a single function assertPrint("if (true) function foo(){return}", "if(true){function foo(){return}}"); assertPrint("var x = 10; { var y = 20; }", "var x=10;var y=20"); assertPrint("while (x-- > 0);", "while(x-- >0);"); assertPrint("x-- >> 1", "x-- >>1"); assertPrint("(function () {})(); ", "(function(){})()"); // Associativity assertPrint("var a,b,c,d;a || (b&& c) && (a || d)", "var a,b,c,d;a||b&&c&&(a||d)"); assertPrint("var a,b,c; a || (b || c); a * (b * c); a | (b | c)", "var a,b,c;a||b||c;a*b*c;a|b|c"); assertPrint("var a,b,c; a / b / c;a / (b / c); a - (b - c);", "var a,b,c;a/b/c;a/(b/c);a-(b-c)"); assertPrint("var a,b; a = b = 3;", "var a,b;a=b=3"); assertPrint("var a,b,c,d; a = (b = c = (d = 3));", "var a,b,c,d;a=b=c=d=3"); assertPrint("var a,b,c; a += (b = c += 3);", "var a,b,c;a+=b=c+=3"); assertPrint("var a,b,c; a *= (b -= c);", "var a,b,c;a*=b-=c"); // Precedence assertPrint("a ? delete b[0] : 3", "a?delete b[0]:3"); assertPrint("(delete a[0])/10", "delete a[0]/10"); // optional '()' for new // simple new assertPrint("new A", "new A"); assertPrint("new A()", "new A"); assertPrint("new A('x')", "new A(\"x\")"); // calling instance method directly after new assertPrint("new A().a()", "(new A).a()"); assertPrint("(new A).a()", "(new A).a()"); // this case should be fixed assertPrint("new A('y').a()", "(new A(\"y\")).a()"); // internal class assertPrint("new A.B", "new A.B"); assertPrint("new A.B()", "new A.B"); assertPrint("new A.B('z')", "new A.B(\"z\")"); // calling instance method directly after new internal class assertPrint("(new A.B).a()", "(new A.B).a()"); assertPrint("new A.B().a()", "(new A.B).a()"); // this case should be fixed assertPrint("new A.B('w').a()", "(new A.B(\"w\")).a()"); // Operators: make sure we don't convert binary + and unary + into ++ assertPrint("x + +y", "x+ +y"); assertPrint("x - (-y)", "x- -y"); assertPrint("x++ +y", "x++ +y"); assertPrint("x-- -y", "x-- -y"); assertPrint("x++ -y", "x++-y"); // Label assertPrint("foo:for(;;){break foo;}", "foo:for(;;)break foo"); assertPrint("foo:while(1){continue foo;}", "foo:while(1)continue foo"); // Object literals. assertPrint("({})", "({})"); assertPrint("var x = {};", "var x={}"); assertPrint("({}).x", "({}).x"); assertPrint("({})['x']", "({})[\"x\"]"); assertPrint("({}) instanceof Object", "({})instanceof Object"); assertPrint("({}) || 1", "({})||1"); assertPrint("1 || ({})", "1||{}"); assertPrint("({}) ? 1 : 2", "({})?1:2"); assertPrint("0 ? ({}) : 2", "0?{}:2"); assertPrint("0 ? 1 : ({})", "0?1:{}"); assertPrint("typeof ({})", "typeof{}"); assertPrint("f({})", "f({})"); // Anonymous function expressions. assertPrint("(function(){})", "(function(){})"); assertPrint("(function(){})()", "(function(){})()"); assertPrint("(function(){})instanceof Object", "(function(){})instanceof Object"); assertPrint("(function(){}).bind().call()", "(function(){}).bind().call()"); assertPrint("var x = function() { };", "var x=function(){}"); assertPrint("var x = function() { }();", "var x=function(){}()"); assertPrint("(function() {}), 2", "(function(){}),2"); // Name functions expression. assertPrint("(function f(){})", "(function f(){})"); // Function declaration. assertPrint("function f(){}", "function f(){}"); // Make sure we don't treat non-Latin character escapes as raw strings. assertPrint("({ 'a': 4, '\\u0100': 4 })", "({\"a\":4,\"\\u0100\":4})"); assertPrint("({ a: 4, '\\u0100': 4 })", "({a:4,\"\\u0100\":4})"); // Test if statement and for statements with single statements in body. assertPrint("if (true) { alert();}", "if(true)alert()"); assertPrint("if (false) {} else {alert(\"a\");}", "if(false);else alert(\"a\")"); assertPrint("for(;;) { alert();};", "for(;;)alert()"); assertPrint("do { alert(); } while(true);", "do alert();while(true)"); assertPrint("myLabel: { alert();}", "myLabel:alert()"); assertPrint("myLabel: for(;;) continue myLabel;", "myLabel:for(;;)continue myLabel"); // Test nested var statement assertPrint("if (true) var x; x = 4;", "if(true)var x;x=4"); // Non-latin identifier. Make sure we keep them escaped. assertPrint("\\u00fb", "\\u00fb"); assertPrint("\\u00fa=1", "\\u00fa=1"); assertPrint("function \\u00f9(){}", "function \\u00f9(){}"); assertPrint("x.\\u00f8", "x.\\u00f8"); assertPrint("x.\\u00f8", "x.\\u00f8"); assertPrint("abc\\u4e00\\u4e01jkl", "abc\\u4e00\\u4e01jkl"); // Test the right-associative unary operators for spurious parens assertPrint("! ! true", "!!true"); assertPrint("!(!(true))", "!!true"); assertPrint("typeof(void(0))", "typeof void 0"); assertPrint("typeof(void(!0))", "typeof void!0"); assertPrint("+ - + + - + 3", "+-+ +-+3"); // chained unary plus/minus assertPrint("+(--x)", "+--x"); assertPrint("-(++x)", "-++x"); // needs a space to prevent an ambiguous parse assertPrint("-(--x)", "- --x"); assertPrint("!(~~5)", "!~~5"); assertPrint("~(a/b)", "~(a/b)"); // Preserve parens to overcome greedy binding of NEW assertPrint("new (foo.bar()).factory(baz)", "new (foo.bar().factory)(baz)"); assertPrint("new (bar()).factory(baz)", "new (bar().factory)(baz)"); assertPrint("new (new foobar(x)).factory(baz)", "new (new foobar(x)).factory(baz)"); // Make sure that HOOK is right associative assertPrint("a ? b : (c ? d : e)", "a?b:c?d:e"); assertPrint("a ? (b ? c : d) : e", "a?b?c:d:e"); assertPrint("(a ? b : c) ? d : e", "(a?b:c)?d:e"); // Test nested ifs assertPrint("if (x) if (y); else;", "if(x)if(y);else;"); // Test comma. assertPrint("a,b,c", "a,b,c"); assertPrint("(a,b),c", "a,b,c"); assertPrint("a,(b,c)", "a,b,c"); assertPrint("x=a,b,c", "x=a,b,c"); assertPrint("x=(a,b),c", "x=(a,b),c"); assertPrint("x=a,(b,c)", "x=a,b,c"); assertPrint("x=a,y=b,z=c", "x=a,y=b,z=c"); assertPrint("x=(a,y=b,z=c)", "x=(a,y=b,z=c)"); assertPrint("x=[a,b,c,d]", "x=[a,b,c,d]"); assertPrint("x=[(a,b,c),d]", "x=[(a,b,c),d]"); assertPrint("x=[(a,(b,c)),d]", "x=[(a,b,c),d]"); assertPrint("x=[a,(b,c,d)]", "x=[a,(b,c,d)]"); assertPrint("var x=(a,b)", "var x=(a,b)"); assertPrint("var x=a,b,c", "var x=a,b,c"); assertPrint("var x=(a,b),c", "var x=(a,b),c"); assertPrint("var x=a,b=(c,d)", "var x=a,b=(c,d)"); assertPrint("foo(a,b,c,d)", "foo(a,b,c,d)"); assertPrint("foo((a,b,c),d)", "foo((a,b,c),d)"); assertPrint("foo((a,(b,c)),d)", "foo((a,b,c),d)"); assertPrint("f(a+b,(c,d,(e,f,g)))", "f(a+b,(c,d,e,f,g))"); assertPrint("({}) , 1 , 2", "({}),1,2"); assertPrint("({}) , {} , {}", "({}),{},{}"); // EMPTY nodes assertPrint("if (x){}", "if(x);"); assertPrint("if(x);", "if(x);"); assertPrint("if(x)if(y);", "if(x)if(y);"); assertPrint("if(x){if(y);}", "if(x)if(y);"); assertPrint("if(x){if(y){};;;}", "if(x)if(y);"); assertPrint("if(x){;;function y(){};;}", "if(x){function y(){}}"); } public void testBreakTrustedStrings() { // Break scripts assertPrint("''", "\"\\x3c/script>\""); assertPrint("\" \"", "\"\\x3c/script> \\x3c/SCRIPT>\""); assertPrint("'-->'", "\"--\\x3e\""); assertPrint("']]>'", "\"]]\\x3e\""); assertPrint("' -->'", "\" --\\x3e\\x3c/script>\""); assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g"); // Break HTML start comments. Certain versions of WebKit // begin an HTML comment when they see this. assertPrint("''", "\"\\x3c!-- I am a string --\\x3e\""); assertPrint("'<=&>'", "\"<=&>\""); } public void testBreakUntrustedStrings() { trustedStrings = false; // Break scripts assertPrint("''", "\"\\x3c/script\\x3e\""); assertPrint("\" \"", "\"\\x3c/script\\x3e \\x3c/SCRIPT\\x3e\""); assertPrint("'-->'", "\"--\\x3e\""); assertPrint("']]>'", "\"]]\\x3e\""); assertPrint("' -->'", "\" --\\x3e\\x3c/script\\x3e\""); assertPrint("/--> <\\/script>/g", "/--\\x3e <\\/script>/g"); // Break HTML start comments. Certain versions of WebKit // begin an HTML comment when they see this. assertPrint("''", "\"\\x3c!-- I am a string --\\x3e\""); assertPrint("'<=&>'", "\"\\x3c\\x3d\\x26\\x3e\""); assertPrint("/(?=x)/", "/(?=x)/"); } public void testPrintArray() { assertPrint("[void 0, void 0]", "[void 0,void 0]"); assertPrint("[undefined, undefined]", "[undefined,undefined]"); assertPrint("[ , , , undefined]", "[,,,undefined]"); assertPrint("[ , , , 0]", "[,,,0]"); } public void testHook() { assertPrint("a ? b = 1 : c = 2", "a?b=1:c=2"); assertPrint("x = a ? b = 1 : c = 2", "x=a?b=1:c=2"); assertPrint("(x = a) ? b = 1 : c = 2", "(x=a)?b=1:c=2"); assertPrint("x, a ? b = 1 : c = 2", "x,a?b=1:c=2"); assertPrint("x, (a ? b = 1 : c = 2)", "x,a?b=1:c=2"); assertPrint("(x, a) ? b = 1 : c = 2", "(x,a)?b=1:c=2"); assertPrint("a ? (x, b) : c = 2", "a?(x,b):c=2"); assertPrint("a ? b = 1 : (x,c)", "a?b=1:(x,c)"); assertPrint("a ? b = 1 : c = 2 + x", "a?b=1:c=2+x"); assertPrint("(a ? b = 1 : c = 2) + x", "(a?b=1:c=2)+x"); assertPrint("a ? b = 1 : (c = 2) + x", "a?b=1:(c=2)+x"); assertPrint("a ? (b?1:2) : 3", "a?b?1:2:3"); } public void testPrintInOperatorInForLoop() { // Check for in expression in for's init expression. // Check alone, with + (higher precedence), with ?: (lower precedence), // and with conditional. assertPrint("var a={}; for (var i = (\"length\" in a); i;) {}", "var a={};for(var i=(\"length\"in a);i;);"); assertPrint("var a={}; for (var i = (\"length\" in a) ? 0 : 1; i;) {}", "var a={};for(var i=(\"length\"in a)?0:1;i;);"); assertPrint("var a={}; for (var i = (\"length\" in a) + 1; i;) {}", "var a={};for(var i=(\"length\"in a)+1;i;);"); assertPrint("var a={};for (var i = (\"length\" in a|| \"size\" in a);;);", "var a={};for(var i=(\"length\"in a)||(\"size\"in a);;);"); assertPrint("var a={};for (var i = a || a || (\"size\" in a);;);", "var a={};for(var i=a||a||(\"size\"in a);;);"); // Test works with unary operators and calls. assertPrint("var a={}; for (var i = -(\"length\" in a); i;) {}", "var a={};for(var i=-(\"length\"in a);i;);"); assertPrint("var a={};function b_(p){ return p;};" + "for(var i=1,j=b_(\"length\" in a);;) {}", "var a={};function b_(p){return p}" + "for(var i=1,j=b_(\"length\"in a);;);"); // Test we correctly handle an in operator in the test clause. assertPrint("var a={}; for (;(\"length\" in a);) {}", "var a={};for(;\"length\"in a;);"); } public void testLiteralProperty() { assertPrint("(64).toString()", "(64).toString()"); } private void assertPrint(String js, String expected) { parse(expected); // validate the expected string is valid JS assertEquals(expected, parsePrint(js, false, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); } private void assertPrintSame(String js) { assertPrint(js, js); } // Make sure that the code generator doesn't associate an // else clause with the wrong if clause. public void testAmbiguousElseClauses() { assertPrintNode("if(x)if(y);else;", new Node(Token.IF, Node.newString(Token.NAME, "x"), new Node(Token.BLOCK, new Node(Token.IF, Node.newString(Token.NAME, "y"), new Node(Token.BLOCK), // ELSE clause for the inner if new Node(Token.BLOCK))))); assertPrintNode("if(x){if(y);}else;", new Node(Token.IF, Node.newString(Token.NAME, "x"), new Node(Token.BLOCK, new Node(Token.IF, Node.newString(Token.NAME, "y"), new Node(Token.BLOCK))), // ELSE clause for the outer if new Node(Token.BLOCK))); assertPrintNode("if(x)if(y);else{if(z);}else;", new Node(Token.IF, Node.newString(Token.NAME, "x"), new Node(Token.BLOCK, new Node(Token.IF, Node.newString(Token.NAME, "y"), new Node(Token.BLOCK), new Node(Token.BLOCK, new Node(Token.IF, Node.newString(Token.NAME, "z"), new Node(Token.BLOCK))))), // ELSE clause for the outermost if new Node(Token.BLOCK))); } public void testLineBreak() { // line break after function if in a statement context assertLineBreak("function a() {}\n" + "function b() {}", "function a(){}\n" + "function b(){}\n"); // line break after ; after a function assertLineBreak("var a = {};\n" + "a.foo = function () {}\n" + "function b() {}", "var a={};a.foo=function(){};\n" + "function b(){}\n"); // break after comma after a function assertLineBreak("var a = {\n" + " b: function() {},\n" + " c: function() {}\n" + "};\n" + "alert(a);", "var a={b:function(){},\n" + "c:function(){}};\n" + "alert(a)"); } private void assertLineBreak(String js, String expected) { assertEquals(expected, parsePrint(js, false, true, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); } public void testPreferLineBreakAtEndOfFile() { // short final line, no previous break, do nothing assertLineBreakAtEndOfFile( "\"1234567890\";", "\"1234567890\"", "\"1234567890\""); // short final line, shift previous break to end assertLineBreakAtEndOfFile( "\"123456789012345678901234567890\";\"1234567890\"", "\"123456789012345678901234567890\";\n\"1234567890\"", "\"123456789012345678901234567890\"; \"1234567890\";\n"); assertLineBreakAtEndOfFile( "var12345678901234567890123456 instanceof Object;", "var12345678901234567890123456 instanceof\nObject", "var12345678901234567890123456 instanceof Object;\n"); // long final line, no previous break, add a break at end assertLineBreakAtEndOfFile( "\"1234567890\";\"12345678901234567890\";", "\"1234567890\";\"12345678901234567890\"", "\"1234567890\";\"12345678901234567890\";\n"); // long final line, previous break, add a break at end assertLineBreakAtEndOfFile( "\"123456789012345678901234567890\";\"12345678901234567890\";", "\"123456789012345678901234567890\";\n\"12345678901234567890\"", "\"123456789012345678901234567890\";\n\"12345678901234567890\";\n"); } private void assertLineBreakAtEndOfFile(String js, String expectedWithoutBreakAtEnd, String expectedWithBreakAtEnd) { assertEquals(expectedWithoutBreakAtEnd, parsePrint(js, false, false, false, 30)); assertEquals(expectedWithBreakAtEnd, parsePrint(js, false, false, true, 30)); } public void testPrettyPrinter() { // Ensure that the pretty printer inserts line breaks at appropriate // places. assertPrettyPrint("(function(){})();","(function() {\n})();\n"); assertPrettyPrint("var a = (function() {});alert(a);", "var a = function() {\n};\nalert(a);\n"); // Check we correctly handle putting brackets around all if clauses so // we can put breakpoints inside statements. assertPrettyPrint("if (1) {}", "if(1) {\n" + "}\n"); assertPrettyPrint("if (1) {alert(\"\");}", "if(1) {\n" + " alert(\"\")\n" + "}\n"); assertPrettyPrint("if (1)alert(\"\");", "if(1) {\n" + " alert(\"\")\n" + "}\n"); assertPrettyPrint("if (1) {alert();alert();}", "if(1) {\n" + " alert();\n" + " alert()\n" + "}\n"); // Don't add blocks if they weren't there already. assertPrettyPrint("label: alert();", "label:alert();\n"); // But if statements and loops get blocks automagically. assertPrettyPrint("if (1) alert();", "if(1) {\n" + " alert()\n" + "}\n"); assertPrettyPrint("for (;;) alert();", "for(;;) {\n" + " alert()\n" + "}\n"); assertPrettyPrint("while (1) alert();", "while(1) {\n" + " alert()\n" + "}\n"); // Do we put else clauses in blocks? assertPrettyPrint("if (1) {} else {alert(a);}", "if(1) {\n" + "}else {\n alert(a)\n}\n"); // Do we add blocks to else clauses? assertPrettyPrint("if (1) alert(a); else alert(b);", "if(1) {\n" + " alert(a)\n" + "}else {\n" + " alert(b)\n" + "}\n"); // Do we put for bodies in blocks? assertPrettyPrint("for(;;) { alert();}", "for(;;) {\n" + " alert()\n" + "}\n"); assertPrettyPrint("for(;;) {}", "for(;;) {\n" + "}\n"); assertPrettyPrint("for(;;) { alert(); alert(); }", "for(;;) {\n" + " alert();\n" + " alert()\n" + "}\n"); // How about do loops? assertPrettyPrint("do { alert(); } while(true);", "do {\n" + " alert()\n" + "}while(true);\n"); // label? assertPrettyPrint("myLabel: { alert();}", "myLabel: {\n" + " alert()\n" + "}\n"); // Don't move the label on a loop, because then break {label} and // continue {label} won't work. assertPrettyPrint("myLabel: for(;;) continue myLabel;", "myLabel:for(;;) {\n" + " continue myLabel\n" + "}\n"); assertPrettyPrint("var a;", "var a;\n"); } public void testPrettyPrinter2() { assertPrettyPrint( "if(true) f();", "if(true) {\n" + " f()\n" + "}\n"); assertPrettyPrint( "if (true) { f() } else { g() }", "if(true) {\n" + " f()\n" + "}else {\n" + " g()\n" + "}\n"); assertPrettyPrint( "if(true) f(); for(;;) g();", "if(true) {\n" + " f()\n" + "}\n" + "for(;;) {\n" + " g()\n" + "}\n"); } public void testPrettyPrinter3() { assertPrettyPrint( "try {} catch(e) {}if (1) {alert();alert();}", "try {\n" + "}catch(e) {\n" + "}\n" + "if(1) {\n" + " alert();\n" + " alert()\n" + "}\n"); assertPrettyPrint( "try {} finally {}if (1) {alert();alert();}", "try {\n" + "}finally {\n" + "}\n" + "if(1) {\n" + " alert();\n" + " alert()\n" + "}\n"); assertPrettyPrint( "try {} catch(e) {} finally {} if (1) {alert();alert();}", "try {\n" + "}catch(e) {\n" + "}finally {\n" + "}\n" + "if(1) {\n" + " alert();\n" + " alert()\n" + "}\n"); } public void testPrettyPrinter4() { assertPrettyPrint( "function f() {}if (1) {alert();}", "function f() {\n" + "}\n" + "if(1) {\n" + " alert()\n" + "}\n"); assertPrettyPrint( "var f = function() {};if (1) {alert();}", "var f = function() {\n" + "};\n" + "if(1) {\n" + " alert()\n" + "}\n"); assertPrettyPrint( "(function() {})();if (1) {alert();}", "(function() {\n" + "})();\n" + "if(1) {\n" + " alert()\n" + "}\n"); assertPrettyPrint( "(function() {alert();alert();})();if (1) {alert();}", "(function() {\n" + " alert();\n" + " alert()\n" + "})();\n" + "if(1) {\n" + " alert()\n" + "}\n"); } public void testTypeAnnotations() { assertTypeAnnotations( "/** @constructor */ function Foo(){}", "/**\n * @return {undefined}\n * @constructor\n */\n" + "function Foo() {\n}\n"); } public void testTypeAnnotationsTypeDef() { // TODO(johnlenz): It would be nice if there were some way to preserve // typedefs but currently they are resolved into the basic types in the // type registry. assertTypeAnnotations( "/** @typedef {Array.} */ goog.java.Long;\n" + "/** @param {!goog.java.Long} a*/\n" + "function f(a){};\n", "goog.java.Long;\n" + "/**\n" + " * @param {(Array.|null)} a\n" + " * @return {undefined}\n" + " */\n" + "function f(a) {\n}\n"); } public void testTypeAnnotationsAssign() { assertTypeAnnotations("/** @constructor */ var Foo = function(){}", "/**\n * @return {undefined}\n * @constructor\n */\n" + "var Foo = function() {\n};\n"); } public void testTypeAnnotationsNamespace() { assertTypeAnnotations("var a = {};" + "/** @constructor */ a.Foo = function(){}", "var a = {};\n" + "/**\n * @return {undefined}\n * @constructor\n */\n" + "a.Foo = function() {\n};\n"); } public void testTypeAnnotationsMemberSubclass() { assertTypeAnnotations("var a = {};" + "/** @constructor */ a.Foo = function(){};" + "/** @constructor \n @extends {a.Foo} */ a.Bar = function(){}", "var a = {};\n" + "/**\n * @return {undefined}\n * @constructor\n */\n" + "a.Foo = function() {\n};\n" + "/**\n * @return {undefined}\n * @extends {a.Foo}\n" + " * @constructor\n */\n" + "a.Bar = function() {\n};\n"); } public void testTypeAnnotationsInterface() { assertTypeAnnotations("var a = {};" + "/** @interface */ a.Foo = function(){};" + "/** @interface \n @extends {a.Foo} */ a.Bar = function(){}", "var a = {};\n" + "/**\n * @interface\n */\n" + "a.Foo = function() {\n};\n" + "/**\n * @extends {a.Foo}\n" + " * @interface\n */\n" + "a.Bar = function() {\n};\n"); } public void testTypeAnnotationsMultipleInterface() { assertTypeAnnotations("var a = {};" + "/** @interface */ a.Foo1 = function(){};" + "/** @interface */ a.Foo2 = function(){};" + "/** @interface \n @extends {a.Foo1} \n @extends {a.Foo2} */" + "a.Bar = function(){}", "var a = {};\n" + "/**\n * @interface\n */\n" + "a.Foo1 = function() {\n};\n" + "/**\n * @interface\n */\n" + "a.Foo2 = function() {\n};\n" + "/**\n * @extends {a.Foo1}\n" + " * @extends {a.Foo2}\n" + " * @interface\n */\n" + "a.Bar = function() {\n};\n"); } public void testTypeAnnotationsMember() { assertTypeAnnotations("var a = {};" + "/** @constructor */ a.Foo = function(){}" + "/** @param {string} foo\n" + " * @return {number} */\n" + "a.Foo.prototype.foo = function(foo) { return 3; };" + "/** @type {string|undefined} */" + "a.Foo.prototype.bar = '';", "var a = {};\n" + "/**\n * @return {undefined}\n * @constructor\n */\n" + "a.Foo = function() {\n};\n" + "/**\n" + " * @param {string} foo\n" + " * @return {number}\n" + " */\n" + "a.Foo.prototype.foo = function(foo) {\n return 3\n};\n" + "/** @type {string} */\n" + "a.Foo.prototype.bar = \"\";\n"); } public void testTypeAnnotationsImplements() { assertTypeAnnotations("var a = {};" + "/** @constructor */ a.Foo = function(){};\n" + "/** @interface */ a.I = function(){};\n" + "/** @interface */ a.I2 = function(){};\n" + "/** @constructor \n @extends {a.Foo}\n" + " * @implements {a.I} \n @implements {a.I2}\n" + "*/ a.Bar = function(){}", "var a = {};\n" + "/**\n * @return {undefined}\n * @constructor\n */\n" + "a.Foo = function() {\n};\n" + "/**\n * @interface\n */\n" + "a.I = function() {\n};\n" + "/**\n * @interface\n */\n" + "a.I2 = function() {\n};\n" + "/**\n * @return {undefined}\n * @extends {a.Foo}\n" + " * @implements {a.I}\n" + " * @implements {a.I2}\n * @constructor\n */\n" + "a.Bar = function() {\n};\n"); } public void testTypeAnnotationsDispatcher1() { assertTypeAnnotations( "var a = {};\n" + "/** \n" + " * @constructor \n" + " * @javadispatch \n" + " */\n" + "a.Foo = function(){}", "var a = {};\n" + "/**\n" + " * @return {undefined}\n" + " * @constructor\n" + " * @javadispatch\n" + " */\n" + "a.Foo = function() {\n" + "};\n"); } public void testTypeAnnotationsDispatcher2() { assertTypeAnnotations( "var a = {};\n" + "/** \n" + " * @constructor \n" + " */\n" + "a.Foo = function(){}\n" + "/**\n" + " * @javadispatch\n" + " */\n" + "a.Foo.prototype.foo = function() {};", "var a = {};\n" + "/**\n" + " * @return {undefined}\n" + " * @constructor\n" + " */\n" + "a.Foo = function() {\n" + "};\n" + "/**\n" + " * @return {undefined}\n" + " * @javadispatch\n" + " */\n" + "a.Foo.prototype.foo = function() {\n" + "};\n"); } public void testU2UFunctionTypeAnnotation1() { assertTypeAnnotations( "/** @type {!Function} */ var x = function() {}", "/** @type {!Function} */\n" + "var x = function() {\n};\n"); } public void testU2UFunctionTypeAnnotation2() { // TODO(johnlenz): we currently report the type of the RHS which is not // correct, we should export the type of the LHS. assertTypeAnnotations( "/** @type {Function} */ var x = function() {}", "/** @type {!Function} */\n" + "var x = function() {\n};\n"); } public void testEmitUnknownParamTypesAsAllType() { assertTypeAnnotations( "var a = function(x) {}", "/**\n" + " * @param {?} x\n" + " * @return {undefined}\n" + " */\n" + "var a = function(x) {\n};\n"); } public void testOptionalTypesAnnotation() { assertTypeAnnotations( "/**\n" + " * @param {string=} x \n" + " */\n" + "var a = function(x) {}", "/**\n" + " * @param {string=} x\n" + " * @return {undefined}\n" + " */\n" + "var a = function(x) {\n};\n"); } public void testVariableArgumentsTypesAnnotation() { assertTypeAnnotations( "/**\n" + " * @param {...string} x \n" + " */\n" + "var a = function(x) {}", "/**\n" + " * @param {...string} x\n" + " * @return {undefined}\n" + " */\n" + "var a = function(x) {\n};\n"); } public void testTempConstructor() { assertTypeAnnotations( "var x = function() {\n/**\n * @constructor\n */\nfunction t1() {}\n" + " /**\n * @constructor\n */\nfunction t2() {}\n" + " t1.prototype = t2.prototype}", "/**\n * @return {undefined}\n */\nvar x = function() {\n" + " /**\n * @return {undefined}\n * @constructor\n */\n" + "function t1() {\n }\n" + " /**\n * @return {undefined}\n * @constructor\n */\n" + "function t2() {\n }\n" + " t1.prototype = t2.prototype\n};\n" ); } public void testEnumAnnotation1() { assertTypeAnnotations( "/** @enum {string} */ var Enum = {FOO: 'x', BAR: 'y'};", "/** @enum {string} */\nvar Enum = {FOO:\"x\", BAR:\"y\"};\n"); } public void testEnumAnnotation2() { assertTypeAnnotations( "var goog = goog || {};" + "/** @enum {string} */ goog.Enum = {FOO: 'x', BAR: 'y'};" + "/** @const */ goog.Enum2 = goog.x ? {} : goog.Enum;", "var goog = goog || {};\n" + "/** @enum {string} */\ngoog.Enum = {FOO:\"x\", BAR:\"y\"};\n" + "/** @type {(Object|{})} */\ngoog.Enum2 = goog.x ? {} : goog.Enum;\n"); } private void assertPrettyPrint(String js, String expected) { assertEquals(expected, parsePrint(js, true, false, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD)); } private void assertTypeAnnotations(String js, String expected) { assertEquals(expected, parsePrint(js, true, false, CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD, true)); } public void testSubtraction() { Compiler compiler = new Compiler(); Node n = compiler.parseTestCode("x - -4"); assertEquals(0, compiler.getErrorCount()); assertEquals( "x- -4", printNode(n)); } public void testFunctionWithCall() { assertPrint( "var user = new function() {" + "alert(\"foo\")}", "var user=new function(){" + "alert(\"foo\")}"); assertPrint( "var user = new function() {" + "this.name = \"foo\";" + "this.local = function(){alert(this.name)};}", "var user=new function(){" + "this.name=\"foo\";" + "this.local=function(){alert(this.name)}}"); } public void testLineLength() { // list assertLineLength("var aba,bcb,cdc", "var aba,bcb," + "\ncdc"); // operators, and two breaks assertLineLength( "\"foo\"+\"bar,baz,bomb\"+\"whee\"+\";long-string\"\n+\"aaa\"", "\"foo\"+\"bar,baz,bomb\"+" + "\n\"whee\"+\";long-string\"+" + "\n\"aaa\""); // assignment assertLineLength("var abazaba=1234", "var abazaba=" + "\n1234"); // statements assertLineLength("var abab=1;var bab=2", "var abab=1;" + "\nvar bab=2"); // don't break regexes assertLineLength("var a=/some[reg](ex),with.*we?rd|chars/i;var b=a", "var a=/some[reg](ex),with.*we?rd|chars/i;" + "\nvar b=a"); // don't break strings assertLineLength("var a=\"foo,{bar};baz\";var b=a", "var a=\"foo,{bar};baz\";" + "\nvar b=a"); // don't break before post inc/dec assertLineLength("var a=\"a\";a++;var b=\"bbb\";", "var a=\"a\";a++;\n" + "var b=\"bbb\""); } private void assertLineLength(String js, String expected) { assertEquals(expected, parsePrint(js, false, true, 10)); } public void testParsePrintParse() { testReparse("3;"); testReparse("var a = b;"); testReparse("var x, y, z;"); testReparse("try { foo() } catch(e) { bar() }"); testReparse("try { foo() } catch(e) { bar() } finally { stuff() }"); testReparse("try { foo() } finally { stuff() }"); testReparse("throw 'me'"); testReparse("function foo(a) { return a + 4; }"); testReparse("function foo() { return; }"); testReparse("var a = function(a, b) { foo(); return a + b; }"); testReparse("b = [3, 4, 'paul', \"Buchhe it\",,5];"); testReparse("v = (5, 6, 7, 8)"); testReparse("d = 34.0; x = 0; y = .3; z = -22"); testReparse("d = -x; t = !x + ~y;"); testReparse("'hi'; /* just a test */ stuff(a,b) \n" + " foo(); // and another \n" + " bar();"); testReparse("a = b++ + ++c; a = b++-++c; a = - --b; a = - ++b;"); testReparse("a++; b= a++; b = ++a; b = a--; b = --a; a+=2; b-=5"); testReparse("a = (2 + 3) * 4;"); testReparse("a = 1 + (2 + 3) + 4;"); testReparse("x = a ? b : c; x = a ? (b,3,5) : (foo(),bar());"); testReparse("a = b | c || d ^ e " + "&& f & !g != h << i <= j < k >>> l > m * n % !o"); testReparse("a == b; a != b; a === b; a == b == a;" + " (a == b) == a; a == (b == a);"); testReparse("if (a > b) a = b; if (b < 3) a = 3; else c = 4;"); testReparse("if (a == b) { a++; } if (a == 0) { a++; } else { a --; }"); testReparse("for (var i in a) b += i;"); testReparse("for (var i = 0; i < 10; i++){ b /= 2;" + " if (b == 2)break;else continue;}"); testReparse("for (x = 0; x < 10; x++) a /= 2;"); testReparse("for (;;) a++;"); testReparse("while(true) { blah(); }while(true) blah();"); testReparse("do stuff(); while(a>b);"); testReparse("[0, null, , true, false, this];"); testReparse("s.replace(/absc/, 'X').replace(/ab/gi, 'Y');"); testReparse("new Foo; new Bar(a, b,c);"); testReparse("with(foo()) { x = z; y = t; } with(bar()) a = z;"); testReparse("delete foo['bar']; delete foo;"); testReparse("var x = { 'a':'paul', 1:'3', 2:(3,4) };"); testReparse("switch(a) { case 2: case 3: stuff(); break;" + "case 4: morestuff(); break; default: done();}"); testReparse("x = foo['bar'] + foo['my stuff'] + foo[bar] + f.stuff;"); testReparse("a.v = b.v; x['foo'] = y['zoo'];"); testReparse("'test' in x; 3 in x; a in x;"); testReparse("'foo\"bar' + \"foo'c\" + 'stuff\\n and \\\\more'"); testReparse("x.__proto__;"); } private void testReparse(String code) { Compiler compiler = new Compiler(); Node parse1 = parse(code); Node parse2 = parse(new CodePrinter.Builder(parse1).build()); String explanation = parse1.checkTreeEquals(parse2); assertNull("\nExpected: " + compiler.toSource(parse1) + "\nResult: " + compiler.toSource(parse2) + "\n" + explanation, explanation); } public void testDoLoopIECompatiblity() { // Do loops within IFs cause syntax errors in IE6 and IE7. assertPrint("function f(){if(e1){do foo();while(e2)}else foo()}", "function f(){if(e1){do foo();while(e2)}else foo()}"); assertPrint("function f(){if(e1)do foo();while(e2)else foo()}", "function f(){if(e1){do foo();while(e2)}else foo()}"); assertPrint("if(x){do{foo()}while(y)}else bar()", "if(x){do foo();while(y)}else bar()"); assertPrint("if(x)do{foo()}while(y);else bar()", "if(x){do foo();while(y)}else bar()"); assertPrint("if(x){do{foo()}while(y)}", "if(x){do foo();while(y)}"); assertPrint("if(x)do{foo()}while(y);", "if(x){do foo();while(y)}"); assertPrint("if(x)A:do{foo()}while(y);", "if(x){A:do foo();while(y)}"); assertPrint("var i = 0;a: do{b: do{i++;break b;} while(0);} while(0);", "var i=0;a:do{b:do{i++;break b}while(0)}while(0)"); } public void testFunctionSafariCompatiblity() { // Functions within IFs cause syntax errors on Safari. assertPrint("function f(){if(e1){function goo(){return true}}else foo()}", "function f(){if(e1){function goo(){return true}}else foo()}"); assertPrint("function f(){if(e1)function goo(){return true}else foo()}", "function f(){if(e1){function goo(){return true}}else foo()}"); assertPrint("if(e1){function goo(){return true}}", "if(e1){function goo(){return true}}"); assertPrint("if(e1)function goo(){return true}", "if(e1){function goo(){return true}}"); assertPrint("if(e1)A:function goo(){return true}", "if(e1){A:function goo(){return true}}"); } public void testExponents() { assertPrintNumber("1", 1); assertPrintNumber("10", 10); assertPrintNumber("100", 100); assertPrintNumber("1E3", 1000); assertPrintNumber("1E4", 10000); assertPrintNumber("1E5", 100000); assertPrintNumber("-1", -1); assertPrintNumber("-10", -10); assertPrintNumber("-100", -100); assertPrintNumber("-1E3", -1000); assertPrintNumber("-12341234E4", -123412340000L); assertPrintNumber("1E18", 1000000000000000000L); assertPrintNumber("1E5", 100000.0); assertPrintNumber("100000.1", 100000.1); assertPrintNumber("1E-6", 0.000001); assertPrintNumber("-0x38d7ea4c68001", -0x38d7ea4c68001L); assertPrintNumber("0x38d7ea4c68001", 0x38d7ea4c68001L); } // Make sure to test as both a String and a Node, because // negative numbers do not parse consistently from strings. private void assertPrintNumber(String expected, double number) { assertPrint(String.valueOf(number), expected); assertPrintNode(expected, Node.newNumber(number)); } private void assertPrintNumber(String expected, int number) { assertPrint(String.valueOf(number), expected); assertPrintNode(expected, Node.newNumber(number)); } public void testDirectEval() { assertPrint("eval('1');", "eval(\"1\")"); } public void testIndirectEval() { Node n = parse("eval('1');"); assertPrintNode("eval(\"1\")", n); n.getFirstChild().getFirstChild().getFirstChild().putBooleanProp( Node.DIRECT_EVAL, false); assertPrintNode("(0,eval)(\"1\")", n); } public void testFreeCall1() { assertPrint("foo(a);", "foo(a)"); assertPrint("x.foo(a);", "x.foo(a)"); } public void testFreeCall2() { Node n = parse("foo(a);"); assertPrintNode("foo(a)", n); Node call = n.getFirstChild().getFirstChild(); assertTrue(call.isCall()); call.putBooleanProp(Node.FREE_CALL, true); assertPrintNode("foo(a)", n); } public void testFreeCall3() { Node n = parse("x.foo(a);"); assertPrintNode("x.foo(a)", n); Node call = n.getFirstChild().getFirstChild(); assertTrue(call.isCall()); call.putBooleanProp(Node.FREE_CALL, true); assertPrintNode("(0,x.foo)(a)", n); } public void testPrintScript() { // Verify that SCRIPT nodes not marked as synthetic are printed as // blocks. Node ast = new Node(Token.SCRIPT, new Node(Token.EXPR_RESULT, Node.newString("f")), new Node(Token.EXPR_RESULT, Node.newString("g"))); String result = new CodePrinter.Builder(ast).setPrettyPrint(true).build(); assertEquals("\"f\";\n\"g\";\n", result); } public void testObjectLit() { assertPrint("({x:1})", "({x:1})"); assertPrint("var x=({x:1})", "var x={x:1}"); assertPrint("var x={'x':1}", "var x={\"x\":1}"); assertPrint("var x={1:1}", "var x={1:1}"); assertPrint("({},42)+0", "({},42)+0"); } public void testObjectLit2() { assertPrint("var x={1:1}", "var x={1:1}"); assertPrint("var x={'1':1}", "var x={1:1}"); assertPrint("var x={'1.0':1}", "var x={\"1.0\":1}"); assertPrint("var x={1.5:1}", "var x={\"1.5\":1}"); } public void testObjectLit3() { assertPrint("var x={3E9:1}", "var x={3E9:1}"); assertPrint("var x={'3000000000':1}", // More than 31 bits "var x={3E9:1}"); assertPrint("var x={'3000000001':1}", "var x={3000000001:1}"); assertPrint("var x={'6000000001':1}", // More than 32 bits "var x={6000000001:1}"); assertPrint("var x={\"12345678901234567\":1}", // More than 53 bits "var x={\"12345678901234567\":1}"); } public void testObjectLit4() { // More than 128 bits. assertPrint( "var x={\"123456789012345671234567890123456712345678901234567\":1}", "var x={\"123456789012345671234567890123456712345678901234567\":1}"); } public void testGetter() { assertPrint("var x = {}", "var x={}"); assertPrint("var x = {get a() {return 1}}", "var x={get a(){return 1}}"); assertPrint( "var x = {get a() {}, get b(){}}", "var x={get a(){},get b(){}}"); assertPrint( "var x = {get 'a'() {return 1}}", "var x={get \"a\"(){return 1}}"); assertPrint( "var x = {get 1() {return 1}}", "var x={get 1(){return 1}}"); assertPrint( "var x = {get \"()\"() {return 1}}", "var x={get \"()\"(){return 1}}"); } public void testSetter() { assertPrint("var x = {}", "var x={}"); assertPrint( "var x = {set a(y) {return 1}}", "var x={set a(y){return 1}}"); assertPrint( "var x = {get 'a'() {return 1}}", "var x={get \"a\"(){return 1}}"); assertPrint( "var x = {set 1(y) {return 1}}", "var x={set 1(y){return 1}}"); assertPrint( "var x = {set \"(x)\"(y) {return 1}}", "var x={set \"(x)\"(y){return 1}}"); } public void testNegCollapse() { // Collapse the negative symbol on numbers at generation time, // to match the Rhino behavior. assertPrint("var x = - - 2;", "var x=2"); assertPrint("var x = - (2);", "var x=-2"); } public void testStrict() { String result = parsePrint("var x", false, false, 0, false, true); assertEquals("'use strict';var x", result); } public void testArrayLiteral() { assertPrint("var x = [,];","var x=[,]"); assertPrint("var x = [,,];","var x=[,,]"); assertPrint("var x = [,s,,];","var x=[,s,,]"); assertPrint("var x = [,s];","var x=[,s]"); assertPrint("var x = [s,];","var x=[s]"); } public void testZero() { assertPrint("var x ='\\0';", "var x=\"\\x00\""); assertPrint("var x ='\\x00';", "var x=\"\\x00\""); assertPrint("var x ='\\u0000';", "var x=\"\\x00\""); assertPrint("var x ='\\u00003';", "var x=\"\\x003\""); } public void testUnicode() { assertPrint("var x ='\\x0f';", "var x=\"\\u000f\""); assertPrint("var x ='\\x68';", "var x=\"h\""); assertPrint("var x ='\\x7f';", "var x=\"\\u007f\""); } public void testUnicodeKeyword() { // keyword "if" assertPrint("var \\u0069\\u0066 = 1;", "var i\\u0066=1"); // keyword "var" assertPrint("var v\\u0061\\u0072 = 1;", "var va\\u0072=1"); // all are keyword "while" assertPrint("var w\\u0068\\u0069\\u006C\\u0065 = 1;" + "\\u0077\\u0068il\\u0065 = 2;" + "\\u0077h\\u0069le = 3;", "var whil\\u0065=1;whil\\u0065=2;whil\\u0065=3"); } public void testNumericKeys() { assertPrint("var x = {010: 1};", "var x={8:1}"); assertPrint("var x = {'010': 1};", "var x={\"010\":1}"); assertPrint("var x = {0x10: 1};", "var x={16:1}"); assertPrint("var x = {'0x10': 1};", "var x={\"0x10\":1}"); // I was surprised at this result too. assertPrint("var x = {.2: 1};", "var x={\"0.2\":1}"); assertPrint("var x = {'.2': 1};", "var x={\".2\":1}"); assertPrint("var x = {0.2: 1};", "var x={\"0.2\":1}"); assertPrint("var x = {'0.2': 1};", "var x={\"0.2\":1}"); } public void testIssue582() { assertPrint("var x = -0.0;", "var x=-0"); } public void testIssue601() { assertPrint("'\\v' == 'v'", "\"\\v\"==\"v\""); assertPrint("'\\u000B' == '\\v'", "\"\\x0B\"==\"\\v\""); assertPrint("'\\x0B' == '\\v'", "\"\\x0B\"==\"\\v\""); } public void testIssue620() { assertPrint("alert(/ / / / /);", "alert(/ // / /)"); assertPrint("alert(/ // / /);", "alert(/ // / /)"); } public void testIssue5746867() { assertPrint("var a = { '$\\\\' : 5 };", "var a={\"$\\\\\":5}"); } public void testCommaSpacing() { assertPrint("var a = (b = 5, c = 5);", "var a=(b=5,c=5)"); assertPrettyPrint("var a = (b = 5, c = 5);", "var a = (b = 5, c = 5);\n"); } public void testManyCommas() { int numCommas = 10000; List numbers = Lists.newArrayList("0", "1"); Node current = new Node(Token.COMMA, Node.newNumber(0), Node.newNumber(1)); for (int i = 2; i < numCommas; i++) { current = new Node(Token.COMMA, current); // 1000 is printed as 1E3, and screws up our test. int num = i % 1000; numbers.add(String.valueOf(num)); current.addChildToBack(Node.newNumber(num)); } String expected = Joiner.on(",").join(numbers); String actual = printNode(current).replace("\n", ""); assertEquals(expected, actual); } public void testManyAdds() { int numAdds = 10000; List numbers = Lists.newArrayList("0", "1"); Node current = new Node(Token.ADD, Node.newNumber(0), Node.newNumber(1)); for (int i = 2; i < numAdds; i++) { current = new Node(Token.ADD, current); // 1000 is printed as 1E3, and screws up our test. int num = i % 1000; numbers.add(String.valueOf(num)); current.addChildToBack(Node.newNumber(num)); } String expected = Joiner.on("+").join(numbers); String actual = printNode(current).replace("\n", ""); assertEquals(expected, actual); } public void testMinusNegativeZero() { // Negative zero is weird, because we have to be able to distinguish // it from positive zero (there are some subtle differences in behavior). assertPrint("x- -0", "x- -0"); } public void testStringEscapeSequences() { // From the SingleEscapeCharacter grammar production. assertPrintSame("var x=\"\\b\""); assertPrintSame("var x=\"\\f\""); assertPrintSame("var x=\"\\n\""); assertPrintSame("var x=\"\\r\""); assertPrintSame("var x=\"\\t\""); assertPrintSame("var x=\"\\v\""); assertPrint("var x=\"\\\"\"", "var x='\"'"); assertPrint("var x=\"\\\'\"", "var x=\"'\""); // From the LineTerminator grammar assertPrint("var x=\"\\u000A\"", "var x=\"\\n\""); assertPrint("var x=\"\\u000D\"", "var x=\"\\r\""); assertPrintSame("var x=\"\\u2028\""); assertPrintSame("var x=\"\\u2029\""); // Now with regular expressions. assertPrintSame("var x=/\\b/"); assertPrintSame("var x=/\\f/"); assertPrintSame("var x=/\\n/"); assertPrintSame("var x=/\\r/"); assertPrintSame("var x=/\\t/"); assertPrintSame("var x=/\\v/"); assertPrintSame("var x=/\\u000A/"); assertPrintSame("var x=/\\u000D/"); assertPrintSame("var x=/\\u2028/"); assertPrintSame("var x=/\\u2029/"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FunctionNamesTest.java0000644000175000017500000000613212115204405027302 0ustar apoapo/* * 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.Maps; import com.google.javascript.rhino.Node; import java.util.Map; /** * Tests for {@link FunctionNames} * */ public class FunctionNamesTest extends CompilerTestCase { private FunctionNames functionNames; public FunctionNamesTest() { this.functionNames = null; } @Override protected CompilerPass getProcessor(Compiler compiler) { functionNames = new FunctionNames(compiler); return functionNames; } public void testFunctionsNamesAndIds() { final String jsSource = "goog.widget = function(str) {\n" + " this.member_fn = function() {};\n" + " local_fn = function() {};\n" + " (function(a){})(1);\n" + "}\n" + "function foo() {\n" + " function bar() {}\n" + "}\n" + "literal = {f1 : function(){}, f2 : function(){}};\n" + "goog.array.map(arr, function named(){});\n" + "goog.array.map(arr, function(){});\n" + "named_twice = function quax(){};\n" + "recliteral = {l1 : {l2 : function(){}}};\n" + "namedliteral = {n1 : function litnamed(){}};\n" + "namedrecliteral = {n1 : {n2 : function reclitnamed(){}}};\n" + "numliteral = {1 : function(){}};\n" + "recnumliteral = {1 : {a : function(){}}};\n"; testSame(jsSource); final Map idNameMap = Maps.newLinkedHashMap(); int count = 0; for (Node f : functionNames.getFunctionNodeList()) { int id = functionNames.getFunctionId(f); String name = functionNames.getFunctionName(f); idNameMap.put(id, name); count++; } assertEquals("Unexpected number of functions", 16, count); final Map expectedMap = Maps.newLinkedHashMap(); expectedMap.put(0, "goog.widget.member_fn"); expectedMap.put(1, "goog.widget::local_fn"); expectedMap.put(2, "goog.widget::"); expectedMap.put(3, "goog.widget"); expectedMap.put(4, "foo::bar"); expectedMap.put(5, "foo"); expectedMap.put(6, "literal.f1"); expectedMap.put(7, "literal.f2"); expectedMap.put(8, "named"); expectedMap.put(9, ""); expectedMap.put(10, "quax"); expectedMap.put(11, "recliteral.l1.l2"); expectedMap.put(12, "litnamed"); expectedMap.put(13, "reclitnamed"); expectedMap.put(14, "numliteral.__2"); expectedMap.put(15, "recnumliteral.__3.a"); assertEquals("Function id/name mismatch", expectedMap, idNameMap); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java0000644000175000017500000011644212115204405031002 0ustar apoapo/* * Copyright 2004 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.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.javascript.rhino.Node; import java.util.List; import java.util.Map; import java.util.Set; /** * Tests for {@link PeepholeFoldConstants} in isolation. Tests for * the interaction of multiple peephole passes are in * {@link PeepholeIntegrationTest}. */ public class PeepholeFoldConstantsTest extends CompilerTestCase { private boolean late; // TODO(user): Remove this when we no longer need to do string comparison. private PeepholeFoldConstantsTest(boolean compareAsTree) { super("", compareAsTree); } public PeepholeFoldConstantsTest() { super(""); } @Override public void setUp() { late = false; enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { CompilerPass peepholePass = new PeepholeOptimizationsPass(compiler, new PeepholeFoldConstants(late)); return peepholePass; } @Override protected int getNumRepetitions() { // Reduce this to 1 if we get better expression evaluators. return 2; } private void foldSame(String js) { testSame(js); } private void fold(String js, String expected) { test(js, expected); } private void fold(String js, String expected, DiagnosticType warning) { test(js, expected, null, warning); } // TODO(user): This is same as fold() except it uses string comparison. Any // test that needs tell us where a folding is constructing an invalid AST. private void assertResultString(String js, String expected) { PeepholeFoldConstantsTest scTest = new PeepholeFoldConstantsTest(false); scTest.test(js, expected); } public void testUndefinedComparison1() { fold("undefined == undefined", "true"); fold("undefined == null", "true"); fold("undefined == void 0", "true"); fold("undefined == 0", "false"); fold("undefined == 1", "false"); fold("undefined == 'hi'", "false"); fold("undefined == true", "false"); fold("undefined == false", "false"); fold("undefined === undefined", "true"); fold("undefined === null", "false"); fold("undefined === void 0", "true"); foldSame("undefined == this"); foldSame("undefined == x"); fold("undefined != undefined", "false"); fold("undefined != null", "false"); fold("undefined != void 0", "false"); fold("undefined != 0", "true"); fold("undefined != 1", "true"); fold("undefined != 'hi'", "true"); fold("undefined != true", "true"); fold("undefined != false", "true"); fold("undefined !== undefined", "false"); fold("undefined !== void 0", "false"); fold("undefined !== null", "true"); foldSame("undefined != this"); foldSame("undefined != x"); fold("undefined < undefined", "false"); fold("undefined > undefined", "false"); fold("undefined >= undefined", "false"); fold("undefined <= undefined", "false"); fold("0 < undefined", "false"); fold("true > undefined", "false"); fold("'hi' >= undefined", "false"); fold("null <= undefined", "false"); fold("undefined < 0", "false"); fold("undefined > true", "false"); fold("undefined >= 'hi'", "false"); fold("undefined <= null", "false"); fold("null == undefined", "true"); fold("0 == undefined", "false"); fold("1 == undefined", "false"); fold("'hi' == undefined", "false"); fold("true == undefined", "false"); fold("false == undefined", "false"); fold("null === undefined", "false"); fold("void 0 === undefined", "true"); fold("undefined == NaN", "false"); fold("NaN == undefined", "false"); fold("undefined == Infinity", "false"); fold("Infinity == undefined", "false"); fold("undefined == -Infinity", "false"); fold("-Infinity == undefined", "false"); fold("({}) == undefined", "false"); fold("undefined == ({})", "false"); fold("([]) == undefined", "false"); fold("undefined == ([])", "false"); fold("(/a/g) == undefined", "false"); fold("undefined == (/a/g)", "false"); fold("(function(){}) == undefined", "false"); fold("undefined == (function(){})", "false"); fold("undefined != NaN", "true"); fold("NaN != undefined", "true"); fold("undefined != Infinity", "true"); fold("Infinity != undefined", "true"); fold("undefined != -Infinity", "true"); fold("-Infinity != undefined", "true"); fold("({}) != undefined", "true"); fold("undefined != ({})", "true"); fold("([]) != undefined", "true"); fold("undefined != ([])", "true"); fold("(/a/g) != undefined", "true"); fold("undefined != (/a/g)", "true"); fold("(function(){}) != undefined", "true"); fold("undefined != (function(){})", "true"); foldSame("this == undefined"); foldSame("x == undefined"); } public void testUndefinedComparison2() { fold("\"123\" !== void 0", "true"); fold("\"123\" === void 0", "false"); fold("void 0 !== \"123\"", "true"); fold("void 0 === \"123\"", "false"); } public void testUndefinedComparison3() { fold("\"123\" !== undefined", "true"); fold("\"123\" === undefined", "false"); fold("undefined !== \"123\"", "true"); fold("undefined === \"123\"", "false"); } public void testUndefinedComparison4() { fold("1 !== void 0", "true"); fold("1 === void 0", "false"); fold("null !== void 0", "true"); fold("null === void 0", "false"); fold("undefined !== void 0", "false"); fold("undefined === void 0", "true"); } public void testNullComparison1() { fold("null == undefined", "true"); fold("null == null", "true"); fold("null == void 0", "true"); fold("null == 0", "false"); fold("null == 1", "false"); fold("null == 'hi'", "false"); fold("null == true", "false"); fold("null == false", "false"); fold("null === undefined", "false"); fold("null === null", "true"); fold("null === void 0", "false"); foldSame("null == this"); foldSame("null == x"); fold("null != undefined", "false"); fold("null != null", "false"); fold("null != void 0", "false"); fold("null != 0", "true"); fold("null != 1", "true"); fold("null != 'hi'", "true"); fold("null != true", "true"); fold("null != false", "true"); fold("null !== undefined", "true"); fold("null !== void 0", "true"); fold("null !== null", "false"); foldSame("null != this"); foldSame("null != x"); fold("null < null", "false"); fold("null > null", "false"); fold("null >= null", "true"); fold("null <= null", "true"); foldSame("0 < null"); // foldable fold("true > null", "true"); foldSame("'hi' >= null"); // foldable fold("null <= null", "true"); foldSame("null < 0"); // foldable fold("null > true", "false"); foldSame("null >= 'hi'"); // foldable fold("null <= null", "true"); fold("null == null", "true"); fold("0 == null", "false"); fold("1 == null", "false"); fold("'hi' == null", "false"); fold("true == null", "false"); fold("false == null", "false"); fold("null === null", "true"); fold("void 0 === null", "false"); fold("null == NaN", "false"); fold("NaN == null", "false"); fold("null == Infinity", "false"); fold("Infinity == null", "false"); fold("null == -Infinity", "false"); fold("-Infinity == null", "false"); fold("({}) == null", "false"); fold("null == ({})", "false"); fold("([]) == null", "false"); fold("null == ([])", "false"); fold("(/a/g) == null", "false"); fold("null == (/a/g)", "false"); fold("(function(){}) == null", "false"); fold("null == (function(){})", "false"); fold("null != NaN", "true"); fold("NaN != null", "true"); fold("null != Infinity", "true"); fold("Infinity != null", "true"); fold("null != -Infinity", "true"); fold("-Infinity != null", "true"); fold("({}) != null", "true"); fold("null != ({})", "true"); fold("([]) != null", "true"); fold("null != ([])", "true"); fold("(/a/g) != null", "true"); fold("null != (/a/g)", "true"); fold("(function(){}) != null", "true"); fold("null != (function(){})", "true"); foldSame("({a:f()}) == null"); foldSame("null == ({a:f()})"); foldSame("([f()]) == null"); foldSame("null == ([f()])"); foldSame("this == null"); foldSame("x == null"); } public void testUnaryOps() { // These cases are handled by PeepholeRemoveDeadCode. foldSame("!foo()"); foldSame("~foo()"); foldSame("-foo()"); // These cases are handled here. fold("a=!true", "a=false"); fold("a=!10", "a=false"); fold("a=!false", "a=true"); fold("a=!foo()", "a=!foo()"); fold("a=-0", "a=-0.0"); fold("a=-(0)", "a=-0.0"); fold("a=-Infinity", "a=-Infinity"); fold("a=-NaN", "a=NaN"); fold("a=-foo()", "a=-foo()"); fold("a=~~0", "a=0"); fold("a=~~10", "a=10"); fold("a=~-7", "a=6"); fold("a=+true", "a=1"); fold("a=+10", "a=10"); fold("a=+false", "a=0"); foldSame("a=+foo()"); foldSame("a=+f"); fold("a=+(f?true:false)", "a=+(f?1:0)"); // TODO(johnlenz): foldable fold("a=+0", "a=0"); fold("a=+Infinity", "a=Infinity"); fold("a=+NaN", "a=NaN"); fold("a=+-7", "a=-7"); fold("a=+.5", "a=.5"); fold("a=~0x100000000", "a=~0x100000000", PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); fold("a=~-0x100000000", "a=~-0x100000000", PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); testSame("a=~.5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); } public void testUnaryOpsStringCompare() { // Negatives are folded into a single number node. assertResultString("a=-1", "a=-1"); assertResultString("a=~0", "a=-1"); assertResultString("a=~1", "a=-2"); assertResultString("a=~101", "a=-102"); } public void testFoldLogicalOp() { fold("x = true && x", "x = x"); foldSame("x = [foo()] && x"); fold("x = false && x", "x = false"); fold("x = true || x", "x = true"); fold("x = false || x", "x = x"); fold("x = 0 && x", "x = 0"); fold("x = 3 || x", "x = 3"); fold("x = false || 0", "x = 0"); // unfoldable, because the right-side may be the result fold("a = x && true", "a=x&&true"); fold("a = x && false", "a=x&&false"); fold("a = x || 3", "a=x||3"); fold("a = x || false", "a=x||false"); fold("a = b ? c : x || false", "a=b?c:x||false"); fold("a = b ? x || false : c", "a=b?x||false:c"); fold("a = b ? c : x && true", "a=b?c:x&&true"); fold("a = b ? x && true : c", "a=b?x&&true:c"); // folded, but not here. foldSame("a = x || false ? b : c"); foldSame("a = x && true ? b : c"); fold("x = foo() || true || bar()", "x = foo()||true"); fold("x = foo() || false || bar()", "x = foo()||bar()"); fold("x = foo() || true && bar()", "x = foo()||bar()"); fold("x = foo() || false && bar()", "x = foo()||false"); fold("x = foo() && false && bar()", "x = foo()&&false"); fold("x = foo() && true && bar()", "x = foo()&&bar()"); fold("x = foo() && false || bar()", "x = foo()&&false||bar()"); fold("1 && b()", "b()"); fold("a() && (1 && b())", "a() && b()"); // TODO(johnlenz): Consider folding the following to: // "(a(),1) && b(); fold("(a() && 1) && b()", "(a() && 1) && b()"); // Really not foldable, because it would change the type of the // expression if foo() returns something equivalent, but not // identical, to true. Cf. FoldConstants.tryFoldAndOr(). foldSame("x = foo() && true || bar()"); foldSame("foo() && true || bar()"); } public void testFoldBitwiseOp() { fold("x = 1 & 1", "x = 1"); fold("x = 1 & 2", "x = 0"); fold("x = 3 & 1", "x = 1"); fold("x = 3 & 3", "x = 3"); fold("x = 1 | 1", "x = 1"); fold("x = 1 | 2", "x = 3"); fold("x = 3 | 1", "x = 3"); fold("x = 3 | 3", "x = 3"); fold("x = 1 ^ 1", "x = 0"); fold("x = 1 ^ 2", "x = 3"); fold("x = 3 ^ 1", "x = 2"); fold("x = 3 ^ 3", "x = 0"); fold("x = -1 & 0", "x = 0"); fold("x = 0 & -1", "x = 0"); fold("x = 1 & 4", "x = 0"); fold("x = 2 & 3", "x = 2"); // make sure we fold only when we are supposed to -- not when doing so would // lose information or when it is performed on nonsensical arguments. fold("x = 1 & 1.1", "x = 1"); fold("x = 1.1 & 1", "x = 1"); fold("x = 1 & 3000000000", "x = 0"); fold("x = 3000000000 & 1", "x = 0"); // Try some cases with | as well fold("x = 1 | 4", "x = 5"); fold("x = 1 | 3", "x = 3"); fold("x = 1 | 1.1", "x = 1"); foldSame("x = 1 | 3E9"); fold("x = 1 | 3000000001", "x = -1294967295"); } public void testFoldBitwiseOp2() { fold("x = y & 1 & 1", "x = y & 1"); fold("x = y & 1 & 2", "x = y & 0"); fold("x = y & 3 & 1", "x = y & 1"); fold("x = 3 & y & 1", "x = y & 1"); fold("x = y & 3 & 3", "x = y & 3"); fold("x = 3 & y & 3", "x = y & 3"); fold("x = y | 1 | 1", "x = y | 1"); fold("x = y | 1 | 2", "x = y | 3"); fold("x = y | 3 | 1", "x = y | 3"); fold("x = 3 | y | 1", "x = y | 3"); fold("x = y | 3 | 3", "x = y | 3"); fold("x = 3 | y | 3", "x = y | 3"); fold("x = y ^ 1 ^ 1", "x = y ^ 0"); fold("x = y ^ 1 ^ 2", "x = y ^ 3"); fold("x = y ^ 3 ^ 1", "x = y ^ 2"); fold("x = 3 ^ y ^ 1", "x = y ^ 2"); fold("x = y ^ 3 ^ 3", "x = y ^ 0"); fold("x = 3 ^ y ^ 3", "x = y ^ 0"); fold("x = Infinity | NaN", "x=0"); fold("x = 12 | NaN", "x=12"); } public void testFoldingMixTypesLate() { late = true; fold("x = x + '2'", "x+='2'"); fold("x = +x + +'2'", "x = +x + 2"); fold("x = x - '2'", "x-=2"); fold("x = x ^ '2'", "x^=2"); fold("x = '2' ^ x", "x^=2"); fold("x = '2' & x", "x&=2"); fold("x = '2' | x", "x|=2"); fold("x = '2' | y", "x=2|y"); fold("x = y | '2'", "x=y|2"); fold("x = y | (a && '2')", "x=y|(a&&2)"); fold("x = y | (a,'2')", "x=y|(a,2)"); fold("x = y | (a?'1':'2')", "x=y|(a?1:2)"); fold("x = y | ('x'?'1':'2')", "x=y|('x'?1:2)"); } public void testFoldingMixTypesEarly() { late = false; foldSame("x = x + '2'"); fold("x = +x + +'2'", "x = +x + 2"); fold("x = x - '2'", "x = x - 2"); fold("x = x ^ '2'", "x = x ^ 2"); fold("x = '2' ^ x", "x = 2 ^ x"); fold("x = '2' & x", "x = 2 & x"); fold("x = '2' | x", "x = 2 | x"); fold("x = '2' | y", "x=2|y"); fold("x = y | '2'", "x=y|2"); fold("x = y | (a && '2')", "x=y|(a&&2)"); fold("x = y | (a,'2')", "x=y|(a,2)"); fold("x = y | (a?'1':'2')", "x=y|(a?1:2)"); fold("x = y | ('x'?'1':'2')", "x=y|('x'?1:2)"); } public void testFoldingAdd() { fold("x = null + true", "x=1"); foldSame("x = a + true"); } public void testFoldBitwiseOpStringCompare() { assertResultString("x = -1 | 0", "x=-1"); // EXPR_RESULT case is in in PeepholeIntegrationTest } public void testFoldBitShifts() { fold("x = 1 << 0", "x = 1"); fold("x = -1 << 0", "x = -1"); fold("x = 1 << 1", "x = 2"); fold("x = 3 << 1", "x = 6"); fold("x = 1 << 8", "x = 256"); fold("x = 1 >> 0", "x = 1"); fold("x = -1 >> 0", "x = -1"); fold("x = 1 >> 1", "x = 0"); fold("x = 2 >> 1", "x = 1"); fold("x = 5 >> 1", "x = 2"); fold("x = 127 >> 3", "x = 15"); fold("x = 3 >> 1", "x = 1"); fold("x = 3 >> 2", "x = 0"); fold("x = 10 >> 1", "x = 5"); fold("x = 10 >> 2", "x = 2"); fold("x = 10 >> 5", "x = 0"); fold("x = 10 >>> 1", "x = 5"); fold("x = 10 >>> 2", "x = 2"); fold("x = 10 >>> 5", "x = 0"); fold("x = -1 >>> 1", "x = 2147483647"); // 0x7fffffff fold("x = -1 >>> 0", "x = 4294967295"); // 0xffffffff fold("x = -2 >>> 0", "x = 4294967294"); // 0xfffffffe testSame("3000000000 << 1", PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); testSame("1 << 32", PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); testSame("1 << -1", PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); testSame("3000000000 >> 1", PeepholeFoldConstants.BITWISE_OPERAND_OUT_OF_RANGE); testSame("1 >> 32", PeepholeFoldConstants.SHIFT_AMOUNT_OUT_OF_BOUNDS); testSame("1.5 << 0", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); testSame("1 << .5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); testSame("1.5 >>> 0", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); testSame("1 >>> .5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); testSame("1.5 >> 0", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); testSame("1 >> .5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND); } public void testFoldBitShiftsStringCompare() { // Negative numbers. assertResultString("x = -1 << 1", "x=-2"); assertResultString("x = -1 << 8", "x=-256"); assertResultString("x = -1 >> 1", "x=-1"); assertResultString("x = -2 >> 1", "x=-1"); assertResultString("x = -1 >> 0", "x=-1"); } public void testStringAdd() { fold("x = 'a' + \"bc\"", "x = \"abc\""); fold("x = 'a' + 5", "x = \"a5\""); fold("x = 5 + 'a'", "x = \"5a\""); fold("x = 'a' + ''", "x = \"a\""); fold("x = \"a\" + foo()", "x = \"a\"+foo()"); fold("x = foo() + 'a' + 'b'", "x = foo()+\"ab\""); fold("x = (foo() + 'a') + 'b'", "x = foo()+\"ab\""); // believe it! fold("x = foo() + 'a' + 'b' + 'cd' + bar()", "x = foo()+\"abcd\"+bar()"); fold("x = foo() + 2 + 'b'", "x = foo()+2+\"b\""); // don't fold! fold("x = foo() + 'a' + 2", "x = foo()+\"a2\""); fold("x = '' + null", "x = \"null\""); fold("x = true + '' + false", "x = \"truefalse\""); fold("x = '' + []", "x = ''"); // cannot fold (but nice if we can) } public void testIssue821() { foldSame("var a =(Math.random()>0.5? '1' : 2 ) + 3 + 4;"); foldSame("var a = ((Math.random() ? 0 : 1) ||" + "(Math.random()>0.5? '1' : 2 )) + 3 + 4;"); } public void testFoldConstructor() { fold("x = this[new String('a')]", "x = this['a']"); fold("x = ob[new String(12)]", "x = ob['12']"); fold("x = ob[new String(false)]", "x = ob['false']"); fold("x = ob[new String(null)]", "x = ob['null']"); fold("x = 'a' + new String('b')", "x = 'ab'"); fold("x = 'a' + new String(23)", "x = 'a23'"); fold("x = 2 + new String(1)", "x = '21'"); foldSame("x = ob[new String(a)]"); foldSame("x = new String('a')"); foldSame("x = (new String('a'))[3]"); } public void testFoldArithmetic() { fold("x = 10 + 20", "x = 30"); fold("x = 2 / 4", "x = 0.5"); fold("x = 2.25 * 3", "x = 6.75"); fold("z = x * y", "z = x * y"); fold("x = y * 5", "x = y * 5"); fold("x = 1 / 0", "x = 1 / 0"); fold("x = 3 % 2", "x = 1"); fold("x = 3 % -2", "x = 1"); fold("x = -1 % 3", "x = -1"); fold("x = 1 % 0", "x = 1 % 0"); } public void testFoldArithmetic2() { foldSame("x = y + 10 + 20"); foldSame("x = y / 2 / 4"); fold("x = y * 2.25 * 3", "x = y * 6.75"); fold("z = x * y", "z = x * y"); fold("x = y * 5", "x = y * 5"); fold("x = y + (z * 24 * 60 * 60 * 1000)", "x = y + z * 864E5"); } public void testFoldArithmetic3() { fold("x = null * undefined", "x = NaN"); fold("x = null * 1", "x = 0"); fold("x = (null - 1) * 2", "x = -2"); fold("x = (null + 1) * 2", "x = 2"); } public void testFoldArithmeticInfinity() { fold("x=-Infinity-2", "x=-Infinity"); fold("x=Infinity-2", "x=Infinity"); fold("x=Infinity*5", "x=Infinity"); } public void testFoldArithmeticStringComp() { // Negative Numbers. assertResultString("x = 10 - 20", "x=-10"); } public void testFoldComparison() { fold("x = 0 == 0", "x = true"); fold("x = 1 == 2", "x = false"); fold("x = 'abc' == 'def'", "x = false"); fold("x = 'abc' == 'abc'", "x = true"); fold("x = \"\" == ''", "x = true"); fold("x = foo() == bar()", "x = foo()==bar()"); fold("x = 1 != 0", "x = true"); fold("x = 'abc' != 'def'", "x = true"); fold("x = 'a' != 'a'", "x = false"); fold("x = 1 < 20", "x = true"); fold("x = 3 < 3", "x = false"); fold("x = 10 > 1.0", "x = true"); fold("x = 10 > 10.25", "x = false"); fold("x = y == y", "x = y==y"); fold("x = y < y", "x = false"); fold("x = y > y", "x = false"); fold("x = 1 <= 1", "x = true"); fold("x = 1 <= 0", "x = false"); fold("x = 0 >= 0", "x = true"); fold("x = -1 >= 9", "x = false"); fold("x = true == true", "x = true"); fold("x = false == false", "x = true"); fold("x = false == null", "x = false"); fold("x = false == true", "x = false"); fold("x = true == null", "x = false"); fold("0 == 0", "true"); fold("1 == 2", "false"); fold("'abc' == 'def'", "false"); fold("'abc' == 'abc'", "true"); fold("\"\" == ''", "true"); foldSame("foo() == bar()"); fold("1 != 0", "true"); fold("'abc' != 'def'", "true"); fold("'a' != 'a'", "false"); fold("1 < 20", "true"); fold("3 < 3", "false"); fold("10 > 1.0", "true"); fold("10 > 10.25", "false"); foldSame("x == x"); fold("x < x", "false"); fold("x > x", "false"); fold("1 <= 1", "true"); fold("1 <= 0", "false"); fold("0 >= 0", "true"); fold("-1 >= 9", "false"); fold("true == true", "true"); fold("false == null", "false"); fold("false == true", "false"); fold("true == null", "false"); } // ===, !== comparison tests public void testFoldComparison2() { fold("x = 0 === 0", "x = true"); fold("x = 1 === 2", "x = false"); fold("x = 'abc' === 'def'", "x = false"); fold("x = 'abc' === 'abc'", "x = true"); fold("x = \"\" === ''", "x = true"); fold("x = foo() === bar()", "x = foo()===bar()"); fold("x = 1 !== 0", "x = true"); fold("x = 'abc' !== 'def'", "x = true"); fold("x = 'a' !== 'a'", "x = false"); fold("x = y === y", "x = y===y"); fold("x = true === true", "x = true"); fold("x = false === false", "x = true"); fold("x = false === null", "x = false"); fold("x = false === true", "x = false"); fold("x = true === null", "x = false"); fold("0 === 0", "true"); fold("1 === 2", "false"); fold("'abc' === 'def'", "false"); fold("'abc' === 'abc'", "true"); fold("\"\" === ''", "true"); foldSame("foo() === bar()"); // TODO(johnlenz): It would be nice to handle these cases as well. foldSame("1 === '1'"); foldSame("1 === true"); foldSame("1 !== '1'"); foldSame("1 !== true"); fold("1 !== 0", "true"); fold("'abc' !== 'def'", "true"); fold("'a' !== 'a'", "false"); foldSame("x === x"); fold("true === true", "true"); fold("false === null", "false"); fold("false === true", "false"); fold("true === null", "false"); } public void testFoldComparison3() { fold("x = !1 == !0", "x = false"); fold("x = !0 == !0", "x = true"); fold("x = !1 == !1", "x = true"); fold("x = !1 == null", "x = false"); fold("x = !1 == !0", "x = false"); fold("x = !0 == null", "x = false"); fold("!0 == !0", "true"); fold("!1 == null", "false"); fold("!1 == !0", "false"); fold("!0 == null", "false"); fold("x = !0 === !0", "x = true"); fold("x = !1 === !1", "x = true"); fold("x = !1 === null", "x = false"); fold("x = !1 === !0", "x = false"); fold("x = !0 === null", "x = false"); fold("!0 === !0", "true"); fold("!1 === null", "false"); fold("!1 === !0", "false"); fold("!0 === null", "false"); } public void testFoldGetElem() { fold("x = [,10][0]", "x = void 0"); fold("x = [10, 20][0]", "x = 10"); fold("x = [10, 20][1]", "x = 20"); testSame("x = [10, 20][0.5]", PeepholeFoldConstants.INVALID_GETELEM_INDEX_ERROR); testSame("x = [10, 20][-1]", PeepholeFoldConstants.INDEX_OUT_OF_BOUNDS_ERROR); testSame("x = [10, 20][2]", PeepholeFoldConstants.INDEX_OUT_OF_BOUNDS_ERROR); foldSame("x = [foo(), 0][1]"); fold("x = [0, foo()][1]", "x = foo()"); foldSame("x = [0, foo()][0]"); } public void testFoldComplex() { fold("x = (3 / 1.0) + (1 * 2)", "x = 5"); fold("x = (1 == 1.0) && foo() && true", "x = foo()&&true"); fold("x = 'abc' + 5 + 10", "x = \"abc510\""); } public void testFoldLeft() { foldSame("(+x - 1) + 2"); // not yet fold("(+x + 1) + 2", "+x + 3"); } public void testFoldArrayLength() { // Can fold fold("x = [].length", "x = 0"); fold("x = [1,2,3].length", "x = 3"); fold("x = [a,b].length", "x = 2"); // Not handled yet fold("x = [,,1].length", "x = 3"); // Cannot fold fold("x = [foo(), 0].length", "x = [foo(),0].length"); fold("x = y.length", "x = y.length"); } public void testFoldStringLength() { // Can fold basic strings. fold("x = ''.length", "x = 0"); fold("x = '123'.length", "x = 3"); // Test Unicode escapes are accounted for. fold("x = '123\u01dc'.length", "x = 4"); } public void testFoldTypeof() { fold("x = typeof 1", "x = \"number\""); fold("x = typeof 'foo'", "x = \"string\""); fold("x = typeof true", "x = \"boolean\""); fold("x = typeof false", "x = \"boolean\""); fold("x = typeof null", "x = \"object\""); fold("x = typeof undefined", "x = \"undefined\""); fold("x = typeof void 0", "x = \"undefined\""); fold("x = typeof []", "x = \"object\""); fold("x = typeof [1]", "x = \"object\""); fold("x = typeof [1,[]]", "x = \"object\""); fold("x = typeof {}", "x = \"object\""); fold("x = typeof function() {}", "x = 'function'"); foldSame("x = typeof[1,[foo()]]"); foldSame("x = typeof{bathwater:baby()}"); } public void testFoldInstanceOf() { // Non object types are never instances of anything. fold("64 instanceof Object", "false"); fold("64 instanceof Number", "false"); fold("'' instanceof Object", "false"); fold("'' instanceof String", "false"); fold("true instanceof Object", "false"); fold("true instanceof Boolean", "false"); fold("!0 instanceof Object", "false"); fold("!0 instanceof Boolean", "false"); fold("false instanceof Object", "false"); fold("null instanceof Object", "false"); fold("undefined instanceof Object", "false"); fold("NaN instanceof Object", "false"); fold("Infinity instanceof Object", "false"); // Array and object literals are known to be objects. fold("[] instanceof Object", "true"); fold("({}) instanceof Object", "true"); // These cases is foldable, but no handled currently. foldSame("new Foo() instanceof Object"); // These would require type information to fold. foldSame("[] instanceof Foo"); foldSame("({}) instanceof Foo"); fold("(function() {}) instanceof Object", "true"); // An unknown value should never be folded. foldSame("x instanceof Foo"); } public void testDivision() { // Make sure the 1/3 does not expand to 0.333333 fold("print(1/3)", "print(1/3)"); // Decimal form is preferable to fraction form when strings are the // same length. fold("print(1/2)", "print(0.5)"); } public void testAssignOpsLate() { late = true; fold("x=x+y", "x+=y"); foldSame("x=y+x"); fold("x=x*y", "x*=y"); fold("x=y*x", "x*=y"); fold("x.y=x.y+z", "x.y+=z"); foldSame("next().x = next().x + 1"); fold("x=x-y", "x-=y"); foldSame("x=y-x"); fold("x=x|y", "x|=y"); fold("x=y|x", "x|=y"); fold("x=x*y", "x*=y"); fold("x=y*x", "x*=y"); fold("x.y=x.y+z", "x.y+=z"); foldSame("next().x = next().x + 1"); // This is OK, really. fold("({a:1}).a = ({a:1}).a + 1", "({a:1}).a = 2"); } public void testAssignOpsEarly() { late = false; foldSame("x=x+y"); foldSame("x=y+x"); foldSame("x=x*y"); foldSame("x=y*x"); foldSame("x.y=x.y+z"); foldSame("next().x = next().x + 1"); foldSame("x=x-y"); foldSame("x=y-x"); foldSame("x=x|y"); foldSame("x=y|x"); foldSame("x=x*y"); foldSame("x=y*x"); foldSame("x.y=x.y+z"); foldSame("next().x = next().x + 1"); // This is OK, really. fold("({a:1}).a = ({a:1}).a + 1", "({a:1}).a = 2"); } public void testFoldAdd1() { fold("x=false+1","x=1"); fold("x=true+1","x=2"); fold("x=1+false","x=1"); fold("x=1+true","x=2"); } public void testFoldLiteralNames() { foldSame("NaN == NaN"); foldSame("Infinity == Infinity"); foldSame("Infinity == NaN"); fold("undefined == NaN", "false"); fold("undefined == Infinity", "false"); foldSame("Infinity >= Infinity"); foldSame("NaN >= NaN"); } public void testFoldLiteralsTypeMismatches() { fold("true == true", "true"); fold("true == false", "false"); fold("true == null", "false"); fold("false == null", "false"); // relational operators convert its operands fold("null <= null", "true"); // 0 = 0 fold("null >= null", "true"); fold("null > null", "false"); fold("null < null", "false"); fold("false >= null", "true"); // 0 = 0 fold("false <= null", "true"); fold("false > null", "false"); fold("false < null", "false"); fold("true >= null", "true"); // 1 > 0 fold("true <= null", "false"); fold("true > null", "true"); fold("true < null", "false"); fold("true >= false", "true"); // 1 > 0 fold("true <= false", "false"); fold("true > false", "true"); fold("true < false", "false"); } public void testFoldLeftChildConcat() { foldSame("x +5 + \"1\""); fold("x+\"5\" + \"1\"", "x + \"51\""); // fold("\"a\"+(c+\"b\")","\"a\"+c+\"b\""); fold("\"a\"+(\"b\"+c)","\"ab\"+c"); } public void testFoldLeftChildOp() { fold("x * Infinity * 2", "x * Infinity"); foldSame("x - Infinity - 2"); // want "x-Infinity" foldSame("x - 1 + Infinity"); foldSame("x - 2 + 1"); foldSame("x - 2 + 3"); foldSame("1 + x - 2 + 1"); foldSame("1 + x - 2 + 3"); foldSame("1 + x - 2 + 3 - 1"); foldSame("f(x)-0"); foldSame("x-0-0"); foldSame("x+2-2+2"); foldSame("x+2-2+2-2"); foldSame("x-2+2"); foldSame("x-2+2-2"); foldSame("x-2+2-2+2"); foldSame("1+x-0-NaN"); foldSame("1+f(x)-0-NaN"); foldSame("1+x-0+NaN"); foldSame("1+f(x)-0+NaN"); foldSame("1+x+NaN"); // unfoldable foldSame("x+2-2"); // unfoldable foldSame("x+2"); // nothing to do foldSame("x-2"); // nothing to do } public void testFoldSimpleArithmeticOp() { foldSame("x*NaN"); foldSame("NaN/y"); foldSame("f(x)-0"); foldSame("f(x)*1"); foldSame("1*f(x)"); foldSame("0+a+b"); foldSame("0-a-b"); foldSame("a+b-0"); foldSame("(1+x)*NaN"); foldSame("(1+f(x))*NaN"); // don't fold side-effects } public void testFoldLiteralsAsNumbers() { fold("x/'12'","x/12"); fold("x/('12'+'6')", "x/126"); fold("true*x", "1*x"); fold("x/false", "x/0"); // should we add an error check? :) } public void testNotFoldBackToTrueFalse() { late = false; fold("!0", "true"); fold("!1", "false"); fold("!3", "false"); late = true; foldSame("!0"); foldSame("!1"); fold("!3", "false"); foldSame("false"); foldSame("true"); } public void testFoldBangConstants() { fold("1 + !0", "2"); fold("1 + !1", "1"); fold("'a ' + !1", "'a false'"); fold("'a ' + !0", "'a true'"); } public void testFoldMixed() { fold("''+[1]", "'1'"); foldSame("false+[]"); // would like: "\"false\"" } public void testFoldVoid() { foldSame("void 0"); fold("void 1", "void 0"); fold("void x", "void 0"); fold("void x()", "void x()"); } public void testObjectLiteral() { test("(!{})", "false"); test("(!{a:1})", "false"); testSame("(!{a:foo()})"); testSame("(!{'a':foo()})"); } public void testArrayLiteral() { test("(![])", "false"); test("(![1])", "false"); test("(![a])", "false"); testSame("(![foo()])"); } public void testIssue601() { testSame("'\\v' == 'v'"); testSame("'v' == '\\v'"); testSame("'\\u000B' == '\\v'"); } public void testFoldObjectLiteralRef1() { // Leave extra side-effects in place testSame("var x = ({a:foo(),b:bar()}).a"); testSame("var x = ({a:1,b:bar()}).a"); testSame("function f() { return {b:foo(), a:2}.a; }"); // on the LHS the object act as a temporary leave it in place. testSame("({a:x}).a = 1"); test("({a:x}).a += 1", "({a:x}).a = x + 1"); testSame("({a:x}).a ++"); testSame("({a:x}).a --"); // functions can't reference the object through 'this'. testSame("({a:function(){return this}}).a"); testSame("({get a() {return this}}).a"); testSame("({set a(b) {return this}}).a"); // Leave unknown props alone, the might be on the prototype testSame("({}).a"); // setters by themselves don't provide a definition testSame("({}).a"); testSame("({set a(b) {}}).a"); // sets don't hide other definitions. test("({a:1,set a(b) {}}).a", "1"); // get is transformed to a call (gets don't have self referential names) test("({get a() {}}).a", "(function (){})()"); // sets don't hide other definitions. test("({get a() {},set a(b) {}}).a", "(function (){})()"); // a function remains a function not a call. test("var x = ({a:function(){return 1}}).a", "var x = function(){return 1}"); test("var x = ({a:1}).a", "var x = 1"); test("var x = ({a:1, a:2}).a", "var x = 2"); test("var x = ({a:1, a:foo()}).a", "var x = foo()"); test("var x = ({a:foo()}).a", "var x = foo()"); test("function f() { return {a:1, b:2}.a; }", "function f() { return 1; }"); // GETELEM is handled the same way. test("var x = ({'a':1})['a']", "var x = 1"); } public void testFoldObjectLiteralRef2() { late = false; test("({a:x}).a += 1", "({a:x}).a = x + 1"); late = true; testSame("({a:x}).a += 1"); } public void testIEString() { testSame("!+'\\v1'"); } public void testIssue522() { testSame("[][1] = 1;"); } private static final List LITERAL_OPERANDS = ImmutableList.of( "null", "undefined", "void 0", "true", "false", "!0", "!1", "0", "1", "''", "'123'", "'abc'", "'def'", "NaN", "Infinity", // TODO(nicksantos): Add more literals "-Infinity" //"({})", // "[]" //"[0]", //"Object", //"(function() {})" ); public void testInvertibleOperators() { Map inverses = ImmutableMap.builder() .put("==", "!=") .put("===", "!==") .put("<=", ">") .put("<", ">=") .put(">=", "<") .put(">", "<=") .put("!=", "==") .put("!==", "===") .build(); Set comparators = ImmutableSet.of("<=", "<", ">=", ">"); Set equalitors = ImmutableSet.of("==", "==="); Set uncomparables = ImmutableSet.of("undefined", "void 0"); List operators = ImmutableList.copyOf(inverses.values()); for (int iOperandA = 0; iOperandA < LITERAL_OPERANDS.size(); iOperandA++) { for (int iOperandB = 0; iOperandB < LITERAL_OPERANDS.size(); iOperandB++) { for (int iOp = 0; iOp < operators.size(); iOp++) { String a = LITERAL_OPERANDS.get(iOperandA); String b = LITERAL_OPERANDS.get(iOperandB); String op = operators.get(iOp); String inverse = inverses.get(op); // Test invertability. if (comparators.contains(op) && (uncomparables.contains(a) || uncomparables.contains(b))) { assertSameResults(join(a, op, b), "false"); assertSameResults(join(a, inverse, b), "false"); } else if (a.equals(b) && equalitors.contains(op)) { if (a.equals("NaN") || a.equals("Infinity") || a.equals("-Infinity")) { foldSame(join(a, op, b)); foldSame(join(a, inverse, b)); } else { assertSameResults(join(a, op, b), "true"); assertSameResults(join(a, inverse, b), "false"); } } else { assertNotSameResults(join(a, op, b), join(a, inverse, b)); } } } } } public void testCommutativeOperators() { late = true; List operators = ImmutableList.of( "==", "!=", "===", "!==", "*", "|", "&", "^"); for (int iOperandA = 0; iOperandA < LITERAL_OPERANDS.size(); iOperandA++) { for (int iOperandB = iOperandA; iOperandB < LITERAL_OPERANDS.size(); iOperandB++) { for (int iOp = 0; iOp < operators.size(); iOp++) { String a = LITERAL_OPERANDS.get(iOperandA); String b = LITERAL_OPERANDS.get(iOperandB); String op = operators.get(iOp); // Test commutativity. // TODO(nicksantos): Eventually, all cases should be collapsed. assertSameResultsOrUncollapsed(join(a, op, b), join(b, op, a)); } } } } public void testConvertToNumberNegativeInf() { foldSame("var x = 3 * (r ? Infinity : -Infinity);"); } private String join(String operandA, String op, String operandB) { return operandA + " " + op + " " + operandB; } private void assertSameResultsOrUncollapsed(String exprA, String exprB) { String resultA = process(exprA); if (resultA.equals(print(exprA))) { foldSame(exprA); foldSame(exprB); } else { assertSameResults(exprA, exprB); } } private void assertSameResults(String exprA, String exprB) { assertEquals( "Expressions did not fold the same\nexprA: " + exprA + "\nexprB: " + exprB, process(exprA), process(exprB)); } private void assertNotSameResults(String exprA, String exprB) { assertFalse( "Expressions folded the same\nexprA: " + exprA + "\nexprB: " + exprB, process(exprA).equals(process(exprB))); } private String process(String js) { return printHelper(js, true); } private String print(String js) { return printHelper(js, false); } private String printHelper(String js, boolean runProcessor) { Compiler compiler = createCompiler(); CompilerOptions options = getOptions(); compiler.init( ImmutableList.of(), ImmutableList.of(SourceFile.fromCode("testcode", js)), options); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()) + "\nEXPR: " + js, root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); if (runProcessor) { getProcessor(compiler).process(externsRoot, mainRoot); } return compiler.toSource(mainRoot); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ClosureRewriteClassTest.java0000644000175000017500000001722212115204405030477 0ustar apoapo/* * Copyright 2012 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.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_CONSTRUCTOR_MISING; import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_DESCRIPTOR_NOT_VALID; import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_STATICS_NOT_VALID; import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_SUPER_CLASS_NOT_VALID; import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_TARGET_INVALID; import static com.google.javascript.jscomp.ClosureRewriteClass.GOOG_CLASS_UNEXPECTED_PARAMS; /** * Unit tests for ClosureRewriteGoogClass * @author johnlenz@google.com (John Lenz) */ public class ClosureRewriteClassTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new ClosureRewriteClass(compiler); } @Override protected void setUp() throws Exception { super.setUp(); this.enableEcmaScript5(false); } @Override protected int getNumRepetitions() { return 1; } public void testBasic1() { test( "var x = goog.defineClass(null, {\n" + " constructor: function(){}\n" + "});", "{var x = function() {};}"); } public void testBasic2() { test( "var x = {};\n" + "x.y = goog.defineClass(null, {\n" + " constructor: function(){}\n" + "});", "var x = {};" + "{x.y = function() {};}"); } public void testBasic3() { test( "var x = goog.labs.classdef.defineClass(null, {\n" + " constructor: function(){}\n" + "});", "{var x = function() {};}"); } public void testInnerClass1() { test( "var x = goog.defineClass(some.Super, {\n" + " constructor: function(){\n" + " this.foo = 1;\n" + " },\n" + " statics: {\n" + " inner: goog.defineClass(x,{\n" + " constructor: function(){\n" + " this.bar = 1;\n" + " }\n" + " })\n" + " }\n" + "});", "{" + "var x=function(){this.foo=1};" + "goog.inherits(x,some.Super);" + "{" + "x.inner=function(){this.bar=1};" + "goog.inherits(x.inner,x);" + "}" + "}"); } public void testComplete1() { test( "var x = goog.defineClass(some.Super, {\n" + " constructor: function(){\n" + " this.foo = 1;\n" + " },\n" + " statics: {\n" + " prop1: 1,\n" + " /** @const */\n" + " PROP2: 2\n" + " },\n" + " anotherProp: 1,\n" + " aMethod: function() {}\n" + "});", "{" + "var x=function(){this.foo=1};" + "goog.inherits(x,some.Super);" + "x.prop1=1;" + "x.PROP2=2;" + "x.prototype.anotherProp=1;" + "x.prototype.aMethod=function(){};" + "}"); } public void testComplete2() { test( "x.y = goog.defineClass(some.Super, {\n" + " constructor: function(){\n" + " this.foo = 1;\n" + " },\n" + " statics: {\n" + " prop1: 1,\n" + " /** @const */\n" + " PROP2: 2\n" + " },\n" + " anotherProp: 1,\n" + " aMethod: function() {}\n" + "});", "{\n" + "/** @constructor */\n" + "x.y=function(){this.foo=1};\n" + "goog.inherits(x.y,some.Super);" + "x.y.prop1=1;\n" + "/** @const */\n" + "x.y.PROP2=2;\n" + "x.y.prototype.anotherProp=1;" + "x.y.prototype.aMethod=function(){};" + "}"); } public void testClassWithStaticInitFn() { test( "x.y = goog.defineClass(some.Super, {\n" + " constructor: function(){\n" + " this.foo = 1;\n" + " },\n" + " statics: function(cls) {\n" + " cls.prop1 = 1;\n" + " /** @const */\n" + " cls.PROP2 = 2;\n" + " },\n" + " anotherProp: 1,\n" + " aMethod: function() {}\n" + "});", "{\n" + "/** @constructor */\n" + "x.y=function(){this.foo=1};\n" + "goog.inherits(x.y,some.Super);" + "x.y.prototype.anotherProp=1;" + "x.y.prototype.aMethod=function(){};" + "(function(cls) {" + " cls.prop1=1;\n" + " /** @const */\n" + " cls.PROP2=2;" + "})(x.y);\n" + "}"); } public void testInvalid1() { testSame( "var x = goog.defineClass();", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); testSame( "var x = goog.defineClass('foo');", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); testSame( "var x = goog.defineClass(foo());", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); testSame( "var x = goog.defineClass({'foo':1});", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); testSame( "var x = goog.defineClass({1:1});", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); this.enableEcmaScript5(true); testSame( "var x = goog.defineClass({get foo() {return 1}});", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); testSame( "var x = goog.defineClass({set foo(a) {}});", GOOG_CLASS_SUPER_CLASS_NOT_VALID, true); } public void testInvalid2() { testSame( "var x = goog.defineClass(null);", GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); testSame( "var x = goog.defineClass(null, null);", GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); testSame( "var x = goog.defineClass(null, foo());", GOOG_CLASS_DESCRIPTOR_NOT_VALID, true); } public void testInvalid3() { testSame( "var x = goog.defineClass(null, {});", GOOG_CLASS_CONSTRUCTOR_MISING, true); } public void testInvalid4() { testSame( "var x = goog.defineClass(null, {" + " constructor: function(){}," + " statics: null" + "});", GOOG_CLASS_STATICS_NOT_VALID, true); testSame( "var x = goog.defineClass(null, {" + " constructor: function(){}," + " statics: foo" + "});", GOOG_CLASS_STATICS_NOT_VALID, true); testSame( "var x = goog.defineClass(null, {" + " constructor: function(){}," + " statics: {'foo': 1}" + "});", GOOG_CLASS_STATICS_NOT_VALID, true); testSame( "var x = goog.defineClass(null, {" + " constructor: function(){}," + " statics: {1: 1}" + "});", GOOG_CLASS_STATICS_NOT_VALID, true); } public void testInvalid5() { testSame( "var x = goog.defineClass(null, {" + " constructor: function(){}" + "}, null);", GOOG_CLASS_UNEXPECTED_PARAMS, true); } public void testInvalid6() { testSame( "goog.defineClass();", GOOG_CLASS_TARGET_INVALID, true); testSame( "var x = goog.defineClass() || null;", GOOG_CLASS_TARGET_INVALID, true); testSame( "({foo: goog.defineClass()});", GOOG_CLASS_TARGET_INVALID, true); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SideEffectsAnalysisTest.java0000644000175000017500000004025212115204405030422 0ustar apoapo/* * 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.common.collect.ImmutableSet; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.SideEffectsAnalysis.LocationAbstractionMode; import com.google.javascript.rhino.Node; /** * Tests for {@link SideEffectsAnalysis}. * * @author dcc@google.com (Devin Coughlin) * */ public class SideEffectsAnalysisTest extends CompilerTestCase { private static final String SHARED_EXTERNS = "var arguments = [];"; LocationAbstractionMode currentLocationAbstractionIdentifier; SideEffectsAnalysis currentAnalysis = null; Compiler currentCompiler = null; Node currentJsRoot = null; @Override protected CompilerPass getProcessor(final Compiler compiler) { currentCompiler = compiler; currentAnalysis = new SideEffectsAnalysis(compiler, currentLocationAbstractionIdentifier); return new CompilerPass() { @Override public void process(Node externs, Node root) { if (currentLocationAbstractionIdentifier == LocationAbstractionMode.VISIBILITY_BASED) { // Run var when using the visibility abstraction // because it is unsound if it fails. final VarCheck varCheck = new VarCheck(compiler); varCheck.process(externs, root); } currentAnalysis.process(externs, root); } }; } @Override public void setUp() throws Exception { super.setUp(); currentAnalysis = null; currentCompiler = null; } public void testDegenerateSafeMoves() { // Env is empty assertSafeMoveDegenerate("src: 1; env: ; dest: 3;"); // Src and env pure assertSafeMoveDegenerate("src: 1; env: 2; dest: 3;"); // Only refs assertSafeMoveDegenerate("src: 1; env: x; dest: 3;"); assertSafeMoveDegenerate("src: x; env: 1; dest: 3;"); // Only mods assertSafeMoveDegenerate("src: 1; env: x++; dest: 3;"); assertSafeMoveDegenerate("src: x++; env: 1; dest: 3;"); } public void testVisibilitySafeMoves() { // Env is empty assertSafeMoveVisibility("src: 1; env: ; dest: 3;"); // Src and env pure assertSafeMoveVisibility("src: 1; env: 2; dest: 3;"); // Only refs assertSafeMoveVisibility("var x; src: 1; env: x; dest: 3;"); assertSafeMoveVisibility("var x; src: x; env: 1; dest: 3;"); // Only mods assertSafeMoveVisibility("var x; src: 1; env: x++; dest: 3;"); assertSafeMoveVisibility("var x; src: x++; env: 1; dest: 3;"); // Source references global, env changes local assertSafeMoveVisibility( "var x;" + "function f(){" + "var y;" + "src: x;" + "env: y++;" + "dest: 3;" + "}"); // Source changes global, env refs local assertSafeMoveVisibility( "var x;" + "function f(){" + "var y;" + "src: x++;" + "env: y;" + "dest: 3;" + "}"); // Source references global, env changes local with shadowing assertSafeMoveVisibility( "var x;" + "var y;" + "function f(){" + "var y;" + "src: x;" + "env: y++;" + "dest: 3;" + "}"); // Source changes global, env refs local with shadowing assertSafeMoveVisibility( "var x;" + "var y;" + "function f(){" + "var y;" + "src: x++;" + "env: y;" + "dest: 3;" + "}"); // Source references captured local, env changes local assertSafeMoveVisibility( "function f(){" + "var x;" + "var y;" + "src: x;" + "env: y++;" + "dest: 3;" + "function inner() {" + "x" + "}" + "}"); // Source changes captured local, env refs local assertSafeMoveVisibility( "function f(){" + "var x;" + "var y;" + "src: x++;" + "env: y;" + "dest: 3;" + "function inner() {" + "x" + "}" + "}"); // Source references heap, env changes local assertSafeMoveVisibility( "var x = {};" + "function f(){" + "var y;" + "src: x.a;" + "env: y++;" + "dest: 3;" + "}"); // Source changes heap, env refs local assertSafeMoveVisibility( "var x = {};" + "function f(){" + "var y;" + "src: x.a++;" + "env: y;" + "dest: 3;" + "}"); // MOD in function expressions shouldn't count assertSafeMoveVisibility( "var x = {};" + "src: x.a;" + "env: (function() {" + "x.a++;" + "});" + "dest: 3;"); // REF in function expressions shouldn't count assertSafeMoveVisibility( "var x = {};" + "src: x.a++;" + "env: (function() {" + "x.a;" + "});" + "dest: 3;"); } public void testDegenerateUnsafeMoves() { // Unsafe to move increment across read assertUnsafeMoveDegenerate("src: x++; env: foo(y); dest: 3;"); // Unsafe to move read across increment assertUnsafeMoveDegenerate("src: foo(y); env: x++; dest: 3;"); // Unsafe to move write across write assertUnsafeMoveDegenerate("src: x = 7; env: y = 3; dest:3;"); } public void testVisibilityUnsafeMoves() { // Unsafe to move increment across read for global variables assertUnsafeMoveVisibility("var x,y; src: x++; env: y; dest: 3;"); // Unsafe to move increment across read for local variables assertUnsafeMoveVisibility("function f() {" + "var x,y; src: x++; env: y; dest: 3;" + "}"); // Unsafe to move increment across read for captured local variables assertUnsafeMoveVisibility( "function f() {" + "var x,y; src: x++; env: y; dest: 3;" + "function inner() {" + "x; y;" + "}" + "}"); // Unsafe to move increment across read for heap locations assertUnsafeMoveVisibility("var x,y; src: x.a++; env: y.b; dest: 3;"); // Unsafe to move read across increment of for global variables assertUnsafeMoveVisibility("var x,y; src: y; env: x++; dest: 3;"); // Unsafe to move read across increment for local variables assertUnsafeMoveVisibility("function f() {" + "var x,y; src: x; env: y++; dest: 3;" + "}"); // Unsafe to move read across increment for captured local variables assertUnsafeMoveVisibility( "function f() {" + "var x,y; src: x; env: y++; dest: 3;" + "function inner() {" + "x; y;" + "}" + "}"); // Unsafe to move read across increment for heap locations assertUnsafeMoveVisibility("var x,y; src: x.a; env: y.b++; dest: 3;"); // Unsafe to move write across write for globals assertUnsafeMoveVisibility("var x,y; src: x = 7; env: y = 3; dest: 3;"); // Unsafe to move write across write for local variables assertUnsafeMoveVisibility("function f() {" + "var x,y; src: x = 7; env: y = 3; dest: 3;" + "}"); // Unsafe to move write across write for captured local variables assertUnsafeMoveVisibility( "function f() {" + "var x,y; src: x = 7; env: y = 3; dest: 3;" + "function inner() {" + "x; y;" + "}" + "}"); // Unsafe to move write across write for heap locations assertUnsafeMoveVisibility("var x,y; src: x.a = 7; env: y.b = 3; dest: 3;"); } public void testVisibilityMoveCalls() { // Interprocedural side effect analysis isn't implemented yet, so any calls // should make movement unsafe, since we don't know what those calls are // doing. // TODO(dcc): implement interprocedural side effect analysis. // Source makes call, env refs global assertUnsafeMoveVisibility( "var x = {};" + "var g = function(){};" + "function f(){" + "var y;" + "src: g();" + "env: x;" + "dest: 3;" + "}"); // Source makes refs global, env makes call assertUnsafeMoveVisibility( "var x = {};" + "var g = function(){};" + "function f(){" + "var y;" + "src: x;" + "env: g();" + "dest: 3;" + "}"); } public void testVisibilityMergesParametersWithHeap() { // For now, we expect the visibility based location abstraction // to merge parameter variable locations with heap locations because // parameters can be references and modified via the arguments object. // Source changes heap, env refs parameter assertUnsafeMoveVisibility( "var x = {};" + "function f(y){" + "src: x[0]++;" + "env: y;" + "dest: 3;" + "}"); // Source refs heap, env changes parameters assertUnsafeMoveVisibility( "var x = {};" + "function f(y){" + "src: x[0];" + "env: y++;" + "dest: 3;" + "}"); // Source changes arguments explicitly, env refs parameter assertUnsafeMoveVisibility( "var x = {};" + "function f(y){" + "src: arguments[0]++;" + "env: y;" + "dest: 3;" + "}"); // Source refs arguments explicitly, env changes parameter assertUnsafeMoveVisibility( "var x = {};" + "function f(y){" + "src: arguments[0];" + "env: y++;" + "dest: 3;" + "}"); } public void testMovedSideEffectsMustHaveSameControlFlow() { // Safe to move within IF block assertSafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "if (l) {" + "src: a++;" + "env: 3;" + "dest: 3;" + "}" + "}" ); // Unsafe to move between two IF blocks assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "if (l) {" + "src: a++;" + "env: 3;" + "}" + "if (l) {" + "dest: 3;" + "}" + "}" ); // Unsafe to move between then/else of same IF block assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "if (l) {" + "src: a++;" + "env: 3;" + "} else {" + "dest: 3;" + "}" + "}" ); // Safe to move within WHILE block assertSafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "while (l) {" + "src: a++;" + "env: 3;" + "dest: 3;" + "}" + "}" ); // Unsafe to move within WHILE block with BREAK assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "while (l) {" + "src: a++;" + "env: l;" + "break;" + "dest: 3;" + "}" + "}" ); // Unsafe to move within WHILE block with continue assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "while (l) {" + "src: a++;" + "env: 3;" + "continue;" + "dest: 3;" + "}" + "}" ); // Unsafe to move within WHILE block with continue assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "while (l) {" + "src: a++;" + "env: 3;" + "return;" + "dest: 3;" + "}" + "}" ); // Safe to move within DO assertSafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "do {" + "src: a++;" + "env: 3;" + "dest: 3;" + "} while(l)" + "}" ); // Unsafe to move outside DO assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "do {" + "src: a++;" + "env: 3;" + "} while(l)" + "dest: 3;" + "}" ); // It should be safe to move within CASE // but we disallow for now because analyzing // CASE fall-through and BREAKs is complicated. assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "switch(l) {" + "case 17:" + "src: a++;" + "env: 3;" + "dest: 3;" + "break;" + "}" + "}" ); // Unsafe to move between CASEs assertUnsafeMoveVisibility( "var a;" + "function f() {" + "var l;" + "switch(l) {" + "case 17:" + "src: a++;" + "env: 3;" + "break;" + "case 18:" + "dest: 3;" + "break;" + "}" + "}" ); // Unsafe to move between FUNCTIONs assertUnsafeMoveVisibility( "var a;" + "function f() {" + "src: a++;" + "env: 3;" + "}" + "function g() {" + "dest: 3;" + "}" ); } private SideEffectsAnalysis.AbstractMotionEnvironment environment( Node ...nodes) { return new SideEffectsAnalysis.RawMotionEnvironment( ImmutableSet.copyOf(nodes)); } private void assertMove(LocationAbstractionMode abstraction, String src, boolean expected) { SideEffectsAnalysis analysis = compileAndRun(src, abstraction); Node sourceNode = findLabeledStatement("src"); Node environmentNode = findLabeledStatement("env"); Node destinationNode = findLabeledStatement("dest"); boolean result = analysis.safeToMoveBefore(sourceNode, environment(environmentNode), destinationNode); if (expected) { assertTrue(result); } else { assertFalse(result); } } private void assertSafeMoveDegenerate(String src) { assertMove(LocationAbstractionMode.DEGENERATE, src, true); } private void assertUnsafeMoveDegenerate(String src) { assertMove(LocationAbstractionMode.DEGENERATE, src, false); } private void assertSafeMoveVisibility(String src) { assertMove(LocationAbstractionMode.VISIBILITY_BASED, src, true); } private void assertUnsafeMoveVisibility(String src) { assertMove(LocationAbstractionMode.VISIBILITY_BASED, src, false); } private SideEffectsAnalysis compileAndRun(String js, LocationAbstractionMode locationAbstractionIdentifier) { currentLocationAbstractionIdentifier = locationAbstractionIdentifier; testSame(SHARED_EXTERNS, js, null); currentJsRoot = currentCompiler.jsRoot; return currentAnalysis; } // Shamelessly stolen from NameReferenceGraphConstructionTest private Node findLabeledStatement(String label) { LabeledStatementSearcher s = new LabeledStatementSearcher(label); new NodeTraversal(currentCompiler, s).traverse(currentCompiler.jsRoot); assertNotNull("Label " + label + " should be in the source code", s.found); return s.found; } /** * Quick traversal to find a given labeled statement in the AST. * * Given "foo", finds the statement a = x in * foo: a = x; */ private class LabeledStatementSearcher extends AbstractPostOrderCallback { Node found = null; final String target; LabeledStatementSearcher(String target) { this.target = target; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isLabel() && target.equals(n.getFirstChild().getString())) { found = n.getLastChild(); } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DataFlowAnalysisTest.java0000644000175000017500000005566612115204405027756 0ustar apoapo/* * 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.common.collect.Maps; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.DataFlowAnalysis.BranchedFlowState; import com.google.javascript.jscomp.DataFlowAnalysis.BranchedForwardDataFlowAnalysis; import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; import com.google.javascript.jscomp.DataFlowAnalysis.MaxIterationsExceededException; import com.google.javascript.jscomp.JoinOp.BinaryJoinOp; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import junit.framework.TestCase; import java.util.Comparator; import java.util.List; import java.util.Map; /** * A test suite with a very small programming language that has two types of * instructions: {@link BranchInstruction} and {@link ArithmeticInstruction}. * Test cases must construct a small program with these instructions and * manually put each instruction in a {@code ControlFlowGraph}. * */ public class DataFlowAnalysisTest extends TestCase { /** * Operations supported by ArithmeticInstruction. */ enum Operation { ADD("+"), SUB("-"), DIV("/"), MUL("*"); private final String stringRep; private Operation(String stringRep) { this.stringRep = stringRep; } @Override public String toString() { return stringRep; } } /** * A simple value. */ abstract static class Value { boolean isNumber() { return this instanceof Number; } boolean isVariable() { return this instanceof Variable; } } /** * A variable. */ static class Variable extends Value { private String name; /** * Constructor. * * @param n Name of the variable. */ Variable(String n) { name = n; } String getName() { return name; } @Override public boolean equals(Object other) { // Use the String's .equals() if (!(other instanceof Variable)) { return false; } return ((Variable) other).name.equals(name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return this.name; } } /** * A number constant. */ static class Number extends Value { private int value; /** * Constructor * * @param v Value */ Number(int v) { value = v; } int getValue() { return value; } @Override public String toString() { return "" + value; } @Override public int hashCode() { return value; } } /** * An instruction of the dummy program. */ abstract static class Instruction { int order = 0; /** * Check whether this is an arithmetic instruction. * * @return {@code true} if it is an arithmetic instruction. */ boolean isArithmetic() { return this instanceof ArithmeticInstruction; } /** * Check whether this is a branch instruction. * * @return {@code true} if it is a branch instruction. */ boolean isBranch() { return this instanceof BranchInstruction; } } /** * Basic arithmetic instruction that only takes the form of: * *
   * Result = Operand1 operator Operand2
   * 
*/ static class ArithmeticInstruction extends Instruction { private Operation operation; private Value operand1; private Value operand2; private Variable result; /** * Constructor * * @param res Result. * @param op1 First Operand. * @param o Operator. * @param op2 Second Operand. */ ArithmeticInstruction(Variable res, int op1, Operation o, int op2) { this(res, new Number(op1), o, new Number(op2)); } /** * Constructor * * @param res Result. * @param op1 First Operand. * @param o Operator. * @param op2 Second Operand. */ ArithmeticInstruction(Variable res, Value op1, Operation o, int op2) { this(res, op1, o, new Number(op2)); } /** * Constructor * * @param res Result. * @param op1 First Operand. * @param o Operator. * @param op2 Second Operand. */ ArithmeticInstruction(Variable res, int op1, Operation o, Value op2) { this(res, new Number(op1), o, op2); } /** * Constructor * * @param res Result. * @param op1 First Operand. * @param o Operator. * @param op2 Second Operand. */ ArithmeticInstruction(Variable res, Value op1, Operation o, Value op2) { result = res; operand1 = op1; operand2 = op2; operation = o; } Operation getOperator() { return operation; } void setOperator(Operation op) { this.operation = op; } Value getOperand1() { return operand1; } void setOperand1(Value operand1) { this.operand1 = operand1; } Value getOperand2() { return operand2; } void setOperand2(Value operand2) { this.operand2 = operand2; } Variable getResult() { return result; } void setResult(Variable result) { this.result = result; } @Override public String toString() { StringBuilder out = new StringBuilder(); out.append(result); out.append(" = "); out.append(operand1); out.append(operation); out.append(operand2); return out.toString(); } @Override public int hashCode() { return toString().hashCode(); } } public static ArithmeticInstruction newAssignNumberToVariableInstruction(Variable res, int num) { return new ArithmeticInstruction(res, num, Operation.ADD, 0); } public static ArithmeticInstruction newAssignVariableToVariableInstruction(Variable lhs, Variable rhs) { return new ArithmeticInstruction(lhs, rhs, Operation.ADD, 0); } /** * Branch instruction based on a {@link Value} as a condition. */ static class BranchInstruction extends Instruction { private Value condition; BranchInstruction(Value cond) { condition = cond; } Value getCondition() { return condition; } void setCondition(Value condition) { this.condition = condition; } } /** * A lattice to represent constant states. Each variable of the program will * have a lattice defined as: * *
   *        TOP
   *   / / |         \
   *  0  1 2 3 ..... MAX_VALUE
   *  \  \ |         /
   *       BOTTOM
   * 
* * Where BOTTOM represents the variable is not a constant. *

* This class will represent a product lattice of each variable's lattice. The * whole lattice is store in a {@code HashMap}. If variable {@code x} is * defined to be constant 10. The map will contain the value 10 with the * variable {@code x} as key. Otherwise, {@code x} is not a constant. */ private static class ConstPropLatticeElement implements LatticeElement { private final Map constMap; private final boolean isTop; /** * Constructor. * * @param isTop To define if the lattice is top. */ ConstPropLatticeElement(boolean isTop) { this.isTop = isTop; this.constMap = Maps.newHashMap(); } /** * Create a lattice where every variable is defined to be not constant. */ ConstPropLatticeElement() { this(false); } ConstPropLatticeElement(ConstPropLatticeElement other) { this.isTop = other.isTop; this.constMap = Maps.newHashMap(other.constMap); } @Override public String toString() { if (isTop) { return "TOP"; } StringBuilder out = new StringBuilder(); out.append("{"); for (Variable var : constMap.keySet()) { out.append(var); out.append("="); out.append(constMap.get(var)); out.append(" "); } out.append("}"); return out.toString(); } @Override public boolean equals(Object other) { if (other instanceof ConstPropLatticeElement) { ConstPropLatticeElement otherLattice = (ConstPropLatticeElement) other; return (this.isTop == otherLattice.isTop) && this.constMap.equals(otherLattice.constMap); } return false; } } private static class ConstPropJoinOp extends BinaryJoinOp { @Override public ConstPropLatticeElement apply(ConstPropLatticeElement a, ConstPropLatticeElement b) { ConstPropLatticeElement result = new ConstPropLatticeElement(); // By the definition of TOP of the lattice. if (a.isTop) { return new ConstPropLatticeElement(a); } if (b.isTop) { return new ConstPropLatticeElement(b); } // Do the join for each variable's lattice. for (Variable var : a.constMap.keySet()) { if (b.constMap.containsKey(var)) { Integer number = b.constMap.get(var); // The result will contain that variable as a known constant // if both lattice has that variable the same constant. if (a.constMap.get(var).equals(number)) { result.constMap.put(var, number); } } } return result; } } /** * A simple forward constant propagation. */ static class DummyConstPropagation extends DataFlowAnalysis { /** * Constructor. * * @param targetCfg Control Flow Graph. */ DummyConstPropagation(ControlFlowGraph targetCfg) { super(targetCfg, new ConstPropJoinOp()); } @Override boolean isForward() { return true; } @Override ConstPropLatticeElement flowThrough(Instruction node, ConstPropLatticeElement input) { if (node.isBranch()) { return new ConstPropLatticeElement(input); } else { return flowThroughArithmeticInstruction((ArithmeticInstruction) node, input); } } @Override ConstPropLatticeElement createEntryLattice() { return new ConstPropLatticeElement(); } @Override ConstPropLatticeElement createInitialEstimateLattice() { return new ConstPropLatticeElement(true); } } static ConstPropLatticeElement flowThroughArithmeticInstruction( ArithmeticInstruction aInst, ConstPropLatticeElement input) { ConstPropLatticeElement out = new ConstPropLatticeElement(input); // Try to see if left is a number. If it is a variable, it might already // be a constant coming in. Integer leftConst = null; if (aInst.operand1.isNumber()) { leftConst = ((Number) aInst.operand1).value; } else { if (input.constMap.containsKey(aInst.operand1)) { leftConst = input.constMap.get(aInst.operand1); } } // Do the same thing to the right. Integer rightConst = null; if (aInst.operand2.isNumber()) { rightConst = ((Number) aInst.operand2).value; } else { if (input.constMap.containsKey(aInst.operand2)) { rightConst = input.constMap.get(aInst.operand2); } } // If both are known constant we can perform the operation. if (leftConst != null && rightConst != null) { Integer constResult = null; if (aInst.operation == Operation.ADD) { constResult = leftConst.intValue() + rightConst.intValue(); } else if (aInst.operation == Operation.SUB) { constResult = leftConst.intValue() - rightConst.intValue(); } else if (aInst.operation == Operation.MUL) { constResult = leftConst.intValue() * rightConst.intValue(); } else if (aInst.operation == Operation.DIV) { constResult = leftConst.intValue() / rightConst.intValue(); } // Put it in the map. (Possibly replacing the existing constant value) out.constMap.put(aInst.result, constResult); } else { // If we cannot find a constant for it out.constMap.remove(aInst.result); } return out; } public void testSimpleIf() { // if (a) { b = 1; } else { b = 1; } c = b; Variable a = new Variable("a"); Variable b = new Variable("b"); Variable c = new Variable("c"); Instruction inst1 = new BranchInstruction(a); Instruction inst2 = newAssignNumberToVariableInstruction(b, 1); Instruction inst3 = newAssignNumberToVariableInstruction(b, 1); Instruction inst4 = newAssignVariableToVariableInstruction(c, b); ControlFlowGraph cfg = new ControlFlowGraph(inst1, true, true); GraphNode n1 = cfg.createNode(inst1); GraphNode n2 = cfg.createNode(inst2); GraphNode n3 = cfg.createNode(inst3); GraphNode n4 = cfg.createNode(inst4); cfg.connect(inst1, ControlFlowGraph.Branch.ON_FALSE, inst2); cfg.connect(inst1, ControlFlowGraph.Branch.ON_TRUE, inst3); cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst4); cfg.connect(inst3, ControlFlowGraph.Branch.UNCOND, inst4); DummyConstPropagation constProp = new DummyConstPropagation(cfg); constProp.analyze(); // We cannot conclude anything from if (a). verifyInHas(n1, a, null); verifyInHas(n1, b, null); verifyInHas(n1, c, null); verifyOutHas(n1, a, null); verifyOutHas(n1, b, null); verifyOutHas(n1, c, null); // We can conclude b = 1 after the instruction. verifyInHas(n2, a, null); verifyInHas(n2, b, null); verifyInHas(n2, c, null); verifyOutHas(n2, a, null); verifyOutHas(n2, b, 1); verifyOutHas(n2, c, null); // Same as above. verifyInHas(n3, a, null); verifyInHas(n3, b, null); verifyInHas(n3, c, null); verifyOutHas(n3, a, null); verifyOutHas(n3, b, 1); verifyOutHas(n3, c, null); // After the merge we should still have b = 1. verifyInHas(n4, a, null); verifyInHas(n4, b, 1); verifyInHas(n4, c, null); verifyOutHas(n4, a, null); // After the instruction both b and c are 1. verifyOutHas(n4, b, 1); verifyOutHas(n4, c, 1); } public void testSimpleLoop() { // a = 0; do { a = a + 1 } while (b); c = a; Variable a = new Variable("a"); Variable b = new Variable("b"); Variable c = new Variable("c"); Instruction inst1 = newAssignNumberToVariableInstruction(a, 0); Instruction inst2 = new ArithmeticInstruction(a, a, Operation.ADD, 1); Instruction inst3 = new BranchInstruction(b); Instruction inst4 = newAssignVariableToVariableInstruction(c, a); ControlFlowGraph cfg = new ControlFlowGraph(inst1, true, true); GraphNode n1 = cfg.createNode(inst1); GraphNode n2 = cfg.createNode(inst2); GraphNode n3 = cfg.createNode(inst3); GraphNode n4 = cfg.createNode(inst4); cfg.connect(inst1, ControlFlowGraph.Branch.UNCOND, inst2); cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst3); cfg.connect(inst3, ControlFlowGraph.Branch.ON_TRUE, inst2); cfg.connect(inst3, ControlFlowGraph.Branch.ON_FALSE, inst4); DummyConstPropagation constProp = new DummyConstPropagation(cfg); // This will also show that the framework terminates properly. constProp.analyze(); // a = 0 is the only thing we know. verifyInHas(n1, a, null); verifyInHas(n1, b, null); verifyInHas(n1, c, null); verifyOutHas(n1, a, 0); verifyOutHas(n1, b, null); verifyOutHas(n1, c, null); // Nothing is provable in this program, so confirm that we haven't // erroneously "proven" something. verifyInHas(n2, a, null); verifyInHas(n2, b, null); verifyInHas(n2, c, null); verifyOutHas(n2, a, null); verifyOutHas(n2, b, null); verifyOutHas(n2, c, null); verifyInHas(n3, a, null); verifyInHas(n3, b, null); verifyInHas(n3, c, null); verifyOutHas(n3, a, null); verifyOutHas(n3, b, null); verifyOutHas(n3, c, null); verifyInHas(n4, a, null); verifyInHas(n4, b, null); verifyInHas(n4, c, null); verifyOutHas(n4, a, null); verifyOutHas(n4, b, null); verifyOutHas(n4, c, null); } public void testLatticeArrayMinimizationWhenMidpointIsEven() { assertEquals(6, JoinOp.BinaryJoinOp.computeMidPoint(12)); } public void testLatticeArrayMinimizationWhenMidpointRoundsDown() { assertEquals(8, JoinOp.BinaryJoinOp.computeMidPoint(18)); } public void testLatticeArrayMinimizationWithTwoElements() { assertEquals(1, JoinOp.BinaryJoinOp.computeMidPoint(2)); } /** * A simple forward constant propagation. */ static class BranchedDummyConstPropagation extends BranchedForwardDataFlowAnalysis { BranchedDummyConstPropagation(ControlFlowGraph targetCfg) { super(targetCfg, new ConstPropJoinOp()); } @Override ConstPropLatticeElement flowThrough(Instruction node, ConstPropLatticeElement input) { if (node.isArithmetic()) { return flowThroughArithmeticInstruction( (ArithmeticInstruction) node, input); } else { return new ConstPropLatticeElement(input); } } @Override List branchedFlowThrough(Instruction node, ConstPropLatticeElement input) { List result = Lists.newArrayList(); List> outEdges = getCfg().getOutEdges(node); if (node.isArithmetic()) { assertTrue(outEdges.size() < 2); ConstPropLatticeElement aResult = flowThroughArithmeticInstruction( (ArithmeticInstruction) node, input); for (int i = 0; i < outEdges.size(); i++) { result.add(aResult); } } else { BranchInstruction branchInst = (BranchInstruction) node; for (DiGraphEdge branch : outEdges) { ConstPropLatticeElement edgeResult = new ConstPropLatticeElement(input); if (branch.getValue() == Branch.ON_FALSE && branchInst.getCondition().isVariable()) { edgeResult.constMap.put((Variable) branchInst.getCondition(), 0); } result.add(edgeResult); } } return result; } @Override ConstPropLatticeElement createEntryLattice() { return new ConstPropLatticeElement(); } @Override ConstPropLatticeElement createInitialEstimateLattice() { return new ConstPropLatticeElement(true); } } public void testBranchedSimpleIf() { // if (a) { a = 0; } else { b = 0; } c = b; Variable a = new Variable("a"); Variable b = new Variable("b"); Variable c = new Variable("c"); Instruction inst1 = new BranchInstruction(a); Instruction inst2 = newAssignNumberToVariableInstruction(a, 0); Instruction inst3 = newAssignNumberToVariableInstruction(b, 0); Instruction inst4 = newAssignVariableToVariableInstruction(c, b); ControlFlowGraph cfg = new ControlFlowGraph(inst1, true, true); GraphNode n1 = cfg.createNode(inst1); GraphNode n2 = cfg.createNode(inst2); GraphNode n3 = cfg.createNode(inst3); GraphNode n4 = cfg.createNode(inst4); cfg.connect(inst1, ControlFlowGraph.Branch.ON_TRUE, inst2); cfg.connect(inst1, ControlFlowGraph.Branch.ON_FALSE, inst3); cfg.connect(inst2, ControlFlowGraph.Branch.UNCOND, inst4); cfg.connect(inst3, ControlFlowGraph.Branch.UNCOND, inst4); BranchedDummyConstPropagation constProp = new BranchedDummyConstPropagation(cfg); constProp.analyze(); // We cannot conclude anything from if (a). verifyBranchedInHas(n1, a, null); verifyBranchedInHas(n1, b, null); verifyBranchedInHas(n1, c, null); // Nothing is known on the true branch. verifyBranchedInHas(n2, a, null); verifyBranchedInHas(n2, b, null); verifyBranchedInHas(n2, c, null); // Verify that we have a = 0 on the false branch. verifyBranchedInHas(n3, a, 0); verifyBranchedInHas(n3, b, null); verifyBranchedInHas(n3, c, null); // After the merge we should still have a = 0. verifyBranchedInHas(n4, a, 0); } public void testMaxIterationsExceededException() { final int MAX_STEP = 10; Variable a = new Variable("a"); Instruction inst1 = new ArithmeticInstruction(a, a, Operation.ADD, a); ControlFlowGraph cfg = new ControlFlowGraph(inst1, true, true) { @Override public Comparator> getOptionalNodeComparator(boolean isForward) { return new Comparator>() { @Override public int compare(DiGraphNode o1, DiGraphNode o2) { return o1.getValue().order - o2.getValue().order; } }; } }; cfg.createNode(inst1); // We have MAX_STEP + 1 nodes, it is impossible to finish the analysis with // MAX_STEP number of steps. for (int i = 0; i < MAX_STEP + 1; i++) { Instruction inst2 = new ArithmeticInstruction(a, a, Operation.ADD, a); cfg.createNode(inst2); inst2.order = i + 1; cfg.connect(inst1, ControlFlowGraph.Branch.UNCOND, inst2); inst1 = inst2; } DummyConstPropagation constProp = new DummyConstPropagation(cfg); try { constProp.analyze(MAX_STEP); fail("Expected MaxIterationsExceededException to be thrown."); } catch (MaxIterationsExceededException e) { assertEquals(e.getMessage(), "Analysis did not terminate after " + MAX_STEP + " iterations"); } } static void verifyInHas(GraphNode node, Variable var, Integer constant) { FlowState fState = node.getAnnotation(); assertEquals(constant, fState.getIn().constMap.get(var)); } static void verifyOutHas(GraphNode node, Variable var, Integer constant) { FlowState fState = node.getAnnotation(); assertEquals(constant, fState.getOut().constMap.get(var)); } static void verifyBranchedInHas(GraphNode node, Variable var, Integer constant) { BranchedFlowState fState = node.getAnnotation(); assertEquals(constant, fState.getIn().constMap.get(var)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckUnreachableCodeTest.java0000644000175000017500000001523412115204405030476 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; /** * Tests for {@link CheckUnreachableCode}. * */ public class CheckUnreachableCodeTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new CombinedCompilerPass(compiler, new CheckUnreachableCode(compiler, CheckLevel.ERROR)); } public void testCorrectSimple() { testSame("var x"); testSame("var x = 1"); testSame("var x = 1; x = 2;"); testSame("if (x) { var x = 1 }"); testSame("if (x) { var x = 1 } else { var y = 2 }"); testSame("while(x) {}"); } public void testIncorrectSimple() { assertUnreachable("function f() { return; x=1; }"); assertUnreachable("function f() { return; x=1; x=1; }"); assertUnreachable("function f() { return; var x = 1; }"); } public void testCorrectIfReturns() { testSame("function f() { if (x) { return } }"); testSame("function f() { if (x) { return } return }"); testSame("function f() { if (x) { if (y) { return } } else { return }}"); testSame("function f()" + "{ if (x) { if (y) { return } return } else { return }}"); } public void testInCorrectIfReturns() { assertUnreachable( "function f() { if (x) { return } else { return } return }"); } public void testCorrectSwitchReturn() { testSame("function f() { switch(x) { default: return; case 1: x++; }}"); testSame("function f() {" + "switch(x) { default: return; case 1: x++; } return }"); testSame("function f() {" + "switch(x) { default: return; case 1: return; }}"); testSame("function f() {" + "switch(x) { case 1: return; } return }"); testSame("function f() {" + "switch(x) { case 1: case 2: return; } return }"); testSame("function f() {" + "switch(x) { case 1: return; case 2: return; } return }"); testSame("function f() {" + "switch(x) { case 1 : return; case 2: return; } return }"); } public void testInCorrectSwitchReturn() { assertUnreachable("function f() {" + "switch(x) { default: return; case 1: return; } return }"); assertUnreachable("function f() {" + "switch(x) { default: return; return; case 1: return; } }"); } public void testCorrectLoopBreaksAndContinues() { testSame("while(1) { foo(); break }"); testSame("while(1) { foo(); continue }"); testSame("for(;;) { foo(); break }"); testSame("for(;;) { foo(); continue }"); testSame("for(;;) { if (x) { break } }"); testSame("for(;;) { if (x) { continue } }"); testSame("do { foo(); continue} while(1)"); } public void testInCorrectLoopBreaksAndContinues() { assertUnreachable("while(1) { foo(); break; bar()}"); assertUnreachable("while(1) { foo(); continue; bar() }"); assertUnreachable("for(;;) { foo(); break; bar() }"); assertUnreachable("for(;;) { foo(); continue; bar() }"); assertUnreachable("for(;;) { if (x) { break; bar() } }"); assertUnreachable("for(;;) { if (x) { continue; bar() } }"); assertUnreachable("do { foo(); continue; bar()} while(1)"); } public void testUncheckedWhileInDo() { assertUnreachable("do { foo(); break} while(1)"); } public void testUncheckedConditionInFor() { assertUnreachable("for(var x = 0; x < 100; x++) { break };"); } public void testFunctionDeclaration() { // functions are not in our CFG. testSame("function f() { return; function ff() { }}"); } public void testVarDeclaration() { assertUnreachable("function f() { return; var x = 1 }"); // I think the user should fix this as well. assertUnreachable("function f() { return; var x }"); } public void testReachableTryCatchFinally() { testSame("try { } finally { }"); testSame("try { foo(); } finally bar(); "); testSame("try { foo() } finally { bar() }"); testSame("try { foo(); } catch (e) {e()} finally bar(); "); testSame("try { foo() } catch (e) {e()} finally { bar() }"); } public void testUnreachableCatch() { assertUnreachable("try { var x = 0 } catch (e) { }"); } public void testSpuriousBreak() { testSame("switch (x) { default: throw x; break; }"); } public void testInstanceOfThrowsException() { testSame("function f() {try { if (value instanceof type) return true; } " + "catch (e) { }}"); } public void testFalseCondition() { assertUnreachable("if(false) { }"); assertUnreachable("if(0) { }"); } public void testUnreachableLoop() { assertUnreachable("while(false) {}"); } public void testInfiniteLoop() { testSame("while (true) { foo(); break; }"); // TODO(user): Have a infinite loop warning instead. assertUnreachable("while(true) {} foo()"); } public void testSuppression() { assertUnreachable("if(false) { }"); testSame( "/** @fileoverview\n" + " * @suppress {uselessCode}\n" + " */\n" + "if(false) { }"); testSame( "/** @fileoverview\n" + " * @suppress {uselessCode}\n" + " */\n" + "function f() { if(false) { } }"); testSame( "/**\n" + " * @suppress {uselessCode}\n" + " */\n" + "function f() { if(false) { } }"); assertUnreachable( "/**\n" + " * @suppress {uselessCode}\n" + " */\n" + "function f() { if(false) { } }\n" + "function g() { if(false) { } }\n"); testSame( "/**\n" + " * @suppress {uselessCode}\n" + " */\n" + "function f() {\n" + " function g() { if(false) { } }\n" + " if(false) { } }\n"); assertUnreachable( "function f() {\n" + " /**\n" + " * @suppress {uselessCode}\n" + " */\n" + " function g() { if(false) { } }\n" + " if(false) { } }\n"); testSame( "function f() {\n" + " /**\n" + " * @suppress {uselessCode}\n" + " */\n" + " function g() { if(false) { } }\n" + "}\n"); } private void assertUnreachable(String js) { test(js, js, CheckUnreachableCode.UNREACHABLE_CODE); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FlowSensitiveInlineVariablesTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FlowSensitiveInlineVariablesTest.j0000644000175000017500000004357112115204405031642 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Unit tests for {@link FlowSensitiveInlineVariables}. * */ public class FlowSensitiveInlineVariablesTest extends CompilerTestCase { public static final String EXTERN_FUNCTIONS = "" + "var print;\n" + "/** @nosideeffects */ function noSFX() {} \n" + " function hasSFX() {} \n"; public FlowSensitiveInlineVariablesTest() { enableNormalize(true); } @Override public int getNumRepetitions() { // Test repeatedly inline. return 3; } @Override protected CompilerPass getProcessor(final Compiler compiler) { //return new FlowSensitiveInlineVariables(compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { (new MarkNoSideEffectCalls(compiler)).process(externs, root); (new FlowSensitiveInlineVariables(compiler)).process(externs, root); } }; } public void testSimpleAssign() { inline("var x; x = 1; print(x)", "var x; print(1)"); inline("var x; x = 1; x", "var x; 1"); inline("var x; x = 1; var a = x", "var x; var a = 1"); inline("var x; x = 1; x = x + 1", "var x; x = 1 + 1"); } public void testSimpleVar() { inline("var x = 1; print(x)", "var x; print(1)"); inline("var x = 1; x", "var x; 1"); inline("var x = 1; var a = x", "var x; var a = 1"); inline("var x = 1; x = x + 1", "var x; x = 1 + 1"); } public void testSimpleForIn() { inline("var a,b,x = a in b; x", "var a,b,x; a in b"); noInline("var a, b; var x = a in b; print(1); x"); noInline("var a,b,x = a in b; delete a[b]; x"); } public void testExported() { noInline("var _x = 1; print(_x)"); } public void testDoNotInlineIncrement() { noInline("var x = 1; x++;"); noInline("var x = 1; x--;"); } public void testDoNotInlineAssignmentOp() { noInline("var x = 1; x += 1;"); noInline("var x = 1; x -= 1;"); } public void testDoNotInlineIntoLhsOfAssign() { noInline("var x = 1; x += 3;"); } public void testMultiUse() { noInline("var x; x = 1; print(x); print (x);"); } public void testMultiUseInSameCfgNode() { noInline("var x; x = 1; print(x) || print (x);"); } public void testMultiUseInTwoDifferentPath() { noInline("var x = 1; if (print) { print(x) } else { alert(x) }"); } public void testAssignmentBeforeDefinition() { inline("x = 1; var x = 0; print(x)","x = 1; var x; print(0)" ); } public void testVarInConditionPath() { noInline("if (foo) { var x = 0 } print(x)"); } public void testMultiDefinitionsBeforeUse() { inline("var x = 0; x = 1; print(x)", "var x = 0; print(1)"); } public void testMultiDefinitionsInSameCfgNode() { noInline("var x; (x = 1) || (x = 2); print(x)"); noInline("var x; x = (1 || (x = 2)); print(x)"); noInline("var x;(x = 1) && (x = 2); print(x)"); noInline("var x;x = (1 && (x = 2)); print(x)"); noInline("var x; x = 1 , x = 2; print(x)"); } public void testNotReachingDefinitions() { noInline("var x; if (foo) { x = 0 } print (x)"); } public void testNoInlineLoopCarriedDefinition() { // First print is undefined instead. noInline("var x; while(true) { print(x); x = 1; }"); // Prints 0 1 1 1 1.... noInline("var x = 0; while(true) { print(x); x = 1; }"); } public void testDoNotExitLoop() { noInline("while (z) { var x = 3; } var y = x;"); } public void testDoNotInlineWithinLoop() { noInline("var y = noSFX(); do { var z = y.foo(); } while (true);"); } public void testDoNotInlineCatchExpression1() { noInline( "var a;\n" + "try {\n" + " throw Error(\"\");\n" + "}catch(err) {" + " a = err;\n" + "}\n" + "return a.stack\n"); } public void testDoNotInlineCatchExpression1a() { noInline( "var a;\n" + "try {\n" + " throw Error(\"\");\n" + "}catch(err) {" + " a = err + 1;\n" + "}\n" + "return a.stack\n"); } public void testDoNotInlineCatchExpression2() { noInline( "var a;\n" + "try {\n" + " if (x) {throw Error(\"\");}\n" + "}catch(err) {" + " a = err;\n" + "}\n" + "return a.stack\n"); } public void testDoNotInlineCatchExpression3() { noInline( "var a;\n" + "try {\n" + " throw Error(\"\");\n" + "} catch(err) {" + " err = x;\n" + " a = err;\n" + "}\n" + "return a.stack\n"); } public void testDoNotInlineCatchExpression4() { // Note: it is valid to inline "x" here but we currently don't. noInline( "try {\n" + " stuff();\n" + "} catch (e) {\n" + " x = e;\n" + " print(x);\n" + "}"); } public void testDefinitionAfterUse() { inline("var x = 0; print(x); x = 1", "var x; print(0); x = 1"); } public void testInlineSameVariableInStraightLine() { inline("var x; x = 1; print(x); x = 2; print(x)", "var x; print(1); print(2)"); } public void testInlineInDifferentPaths() { inline("var x; if (print) {x = 1; print(x)} else {x = 2; print(x)}", "var x; if (print) {print(1)} else {print(2)}"); } public void testNoInlineInMergedPath() { noInline( "var x,y;x = 1;while(y) { if(y){ print(x) } else { x = 1 } } print(x)"); } public void testInlineIntoExpressions() { inline("var x = 1; print(x + 1);", "var x; print(1 + 1)"); } public void testInlineExpressions1() { inline("var a, b; var x = a+b; print(x)", "var a, b; var x; print(a+b)"); } public void testInlineExpressions2() { // We can't inline because of the redefinition of "a". noInline("var a, b; var x = a + b; a = 1; print(x)"); } public void testInlineExpressions3() { inline("var a,b,x; x=a+b; x=a-b ; print(x)", "var a,b,x; x=a+b; print(a-b)"); } public void testInlineExpressions4() { // Precision is lost due to comma's. noInline("var a,b,x; x=a+b, x=a-b; print(x)"); } public void testInlineExpressions5() { noInline("var a; var x = a = 1; print(x)"); } public void testInlineExpressions6() { noInline("var a, x; a = 1 + (x = 1); print(x)"); } public void testInlineExpression7() { // Possible side effects in foo() that might conflict with bar(); noInline("var x = foo() + 1; bar(); print(x)"); // This is a possible case but we don't have analysis to prove this yet. // TODO(user): It is possible to cover this case with the same algorithm // as the missing return check. noInline("var x = foo() + 1; print(x)"); } public void testInlineExpression8() { // The same variable inlined twice. inline( "var a,b;" + "var x = a + b; print(x); x = a - b; print(x)", "var a,b;" + "var x; print(a + b); print(a - b)"); } public void testInlineExpression9() { // Check for actual control flow sensitivity. inline( "var a,b;" + "var x; if (g) { x= a + b; print(x) } x = a - b; print(x)", "var a,b;" + "var x; if (g) { print(a + b)} print(a - b)"); } public void testInlineExpression10() { // The DFA is not fine grain enough for this. noInline("var x, y; x = ((y = 1), print(y))"); } public void testInlineExpressions11() { inline("var x; x = x + 1; print(x)", "var x; print(x + 1)"); noInline("var x; x = x + 1; print(x); print(x)"); } public void testInlineExpressions12() { // ++ is an assignment and considered to modify state so it will not be // inlined. noInline("var x = 10; x = c++; print(x)"); } public void testInlineExpressions13() { inline("var a = 1, b = 2;" + "var x = a;" + "var y = b;" + "var z = x + y;" + "var i = z;" + "var j = z + y;" + "var k = i;", "var a, b;" + "var x;" + "var y = 2;" + "var z = 1 + y;" + "var i;" + "var j = z + y;" + "var k = z;"); } public void testNoInlineIfDefinitionMayNotReach() { noInline("var x; if (x=1) {} x;"); } public void testNoInlineEscapedToInnerFunction() { noInline("var x = 1; function foo() { x = 2 }; print(x)"); } public void testNoInlineLValue() { noInline("var x; if (x = 1) { print(x) }"); } public void testSwitchCase() { inline("var x = 1; switch(x) { }", "var x; switch(1) { }"); } public void testShadowedVariableInnerFunction() { inline("var x = 1; print(x) || (function() { var x; x = 1; print(x)})()", "var x; print(1) || (function() { var x; print(1)})()"); } public void testCatch() { noInline("var x = 0; try { } catch (x) { }"); noInline("try { } catch (x) { print(x) }"); } public void testNoInlineGetProp() { // We don't know if j alias a.b noInline("var x = a.b.c; j.c = 1; print(x);"); } public void testNoInlineGetProp2() { noInline("var x = 1 * a.b.c; j.c = 1; print(x);"); } public void testNoInlineGetProp3() { // Anything inside a function is fine. inline("var x = function(){1 * a.b.c}; print(x);", "var x; print(function(){1 * a.b.c});"); } public void testNoInlineGetEle() { // Again we don't know if i = j noInline("var x = a[i]; a[j] = 2; print(x); "); } // TODO(user): These should be inlinable. public void testNoInlineConstructors() { noInline("var x = new Iterator(); x.next();"); } // TODO(user): These should be inlinable. public void testNoInlineArrayLits() { noInline("var x = []; print(x)"); } // TODO(user): These should be inlinable. public void testNoInlineObjectLits() { noInline("var x = {}; print(x)"); } // TODO(user): These should be inlinable after the REGEX checks. public void testNoInlineRegExpLits() { noInline("var x = /y/; print(x)"); } public void testInlineConstructorCallsIntoLoop() { // Don't inline construction into loops. noInline("var x = new Iterator();" + "for(i = 0; i < 10; i++) {j = x.next()}"); } public void testRemoveWithLabels() { inline("var x = 1; L: x = 2; print(x)", "var x = 1; L:{} print(2)"); inline("var x = 1; L: M: x = 2; print(x)", "var x = 1; L:M:{} print(2)"); inline("var x = 1; L: M: N: x = 2; print(x)", "var x = 1; L:M:N:{} print(2)"); } public void testInlineAcrossSideEffect1() { // This can't be inlined because print() has side-effects and might change // the definition of noSFX. // // noSFX must be both const and pure in order to inline it. noInline("var y; var x = noSFX(y); print(x)"); //inline("var y; var x = noSFX(y); print(x)", "var y;var x;print(noSFX(y))"); } public void testInlineAcrossSideEffect2() { // Think noSFX() as a function that reads y.foo and return it // and SFX() write some new value of y.foo. If that's the case, // inlining across hasSFX() is not valid. // This is a case where hasSFX is right of the source of the inlining. noInline("var y; var x = noSFX(y), z = hasSFX(y); print(x)"); noInline("var y; var x = noSFX(y), z = new hasSFX(y); print(x)"); noInline("var y; var x = new noSFX(y), z = new hasSFX(y); print(x)"); } public void testInlineAcrossSideEffect3() { // This is a case where hasSFX is left of the destination of the inlining. noInline("var y; var x = noSFX(y); hasSFX(y), print(x)"); noInline("var y; var x = noSFX(y); new hasSFX(y), print(x)"); noInline("var y; var x = new noSFX(y); new hasSFX(y), print(x)"); } public void testInlineAcrossSideEffect4() { // This is a case where hasSFX is some control flow path between the // source and its destination. noInline("var y; var x = noSFX(y); hasSFX(y); print(x)"); noInline("var y; var x = noSFX(y); new hasSFX(y); print(x)"); noInline("var y; var x = new noSFX(y); new hasSFX(y); print(x)"); } public void testCanInlineAcrossNoSideEffect() { // This can't be inlined because print() has side-effects and might change // the definition of noSFX. We should be able to mark noSFX as const // in some way. noInline( "var y; var x = noSFX(y), z = noSFX(); noSFX(); noSFX(), print(x)"); //inline( // "var y; var x = noSFX(y), z = noSFX(); noSFX(); noSFX(), print(x)", // "var y; var x, z = noSFX(); noSFX(); noSFX(), print(noSFX(y))"); } public void testDependOnOuterScopeVariables() { noInline("var x; function foo() { var y = x; x = 0; print(y) }"); noInline("var x; function foo() { var y = x; x++; print(y) }"); // Sadly, we don't understand the data flow of outer scoped variables as // it can be modified by code outside of this scope. We can't inline // at all if the definition has dependence on such variable. noInline("var x; function foo() { var y = x; print(y) }"); } public void testInlineIfNameIsLeftSideOfAssign() { inline("var x = 1; x = print(x) + 1", "var x; x = print(1) + 1"); inline("var x = 1; L: x = x + 2", "var x; L: x = 1 + 2"); inline("var x = 1; x = (x = x + 1)", "var x; x = (x = 1 + 1)"); noInline("var x = 1; x = (x = (x = 10) + x)"); noInline("var x = 1; x = (f(x) + (x = 10) + x);"); noInline("var x = 1; x=-1,foo(x)"); noInline("var x = 1; x-=1,foo(x)"); } public void testInlineArguments() { testSame("function _func(x) { print(x) }"); testSame("function _func(x,y) { if(y) { x = 1 }; print(x) }"); test("function f(x, y) { x = 1; print(x) }", "function f(x, y) { print(1) }"); test("function f(x, y) { if (y) { x = 1; print(x) }}", "function f(x, y) { if (y) { print(1) }}"); } public void testInvalidInlineArguments1() { testSame("function f(x, y) { x = 1; arguments[0] = 2; print(x) }"); testSame("function f(x, y) { x = 1; var z = arguments;" + "z[0] = 2; z[1] = 3; print(x)}"); testSame("function g(a){a[0]=2} function f(x){x=1;g(arguments);print(x)}"); } public void testInvalidInlineArguments2() { testSame("function f(c) {var f = c; arguments[0] = this;" + "f.apply(this, arguments); return this;}"); } public void testForIn() { noInline("var x; var y = {}; for(x in y){}"); noInline("var x; var y = {}; var z; for(x in z = y){print(z)}"); noInline("var x; var y = {}; var z; for(x in y){print(z)}"); } public void testNotOkToSkipCheckPathBetweenNodes() { noInline("var x; for(x = 1; foo(x);) {}"); noInline("var x; for(; x = 1;foo(x)) {}"); } public void testIssue698() { // Most of the flow algorithms operate on Vars. We want to make // sure the algorithm bails out appropriately if it sees // a var that it doesn't know about. inline( "var x = ''; " + "unknown.length < 2 && (unknown='0' + unknown);" + "x = x + unknown; " + "unknown.length < 3 && (unknown='0' + unknown);" + "x = x + unknown; " + "return x;", "var x; " + "unknown.length < 2 && (unknown='0' + unknown);" + "x = '' + unknown; " + "unknown.length < 3 && (unknown='0' + unknown);" + "x = x + unknown; " + "return x;"); } public void testIssue777() { test( "function f(cmd, ta) {" + " var temp = cmd;" + " var temp2 = temp >> 2;" + " cmd = STACKTOP;" + " for (var src = temp2, dest = cmd >> 2, stop = src + 37;" + " src < stop;" + " src++, dest++) {" + " HEAP32[dest] = HEAP32[src];" + " }" + " temp = ta;" + " temp2 = temp >> 2;" + " ta = STACKTOP;" + " STACKTOP += 8;" + " HEAP32[ta >> 2] = HEAP32[temp2];" + " HEAP32[ta + 4 >> 2] = HEAP32[temp2 + 1];" + "}", "function f(cmd, ta){" + " var temp;" + " var temp2 = cmd >> 2;" + " cmd = STACKTOP;" + " var src = temp2;" + " var dest = cmd >> 2;" + " var stop = src + 37;" + " for(;src> 2;" + " ta = STACKTOP;" + " STACKTOP += 8;" + " HEAP32[ta>>2] = HEAP32[temp2];" + " HEAP32[ta+4>>2] = HEAP32[temp2+1];" + "}"); } public void testTransitiveDependencies1() { test( "function f(x) { var a = x; var b = a; x = 3; return b; }", "function f(x) { var a; var b = x; x = 3; return b; }"); } public void testTransitiveDependencies2() { test( "function f(x) { var a = x; var b = a; var c = b; x = 3; return c; }", "function f(x) { var a ; var b = x; var c ; x = 3; return b; }"); } public void testIssue794a() { noInline( "var x = 1; " + "try { x += someFunction(); } catch (e) {}" + "x += 1;" + "try { x += someFunction(); } catch (e) {}" + "return x;"); } public void testIssue794b() { noInline( "var x = 1; " + "try { x = x + someFunction(); } catch (e) {}" + "x = x + 1;" + "try { x = x + someFunction(); } catch (e) {}" + "return x;"); } private void noInline(String input) { inline(input, input); } private void inline(String input, String expected) { test(EXTERN_FUNCTIONS, "function _func() {" + input + "}", "function _func() {" + expected + "}", null, null); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NodeUtilTest.java0000644000175000017500000020046512115204405026261 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; import junit.framework.TestCase; import java.util.Collection; import java.util.Set; /** * Tests for NodeUtil */ public class NodeUtilTest extends TestCase { private static Node parse(String js) { Compiler compiler = new Compiler(); compiler.initCompilerOptionsIfTesting(); compiler.getOptions().setLanguageIn(LanguageMode.ECMASCRIPT5); Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); return n; } static Node getNode(String js) { Node root = parse("var a=(" + js + ");"); Node expr = root.getFirstChild(); Node var = expr.getFirstChild(); return var.getFirstChild(); } public void testIsLiteralOrConstValue() { assertLiteralAndImmutable(getNode("10")); assertLiteralAndImmutable(getNode("-10")); assertLiteralButNotImmutable(getNode("[10, 20]")); assertLiteralButNotImmutable(getNode("{'a': 20}")); assertLiteralButNotImmutable(getNode("[10, , 1.0, [undefined], 'a']")); assertLiteralButNotImmutable(getNode("/abc/")); assertLiteralAndImmutable(getNode("\"string\"")); assertLiteralAndImmutable(getNode("'aaa'")); assertLiteralAndImmutable(getNode("null")); assertLiteralAndImmutable(getNode("undefined")); assertLiteralAndImmutable(getNode("void 0")); assertNotLiteral(getNode("abc")); assertNotLiteral(getNode("[10, foo(), 20]")); assertNotLiteral(getNode("foo()")); assertNotLiteral(getNode("c + d")); assertNotLiteral(getNode("{'a': foo()}")); assertNotLiteral(getNode("void foo()")); } public void assertLiteralAndImmutable(Node n) { assertTrue(NodeUtil.isLiteralValue(n, true)); assertTrue(NodeUtil.isLiteralValue(n, false)); assertTrue(NodeUtil.isImmutableValue(n)); } public void assertLiteralButNotImmutable(Node n) { assertTrue(NodeUtil.isLiteralValue(n, true)); assertTrue(NodeUtil.isLiteralValue(n, false)); assertFalse(NodeUtil.isImmutableValue(n)); } public void assertNotLiteral(Node n) { assertFalse(NodeUtil.isLiteralValue(n, true)); assertFalse(NodeUtil.isLiteralValue(n, false)); assertFalse(NodeUtil.isImmutableValue(n)); } public void testGetBooleanValue() { assertPureBooleanTrue("true"); assertPureBooleanTrue("10"); assertPureBooleanTrue("'0'"); assertPureBooleanTrue("/a/"); assertPureBooleanTrue("{}"); assertPureBooleanTrue("[]"); assertPureBooleanFalse("false"); assertPureBooleanFalse("null"); assertPureBooleanFalse("0"); assertPureBooleanFalse("''"); assertPureBooleanFalse("undefined"); assertPureBooleanFalse("void 0"); assertPureBooleanUnknown("void foo()"); assertPureBooleanUnknown("b"); assertPureBooleanUnknown("-'0.0'"); // Known but getBooleanValue return false for expressions with side-effects assertPureBooleanUnknown("{a:foo()}"); assertPureBooleanUnknown("[foo()]"); } private void assertPureBooleanTrue(String val) { assertEquals(TernaryValue.TRUE, NodeUtil.getPureBooleanValue(getNode(val))); } private void assertPureBooleanFalse(String val) { assertEquals( TernaryValue.FALSE, NodeUtil.getPureBooleanValue(getNode(val))); } private void assertPureBooleanUnknown(String val) { assertEquals( TernaryValue.UNKNOWN, NodeUtil.getPureBooleanValue(getNode(val))); } public void testGetExpressionBooleanValue() { assertImpureBooleanTrue("a=true"); assertImpureBooleanFalse("a=false"); assertImpureBooleanTrue("a=(false,true)"); assertImpureBooleanFalse("a=(true,false)"); assertImpureBooleanTrue("a=(false || true)"); assertImpureBooleanFalse("a=(true && false)"); assertImpureBooleanTrue("a=!(true && false)"); assertImpureBooleanTrue("a,true"); assertImpureBooleanFalse("a,false"); assertImpureBooleanTrue("true||false"); assertImpureBooleanFalse("false||false"); assertImpureBooleanTrue("true&&true"); assertImpureBooleanFalse("true&&false"); assertImpureBooleanFalse("!true"); assertImpureBooleanTrue("!false"); assertImpureBooleanTrue("!''"); // Assignment ops other than ASSIGN are unknown. assertImpureBooleanUnknown("a *= 2"); // Complex expressions that contain anything other then "=", ",", or "!" are // unknown. assertImpureBooleanUnknown("2 + 2"); assertImpureBooleanTrue("a=1"); assertImpureBooleanTrue("a=/a/"); assertImpureBooleanTrue("a={}"); assertImpureBooleanTrue("true"); assertImpureBooleanTrue("10"); assertImpureBooleanTrue("'0'"); assertImpureBooleanTrue("/a/"); assertImpureBooleanTrue("{}"); assertImpureBooleanTrue("[]"); assertImpureBooleanFalse("false"); assertImpureBooleanFalse("null"); assertImpureBooleanFalse("0"); assertImpureBooleanFalse("''"); assertImpureBooleanFalse("undefined"); assertImpureBooleanFalse("void 0"); assertImpureBooleanFalse("void foo()"); assertImpureBooleanTrue("a?true:true"); assertImpureBooleanFalse("a?false:false"); assertImpureBooleanUnknown("a?true:false"); assertImpureBooleanUnknown("a?true:foo()"); assertImpureBooleanUnknown("b"); assertImpureBooleanUnknown("-'0.0'"); assertImpureBooleanTrue("{a:foo()}"); assertImpureBooleanTrue("[foo()]"); } private void assertImpureBooleanTrue(String val) { assertEquals(TernaryValue.TRUE, NodeUtil.getImpureBooleanValue(getNode(val))); } private void assertImpureBooleanFalse(String val) { assertEquals(TernaryValue.FALSE, NodeUtil.getImpureBooleanValue(getNode(val))); } private void assertImpureBooleanUnknown(String val) { assertEquals(TernaryValue.UNKNOWN, NodeUtil.getImpureBooleanValue(getNode(val))); } public void testGetStringValue() { assertEquals("true", NodeUtil.getStringValue(getNode("true"))); assertEquals("10", NodeUtil.getStringValue(getNode("10"))); assertEquals("1", NodeUtil.getStringValue(getNode("1.0"))); assertEquals("0", NodeUtil.getStringValue(getNode("'0'"))); assertEquals(null, NodeUtil.getStringValue(getNode("/a/"))); assertEquals("[object Object]", NodeUtil.getStringValue(getNode("{}"))); assertEquals("", NodeUtil.getStringValue(getNode("[]"))); assertEquals("false", NodeUtil.getStringValue(getNode("false"))); assertEquals("null", NodeUtil.getStringValue(getNode("null"))); assertEquals("0", NodeUtil.getStringValue(getNode("0"))); assertEquals("", NodeUtil.getStringValue(getNode("''"))); assertEquals("undefined", NodeUtil.getStringValue(getNode("undefined"))); assertEquals("undefined", NodeUtil.getStringValue(getNode("void 0"))); assertEquals("undefined", NodeUtil.getStringValue(getNode("void foo()"))); assertEquals("NaN", NodeUtil.getStringValue(getNode("NaN"))); assertEquals("Infinity", NodeUtil.getStringValue(getNode("Infinity"))); assertEquals(null, NodeUtil.getStringValue(getNode("x"))); } public void testGetArrayStringValue() { assertEquals("", NodeUtil.getStringValue(getNode("[]"))); assertEquals("", NodeUtil.getStringValue(getNode("['']"))); assertEquals("", NodeUtil.getStringValue(getNode("[null]"))); assertEquals("", NodeUtil.getStringValue(getNode("[undefined]"))); assertEquals("", NodeUtil.getStringValue(getNode("[void 0]"))); assertEquals("NaN", NodeUtil.getStringValue(getNode("[NaN]"))); assertEquals(",", NodeUtil.getStringValue(getNode("[,'']"))); assertEquals(",,", NodeUtil.getStringValue(getNode("[[''],[''],['']]"))); assertEquals("1,2", NodeUtil.getStringValue(getNode("[[1.0],[2.0]]"))); assertEquals(null, NodeUtil.getStringValue(getNode("[a]"))); assertEquals(null, NodeUtil.getStringValue(getNode("[1,a]"))); } public void testIsObjectLiteralKey1() throws Exception { testIsObjectLiteralKey( parseExpr("({})"), false); testIsObjectLiteralKey( parseExpr("a"), false); testIsObjectLiteralKey( parseExpr("'a'"), false); testIsObjectLiteralKey( parseExpr("1"), false); testIsObjectLiteralKey( parseExpr("({a: 1})").getFirstChild(), true); testIsObjectLiteralKey( parseExpr("({1: 1})").getFirstChild(), true); testIsObjectLiteralKey( parseExpr("({get a(){}})").getFirstChild(), true); testIsObjectLiteralKey( parseExpr("({set a(b){}})").getFirstChild(), true); } private Node parseExpr(String js) { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5); compiler.initOptions(options); Node root = compiler.parseTestCode(js); return root.getFirstChild().getFirstChild(); } private void testIsObjectLiteralKey(Node node, boolean expected) { assertEquals(expected, NodeUtil.isObjectLitKey(node)); } public void testGetFunctionName1() throws Exception { Compiler compiler = new Compiler(); Node parent = compiler.parseTestCode("function name(){}"); testGetFunctionName(parent.getFirstChild(), "name"); } public void testGetFunctionName2() throws Exception { Compiler compiler = new Compiler(); Node parent = compiler.parseTestCode("var name = function(){}") .getFirstChild().getFirstChild(); testGetFunctionName(parent.getFirstChild(), "name"); } public void testGetFunctionName3() throws Exception { Compiler compiler = new Compiler(); Node parent = compiler.parseTestCode("qualified.name = function(){}") .getFirstChild().getFirstChild(); testGetFunctionName(parent.getLastChild(), "qualified.name"); } public void testGetFunctionName4() throws Exception { Compiler compiler = new Compiler(); Node parent = compiler.parseTestCode("var name2 = function name1(){}") .getFirstChild().getFirstChild(); testGetFunctionName(parent.getFirstChild(), "name2"); } public void testGetFunctionName5() throws Exception { Compiler compiler = new Compiler(); Node n = compiler.parseTestCode("qualified.name2 = function name1(){}"); Node parent = n.getFirstChild().getFirstChild(); testGetFunctionName(parent.getLastChild(), "qualified.name2"); } private void testGetFunctionName(Node function, String name) { assertEquals(Token.FUNCTION, function.getType()); assertEquals(name, NodeUtil.getFunctionName(function)); } public void testContainsFunctionDeclaration() { assertTrue(NodeUtil.containsFunction( getNode("function foo(){}"))); assertTrue(NodeUtil.containsFunction( getNode("(b?function(){}:null)"))); assertFalse(NodeUtil.containsFunction( getNode("(b?foo():null)"))); assertFalse(NodeUtil.containsFunction( getNode("foo()"))); } private void assertSideEffect(boolean se, String js) { Node n = parse(js); assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild())); } private void assertSideEffect(boolean se, String js, boolean globalRegExp) { Node n = parse(js); Compiler compiler = new Compiler(); compiler.setHasRegExpGlobalReferences(globalRegExp); assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild(), compiler)); } public void testMayHaveSideEffects() { assertSideEffect(true, "i++"); assertSideEffect(true, "[b, [a, i++]]"); assertSideEffect(true, "i=3"); assertSideEffect(true, "[0, i=3]"); assertSideEffect(true, "b()"); assertSideEffect(true, "[1, b()]"); assertSideEffect(true, "b.b=4"); assertSideEffect(true, "b.b--"); assertSideEffect(true, "i--"); assertSideEffect(true, "a[0][i=4]"); assertSideEffect(true, "a += 3"); assertSideEffect(true, "a, b, z += 4"); assertSideEffect(true, "a ? c : d++"); assertSideEffect(true, "a + c++"); assertSideEffect(true, "a + c - d()"); assertSideEffect(true, "a + c - d()"); assertSideEffect(true, "function foo() {}"); assertSideEffect(true, "while(true);"); assertSideEffect(true, "if(true){a()}"); assertSideEffect(false, "if(true){a}"); assertSideEffect(false, "(function() { })"); assertSideEffect(false, "(function() { i++ })"); assertSideEffect(false, "[function a(){}]"); assertSideEffect(false, "a"); assertSideEffect(false, "[b, c [d, [e]]]"); assertSideEffect(false, "({a: x, b: y, c: z})"); assertSideEffect(false, "/abc/gi"); assertSideEffect(false, "'a'"); assertSideEffect(false, "0"); assertSideEffect(false, "a + c"); assertSideEffect(false, "'c' + a[0]"); assertSideEffect(false, "a[0][1]"); assertSideEffect(false, "'a' + c"); assertSideEffect(false, "'a' + a.name"); assertSideEffect(false, "1, 2, 3"); assertSideEffect(false, "a, b, 3"); assertSideEffect(false, "(function(a, b) { })"); assertSideEffect(false, "a ? c : d"); assertSideEffect(false, "'1' + navigator.userAgent"); assertSideEffect(false, "new RegExp('foobar', 'i')"); assertSideEffect(true, "new RegExp(SomethingWacky(), 'i')"); assertSideEffect(false, "new Array()"); assertSideEffect(false, "new Array"); assertSideEffect(false, "new Array(4)"); assertSideEffect(false, "new Array('a', 'b', 'c')"); assertSideEffect(true, "new SomeClassINeverHeardOf()"); assertSideEffect(true, "new SomeClassINeverHeardOf()"); assertSideEffect(false, "({}).foo = 4"); assertSideEffect(false, "([]).foo = 4"); assertSideEffect(false, "(function() {}).foo = 4"); assertSideEffect(true, "this.foo = 4"); assertSideEffect(true, "a.foo = 4"); assertSideEffect(true, "(function() { return n; })().foo = 4"); assertSideEffect(true, "([]).foo = bar()"); assertSideEffect(false, "undefined"); assertSideEffect(false, "void 0"); assertSideEffect(true, "void foo()"); assertSideEffect(false, "-Infinity"); assertSideEffect(false, "Infinity"); assertSideEffect(false, "NaN"); assertSideEffect(false, "({}||[]).foo = 2;"); assertSideEffect(false, "(true ? {} : []).foo = 2;"); assertSideEffect(false, "({},[]).foo = 2;"); assertSideEffect(true, "delete a.b"); } public void testObjectMethodSideEffects() { // "toString" and "valueOf" are assumed to be side-effect free assertSideEffect(false, "o.toString()"); assertSideEffect(false, "o.valueOf()"); // other methods depend on the extern definitions assertSideEffect(true, "o.watch()"); } public void testRegExpSideEffect() { // A RegExp Object by itself doesn't have any side-effects assertSideEffect(false, "/abc/gi", true); assertSideEffect(false, "/abc/gi", false); // RegExp instance methods have global side-effects, so whether they are // considered side-effect free depends on whether the global properties // are referenced. assertSideEffect(true, "(/abc/gi).test('')", true); assertSideEffect(false, "(/abc/gi).test('')", false); assertSideEffect(true, "(/abc/gi).test(a)", true); assertSideEffect(false, "(/abc/gi).test(b)", false); assertSideEffect(true, "(/abc/gi).exec('')", true); assertSideEffect(false, "(/abc/gi).exec('')", false); // Some RegExp object method that may have side-effects. assertSideEffect(true, "(/abc/gi).foo('')", true); assertSideEffect(true, "(/abc/gi).foo('')", false); // Try the string RegExp ops. assertSideEffect(true, "''.match('a')", true); assertSideEffect(false, "''.match('a')", false); assertSideEffect(true, "''.match(/(a)/)", true); assertSideEffect(false, "''.match(/(a)/)", false); assertSideEffect(true, "''.replace('a')", true); assertSideEffect(false, "''.replace('a')", false); assertSideEffect(true, "''.search('a')", true); assertSideEffect(false, "''.search('a')", false); assertSideEffect(true, "''.split('a')", true); assertSideEffect(false, "''.split('a')", false); // Some non-RegExp string op that may have side-effects. assertSideEffect(true, "''.foo('a')", true); assertSideEffect(true, "''.foo('a')", false); // 'a' might be a RegExp object with the 'g' flag, in which case // the state might change by running any of the string ops. // Specifically, using these methods resets the "lastIndex" if used // in combination with a RegExp instance "exec" method. assertSideEffect(true, "''.match(a)", true); assertSideEffect(true, "''.match(a)", false); } private void assertMutableState(boolean se, String js) { Node n = parse(js); assertEquals(se, NodeUtil.mayEffectMutableState(n.getFirstChild())); } public void testMayEffectMutableState() { assertMutableState(true, "i++"); assertMutableState(true, "[b, [a, i++]]"); assertMutableState(true, "i=3"); assertMutableState(true, "[0, i=3]"); assertMutableState(true, "b()"); assertMutableState(true, "void b()"); assertMutableState(true, "[1, b()]"); assertMutableState(true, "b.b=4"); assertMutableState(true, "b.b--"); assertMutableState(true, "i--"); assertMutableState(true, "a[0][i=4]"); assertMutableState(true, "a += 3"); assertMutableState(true, "a, b, z += 4"); assertMutableState(true, "a ? c : d++"); assertMutableState(true, "a + c++"); assertMutableState(true, "a + c - d()"); assertMutableState(true, "a + c - d()"); assertMutableState(true, "function foo() {}"); assertMutableState(true, "while(true);"); assertMutableState(true, "if(true){a()}"); assertMutableState(false, "if(true){a}"); assertMutableState(true, "(function() { })"); assertMutableState(true, "(function() { i++ })"); assertMutableState(true, "[function a(){}]"); assertMutableState(false, "a"); assertMutableState(true, "[b, c [d, [e]]]"); assertMutableState(true, "({a: x, b: y, c: z})"); // Note: RegExp objects are not immutable, for instance, the exec // method maintains state for "global" searches. assertMutableState(true, "/abc/gi"); assertMutableState(false, "'a'"); assertMutableState(false, "0"); assertMutableState(false, "a + c"); assertMutableState(false, "'c' + a[0]"); assertMutableState(false, "a[0][1]"); assertMutableState(false, "'a' + c"); assertMutableState(false, "'a' + a.name"); assertMutableState(false, "1, 2, 3"); assertMutableState(false, "a, b, 3"); assertMutableState(true, "(function(a, b) { })"); assertMutableState(false, "a ? c : d"); assertMutableState(false, "'1' + navigator.userAgent"); assertMutableState(true, "new RegExp('foobar', 'i')"); assertMutableState(true, "new RegExp(SomethingWacky(), 'i')"); assertMutableState(true, "new Array()"); assertMutableState(true, "new Array"); assertMutableState(true, "new Array(4)"); assertMutableState(true, "new Array('a', 'b', 'c')"); assertMutableState(true, "new SomeClassINeverHeardOf()"); } public void testIsFunctionExpression() { assertContainsAnonFunc(true, "(function(){})"); assertContainsAnonFunc(true, "[function a(){}]"); assertContainsAnonFunc(false, "{x: function a(){}}"); assertContainsAnonFunc(true, "(function a(){})()"); assertContainsAnonFunc(true, "x = function a(){};"); assertContainsAnonFunc(true, "var x = function a(){};"); assertContainsAnonFunc(true, "if (function a(){});"); assertContainsAnonFunc(true, "while (function a(){});"); assertContainsAnonFunc(true, "do; while (function a(){});"); assertContainsAnonFunc(true, "for (function a(){};;);"); assertContainsAnonFunc(true, "for (;function a(){};);"); assertContainsAnonFunc(true, "for (;;function a(){});"); assertContainsAnonFunc(true, "for (p in function a(){});"); assertContainsAnonFunc(true, "with (function a(){}) {}"); assertContainsAnonFunc(false, "function a(){}"); assertContainsAnonFunc(false, "if (x) function a(){};"); assertContainsAnonFunc(false, "if (x) { function a(){} }"); assertContainsAnonFunc(false, "if (x); else function a(){};"); assertContainsAnonFunc(false, "while (x) function a(){};"); assertContainsAnonFunc(false, "do function a(){} while (0);"); assertContainsAnonFunc(false, "for (;;) function a(){}"); assertContainsAnonFunc(false, "for (p in o) function a(){};"); assertContainsAnonFunc(false, "with (x) function a(){}"); } private void assertContainsAnonFunc(boolean expected, String js) { Node funcParent = findParentOfFuncDescendant(parse(js)); assertNotNull("Expected function node in parse tree of: " + js, funcParent); Node funcNode = getFuncChild(funcParent); assertEquals(expected, NodeUtil.isFunctionExpression(funcNode)); } private Node findParentOfFuncDescendant(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.isFunction()) { return n; } Node result = findParentOfFuncDescendant(c); if (result != null) { return result; } } return null; } private Node getFuncChild(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.isFunction()) { return c; } } return null; } public void testContainsType() { assertTrue(NodeUtil.containsType( parse("this"), Token.THIS)); assertTrue(NodeUtil.containsType( parse("function foo(){}(this)"), Token.THIS)); assertTrue(NodeUtil.containsType( parse("b?this:null"), Token.THIS)); assertFalse(NodeUtil.containsType( parse("a"), Token.THIS)); assertFalse(NodeUtil.containsType( parse("function foo(){}"), Token.THIS)); assertFalse(NodeUtil.containsType( parse("(b?foo():null)"), Token.THIS)); } public void testReferencesThis() { assertTrue(NodeUtil.referencesThis( parse("this"))); // Don't descend into functions (starts at the script node) assertFalse(NodeUtil.referencesThis( parse("function foo(){this}"))); // But starting with a function properly check for 'this' Node n = parse("function foo(){this}").getFirstChild(); assertEquals(n.getType(), Token.FUNCTION); assertTrue(NodeUtil.referencesThis(n)); assertTrue(NodeUtil.referencesThis( parse("b?this:null"))); assertFalse(NodeUtil.referencesThis( parse("a"))); n = parse("function foo(){}").getFirstChild(); assertEquals(n.getType(), Token.FUNCTION); assertFalse(NodeUtil.referencesThis(n)); assertFalse(NodeUtil.referencesThis( parse("(b?foo():null)"))); } public void testGetNodeTypeReferenceCount() { assertEquals(0, NodeUtil.getNodeTypeReferenceCount( parse("function foo(){}"), Token.THIS, Predicates.alwaysTrue())); assertEquals(1, NodeUtil.getNodeTypeReferenceCount( parse("this"), Token.THIS, Predicates.alwaysTrue())); assertEquals(2, NodeUtil.getNodeTypeReferenceCount( parse("this;function foo(){}(this)"), Token.THIS, Predicates.alwaysTrue())); } public void testIsNameReferenceCount() { assertTrue(NodeUtil.isNameReferenced( parse("function foo(){}"), "foo")); assertTrue(NodeUtil.isNameReferenced( parse("var foo = function(){}"), "foo")); assertFalse(NodeUtil.isNameReferenced( parse("function foo(){}"), "undefined")); assertTrue(NodeUtil.isNameReferenced( parse("undefined"), "undefined")); assertTrue(NodeUtil.isNameReferenced( parse("undefined;function foo(){}(undefined)"), "undefined")); assertTrue(NodeUtil.isNameReferenced( parse("goo.foo"), "goo")); assertFalse(NodeUtil.isNameReferenced( parse("goo.foo"), "foo")); } public void testGetNameReferenceCount() { assertEquals(0, NodeUtil.getNameReferenceCount( parse("function foo(){}"), "undefined")); assertEquals(1, NodeUtil.getNameReferenceCount( parse("undefined"), "undefined")); assertEquals(2, NodeUtil.getNameReferenceCount( parse("undefined;function foo(){}(undefined)"), "undefined")); assertEquals(1, NodeUtil.getNameReferenceCount( parse("goo.foo"), "goo")); assertEquals(0, NodeUtil.getNameReferenceCount( parse("goo.foo"), "foo")); assertEquals(1, NodeUtil.getNameReferenceCount( parse("function foo(){}"), "foo")); assertEquals(1, NodeUtil.getNameReferenceCount( parse("var foo = function(){}"), "foo")); } public void testGetVarsDeclaredInBranch() { assertNodeNames(Sets.newHashSet("foo"), NodeUtil.getVarsDeclaredInBranch( parse("var foo;"))); assertNodeNames(Sets.newHashSet("foo", "goo"), NodeUtil.getVarsDeclaredInBranch( parse("var foo,goo;"))); assertNodeNames(Sets.newHashSet(), NodeUtil.getVarsDeclaredInBranch( parse("foo();"))); assertNodeNames(Sets.newHashSet(), NodeUtil.getVarsDeclaredInBranch( parse("function f(){var foo;}"))); assertNodeNames(Sets.newHashSet("goo"), NodeUtil.getVarsDeclaredInBranch( parse("var goo;function f(){var foo;}"))); } private void assertNodeNames(Set nodeNames, Collection nodes) { Set actualNames = Sets.newHashSet(); for (Node node : nodes) { actualNames.add(node.getString()); } assertEquals(nodeNames, actualNames); } public void testIsControlStructureCodeBlock() { Node root = parse("if (x) foo(); else boo();"); Node ifNode = root.getFirstChild(); Node ifCondition = ifNode.getFirstChild(); Node ifCase = ifNode.getFirstChild().getNext(); Node elseCase = ifNode.getLastChild(); assertFalse(NodeUtil.isControlStructureCodeBlock(ifNode, ifCondition)); assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, ifCase)); assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, elseCase)); } public void testIsFunctionExpression1() { Node root = parse("(function foo() {})"); Node statementNode = root.getFirstChild(); assertTrue(statementNode.isExprResult()); Node functionNode = statementNode.getFirstChild(); assertTrue(functionNode.isFunction()); assertTrue(NodeUtil.isFunctionExpression(functionNode)); } public void testIsFunctionExpression2() { Node root = parse("function foo() {}"); Node functionNode = root.getFirstChild(); assertTrue(functionNode.isFunction()); assertFalse(NodeUtil.isFunctionExpression(functionNode)); } public void testRemoveChildBlock() { // Test removing the inner block. Node actual = parse("{{x()}}"); Node outerBlockNode = actual.getFirstChild(); Node innerBlockNode = outerBlockNode.getFirstChild(); innerBlockNode.setIsSyntheticBlock(true); NodeUtil.removeChild(outerBlockNode, innerBlockNode); String expected = "{{}}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveTryChild1() { // Test removing the finally clause. Node actual = parse("try {foo()} catch(e) {} finally {}"); Node tryNode = actual.getFirstChild(); Node finallyBlock = tryNode.getLastChild(); NodeUtil.removeChild(tryNode, finallyBlock); String expected = "try {foo()} catch(e) {}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveTryChild2() { // Test removing the try clause. Node actual = parse("try {foo()} catch(e) {} finally {}"); Node tryNode = actual.getFirstChild(); Node tryBlock = tryNode.getFirstChild(); NodeUtil.removeChild(tryNode, tryBlock); String expected = "try {} catch(e) {} finally {}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveTryChild3() { // Test removing the catch clause. Node actual = parse("try {foo()} catch(e) {} finally {}"); Node tryNode = actual.getFirstChild(); Node catchBlocks = tryNode.getFirstChild().getNext(); Node catchBlock = catchBlocks.getFirstChild(); NodeUtil.removeChild(catchBlocks, catchBlock); String expected = "try {foo()} finally {}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveTryChild4() { // Test removing the catch clause without a finally. Node actual = parse("try {foo()} catch(e) {} finally {}"); Node tryNode = actual.getFirstChild(); Node catchBlocks = tryNode.getFirstChild().getNext(); NodeUtil.removeChild(tryNode, catchBlocks); String expected = "try {foo()} finally {}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveTryChild5() { Node actual = parse("try {foo()} catch(e) {} finally {}"); Node tryNode = actual.getFirstChild(); Node catchBlocks = tryNode.getFirstChild().getNext(); Node catchBlock = catchBlocks.getFirstChild(); NodeUtil.removeChild(catchBlocks, catchBlock); String expected = "try {foo()} finally {}"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveVarChild() { // Test removing the first child. Node actual = parse("var foo, goo, hoo"); Node varNode = actual.getFirstChild(); Node nameNode = varNode.getFirstChild(); NodeUtil.removeChild(varNode, nameNode); String expected = "var goo, hoo"; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } // Test removing the second child. actual = parse("var foo, goo, hoo"); varNode = actual.getFirstChild(); nameNode = varNode.getFirstChild().getNext(); NodeUtil.removeChild(varNode, nameNode); expected = "var foo, hoo"; difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } // Test removing the last child of several children. actual = parse("var foo, hoo"); varNode = actual.getFirstChild(); nameNode = varNode.getFirstChild().getNext(); NodeUtil.removeChild(varNode, nameNode); expected = "var foo"; difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } // Test removing the last. actual = parse("var hoo"); varNode = actual.getFirstChild(); nameNode = varNode.getFirstChild(); NodeUtil.removeChild(varNode, nameNode); expected = ""; difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveLabelChild1() { // Test removing the first child. Node actual = parse("foo: goo()"); Node labelNode = actual.getFirstChild(); Node callExpressNode = labelNode.getLastChild(); NodeUtil.removeChild(labelNode, callExpressNode); String expected = ""; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveLabelChild2() { // Test removing the first child. Node actual = parse("achoo: foo: goo()"); Node labelNode = actual.getFirstChild(); Node callExpressNode = labelNode.getLastChild(); NodeUtil.removeChild(labelNode, callExpressNode); String expected = ""; String difference = parse(expected).checkTreeEquals(actual); if (difference != null) { assertTrue("Nodes do not match:\n" + difference, false); } } public void testRemoveForChild() { // Test removing the initializer. Node actual = parse("for(var a=0;a<0;a++)foo()"); Node forNode = actual.getFirstChild(); Node child = forNode.getFirstChild(); NodeUtil.removeChild(forNode, child); String expected = "for(;a<0;a++)foo()"; String difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); // Test removing the condition. actual = parse("for(var a=0;a<0;a++)foo()"); forNode = actual.getFirstChild(); child = forNode.getFirstChild().getNext(); NodeUtil.removeChild(forNode, child); expected = "for(var a=0;;a++)foo()"; difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); // Test removing the increment. actual = parse("for(var a=0;a<0;a++)foo()"); forNode = actual.getFirstChild(); child = forNode.getFirstChild().getNext().getNext(); NodeUtil.removeChild(forNode, child); expected = "for(var a=0;a<0;)foo()"; difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); // Test removing the body. actual = parse("for(var a=0;a<0;a++)foo()"); forNode = actual.getFirstChild(); child = forNode.getLastChild(); NodeUtil.removeChild(forNode, child); expected = "for(var a=0;a<0;a++);"; difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); // Test removing the body. actual = parse("for(a in ack)foo();"); forNode = actual.getFirstChild(); child = forNode.getLastChild(); NodeUtil.removeChild(forNode, child); expected = "for(a in ack);"; difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); } public void testMergeBlock1() { // Test removing the initializer. Node actual = parse("{{a();b();}}"); Node parentBlock = actual.getFirstChild(); Node childBlock = parentBlock.getFirstChild(); assertTrue(NodeUtil.tryMergeBlock(childBlock)); String expected = "{a();b();}"; String difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); } public void testMergeBlock2() { // Test removing the initializer. Node actual = parse("foo:{a();}"); Node parentLabel = actual.getFirstChild(); Node childBlock = parentLabel.getLastChild(); assertFalse(NodeUtil.tryMergeBlock(childBlock)); } public void testMergeBlock3() { // Test removing the initializer. String code = "foo:{a();boo()}"; Node actual = parse("foo:{a();boo()}"); Node parentLabel = actual.getFirstChild(); Node childBlock = parentLabel.getLastChild(); assertFalse(NodeUtil.tryMergeBlock(childBlock)); String expected = code; String difference = parse(expected).checkTreeEquals(actual); assertNull("Nodes do not match:\n" + difference, difference); } public void testGetSourceName() { Node n = new Node(Token.BLOCK); Node parent = new Node(Token.BLOCK, n); parent.setSourceFileForTesting("foo"); assertEquals("foo", NodeUtil.getSourceName(n)); } public void testLocalValue1() throws Exception { // Names are not known to be local. assertFalse(testLocalValue("x")); assertFalse(testLocalValue("x()")); assertFalse(testLocalValue("this")); assertFalse(testLocalValue("arguments")); // We can't know if new objects are local unless we know // that they don't alias themselves. assertFalse(testLocalValue("new x()")); // property references are assume to be non-local assertFalse(testLocalValue("(new x()).y")); assertFalse(testLocalValue("(new x())['y']")); // Primitive values are local assertTrue(testLocalValue("null")); assertTrue(testLocalValue("undefined")); assertTrue(testLocalValue("Infinity")); assertTrue(testLocalValue("NaN")); assertTrue(testLocalValue("1")); assertTrue(testLocalValue("'a'")); assertTrue(testLocalValue("true")); assertTrue(testLocalValue("false")); assertTrue(testLocalValue("[]")); assertTrue(testLocalValue("{}")); // The contents of arrays and objects don't matter assertTrue(testLocalValue("[x]")); assertTrue(testLocalValue("{'a':x}")); // Pre-increment results in primitive number assertTrue(testLocalValue("++x")); assertTrue(testLocalValue("--x")); // Post-increment, the previous value matters. assertFalse(testLocalValue("x++")); assertFalse(testLocalValue("x--")); // The left side of an only assign matters if it is an alias or mutable. assertTrue(testLocalValue("x=1")); assertFalse(testLocalValue("x=[]")); assertFalse(testLocalValue("x=y")); // The right hand side of assignment opts don't matter, as they force // a local result. assertTrue(testLocalValue("x+=y")); assertTrue(testLocalValue("x*=y")); // Comparisons always result in locals, as they force a local boolean // result. assertTrue(testLocalValue("x==y")); assertTrue(testLocalValue("x!=y")); assertTrue(testLocalValue("x>y")); // Only the right side of a comma matters assertTrue(testLocalValue("(1,2)")); assertTrue(testLocalValue("(x,1)")); assertFalse(testLocalValue("(x,y)")); // Both the operands of OR matter assertTrue(testLocalValue("1||2")); assertFalse(testLocalValue("x||1")); assertFalse(testLocalValue("x||y")); assertFalse(testLocalValue("1||y")); // Both the operands of AND matter assertTrue(testLocalValue("1&&2")); assertFalse(testLocalValue("x&&1")); assertFalse(testLocalValue("x&&y")); assertFalse(testLocalValue("1&&y")); // Only the results of HOOK matter assertTrue(testLocalValue("x?1:2")); assertFalse(testLocalValue("x?x:2")); assertFalse(testLocalValue("x?1:x")); assertFalse(testLocalValue("x?x:y")); // Results of ops are local values assertTrue(testLocalValue("!y")); assertTrue(testLocalValue("~y")); assertTrue(testLocalValue("y + 1")); assertTrue(testLocalValue("y + z")); assertTrue(testLocalValue("y * z")); assertTrue(testLocalValue("'a' in x")); assertTrue(testLocalValue("typeof x")); assertTrue(testLocalValue("x instanceof y")); assertTrue(testLocalValue("void x")); assertTrue(testLocalValue("void 0")); assertFalse(testLocalValue("{}.x")); assertTrue(testLocalValue("{}.toString()")); assertTrue(testLocalValue("o.toString()")); assertFalse(testLocalValue("o.valueOf()")); assertTrue(testLocalValue("delete a.b")); } public void testLocalValue2() { Node newExpr = getNode("new x()"); assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); Preconditions.checkState(newExpr.isNew()); Node.SideEffectFlags flags = new Node.SideEffectFlags(); flags.clearAllFlags(); newExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); flags.clearAllFlags(); flags.setMutatesThis(); newExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); flags.clearAllFlags(); flags.setReturnsTainted(); newExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(newExpr)); flags.clearAllFlags(); flags.setThrows(); newExpr.setSideEffectFlags(flags.valueOf()); assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); flags.clearAllFlags(); flags.setMutatesArguments(); newExpr.setSideEffectFlags(flags.valueOf()); assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); flags.clearAllFlags(); flags.setMutatesGlobalState(); newExpr.setSideEffectFlags(flags.valueOf()); assertFalse(NodeUtil.evaluatesToLocalValue(newExpr)); } public void testCallSideEffects() { Node callExpr = getNode("new x().method()"); assertTrue(NodeUtil.functionCallHasSideEffects(callExpr)); Node newExpr = callExpr.getFirstChild().getFirstChild(); Preconditions.checkState(newExpr.isNew()); Node.SideEffectFlags flags = new Node.SideEffectFlags(); // No side effects, local result flags.clearAllFlags(); newExpr.setSideEffectFlags(flags.valueOf()); flags.clearAllFlags(); callExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); // Modifies this, local result flags.clearAllFlags(); newExpr.setSideEffectFlags(flags.valueOf()); flags.clearAllFlags(); flags.setMutatesThis(); callExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); // Modifies this, non-local result flags.clearAllFlags(); newExpr.setSideEffectFlags(flags.valueOf()); flags.clearAllFlags(); flags.setMutatesThis(); flags.setReturnsTainted(); callExpr.setSideEffectFlags(flags.valueOf()); assertFalse(NodeUtil.evaluatesToLocalValue(callExpr)); assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); // No modifications, non-local result flags.clearAllFlags(); newExpr.setSideEffectFlags(flags.valueOf()); flags.clearAllFlags(); flags.setReturnsTainted(); callExpr.setSideEffectFlags(flags.valueOf()); assertFalse(NodeUtil.evaluatesToLocalValue(callExpr)); assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); assertFalse(NodeUtil.mayHaveSideEffects(callExpr)); // The new modifies global state, no side-effect call, non-local result // This call could be removed, but not the new. flags.clearAllFlags(); flags.setMutatesGlobalState(); newExpr.setSideEffectFlags(flags.valueOf()); flags.clearAllFlags(); callExpr.setSideEffectFlags(flags.valueOf()); assertTrue(NodeUtil.evaluatesToLocalValue(callExpr)); assertFalse(NodeUtil.functionCallHasSideEffects(callExpr)); assertTrue(NodeUtil.mayHaveSideEffects(callExpr)); } private boolean testLocalValue(String js) { return NodeUtil.evaluatesToLocalValue(getNode(js)); } public void testValidDefine() { assertTrue(testValidDefineValue("1")); assertTrue(testValidDefineValue("-3")); assertTrue(testValidDefineValue("true")); assertTrue(testValidDefineValue("false")); assertTrue(testValidDefineValue("'foo'")); assertFalse(testValidDefineValue("x")); assertFalse(testValidDefineValue("null")); assertFalse(testValidDefineValue("undefined")); assertFalse(testValidDefineValue("NaN")); assertTrue(testValidDefineValue("!true")); assertTrue(testValidDefineValue("-true")); assertTrue(testValidDefineValue("1 & 8")); assertTrue(testValidDefineValue("1 + 8")); assertTrue(testValidDefineValue("'a' + 'b'")); assertFalse(testValidDefineValue("1 & foo")); } private boolean testValidDefineValue(String js) { Node script = parse("var test = " + js + ";"); Node var = script.getFirstChild(); Node name = var.getFirstChild(); Node value = name.getFirstChild(); ImmutableSet defines = ImmutableSet.of(); return NodeUtil.isValidDefineValue(value, defines); } public void testGetNumberValue() { // Strings assertEquals(1.0, NodeUtil.getNumberValue(getNode("'\\uFEFF1'"))); assertEquals(0.0, NodeUtil.getNumberValue(getNode("''"))); assertEquals(0.0, NodeUtil.getNumberValue(getNode("' '"))); assertEquals(0.0, NodeUtil.getNumberValue(getNode("' \\t'"))); assertEquals(0.0, NodeUtil.getNumberValue(getNode("'+0'"))); assertEquals(-0.0, NodeUtil.getNumberValue(getNode("'-0'"))); assertEquals(2.0, NodeUtil.getNumberValue(getNode("'+2'"))); assertEquals(-1.6, NodeUtil.getNumberValue(getNode("'-1.6'"))); assertEquals(16.0, NodeUtil.getNumberValue(getNode("'16'"))); assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '"))); assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '"))); assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123e2'"))); assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123E2'"))); assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123e-2'"))); assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123E-2'"))); assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123e-2'"))); assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123E-2'"))); assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123e-2'"))); assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123E-2'"))); assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123e+2'"))); assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123E+2'"))); assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xf'"))); assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xF'"))); // Chrome and rhino behavior differently from FF and IE. FF and IE // consider a negative hex number to be invalid assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xf'"))); assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xF'"))); assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xf'"))); assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xF'"))); assertEquals(16.0, NodeUtil.getNumberValue(getNode("'0X10'"))); assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'0X10.8'"))); assertEquals(77.0, NodeUtil.getNumberValue(getNode("'077'"))); assertEquals(-77.0, NodeUtil.getNumberValue(getNode("'-077'"))); assertEquals(-77.5, NodeUtil.getNumberValue(getNode("'-077.5'"))); assertEquals( Double.NEGATIVE_INFINITY, NodeUtil.getNumberValue(getNode("'-Infinity'"))); assertEquals( Double.POSITIVE_INFINITY, NodeUtil.getNumberValue(getNode("'Infinity'"))); assertEquals( Double.POSITIVE_INFINITY, NodeUtil.getNumberValue(getNode("'+Infinity'"))); // Firefox treats "infinity" as "Infinity", IE treats it as NaN assertEquals(null, NodeUtil.getNumberValue(getNode("'-infinity'"))); assertEquals(null, NodeUtil.getNumberValue(getNode("'infinity'"))); assertEquals(null, NodeUtil.getNumberValue(getNode("'+infinity'"))); assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'NaN'"))); assertEquals( Double.NaN, NodeUtil.getNumberValue(getNode("'some unknown string'"))); assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'123 blah'"))); // Literals assertEquals(1.0, NodeUtil.getNumberValue(getNode("1"))); // "-1" is parsed as a literal assertEquals(-1.0, NodeUtil.getNumberValue(getNode("-1"))); // "+1" is parse as an op + literal assertEquals(null, NodeUtil.getNumberValue(getNode("+1"))); assertEquals(22.0, NodeUtil.getNumberValue(getNode("22"))); assertEquals(18.0, NodeUtil.getNumberValue(getNode("022"))); assertEquals(34.0, NodeUtil.getNumberValue(getNode("0x22"))); assertEquals( 1.0, NodeUtil.getNumberValue(getNode("true"))); assertEquals( 0.0, NodeUtil.getNumberValue(getNode("false"))); assertEquals( 0.0, NodeUtil.getNumberValue(getNode("null"))); assertEquals( Double.NaN, NodeUtil.getNumberValue(getNode("void 0"))); assertEquals( Double.NaN, NodeUtil.getNumberValue(getNode("void f"))); // values with side-effects are ignored. assertEquals( null, NodeUtil.getNumberValue(getNode("void f()"))); assertEquals( Double.NaN, NodeUtil.getNumberValue(getNode("NaN"))); assertEquals( Double.POSITIVE_INFINITY, NodeUtil.getNumberValue(getNode("Infinity"))); assertEquals( Double.NEGATIVE_INFINITY, NodeUtil.getNumberValue(getNode("-Infinity"))); // "infinity" is not a known name. assertEquals(null, NodeUtil.getNumberValue(getNode("infinity"))); assertEquals(null, NodeUtil.getNumberValue(getNode("-infinity"))); // getNumberValue only converts literals assertEquals(null, NodeUtil.getNumberValue(getNode("x"))); assertEquals(null, NodeUtil.getNumberValue(getNode("x.y"))); assertEquals(null, NodeUtil.getNumberValue(getNode("1/2"))); assertEquals(null, NodeUtil.getNumberValue(getNode("1-2"))); assertEquals(null, NodeUtil.getNumberValue(getNode("+1"))); } public void testIsNumbericResult() { assertTrue(NodeUtil.isNumericResult(getNode("1"))); assertFalse(NodeUtil.isNumericResult(getNode("true"))); assertTrue(NodeUtil.isNumericResult(getNode("+true"))); assertTrue(NodeUtil.isNumericResult(getNode("+1"))); assertTrue(NodeUtil.isNumericResult(getNode("-1"))); assertTrue(NodeUtil.isNumericResult(getNode("-Infinity"))); assertTrue(NodeUtil.isNumericResult(getNode("Infinity"))); assertTrue(NodeUtil.isNumericResult(getNode("NaN"))); assertFalse(NodeUtil.isNumericResult(getNode("undefined"))); assertFalse(NodeUtil.isNumericResult(getNode("void 0"))); assertTrue(NodeUtil.isNumericResult(getNode("a << b"))); assertTrue(NodeUtil.isNumericResult(getNode("a >> b"))); assertTrue(NodeUtil.isNumericResult(getNode("a >>> b"))); assertFalse(NodeUtil.isNumericResult(getNode("a == b"))); assertFalse(NodeUtil.isNumericResult(getNode("a != b"))); assertFalse(NodeUtil.isNumericResult(getNode("a === b"))); assertFalse(NodeUtil.isNumericResult(getNode("a !== b"))); assertFalse(NodeUtil.isNumericResult(getNode("a < b"))); assertFalse(NodeUtil.isNumericResult(getNode("a > b"))); assertFalse(NodeUtil.isNumericResult(getNode("a <= b"))); assertFalse(NodeUtil.isNumericResult(getNode("a >= b"))); assertFalse(NodeUtil.isNumericResult(getNode("a in b"))); assertFalse(NodeUtil.isNumericResult(getNode("a instanceof b"))); assertFalse(NodeUtil.isNumericResult(getNode("'a'"))); assertFalse(NodeUtil.isNumericResult(getNode("'a'+b"))); assertFalse(NodeUtil.isNumericResult(getNode("a+'b'"))); assertFalse(NodeUtil.isNumericResult(getNode("a+b"))); assertFalse(NodeUtil.isNumericResult(getNode("a()"))); assertFalse(NodeUtil.isNumericResult(getNode("''.a"))); assertFalse(NodeUtil.isNumericResult(getNode("a.b"))); assertFalse(NodeUtil.isNumericResult(getNode("a.b()"))); assertFalse(NodeUtil.isNumericResult(getNode("a().b()"))); assertFalse(NodeUtil.isNumericResult(getNode("new a()"))); // Definitely not numeric assertFalse(NodeUtil.isNumericResult(getNode("([1,2])"))); assertFalse(NodeUtil.isNumericResult(getNode("({a:1})"))); // Recurse into the expression when necessary. assertTrue(NodeUtil.isNumericResult(getNode("1 && 2"))); assertTrue(NodeUtil.isNumericResult(getNode("1 || 2"))); assertTrue(NodeUtil.isNumericResult(getNode("a ? 2 : 3"))); assertTrue(NodeUtil.isNumericResult(getNode("a,1"))); assertTrue(NodeUtil.isNumericResult(getNode("a=1"))); } public void testIsBooleanResult() { assertFalse(NodeUtil.isBooleanResult(getNode("1"))); assertTrue(NodeUtil.isBooleanResult(getNode("true"))); assertFalse(NodeUtil.isBooleanResult(getNode("+true"))); assertFalse(NodeUtil.isBooleanResult(getNode("+1"))); assertFalse(NodeUtil.isBooleanResult(getNode("-1"))); assertFalse(NodeUtil.isBooleanResult(getNode("-Infinity"))); assertFalse(NodeUtil.isBooleanResult(getNode("Infinity"))); assertFalse(NodeUtil.isBooleanResult(getNode("NaN"))); assertFalse(NodeUtil.isBooleanResult(getNode("undefined"))); assertFalse(NodeUtil.isBooleanResult(getNode("void 0"))); assertFalse(NodeUtil.isBooleanResult(getNode("a << b"))); assertFalse(NodeUtil.isBooleanResult(getNode("a >> b"))); assertFalse(NodeUtil.isBooleanResult(getNode("a >>> b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a == b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a != b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a === b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a !== b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a < b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a > b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a <= b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a >= b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a in b"))); assertTrue(NodeUtil.isBooleanResult(getNode("a instanceof b"))); assertFalse(NodeUtil.isBooleanResult(getNode("'a'"))); assertFalse(NodeUtil.isBooleanResult(getNode("'a'+b"))); assertFalse(NodeUtil.isBooleanResult(getNode("a+'b'"))); assertFalse(NodeUtil.isBooleanResult(getNode("a+b"))); assertFalse(NodeUtil.isBooleanResult(getNode("a()"))); assertFalse(NodeUtil.isBooleanResult(getNode("''.a"))); assertFalse(NodeUtil.isBooleanResult(getNode("a.b"))); assertFalse(NodeUtil.isBooleanResult(getNode("a.b()"))); assertFalse(NodeUtil.isBooleanResult(getNode("a().b()"))); assertFalse(NodeUtil.isBooleanResult(getNode("new a()"))); assertTrue(NodeUtil.isBooleanResult(getNode("delete a"))); // Definitely not boolean assertFalse(NodeUtil.isBooleanResult(getNode("([true,false])"))); assertFalse(NodeUtil.isBooleanResult(getNode("({a:true})"))); // These are boolean but aren't handled yet, "false" here means "unknown". assertTrue(NodeUtil.isBooleanResult(getNode("true && false"))); assertTrue(NodeUtil.isBooleanResult(getNode("true || false"))); assertTrue(NodeUtil.isBooleanResult(getNode("a ? true : false"))); assertTrue(NodeUtil.isBooleanResult(getNode("a,true"))); assertTrue(NodeUtil.isBooleanResult(getNode("a=true"))); assertFalse(NodeUtil.isBooleanResult(getNode("a=1"))); } public void testMayBeString() { assertFalse(NodeUtil.mayBeString(getNode("1"))); assertFalse(NodeUtil.mayBeString(getNode("true"))); assertFalse(NodeUtil.mayBeString(getNode("+true"))); assertFalse(NodeUtil.mayBeString(getNode("+1"))); assertFalse(NodeUtil.mayBeString(getNode("-1"))); assertFalse(NodeUtil.mayBeString(getNode("-Infinity"))); assertFalse(NodeUtil.mayBeString(getNode("Infinity"))); assertFalse(NodeUtil.mayBeString(getNode("NaN"))); assertFalse(NodeUtil.mayBeString(getNode("undefined"))); assertFalse(NodeUtil.mayBeString(getNode("void 0"))); assertFalse(NodeUtil.mayBeString(getNode("null"))); assertFalse(NodeUtil.mayBeString(getNode("a << b"))); assertFalse(NodeUtil.mayBeString(getNode("a >> b"))); assertFalse(NodeUtil.mayBeString(getNode("a >>> b"))); assertFalse(NodeUtil.mayBeString(getNode("a == b"))); assertFalse(NodeUtil.mayBeString(getNode("a != b"))); assertFalse(NodeUtil.mayBeString(getNode("a === b"))); assertFalse(NodeUtil.mayBeString(getNode("a !== b"))); assertFalse(NodeUtil.mayBeString(getNode("a < b"))); assertFalse(NodeUtil.mayBeString(getNode("a > b"))); assertFalse(NodeUtil.mayBeString(getNode("a <= b"))); assertFalse(NodeUtil.mayBeString(getNode("a >= b"))); assertFalse(NodeUtil.mayBeString(getNode("a in b"))); assertFalse(NodeUtil.mayBeString(getNode("a instanceof b"))); assertTrue(NodeUtil.mayBeString(getNode("'a'"))); assertTrue(NodeUtil.mayBeString(getNode("'a'+b"))); assertTrue(NodeUtil.mayBeString(getNode("a+'b'"))); assertTrue(NodeUtil.mayBeString(getNode("a+b"))); assertTrue(NodeUtil.mayBeString(getNode("a()"))); assertTrue(NodeUtil.mayBeString(getNode("''.a"))); assertTrue(NodeUtil.mayBeString(getNode("a.b"))); assertTrue(NodeUtil.mayBeString(getNode("a.b()"))); assertTrue(NodeUtil.mayBeString(getNode("a().b()"))); assertTrue(NodeUtil.mayBeString(getNode("new a()"))); // These can't be strings but they aren't handled yet. assertFalse(NodeUtil.mayBeString(getNode("1 && 2"))); assertFalse(NodeUtil.mayBeString(getNode("1 || 2"))); assertFalse(NodeUtil.mayBeString(getNode("1 ? 2 : 3"))); assertFalse(NodeUtil.mayBeString(getNode("1,2"))); assertFalse(NodeUtil.mayBeString(getNode("a=1"))); assertFalse(NodeUtil.mayBeString(getNode("1+1"))); assertFalse(NodeUtil.mayBeString(getNode("true+true"))); assertFalse(NodeUtil.mayBeString(getNode("null+null"))); assertFalse(NodeUtil.mayBeString(getNode("NaN+NaN"))); // These are not strings but they aren't primitives either assertTrue(NodeUtil.mayBeString(getNode("([1,2])"))); assertTrue(NodeUtil.mayBeString(getNode("({a:1})"))); assertTrue(NodeUtil.mayBeString(getNode("({}+1)"))); assertTrue(NodeUtil.mayBeString(getNode("(1+{})"))); assertTrue(NodeUtil.mayBeString(getNode("([]+1)"))); assertTrue(NodeUtil.mayBeString(getNode("(1+[])"))); } public void testValidNames() { assertTrue(NodeUtil.isValidPropertyName("a")); assertTrue(NodeUtil.isValidPropertyName("a3")); assertFalse(NodeUtil.isValidPropertyName("3a")); assertFalse(NodeUtil.isValidPropertyName("a.")); assertFalse(NodeUtil.isValidPropertyName(".a")); assertFalse(NodeUtil.isValidPropertyName("a.b")); assertFalse(NodeUtil.isValidPropertyName("true")); assertFalse(NodeUtil.isValidPropertyName("a.true")); assertFalse(NodeUtil.isValidPropertyName("a..b")); assertTrue(NodeUtil.isValidSimpleName("a")); assertTrue(NodeUtil.isValidSimpleName("a3")); assertFalse(NodeUtil.isValidSimpleName("3a")); assertFalse(NodeUtil.isValidSimpleName("a.")); assertFalse(NodeUtil.isValidSimpleName(".a")); assertFalse(NodeUtil.isValidSimpleName("a.b")); assertFalse(NodeUtil.isValidSimpleName("true")); assertFalse(NodeUtil.isValidSimpleName("a.true")); assertFalse(NodeUtil.isValidSimpleName("a..b")); assertTrue(NodeUtil.isValidQualifiedName("a")); assertTrue(NodeUtil.isValidQualifiedName("a3")); assertFalse(NodeUtil.isValidQualifiedName("3a")); assertFalse(NodeUtil.isValidQualifiedName("a.")); assertFalse(NodeUtil.isValidQualifiedName(".a")); assertTrue(NodeUtil.isValidQualifiedName("a.b")); assertFalse(NodeUtil.isValidQualifiedName("true")); assertFalse(NodeUtil.isValidQualifiedName("a.true")); assertFalse(NodeUtil.isValidQualifiedName("a..b")); } public void testGetNearestFunctionName() { testFunctionName("(function() {})()", null); testFunctionName("function a() {}", "a"); testFunctionName("(function a() {})", "a"); testFunctionName("({a:function () {}})", "a"); testFunctionName("({get a() {}})", "a"); testFunctionName("({set a(b) {}})", "a"); testFunctionName("({set a(b) {}})", "a"); testFunctionName("({1:function () {}})", "1"); testFunctionName("var a = function a() {}", "a"); testFunctionName("var a;a = function a() {}", "a"); testFunctionName("var o;o.a = function a() {}", "o.a"); testFunctionName("this.a = function a() {}", "this.a"); } public void testGetBestLValue() { assertEquals("x", getFunctionLValue("var x = function() {};")); assertEquals("x", getFunctionLValue("x = function() {};")); assertEquals("x", getFunctionLValue("function x() {};")); assertEquals("x", getFunctionLValue("var x = y ? z : function() {};")); assertEquals("x", getFunctionLValue("var x = y ? function() {} : z;")); assertEquals("x", getFunctionLValue("var x = y && function() {};")); assertEquals("x", getFunctionLValue("var x = y || function() {};")); assertEquals("x", getFunctionLValue("var x = (y, function() {});")); } public void testIsNaN() { assertEquals(true, NodeUtil.isNaN(getNode("NaN"))); assertEquals(false, NodeUtil.isNaN(getNode("Infinity"))); assertEquals(false, NodeUtil.isNaN(getNode("x"))); assertEquals(true, NodeUtil.isNaN(getNode("0/0"))); assertEquals(false, NodeUtil.isNaN(getNode("1/0"))); assertEquals(false, NodeUtil.isNaN(getNode("0/1"))); assertEquals(false, NodeUtil.isNaN(IR.number(0.0))); } public void testIsExecutedExactlyOnce() { assertEquals(true, executedOnceTestCase("x;")); assertEquals(true, executedOnceTestCase("x && 1;")); assertEquals(false, executedOnceTestCase("1 && x;")); assertEquals(false, executedOnceTestCase("1 && (x && 1);")); assertEquals(true, executedOnceTestCase("x || 1;")); assertEquals(false, executedOnceTestCase("1 || x;")); assertEquals(false, executedOnceTestCase("1 && (x || 1);")); assertEquals(true, executedOnceTestCase("x ? 1 : 2;")); assertEquals(false, executedOnceTestCase("1 ? 1 : x;")); assertEquals(false, executedOnceTestCase("1 ? x : 2;")); assertEquals(false, executedOnceTestCase("1 && (x ? 1 : 2);")); assertEquals(true, executedOnceTestCase("if (x) {}")); assertEquals(false, executedOnceTestCase("if (true) {x;}")); assertEquals(false, executedOnceTestCase("if (true) {} else {x;}")); assertEquals(false, executedOnceTestCase("if (1) { if (x) {} }")); assertEquals(true, executedOnceTestCase("for(x;;){}")); assertEquals(false, executedOnceTestCase("for(;x;){}")); assertEquals(false, executedOnceTestCase("for(;;x){}")); assertEquals(false, executedOnceTestCase("for(;;){x;}")); assertEquals(false, executedOnceTestCase("if (1) { for(x;;){} }")); assertEquals(false, executedOnceTestCase("for(x in {}){}")); assertEquals(true, executedOnceTestCase("for({}.a in x){}")); assertEquals(false, executedOnceTestCase("for({}.a in {}){x}")); assertEquals(false, executedOnceTestCase("if (1) { for(x in {}){} }")); assertEquals(true, executedOnceTestCase("switch (x) {}")); assertEquals(false, executedOnceTestCase("switch (1) {case x:}")); assertEquals(false, executedOnceTestCase("switch (1) {case 1: x}")); assertEquals(false, executedOnceTestCase("switch (1) {default: x}")); assertEquals(false, executedOnceTestCase("if (1) { switch (x) {} }")); assertEquals(false, executedOnceTestCase("while (x) {}")); assertEquals(false, executedOnceTestCase("while (1) {x}")); assertEquals(false, executedOnceTestCase("do {} while (x)")); assertEquals(false, executedOnceTestCase("do {x} while (1)")); assertEquals(false, executedOnceTestCase("try {x} catch (e) {}")); assertEquals(false, executedOnceTestCase("try {} catch (e) {x}")); assertEquals(true, executedOnceTestCase("try {} finally {x}")); assertEquals(false, executedOnceTestCase("if (1) { try {} finally {x} }")); } public void testNewQualifiedNameNode1() { Node actual = NodeUtil.newQualifiedNameNode( new GoogleCodingConvention(), "ns.prop"); Node expected = IR.getprop( IR.name("ns"), IR.string("prop")); assertNodeTreesEqual(expected, actual); } public void testNewQualifiedNameNode2() { Node actual = NodeUtil.newQualifiedNameNode( new GoogleCodingConvention(), "this.prop"); Node expected = IR.getprop( IR.thisNode(), IR.string("prop")); assertNodeTreesEqual(expected, actual); } private boolean executedOnceTestCase(String code) { Node ast = parse(code); Node nameNode = getNameNode(ast, "x"); return NodeUtil.isExecutedExactlyOnce(nameNode); } private String getFunctionLValue(String js) { Node lVal = NodeUtil.getBestLValue(getFunctionNode(js)); return lVal == null ? null : lVal.getString(); } private void assertNodeTreesEqual( Node expected, Node actual) { String error = expected.checkTreeEquals(actual); assertNull(error, error); } static void testFunctionName(String js, String expected) { assertEquals( expected, NodeUtil.getNearestFunctionName(getFunctionNode(js))); } static Node getFunctionNode(String js) { Node root = parse(js); return getFunctionNode(root); } static Node getFunctionNode(Node n) { if (n.isFunction()) { return n; } for (Node c : n.children()) { Node result = getFunctionNode(c); if (result != null) { return result; } } return null; } static Node getNameNode(Node n, String name) { if (n.isName() && n.getString().equals(name)) { return n; } for (Node c : n.children()) { Node result = getNameNode(c, name); if (result != null) { return result; } } return null; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ReplaceMessagesForChromeTest.java0000644000175000017500000000702112115204405031377 0ustar apoapo/* * Copyright 2012 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.javascript.jscomp.JsMessage.Style.RELAX; import com.google.javascript.jscomp.JsMessage.Style; /** * Test which checks that replacer works correctly. * */ public class ReplaceMessagesForChromeTest extends CompilerTestCase { private Style style = RELAX; @Override protected CompilerPass getProcessor(Compiler compiler) { return new ReplaceMessagesForChrome(compiler, new GoogleJsMessageIdGenerator(null), false, style); } @Override protected int getNumRepetitions() { // No longer valid on the second run. return 1; } @Override protected void setUp() { style = RELAX; } public void testReplaceSimpleMessage() { test("/** @desc A simple message. */\n" + "var MSG_A = goog.getMsg('Hello world');", "var MSG_A=chrome.i18n.getMessage('8660696502365331902');"); test("/** @desc A message attached to an object. */\n" + "foo.bar.MSG_B = goog.getMsg('Goodbye world');", "foo.bar.MSG_B=chrome.i18n.getMessage('2356086230621084760');"); } public void testReplaceSinglePlaceholder() { test("/** @desc A message with one placeholder. */\n" + "var MSG_C = goog.getMsg('Hello, {$name}', {name: 'Tyler'});", "var MSG_C=chrome.i18n.getMessage('4985325380591528435', ['Tyler']);"); } public void testReplaceTwoPlaceholders() { test("/** @desc A message with two placeholders. */\n" + "var MSG_D = goog.getMsg('{$greeting}, {$name}', " + "{greeting: 'Hi', name: 'Tyler'});", "var MSG_D=chrome.i18n.getMessage('3605047247574980322', " + "['Hi', 'Tyler']);"); test("/** @desc A message with two placeholders, but their order is\n" + " * reversed in the object literal. (Shouldn't make a difference.)\n" + " */\n" + "var MSG_E = goog.getMsg('{$greeting}, {$name}!', " + "{name: 'Tyler', greeting: 'Hi'});", "var MSG_E=chrome.i18n.getMessage('691522386483664339', " + "['Hi', 'Tyler']);"); } public void testReplacePlaceholderMissingValue() { test("/** @desc A message with two placeholders, but one is missing. */\n" + "var MSG_F = goog.getMsg('{$greeting}, {$name}!', {name: 'Tyler'});", null, JsMessageVisitor.MESSAGE_TREE_MALFORMED); } public void testReplaceTwoPlaceholdersNonAlphaOrder() { test("/** @desc A message with two placeholders not in order .*/\n" + "var MSG_G = goog.getMsg('{$name}: {$greeting}', " + "{greeting: 'Salutations', name: 'Tyler'});", "var MSG_G=chrome.i18n.getMessage('7437383242562773138', " + "['Salutations', 'Tyler']);"); } public void testReplaceExternalMessage() { test("/** @desc A message that was extracted with SoyMsgExtractor. */\n" + "var MSG_EXTERNAL_1357902468 = goog.getMsg('Hello world');", "var MSG_EXTERNAL_1357902468 = chrome.i18n.getMessage('1357902468');"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CompilerTypeTestCase.java0000644000175000017500000001067112115204405027744 0ustar apoapo/* * 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.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; abstract class CompilerTypeTestCase extends BaseJSTypeTestCase { static final String ACTIVE_X_OBJECT_DEF = "/**\n" + " * @param {string} progId\n" + " * @param {string=} opt_location\n" + " * @constructor\n" + " * @see http://msdn.microsoft.com/en-us/library/7sw4ddf8.aspx\n" + " */\n" + "function ActiveXObject(progId, opt_location) {}\n"; static final String CLOSURE_DEFS = "var goog = {};" + "goog.inherits = function(x, y) {};" + "/** @type {!Function} */ goog.abstractMethod = function() {};" + "goog.isArray = function(x) {};" + "goog.isDef = function(x) {};" + "goog.isFunction = function(x) {};" + "goog.isNull = function(x) {};" + "goog.isString = function(x) {};" + "goog.isObject = function(x) {};" + "goog.isDefAndNotNull = function(x) {};" + "goog.array = {};" + // simplified ArrayLike definition "/**\n" + " * @typedef {Array|{length: number}}\n" + " */\n" + "goog.array.ArrayLike;" + "/**\n" + " * @param {Array.|{length:number}} arr\n" + " * @param {function(this:S, T, number, goog.array.ArrayLike):boolean} f\n" + " * @param {S=} opt_obj\n" + " * @return {!Array.}\n" + " * @template T,S\n" + " */" + "goog.array.filter = function(arr, f, opt_obj){};" + "goog.asserts = {};" + "/** @return {*} */ goog.asserts.assert = function(x) { return x; };"; /** A default set of externs for testing. */ static final String DEFAULT_EXTERNS = "/** @constructor \n * @param {*=} opt_value */ " + "function Object(opt_value) {}" + "/** @constructor \n * @param {*} var_args */ " + "function Function(var_args) {}" + "/** @type {!Function} */ Function.prototype.apply;" + "/** @type {!Function} */ Function.prototype.bind;" + "/** @type {!Function} */ Function.prototype.call;" + "/** @constructor \n * @param {*=} arg \n @return {string} */" + "function String(arg) {}" + "/** @param {number} sliceArg */\n" + "String.prototype.slice = function(sliceArg) {};" + "/** @type {number} */ String.prototype.length;" + "/** @constructor \n * @param {*} var_args \n @return {!Array} */" + "function Array(var_args) {}\n" + "/** @type {number} */ Array.prototype.length;\n" + "/**\n" + " * @param {...T} var_args\n" + " * @return {number} The new length of the array.\n" + " * @this {{length: number}|Array.}\n" + " * @template T\n" + " * @modifies {this}\n" + " */\n" + "Array.prototype.push = function(var_args) {};" + "/** @constructor */\n" + "function Arguments() {}\n" + "/** @type {number} */\n" + "Arguments.prototype.length;\n" + "/** @type {!Arguments} */\n" + "var arguments;" + "" + ACTIVE_X_OBJECT_DEF; protected Compiler compiler; protected CompilerOptions getOptions() { CompilerOptions options = new CompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5); options.setWarningLevel( DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.MISPLACED_TYPE_ANNOTATION, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.INVALID_CASTS, CheckLevel.WARNING); options.setCodingConvention(getCodingConvention()); return options; } protected CodingConvention getCodingConvention() { return new GoogleCodingConvention(); } @Override protected void setUp() throws Exception { compiler = new Compiler(); compiler.initOptions(getOptions()); registry = compiler.getTypeRegistry(); initTypes(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/LiveVariableAnalysisTest.java0000644000175000017500000003542012115204405030604 0ustar apoapo/* * 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.javascript.jscomp.DataFlowAnalysis.FlowState; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; /** * Tests for {@link LiveVariablesAnalysis}. Test cases are snippets of a * function and assertions are made at the instruction labeled with {@code X}. * */ public class LiveVariableAnalysisTest extends TestCase { private LiveVariablesAnalysis liveness = null; public void testStraightLine() { // A sample of simple straight line of code with different liveness changes. assertNotLiveBeforeX("X:var a;", "a"); assertNotLiveAfterX("X:var a;", "a"); assertNotLiveAfterX("X:var a=1;", "a"); assertLiveAfterX("X:var a=1; a()", "a"); assertNotLiveBeforeX("X:var a=1; a()", "a"); assertLiveBeforeX("var a;X:a;", "a"); assertLiveBeforeX("var a;X:a=a+1;", "a"); assertLiveBeforeX("var a;X:a+=1;", "a"); assertLiveBeforeX("var a;X:a++;", "a"); assertNotLiveAfterX("var a,b;X:b();", "a"); assertNotLiveBeforeX("var a,b;X:b();", "a"); assertLiveBeforeX("var a,b;X:b(a);", "a"); assertLiveBeforeX("var a,b;X:b(1,2,3,b(a + 1));", "a"); assertNotLiveBeforeX("var a,b;X:a=1;b(a)", "a"); assertNotLiveAfterX("var a,b;X:b(a);b()", "a"); assertLiveBeforeX("var a,b;X:b();b=1;a()", "b"); assertLiveAfterX("X:a();var a;a()", "a"); assertNotLiveAfterX("X:a();var a=1;a()", "a"); assertLiveBeforeX("var a,b;X:a,b=1", "a"); } public void testProperties() { // Reading property of a local variable makes that variable live. assertLiveBeforeX("var a,b;X:a.P;", "a"); // Assigning to a property doesn't kill "a". It makes it live instead. assertLiveBeforeX("var a,b;X:a.P=1;b()", "a"); assertLiveBeforeX("var a,b;X:a.P.Q=1;b()", "a"); // An "a" in a different context. assertNotLiveAfterX("var a,b;X:b.P.Q.a=1;", "a"); assertLiveBeforeX("var a,b;X:b.P.Q=a;", "a"); } public void testConditions() { // Reading the condition makes the variable live. assertLiveBeforeX("var a,b;X:if(a){}", "a"); assertLiveBeforeX("var a,b;X:if(a||b) {}", "a"); assertLiveBeforeX("var a,b;X:if(b||a) {}", "a"); assertLiveBeforeX("var a,b;X:if(b||b(a)) {}", "a"); assertNotLiveAfterX("var a,b;X:b();if(a) {}", "b"); // We can kill within a condition as well. assertNotLiveAfterX("var a,b;X:a();if(a=b){}a()", "a"); assertNotLiveAfterX("var a,b;X:a();while(a=b){}a()", "a"); // The kill can be "conditional" due to short circuit. assertNotLiveAfterX("var a,b;X:a();if((a=b)&&b){}a()", "a"); assertNotLiveAfterX("var a,b;X:a();while((a=b)&&b){}a()", "a"); assertLiveBeforeX("var a,b;a();X:if(b&&(a=b)){}a()", "a"); // Assumed live. assertLiveBeforeX("var a,b;a();X:if(a&&(a=b)){}a()", "a"); assertLiveBeforeX("var a,b;a();X:while(b&&(a=b)){}a()", "a"); assertLiveBeforeX("var a,b;a();X:while(a&&(a=b)){}a()", "a"); } public void testArrays() { assertLiveBeforeX("var a;X:a[1]", "a"); assertLiveBeforeX("var a,b;X:b[a]", "a"); assertLiveBeforeX("var a,b;X:b[1,2,3,4,b(a)]", "a"); assertLiveBeforeX("var a,b;X:b=[a,'a']", "a"); assertNotLiveBeforeX("var a,b;X:a=[];b(a)", "a"); // Element assignment doesn't kill the array. assertLiveBeforeX("var a;X:a[1]=1", "a"); } public void testTwoPaths() { // Both Paths. assertLiveBeforeX("var a,b;X:if(b){b(a)}else{b(a)};", "a"); // Only one path. assertLiveBeforeX("var a,b;X:if(b){b(b)}else{b(a)};", "a"); assertLiveBeforeX("var a,b;X:if(b){b(a)}else{b(b)};", "a"); // None of the paths. assertNotLiveAfterX("var a,b;X:if(b){b(b)}else{b(b)};", "a"); // At the very end. assertLiveBeforeX("var a,b;X:if(b){b(b)}else{b(b)}a();", "a"); // The loop might or might not be executed. assertLiveBeforeX("var a;X:while(param1){a()};", "a"); assertLiveBeforeX("var a;X:while(param1){a=1};a()", "a"); // Same idea with if. assertLiveBeforeX("var a;X:if(param1){a()};", "a"); assertLiveBeforeX("var a;X:if(param1){a=1};a()", "a"); // This is different in DO. We know for sure at least one iteration is // executed. assertNotLiveAfterX("X:var a;do{a=1}while(param1);a()", "a"); } public void testThreePaths() { assertLiveBeforeX("var a;X:if(1){}else if(2){}else{a()};", "a"); assertLiveBeforeX("var a;X:if(1){}else if(2){a()}else{};", "a"); assertLiveBeforeX("var a;X:if(1){a()}else if(2){}else{};", "a"); assertLiveBeforeX("var a;X:if(1){}else if(2){}else{};a()", "a"); } public void testHooks() { assertLiveBeforeX("var a;X:1?a=1:1;a()", "a"); // Unfortunately, we cannot prove the following because we assume there is // no control flow within a hook (i.e. no joins / set unions). // assertNotLiveAfterX("var a;X:1?a=1:a=2;a", "a"); assertLiveBeforeX("var a,b;X:b=1?a:2", "a"); } public void testForLoops() { // Induction variable should not be live after the loop. assertNotLiveBeforeX("var a,b;for(a=0;a<9;a++){b(a)};X:b", "a"); assertNotLiveBeforeX("var a,b;for(a in b){a()};X:b", "a"); assertNotLiveBeforeX("var a,b;for(a in b){a()};X:a", "b"); assertLiveBeforeX("var b;for(var a in b){X:a()};", "a"); // It should be live within the loop even if it is not used. assertLiveBeforeX("var a,b;for(a=0;a<9;a++){X:1}", "a"); assertLiveAfterX("var a,b;for(a in b){X:b};", "a"); // For-In should serve as a gen as well. assertLiveBeforeX("var a,b; X:for(a in b){ }", "a"); // "a in b" should kill "a" before it. // Can't prove this unless we have branched backward DFA. //assertNotLiveAfterX("var a,b;X:b;for(a in b){a()};", "a"); // Unless it is used before. assertLiveBeforeX("var a,b;X:a();b();for(a in b){a()};", "a"); // Initializer assertLiveBeforeX("var a,b;X:b;for(b=a;;){};", "a"); assertNotLiveBeforeX("var a,b;X:a;for(b=a;;){b()};b();", "b"); } public void testNestedLoops() { assertLiveBeforeX("var a;X:while(1){while(1){a()}}", "a"); assertLiveBeforeX("var a;X:while(1){while(1){while(1){a()}}}", "a"); assertLiveBeforeX("var a;X:while(1){while(1){a()};a=1}", "a"); assertLiveAfterX("var a;while(1){while(1){a()};X:a=1;}", "a"); assertLiveAfterX("var a;while(1){X:a=1;while(1){a()}}", "a"); assertNotLiveBeforeX( "var a;X:1;do{do{do{a=1;}while(1)}while(1)}while(1);a()", "a"); } public void testSwitches() { assertLiveBeforeX("var a,b;X:switch(a){}", "a"); assertLiveBeforeX("var a,b;X:switch(b){case(a):break;}", "a"); assertLiveBeforeX("var a,b;X:switch(b){case(b):case(a):break;}", "a"); assertNotLiveBeforeX( "var a,b;X:switch(b){case 1:a=1;break;default:a=2;break};a()", "a"); assertLiveBeforeX("var a,b;X:switch(b){default:a();break;}", "a"); } public void testAssignAndReadInCondition() { // BUG #1358904 // Technically, this isn't exactly true....but we haven't model control flow // within an instruction. assertLiveBeforeX("var a, b; X: if ((a = this) && (b = a)) {}", "a"); assertNotLiveBeforeX("var a, b; X: a = 1, b = 1;", "a"); assertNotLiveBeforeX("var a; X: a = 1, a = 1;", "a"); } public void testParam() { // Unused parameter should not be live. assertNotLiveAfterX("var a;X:a()", "param1"); assertLiveBeforeX("var a;X:a(param1)", "param1"); assertNotLiveAfterX("var a;X:a();a(param2)", "param1"); } public void testExpressionInForIn() { assertLiveBeforeX("var a = [0]; X:for (a[1] in foo) { }", "a"); } public void testArgumentsArray() { // Check that use of arguments forces the parameters into the // escaped set. assertEscaped("arguments[0]", "param1"); assertEscaped("arguments[0]", "param2"); assertEscaped("var args = arguments", "param1"); assertEscaped("var args = arguments", "param2"); assertNotEscaped("arguments = []", "param1"); assertNotEscaped("arguments = []", "param2"); assertEscaped("arguments[0] = 1", "param1"); assertEscaped("arguments[0] = 1", "param2"); assertEscaped("arguments[arguments[0]] = 1", "param1"); assertEscaped("arguments[arguments[0]] = 1", "param2"); } public void testTryCatchFinally() { assertLiveAfterX("var a; try {X:a=1} finally {a}", "a"); assertLiveAfterX("var a; try {a()} catch(e) {X:a=1} finally {a}", "a"); // Because the outer catch doesn't catch any exceptions at all, the read of // "a" within the catch block should not make "a" live. assertNotLiveAfterX("var a = 1; try {" + "try {a()} catch(e) {X:1} } catch(E) {a}", "a"); assertLiveAfterX("var a; while(1) { try {X:a=1;break} finally {a}}", "a"); } public void testForInAssignment() { assertLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "a"); // No one refers to b after the first iteration. assertNotLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "b"); assertLiveBeforeX("var a,b; for (var y in a = b) { X:a[y] }", "y"); assertLiveAfterX("var a,b; for (var y in a = b) { a[y]; X: y();}", "a"); } public void testExceptionThrowingAssignments() { assertLiveBeforeX("try{var a; X:a=foo();a} catch(e) {e()}", "a"); assertLiveBeforeX("try{X:var a=foo();a} catch(e) {e()}", "a"); assertLiveBeforeX("try{X:var a=foo()} catch(e) {e(a)}", "a"); } public void testInnerFunctions() { assertLiveBeforeX("function a() {}; X: a()", "a"); assertNotLiveBeforeX("X: function a() {}", "a"); assertLiveBeforeX("a = function(){}; function a() {}; X: a()", "a"); // NOTE: function a() {} has no CFG node representation since it is not // part of the control execution. assertLiveAfterX("X: a = function(){}; function a() {}; a()", "a"); assertNotLiveBeforeX("X: a = function(){}; function a() {}; a()", "a"); } public void testEscaped() { assertEscaped("var a;function b(){a()}", "a"); assertEscaped("var a;function b(){param1()}", "param1"); assertEscaped("var a;function b(){function c(){a()}}", "a"); assertEscaped("var a;function b(){param1.x = function() {a()}}", "a"); assertEscaped("try{} catch(e){}", "e"); assertNotEscaped("var a;function b(){var c; c()}", "c"); assertNotEscaped("var a;function f(){function b(){var c;c()}}", "c"); assertNotEscaped("var a;function b(){};a()", "a"); assertNotEscaped("var a;function f(){function b(){}}a()", "a"); assertNotEscaped("var a;function b(){var a;a()};a()", "a"); // Escaped by exporting. assertEscaped("var _x", "_x"); } public void testEscapedLiveness() { assertNotLiveBeforeX("var a;X:a();function b(){a()}", "a"); } public void testBug1449316() { assertLiveBeforeX("try {var x=[]; X:var y=x[0]} finally {foo()}", "x"); } private void assertLiveBeforeX(String src, String var) { FlowState state = getFlowStateAtX(src); assertNotNull(src + " should contain a label 'X:'", state); assertTrue("Variable" + var + " should be live before X", state.getIn() .isLive(liveness.getVarIndex(var))); } private void assertLiveAfterX(String src, String var) { FlowState state = getFlowStateAtX(src); assertTrue("Label X should be in the input program.", state != null); assertTrue("Variable" + var + " should be live after X", state.getOut() .isLive(liveness.getVarIndex(var))); } private void assertNotLiveAfterX(String src, String var) { FlowState state = getFlowStateAtX(src); assertTrue("Label X should be in the input program.", state != null); assertTrue("Variable" + var + " should not be live after X", !state .getOut().isLive(liveness.getVarIndex(var))); } private void assertNotLiveBeforeX(String src, String var) { FlowState state = getFlowStateAtX(src); assertTrue("Label X should be in the input program.", state != null); assertTrue("Variable" + var + " should not be live before X", !state .getIn().isLive(liveness.getVarIndex(var))); } private FlowState getFlowStateAtX( String src) { liveness = computeLiveness(src); return getFlowStateAtX(liveness.getCfg().getEntry().getValue(), liveness .getCfg()); } private FlowState getFlowStateAtX( Node node, ControlFlowGraph cfg) { if (node.isLabel()) { if (node.getFirstChild().getString().equals("X")) { return cfg.getNode(node.getLastChild()).getAnnotation(); } } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { FlowState state = getFlowStateAtX(c, cfg); if (state != null) { return state; } } return null; } private static void assertEscaped(String src, String name) { for (Var var : computeLiveness(src).getEscapedLocals()) { if (var.name.equals(name)) { return; } } fail("Variable " + name + " should be in the escaped local list."); } private static void assertNotEscaped(String src, String name) { for (Var var : computeLiveness(src).getEscapedLocals()) { assertFalse(var.name.equals(name)); } } private static LiveVariablesAnalysis computeLiveness(String src) { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.setCodingConvention(new GoogleCodingConvention()); compiler.initOptions(options); src = "function _FUNCTION(param1, param2){" + src + "}"; Node n = compiler.parseTestCode(src).removeFirstChild(); Node script = new Node(Token.SCRIPT, n); script.setInputId(new InputId("test")); assertEquals(0, compiler.getErrorCount()); Scope scope = new SyntacticScopeCreator(compiler).createScope( n, Scope.createGlobalScope(script)); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, n); ControlFlowGraph cfg = cfa.getCfg(); LiveVariablesAnalysis analysis = new LiveVariablesAnalysis(cfg, scope, compiler); analysis.analyze(); return analysis; } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/VariableVisibilityAnalysisTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/VariableVisibilityAnalysisTest.jav0000644000175000017500000001476612115204405031705 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.VariableVisibilityAnalysis.VariableVisibility; import com.google.javascript.rhino.Node; /** * Tests of {@link VariableVisibilityAnalysis}. * * @author dcc@google.com (Devin Coughlin) */ public class VariableVisibilityAnalysisTest extends CompilerTestCase { private Compiler lastCompiler; private VariableVisibilityAnalysis lastAnalysis; @Override protected CompilerPass getProcessor(Compiler compiler) { lastAnalysis = new VariableVisibilityAnalysis(compiler); lastCompiler = compiler; return lastAnalysis; } public void testCapturedVariables() { String source = "global:var global;\n" + "function Outer() {\n" + " captured:var captured;\n" + " notcaptured:var notCaptured;\n" + " function Inner() {\n" + " alert(captured);" + " }\n" + "}\n"; analyze(source); assertIsCapturedLocal("captured"); assertIsUncapturedLocal("notcaptured"); } public void testGlobals() { String source = "global:var global;"; analyze(source); assertIsGlobal("global"); } public void testParameters() { String source = "function A(a,b,c) {\n" + "}\n"; analyze(source); assertIsParameter("a"); assertIsParameter("b"); assertIsParameter("c"); } public void testFunctions() { String source = "function global() {\n" + " function inner() {\n" + " }\n" + " function innerCaptured() {\n" + " (function(){innerCaptured()})()\n" + " }\n" + "}\n"; analyze(source); assertFunctionHasVisibility("global", VariableVisibility.GLOBAL); assertFunctionHasVisibility("inner", VariableVisibility.LOCAL); assertFunctionHasVisibility("innerCaptured", VariableVisibility.CAPTURED_LOCAL); } private void assertFunctionHasVisibility(String functionName, VariableVisibility visibility) { Node functionNode = searchForFunction(functionName); assertNotNull(functionNode); Node nameNode = functionNode.getFirstChild(); assertEquals(visibility, lastAnalysis.getVariableVisibility(nameNode)); } private void assertLabeledVariableHasVisibility(String label, VariableVisibility visibility) { Node labeledVariable = searchLabel(label); Preconditions.checkState(labeledVariable.isVar()); // VAR // NAME Node nameNode = labeledVariable.getFirstChild(); assertEquals(visibility, lastAnalysis.getVariableVisibility(nameNode)); } private void assertIsCapturedLocal(String label) { assertLabeledVariableHasVisibility(label, VariableVisibility.CAPTURED_LOCAL); } private void assertIsUncapturedLocal(String label) { assertLabeledVariableHasVisibility(label, VariableVisibility.LOCAL); } private void assertIsGlobal(String label) { assertLabeledVariableHasVisibility(label, VariableVisibility.GLOBAL); } private void assertIsParameter(String parameterName) { Node parameterNode = searchForParameter(parameterName); assertNotNull(parameterNode); assertEquals(VariableVisibility.PARAMETER, lastAnalysis.getVariableVisibility(parameterNode)); } private VariableVisibilityAnalysis analyze(String src) { testSame(src); return lastAnalysis; } /* * Finds a parameter NAME node with the given name in the source AST. * * Behavior is undefined if there are multiple parameters with * parameterName. */ private Node searchForParameter(final String parameterName) { Preconditions.checkArgument(parameterName != null); final Node[] foundNode = new Node[1]; AbstractPostOrderCallback findParameter = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getParent().isParamList() && parameterName.equals(n.getString())) { foundNode[0] = n; } } }; new NodeTraversal(lastCompiler, findParameter) .traverse(lastCompiler.jsRoot); return foundNode[0]; } /* * Finds a function node with the given name in the source AST. * * Behavior is undefined if there are multiple functions with * parameterName. */ private Node searchForFunction(final String functionName) { Preconditions.checkArgument(functionName != null); final Node[] foundNode = new Node[1]; AbstractPostOrderCallback findFunction = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction() && functionName.equals(NodeUtil.getFunctionName(n))) { foundNode[0] = n; } } }; new NodeTraversal(lastCompiler, findFunction) .traverse(lastCompiler.jsRoot); return foundNode[0]; } // Shamelessly stolen from NameReferenceGraphConstructionTest private Node searchLabel(String label) { LabeledVariableSearcher s = new LabeledVariableSearcher(label); new NodeTraversal(lastCompiler, s).traverse(lastCompiler.jsRoot); assertNotNull("Label " + label + " should be in the source code", s.found); return s.found; } /** * Quick traversal to find a given labeled variable in the AST. * * Finds the variable for foo in: * foo: var a = ... */ private class LabeledVariableSearcher extends AbstractPostOrderCallback { Node found = null; final String target; LabeledVariableSearcher(String target) { this.target = target; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isLabel() && target.equals(n.getFirstChild().getString())) { // LABEL // VAR // NAME found = n.getLastChild(); } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CombinedCompilerPassTest.java0000644000175000017500000001674212115204405030603 0ustar apoapo/* * 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.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.Collection; import java.util.List; import java.util.Set; /** */ public class CombinedCompilerPassTest extends TestCase { private Compiler compiler; /** * Returns a Node tree with the post-order traversal a b c d e f g h i j k l m * and the in-order traversal m d a b c h e f g l i j k: * * m * ,---------|---------. * d h l * ,--|--. ,--|--. ,--|--. * a b c e f g i j k * */ private static Node createPostOrderAlphabet() { Node a = Node.newString("a"); Node b = Node.newString("b"); Node c = Node.newString("c"); Node d = Node.newString("d"); Node e = Node.newString("e"); Node f = Node.newString("f"); Node g = Node.newString("g"); Node h = Node.newString("h"); Node i = Node.newString("i"); Node j = Node.newString("j"); Node k = Node.newString("k"); Node l = Node.newString("l"); Node m = Node.newString("m"); d.addChildToBack(a); d.addChildToBack(b); d.addChildToBack(c); h.addChildrenToBack(e); h.addChildrenToBack(f); h.addChildrenToBack(g); l.addChildToBack(i); l.addChildToBack(j); l.addChildToBack(k); m.addChildToBack(d); m.addChildToBack(h); m.addChildToBack(l); return m; } @Override public void setUp() throws Exception { super.setUp(); compiler = new Compiler(); } /** * Concatenates contents of string nodes encountered in pre-order * and post-order traversals. Abbreviates traversals by ignoring subtrees * rooted with specified strings. */ private static class ConcatTraversal implements Callback { private StringBuilder visited = new StringBuilder(); private StringBuilder shouldTraversed = new StringBuilder(); private Set ignoring = Sets.newHashSet(); ConcatTraversal ignore(String s) { ignoring.add(s); return this; } @Override public void visit(NodeTraversal t, Node n, Node parent) { assertEquals(Token.STRING, n.getType()); visited.append(n.getString()); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { assertEquals(Token.STRING, n.getType()); shouldTraversed.append(n.getString()); return !ignoring.contains(n.getString()); } /** Returns strings concatenated during post-order traversal. */ String getVisited() { return visited.toString(); } /** Returns strings concatenated during pre-order traversal. */ String getShouldTraversed() { return shouldTraversed.toString(); } Collection getIgnoring() { return ignoring; } } /** * Collection of data for a traversal test. Contains the traversal callback * and the expected pre- and post-order traversal results. */ private static class TestHelper { private ConcatTraversal traversal; private String expectedVisited; private String shouldTraverseExpected; TestHelper(ConcatTraversal traversal, String expectedVisited, String shouldTraverseExpected) { this.traversal = traversal; this.expectedVisited = expectedVisited; this.shouldTraverseExpected = shouldTraverseExpected; } ConcatTraversal getTraversal() { return traversal; } void checkResults() { assertEquals("ConcatTraversal ignoring " + traversal.getIgnoring().toString() + " has unexpected visiting order", expectedVisited, traversal.getVisited()); assertEquals("ConcatTraversal ignoring " + traversal.getIgnoring().toString() + " has unexpected traversal order", shouldTraverseExpected, traversal.getShouldTraversed()); } } private static List createStringTests() { List tests = Lists.newArrayList(); tests.add(new TestHelper( new ConcatTraversal(), "abcdefghijklm", "mdabchefglijk")); tests.add(new TestHelper( new ConcatTraversal().ignore("d"), "efghijklm", "mdhefglijk")); tests.add(new TestHelper( new ConcatTraversal().ignore("f"), "abcdeghijklm", "mdabchefglijk")); tests.add(new TestHelper(new ConcatTraversal().ignore("m"), "", "m")); return tests; } public void testIndividualPasses() { for (TestHelper test : createStringTests()) { CombinedCompilerPass pass = new CombinedCompilerPass(compiler, test.getTraversal()); pass.process(null, createPostOrderAlphabet()); test.checkResults(); } } public void testCombinedPasses() { List tests = createStringTests(); Callback[] callbacks = new Callback[tests.size()]; int i = 0; for (TestHelper test : tests) { callbacks[i++] = test.getTraversal(); } CombinedCompilerPass pass = new CombinedCompilerPass(compiler, callbacks); pass.process(null, createPostOrderAlphabet()); for (TestHelper test : tests) { test.checkResults(); } } /** * Records the scopes visited during an AST traversal. Abbreviates traversals * by ignoring subtrees rooted with specified NAME nodes. */ private static class ScopeRecordingCallback implements ScopedCallback { Set visitedScopes = Sets.newHashSet(); Set ignoring = Sets.newHashSet(); void ignore(String name) { ignoring.add(name); } @Override public void enterScope(NodeTraversal t) { visitedScopes.add(t.getScopeRoot()); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return !n.isName() || !ignoring.contains(n.getString()); } Set getVisitedScopes() { return visitedScopes; } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { } } public void testScopes() { Node root = compiler.parseTestCode("var y = function() { var x = function() { };}"); ScopeRecordingCallback c1 = new ScopeRecordingCallback(); c1.ignore("y"); ScopeRecordingCallback c2 = new ScopeRecordingCallback(); c2.ignore("x"); ScopeRecordingCallback c3 = new ScopeRecordingCallback(); CombinedCompilerPass pass = new CombinedCompilerPass(compiler, c1, c2, c3); pass.process(null, root); assertEquals(1, c1.getVisitedScopes().size()); assertEquals(2, c2.getVisitedScopes().size()); assertEquals(3, c3.getVisitedScopes().size()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RenameLocalVarsTest.java0000644000175000017500000000724712115204405027557 0ustar apoapo/* * Copyright 2007 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; /** * Tests for {@link RenameVars}. * @see RenameVarsTest */ public class RenameLocalVarsTest extends CompilerTestCase { private static final String DEFAULT_PREFIX = ""; private String prefix = DEFAULT_PREFIX; @Override protected CompilerPass getProcessor(Compiler compiler) { return new RenameVars( compiler, prefix, true, false, false, false, null, null, null); } public void testRenameSimple() { test("function Foo(v1, v2) {return v1;} Foo();", "function Foo(a, b) {return a;} Foo();"); } public void testRenameGlobals() { testSame("var Foo; var Bar, y; function x() { Bar++; }"); } public void testRenameLocals() { test("(function (v1, v2) {}); (function (v3, v4) {});", "(function (a, b) {}); (function (a, b) {});"); test("function f1(v1, v2) {}; function f2(v3, v4) {};", "function f1(a, b) {}; function f2(a, b) {};"); } public void testRenameLocalsClashingWithGlobals() { test("function a(v1, v2) {return v1;} a();", "function a(b, c) {return b;} a();"); } public void testRenameNested() { test("function f1(v1, v2) { (function(v3, v4) {}) }", "function f1(a, b) { (function(c, d) {}) }"); test("function f1(v1, v2) { function f2(v3, v4) {} }", "function f1(a, b) { function c(d, e) {} }"); } public void testRenameWithExterns1() { String externs = "var bar; function alert() {}"; test(externs, "function foo(bar) { alert(bar); } foo(3)", "function foo(a) { alert(a); } foo(3)", null, null); } public void testRenameWithExterns2() { test("var a; function alert() {}", "function foo(bar) { alert(a);alert(bar); } foo(3);", "function foo(b) { alert(a);alert(b); } foo(3);", null, null); } public void testDoNotRenameExportedName() { test("_foo()", "_foo()"); } public void testRenameWithNameOverlap() { test("function local() { var a = 1; var b = 2; b + b; }", "function local() { var b = 1; var a = 2; a + a; }"); } public void testRenameWithPrefix1() { prefix = "PRE_"; test("function Foo(v1, v2) {return v1} Foo();", "function Foo(a, b) {return a} Foo();"); prefix = DEFAULT_PREFIX; } public void testRenameWithPrefix2() { prefix = "PRE_"; test("function Foo(v1, v2) {var v3 = v1 + v2; return v3;} Foo();", "function Foo(a, b) {var c = a + b; return c;} Foo();"); prefix = DEFAULT_PREFIX; } public void testRenameWithPrefix3() { prefix = "a"; test("function Foo() {return 1;}" + "function Bar() {" + " var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z," + " A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,aa,ab;" + " Foo();" + "} Bar();", "function Foo() {return 1;}" + "function Bar() {" + " var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C," + " D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,aa;" + " Foo();" + "} Bar();"); prefix = DEFAULT_PREFIX; } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SourceInformationAnnotatorTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SourceInformationAnnotatorTest.jav0000644000175000017500000000276012115204405031727 0ustar apoapo/* * Copyright 2011 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.rhino.Node; import com.google.javascript.rhino.Token; /** * @author johnlenz@google.com (John Lenz) */ public class SourceInformationAnnotatorTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new SourceInformationAnnotator("", false)); }}; } public void testPreserveAnnotatedName() { Node root = new Node(Token.SCRIPT); Node name = Node.newString("foo"); name.putProp(Node.ORIGINALNAME_PROP, "bar"); root.addChildToBack(name); NodeTraversal.traverse(null, root, new SourceInformationAnnotator("", false)); assertEquals(name.getProp(Node.ORIGINALNAME_PROP), "bar"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RenameLabelsTest.java0000644000175000017500000000525012115204405027063 0ustar apoapo/* * 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; /** * Tests for {@link RenameLabels}. */ public class RenameLabelsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new RenameLabels(compiler); } public void testRenameInFunction() { test("function x(){ Foo:a(); }", "function x(){ a(); }"); test("function x(){ Foo:{ a(); break Foo; } }", "function x(){ a:{ a(); break a; } }"); test("function x() { " + "Foo:{ " + "function goo() {" + "Foo: {" + "a(); " + "break Foo; " + "}" + "}" + "}" + "}", "function x(){function goo(){a:{ a(); break a; }}}"); test("function x() { " + "Foo:{ " + "function goo() {" + "Foo: {" + "a(); " + "break Foo; " + "}" + "}" + "break Foo;" + "}" + "}", "function x(){a:{function goo(){a:{ a(); break a; }} break a;}}"); } public void testRenameGlobals() { test("Foo:{a();}", "a();"); test("Foo:{a(); break Foo;}", "a:{a(); break a;}"); test("Foo:{Goo:a(); break Foo;}", "a:{a(); break a;}"); test("Foo:{Goo:while(1){a(); continue Goo; break Foo;}}", "a:{b:while(1){a(); continue b;break a;}}"); test("Foo:Goo:while(1){a(); continue Goo; break Foo;}", "a:b:while(1){a(); continue b;break a;}"); test("Foo:Bar:X:{ break Bar; }", "a:{ break a; }"); test("Foo:Bar:X:{ break Bar; break X; }", "a:b:{ break a; break b;}"); test("Foo:Bar:X:{ break Bar; break Foo; }", "a:b:{ break b; break a;}"); test("Foo:while (1){a(); break;}", "while (1){a(); break;}"); // Remove label that is not referenced. test("Foo:{a(); while (1) break;}", "a(); while (1) break;"); } public void testRenameReused() { test("foo:{break foo}; foo:{break foo}", "a:{break a};a:{break a}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/StrictModeCheckTest.java0000644000175000017500000001543012115204405027545 0ustar apoapo/* * 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; public class StrictModeCheckTest extends CompilerTestCase { private static final String EXTERNS = "var arguments; function eval(str) {}"; private boolean noVarCheck; private boolean noCajaChecks; public StrictModeCheckTest() { super(EXTERNS); } @Override protected void setUp() throws Exception { super.setUp(); noVarCheck = false; noCajaChecks = false; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new StrictModeCheck(compiler, noVarCheck, noCajaChecks); } @Override protected int getNumRepetitions() { return 1; } public void testEval() { test("function foo() { eval('a'); }", null, StrictModeCheck.EVAL_USE); } public void testEval2() { testSame("function foo(eval) {}", StrictModeCheck.EVAL_DECLARATION); } public void testEval3() { testSame("function foo() {} foo.eval = 3;"); } public void testEval4() { testSame("function foo() { var eval = 3; }", StrictModeCheck.EVAL_DECLARATION); } public void testEval5() { testSame("function eval() {}", StrictModeCheck.EVAL_DECLARATION); } public void testEval6() { testSame("try {} catch (eval) {}", StrictModeCheck.EVAL_DECLARATION); } public void testEval7() { testSame("var o = {eval: 3};"); } public void testEval8() { testSame("var a; eval: while (true) { a = 3; }"); } public void testUnknownVariable() { testSame("function foo(a) { a = b; }", StrictModeCheck.UNKNOWN_VARIABLE); } public void testUnknownVariable2() { testSame("a: while (true) { a = 3; }", StrictModeCheck.UNKNOWN_VARIABLE); } public void testUnknownVariable3() { testSame("try {} catch (ex) { ex = 3; }"); } public void testArguments() { testSame("function foo(arguments) {}", StrictModeCheck.ARGUMENTS_DECLARATION); } public void testArguments2() { testSame("function foo() { var arguments = 3; }", StrictModeCheck.ARGUMENTS_DECLARATION); } public void testArguments3() { testSame("function arguments() {}", StrictModeCheck.ARGUMENTS_DECLARATION); } public void testArguments4() { testSame("try {} catch (arguments) {}", StrictModeCheck.ARGUMENTS_DECLARATION); } public void testArguments5() { testSame("var o = {arguments: 3};"); } public void testEvalAssignment() { noCajaChecks = true; testSame("function foo() { eval = []; }", StrictModeCheck.EVAL_ASSIGNMENT); } public void testEvalAssignment2() { test("function foo() { eval = []; }", null, StrictModeCheck.EVAL_USE); } public void testAssignToArguments() { testSame("function foo() { arguments = []; }", StrictModeCheck.ARGUMENTS_ASSIGNMENT); } public void testDeleteVar() { testSame("var a; delete a", StrictModeCheck.DELETE_VARIABLE); } public void testDeleteFunction() { testSame("function a() {} delete a", StrictModeCheck.DELETE_VARIABLE); } public void testDeleteArgument() { testSame("function b(a) { delete a; }", StrictModeCheck.DELETE_VARIABLE); } public void testDeleteProperty() { testSame("function f(obj) { delete obj.a; }"); } public void testIllegalName() { test("var a__ = 3;", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName2() { test("function a__() {}", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName3() { test("function f(a__) {}", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName4() { test("try {} catch (a__) {}", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName5() { noVarCheck = true; test("var a = b__;", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName6() { test("function f(obj) { return obj.a__; }", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName7() { noCajaChecks = true; testSame("var a__ = 3;"); } public void testIllegalName8() { test("var o = {a__: 3};", null, StrictModeCheck.ILLEGAL_NAME); test("var o = {b: 3, a__: 4};", null, StrictModeCheck.ILLEGAL_NAME); test("var o = {b: 3, get a__() {}};", null, StrictModeCheck.ILLEGAL_NAME); test("var o = {b: 3, set a__(c) {}};", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName9() { test("a__: while (true) { var b = 3; }", null, StrictModeCheck.ILLEGAL_NAME); } public void testIllegalName10() { // Validate that number as objlit key testSame("var o = {1: 3, 2: 4};"); } public void testDuplicateObjectLiteralKey() { testSame("var o = {a: 1, b: 2, c: 3};"); testSame("var x = { get a() {}, set a(p) {} };"); testSame("var o = {a: 1, b: 2, a: 3};", StrictModeCheck.DUPLICATE_OBJECT_KEY); testSame("var x = { get a() {}, get a() {} };", StrictModeCheck.DUPLICATE_OBJECT_KEY); testSame("var x = { get a() {}, a: 1 };", StrictModeCheck.DUPLICATE_OBJECT_KEY); testSame("var x = { set a(p) {}, a: 1 };", StrictModeCheck.DUPLICATE_OBJECT_KEY); testSame( "'use strict';\n" + "function App() {}\n" + "App.prototype = {\n" + " get appData() { return this.appData_; },\n" + " set appData(data) { this.appData_ = data; }\n" + "};"); } public void testFunctionDecl() { testSame("function g() {}"); testSame("var g = function() {};"); testSame("(function() {})();"); testSame("(function() {});"); testSame(inFn("function g() {}")); testSame(inFn("var g = function() {};")); testSame(inFn("(function() {})();")); testSame(inFn("(function() {});")); test("{function g() {}}", null, StrictModeCheck.BAD_FUNCTION_DECLARATION); testSame("{var g = function () {}}"); testSame("{(function g() {})()}"); test("var x;if (x) { function g(){} }", null, StrictModeCheck.BAD_FUNCTION_DECLARATION); testSame("var x;if (x) {var g = function () {}}"); testSame("var x;if (x) {(function g() {})()}"); } public void testFunctionDecl2() { test("{function g() {}}", null, StrictModeCheck.BAD_FUNCTION_DECLARATION); } private String inFn(String body) { return "function func() {" + body + "}"; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SymbolTableTest.java0000644000175000017500000010626112115204405026752 0ustar apoapo/* * Copyright 2011 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.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.javascript.jscomp.SymbolTable.Reference; import com.google.javascript.jscomp.SymbolTable.Symbol; import com.google.javascript.jscomp.SymbolTable.SymbolScope; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.List; import java.util.Set; /** * @author nicksantos@google.com (Nick Santos) */ public class SymbolTableTest extends TestCase { private static final String EXTERNS = CompilerTypeTestCase.DEFAULT_EXTERNS + "\nfunction customExternFn(customExternArg) {}"; private CompilerOptions options; @Override public void setUp() throws Exception { super.setUp(); options = new CompilerOptions(); options.setCodingConvention(new ClosureCodingConvention()); CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( options); WarningLevel.VERBOSE.setOptionsForWarningLevel(options); options.ideMode = true; } public void testGlobalVar() throws Exception { SymbolTable table = createSymbolTable( "/** @type {number} */ var x = 5;"); assertNull(getGlobalVar(table, "y")); assertNotNull(getGlobalVar(table, "x")); assertEquals("number", getGlobalVar(table, "x").getType().toString()); // 2 == sizeof({x, *global*}) assertEquals(2, getVars(table).size()); } public void testGlobalThisReferences() throws Exception { SymbolTable table = createSymbolTable( "var x = this; function f() { return this + this + this; }"); Symbol global = getGlobalVar(table, "*global*"); assertNotNull(global); List refs = table.getReferenceList(global); assertEquals(1, refs.size()); } public void testGlobalThisReferences2() throws Exception { // Make sure the global this is declared, even if it isn't referenced. SymbolTable table = createSymbolTable(""); Symbol global = getGlobalVar(table, "*global*"); assertNotNull(global); List refs = table.getReferenceList(global); assertEquals(0, refs.size()); } public void testGlobalThisReferences3() throws Exception { SymbolTable table = createSymbolTable("this.foo = {}; this.foo.bar = {};"); Symbol global = getGlobalVar(table, "*global*"); assertNotNull(global); List refs = table.getReferenceList(global); assertEquals(2, refs.size()); } public void testGlobalThisPropertyReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function Foo() {} this.Foo;"); Symbol foo = getGlobalVar(table, "Foo"); assertNotNull(foo); List refs = table.getReferenceList(foo); assertEquals(2, refs.size()); } public void testGlobalVarReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @type {number} */ var x = 5; x = 6;"); Symbol x = getGlobalVar(table, "x"); List refs = table.getReferenceList(x); assertEquals(2, refs.size()); assertEquals(x.getDeclaration(), refs.get(0)); assertEquals(Token.VAR, refs.get(0).getNode().getParent().getType()); assertEquals(Token.ASSIGN, refs.get(1).getNode().getParent().getType()); } public void testLocalVarReferences() throws Exception { SymbolTable table = createSymbolTable( "function f(x) { return x; }"); Symbol x = getLocalVar(table, "x"); List refs = table.getReferenceList(x); assertEquals(2, refs.size()); assertEquals(x.getDeclaration(), refs.get(0)); assertEquals(Token.PARAM_LIST, refs.get(0).getNode().getParent().getType()); assertEquals(Token.RETURN, refs.get(1).getNode().getParent().getType()); } public void testLocalThisReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function F() { this.foo = 3; this.bar = 5; }"); Symbol f = getGlobalVar(table, "F"); assertNotNull(f); Symbol t = table.getParameterInFunction(f, "this"); assertNotNull(t); List refs = table.getReferenceList(t); assertEquals(2, refs.size()); } public void testLocalThisReferences2() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function F() {}" + "F.prototype.baz = " + " function() { this.foo = 3; this.bar = 5; };"); Symbol baz = getGlobalVar(table, "F.prototype.baz"); assertNotNull(baz); Symbol t = table.getParameterInFunction(baz, "this"); assertNotNull(t); List refs = table.getReferenceList(t); assertEquals(2, refs.size()); } public void testLocalThisReferences3() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function F() {}"); Symbol baz = getGlobalVar(table, "F"); assertNotNull(baz); Symbol t = table.getParameterInFunction(baz, "this"); assertNotNull(t); List refs = table.getReferenceList(t); assertEquals(0, refs.size()); } public void testNamespacedReferences() throws Exception { // Because the type of goog is anonymous, we build its properties into // the global scope. SymbolTable table = createSymbolTable( "var goog = {};" + "goog.dom = {};" + "goog.dom.DomHelper = function(){};"); Symbol goog = getGlobalVar(table, "goog"); assertNotNull(goog); assertEquals(3, Iterables.size(table.getReferences(goog))); Symbol googDom = getGlobalVar(table, "goog.dom"); assertNotNull(googDom); assertEquals(2, Iterables.size(table.getReferences(googDom))); Symbol googDomHelper = getGlobalVar(table, "goog.dom.DomHelper"); assertNotNull(googDomHelper); assertEquals(1, Iterables.size(table.getReferences(googDomHelper))); } public void testIncompleteNamespacedReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */\n" + "goog.dom.DomHelper = function(){};\n" + "var y = goog.dom.DomHelper;\n"); Symbol goog = getGlobalVar(table, "goog"); assertNotNull(goog); assertEquals(2, table.getReferenceList(goog).size()); Symbol googDom = getGlobalVar(table, "goog.dom"); assertNotNull(googDom); assertEquals(2, table.getReferenceList(googDom).size()); Symbol googDomHelper = getGlobalVar(table, "goog.dom.DomHelper"); assertNotNull(googDomHelper); assertEquals(2, Iterables.size(table.getReferences(googDomHelper))); } public void testGlobalRichObjectReference() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */\n" + "function A(){};\n" + "/** @type {?A} */ A.prototype.b;\n" + "/** @type {A} */ var a = new A();\n" + "function g() {\n" + " return a.b ? 'x' : 'y';\n" + "}\n" + "(function() {\n" + " var x; if (x) { x = a.b.b; } else { x = a.b.c; }\n" + " return x;\n" + "})();\n"); Symbol ab = getGlobalVar(table, "a.b"); assertNull(ab); Symbol propB = getGlobalVar(table, "A.prototype.b"); assertNotNull(propB); assertEquals(5, table.getReferenceList(propB).size()); } public void testRemovalOfNamespacedReferencesOfProperties() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ var DomHelper = function(){};" + "/** method */ DomHelper.method = function() {};"); Symbol domHelper = getGlobalVar(table, "DomHelper"); assertNotNull(domHelper); Symbol domHelperNamespacedMethod = getGlobalVar(table, "DomHelper.method"); assertEquals("method", domHelperNamespacedMethod.getName()); Symbol domHelperMethod = domHelper.getPropertyScope().getSlot("method"); assertNotNull(domHelperMethod); } public void testGoogScopeReferences() throws Exception { SymbolTable table = createSymbolTable( "var goog = {};" + "goog.scope = function() {};" + "goog.scope(function() {});"); Symbol googScope = getGlobalVar(table, "goog.scope"); assertNotNull(googScope); assertEquals(2, Iterables.size(table.getReferences(googScope))); } public void testGoogRequireReferences() throws Exception { SymbolTable table = createSymbolTable( "var goog = {};" + "goog.provide = function() {};" + "goog.require = function() {};" + "goog.provide('goog.dom');" + "goog.require('goog.dom');"); Symbol goog = getGlobalVar(table, "goog"); assertNotNull(goog); // 8 references: // 5 in code // 2 in strings // 1 created by ProcessClosurePrimitives when it processes the provide. // // NOTE(nicksantos): In the future, we may de-dupe references such // that the one in the goog.provide string and the one created by // ProcessClosurePrimitives count as the same reference. assertEquals(8, Iterables.size(table.getReferences(goog))); } public void testGoogRequireReferences2() throws Exception { options.brokenClosureRequiresLevel = CheckLevel.OFF; SymbolTable table = createSymbolTable( "foo.bar = function(){}; // definition\n" + "goog.require('foo.bar')\n"); Symbol fooBar = getGlobalVar(table, "foo.bar"); assertNotNull(fooBar); assertEquals(2, Iterables.size(table.getReferences(fooBar))); } public void testGlobalVarInExterns() throws Exception { SymbolTable table = createSymbolTable("customExternFn(1);"); Symbol fn = getGlobalVar(table, "customExternFn"); List refs = table.getReferenceList(fn); assertEquals(2, refs.size()); SymbolScope scope = table.getEnclosingScope(refs.get(0).getNode()); assertTrue(scope.isGlobalScope()); assertEquals(SymbolTable.GLOBAL_THIS, table.getSymbolForScope(scope).getName()); } public void testLocalVarInExterns() throws Exception { SymbolTable table = createSymbolTable(""); Symbol arg = getLocalVar(table, "customExternArg"); List refs = table.getReferenceList(arg); assertEquals(1, refs.size()); Symbol fn = getGlobalVar(table, "customExternFn"); SymbolScope scope = table.getEnclosingScope(refs.get(0).getNode()); assertFalse(scope.isGlobalScope()); assertEquals(fn, table.getSymbolForScope(scope)); } public void testSymbolsForType() throws Exception { SymbolTable table = createSymbolTable( "function random() { return 1; }" + "/** @constructor */ function Foo() {}" + "/** @constructor */ function Bar() {}" + "var x = random() ? new Foo() : new Bar();"); Symbol x = getGlobalVar(table, "x"); Symbol foo = getGlobalVar(table, "Foo"); Symbol bar = getGlobalVar(table, "Bar"); Symbol fooPrototype = getGlobalVar(table, "Foo.prototype"); Symbol fn = getGlobalVar(table, "Function"); assertEquals( Lists.newArrayList(foo, bar), table.getAllSymbolsForTypeOf(x)); assertEquals( Lists.newArrayList(fn), table.getAllSymbolsForTypeOf(foo)); assertEquals( Lists.newArrayList(foo), table.getAllSymbolsForTypeOf(fooPrototype)); assertEquals( foo, table.getSymbolDeclaredBy( foo.getType().toMaybeFunctionType())); } public void testStaticMethodReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ var DomHelper = function(){};" + "/** method */ DomHelper.method = function() {};" + "function f() { var x = DomHelper; x.method() + x.method(); }"); Symbol method = getGlobalVar(table, "DomHelper").getPropertyScope().getSlot("method"); assertEquals( 3, Iterables.size(table.getReferences(method))); } public void testMethodReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ var DomHelper = function(){};" + "/** method */ DomHelper.prototype.method = function() {};" + "function f() { " + " (new DomHelper()).method(); (new DomHelper()).method(); };"); Symbol method = getGlobalVar(table, "DomHelper.prototype.method"); assertEquals( 3, Iterables.size(table.getReferences(method))); } public void testSuperClassMethodReferences() throws Exception { SymbolTable table = createSymbolTable( "var goog = {};" + "goog.inherits = function(a, b) {};" + "/** @constructor */ var A = function(){};" + "/** method */ A.prototype.method = function() {};" + "/**\n" + " * @constructor\n" + " * @extends {A}\n" + " */\n" + "var B = function(){};\n" + "goog.inherits(B, A);" + "/** method */ B.prototype.method = function() {" + " B.superClass_.method();" + "};"); Symbol methodA = getGlobalVar(table, "A.prototype.method"); assertEquals( 2, Iterables.size(table.getReferences(methodA))); } public void testMethodReferencesMissingTypeInfo() throws Exception { SymbolTable table = createSymbolTable( "/**\n" + " * @constructor\n" + " * @extends {Missing}\n" + " */ var DomHelper = function(){};\n" + "/** method */ DomHelper.prototype.method = function() {\n" + " this.method();\n" + "};\n" + "function f() { " + " (new DomHelper()).method();\n" + "};"); Symbol method = getGlobalVar(table, "DomHelper.prototype.method"); assertEquals( 3, Iterables.size(table.getReferences(method))); } public void testFieldReferencesMissingTypeInfo() throws Exception { SymbolTable table = createSymbolTable( "/**\n" + " * @constructor\n" + " * @extends {Missing}\n" + " */ var DomHelper = function(){ this.prop = 1; };\n" + "/** @type {number} */ DomHelper.prototype.prop = 2;\n" + "function f() {\n" + " return (new DomHelper()).prop;\n" + "};"); Symbol prop = getGlobalVar(table, "DomHelper.prototype.prop"); assertEquals(3, table.getReferenceList(prop).size()); assertNull(getLocalVar(table, "this.prop")); } public void testFieldReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ var DomHelper = function(){" + " /** @type {number} */ this.field = 3;" + "};" + "function f() { " + " return (new DomHelper()).field + (new DomHelper()).field; };"); Symbol field = getGlobalVar(table, "DomHelper.prototype.field"); assertEquals( 3, Iterables.size(table.getReferences(field))); } public void testUndeclaredFieldReferences() throws Exception { // We do not currently create symbol table entries for undeclared fields, // but this may change in the future. SymbolTable table = createSymbolTable( "/** @constructor */ var DomHelper = function(){};" + "DomHelper.prototype.method = function() { " + " this.field = 3;" + " return x.field;" + "}"); Symbol field = getGlobalVar(table, "DomHelper.prototype.field"); assertNull(field); } public void testPrototypeReferences() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function DomHelper() {}" + "DomHelper.prototype.method = function() {};"); Symbol prototype = getGlobalVar(table, "DomHelper.prototype"); assertNotNull(prototype); List refs = table.getReferenceList(prototype); // One of the refs is implicit in the declaration of the function. assertEquals(refs.toString(), 2, refs.size()); } public void testPrototypeReferences2() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */\n" + "function Snork() {}\n" + "Snork.prototype.baz = 3;\n"); Symbol prototype = getGlobalVar(table, "Snork.prototype"); assertNotNull(prototype); List refs = table.getReferenceList(prototype); assertEquals(2, refs.size()); } public void testPrototypeReferences3() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function Foo() {}"); Symbol fooPrototype = getGlobalVar(table, "Foo.prototype"); assertNotNull(fooPrototype); List refs = table.getReferenceList(fooPrototype); assertEquals(1, refs.size()); assertEquals(Token.NAME, refs.get(0).getNode().getType()); // Make sure that the ctor and its prototype are declared at the // same node. assertEquals( refs.get(0).getNode(), table.getReferenceList(getGlobalVar(table, "Foo")).get(0).getNode()); } public void testPrototypeReferences4() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ function Foo() {}" + "Foo.prototype = {bar: 3}"); Symbol fooPrototype = getGlobalVar(table, "Foo.prototype"); assertNotNull(fooPrototype); List refs = Lists.newArrayList( table.getReferences(fooPrototype)); assertEquals(1, refs.size()); assertEquals(Token.GETPROP, refs.get(0).getNode().getType()); assertEquals("Foo.prototype", refs.get(0).getNode().getQualifiedName()); } public void testPrototypeReferences5() throws Exception { SymbolTable table = createSymbolTable( "var goog = {}; /** @constructor */ goog.Foo = function() {};"); Symbol fooPrototype = getGlobalVar(table, "goog.Foo.prototype"); assertNotNull(fooPrototype); List refs = table.getReferenceList(fooPrototype); assertEquals(1, refs.size()); assertEquals(Token.GETPROP, refs.get(0).getNode().getType()); // Make sure that the ctor and its prototype are declared at the // same node. assertEquals( refs.get(0).getNode(), table.getReferenceList( getGlobalVar(table, "goog.Foo")).get(0).getNode()); } public void testReferencesInJSDocType() { SymbolTable table = createSymbolTable( "/** @constructor */ function Foo() {}\n" + "/** @type {Foo} */ var x;\n" + "/** @param {Foo} x */ function f(x) {}\n" + "/** @return {function(): Foo} */ function g() {}\n" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */ function Sub() {}"); Symbol foo = getGlobalVar(table, "Foo"); assertNotNull(foo); List refs = table.getReferenceList(foo); assertEquals(5, refs.size()); assertEquals(1, refs.get(0).getNode().getLineno()); assertEquals(29, refs.get(0).getNode().getCharno()); assertEquals(3, refs.get(0).getNode().getLength()); assertEquals(2, refs.get(1).getNode().getLineno()); assertEquals(11, refs.get(1).getNode().getCharno()); assertEquals(3, refs.get(2).getNode().getLineno()); assertEquals(12, refs.get(2).getNode().getCharno()); assertEquals(4, refs.get(3).getNode().getLineno()); assertEquals(25, refs.get(3).getNode().getCharno()); assertEquals(7, refs.get(4).getNode().getLineno()); assertEquals(13, refs.get(4).getNode().getCharno()); } public void testReferencesInJSDocType2() { SymbolTable table = createSymbolTable( "/** @param {string} x */ function f(x) {}\n"); Symbol str = getGlobalVar(table, "String"); assertNotNull(str); List refs = table.getReferenceList(str); // We're going to pick up a lot of references from the externs, // so it's not meaningful to check the number of references. // We really want to make sure that all the references are in the externs, // except the last one. assertTrue(refs.size() > 1); int last = refs.size() - 1; for (int i = 0; i < refs.size(); i++) { Reference ref = refs.get(i); assertEquals(i != last, ref.getNode().isFromExterns()); if (!ref.getNode().isFromExterns()) { assertEquals("in1", ref.getNode().getSourceFileName()); } } } public void testReferencesInJSDocName() { String code = "/** @param {Object} x */ function f(x) {}\n"; SymbolTable table = createSymbolTable(code); Symbol x = getLocalVar(table, "x"); assertNotNull(x); List refs = table.getReferenceList(x); assertEquals(2, refs.size()); assertEquals(code.indexOf("x) {"), refs.get(0).getNode().getCharno()); assertEquals(code.indexOf("x */"), refs.get(1).getNode().getCharno()); assertEquals("in1", refs.get(0).getNode().getSourceFileName()); } public void testLocalQualifiedNamesInLocalScopes() { SymbolTable table = createSymbolTable( "function f() { var x = {}; x.number = 3; }"); Symbol xNumber = getLocalVar(table, "x.number"); assertNotNull(xNumber); assertFalse(table.getScope(xNumber).isGlobalScope()); assertEquals("number", xNumber.getType().toString()); } public void testNaturalSymbolOrdering() { SymbolTable table = createSymbolTable( "/** @const */ var a = {};" + "/** @const */ a.b = {};" + "/** @param {number} x */ function f(x) {}"); Symbol a = getGlobalVar(table, "a"); Symbol ab = getGlobalVar(table, "a.b"); Symbol f = getGlobalVar(table, "f"); Symbol x = getLocalVar(table, "x"); Ordering ordering = table.getNaturalSymbolOrdering(); assertSymmetricOrdering(ordering, a, ab); assertSymmetricOrdering(ordering, a, f); assertSymmetricOrdering(ordering, f, ab); assertSymmetricOrdering(ordering, f, x); } public void testDeclarationDisagreement() { SymbolTable table = createSymbolTable( "/** @const */ var goog = goog || {};\n" + "/** @param {!Function} x */\n" + "goog.addSingletonGetter2 = function(x) {};\n" + "/** Wakka wakka wakka */\n" + "goog.addSingletonGetter = goog.addSingletonGetter2;\n" + "/** @param {!Function} x */\n" + "goog.addSingletonGetter = function(x) {};\n"); Symbol method = getGlobalVar(table, "goog.addSingletonGetter"); List refs = table.getReferenceList(method); assertEquals(2, refs.size()); // Note that the declaration should show up second. assertEquals(7, method.getDeclaration().getNode().getLineno()); assertEquals(5, refs.get(1).getNode().getLineno()); } public void testMultipleExtends() { SymbolTable table = createSymbolTable( "/** @const */ var goog = goog || {};\n" + "goog.inherits = function(x, y) {};\n" + "/** @constructor */\n" + "goog.A = function() { this.fieldA = this.constructor; };\n" + "/** @constructor */ goog.A.FooA = function() {};\n" + "/** @return {void} */ goog.A.prototype.methodA = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {goog.A}\n" + " */\n" + "goog.B = function() { this.fieldB = this.constructor; };\n" + "goog.inherits(goog.B, goog.A);\n" + "/** @return {void} */ goog.B.prototype.methodB = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {goog.A}\n" + " */\n" + "goog.B2 = function() { this.fieldB = this.constructor; };\n" + "goog.inherits(goog.B2, goog.A);\n" + "/** @constructor */ goog.B2.FooB = function() {};\n" + "/** @return {void} */ goog.B2.prototype.methodB = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {goog.B}\n" + " */\n" + "goog.C = function() { this.fieldC = this.constructor; };\n" + "goog.inherits(goog.C, goog.B);\n" + "/** @constructor */ goog.C.FooC = function() {};\n" + "/** @return {void} */ goog.C.prototype.methodC = function() {};\n"); Symbol bCtor = getGlobalVar(table, "goog.B.prototype.constructor"); assertNotNull(bCtor); List bRefs = table.getReferenceList(bCtor); assertEquals(2, bRefs.size()); assertEquals(11, bCtor.getDeclaration().getNode().getLineno()); Symbol cCtor = getGlobalVar(table, "goog.C.prototype.constructor"); assertNotNull(cCtor); List cRefs = table.getReferenceList(cCtor); assertEquals(2, cRefs.size()); assertEquals(26, cCtor.getDeclaration().getNode().getLineno()); } public void testJSDocAssociationWithBadNamespace() { SymbolTable table = createSymbolTable( // Notice that the declaration for "goog" is missing. // We want to recover anyway and print out what we know // about goog.Foo. "/** @constructor */ goog.Foo = function(){};"); Symbol foo = getGlobalVar(table, "goog.Foo"); assertNotNull(foo); JSDocInfo info = foo.getJSDocInfo(); assertNotNull(info); assertTrue(info.isConstructor()); } public void testMissingConstructorTag() { SymbolTable table = createSymbolTable( "function F() {" + " this.field1 = 3;" + "}" + "F.prototype.method1 = function() {" + " this.field1 = 5;" + "};" + "(new F()).method1();"); // Because the constructor tag is missing, this is going // to be missing a lot of inference. assertNull(getGlobalVar(table, "F.prototype.field1")); Symbol sym = getGlobalVar(table, "F.prototype.method1"); assertEquals(1, table.getReferenceList(sym).size()); } public void testTypeCheckingOff() { options = new CompilerOptions(); // Turning type-checking off is even worse than not annotating anything. SymbolTable table = createSymbolTable( "/** @contstructor */" + "function F() {" + " this.field1 = 3;" + "}" + "F.prototype.method1 = function() {" + " this.field1 = 5;" + "};" + "(new F()).method1();"); assertNull(getGlobalVar(table, "F.prototype.field1")); assertNull(getGlobalVar(table, "F.prototype.method1")); Symbol sym = getGlobalVar(table, "F"); assertEquals(3, table.getReferenceList(sym).size()); } public void testSuperClassReference() throws Exception { SymbolTable table = createSymbolTable( " var a = {b: {}};\n" + "/** @constructor */\n" + "a.b.BaseClass = function() {};\n" + "a.b.BaseClass.prototype.doSomething = function() {\n" + " alert('hi');\n" + "};\n" + "/**\n" + " * @constructor\n" + " * @extends {a.b.BaseClass}\n" + " */\n" + "a.b.DerivedClass = function() {};\n" + "goog.inherits(a.b.DerivedClass, a.b.BaseClass);\n" + "/** @override */\n" + "a.b.DerivedClass.prototype.doSomething = function() {\n" + " a.b.DerivedClass.superClass_.doSomething();\n" + "};\n"); Symbol bad = getGlobalVar( table, "a.b.DerivedClass.superClass_.doSomething"); assertNull(bad); Symbol good = getGlobalVar( table, "a.b.BaseClass.prototype.doSomething"); assertNotNull(good); List refs = table.getReferenceList(good); assertEquals(2, refs.size()); assertEquals("a.b.DerivedClass.superClass_.doSomething", refs.get(1).getNode().getQualifiedName()); } public void testInnerEnum() throws Exception { SymbolTable table = createSymbolTable( "var goog = {}; goog.ui = {};" + " /** @constructor */\n" + "goog.ui.Zippy = function() {};\n" + "/** @enum {string} */\n" + "goog.ui.Zippy.EventType = { TOGGLE: 'toggle' };\n"); Symbol eventType = getGlobalVar(table, "goog.ui.Zippy.EventType"); assertNotNull(eventType); assertTrue(eventType.getType().isEnumType()); Symbol toggle = getGlobalVar(table, "goog.ui.Zippy.EventType.TOGGLE"); assertNotNull(toggle); } public void testMethodInAnonObject1() throws Exception { SymbolTable table = createSymbolTable( "var a = {}; a.b = {}; a.b.c = function() {};"); Symbol a = getGlobalVar(table, "a"); Symbol ab = getGlobalVar(table, "a.b"); Symbol abc = getGlobalVar(table, "a.b.c"); assertNotNull(abc); assertEquals(1, table.getReferenceList(abc).size()); assertEquals("{b: {c: function (): undefined}}", a.getType().toString()); assertEquals("{c: function (): undefined}", ab.getType().toString()); assertEquals("function (): undefined", abc.getType().toString()); } public void testMethodInAnonObject2() throws Exception { SymbolTable table = createSymbolTable( "var a = {b: {c: function() {}}};"); Symbol a = getGlobalVar(table, "a"); Symbol ab = getGlobalVar(table, "a.b"); Symbol abc = getGlobalVar(table, "a.b.c"); assertNotNull(abc); assertEquals(1, table.getReferenceList(abc).size()); assertEquals("{b: {c: function (): undefined}}", a.getType().toString()); assertEquals("{c: function (): undefined}", ab.getType().toString()); assertEquals("function (): undefined", abc.getType().toString()); } public void testJSDocOnlySymbol() throws Exception { SymbolTable table = createSymbolTable( "/**\n" + " * @param {number} x\n" + " * @param y\n" + " */\n" + "var a;"); Symbol x = getDocVar(table, "x"); assertNotNull(x); assertEquals("number", x.getType().toString()); assertEquals(1, table.getReferenceList(x).size()); Symbol y = getDocVar(table, "y"); assertNotNull(x); assertEquals(null, y.getType()); assertEquals(1, table.getReferenceList(y).size()); } public void testNamespaceDefinitionOrder() throws Exception { // Sometimes, weird things can happen where the files appear in // a strange order. We need to make sure we're robust against this. SymbolTable table = createSymbolTable( "/** @const */ var goog = {};\n" + "/** @constructor */ goog.dom.Foo = function() {};\n" + "/** @const */ goog.dom = {};\n"); Symbol goog = getGlobalVar(table, "goog"); Symbol dom = getGlobalVar(table, "goog.dom"); Symbol Foo = getGlobalVar(table, "goog.dom.Foo"); assertNotNull(goog); assertNotNull(dom); assertNotNull(Foo); assertEquals(dom, goog.getPropertyScope().getSlot("dom")); assertEquals(Foo, dom.getPropertyScope().getSlot("Foo")); } public void testConstructorAlias() throws Exception { SymbolTable table = createSymbolTable( "/** @constructor */ var Foo = function() {};\n" + "/** desc */ Foo.prototype.bar = function() {};\n" + "/** @constructor */ var FooAlias = Foo;\n" + "/** desc */ FooAlias.prototype.baz = function() {};\n"); Symbol foo = getGlobalVar(table, "Foo"); Symbol fooAlias = getGlobalVar(table, "FooAlias"); Symbol bar = getGlobalVar(table, "Foo.prototype.bar"); Symbol baz = getGlobalVar(table, "Foo.prototype.baz"); Symbol bazAlias = getGlobalVar(table, "FooAlias.prototype.baz"); assertNotNull(foo); assertNotNull(fooAlias); assertNotNull(bar); assertNotNull(baz); assertNull(bazAlias); Symbol barScope = table.getSymbolForScope(table.getScope(bar)); assertNotNull(barScope); Symbol bazScope = table.getSymbolForScope(table.getScope(baz)); assertNotNull(bazScope); Symbol fooPrototype = foo.getPropertyScope().getSlot("prototype"); assertNotNull(fooPrototype); assertEquals(fooPrototype, barScope); assertEquals(fooPrototype, bazScope); } public void testSymbolForScopeOfNatives() throws Exception { SymbolTable table = createSymbolTable(""); // From the externs. Symbol sliceArg = getLocalVar(table, "sliceArg"); assertNotNull(sliceArg); Symbol scope = table.getSymbolForScope(table.getScope(sliceArg)); assertNotNull(scope); assertEquals(scope, getGlobalVar(table, "String.prototype.slice")); Symbol proto = getGlobalVar(table, "String.prototype"); assertEquals( "externs1", proto.getDeclaration().getNode().getSourceFileName()); } private void assertSymmetricOrdering( Ordering ordering, Symbol first, Symbol second) { assertTrue(ordering.compare(first, first) == 0); assertTrue(ordering.compare(second, second) == 0); assertTrue(ordering.compare(first, second) < 0); assertTrue(ordering.compare(second, first) > 0); } private Symbol getGlobalVar(SymbolTable table, String name) { return table.getGlobalScope().getQualifiedSlot(name); } private Symbol getDocVar(SymbolTable table, String name) { for (Symbol sym : table.getAllSymbols()) { if (sym.isDocOnlyParameter() && sym.getName().equals(name)) { return sym; } } return null; } private Symbol getLocalVar(SymbolTable table, String name) { for (SymbolScope scope : table.getAllScopes()) { if (!scope.isGlobalScope() && scope.isLexicalScope() && scope.getQualifiedSlot(name) != null) { return scope.getQualifiedSlot(name); } } return null; } /** Returns all non-extern vars. */ private List getVars(SymbolTable table) { List result = Lists.newArrayList(); for (Symbol symbol : table.getAllSymbols()) { if (symbol.getDeclaration() != null && !symbol.getDeclaration().getNode().isFromExterns()) { result.add(symbol); } } return result; } private SymbolTable createSymbolTable(String input) { List inputs = Lists.newArrayList( SourceFile.fromCode("in1", input)); List externs = Lists.newArrayList( SourceFile.fromCode("externs1", EXTERNS)); Compiler compiler = new Compiler(); compiler.compile(externs, inputs, options); return assertSymbolTableValid(compiler.buildKnownSymbolTable()); } /** * Asserts that the symbol table meets some invariants. * Returns the same table for easy chaining. */ private SymbolTable assertSymbolTableValid(SymbolTable table) { Set allSymbols = Sets.newHashSet(table.getAllSymbols()); for (Symbol sym : table.getAllSymbols()) { // Make sure that grabbing the symbol's scope and looking it up // again produces the same symbol. assertEquals(sym, table.getScope(sym).getQualifiedSlot(sym.getName())); for (Reference ref : table.getReferences(sym)) { // Make sure that the symbol and reference are mutually linked. assertEquals(sym, ref.getSymbol()); } Symbol scope = table.getSymbolForScope(table.getScope(sym)); assertTrue( "The symbol's scope is a zombie scope that shouldn't exist.\n" + "Symbol: " + sym + "\n" + "Scope: " + table.getScope(sym), scope == null || allSymbols.contains(scope)); } // Make sure that the global "this" is declared at the first input root. Symbol global = getGlobalVar(table, SymbolTable.GLOBAL_THIS); assertNotNull(global); assertNotNull(global.getDeclaration()); assertEquals(Token.SCRIPT, global.getDeclaration().getNode().getType()); List globalRefs = table.getReferenceList(global); // The main reference list should never contain the synthetic declaration // for the global root. assertFalse(globalRefs.contains(global.getDeclaration())); return table; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlinePropertiesTest.java0000644000175000017500000001176212115204405030031 0ustar apoapo/* * Copyright 2012 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; /** * @author johnlenz@google.com (John Lenz) */ public class InlinePropertiesTest extends CompilerTestCase { private static final String EXTERNS = "Function.prototype.call=function(){};" + "Function.prototype.inherits=function(){};" + "prop.toString;" + "var google = { gears: { factory: {}, workerPool: {} } };"; public InlinePropertiesTest() { super(EXTERNS); enableNormalize(); enableTypeCheck(CheckLevel.WARNING); enableClosurePass(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new InlineProperties(compiler); } public void testConstInstanceProp1() { // Replace a reference to known constant property. test( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "new C().foo;", "function C() {\n" + " this.foo = 1;\n" + "}\n" + "new C(), 1;"); } public void testConstInstanceProp2() { // Replace a constant reference test( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "var x = new C();\n" + "x.foo;", "function C() {\n" + " this.foo = 1\n" + "}\n" + "var x = new C();\n" + "1;\n"); } public void testConstInstanceProp3() { // Replace a constant reference test( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "/** @type {C} */\n" + "var x = new C();\n" + "x.foo;", "function C() {\n" + " this.foo = 1\n" + "}\n" + "var x = new C();\n" + "1;\n"); } public void testConstInstanceProp4() { // This pass replies on DisambiguateProperties to distinguish like named // properties so it doesn't handle this case. testSame( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "/** @constructor */\n" + "function B() {\n" + " this.foo = 1;\n" + "}\n" + "new C().foo;\n"); } public void testConstClassProps1() { // For now, don't inline constant class properties, // CollapseProperties should handle this in most cases. testSame( "/** @constructor */\n" + "function C() {\n" + "}\n" + "C.foo = 1;\n" + "C.foo;"); } public void testConstClassProps2() { // Don't confuse, class properties with instance properties testSame( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "C.foo;"); } public void testConstClassProps3() { // Don't confuse, class properties with prototype properties testSame( "/** @constructor */\n" + "function C() {}\n" + "C.prototype.foo = 1;\n" + "c.foo;\n"); } public void testNonConstClassProp1() { testSame( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "var x = new C();\n" + "alert(x.foo);\n" + "delete x.foo;"); } public void testNonConstClassProp2() { testSame( "/** @constructor */\n" + "function C() {\n" + " this.foo = 1;\n" + "}\n" + "var x = new C();\n" + "alert(x.foo);\n" + "x.foo = 2;"); } public void testNonConstructorClassProp1() { testSame( "function C() {\n" + " this.foo = 1;\n" + " return this;\n" + "}\n" + "C().foo;"); } public void testConditionalClassProp1() { testSame( "/** @constructor */\n" + "function C() {\n" + " if (false) this.foo = 1;\n" + "}\n" + "new C().foo;"); } public void testConstPrototypeProp1() { test( "/** @constructor */\n" + "function C() {}\n" + "C.prototype.foo = 1;\n" + "new C().foo;\n", "function C() {}\n" + "C.prototype.foo = 1;\n" + "new C(), 1;\n"); } public void testConstPrototypeProp2() { test( "/** @constructor */\n" + "function C() {}\n" + "C.prototype.foo = 1;\n" + "var x = new C();\n" + "x.foo;\n", "function C() {}\n" + "C.prototype.foo = 1;\n" + "var x = new C();\n" + "1;\n"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TypedScopeCreatorTest.java0000644000175000017500000016141512115204405030136 0ustar apoapo/* * 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 static com.google.javascript.jscomp.TypedScopeCreator.CTOR_INITIALIZER; import static com.google.javascript.jscomp.TypedScopeCreator.IFACE_INITIALIZER; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.testing.Asserts; import java.util.Deque; /** * Tests for {@link TypedScopeCreator} and {@link TypeInference}. Admittedly, * the name is a bit of a misnomer. * @author nicksantos@google.com (Nick Santos) */ public class TypedScopeCreatorTest extends CompilerTestCase { private JSTypeRegistry registry; private Scope globalScope; private Scope lastLocalScope; @Override public int getNumRepetitions() { return 1; } private final Callback callback = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { Scope s = t.getScope(); if (s.isGlobal()) { globalScope = s; } else { lastLocalScope = s; } } }; @Override public CompilerPass getProcessor(final Compiler compiler) { registry = compiler.getTypeRegistry(); return new CompilerPass() { @Override public void process(Node externs, Node root) { MemoizedScopeCreator scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); Scope topScope = scopeCreator.createScope(root.getParent(), null); (new TypeInferencePass( compiler, compiler.getReverseAbstractInterpreter(), topScope, scopeCreator)).process(externs, root); NodeTraversal t = new NodeTraversal( compiler, callback, scopeCreator); t.traverseRoots(Lists.newArrayList(externs, root)); } }; } public void testStubProperty() { testSame("function Foo() {}; Foo.bar;"); ObjectType foo = (ObjectType) globalScope.getVar("Foo").getType(); assertFalse(foo.hasProperty("bar")); Asserts.assertTypeEquals(registry.getNativeType(UNKNOWN_TYPE), foo.getPropertyType("bar")); Asserts.assertTypeCollectionEquals( Lists.newArrayList(foo), registry.getTypesWithProperty("bar")); } public void testConstructorProperty() { testSame("var foo = {}; /** @constructor */ foo.Bar = function() {};"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.hasProperty("Bar")); assertFalse(foo.isPropertyTypeInferred("Bar")); JSType fooBar = foo.getPropertyType("Bar"); assertEquals("function (new:foo.Bar): undefined", fooBar.toString()); Asserts.assertTypeCollectionEquals( Lists.newArrayList(foo), registry.getTypesWithProperty("Bar")); } public void testPrototypePropertyMethodWithoutAnnotation() { testSame("var Foo = function Foo() {};" + "var proto = Foo.prototype = {" + " bar: function(a, b){}" + "};" + "proto.baz = function(c) {};" + "(function() { proto.baz = function() {}; })();"); ObjectType foo = (ObjectType) findNameType("Foo", globalScope); assertTrue(foo.hasProperty("prototype")); ObjectType fooProto = (ObjectType) foo.getPropertyType("prototype"); assertTrue(fooProto.hasProperty("bar")); assertEquals("function (?, ?): undefined", fooProto.getPropertyType("bar").toString()); assertTrue(fooProto.hasProperty("baz")); assertEquals("function (?): undefined", fooProto.getPropertyType("baz").toString()); } public void testEnumProperty() { testSame("var foo = {}; /** @enum */ foo.Bar = {XXX: 'xxx'};"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.hasProperty("Bar")); assertFalse(foo.isPropertyTypeInferred("Bar")); assertTrue(foo.isPropertyTypeDeclared("Bar")); JSType fooBar = foo.getPropertyType("Bar"); assertEquals("enum{foo.Bar}", fooBar.toString()); Asserts.assertTypeCollectionEquals( Lists.newArrayList(foo), registry.getTypesWithProperty("Bar")); } public void testInferredProperty1() { testSame("var foo = {}; foo.Bar = 3;"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertTrue(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty1a() { testSame("var foo = {}; /** @type {number} */ foo.Bar = 3;"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertFalse(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty2() { testSame("var foo = { Bar: 3 };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertTrue(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty2b() { testSame("var foo = { /** @type {number} */ Bar: 3 };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertFalse(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty2c() { testSame("var foo = { /** @return {number} */ Bar: 3 };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("function (): number", foo.getPropertyType("Bar").toString()); assertFalse(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty3() { testSame("var foo = { /** @type {number} */ get Bar() { return 3 } };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("?", foo.getPropertyType("Bar").toString()); assertTrue(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty4() { testSame("var foo = { /** @type {number} */ set Bar(a) {} };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("?", foo.getPropertyType("Bar").toString()); assertTrue(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty5() { testSame("var foo = { /** @return {number} */ get Bar() { return 3 } };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertFalse(foo.isPropertyTypeInferred("Bar")); } public void testInferredProperty6() { testSame("var foo = { /** @param {number} a */ set Bar(a) {} };"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.toString(), foo.hasProperty("Bar")); assertEquals("number", foo.getPropertyType("Bar").toString()); assertFalse(foo.isPropertyTypeInferred("Bar")); } public void testPrototypeInit() { testSame("/** @constructor */ var Foo = function() {};" + "Foo.prototype = {bar: 1}; var foo = new Foo();"); ObjectType foo = (ObjectType) findNameType("foo", globalScope); assertTrue(foo.hasProperty("bar")); assertEquals("number", foo.getPropertyType("bar").toString()); assertTrue(foo.isPropertyTypeInferred("bar")); } public void testBogusPrototypeInit() { // This used to cause a compiler crash. testSame("/** @const */ var goog = {}; " + "goog.F = {}; /** @const */ goog.F.prototype = {};" + "/** @constructor */ goog.F = function() {};"); } public void testInferredPrototypeProperty1() { testSame("/** @constructor */ var Foo = function() {};" + "Foo.prototype.bar = 1; var x = new Foo();"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertTrue(x.hasProperty("bar")); assertEquals("number", x.getPropertyType("bar").toString()); assertTrue(x.isPropertyTypeInferred("bar")); } public void testInferredPrototypeProperty2() { testSame("/** @constructor */ var Foo = function() {};" + "Foo.prototype = {bar: 1}; var x = new Foo();"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertTrue(x.hasProperty("bar")); assertEquals("number", x.getPropertyType("bar").toString()); assertTrue(x.isPropertyTypeInferred("bar")); } public void testEnum() { testSame("/** @enum */ var Foo = {BAR: 1}; var f = Foo;"); ObjectType f = (ObjectType) findNameType("f", globalScope); assertTrue(f.hasProperty("BAR")); assertEquals("Foo.", f.getPropertyType("BAR").toString()); assertTrue(f instanceof EnumType); } public void testEnumElement() { testSame("/** @enum */ var Foo = {BAR: 1}; var f = Foo;"); Var bar = globalScope.getVar("Foo.BAR"); assertNotNull(bar); assertEquals("Foo.", bar.getType().toString()); } public void testNamespacedEnum() { testSame("var goog = {}; goog.ui = {};" + "/** @constructor */goog.ui.Zippy = function() {};" + "/** @enum{string} */goog.ui.Zippy.EventType = { TOGGLE: 'toggle' };" + "var x = goog.ui.Zippy.EventType;" + "var y = goog.ui.Zippy.EventType.TOGGLE;"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertTrue(x.isEnumType()); assertTrue(x.hasProperty("TOGGLE")); assertEquals("enum{goog.ui.Zippy.EventType}", x.getReferenceName()); ObjectType y = (ObjectType) findNameType("y", globalScope); assertTrue(y.isSubtype(getNativeType(STRING_TYPE))); assertTrue(y.isEnumElementType()); assertEquals("goog.ui.Zippy.EventType", y.getReferenceName()); } public void testEnumAlias() { testSame("/** @enum */ var Foo = {BAR: 1}; " + "/** @enum */ var FooAlias = Foo; var f = FooAlias;"); assertEquals("Foo.", registry.getType("FooAlias").toString()); Asserts.assertTypeEquals(registry.getType("FooAlias"), registry.getType("Foo")); ObjectType f = (ObjectType) findNameType("f", globalScope); assertTrue(f.hasProperty("BAR")); assertEquals("Foo.", f.getPropertyType("BAR").toString()); assertTrue(f instanceof EnumType); } public void testNamespacesEnumAlias() { testSame("var goog = {}; /** @enum */ goog.Foo = {BAR: 1}; " + "/** @enum */ goog.FooAlias = goog.Foo;"); assertEquals("goog.Foo.", registry.getType("goog.FooAlias").toString()); Asserts.assertTypeEquals(registry.getType("goog.Foo"), registry.getType("goog.FooAlias")); } public void testCollectedFunctionStub() { testSame( "/** @constructor */ function f() { " + " /** @return {number} */ this.foo;" + "}" + "var x = new f();"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("f", x.toString()); assertTrue(x.hasProperty("foo")); assertEquals("function (this:f): number", x.getPropertyType("foo").toString()); assertFalse(x.isPropertyTypeInferred("foo")); } public void testCollectedFunctionStubLocal() { testSame( "(function() {" + "/** @constructor */ function f() { " + " /** @return {number} */ this.foo;" + "}" + "var x = new f();" + "});"); ObjectType x = (ObjectType) findNameType("x", lastLocalScope); assertEquals("f", x.toString()); assertTrue(x.hasProperty("foo")); assertEquals("function (this:f): number", x.getPropertyType("foo").toString()); assertFalse(x.isPropertyTypeInferred("foo")); } public void testNamespacedFunctionStub() { testSame( "var goog = {};" + "/** @param {number} x */ goog.foo;"); ObjectType goog = (ObjectType) findNameType("goog", globalScope); assertTrue(goog.hasProperty("foo")); assertEquals("function (number): ?", goog.getPropertyType("foo").toString()); assertTrue(goog.isPropertyTypeDeclared("foo")); Asserts.assertTypeEquals(globalScope.getVar("goog.foo").getType(), goog.getPropertyType("foo")); } public void testNamespacedFunctionStubLocal() { testSame( "(function() {" + "var goog = {};" + "/** @param {number} x */ goog.foo;" + "});"); ObjectType goog = (ObjectType) findNameType("goog", lastLocalScope); assertTrue(goog.hasProperty("foo")); assertEquals("function (number): ?", goog.getPropertyType("foo").toString()); assertTrue(goog.isPropertyTypeDeclared("foo")); Asserts.assertTypeEquals(lastLocalScope.getVar("goog.foo").getType(), goog.getPropertyType("foo")); } public void testCollectedCtorProperty() { testSame( "/** @constructor */ function f() { " + " /** @type {number} */ this.foo = 3;" + "}" + "var x = new f();"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("f", x.toString()); assertTrue(x.hasProperty("foo")); assertEquals("number", x.getPropertyType("foo").toString()); assertFalse(x.isPropertyTypeInferred("foo")); } public void testPropertyOnUnknownSuperClass1() { testSame( "var goog = this.foo();" + "/** @constructor \n * @extends {goog.Unknown} */" + "function Foo() {}" + "Foo.prototype.bar = 1;" + "var x = new Foo();", RhinoErrorReporter.TYPE_PARSE_ERROR); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("Foo", x.toString()); assertTrue(x.getImplicitPrototype().hasOwnProperty("bar")); assertEquals("?", x.getPropertyType("bar").toString()); assertTrue(x.isPropertyTypeInferred("bar")); } public void testPropertyOnUnknownSuperClass2() { testSame( "var goog = this.foo();" + "/** @constructor \n * @extends {goog.Unknown} */" + "function Foo() {}" + "Foo.prototype = {bar: 1};" + "var x = new Foo();", RhinoErrorReporter.TYPE_PARSE_ERROR); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("Foo", x.toString()); assertEquals("Foo.prototype", x.getImplicitPrototype().toString()); assertTrue(x.getImplicitPrototype().hasOwnProperty("bar")); assertEquals("?", x.getPropertyType("bar").toString()); assertTrue(x.isPropertyTypeInferred("bar")); } public void testMethodBeforeFunction1() throws Exception { testSame( "var y = Window.prototype;" + "Window.prototype.alert = function(message) {};" + "/** @constructor */ function Window() {}\n" + "var window = new Window(); \n" + "var x = window;"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("Window", x.toString()); assertTrue(x.getImplicitPrototype().hasOwnProperty("alert")); assertEquals("function (this:Window, ?): undefined", x.getPropertyType("alert").toString()); assertTrue(x.isPropertyTypeDeclared("alert")); ObjectType y = (ObjectType) findNameType("y", globalScope); assertEquals("function (this:Window, ?): undefined", y.getPropertyType("alert").toString()); } public void testMethodBeforeFunction2() throws Exception { testSame( "var y = Window.prototype;" + "Window.prototype = {alert: function(message) {}};" + "/** @constructor */ function Window() {}\n" + "var window = new Window(); \n" + "var x = window;"); ObjectType x = (ObjectType) findNameType("x", globalScope); assertEquals("Window", x.toString()); assertTrue(x.getImplicitPrototype().hasOwnProperty("alert")); assertEquals("function (this:Window, ?): undefined", x.getPropertyType("alert").toString()); assertFalse(x.isPropertyTypeDeclared("alert")); ObjectType y = (ObjectType) findNameType("y", globalScope); assertEquals("?", y.getPropertyType("alert").toString()); } public void testAddMethodsPrototypeTwoWays() throws Exception { testSame( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true};" + "A.prototype.m3 = 'third property!';" + "var x = new A();"); ObjectType instanceType = (ObjectType) findNameType("x", globalScope); assertEquals( getNativeObjectType(OBJECT_TYPE).getPropertiesCount() + 3, instanceType.getPropertiesCount()); Asserts.assertTypeEquals(getNativeType(NUMBER_TYPE), instanceType.getPropertyType("m1")); Asserts.assertTypeEquals(getNativeType(BOOLEAN_TYPE), instanceType.getPropertyType("m2")); Asserts.assertTypeEquals(getNativeType(STRING_TYPE), instanceType.getPropertyType("m3")); // Verify the prototype chain. // This is a special case where we want the anonymous object to // become a prototype. assertFalse(instanceType.hasOwnProperty("m1")); assertFalse(instanceType.hasOwnProperty("m2")); assertFalse(instanceType.hasOwnProperty("m3")); ObjectType proto1 = instanceType.getImplicitPrototype(); assertTrue(proto1.hasOwnProperty("m1")); assertTrue(proto1.hasOwnProperty("m2")); assertTrue(proto1.hasOwnProperty("m3")); ObjectType proto2 = proto1.getImplicitPrototype(); assertFalse(proto2.hasProperty("m1")); assertFalse(proto2.hasProperty("m2")); assertFalse(proto2.hasProperty("m3")); } public void testInferredVar() throws Exception { testSame("var x = 3; x = 'x'; x = true;"); Var x = globalScope.getVar("x"); assertEquals("(boolean|number|string)", x.getType().toString()); assertTrue(x.isTypeInferred()); } public void testDeclaredVar() throws Exception { testSame("/** @type {?number} */ var x = 3; var y = x;"); Var x = globalScope.getVar("x"); assertEquals("(null|number)", x.getType().toString()); assertFalse(x.isTypeInferred()); JSType y = findNameType("y", globalScope); assertEquals("(null|number)", y.toString()); } public void testPropertiesOnInterface() throws Exception { testSame("/** @interface */ var I = function() {};" + "/** @type {number} */ I.prototype.bar;" + "I.prototype.baz = function(){};"); Var i = globalScope.getVar("I"); assertEquals("function (this:I): ?", i.getType().toString()); assertTrue(i.getType().isInterface()); ObjectType iPrototype = (ObjectType) ((ObjectType) i.getType()).getPropertyType("prototype"); assertEquals("I.prototype", iPrototype.toString()); assertTrue(iPrototype.isFunctionPrototypeType()); assertEquals("number", iPrototype.getPropertyType("bar").toString()); assertEquals("function (this:I): undefined", iPrototype.getPropertyType("baz").toString()); Asserts.assertTypeEquals(iPrototype, globalScope.getVar("I.prototype").getType()); } public void testPropertiesOnInterface2() throws Exception { testSame("/** @interface */ var I = function() {};" + "I.prototype = {baz: function(){}};" + "/** @type {number} */ I.prototype.bar;"); Var i = globalScope.getVar("I"); assertEquals("function (this:I): ?", i.getType().toString()); assertTrue(i.getType().isInterface()); ObjectType iPrototype = (ObjectType) ((ObjectType) i.getType()).getPropertyType("prototype"); assertEquals("I.prototype", iPrototype.toString()); assertTrue(iPrototype.isFunctionPrototypeType()); assertEquals("number", iPrototype.getPropertyType("bar").toString()); assertEquals("function (this:I): undefined", iPrototype.getPropertyType("baz").toString()); // should not be null assertNull(globalScope.getVar("I.prototype")); // assertEquals(iPrototype, globalScope.getVar("I.prototype").getType()); } // TODO(johnlenz): A syntax for stubs using object literals? public void testStubsInExterns() { testSame( "/** @constructor */ function Extern() {}" + "Extern.prototype.bar;" + "var e = new Extern(); e.baz;", "/** @constructor */ function Foo() {}" + "Foo.prototype.bar;" + "var f = new Foo(); f.baz;", null); ObjectType e = (ObjectType) globalScope.getVar("e").getType(); assertEquals("?", e.getPropertyType("bar").toString()); assertEquals("?", e.getPropertyType("baz").toString()); ObjectType f = (ObjectType) globalScope.getVar("f").getType(); assertEquals("?", f.getPropertyType("bar").toString()); assertFalse(f.hasProperty("baz")); } public void testStubsInExterns2() { testSame( "/** @constructor */ function Extern() {}" + "/** @type {Extern} */ var myExtern;" + "/** @type {number} */ myExtern.foo;", "", null); JSType e = globalScope.getVar("myExtern").getType(); assertEquals("(Extern|null)", e.toString()); ObjectType externType = (ObjectType) e.restrictByNotNullOrUndefined(); assertTrue(globalScope.getRootNode().toStringTree(), externType.hasOwnProperty("foo")); assertTrue(externType.isPropertyTypeDeclared("foo")); assertEquals("number", externType.getPropertyType("foo").toString()); assertTrue(externType.isPropertyInExterns("foo")); } public void testStubsInExterns3() { testSame( "/** @type {number} */ myExtern.foo;" + "/** @type {Extern} */ var myExtern;" + "/** @constructor */ function Extern() {}", "", null); JSType e = globalScope.getVar("myExtern").getType(); assertEquals("(Extern|null)", e.toString()); ObjectType externType = (ObjectType) e.restrictByNotNullOrUndefined(); assertTrue(globalScope.getRootNode().toStringTree(), externType.hasOwnProperty("foo")); assertTrue(externType.isPropertyTypeDeclared("foo")); assertEquals("number", externType.getPropertyType("foo").toString()); assertTrue(externType.isPropertyInExterns("foo")); } public void testStubsInExterns4() { testSame( "Extern.prototype.foo;" + "/** @constructor */ function Extern() {}", "", null); JSType e = globalScope.getVar("Extern").getType(); assertEquals("function (new:Extern): ?", e.toString()); ObjectType externProto = ((FunctionType) e).getPrototype(); assertTrue(globalScope.getRootNode().toStringTree(), externProto.hasOwnProperty("foo")); assertTrue(externProto.isPropertyTypeInferred("foo")); assertEquals("?", externProto.getPropertyType("foo").toString()); assertTrue(externProto.isPropertyInExterns("foo")); } public void testPropertyInExterns1() { testSame( "/** @constructor */ function Extern() {}" + "/** @type {Extern} */ var extern;" + "/** @return {number} */ extern.one;", "/** @constructor */ function Normal() {}" + "/** @type {Normal} */ var normal;" + "/** @return {number} */ normal.one;", null); JSType e = globalScope.getVar("Extern").getType(); ObjectType externInstance = ((FunctionType) e).getInstanceType(); assertTrue(externInstance.hasOwnProperty("one")); assertTrue(externInstance.isPropertyTypeDeclared("one")); assertEquals("function (): number", externInstance.getPropertyType("one").toString()); JSType n = globalScope.getVar("Normal").getType(); ObjectType normalInstance = ((FunctionType) n).getInstanceType(); assertFalse(normalInstance.hasOwnProperty("one")); } public void testPropertyInExterns2() { testSame( "/** @type {Object} */ var extern;" + "/** @return {number} */ extern.one;", "/** @type {Object} */ var normal;" + "/** @return {number} */ normal.one;", null); JSType e = globalScope.getVar("extern").getType(); assertFalse(e.dereference().hasOwnProperty("one")); JSType normal = globalScope.getVar("normal").getType(); assertFalse(normal.dereference().hasOwnProperty("one")); } public void testPropertyInExterns3() { testSame( "/** @constructor \n * @param {*=} x */ function Object(x) {}" + "/** @type {number} */ Object.one;", "", null); ObjectType obj = globalScope.getVar("Object").getType().dereference(); assertTrue(obj.hasOwnProperty("one")); assertEquals("number", obj.getPropertyType("one").toString()); } public void testTypedStubsInExterns() { testSame( "/** @constructor \n * @param {*} var_args */ " + "function Function(var_args) {}" + "/** @type {!Function} */ Function.prototype.apply;", "var f = new Function();", null); ObjectType f = (ObjectType) globalScope.getVar("f").getType(); // The type of apply() on a function instance is resolved dynamically, // since apply varies with the type of the function it's called on. assertEquals( "function (?=, (Object|null)=): ?", f.getPropertyType("apply").toString()); // The type of apply() on the function prototype just takes what it was // declared with. FunctionType func = (FunctionType) globalScope.getVar("Function").getType(); assertEquals("Function", func.getPrototype().getPropertyType("apply").toString()); } public void testTypesInExterns() throws Exception { testSame( CompilerTypeTestCase.DEFAULT_EXTERNS, "", null); Var v = globalScope.getVar("Object"); FunctionType obj = (FunctionType) v.getType(); assertEquals("function (new:Object, *=): ?", obj.toString()); assertNotNull(v.getNode()); assertNotNull(v.input); } public void testPropertyDeclarationOnInstanceType() { testSame( "/** @type {!Object} */ var a = {};" + "/** @type {number} */ a.name = 0;"); assertEquals("number", globalScope.getVar("a.name").getType().toString()); ObjectType a = (ObjectType) (globalScope.getVar("a").getType()); assertFalse(a.hasProperty("name")); assertFalse(getNativeObjectType(OBJECT_TYPE).hasProperty("name")); } public void testPropertyDeclarationOnRecordType() { testSame( "/** @type {{foo: number}} */ var a = {foo: 3};" + "/** @type {number} */ a.name = 0;"); assertEquals("number", globalScope.getVar("a.name").getType().toString()); ObjectType a = (ObjectType) (globalScope.getVar("a").getType()); assertEquals("{foo: number}", a.toString()); assertFalse(a.hasProperty("name")); } public void testGlobalThis1() { testSame( "/** @constructor */ function Window() {}" + "Window.prototype.alert = function() {};" + "var x = this;"); ObjectType x = (ObjectType) (globalScope.getVar("x").getType()); FunctionType windowCtor = (FunctionType) (globalScope.getVar("Window").getType()); assertEquals("global this", x.toString()); assertTrue(x.isSubtype(windowCtor.getInstanceType())); assertFalse(x.isEquivalentTo(windowCtor.getInstanceType())); assertTrue(x.hasProperty("alert")); } public void testGlobalThis2() { testSame( "/** @constructor */ function Window() {}" + "Window.prototype = {alert: function() {}};" + "var x = this;"); ObjectType x = (ObjectType) (globalScope.getVar("x").getType()); FunctionType windowCtor = (FunctionType) (globalScope.getVar("Window").getType()); assertEquals("global this", x.toString()); assertTrue(x.isSubtype(windowCtor.getInstanceType())); assertFalse(x.isEquivalentTo(windowCtor.getInstanceType())); assertTrue(x.hasProperty("alert")); } public void testObjectLiteralCast() { // Verify that "goog.reflect.object" does not modify the types on // "A.B" testSame("/** @constructor */ A.B = function() {}\n" + "A.B.prototype.isEnabled = true;\n" + "goog.reflect.object(A.B, {isEnabled: 3})\n" + "var x = (new A.B()).isEnabled;"); assertEquals("A.B", findTokenType(Token.OBJECTLIT, globalScope).toString()); assertEquals("boolean", findNameType("x", globalScope).toString()); } public void testBadObjectLiteralCast1() { testSame("/** @constructor */ A.B = function() {}\n" + "goog.reflect.object(A.B, 1)", ClosureCodingConvention.OBJECTLIT_EXPECTED); } public void testBadObjectLiteralCast2() { testSame("goog.reflect.object(A.B, {})", TypedScopeCreator.CONSTRUCTOR_EXPECTED); } public void testConstructorNode() { testSame("var goog = {}; /** @constructor */ goog.Foo = function() {};"); ObjectType ctor = (ObjectType) (findNameType("goog.Foo", globalScope)); assertNotNull(ctor); assertTrue(ctor.isConstructor()); assertEquals("function (new:goog.Foo): undefined", ctor.toString()); } public void testForLoopIntegration() { testSame("var y = 3; for (var x = true; x; y = x) {}"); Var y = globalScope.getVar("y"); assertTrue(y.isTypeInferred()); assertEquals("(boolean|number)", y.getType().toString()); } public void testConstructorAlias() { testSame( "/** @constructor */ var Foo = function() {};" + "/** @constructor */ var FooAlias = Foo;"); assertEquals("Foo", registry.getType("FooAlias").toString()); Asserts.assertTypeEquals(registry.getType("Foo"), registry.getType("FooAlias")); } public void testNamespacedConstructorAlias() { testSame( "var goog = {};" + "/** @constructor */ goog.Foo = function() {};" + "/** @constructor */ goog.FooAlias = goog.Foo;"); assertEquals("goog.Foo", registry.getType("goog.FooAlias").toString()); Asserts.assertTypeEquals(registry.getType("goog.Foo"), registry.getType("goog.FooAlias")); } public void testTemplateType1() { testSame( "/**\n" + " * @param {function(this:T, ...)} fn\n" + " * @param {T} thisObj\n" + " * @template T\n" + " */\n" + "function bind(fn, thisObj) {}" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @return {number} */\n" + "Foo.prototype.baz = function() {};\n" + "bind(function() { var g = this; var f = this.baz(); }, new Foo());"); assertEquals("Foo", findNameType("g", lastLocalScope).toString()); assertEquals("number", findNameType("f", lastLocalScope).toString()); } public void testTemplateType2() { testSame( "/**\n" + " * @param {T} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @type {string} */\n" + "var val = 'hi';\n" + "var result = f(val);"); assertEquals("string", findNameType("result", globalScope).toString()); } public void testTemplateType2a() { testSame( "/**\n" + " * @param {T} x\n" + " * @return {T|undefined}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @type {string} */\n" + "var val = 'hi';\n" + "var result = f(val);"); assertEquals("(string|undefined)", findNameType("result", globalScope).toString()); } public void testTemplateType2b() { testSame( "/**\n" + " * @param {T} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @type {string|undefined} */\n" + "var val = 'hi';\n" + "var result = f(val);"); assertEquals("(string|undefined)", findNameType("result", globalScope).toString()); } public void testTemplateType3() { testSame( "/**\n" + " * @param {T} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @type {string} */\n" + "var val1 = 'hi';\n" + "var result1 = f(val1);" + "/** @type {number} */\n" + "var val2 = 0;\n" + "var result2 = f(val2);"); assertEquals("string", findNameType("result1", globalScope).toString()); assertEquals("number", findNameType("result2", globalScope).toString()); } public void testTemplateType4() { testSame( "/**\n" + " * @param {T} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @type {!Array.} */\n" + "var arr = [];\n" + "(function () {var result = f(arr);})();"); JSType resultType = findNameType("result", lastLocalScope); assertEquals("Array.", resultType.toString()); } public void testTemplateType4a() { testSame( "/**\n" + " * @param {function():T} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @return {string} */\n" + "var g = function(){return 'hi'};\n" + "(function () {var result = f(g);})();"); JSType resultType = findNameType("result", lastLocalScope); assertEquals("string", resultType.toString()); } public void testTemplateType4b() { testSame( "/**\n" + " * @param {function(T):void} x\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "function f(x) {\n" + " return x;\n" + "}" + "/** @param {string} x */\n" + "var g = function(x){};\n" + "(function () {var result = f(g);})();"); JSType resultType = findNameType("result", lastLocalScope); assertEquals("string", resultType.toString()); } public void testTemplateType5() { testSame( "/**\n" + " * @param {Array.} arr\n" + " * @return {!Array.}\n" + " * @template T\n" + " */\n" + "function f(arr) {\n" + " return arr;\n" + "}" + "/** @type {Array.} */\n" + "var arr = [];\n" + "var result = f(arr);"); assertEquals("Array.", findNameTypeStr("result", globalScope)); } public void testTemplateType6() { testSame( "/**\n" + " * @param {Array.|string|undefined} arr\n" + " * @return {!Array.}\n" + " * @template T\n" + " */\n" + "function f(arr) {\n" + " return arr;\n" + "}" + "/** @type {Array.} */\n" + "var arr = [];\n" + "var result = f(arr);"); assertEquals("Array.", findNameTypeStr("result", globalScope)); } public void testTemplateType7() { testSame( "var goog = {};\n" + "goog.array = {};\n" + "/**\n" + " * @param {Array.} arr\n" + " * @param {function(this:S, !T, number, !Array.):boolean} f\n" + " * @param {!S=} opt_obj\n" + " * @return {!Array.}\n" + " * @template T,S\n" + " */\n" + "goog.array.filter = function(arr, f, opt_obj) {\n" + " var res = [];\n" + " for (var i = 0; i < arr.length; i++) {\n" + " if (f.call(opt_obj, arr[i], i, arr)) {\n" + " res.push(val);\n" + " }\n" + " }\n" + " return res;\n" + "}" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @type {Array.} */\n" + "var arr = [];\n" + "var result = goog.array.filter(arr," + " function(a,b,c) {var self=this;}, new Foo());"); assertEquals("Foo", findNameType("self", lastLocalScope).toString()); assertEquals("string", findNameType("a", lastLocalScope).toString()); assertEquals("number", findNameType("b", lastLocalScope).toString()); assertEquals("Array.", findNameType("c", lastLocalScope).toString()); assertEquals("Array.", findNameType("result", globalScope).toString()); } public void testTemplateType7b() { testSame( "var goog = {};\n" + "goog.array = {};\n" + "/**\n" + " * @param {Array.} arr\n" + " * @param {function(this:S, !T, number, !Array.):boolean} f\n" + " * @param {!S=} opt_obj\n" + " * @return {!Array.}\n" + " * @template T,S\n" + " */\n" + "goog.array.filter = function(arr, f, opt_obj) {\n" + " var res = [];\n" + " for (var i = 0; i < arr.length; i++) {\n" + " if (f.call(opt_obj, arr[i], i, arr)) {\n" + " res.push(val);\n" + " }\n" + " }\n" + " return res;\n" + "}" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @type {Array.} */\n" + "var arr = [];\n" + "var result = goog.array.filter(arr," + " function(a,b,c) {var self=this;}, new Foo());"); assertEquals("Foo", findNameType("self", lastLocalScope).toString()); assertEquals("string", findNameType("a", lastLocalScope).toString()); assertEquals("number", findNameType("b", lastLocalScope).toString()); assertEquals("Array.", findNameType("c", lastLocalScope).toString()); assertEquals("Array.", findNameType("result", globalScope).toString()); } public void testTemplateType7c() { testSame( "var goog = {};\n" + "goog.array = {};\n" + "/**\n" + " * @param {Array.} arr\n" + " * @param {function(this:S, T, number, Array.):boolean} f\n" + " * @param {!S=} opt_obj\n" + " * @return {!Array.}\n" + " * @template T,S\n" + " */\n" + "goog.array.filter = function(arr, f, opt_obj) {\n" + " var res = [];\n" + " for (var i = 0; i < arr.length; i++) {\n" + " if (f.call(opt_obj, arr[i], i, arr)) {\n" + " res.push(val);\n" + " }\n" + " }\n" + " return res;\n" + "}" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @type {Array.} */\n" + "var arr = [];\n" + "var result = goog.array.filter(arr," + " function(a,b,c) {var self=this;}, new Foo());"); assertEquals("Foo", findNameType("self", lastLocalScope).toString()); assertEquals("string", findNameType("a", lastLocalScope).toString()); assertEquals("number", findNameType("b", lastLocalScope).toString()); assertEquals("(Array.|null)", findNameType("c", lastLocalScope).toString()); assertEquals("Array.", findNameType("result", globalScope).toString()); } public void disable_testTemplateType8() { // TODO(johnlenz): somehow allow templated typedefs testSame( "/** @constructor */ NodeList = function() {};" + "/** @constructor */ Arguments = function() {};" + "var goog = {};" + "goog.array = {};" + "/**\n" + " * @typedef {Array.|NodeList|Arguments|{length: number}}\n" + " * @template T\n" + " */\n" + "goog.array.ArrayLike;" + "/**\n" + " * @param {function(this:T, ...)} fn\n" + " * @param {T} thisObj\n" + " * @template T\n" + " */\n" + "function bind(fn, thisObj) {}" + "/** @constructor */\n" + "function Foo() {}\n" + "/** @return {number} */\n" + "Foo.prototype.baz = function() {};\n" + "bind(function() { var g = this; var f = this.baz(); }, new Foo());"); assertEquals("T", findNameType("g", lastLocalScope).toString()); assertTrue(findNameType("g", lastLocalScope).isEquivalentTo( registry.getType("Foo"))); assertEquals("number", findNameType("f", lastLocalScope).toString()); } public void testTemplateType9() { testSame( "/** @constructor */\n" + "function Foo() {}\n" + "/**\n" + " * @this {T}\n" + " * @return {T}\n" + " * @template T\n" + " */\n" + "Foo.prototype.method = function() {};\n" + "/**\n" + " * @constructor\n" + " * @extends {Foo}\n" + " */\n" + "function Bar() {}\n" + "\n" + "var g = new Bar().method();\n"); assertEquals("Bar", findNameType("g", globalScope).toString()); } public void testTemplateType10() { // NOTE: we would like the type within the function to remain "Foo" // we can handle this by support template type like "T extends Foo" // to provide a "minimum" type for "Foo" within the function body. testSame( "/** @constructor */\n" + "function Foo() {}\n" + "\n" + "/**\n" + " * @this {T}\n" + " * @return {T} fn\n" + " * @template T\n" + " */\n" + "Foo.prototype.method = function() {var g = this;};\n"); assertEquals("T", findNameType("g", lastLocalScope).toString()); } public void testTemplateType11() { testSame( "/**\n" + " * @this {T}\n" + " * @return {T} fn\n" + " * @template T\n" + " */\n" + "var method = function() {};\n" + "/**\n" + " * @constructor\n" + " */\n" + "function Bar() {}\n" + "\n" + "var g = method().call(new Bar());\n"); // NOTE: we would like this to be "Bar" assertEquals("?", findNameType("g", globalScope).toString()); } public void testTemplateType12() { testSame( "/** @constructor */\n" + "function Foo() {}\n" + "\n" + "/**\n" + " * @this {Array.|{length:number}}\n" + " * @return {T} fn\n" + " * @template T\n" + " */\n" + "Foo.prototype.method = function() {var g = this;};\n"); assertEquals("(Array.|{length: number})", findNameType("g", lastLocalScope).toString()); } public void disable_testTemplateType13() { // TODO(johnlenz): allow template types in @type function expressions testSame( "/**\n" + " * @type {function(T):T}\n" + " * @template T\n" + " */\n" + "var f;" + "/** @type {string} */\n" + "var val = 'hi';\n" + "var result = f(val);"); assertEquals("(string|undefined)", findNameType("result", globalScope).toString()); } public void testClosureParameterTypesWithoutJSDoc() { testSame( "/**\n" + " * @param {function(!Object)} bar\n" + " */\n" + "function foo(bar) {}\n" + "foo(function(baz) { var f = baz; })\n"); assertEquals("Object", findNameType("f", lastLocalScope).toString()); } public void testClosureParameterTypesWithJSDoc() { testSame( "/**\n" + " * @param {function(!Object)} bar\n" + " */\n" + "function foo(bar) {}\n" + "foo(/** @type {function(string)} */" + " (function(baz) { var f = baz; }))\n"); assertEquals("string", findNameType("f", lastLocalScope).toString()); } public void testDuplicateExternProperty1() { testSame( "/** @constructor */ function Foo() {}" + "Foo.prototype.bar;" + "/** @type {number} */ Foo.prototype.bar; var x = (new Foo).bar;", null); assertEquals("number", findNameType("x", globalScope).toString()); } public void testDuplicateExternProperty2() { testSame( "/** @constructor */ function Foo() {}" + "/** @type {number} */ Foo.prototype.bar;" + "Foo.prototype.bar; var x = (new Foo).bar;", null); assertEquals("number", findNameType("x", globalScope).toString()); } public void testAbstractMethod() { testSame( "/** @type {!Function} */ var abstractMethod;" + "/** @constructor */ function Foo() {}" + "/** @param {number} x */ Foo.prototype.bar = abstractMethod;"); assertEquals( "Function", findNameType("abstractMethod", globalScope).toString()); FunctionType ctor = (FunctionType) findNameType("Foo", globalScope); ObjectType instance = ctor.getInstanceType(); assertEquals("Foo", instance.toString()); ObjectType proto = instance.getImplicitPrototype(); assertEquals("Foo.prototype", proto.toString()); assertEquals( "function (this:Foo, number): ?", proto.getPropertyType("bar").toString()); } public void testAbstractMethod2() { testSame( "/** @type {!Function} */ var abstractMethod;" + "/** @param {number} x */ var y = abstractMethod;"); assertEquals( "Function", findNameType("y", globalScope).toString()); assertEquals( "function (number): ?", globalScope.getVar("y").getType().toString()); } public void testAbstractMethod3() { testSame( "/** @type {!Function} */ var abstractMethod;" + "/** @param {number} x */ var y = abstractMethod; y;"); assertEquals( "function (number): ?", findNameType("y", globalScope).toString()); } public void testAbstractMethod4() { testSame( "/** @type {!Function} */ var abstractMethod;" + "/** @constructor */ function Foo() {}" + "Foo.prototype = {/** @param {number} x */ bar: abstractMethod};"); assertEquals( "Function", findNameType("abstractMethod", globalScope).toString()); FunctionType ctor = (FunctionType) findNameType("Foo", globalScope); ObjectType instance = ctor.getInstanceType(); assertEquals("Foo", instance.toString()); ObjectType proto = instance.getImplicitPrototype(); assertEquals("Foo.prototype", proto.toString()); assertEquals( // should be: "function (this:Foo, number): ?" "function (this:Foo, number): ?", proto.getPropertyType("bar").toString()); } public void testActiveXObject() { testSame( CompilerTypeTestCase.ACTIVE_X_OBJECT_DEF, "var x = new ActiveXObject();", null); assertEquals( "?", findNameType("x", globalScope).toString()); } public void testReturnTypeInference1() { testSame("function f() {}"); assertEquals( "function (): undefined", findNameType("f", globalScope).toString()); } public void testReturnTypeInference2() { testSame("/** @return {?} */ function f() {}"); assertEquals( "function (): ?", findNameType("f", globalScope).toString()); } public void testReturnTypeInference3() { testSame("function f() {x: return 3;}"); assertEquals( "function (): ?", findNameType("f", globalScope).toString()); } public void testReturnTypeInference4() { testSame("function f() { throw Error(); }"); assertEquals( "function (): ?", findNameType("f", globalScope).toString()); } public void testReturnTypeInference5() { testSame("function f() { if (true) { return 1; } }"); assertEquals( "function (): ?", findNameType("f", globalScope).toString()); } public void testLiteralTypesInferred() { testSame("null + true + false + 0 + '' + {}"); assertEquals( "null", findTokenType(Token.NULL, globalScope).toString()); assertEquals( "boolean", findTokenType(Token.TRUE, globalScope).toString()); assertEquals( "boolean", findTokenType(Token.FALSE, globalScope).toString()); assertEquals( "number", findTokenType(Token.NUMBER, globalScope).toString()); assertEquals( "string", findTokenType(Token.STRING, globalScope).toString()); assertEquals( "{}", findTokenType(Token.OBJECTLIT, globalScope).toString()); } public void testGlobalQualifiedNameInLocalScope() { testSame( "var ns = {}; " + "(function() { " + " /** @param {number} x */ ns.foo = function(x) {}; })();" + "(function() { ns.foo(3); })();"); assertNotNull(globalScope.getVar("ns.foo")); assertEquals( "function (number): undefined", globalScope.getVar("ns.foo").getType().toString()); } public void testDeclaredObjectLitProperty1() throws Exception { testSame("var x = {/** @type {number} */ y: 3};"); ObjectType xType = ObjectType.cast(globalScope.getVar("x").getType()); assertEquals( "number", xType.getPropertyType("y").toString()); assertEquals( "{y: number}", xType.toString()); } public void testDeclaredObjectLitProperty2() throws Exception { testSame("var x = {/** @param {number} z */ y: function(z){}};"); ObjectType xType = ObjectType.cast(globalScope.getVar("x").getType()); assertEquals( "function (number): undefined", xType.getPropertyType("y").toString()); assertEquals( "{y: function (number): undefined}", xType.toString()); } public void testDeclaredObjectLitProperty3() throws Exception { testSame("function f() {" + " var x = {/** @return {number} */ y: function(z){ return 3; }};" + "}"); ObjectType xType = ObjectType.cast(lastLocalScope.getVar("x").getType()); assertEquals( "function (?): number", xType.getPropertyType("y").toString()); assertEquals( "{y: function (?): number}", xType.toString()); } public void testDeclaredObjectLitProperty4() throws Exception { testSame("var x = {y: 5, /** @type {number} */ z: 3};"); ObjectType xType = ObjectType.cast(globalScope.getVar("x").getType()); assertEquals( "number", xType.getPropertyType("y").toString()); assertFalse(xType.isPropertyTypeDeclared("y")); assertTrue(xType.isPropertyTypeDeclared("z")); assertEquals( "{y: number, z: number}", xType.toString()); } public void testDeclaredObjectLitProperty5() throws Exception { testSame("var x = {/** @type {number} */ prop: 3};" + "function f() { var y = x.prop; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("number", yType.toString()); } public void testDeclaredObjectLitProperty6() throws Exception { testSame("var x = {/** This is JsDoc */ prop: function(){}};"); Var prop = globalScope.getVar("x.prop"); JSType propType = prop.getType(); assertEquals("function (): undefined", propType.toString()); assertFalse(prop.isTypeInferred()); assertFalse( ObjectType.cast(globalScope.getVar("x").getType()) .isPropertyTypeInferred("prop")); } public void testInferredObjectLitProperty1() throws Exception { testSame("var x = {prop: 3};"); Var prop = globalScope.getVar("x.prop"); JSType propType = prop.getType(); assertEquals("number", propType.toString()); assertTrue(prop.isTypeInferred()); assertTrue( ObjectType.cast(globalScope.getVar("x").getType()) .isPropertyTypeInferred("prop")); } public void testInferredObjectLitProperty2() throws Exception { testSame("var x = {prop: function(){}};"); Var prop = globalScope.getVar("x.prop"); JSType propType = prop.getType(); assertEquals("function (): undefined", propType.toString()); assertTrue(prop.isTypeInferred()); assertTrue( ObjectType.cast(globalScope.getVar("x").getType()) .isPropertyTypeInferred("prop")); } public void testDeclaredConstType1() throws Exception { testSame( "/** @const */ var x = 3;" + "function f() { var y = x; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("number", yType.toString()); } public void testDeclaredConstType2() throws Exception { testSame( "/** @const */ var x = {};" + "function f() { var y = x; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("{}", yType.toString()); } public void testDeclaredConstType3() throws Exception { testSame( "/** @const */ var x = {};" + "/** @const */ x.z = 'hi';" + "function f() { var y = x.z; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("string", yType.toString()); } public void testDeclaredConstType4() throws Exception { testSame( "/** @constructor */ function Foo() {}" + "/** @const */ Foo.prototype.z = 'hi';" + "function f() { var y = (new Foo()).z; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("string", yType.toString()); ObjectType fooType = ((FunctionType) globalScope.getVar("Foo").getType()).getInstanceType(); assertTrue(fooType.isPropertyTypeDeclared("z")); } public void testDeclaredConstType5() throws Exception { testSame( "/** @const */ var goog = goog || {};" + "/** @const */ var foo = goog || {};" + "function f() { var y = goog; var z = foo; }"); JSType yType = lastLocalScope.getVar("y").getType(); assertEquals("{}", yType.toString()); JSType zType = lastLocalScope.getVar("z").getType(); assertEquals("?", zType.toString()); } public void testBadCtorInit1() throws Exception { testSame("/** @constructor */ var f;", CTOR_INITIALIZER); } public void testBadCtorInit2() throws Exception { testSame("var x = {}; /** @constructor */ x.f;", CTOR_INITIALIZER); } public void testBadIfaceInit1() throws Exception { testSame("/** @interface */ var f;", IFACE_INITIALIZER); } public void testBadIfaceInit2() throws Exception { testSame("var x = {}; /** @interface */ x.f;", IFACE_INITIALIZER); } public void testFunctionInHook() throws Exception { testSame("/** @param {number} x */ var f = Math.random() ? " + "function(x) {} : function(x) {};"); assertEquals("number", lastLocalScope.getVar("x").getType().toString()); } public void testFunctionInAnd() throws Exception { testSame("/** @param {number} x */ var f = Math.random() && " + "function(x) {};"); assertEquals("number", lastLocalScope.getVar("x").getType().toString()); } public void testFunctionInOr() throws Exception { testSame("/** @param {number} x */ var f = Math.random() || " + "function(x) {};"); assertEquals("number", lastLocalScope.getVar("x").getType().toString()); } public void testFunctionInComma() throws Exception { testSame("/** @param {number} x */ var f = (Math.random(), " + "function(x) {});"); assertEquals("number", lastLocalScope.getVar("x").getType().toString()); } public void testDeclaredCatchExpression1() { testSame( "try {} catch (e) {}"); // Note: "e" actually belongs to a inner scope but we don't // model catches as separate scopes currently. assertEquals(null, globalScope.getVar("e").getType()); } public void testDeclaredCatchExpression2() { testSame( "try {} catch (/** @type {string} */ e) {}"); // Note: "e" actually belongs to a inner scope but we don't // model catches as separate scopes currently. assertEquals("string", globalScope.getVar("e").getType().toString()); } private JSType findNameType(final String name, Scope scope) { return findTypeOnMatchedNode(new Predicate() { @Override public boolean apply(Node n) { return name.equals(n.getQualifiedName()); } }, scope); } private String findNameTypeStr(final String name, Scope scope) { return findNameType(name, scope).toString(); } private JSType findTokenType(final int type, Scope scope) { return findTypeOnMatchedNode(new Predicate() { @Override public boolean apply(Node n) { return type == n.getType(); } }, scope); } private JSType findTypeOnMatchedNode(Predicate matcher, Scope scope) { Node root = scope.getRootNode(); Deque queue = Lists.newLinkedList(); queue.push(root); while (!queue.isEmpty()) { Node current = queue.pop(); if (matcher.apply(current) && current.getJSType() != null) { return current.getJSType(); } for (Node child : current.children()) { queue.push(child); } } return null; } private JSType getNativeType(JSTypeNative type) { return registry.getNativeType(type); } private ObjectType getNativeObjectType(JSTypeNative type) { return (ObjectType) registry.getNativeType(type); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ClosureOptimizePrimitivesTest.java0000644000175000017500000000333712115204405031746 0ustar apoapo/* * Copyright 2011 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; /** * Tests for {@link ClosureOptimizePrimitives}. * * @author agrieve@google.com (Andrew Grieve) */ public class ClosureOptimizePrimitivesTest extends CompilerTestCase { @Override public CompilerPass getProcessor(final Compiler compiler) { return new ClosureOptimizePrimitives(compiler); } public void testObjectCreateNonConstKey() { testSame("goog.object.create('a',1,2,3,foo,bar);"); } public void testObjectCreateOddParams() { testSame("goog.object.create('a',1,2);"); } public void testObjectCreate1() { test("var a = goog.object.create()", "var a = {}"); } public void testObjectCreate2() { test("var a = goog$object$create('b',goog$object$create('c','d'))", "var a = {'b':{'c':'d'}};"); } public void testObjectCreate3() { test("var a = goog.object.create(1,2)", "var a = {1:2}"); } public void testObjectCreate4() { test("alert(goog.object.create(1,2).toString())", "alert({1:2}.toString())"); } public void testObjectCreate5() { test("goog.object.create('a',2).toString()", "({'a':2}).toString()"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MinimizeExitPointsTest.java0000644000175000017500000002473612115204405030353 0ustar apoapo/* * 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.javascript.rhino.Node; /** * @author johnlenz@google.com (John Lenz) */ public class MinimizeExitPointsTest extends CompilerTestCase { @Override public void setUp() { super.enableLineNumberCheck(true); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, new MinimizeExitPoints(compiler)); } }; } @Override protected int getNumRepetitions() { return 1; } void foldSame(String js) { testSame(js); } void fold(String js, String expected) { test(js, expected); } void fold(String js, String expected, DiagnosticType warning) { test(js, expected, warning); } public void testBreakOptimization() throws Exception { fold("f:{if(true){a();break f;}else;b();}", "f:{if(true){a()}else{b()}}"); fold("f:{if(false){a();break f;}else;b();break f;}", "f:{if(false){a()}else{b()}}"); fold("f:{if(a()){b();break f;}else;c();}", "f:{if(a()){b();}else{c();}}"); fold("f:{if(a()){b()}else{c();break f;}}", "f:{if(a()){b()}else{c();}}"); fold("f:{if(a()){b();break f;}else;}", "f:{if(a()){b();}else;}"); fold("f:{if(a()){break f;}else;}", "f:{if(a()){}else;}"); fold("f:while(a())break f;", "f:while(a())break f"); foldSame("f:for(x in a())break f"); fold("f:{while(a())break;}", "f:{while(a())break;}"); foldSame("f:{for(x in a())break}"); fold("f:try{break f;}catch(e){break f;}", "f:try{}catch(e){}"); fold("f:try{if(a()){break f;}else{break f;} break f;}catch(e){}", "f:try{if(a()){}else{}}catch(e){}"); fold("f:g:break f", ""); fold("f:g:{if(a()){break f;}else{break f;} break f;}", "f:g:{if(a()){}else{}}"); } public void testFunctionReturnOptimization() throws Exception { fold("function f(){if(a()){b();if(c())return;}}", "function f(){if(a()){b();if(c());}}"); fold("function f(){if(x)return; x=3; return; }", "function f(){if(x); else x=3}"); fold("function f(){if(true){a();return;}else;b();}", "function f(){if(true){a();}else{b();}}"); fold("function f(){if(false){a();return;}else;b();return;}", "function f(){if(false){a();}else{b();}}"); fold("function f(){if(a()){b();return;}else;c();}", "function f(){if(a()){b();}else{c();}}"); fold("function f(){if(a()){b()}else{c();return;}}", "function f(){if(a()){b()}else{c();}}"); fold("function f(){if(a()){b();return;}else;}", "function f(){if(a()){b();}else;}"); fold("function f(){if(a()){return;}else{return;} return;}", "function f(){if(a()){}else{}}"); fold("function f(){if(a()){return;}else{return;} b();}", "function f(){if(a()){}else{return;b()}}"); fold("function f(){ if (x) return; if (y) return; if (z) return; w(); }", " function f() {" + " if (x) {} else { if (y) {} else { if (z) {} else w(); }}" + " }"); fold("function f(){while(a())return;}", "function f(){while(a())return}"); foldSame("function f(){for(x in a())return}"); fold("function f(){while(a())break;}", "function f(){while(a())break}"); foldSame("function f(){for(x in a())break}"); fold("function f(){try{return;}catch(e){return;}finally{return}}", "function f(){try{}catch(e){}finally{}}"); fold("function f(){try{return;}catch(e){return;}}", "function f(){try{}catch(e){}}"); fold("function f(){try{return;}finally{return;}}", "function f(){try{}finally{}}"); fold("function f(){try{if(a()){return;}else{return;} return;}catch(e){}}", "function f(){try{if(a()){}else{}}catch(e){}}"); fold("function f(){g:return}", "function f(){}"); fold("function f(){g:if(a()){return;}else{return;} return;}", "function f(){g:if(a()){}else{}}"); fold("function f(){try{g:if(a()){} return;}finally{return}}", "function f(){try{g:if(a()){}}finally{}}"); } public void testWhileContinueOptimization() throws Exception { fold("while(true){if(x)continue; x=3; continue; }", "while(true)if(x);else x=3"); foldSame("while(true){a();continue;b();}"); fold("while(true){if(true){a();continue;}else;b();}", "while(true){if(true){a();}else{b()}}"); fold("while(true){if(false){a();continue;}else;b();continue;}", "while(true){if(false){a()}else{b();}}"); fold("while(true){if(a()){b();continue;}else;c();}", "while(true){if(a()){b();}else{c();}}"); fold("while(true){if(a()){b();}else{c();continue;}}", "while(true){if(a()){b();}else{c();}}"); fold("while(true){if(a()){b();continue;}else;}", "while(true){if(a()){b();}else;}"); fold("while(true){if(a()){continue;}else{continue;} continue;}", "while(true){if(a()){}else{}}"); fold("while(true){if(a()){continue;}else{continue;} b();}", "while(true){if(a()){}else{continue;b();}}"); fold("while(true)while(a())continue;", "while(true)while(a());"); fold("while(true)for(x in a())continue", "while(true)for(x in a());"); fold("while(true)while(a())break;", "while(true)while(a())break"); fold("while(true)for(x in a())break", "while(true)for(x in a())break"); fold("while(true){try{continue;}catch(e){continue;}}", "while(true){try{}catch(e){}}"); fold("while(true){try{if(a()){continue;}else{continue;}" + "continue;}catch(e){}}", "while(true){try{if(a()){}else{}}catch(e){}}"); fold("while(true){g:continue}", "while(true){}"); // This case could be improved. fold("while(true){g:if(a()){continue;}else{continue;} continue;}", "while(true){g:if(a());else;}"); } public void testDoContinueOptimization() throws Exception { fold("do{if(x)continue; x=3; continue; }while(true)", "do if(x); else x=3; while(true)"); foldSame("do{a();continue;b()}while(true)"); fold("do{if(true){a();continue;}else;b();}while(true)", "do{if(true){a();}else{b();}}while(true)"); fold("do{if(false){a();continue;}else;b();continue;}while(true)", "do{if(false){a();}else{b();}}while(true)"); fold("do{if(a()){b();continue;}else;c();}while(true)", "do{if(a()){b();}else{c()}}while(true)"); fold("do{if(a()){b();}else{c();continue;}}while(true)", "do{if(a()){b();}else{c();}}while(true)"); fold("do{if(a()){b();continue;}else;}while(true)", "do{if(a()){b();}else;}while(true)"); fold("do{if(a()){continue;}else{continue;} continue;}while(true)", "do{if(a()){}else{}}while(true)"); fold("do{if(a()){continue;}else{continue;} b();}while(true)", "do{if(a()){}else{continue; b();}}while(true)"); fold("do{while(a())continue;}while(true)", "do while(a());while(true)"); fold("do{for(x in a())continue}while(true)", "do for(x in a());while(true)"); fold("do{while(a())break;}while(true)", "do while(a())break;while(true)"); fold("do for(x in a())break;while(true)", "do for(x in a())break;while(true)"); fold("do{try{continue;}catch(e){continue;}}while(true)", "do{try{}catch(e){}}while(true)"); fold("do{try{if(a()){continue;}else{continue;}" + "continue;}catch(e){}}while(true)", "do{try{if(a()){}else{}}catch(e){}}while(true)"); fold("do{g:continue}while(true)", "do{}while(true)"); // This case could be improved. fold("do{g:if(a()){continue;}else{continue;} continue;}while(true)", "do{g:if(a());else;}while(true)"); fold("do { foo(); continue; } while(false)", "do { foo(); } while(false)"); fold("do { foo(); break; } while(false)", "do { foo(); } while(false)"); } public void testForContinueOptimization() throws Exception { fold("for(x in y){if(x)continue; x=3; continue; }", "for(x in y)if(x);else x=3"); foldSame("for(x in y){a();continue;b()}"); fold("for(x in y){if(true){a();continue;}else;b();}", "for(x in y){if(true)a();else b();}"); fold("for(x in y){if(false){a();continue;}else;b();continue;}", "for(x in y){if(false){a();}else{b()}}"); fold("for(x in y){if(a()){b();continue;}else;c();}", "for(x in y){if(a()){b();}else{c();}}"); fold("for(x in y){if(a()){b();}else{c();continue;}}", "for(x in y){if(a()){b();}else{c();}}"); fold("for(x=0;x0){var k=1;}", "var i=0;if(i>0);"); // Test with for loop test("for (var i in booyah) {" + " if (i > 0) x += ', ';" + " var arg = 'foo';" + " if (arg.length > 40) {" + " var unused = 'bar';" + // this variable is unused " arg = arg.substr(0, 40) + '...';" + " }" + " x += arg;" + "}", "for(var i in booyah){if(i>0)x+=\", \";" + "var arg=\"foo\";if(arg.length>40)arg=arg.substr(0,40)+\"...\";" + "x+=arg}"); // Test with function expressions in another function call test("function A(){}" + "if(0){function B(){}}win.setTimeout(function(){A()})", "function A(){}" + "if(0);win.setTimeout(function(){A()})"); // Test with recursive functions test("function A(){A()}function B(){B()}B()", "function B(){B()}B()"); // Test with multiple var declarations. test("var x,y=2,z=3;A(x);B(z);var a,b,c=4;C()", "var x,z=3;A(x);B(z);C()"); // Test with for loop declarations test("for(var i=0,j=0;i<10;){}" + "for(var x=0,y=0;;y++){}" + "for(var a,b;;){a}" + "for(var c,d;;);" + "for(var item in items){}", "for(var i=0;i<10;);" + "for(var y=0;;y++);" + "for(var a;;)a;" + "for(;;);" + "for(var item in items);"); // Test multiple passes required test("var a,b,c,d;var e=[b,c];var x=e[3];var f=[d];print(f[0])", "var d;var f=[d];print(f[0])"); // Test proper scoping (static vs dynamic) test("var x;function A(){var x;B()}function B(){print(x)}A()", "var x;function A(){B()}function B(){print(x)}A()"); // Test closures in a return statement test("function A(){var x;return function(){print(x)}}A()", "function A(){var x;return function(){print(x)}}A()"); // Test other closures, multiple passes test("function A(){}function B(){" + "var c,d,e,f,g,h;" + "function C(){print(c)}" + "var handler=function(){print(d)};" + "var handler2=function(){handler()};" + "e=function(){print(e)};" + "if(1){function G(){print(g)}}" + "arr=[function(){print(h)}];" + "return function(){print(f)}}B()", "function B(){" + "var f,h;" + "if(1);" + "arr=[function(){print(h)}];" + "return function(){print(f)}}B()"); // Test exported names test("var a,b=1; function _A1() {this.foo(a)}", "var a;function _A1(){this.foo(a)}"); // Test undefined (i.e. externally defined) names test("undefinedVar = 1", "undefinedVar=1"); // Test unused vars with side effects test("var a,b=foo(),c=i++,d;var e=boo();var f;print(d);", "foo(); i++; var d; boo(); print(d)"); test("var a,b=foo()", "foo()"); test("var b=foo(),a", "foo()"); test("var a,b=foo(a)", "var a; foo(a);"); } public void testFunctionArgRemoval() { // remove all function arguments test("var b=function(c,d){return};b(1,2)", "var b=function(){return};b(1,2)"); // remove no function arguments testSame("var b=function(c,d){return c+d};b(1,2)"); testSame("var b=function(e,f,c,d){return c+d};b(1,2)"); // remove some function arguments test("var b=function(c,d,e,f){return c+d};b(1,2)", "var b=function(c,d){return c+d};b(1,2)"); test("var b=function(e,c,f,d,g){return c+d};b(1,2)", "var b=function(e,c,f,d){return c+d};b(1,2)"); } public void testFunctionArgRemovalFromCallSites() { this.modifyCallSites = true; // remove all function arguments test("var b=function(c,d){return};b(1,2)", "var b=function(){return};b()"); // remove no function arguments testSame("var b=function(c,d){return c+d};b(1,2)"); test("var b=function(e,f,c,d){return c+d};b(1,2)", "var b=function(c,d){return c+d};b()"); // remove some function arguments test("var b=function(c,d,e,f){return c+d};b(1,2)", "var b=function(c,d){return c+d};b(1,2)"); test("var b=function(e,c,f,d,g){return c+d};b(1,2)", "var b=function(c,d){return c+d};b(2)"); } public void testFunctionsDeadButEscaped() { testSame("function b(a) { a = 1; print(arguments[0]) }; b(6)"); testSame("function b(a) { a = 1; arguments=1; }; b(6)"); testSame("function b(a) { var c = 2; a = c; print(arguments[0]) }; b(6)"); } public void testVarInControlStructure() { test("if (true) var b = 3;", "if(true);"); test("if (true) var b = 3; else var c = 5;", "if(true);else;"); test("while (true) var b = 3;", "while(true);"); test("for (;;) var b = 3;", "for(;;);"); test("do var b = 3; while(true)", "do;while(true)"); test("with (true) var b = 3;", "with(true);"); test("f: var b = 3;","f:{}"); } public void testRValueHoisting() { test("var x = foo();", "foo()"); test("var x = {a: foo()};", "({a:foo()})"); test("var x=function y(){}", ""); } public void testModule() { test(createModules( "var unreferenced=1; function x() { foo(); }" + "function uncalled() { var x; return 2; }", "var a,b; function foo() { this.foo(a); } x()"), new String[] { "function x(){foo()}", "var a;function foo(){this.foo(a)}x()" }); } public void testRecursiveFunction1() { testSame("(function x(){return x()})()"); } public void testRecursiveFunction2() { test("var x = 3; (function x() { return x(); })();", "(function x$$1(){return x$$1()})()"); } public void testFunctionWithName1() { test("var x=function f(){};x()", "var x=function(){};x()"); preserveFunctionExpressionNames = true; testSame("var x=function f(){};x()"); } public void testFunctionWithName2() { test("foo(function bar(){})", "foo(function(){})"); preserveFunctionExpressionNames = true; testSame("foo(function bar(){})"); } public void testRemoveGlobal1() { removeGlobal = false; testSame("var x=1"); test("var y=function(x){var z;}", "var y=function(x){}"); } public void testRemoveGlobal2() { removeGlobal = false; testSame("var x=1"); test("function y(x){var z;}", "function y(x){}"); } public void testRemoveGlobal3() { removeGlobal = false; testSame("var x=1"); test("function x(){function y(x){var z;}y()}", "function x(){function y(x){}y()}"); } public void testRemoveGlobal4() { removeGlobal = false; testSame("var x=1"); test("function x(){function y(x){var z;}}", "function x(){}"); } public void testIssue168a() { test("function _a(){" + " (function(x){ _b(); })(1);" + "}" + "function _b(){" + " _a();" + "}", "function _a(){(function(){_b()})(1)}" + "function _b(){_a()}"); } public void testIssue168b() { removeGlobal = false; test("function a(){" + " (function(x){ b(); })(1);" + "}" + "function b(){" + " a();" + "}", "function a(){(function(x){b()})(1)}" + "function b(){a()}"); } public void testUnusedAssign1() { test("var x = 3; x = 5;", ""); } public void testUnusedAssign2() { test("function f(a) { a = 3; } this.x = f;", "function f(){} this.x=f"); } public void testUnusedAssign3() { // e can't be removed, so we don't try to remove the dead assign. // We might be able to improve on this case. test("try { throw ''; } catch (e) { e = 3; }", "try{throw\"\";}catch(e){e=3}"); } public void testUnusedAssign4() { test("function f(a, b) { this.foo(b); a = 3; } this.x = f;", "function f(a,b){this.foo(b);}this.x=f"); } public void testUnusedAssign5() { test("var z = function f() { f = 3; }; z();", "var z=function(){};z()"); } public void testUnusedAssign5b() { test("var z = function f() { f = alert(); }; z();", "var z=function(){alert()};z()"); } public void testUnusedAssign6() { test("var z; z = 3;", ""); } public void testUnusedAssign6b() { test("var z; z = alert();", "alert()"); } public void testUnusedAssign7() { // This loop is normalized to "var i;for(i in..." test("var a = 3; for (var i in {}) { i = a; }", // TODO(johnlenz): "i = a" should be removed here. "var a = 3; var i; for (i in {}) {i = a;}"); } public void testUnusedAssign8() { // This loop is normalized to "var i;for(i in..." test("var a = 3; for (var i in {}) { i = a; } alert(a);", // TODO(johnlenz): "i = a" should be removed here. "var a = 3; var i; for (i in {}) {i = a} alert(a);"); } public void testUnusedPropAssign1() { test("var x = {}; x.foo = 3;", ""); } public void testUnusedPropAssign1b() { test("var x = {}; x.foo = alert();", "alert()"); } public void testUnusedPropAssign2() { test("var x = {}; x['foo'] = 3;", ""); } public void testUnusedPropAssign2b() { test("var x = {}; x[alert()] = alert();", "alert(),alert()"); } public void testUnusedPropAssign3() { test("var x = {}; x['foo'] = {}; x['bar'] = 3", ""); } public void testUnusedPropAssign3b() { test("var x = {}; x[alert()] = alert(); x[alert() + alert()] = alert()", "alert(),alert();(alert() + alert()),alert()"); } public void testUnusedPropAssign4() { test("var x = {foo: 3}; x['foo'] = 5;", ""); } public void testUnusedPropAssign5() { test("var x = {foo: bar()}; x['foo'] = 5;", "var x={foo:bar()};x[\"foo\"]=5"); } public void testUnusedPropAssign6() { test("var x = function() {}; x.prototype.bar = function() {};", ""); } public void testUnusedPropAssign7() { test("var x = {}; x[x.foo] = x.bar;", ""); } public void testUnusedPropAssign7b() { testSame("var x = {}; x[x.foo] = alert(x.bar);"); } public void testUnusedPropAssign7c() { test("var x = {}; x[alert(x.foo)] = x.bar;", "var x={};x[alert(x.foo)]=x.bar"); } public void testUsedPropAssign1() { test("function f(x) { x.bar = 3; } f({});", "function f(x){x.bar=3}f({})"); } public void testUsedPropAssign2() { test("try { throw z; } catch (e) { e.bar = 3; }", "try{throw z;}catch(e){e.bar=3}"); } public void testUsedPropAssign3() { // This pass does not do flow analysis. test("var x = {}; x.foo = 3; x = bar();", "var x={};x.foo=3;x=bar()"); } public void testUsedPropAssign4() { test("var y = foo(); var x = {}; x.foo = 3; y[x.foo] = 5;", "var y=foo();var x={};x.foo=3;y[x.foo]=5"); } public void testUsedPropAssign5() { test("var y = foo(); var x = 3; y[x] = 5;", "var y=foo();var x=3;y[x]=5"); } public void testUsedPropAssign6() { test("var x = newNodeInDom(doc); x.innerHTML = 'new text';", "var x=newNodeInDom(doc);x.innerHTML=\"new text\""); } public void testUsedPropAssign7() { testSame("var x = {}; for (x in alert()) { x.foo = 3; }"); } public void testUsedPropAssign8() { testSame("for (var x in alert()) { x.foo = 3; }"); } public void testUsedPropAssign9() { testSame( "var x = {}; x.foo = newNodeInDom(doc); x.foo.innerHTML = 'new test';"); } public void testDependencies1() { test("var a = 3; var b = function() { alert(a); };", ""); } public void testDependencies1b() { test("var a = 3; var b = alert(function() { alert(a); });", "var a=3;alert(function(){alert(a)})"); } public void testDependencies1c() { test("var a = 3; var _b = function() { alert(a); };", "var a=3;var _b=function(){alert(a)}"); } public void testDependencies2() { test("var a = 3; var b = 3; b = function() { alert(a); };", ""); } public void testDependencies2b() { test("var a = 3; var b = 3; b = alert(function() { alert(a); });", "var a=3;alert(function(){alert(a)})"); } public void testDependencies2c() { testSame("var a=3;var _b=3;_b=function(){alert(a)}"); } public void testGlobalVarReferencesLocalVar() { testSame("var a=3;function f(){var b=4;a=b}alert(a + f())"); } public void testLocalVarReferencesGlobalVar1() { testSame("var a=3;function f(b, c){b=a; alert(b + c);} f();"); } public void testLocalVarReferencesGlobalVar2() { test("var a=3;function f(b, c){b=a; alert(c);} f();", "function f(b, c) { alert(c); } f();"); this.modifyCallSites = true; test("var a=3;function f(b, c){b=a; alert(c);} f();", "function f(c) { alert(c); } f();"); } public void testNestedAssign1() { test("var b = null; var a = (b = 3); alert(a);", "var a = 3; alert(a);"); } public void testNestedAssign2() { test("var a = 1; var b = 2; var c = (b = a); alert(c);", "var a = 1; var c = a; alert(c);"); } public void testNestedAssign3() { test("var b = 0; var z; z = z = b = 1; alert(b);", "var b = 0; b = 1; alert(b);"); } public void testCallSiteInteraction() { this.modifyCallSites = true; testSame("var b=function(){return};b()"); testSame("var b=function(c){return c};b(1)"); test("var b=function(c){};b.call(null, x)", "var b=function(){};b.call(null)"); test("var b=function(c){};b.apply(null, x)", "var b=function(){};b.apply(null, x)"); test("var b=function(c){return};b(1)", "var b=function(){return};b()"); test("var b=function(c){return};b(1,2)", "var b=function(){return};b()"); test("var b=function(c){return};b(1,2);b(3,4)", "var b=function(){return};b();b()"); // Here there is a unknown reference to the function so we can't // change the signature. test("var b=function(c,d){return d};b(1,2);b(3,4);b.length", "var b=function(c,d){return d};b(0,2);b(0,4);b.length"); test("var b=function(c){return};b(1,2);b(3,new x())", "var b=function(){return};b();b(new x())"); test("var b=function(c){return};b(1,2);b(new x(),4)", "var b=function(){return};b();b(new x())"); test("var b=function(c,d){return d};b(1,2);b(new x(),4)", "var b=function(c,d){return d};b(0,2);b(new x(),4)"); test("var b=function(c,d,e){return d};b(1,2,3);b(new x(),4,new x())", "var b=function(c,d){return d};b(0,2);b(new x(),4,new x())"); // Recursive calls are OK. test("var b=function(c,d){b(1,2);return d};b(3,4);b(5,6)", "var b=function(d){b(2);return d};b(4);b(6)"); testSame("var b=function(c){return arguments};b(1,2);b(3,4)"); // remove all function arguments test("var b=function(c,d){return};b(1,2)", "var b=function(){return};b()"); // remove no function arguments testSame("var b=function(c,d){return c+d};b(1,2)"); // remove some function arguments test("var b=function(e,f,c,d){return c+d};b(1,2)", "var b=function(c,d){return c+d};b()"); test("var b=function(c,d,e,f){return c+d};b(1,2)", "var b=function(c,d){return c+d};b(1,2)"); test("var b=function(e,c,f,d,g){return c+d};b(1,2)", "var b=function(c,d){return c+d};b(2)"); // multiple definitions of "b", the parameters can be removed but // the call sites are left unmodified for now. test("var b=function(c,d){};var b=function(e,f){};b(1,2)", "var b=function(){};var b=function(){};b(1,2)"); } public void testCallSiteInteraction_contructors() { this.modifyCallSites = true; // The third level tests that the functions which have already been looked // at get re-visited if they are changed by a call site removal. test("var Ctor1=function(a,b){return a};" + "var Ctor2=function(a,b){Ctor1.call(this,a,b)};" + "goog$inherits(Ctor2, Ctor1);" + "new Ctor2(1,2)", "var Ctor1=function(a){return a};" + "var Ctor2=function(a){Ctor1.call(this,a)};" + "goog$inherits(Ctor2, Ctor1);" + "new Ctor2(1)"); } public void testFunctionArgRemovalCausingInconsistency() { this.modifyCallSites = true; // Test the case where an unused argument is removed and the argument // contains a call site in its subtree (will cause the call site's parent // pointer to be null). test("var a=function(x,y){};" + "var b=function(z){};" + "a(new b, b)", "var a=function(){};" + "var b=function(){};" + "a(new b)"); } public void testRemoveUnusedVarsPossibleNpeCase() { this.modifyCallSites = true; test("var a = [];" + "var register = function(callback) {a[0] = callback};" + "register(function(transformer) {});" + "register(function(transformer) {});", "var register=function(){};register();register()"); } public void testDoNotOptimizeJSCompiler_renameProperty() { this.modifyCallSites = true; // Only the function definition can be modified, none of the call sites. test("function JSCompiler_renameProperty(a) {};" + "JSCompiler_renameProperty('a');", "function JSCompiler_renameProperty() {};" + "JSCompiler_renameProperty('a');"); } public void testDoNotOptimizeJSCompiler_ObjectPropertyString() { this.modifyCallSites = true; test("function JSCompiler_ObjectPropertyString(a, b) {};" + "JSCompiler_ObjectPropertyString(window,'b');", "function JSCompiler_ObjectPropertyString() {};" + "JSCompiler_ObjectPropertyString(window,'b');"); } public void testDoNotOptimizeSetters() { testSame("({set s(a) {}})"); } public void testRemoveSingletonClass1() { test("function goog$addSingletonGetter(a){}" + "/**@constructor*/function a(){}" + "goog$addSingletonGetter(a);", ""); } public void testRemoveInheritedClass1() { test("function goog$inherits(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$inherits(b,a); new a", "function a(){} new a"); } public void testRemoveInheritedClass2() { test("function goog$inherits(){}" + "function goog$mixin(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "/**@constructor*/function c(){}" + "goog$inherits(b,a);" + "goog$mixin(c.prototype,b.prototype);", ""); } public void testRemoveInheritedClass3() { testSame("/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$inherits(b,a); new b"); } public void testRemoveInheritedClass4() { testSame("function goog$inherits(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$inherits(b,a);" + "/**@constructor*/function c(){}" + "goog$inherits(c,b); new c"); } public void testRemoveInheritedClass5() { test("function goog$inherits(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$inherits(b,a);" + "/**@constructor*/function c(){}" + "goog$inherits(c,b); new b", "function goog$inherits(){}" + "function a(){}" + "function b(){}" + "goog$inherits(b,a); new b"); } public void testRemoveInheritedClass6() { test("function goog$mixin(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "/**@constructor*/function c(){}" + "/**@constructor*/function d(){}" + "goog$mixin(b.prototype,a.prototype);" + "goog$mixin(c.prototype,a.prototype); new c;" + "goog$mixin(d.prototype,a.prototype)", "function goog$mixin(){}" + "function a(){}" + "function c(){}" + "goog$mixin(c.prototype,a.prototype); new c"); } public void testRemoveInheritedClass7() { test("function goog$mixin(){}" + "/**@constructor*/function a(){alert(goog$mixin(a, a))}" + "/**@constructor*/function b(){}" + "goog$mixin(b.prototype,a.prototype); new a", "function goog$mixin(){}" + "function a(){alert(goog$mixin(a, a))} new a"); } public void testRemoveInheritedClass8() { test("/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "/**@constructor*/function c(){}" + "b.inherits(a);c.mixin(b.prototype)", ""); } public void testRemoveInheritedClass9() { testSame("/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "/**@constructor*/function c(){}" + "b.inherits(a);c.mixin(b.prototype);new c"); } public void testRemoveInheritedClass10() { test("function goog$inherits(){}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$inherits(b,a); new a;" + "var c = a; var d = a.g; new b", "function goog$inherits(){}" + "function a(){} function b(){} goog$inherits(b,a); new a; new b"); } public void testRemoveInheritedClass11() { testSame("function goog$inherits(){}" + "function goog$mixin(a,b){goog$inherits(a,b)}" + "/**@constructor*/function a(){}" + "/**@constructor*/function b(){}" + "goog$mixin(b.prototype,a.prototype);new b"); } public void testRemoveInheritedClass12() { testSame("function goog$inherits(){}" + "/**@constructor*/function a(){}" + "var b = {};" + "goog$inherits(b.foo, a)"); } public void testReflectedMethods() { this.modifyCallSites = true; testSame( "/** @constructor */" + "function Foo() {}" + "Foo.prototype.handle = function(x, y) { alert(y); };" + "var x = goog.reflect.object(Foo, {handle: 1});" + "for (var i in x) { x[i].call(x); }" + "window['Foo'] = Foo;"); } public void testIssue618_1() { this.removeGlobal = false; testSame( "function f() {\n" + " var a = [], b;\n" + " a.push(b = []);\n" + " b[0] = 1;\n" + " return a;\n" + "}"); } public void testIssue618_2() { this.removeGlobal = false; testSame( "var b;\n" + "a.push(b = []);\n" + "b[0] = 1;\n"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NameAnalyzerTest.java0000644000175000017500000015510712115204405027126 0ustar apoapo/* * Copyright 2006 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.rhino.Node; /** * Tests for {@link NameAnalyzer} * */ public class NameAnalyzerTest extends CompilerTestCase { private static String kExterns = "var window, top;" + "var document;" + "var Function;" + "var Array;" + "var externfoo; methods.externfoo;"; public NameAnalyzerTest() { super(kExterns); } @Override protected void setUp() { super.enableNormalize(); super.enableLineNumberCheck(true); } @Override protected int getNumRepetitions() { // pass reaches steady state after 1 iteration. return 1; } public void testRemoveVarDeclaration1() { test("var foo = 3;", ""); } public void testRemoveVarDeclaration2() { test("var foo = 3, bar = 4; externfoo = foo;", "var foo = 3; externfoo = foo;"); } public void testRemoveVarDeclaration3() { test("var a = f(), b = 1, c = 2; b; c", "f();var b = 1, c = 2; b; c"); } public void testRemoveVarDeclaration4() { test("var a = 0, b = f(), c = 2; a; c", "var a = 0;f();var c = 2; a; c"); } public void testRemoveVarDeclaration5() { test("var a = 0, b = 1, c = f(); a; b", "var a = 0, b = 1; f(); a; b"); } public void testRemoveVarDeclaration6() { test("var a = 0, b = a = 1; a", "var a = 0; a = 1; a"); } public void testRemoveVarDeclaration7() { test("var a = 0, b = a = 1", ""); } public void testRemoveVarDeclaration8() { test("var a;var b = 0, c = a = b = 1", ""); } public void testRemoveDeclaration1() { test("var a;var b = 0, c = a = b = 1", ""); } public void testRemoveDeclaration2() { test("var a,b,c; c = a = b = 1", ""); } public void testRemoveDeclaration3() { test("var a,b,c; c = a = b = {}; a.x = 1;", ""); } public void testRemoveDeclaration4() { testSame("var a,b,c; c = a = b = {}; a.x = 1;alert(c.x);"); } public void testRemoveDeclaration5() { test("var a,b,c; c = a = b = null; use(b)", "var b;b=null;use(b)"); } public void testRemoveDeclaration6() { test("var a,b,c; c = a = b = 'str';use(b)", "var b;b='str';use(b)"); } public void testRemoveDeclaration7() { test("var a,b,c; c = a = b = true;use(b)", "var b;b=true;use(b)"); } public void testRemoveFunction1() { test("var foo = function(){};", ""); } public void testRemoveFunction2() { test("var foo; foo = function(){};", ""); } public void testRemoveFunction3() { test("var foo = {}; foo.bar = function() {};", ""); } public void testRemoveFunction4() { test("var a = {}; a.b = {}; a.b.c = function() {};", ""); } public void testReferredToByWindow() { testSame("var foo = {}; foo.bar = function() {}; window['fooz'] = foo.bar"); } public void testExtern() { testSame("externfoo = 5"); } public void testRemoveNamedFunction() { test("function foo(){}", ""); } public void testRemoveRecursiveFunction1() { test("function f(){f()}", ""); } public void testRemoveRecursiveFunction2() { test("var f = function (){f()}", ""); } public void testRemoveRecursiveFunction2a() { test("var f = function g(){g()}", ""); } public void testRemoveRecursiveFunction3() { test("var f;f = function (){f()}", ""); } public void testRemoveRecursiveFunction4() { // don't removed if name definition doesn't exist. testSame("f = function (){f()}"); } public void testRemoveRecursiveFunction5() { test("function g(){f()}function f(){g()}", ""); } public void testRemoveRecursiveFunction6() { test("var f=function(){g()};function g(){f()}", ""); } public void testRemoveRecursiveFunction7() { test("var g = function(){f()};var f = function(){g()}", ""); } public void testRemoveRecursiveFunction8() { test("var o = {};o.f = function(){o.f()}", ""); } public void testRemoveRecursiveFunction9() { testSame("var o = {};o.f = function(){o.f()};o.f()"); } public void testSideEffectClassification1() { test("foo();", "foo();"); } public void testSideEffectClassification2() { test("var a = foo();", "foo();"); } public void testSideEffectClassification3() { testSame("var a = foo();window['b']=a;"); } public void testSideEffectClassification4() { testSame("function sef(){} sef();"); } public void testSideEffectClassification5() { testSame("function nsef(){} var a = nsef();window['b']=a;"); } public void testSideEffectClassification6() { test("function sef(){} sef();", "function sef(){} sef();"); } public void testSideEffectClassification7() { testSame("function sef(){} var a = sef();window['b']=a;"); } public void testNoSideEffectAnnotation1() { test("function f(){} var a = f();", "function f(){} f()"); } public void testNoSideEffectAnnotation2() { test("/**@nosideeffects*/function f(){}", "var a = f();", "", null, null); } public void testNoSideEffectAnnotation3() { test("var f = function(){}; var a = f();", "var f = function(){}; f();"); } public void testNoSideEffectAnnotation4() { test("var f = /**@nosideeffects*/function(){};", "var a = f();", "", null, null); } public void testNoSideEffectAnnotation5() { test("var f; f = function(){}; var a = f();", "var f; f = function(){}; f();"); } public void testNoSideEffectAnnotation6() { test("var f; f = /**@nosideeffects*/function(){};", "var a = f();", "", null, null); } public void testNoSideEffectAnnotation7() { test("var f;" + "f = /**@nosideeffects*/function(){};", "f = function(){};" + "var a = f();", "f = function(){}; f();", null, null); } public void testNoSideEffectAnnotation8() { test("var f;" + "f = function(){};" + "f = /**@nosideeffects*/function(){};", "var a = f();", "f();", null, null); } public void testNoSideEffectAnnotation9() { test("var f;" + "f = /**@nosideeffects*/function(){};" + "f = /**@nosideeffects*/function(){};", "var a = f();", "", null, null); test("var f; f = /**@nosideeffects*/function(){};", "var a = f();", "", null, null); } public void testNoSideEffectAnnotation10() { test("var o = {}; o.f = function(){}; var a = o.f();", "var o = {}; o.f = function(){}; o.f();"); } public void testNoSideEffectAnnotation11() { test("var o = {}; o.f = /**@nosideeffects*/function(){};", "var a = o.f();", "", null, null); } public void testNoSideEffectAnnotation12() { test("function c(){} var a = new c", "function c(){} new c"); } public void testNoSideEffectAnnotation13() { test("/**@nosideeffects*/function c(){}", "var a = new c", "", null, null); } public void testNoSideEffectAnnotation14() { String common = "function c(){};" + "c.prototype.f = /**@nosideeffects*/function(){};"; test(common, "var o = new c; var a = o.f()", "new c", null, null); } public void testNoSideEffectAnnotation15() { test("function c(){}; c.prototype.f = function(){}; var a = (new c).f()", "function c(){}; c.prototype.f = function(){}; (new c).f()"); } public void testNoSideEffectAnnotation16() { test("/**@nosideeffects*/function c(){}" + "c.prototype.f = /**@nosideeffects*/function(){};", "var a = (new c).f()", "", null, null); } public void testFunctionPrototype() { testSame("var a = 5; Function.prototype.foo = function() {return a;}"); } public void testTopLevelClass1() { test("var Point = function() {}; Point.prototype.foo = function() {}", ""); } public void testTopLevelClass2() { testSame("var Point = {}; Point.prototype.foo = function() {};" + "externfoo = new Point()"); } public void testTopLevelClass3() { test("function Point() {this.me_ = Point}", ""); } public void testTopLevelClass4() { test("function f(){} function A(){} A.prototype = {x: function() {}}; f();", "function f(){} f();"); } public void testTopLevelClass5() { testSame("function f(){} function A(){}" + "A.prototype = {x: function() { f(); }}; new A();"); } public void testTopLevelClass6() { testSame("function f(){} function A(){}" + "A.prototype = {x: function() { f(); }}; new A().x();"); } public void testTopLevelClass7() { test("A.prototype.foo = function(){}; function A() {}", ""); } public void testNamespacedClass1() { test("var foo = {};foo.bar = {};foo.bar.prototype.baz = {}", ""); } public void testNamespacedClass2() { testSame("var foo = {};foo.bar = {};foo.bar.prototype.baz = {};" + "window.z = new foo.bar()"); } public void testNamespacedClass3() { test("var a = {}; a.b = function() {}; a.b.prototype = {x: function() {}};", ""); } public void testNamespacedClass4() { testSame("function f(){} var a = {}; a.b = function() {};" + "a.b.prototype = {x: function() { f(); }}; new a.b();"); } public void testNamespacedClass5() { testSame("function f(){} var a = {}; a.b = function() {};" + "a.b.prototype = {x: function() { f(); }}; new a.b().x();"); } public void testAssignmentToThisPrototype() { testSame("Function.prototype.inherits = function(parentCtor) {" + " function tempCtor() {};" + " tempCtor.prototype = parentCtor.prototype;" + " this.superClass_ = parentCtor.prototype;" + " this.prototype = new tempCtor();" + " this.prototype.constructor = this;" + "};"); } public void testAssignmentToCallResultPrototype() { testSame("function f() { return function(){}; } f().prototype = {};"); } public void testAssignmentToExternPrototype() { testSame("externfoo.prototype = {};"); } public void testAssignmentToUnknownPrototype() { testSame( "/** @suppress {duplicate} */ var window;" + "window['a'].prototype = {};"); } public void testBug2099540() { testSame( "/** @suppress {duplicate} */ var document;\n" + "/** @suppress {duplicate} */ var window;\n" + "var klass;\n" + "window[klass].prototype = " + "document.createElement(tagName)['__proto__'];"); } public void testOtherGlobal() { testSame("goog.global.foo = bar(); function bar(){}"); } public void testExternName1() { testSame("top.z = bar(); function bar(){}"); } public void testExternName2() { testSame("top['z'] = bar(); function bar(){}"); } public void testInherits1() { test("var a = {}; var b = {}; b.inherits(a)", ""); } public void testInherits2() { test("var a = {}; var b = {}; var goog = {}; goog.inherits(b, a)", ""); } public void testInherits3() { testSame("var a = {}; this.b = {}; b.inherits(a);"); } public void testInherits4() { testSame("var a = {}; this.b = {}; var goog = {}; goog.inherits(b, a);"); } public void testInherits5() { test("this.a = {}; var b = {}; b.inherits(a);", "this.a = {}"); } public void testInherits6() { test("this.a = {}; var b = {}; var goog = {}; goog.inherits(b, a);", "this.a = {}"); } public void testInherits7() { testSame("var a = {}; this.b = {}; var goog = {};" + " goog.inherits = function() {}; goog.inherits(b, a);"); } public void testInherits8() { // Make sure that exceptions aren't thrown if inherits() is used as // an R-value test("this.a = {}; var b = {}; var c = b.inherits(a);", "this.a = {};"); } public void testMixin1() { testSame("var goog = {}; goog.mixin = function() {};" + "Function.prototype.mixin = function(base) {" + " goog.mixin(this.prototype, base); " + "};"); } public void testMixin2() { testSame("var a = {}; this.b = {}; var goog = {};" + " goog.mixin = function() {}; goog.mixin(b.prototype, a.prototype);"); } public void testMixin3() { test("this.a = {}; var b = {}; var goog = {};" + " goog.mixin = function() {}; goog.mixin(b.prototype, a.prototype);", "this.a = {};"); } public void testMixin4() { testSame("this.a = {}; var b = {}; var goog = {};" + "goog.mixin = function() {};" + "goog.mixin(b.prototype, a.prototype);" + "new b()"); } public void testMixin5() { test("this.a = {}; var b = {}; var c = {}; var goog = {};" + "goog.mixin = function() {};" + "goog.mixin(b.prototype, a.prototype);" + "goog.mixin(c.prototype, a.prototype);" + "new b()", "this.a = {}; var b = {}; var goog = {};" + "goog.mixin = function() {};" + "goog.mixin(b.prototype, a.prototype);" + "new b()"); } public void testMixin6() { testSame("this.a = {}; var b = {}; var c = {}; var goog = {};" + "goog.mixin = function() {};" + "goog.mixin(c.prototype, a.prototype) + " + "goog.mixin(b.prototype, a.prototype);" + "new b()"); } public void testMixin7() { test("this.a = {}; var b = {}; var c = {}; var goog = {};" + "goog.mixin = function() {};" + "var d = goog.mixin(c.prototype, a.prototype) + " + "goog.mixin(b.prototype, a.prototype);" + "new b()", "this.a = {}; var b = {}; var goog = {};" + "goog.mixin = function() {};" + "goog.mixin(b.prototype, a.prototype);" + "new b()"); } public void testConstants1() { testSame("var bar = function(){}; var EXP_FOO = true; if (EXP_FOO) bar();"); } public void testConstants2() { test("var bar = function(){}; var EXP_FOO = true; var EXP_BAR = true;" + "if (EXP_FOO) bar();", "var bar = function(){}; var EXP_FOO = true; if (EXP_FOO) bar();"); } public void testExpressions1() { test("var foo={}; foo.A='A'; foo.AB=foo.A+'B'; foo.ABC=foo.AB+'C'", ""); } public void testExpressions2() { testSame("var foo={}; foo.A='A'; foo.AB=foo.A+'B'; this.ABC=foo.AB+'C'"); } public void testExpressions3() { testSame("var foo = 2; window.bar(foo + 3)"); } public void testSetCreatingReference() { testSame("var foo; var bar = function(){foo=6;}; bar();"); } public void testAnonymous1() { testSame("function foo() {}; function bar() {}; foo(function() {bar()})"); } public void testAnonymous2() { test("var foo;(function(){foo=6;})()", "(function(){})()"); } public void testAnonymous3() { testSame("var foo; (function(){ if(!foo)foo=6; })()"); } public void testAnonymous4() { testSame("var foo; (function(){ foo=6; })(); externfoo=foo;"); } public void testAnonymous5() { testSame("var foo;" + "(function(){ foo=function(){ bar() }; function bar(){} })();" + "foo();"); } public void testAnonymous6() { testSame("function foo(){}" + "function bar(){}" + "foo(function(){externfoo = bar});"); } public void testAnonymous7() { testSame("var foo;" + "(function (){ function bar(){ externfoo = foo; } bar(); })();"); } public void testAnonymous8() { testSame("var foo;" + "(function (){ var g=function(){ externfoo = foo; }; g(); })();"); } public void testAnonymous9() { testSame("function foo(){}" + "function bar(){}" + "foo(function(){ function baz(){ externfoo = bar; } baz(); });"); } public void testFunctions1() { testSame("var foo = null; function baz() {}" + "function bar() {foo=baz();} bar();"); } public void testFunctions2() { testSame("var foo; foo = function() {var a = bar()};" + "var bar = function(){}; foo();"); } public void testGetElem1() { testSame("var foo = {}; foo.bar = {}; foo.bar.baz = {a: 5, b: 10};" + "var fn = function() {window[foo.bar.baz.a] = 5;}; fn()"); } public void testGetElem2() { testSame("var foo = {}; foo.bar = {}; foo.bar.baz = {a: 5, b: 10};" + "var fn = function() {this[foo.bar.baz.a] = 5;}; fn()"); } public void testGetElem3() { testSame("var foo = {'i': 0, 'j': 1}; foo['k'] = 2; top.foo = foo;"); } public void testIf1() { test("var foo = {};if(e)foo.bar=function(){};", "if(e);"); } public void testIf2() { test("var e = false;var foo = {};if(e)foo.bar=function(){};", "var e = false;if(e);"); } public void testIf3() { test("var e = false;var foo = {};if(e + 1)foo.bar=function(){};", "var e = false;if(e + 1);"); } public void testIf4() { test("var e = false, f;var foo = {};if(f=e)foo.bar=function(){};", "var e = false;if(e);"); } public void testIf4a() { // TODO(johnlenz): fix this. testSame("var e = [], f;if(f=e);f[0] = 1;"); } public void testIf4b() { // TODO(johnlenz): fix this. test("var e = [], f;if(e=f);f[0] = 1;", "var f;if(f);f[0] = 1;"); } public void testIf4c() { test("var e = [], f;if(f=e);e[0] = 1;", "var e = [];if(e);e[0] = 1;"); } public void testIf5() { test("var e = false, f;var foo = {};if(f = e + 1)foo.bar=function(){};", "var e = false;if(e + 1);"); } public void testIfElse() { test("var foo = {};if(e)foo.bar=function(){};else foo.bar=function(){};", "if(e);else;"); } public void testWhile() { test("var foo = {};while(e)foo.bar=function(){};", "while(e);"); } public void testFor() { test("var foo = {};for(e in x)foo.bar=function(){};", "for(e in x);"); } public void testDo() { test("var cond = false;do {var a = 1} while (cond)", "var cond = false;do {} while (cond)"); } public void testSetterInForStruct1() { test("var j = 0; for (var i = 1; i = 0; j++);", "var j = 0; for (; 0; j++);"); } public void testSetterInForStruct2() { test("var Class = function() {}; " + "for (var i = 1; Class.prototype.property_ = 0; i++);", "for (var i = 1; 0; i++);"); } public void testSetterInForStruct3() { test("var j = 0; for (var i = 1 + f() + g() + h(); i = 0; j++);", "var j = 0; f(); g(); h(); for (; 0; j++);"); } public void testSetterInForStruct4() { test("var i = 0;var j = 0; for (i = 1 + f() + g() + h(); i = 0; j++);", "var j = 0; f(); g(); h(); for (; 0; j++);"); } public void testSetterInForStruct5() { test("var i = 0, j = 0; for (i = f(), j = g(); 0;);", "for (f(), g(); 0;);"); } public void testSetterInForStruct6() { test("var i = 0, j = 0, k = 0; for (i = f(), j = g(), k = h(); i = 0;);", "for (f(), g(), h(); 0;);"); } public void testSetterInForStruct7() { test("var i = 0, j = 0, k = 0; for (i = 1, j = 2, k = 3; i = 0;);", "for (1, 2, 3; 0;);"); } public void testSetterInForStruct8() { test("var i = 0, j = 0, k = 0; for (i = 1, j = i, k = 2; i = 0;);", "var i = 0; for(i = 1, i , 2; i = 0;);"); } public void testSetterInForStruct9() { test("var Class = function() {}; " + "for (var i = 1; Class.property_ = 0; i++);", "for (var i = 1; 0; i++);"); } public void testSetterInForStruct10() { test("var Class = function() {}; " + "for (var i = 1; Class.property_ = 0; i = 2);", "for (; 0;);"); } public void testSetterInForStruct11() { test("var Class = function() {}; " + "for (;Class.property_ = 0;);", "for (;0;);"); } public void testSetterInForStruct12() { test("var a = 1; var Class = function() {}; " + "for (;Class.property_ = a;);", "var a = 1; for (; a;);"); } public void testSetterInForStruct13() { test("var a = 1; var Class = function() {}; " + "for (Class.property_ = a; 0 ;);", "for (; 0;);"); } public void testSetterInForStruct14() { test("var a = 1; var Class = function() {}; " + "for (; 0; Class.property_ = a);", "for (; 0;);"); } public void testSetterInForStruct15() { test("var Class = function() {}; " + "for (var i = 1; 0; Class.prototype.property_ = 0);", "for (; 0; 0);"); } public void testSetterInForStruct16() { test("var Class = function() {}; " + "for (var i = 1; i = 0; Class.prototype.property_ = 0);", "for (; 0; 0);"); } public void testSetterInForIn1() { test("var foo = {}; var bar; for(e in bar = foo.a);", "var foo = {}; for(e in foo.a);"); } public void testSetterInForIn2() { testSame("var foo = {}; var bar; for(e in bar = foo.a); bar"); } public void testSetterInForIn3() { testSame("var foo = {}; var bar; for(e in bar = foo.a); bar.b = 3"); } public void testSetterInForIn4() { testSame("var foo = {}; var bar; for (e in bar = foo.a); bar.b = 3; foo.a"); } public void testSetterInForIn5() { // TODO(user) Fix issue similar to b/2316773: bar should be preserved // but isn't due to missing references between e and foo.a test("var foo = {}; var bar; for (e in foo.a) { bar = e } bar.b = 3; foo.a", "var foo={};for(e in foo.a);foo.a"); } public void testSetterInForIn6() { testSame("var foo = {};for(e in foo);"); } public void testSetterInIfPredicate() { // TODO(user) Make NameAnalyzer smarter so it can remove "Class". testSame("var a = 1;" + "var Class = function() {}; " + "if (Class.property_ = a);"); } public void testSetterInWhilePredicate() { test("var a = 1;" + "var Class = function() {}; " + "while (Class.property_ = a);", "var a = 1; for (;a;) {}"); } public void testSetterInDoWhilePredicate() { // TODO(user) Make NameAnalyzer smarter so it can remove "Class". testSame("var a = 1;" + "var Class = function() {}; " + "do {} while(Class.property_ = a);"); } public void testSetterInSwitchInput() { // TODO(user) Make NameAnalyzer smarter so it can remove "Class". testSame("var a = 1;" + "var Class = function() {}; " + "switch (Class.property_ = a) {" + " default:" + "}"); } public void testComplexAssigns() { // Complex assigns are not removed by the current pass. testSame("var x = 0; x += 3; x *= 5;"); } public void testNestedAssigns1() { test("var x = 0; var y = x = 3; window.alert(y);", "var y = 3; window.alert(y);"); } public void testNestedAssigns2() { testSame("var x = 0; var y = x = {}; x.b = 3; window.alert(y);"); } public void testComplexNestedAssigns1() { // TODO(nicksantos): Make NameAnalyzer smarter, so that we can eliminate y. testSame("var x = 0; var y = 2; y += x = 3; window.alert(x);"); } public void testComplexNestedAssigns2() { test("var x = 0; var y = 2; y += x = 3; window.alert(y);", "var y = 2; y += 3; window.alert(y);"); } public void testComplexNestedAssigns3() { test("var x = 0; var y = x += 3; window.alert(x);", "var x = 0; x += 3; window.alert(x);"); } public void testComplexNestedAssigns4() { testSame("var x = 0; var y = x += 3; window.alert(y);"); } public void testUnintendedUseOfInheritsInLocalScope1() { testSame("goog.mixin = function() {}; " + "(function() { var x = {}; var y = {}; goog.mixin(x, y); })();"); } public void testUnintendedUseOfInheritsInLocalScope2() { testSame("goog.mixin = function() {}; " + "var x = {}; var y = {}; (function() { goog.mixin(x, y); })();"); } public void testUnintendedUseOfInheritsInLocalScope3() { testSame("goog.mixin = function() {}; " + "var x = {}; var y = {}; (function() { goog.mixin(x, y); })(); " + "window.alert(x);"); } public void testUnintendedUseOfInheritsInLocalScope4() { // Ensures that the "goog$mixin" variable doesn't get stripped out, // even when it's only used in a local scope. testSame("var goog$mixin = function() {}; " + "(function() { var x = {}; var y = {}; goog$mixin(x, y); })();"); } public void testPrototypePropertySetInLocalScope1() { testSame("(function() { var x = function(){}; x.prototype.bar = 3; })();"); } public void testPrototypePropertySetInLocalScope2() { testSame("var x = function(){}; (function() { x.prototype.bar = 3; })();"); } public void testPrototypePropertySetInLocalScope3() { test("var x = function(){ x.prototype.bar = 3; };", ""); } public void testPrototypePropertySetInLocalScope4() { test("var x = {}; x.foo = function(){ x.foo.prototype.bar = 3; };", ""); } public void testPrototypePropertySetInLocalScope5() { test("var x = {}; x.prototype.foo = 3;", ""); } public void testPrototypePropertySetInLocalScope6() { testSame("var x = {}; x.prototype.foo = 3; bar(x.prototype.foo)"); } public void testPrototypePropertySetInLocalScope7() { testSame("var x = {}; x.foo = 3; bar(x.foo)"); } public void testRValueReference1() { testSame("var a = 1; a"); } public void testRValueReference2() { testSame("var a = 1; 1+a"); } public void testRValueReference3() { testSame("var x = {}; x.prototype.foo = 3; var a = x.prototype.foo; 1+a"); } public void testRValueReference4() { testSame("var x = {}; x.prototype.foo = 3; x.prototype.foo"); } public void testRValueReference5() { testSame("var x = {}; x.prototype.foo = 3; 1+x.prototype.foo"); } public void testRValueReference6() { testSame("var x = {}; var idx = 2; x[idx]"); } public void testUnhandledTopNode() { testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + "function Bar() {}; Bar.prototype.isFoo = function() {};" + "var foo = new Foo(); var bar = new Bar();" + // The boolean AND here is currently unhandled by this pass, but // it should not cause it to blow up. "var cond = foo.isBar() && bar.isFoo();" + "if (cond) {window.alert('hello');}"); } public void testPropertyDefinedInGlobalScope() { testSame("function Foo() {}; var x = new Foo(); x.cssClass = 'bar';" + "window.alert(x);"); } public void testConditionallyDefinedFunction1() { testSame("var g; externfoo.x || (externfoo.x = function() { g; })"); } public void testConditionallyDefinedFunction2() { testSame("var g; 1 || (externfoo.x = function() { g; })"); } public void testConditionallyDefinedFunction3() { testSame("var a = {};" + "rand() % 2 || (a.f = function() { externfoo = 1; } || alert());"); } public void testGetElemOnThis() { testSame("var a = 3; this['foo'] = a;"); testSame("this['foo'] = 3;"); } public void testRemoveInstanceOfOnly() { test("function Foo() {}; Foo.prototype.isBar = function() {};" + "var x; if (x instanceof Foo) { window.alert(x); }", ";var x; if (false) { window.alert(x); }"); } public void testRemoveLocalScopedInstanceOfOnly() { test("function Foo() {}; function Bar(x) { this.z = x instanceof Foo; };" + "externfoo.x = new Bar({});", ";function Bar(x) { this.z = false }; externfoo.x = new Bar({});"); } public void testRemoveInstanceOfWithReferencedMethod() { test("function Foo() {}; Foo.prototype.isBar = function() {};" + "var x; if (x instanceof Foo) { window.alert(x.isBar()); }", ";var x; if (false) { window.alert(x.isBar()); }"); } public void testDoNotChangeReferencedInstanceOf() { testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + "var x = new Foo(); if (x instanceof Foo) { window.alert(x); }"); } public void testDoNotChangeReferencedLocalScopedInstanceOf() { testSame("function Foo() {}; externfoo.x = new Foo();" + "function Bar() { if (x instanceof Foo) { window.alert(x); } };" + "externfoo.y = new Bar();"); } public void testDoNotChangeLocalScopeReferencedInstanceOf() { testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + "function Bar() { this.z = new Foo(); }; externfoo.x = new Bar();" + "if (x instanceof Foo) { window.alert(x); }"); } public void testDoNotChangeLocalScopeReferencedLocalScopedInstanceOf() { testSame("function Foo() {}; Foo.prototype.isBar = function() {};" + "function Bar() { this.z = new Foo(); };" + "Bar.prototype.func = function(x) {" + "if (x instanceof Foo) { window.alert(x); }" + "}; new Bar().func();"); } public void testDoNotChangeLocalScopeReferencedLocalScopedInstanceOf2() { test( "function Foo() {}" + "var createAxis = function(f) { return window.passThru(f); };" + "var axis = createAxis(function(test) {" + " return test instanceof Foo;" + "});", "var createAxis = function(f) { return window.passThru(f); };" + "createAxis(function(test) {" + " return false;" + "});"); } public void testDoNotChangeInstanceOfGetElem() { testSame("var goog = {};" + "function f(obj, name) {" + " if (obj instanceof goog[name]) {" + " return name;" + " }" + "}" + "window['f'] = f;"); } public void testWeirdnessOnLeftSideOfPrototype() { // This checks a bug where 'x' was removed, but the function referencing // it was not, causing problems. testSame("var x = 3; " + "(function() { this.bar = 3; }).z = function() {" + " return x;" + "};"); } public void testShortCircuit1() { test("var a = b() || 1", "b()"); } public void testShortCircuit2() { test("var a = 1 || c()", "1 || c()"); } public void testShortCircuit3() { test("var a = b() || c()", "b() || c()"); } public void testShortCircuit4() { test("var a = b() || 3 || c()", "b() || 3 || c()"); } public void testShortCircuit5() { test("var a = b() && 1", "b()"); } public void testShortCircuit6() { test("var a = 1 && c()", "1 && c()"); } public void testShortCircuit7() { test("var a = b() && c()", "b() && c()"); } public void testShortCircuit8() { test("var a = b() && 3 && c()", "b() && 3 && c()"); } public void testRhsReference1() { testSame("var a = 1; a"); } public void testRhsReference2() { testSame("var a = 1; a || b()"); } public void testRhsReference3() { testSame("var a = 1; 1 || a"); } public void testRhsReference4() { test("var a = 1; var b = a || foo()", "var a = 1; a || foo()"); } public void testRhsReference5() { test("var a = 1, b = 5; a; foo(b)", "var a = 1, b = 5; a; foo(b)"); } public void testRhsAssign1() { test("var foo, bar; foo || (bar = 1)", "var foo; foo || 1"); } public void testRhsAssign2() { test("var foo, bar, baz; foo || (baz = bar = 1)", "var foo; foo || 1"); } public void testRhsAssign3() { testSame("var foo = null; foo || (foo = 1)"); } public void testRhsAssign4() { test("var foo = null; foo = (foo || 1)", ""); } public void testRhsAssign5() { test("var a = 3, foo, bar; foo || (bar = a)", "var a = 3, foo; foo || a"); } public void testRhsAssign6() { test("function Foo(){} var foo = null;" + "var f = function () {foo || (foo = new Foo()); return foo}", ""); } public void testRhsAssign7() { testSame("function Foo(){} var foo = null;" + "var f = function () {foo || (foo = new Foo())}; f()"); } public void testRhsAssign8() { testSame("function Foo(){} var foo = null;" + "var f = function () {(foo = new Foo()) || g()}; f()"); } public void testRhsAssign9() { test("function Foo(){} var foo = null;" + "var f = function () {1 + (foo = new Foo()); return foo}", ""); } public void testAssignWithOr1() { testSame("var foo = null;" + "var f = window.a || function () {return foo}; f()"); } public void testAssignWithOr2() { test("var foo = null;" + "var f = window.a || function () {return foo};", "var foo = null"); // why is this left? } public void testAssignWithAnd1() { testSame("var foo = null;" + "var f = window.a && function () {return foo}; f()"); } public void testAssignWithAnd2() { test("var foo = null;" + "var f = window.a && function () {return foo};", "var foo = null;"); // why is this left? } public void testAssignWithHook1() { testSame("function Foo(){} var foo = null;" + "var f = window.a ? " + " function () {return new Foo()} : function () {return foo}; f()"); } public void testAssignWithHook2() { test("function Foo(){} var foo = null;" + "var f = window.a ? " + " function () {return new Foo()} : function () {return foo};", ""); } public void testAssignWithHook2a() { test("function Foo(){} var foo = null;" + "var f; f = window.a ? " + " function () {return new Foo()} : function () {return foo};", ""); } public void testAssignWithHook3() { testSame("function Foo(){} var foo = null; var f = {};" + "f.b = window.a ? " + " function () {return new Foo()} : function () {return foo}; f.b()"); } public void testAssignWithHook4() { test("function Foo(){} var foo = null; var f = {};" + "f.b = window.a ? " + " function () {return new Foo()} : function () {return foo};", ""); } public void testAssignWithHook5() { testSame("function Foo(){} var foo = null; var f = {};" + "f.b = window.a ? function () {return new Foo()} :" + " window.b ? function () {return foo} :" + " function() { return Foo }; f.b()"); } public void testAssignWithHook6() { test("function Foo(){} var foo = null; var f = {};" + "f.b = window.a ? function () {return new Foo()} :" + " window.b ? function () {return foo} :" + " function() { return Foo };", ""); } public void testAssignWithHook7() { testSame("function Foo(){} var foo = null;" + "var f = window.a ? new Foo() : foo;" + "f()"); } public void testAssignWithHook8() { test("function Foo(){} var foo = null;" + "var f = window.a ? new Foo() : foo;", "function Foo(){}" + "window.a && new Foo()"); } public void testAssignWithHook9() { test("function Foo(){} var foo = null; var f = {};" + "f.b = window.a ? new Foo() : foo;", "function Foo(){} window.a && new Foo()"); } public void testAssign1() { test("function Foo(){} var foo = null; var f = {};" + "f.b = window.a;", ""); } public void testAssign2() { test("function Foo(){} var foo = null; var f = {};" + "f.b = window;", ""); } public void testAssign3() { test("var f = {};" + "f.b = window;", ""); } public void testAssign4() { test("function Foo(){} var foo = null; var f = {};" + "f.b = new Foo();", "function Foo(){} new Foo()"); } public void testAssign5() { test("function Foo(){} var foo = null; var f = {};" + "f.b = foo;", ""); } public void testNestedAssign1() { test("var a, b = a = 1, c = 2", ""); } public void testNestedAssign2() { test("var a, b = a = 1; foo(b)", "var b = 1; foo(b)"); } public void testNestedAssign3() { test("var a, b = a = 1; a = b = 2; foo(b)", "var b = 1; b = 2; foo(b)"); } public void testNestedAssign4() { test("var a, b = a = 1; b = a = 2; foo(b)", "var b = 1; b = 2; foo(b)"); } public void testNestedAssign5() { test("var a, b = a = 1; b = a = 2", ""); } public void testNestedAssign15() { test("var a, b, c; c = b = a = 2", ""); } public void testNestedAssign6() { testSame("var a, b, c; a = b = c = 1; foo(a, b, c)"); } public void testNestedAssign7() { testSame("var a = 0; a = i[j] = 1; b(a, i[j])"); } public void testNestedAssign8() { testSame("function f(){" + "this.lockedToken_ = this.lastToken_ = " + "SETPROP_value(this.hiddenInput_, a)}f()"); } public void testRefChain1() { test("var a = 1; var b = a; var c = b; var d = c", ""); } public void testRefChain2() { test("var a = 1; var b = a; var c = b; var d = c || f()", "var a = 1; var b = a; var c = b; c || f()"); } public void testRefChain3() { test("var a = 1; var b = a; var c = b; var d = c + f()", "f()"); } public void testRefChain4() { test("var a = 1; var b = a; var c = b; var d = f() || c", "f()"); } public void testRefChain5() { test("var a = 1; var b = a; var c = b; var d = f() ? g() : c", "f() && g()"); } public void testRefChain6() { test("var a = 1; var b = a; var c = b; var d = c ? f() : g()", "var a = 1; var b = a; var c = b; c ? f() : g()"); } public void testRefChain7() { test("var a = 1; var b = a; var c = b; var d = (b + f()) ? g() : c", "var a = 1; var b = a; (b+f()) && g()"); } public void testRefChain8() { test("var a = 1; var b = a; var c = b; var d = f()[b] ? g() : 0", "var a = 1; var b = a; f()[b] && g()"); } public void testRefChain9() { test("var a = 1; var b = a; var c = 5; var d = f()[b+c] ? g() : 0", "var a = 1; var b = a; var c = 5; f()[b+c] && g()"); } public void testRefChain10() { test("var a = 1; var b = a; var c = b; var d = f()[b] ? g() : 0", "var a = 1; var b = a; f()[b] && g()"); } public void testRefChain11() { test("var a = 1; var b = a; var d = f()[b] ? g() : 0", "var a = 1; var b = a; f()[b] && g()"); } public void testRefChain12() { testSame("var a = 1; var b = a; f()[b] ? g() : 0"); } public void testRefChain13() { test("function f(){}var a = 1; var b = a; var d = f()[b] ? g() : 0", "function f(){}var a = 1; var b = a; f()[b] && g()"); } public void testRefChain14() { testSame("function f(){}var a = 1; var b = a; f()[b] ? g() : 0"); } public void testRefChain15() { test("function f(){}var a = 1, b = a; var c = f(); var d = c[b] ? g() : 0", "function f(){}var a = 1, b = a; var c = f(); c[b] && g()"); } public void testRefChain16() { testSame("function f(){}var a = 1; var b = a; var c = f(); c[b] ? g() : 0"); } public void testRefChain17() { test("function f(){}var a = 1; var b = a; var c = f(); var d = c[b]", "function f(){} f()"); } public void testRefChain18() { testSame("var a = 1; f()[a] && g()"); } public void testRefChain19() { test("var a = 1; var b = [a]; var c = b; b[f()] ? g() : 0", "var a=1; var b=[a]; b[f()] ? g() : 0"); } public void testRefChain20() { test("var a = 1; var b = [a]; var c = b; var d = b[f()] ? g() : 0", "var a=1; var b=[a]; b[f()]&&g()"); } public void testRefChain21() { testSame("var a = 1; var b = 2; var c = a + b; f(c)"); } public void testRefChain22() { test("var a = 2; var b = a = 4; f(a)", "var a = 2; a = 4; f(a)"); } public void testRefChain23() { test("var a = {}; var b = a[1] || f()", "var a = {}; a[1] || f()"); } /** * Expressions that cannot be attributed to any enclosing dependency * scope should be treated as global references. * @bug 1739062 */ public void testAssignmentWithComplexLhs() { testSame("function f() { return this; }" + "var o = {'key': 'val'};" + "f().x_ = o['key'];"); } public void testAssignmentWithComplexLhs2() { testSame("function f() { return this; }" + "var o = {'key': 'val'};" + "f().foo = function() {" + " o" + "};"); } public void testAssignmentWithComplexLhs3() { String source = "var o = {'key': 'val'};" + "function init_() {" + " this.x = o['key']" + "}"; test(source, ""); testSame(source + ";init_()"); } public void testAssignmentWithComplexLhs4() { testSame("function f() { return this; }" + "var o = {'key': 'val'};" + "f().foo = function() {" + " this.x = o['key']" + "};"); } /** * Do not "prototype" property of variables that are not being * tracked (because they are local). * @bug 1809442 */ public void testNoRemovePrototypeDefinitionsOutsideGlobalScope1() { testSame("function f(arg){}" + "" + "(function(){" + " var O = {};" + " O.prototype = 'foo';" + " f(O);" + "})()"); } public void testNoRemovePrototypeDefinitionsOutsideGlobalScope2() { testSame("function f(arg){}" + "(function h(){" + " var L = {};" + " L.prototype = 'foo';" + " f(L);" + "})()"); } public void testNoRemovePrototypeDefinitionsOutsideGlobalScope4() { testSame("function f(arg){}" + "function g(){" + " var N = {};" + " N.prototype = 'foo';" + " f(N);" + "}" + "g()"); } public void testNoRemovePrototypeDefinitionsOutsideGlobalScope5() { // function body not removed due to @bug 1898561 testSame("function g(){ var R = {}; R.prototype = 'foo' } g()"); } public void testRemovePrototypeDefinitionsInGlobalScope1() { testSame("function f(arg){}" + "var M = {};" + "M.prototype = 'foo';" + "f(M);"); } public void testRemovePrototypeDefinitionsInGlobalScope2() { test("var Q = {}; Q.prototype = 'foo'", ""); } public void testRemoveLabeledStatment() { test("LBL: var x = 1;", "LBL: {}"); } public void testRemoveLabeledStatment2() { test("var x; LBL: x = f() + g()", "LBL: { f() ; g()}"); } public void testRemoveLabeledStatment3() { test("var x; LBL: x = 1;", "LBL: {}"); } public void testRemoveLabeledStatment4() { test("var a; LBL: a = f()", "LBL: f()"); } public void testPreservePropertyMutationsToAlias1() { // Test for issue b/2316773 - property get case // Since a is referenced, property mutations via a's alias b must // be preserved. testSame("var a = {}; var b = a; b.x = 1; a"); } public void testPreservePropertyMutationsToAlias2() { // Test for issue b/2316773 - property get case, don't keep 'c' test("var a = {}; var b = a; var c = a; b.x = 1; a", "var a = {}; var b = a; b.x = 1; a"); } public void testPreservePropertyMutationsToAlias3() { // Test for issue b/2316773 - property get case, chain testSame("var a = {}; var b = a; var c = b; c.x = 1; a"); } public void testPreservePropertyMutationsToAlias4() { // Test for issue b/2316773 - element get case testSame("var a = {}; var b = a; b['x'] = 1; a"); } public void testPreservePropertyMutationsToAlias5() { // From issue b/2316773 description testSame("function testCall(o){}" + "var DATA = {'prop': 'foo','attr': {}};" + "var SUBDATA = DATA['attr'];" + "SUBDATA['subprop'] = 'bar';" + "testCall(DATA);"); } public void testPreservePropertyMutationsToAlias6() { // Longer GETELEM chain testSame("function testCall(o){}" + "var DATA = {'prop': 'foo','attr': {}};" + "var SUBDATA = DATA['attr'];" + "var SUBSUBDATA = SUBDATA['subprop'];" + "SUBSUBDATA['subsubprop'] = 'bar';" + "testCall(DATA);"); } public void testPreservePropertyMutationsToAlias7() { // Make sure that the base class does not depend on the derived class. test("var a = {}; var b = {}; b.x = 0;" + "var goog = {}; goog.inherits(b, a); a", "var a = {}; a"); } public void testPreservePropertyMutationsToAlias8() { // Make sure that the derived classes don't end up depending on each other. test("var a = {};" + "var b = {}; b.x = 0;" + "var c = {}; c.y = 0;" + "var goog = {}; goog.inherits(b, a); goog.inherits(c, a); c", "var a = {}; var c = {}; c.y = 0;" + "var goog = {}; goog.inherits(c, a); c"); } public void testPreservePropertyMutationsToAlias9() { testSame("var a = {b: {}};" + "var c = a.b; c.d = 3;" + "a.d = 3; a.d;"); } public void testRemoveAlias() { test("var a = {b: {}};" + "var c = a.b;" + "a.d = 3; a.d;", "var a = {b: {}}; a.d = 3; a.d;"); } public void testSingletonGetter1() { test("function Foo() {} goog.addSingletonGetter(Foo);", ""); } public void testSingletonGetter2() { test("function Foo() {} goog$addSingletonGetter(Foo);", ""); } public void testSingletonGetter3() { // addSingletonGetter adds a getInstance method to a class. testSame("function Foo() {} goog$addSingletonGetter(Foo);" + "this.x = Foo.getInstance();"); } public void testNoRemoveWindowPropertyAlias1() { testSame( "var self_ = window.gbar;\n" + "self_.qs = function() {};"); } public void testNoRemoveWindowPropertyAlias2() { testSame( "var self_ = window;\n" + "self_.qs = function() {};"); } public void testNoRemoveWindowPropertyAlias3() { testSame( "var self_ = window;\n" + "self_['qs'] = function() {};"); } public void testNoRemoveWindowPropertyAlias4() { // TODO(johnlenz): fix this. "self_" should remain. test( "var self_ = window['gbar'] || {};\n" + "self_.qs = function() {};", ""); } public void testNoRemoveWindowPropertyAlias4a() { // TODO(johnlenz): fix this. "self_" should remain. test( "var self_; self_ = window.gbar || {};\n" + "self_.qs = function() {};", ""); } public void testNoRemoveWindowPropertyAlias5() { // TODO(johnlenz): fix this. "self_" should remain. test( "var self_ = window || {};\n" + "self_['qs'] = function() {};", ""); } public void testNoRemoveWindowPropertyAlias5a() { // TODO(johnlenz): fix this. test( "var self_; self_ = window || {};\n" + "self_['qs'] = function() {};", ""); } public void testNoRemoveWindowPropertyAlias6() { testSame( "var self_ = (window.gbar = window.gbar || {});\n" + "self_.qs = function() {};"); } public void testNoRemoveWindowPropertyAlias6a() { testSame( "var self_; self_ = (window.gbar = window.gbar || {});\n" + "self_.qs = function() {};"); } public void testNoRemoveWindowPropertyAlias7() { testSame( "var self_ = (window = window || {});\n" + "self_['qs'] = function() {};"); } public void testNoRemoveWindowPropertyAlias7a() { testSame( "var self_; self_ = (window = window || {});\n" + "self_['qs'] = function() {};"); } public void testNoRemoveAlias0() { testSame( "var x = {}; function f() { return x; }; " + "f().style.display = 'block';" + "alert(x.style)"); } public void testNoRemoveAlias1() { testSame( "var x = {}; function f() { return x; };" + "var map = f();\n" + "map.style.display = 'block';" + "alert(x.style)"); } public void testNoRemoveAlias2() { testSame( "var x = {};" + "var map = (function () { return x; })();\n" + "map.style = 'block';" + "alert(x.style)"); } public void testNoRemoveAlias3() { testSame( "var x = {}; function f() { return x; };" + "var map = {}\n" + "map[1] = f();\n" + "map[1].style.display = 'block';"); } public void testNoRemoveAliasOfExternal0() { testSame( "document.getElementById('foo').style.display = 'block';"); } public void testNoRemoveAliasOfExternal1() { testSame( "var map = document.getElementById('foo');\n" + "map.style.display = 'block';"); } public void testNoRemoveAliasOfExternal2() { testSame( "var map = {}\n" + "map[1] = document.getElementById('foo');\n" + "map[1].style.display = 'block';"); } public void testNoRemoveThrowReference1() { testSame( "var e = {}\n" + "throw e;"); } public void testNoRemoveThrowReference2() { testSame( "function e() {}\n" + "throw new e();"); } public void testClassDefinedInObjectLit1() { test( "var data = {Foo: function() {}};" + "data.Foo.prototype.toString = function() {};", ""); } public void testClassDefinedInObjectLit2() { test( "var data = {}; data.bar = {Foo: function() {}};" + "data.bar.Foo.prototype.toString = function() {};", ""); } public void testClassDefinedInObjectLit3() { test( "var data = {bar: {Foo: function() {}}};" + "data.bar.Foo.prototype.toString = function() {};", ""); } public void testClassDefinedInObjectLit4() { test( "var data = {};" + "data.baz = {bar: {Foo: function() {}}};" + "data.baz.bar.Foo.prototype.toString = function() {};", ""); } public void testVarReferencedInClassDefinedInObjectLit1() { testSame( "var ref = 3;" + "var data = {Foo: function() { this.x = ref; }};" + "window.Foo = data.Foo;"); } public void testVarReferencedInClassDefinedInObjectLit2() { testSame( "var ref = 3;" + "var data = {Foo: function() { this.x = ref; }," + " Bar: function() {}};" + "window.Bar = data.Bar;"); } public void testArrayExt() { testSame( "Array.prototype.foo = function() { return 1 };" + "var y = [];" + "switch (y.foo()) {" + "}"); } public void testArrayAliasExt() { testSame( "Array$X = Array;" + "Array$X.prototype.foo = function() { return 1 };" + "function Array$X() {}" + "var y = [];" + "switch (y.foo()) {" + "}"); } public void testExternalAliasInstanceof1() { test( "Array$X = Array;" + "function Array$X() {}" + "var y = [];" + "if (y instanceof Array) {}", "var y = [];" + "if (y instanceof Array) {}" ); } public void testExternalAliasInstanceof2() { testSame( "Array$X = Array;" + "function Array$X() {}" + "var y = [];" + "if (y instanceof Array$X) {}"); } public void testExternalAliasInstanceof3() { testSame( "var b = Array;" + "var y = [];" + "if (y instanceof b) {}"); } public void testAliasInstanceof4() { testSame( "function Foo() {};" + "var b = Foo;" + "var y = new Foo();" + "if (y instanceof b) {}"); } public void testAliasInstanceof5() { // TODO(johnlenz): fix this. "b" should remain. test( "function Foo() {}" + "function Bar() {}" + "var b = x ? Foo : Bar;" + "var y = new Foo();" + "if (y instanceof b) {}", "function Foo() {}" + "var y = new Foo;" + "if (false){}"); } // We cannot leave x.a.prototype there because it will // fail sanity var check. public void testBrokenNamespaceWithPrototypeAssignment() { test("var x = {}; x.a.prototype = 1", ""); } public void testRemovePrototypeAliases() { test( "function g() {}" + "function F() {} F.prototype.bar = g;" + "window.g = g;", "function g() {}" + "window.g = g;"); } public void testIssue284() { test( "var goog = {};" + "goog.inherits = function(x, y) {};" + "var ns = {};" + "/** @constructor */" + "ns.PageSelectionModel = function() {};" + "/** @constructor */" + "ns.PageSelectionModel.FooEvent = function() {};" + "/** @constructor */" + "ns.PageSelectionModel.SelectEvent = function() {};" + "goog.inherits(ns.PageSelectionModel.ChangeEvent," + " ns.PageSelectionModel.FooEvent);", ""); } public void testIssue838a() { testSame("var z = window['z'] || (window['z'] = {});\n" + "z['hello'] = 'Hello';\n" + "z['world'] = 'World';"); } public void testIssue838b() { testSame( "var z;" + "window['z'] = z || (z = {});\n" + "z['hello'] = 'Hello';\n" + "z['world'] = 'World';"); } public void testIssue874a() { testSame( "var a = a || {};\n" + "var b = a;\n" + "b.View = b.View || {}\n" + "var c = b.View;\n" + "c.Editor = function f(d, e) {\n" + " return d + e\n" + "};\n" + "window.ImageEditor.View.Editor = a.View.Editor;"); } public void testIssue874b() { testSame( "var b;\n" + "var c = b = {};\n" + "c.Editor = function f(d, e) {\n" + " return d + e\n" + "};\n" + "window['Editor'] = b.Editor;"); } public void testIssue874c() { testSame( "var b, c;\n" + "c = b = {};\n" + "c.Editor = function f(d, e) {\n" + " return d + e\n" + "};\n" + "window['Editor'] = b.Editor;"); } public void testIssue874d() { testSame( "var b = {}, c;\n" + "c = b;\n" + "c.Editor = function f(d, e) {\n" + " return d + e\n" + "};\n" + "window['Editor'] = b.Editor;"); } public void testIssue874e() { testSame( "var a;\n" + "var b = a || (a = {});\n" + "var c = b.View || (b.View = {});\n" + "c.Editor = function f(d, e) {\n" + " return d + e\n" + "};\n" + "window.ImageEditor.View.Editor = a.View.Editor;"); } public void testBug6575051() { testSame( "var hackhack = window['__o_o_o__'] = window['__o_o_o__'] || {};\n" + "window['__o_o_o__']['va'] = 1;\n" + "hackhack['Vb'] = 1;"); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new MarkNoSideEffectCallsAndNameAnalyzerRunner(compiler); } private class MarkNoSideEffectCallsAndNameAnalyzerRunner implements CompilerPass { MarkNoSideEffectCalls markNoSideEffectCalls; NameAnalyzer analyzer; MarkNoSideEffectCallsAndNameAnalyzerRunner(Compiler compiler) { this.markNoSideEffectCalls = new MarkNoSideEffectCalls(compiler); this.analyzer = new NameAnalyzer(compiler, true); } @Override public void process(Node externs, Node root) { markNoSideEffectCalls.process(externs, root); analyzer.process(externs, root); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FunctionInjectorTest.java0000644000175000017500000015465512115204405030032 0ustar apoapo/* * 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.common.collect.Sets; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.FunctionInjector.CanInlineResult; import com.google.javascript.jscomp.FunctionInjector.InliningMode; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.List; import java.util.Set; /** * Inline function tests. * @author johnlenz@google.com (John Lenz) */ public class FunctionInjectorTest extends TestCase { static final InliningMode INLINE_DIRECT = InliningMode.DIRECT; static final InliningMode INLINE_BLOCK = InliningMode.BLOCK; private boolean assumeStrictThis = false; private boolean assumeMinimumCapture = false; @Override protected void setUp() throws Exception { super.setUp(); assumeStrictThis = false; } private FunctionInjector getInjector() { Compiler compiler = new Compiler(); return new FunctionInjector( compiler, compiler.getUniqueNameIdSupplier(), true, assumeStrictThis, assumeMinimumCapture); } public void testIsSimpleFunction1() { assertTrue(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){}"))); } public void testIsSimpleFunction2() { assertTrue(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){return 0;}"))); } public void testIsSimpleFunction3() { assertTrue(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){return x ? 0 : 1}"))); } public void testIsSimpleFunction4() { assertFalse(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){return;}"))); } public void testIsSimpleFunction5() { assertFalse(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){return 0; return 0;}"))); } public void testIsSimpleFunction6() { assertFalse(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){var x=true;return x ? 0 : 1}"))); } public void testIsSimpleFunction7() { assertFalse(getInjector().isDirectCallNodeReplacementPossible( prep("function f(){if (x) return 0; else return 1}"))); } public void testCanInlineReferenceToFunction1() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction2() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction3() { // NOTE: FoldConstants will convert this to a empty function, // so there is no need to explicitly support it. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return;}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction4() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return;}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction5() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction6() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction7() { // In var initialization. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; var x=foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction8() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; var x=foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction9() { // In assignment. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; var x; x=foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction10() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; var x; x=foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction11() { // In expression. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; var x; x=x+foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction12() { // "foo" is not known to be side-effect free, it might change the value // of "x", so it can't be inlined. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return true;}; var x; x=x+foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction12b() { // "foo" is not known to be side-effect free, it might change the value // of "x", so it can't be inlined. helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(){return true;}; var x; x=x+foo();", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testCanInlineReferenceToFunction13() { // // ... if foo is side-effect free we can inline here. // helperCanInlineReferenceToFunction(true, // "/** @nosideeffects */ function foo(){return true;};" + // "var x; x=x+foo();", "foo", INLINE_BLOCK); // } public void testCanInlineReferenceToFunction14() { // Simple call with parameters helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; foo(x);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction15() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; foo(x);", "foo", INLINE_BLOCK); } // TODO(johnlenz): remove this constant once this has been proven in // production code. static final CanInlineResult NEW_VARS_IN_GLOBAL_SCOPE = CanInlineResult.YES; public void testCanInlineReferenceToFunction16() { // Function "foo" as it contains "var b" which // must be brought into the global scope. helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, "function foo(a){var b;return a;}; foo(goo());", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction17() { // This doesn't bring names into the global name space. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return a;}; " + "function x() { foo(goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction18() { // Parameter has side-effects. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a;} foo(x++);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction19() { // Parameter has mutable parameter referenced more than once. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a+a} foo([]);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction20() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a+a} foo({});", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction21() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a+a} foo(new Date);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction22() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a+a} foo(true && new Date);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction23() { // variables to global scope. helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, "function foo(a){return a;}; foo(x++);", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction24() { // ... this is OK, because it doesn't introduce a new global name. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return a;}; " + "function x() { foo(x++); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction25() { // Parameter has side-effects. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return a+a;}; foo(x++);", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction26() { helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, "function foo(a){return a+a;}; foo(x++);", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction27() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return a+a;}; " + "function x() { foo(x++); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction28() { // Parameter has side-effects. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; foo(goo());", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction29() { helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE, "function foo(a){return true;}; foo(goo());", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction30() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo(goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction31() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a) {return true;}; " + "function x() {foo.call(this, 1);}", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction32() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.apply(this, [1]); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction33() { // No special handling is required for method calls passing this. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.bar(this, 1); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction34() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.call(this, goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction35() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.apply(this, goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction36() { helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.bar(this, goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction37() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(null, 1); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction38() { assumeStrictThis = false; helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(null, goo()); }", "foo", INLINE_BLOCK); assumeStrictThis = true; helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.call(null, goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction39() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(bar, 1); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction40() { assumeStrictThis = false; helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(bar, goo()); }", "foo", INLINE_BLOCK); assumeStrictThis = true; helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.call(bar, goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction41() { helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(new bar(), 1); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction42() { assumeStrictThis = false; helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() { foo.call(new bar(), goo()); }", "foo", INLINE_BLOCK); assumeStrictThis = true; helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(a){return true;}; " + "function x() { foo.call(new bar(), goo()); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction43() { // Handle the case of a missing 'this' value in a call. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return true;}; " + "function x() { foo.call(); }", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction44() { assumeStrictThis = false; // Handle the case of a missing 'this' value in a call. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return true;}; " + "function x() { foo.call(); }", "foo", INLINE_BLOCK); assumeStrictThis = true; // Handle the case of a missing 'this' value in a call. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return true;}; " + "function x() { foo.call(); }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction45() { // Call with inner function expression. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return function() {return true;}}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction46() { // Call with inner function expression. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return function() {return true;}}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction47() { // Call with inner function expression and variable decl. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){var a; return function() {return true;}}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction48() { // Call with inner function expression and variable decl. // TODO(johnlenz): should we validate no values in scope? helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){var a; return function() {return true;}}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction49() { // Call with inner function expression. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return function() {var a; return true;}}; foo();", "foo", INLINE_DIRECT); } public void testCanInlineReferenceToFunction50() { // Call with inner function expression. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){return function() {var a; return true;}}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunction51() { // Call with inner function statement. helperCanInlineReferenceToFunction(CanInlineResult.YES, "function foo(){function x() {var a; return true;} return x}; foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression1() { // Call in if condition helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() { if (foo(1)) throw 'test'; }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression2() { // Call in return expression helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() { return foo(1); }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression3() { // Call in switch expression helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() { switch(foo(1)) { default:break; } }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression4() { // Call in hook condition helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {foo(1)?0:1 }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression5() { // Call in hook side-effect free condition helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() {true?foo(1):1 }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression5a() { // Call in hook side-effect free condition helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {true?foo(1):1 }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression6() { // Call in expression statement "condition" helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {foo(1) && 1 }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression7() { // Call in expression statement after side-effect free "condition" helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() {1 && foo(1) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression7a() { // Call in expression statement after side-effect free "condition" helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {1 && foo(1) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression8() { // Call in expression statement after side-effect free operator helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {1 + foo(1) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression9() { // Call in VAR expression. helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {var b = 1 + foo(1)}", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression10() { // Call in assignment expression. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(a){return true;}; " + "function x() {var b; b += 1 + foo(1) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression10a() { // Call in assignment expression. helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {var b; b += 1 + foo(1) }", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testCanInlineReferenceToFunctionInExpression11() { // helperCanInlineReferenceToFunction(true, // "/** @nosideeffects */ function foo(a){return true;}; " + // "function x() {var b; b += 1 + foo(1) }", // "foo", INLINE_BLOCK); // } public void testCanInlineReferenceToFunctionInExpression12() { helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {var a,b,c; a = b = c = foo(1) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression13() { helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(a){return true;}; " + "function x() {var a,b,c; a = b = c = 1 + foo(1) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression14() { // ... foo can not be inlined because of possible changes to "c". helperCanInlineReferenceToFunction(CanInlineResult.NO, "var a = {}, b = {}, c;" + "a.test = 'a';" + "b.test = 'b';" + "c = a;" + "function foo(){c = b; return 'foo'};" + "c.test=foo();", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression14a() { // ... foo can be inlined despite possible changes to "c". helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "var a = {}, b = {}, c;" + "a.test = 'a';" + "b.test = 'b';" + "c = a;" + "function foo(){c = b; return 'foo'};" + "c.test=foo();", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testCanInlineReferenceToFunctionInExpression15() { // // ... foo can be inlined as it is side-effect free. // helperCanInlineReferenceToFunction(true, // "var a = {}, b = {}, c;" + // "a.test = 'a';" + // "b.test = 'b';" + // "c = a;" + // "/** @nosideeffects */ function foo(){return 'foo'};" + // "c.test=foo();", // "foo", INLINE_BLOCK); // } // public void testCanInlineReferenceToFunctionInExpression16() { // // ... foo can not be inlined because of possible side-effects of x() // helperCanInlineReferenceToFunction(false, // "var a = {}, b = {}, c;" + // "a.test = 'a';" + // "b.test = 'b';" + // "c = a;" + // "function x(){return c};" + // "/** @nosideeffects */ function foo(){return 'foo'};" + // "x().test=foo();", // "foo", INLINE_BLOCK); // } // public void testCanInlineReferenceToFunctionInExpression17() { // // ... foo can be inlined because of x() is side-effect free. // helperCanInlineReferenceToFunction(true, // "var a = {}, b = {}, c;" + // "a.test = 'a';" + // "b.test = 'b';" + // "c = a;" + // "/** @nosideeffects */ function x(){return c};" + // "/** @nosideeffects */ function foo(){return 'foo'};" + // "x().test=foo();", // "foo", INLINE_BLOCK); // } public void testCanInlineReferenceToFunctionInExpression18() { // Call in within a call helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION, "function foo(){return _g();}; " + "function x() {1 + foo()() }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression19() { // ... unless foo is known to be side-effect free, it might actually // change the value of "_g" which would unfortunately change the behavior, // so we can't inline here. helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return a;}; " + "function x() {1 + _g(foo()) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression19a() { // ... unless foo is known to be side-effect free, it might actually // change the value of "_g" which would unfortunately change the behavior, // so we can't inline here. helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(){return a;}; " + "function x() {1 + _g(foo()) }", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testCanInlineReferenceToFunctionInExpression20() { // helperCanInlineReferenceToFunction(true, // "/** @nosideeffects */ function foo(){return a;}; " + // "function x() {1 + _g(foo()) }", // "foo", INLINE_BLOCK); // } public void testCanInlineReferenceToFunctionInExpression21() { // Assignments to object are problematic if the call has side-effects, // as the object that is being referred to can change. // Note: This could be changed be inlined if we in some way make "z" // as not escaping from the local scope. helperCanInlineReferenceToFunction(CanInlineResult.NO, "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z.gack = foo(1) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression21a() { // Assignments to object are problematic if the call has side-effects, // as the object that is being referred to can change. // Note: This could be changed be inlined if we in some way make "z" // as not escaping from the local scope. helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z.gack = foo(1) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression22() { // ... foo() is after a side-effect helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return a;}; " + "function x() {1 + _g(_a(), foo()) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression22a() { // ... foo() is after a side-effect helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(){return a;}; " + "function x() {1 + _g(_a(), foo()) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInExpression23() { // ... foo() is after a side-effect helperCanInlineReferenceToFunction(CanInlineResult.NO, "function foo(){return a;}; " + "function x() {1 + _g(_a(), foo.call(this)) }", "foo", INLINE_BLOCK); } public void testCanInlineReferenceToFunctionInExpression23a() { // ... foo() is after a side-effect helperCanInlineReferenceToFunction( CanInlineResult.AFTER_PREPARATION, "function foo(){return a;}; " + "function x() {1 + _g(_a(), foo.call(this)) }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInLoop1() { helperCanInlineReferenceToFunction( CanInlineResult.YES, "function foo(){return a;}; " + "while(1) { foo(); }", "foo", INLINE_BLOCK, true); } public void testCanInlineReferenceToFunctionInLoop2() { // If function contains function, don't inline it into a loop. // TODO(johnlenz): this can be improved by looking to see // if the inner function contains any references to values defined // in the outer function. helperCanInlineReferenceToFunction( CanInlineResult.NO, "function foo(){return function() {};}; " + "while(1) { foo(); }", "foo", INLINE_BLOCK, true); } public void testInline1() { helperInlineReferenceToFunction( "function foo(){}; foo();", "function foo(){}; void 0", "foo", INLINE_DIRECT); } public void testInline2() { helperInlineReferenceToFunction( "function foo(){}; foo();", "function foo(){}; {}", "foo", INLINE_BLOCK); } public void testInline3() { helperInlineReferenceToFunction( "function foo(){return;}; foo();", "function foo(){return;}; {}", "foo", INLINE_BLOCK); } public void testInline4() { helperInlineReferenceToFunction( "function foo(){return true;}; foo();", "function foo(){return true;}; true;", "foo", INLINE_DIRECT); } public void testInline5() { helperInlineReferenceToFunction( "function foo(){return true;}; foo();", "function foo(){return true;}; {true;}", "foo", INLINE_BLOCK); } public void testInline6() { // In var initialization. helperInlineReferenceToFunction( "function foo(){return true;}; var x=foo();", "function foo(){return true;}; var x=true;", "foo", INLINE_DIRECT); } public void testInline7() { helperInlineReferenceToFunction( "function foo(){return true;}; var x=foo();", "function foo(){return true;}; var x;" + "{x=true}", "foo", INLINE_BLOCK); } public void testInline8() { // In assignment. helperInlineReferenceToFunction( "function foo(){return true;}; var x; x=foo();", "function foo(){return true;}; var x; x=true;", "foo", INLINE_DIRECT); } public void testInline9() { helperInlineReferenceToFunction( "function foo(){return true;}; var x; x=foo();", "function foo(){return true;}; var x;{x=true}", "foo", INLINE_BLOCK); } public void testInline10() { // In expression. helperInlineReferenceToFunction( "function foo(){return true;}; var x; x=x+foo();", "function foo(){return true;}; var x; x=x+true;", "foo", INLINE_DIRECT); } public void testInline11() { // Simple call with parameters helperInlineReferenceToFunction( "function foo(a){return true;}; foo(x);", "function foo(a){return true;}; true;", "foo", INLINE_DIRECT); } public void testInline12() { helperInlineReferenceToFunction( "function foo(a){return true;}; foo(x);", "function foo(a){return true;}; {true}", "foo", INLINE_BLOCK); } public void testInline13() { // Parameter has side-effects. helperInlineReferenceToFunction( "function foo(a){return a;}; " + "function x() { foo(x++); }", "function foo(a){return a;}; " + "function x() {{var a$$inline_0=x++;" + "a$$inline_0}}", "foo", INLINE_BLOCK); } public void testInline14() { // Parameter has side-effects. helperInlineReferenceToFunction( "function foo(a){return a+a;}; foo(x++);", "function foo(a){return a+a;}; " + "{var a$$inline_0=x++;" + " a$$inline_0+" + "a$$inline_0;}", "foo", INLINE_BLOCK); } public void testInline15() { // Parameter has mutable, references more than once. helperInlineReferenceToFunction( "function foo(a){return a+a;}; foo(new Date());", "function foo(a){return a+a;}; " + "{var a$$inline_0=new Date();" + " a$$inline_0+" + "a$$inline_0;}", "foo", INLINE_BLOCK); } public void testInline16() { // Parameter is large, references more than once. helperInlineReferenceToFunction( "function foo(a){return a+a;}; foo(function(){});", "function foo(a){return a+a;}; " + "{var a$$inline_0=function(){};" + " a$$inline_0+" + "a$$inline_0;}", "foo", INLINE_BLOCK); } public void testInline17() { // Parameter has side-effects. helperInlineReferenceToFunction( "function foo(a){return true;}; foo(goo());", "function foo(a){return true;};" + "{var a$$inline_0=goo();true}", "foo", INLINE_BLOCK); } public void testInline18() { // This doesn't bring names into the global name space. helperInlineReferenceToFunction( "function foo(a){var b;return a;}; " + "function x() { foo(goo()); }", "function foo(a){var b;return a;}; " + "function x() {{var a$$inline_0=goo();" + "var b$$inline_1;a$$inline_0}}", "foo", INLINE_BLOCK); } public void testInline19() { // Properly alias. helperInlineReferenceToFunction( "var x = 1; var y = 2;" + "function foo(a,b){x = b; y = a;}; " + "function bar() { foo(x,y); }", "var x = 1; var y = 2;" + "function foo(a,b){x = b; y = a;}; " + "function bar() {" + "{var a$$inline_0=x;" + "x = y;" + "y = a$$inline_0;}" + "}", "foo", INLINE_BLOCK); } public void testInline19b() { helperInlineReferenceToFunction( "var x = 1; var y = 2;" + "function foo(a,b){y = a; x = b;}; " + "function bar() { foo(x,y); }", "var x = 1; var y = 2;" + "function foo(a,b){y = a; x = b;}; " + "function bar() {" + "{var b$$inline_1=y;" + "y = x;" + "x = b$$inline_1;}" + "}", "foo", INLINE_BLOCK); } public void testInlineIntoLoop() { helperInlineReferenceToFunction( "function foo(a){var b;return a;}; " + "for(;1;){ foo(1); }", "function foo(a){var b;return a;}; " + "for(;1;){ {" + "var b$$inline_1=void 0;1}}", "foo", INLINE_BLOCK); helperInlineReferenceToFunction( "function foo(a){var b;return a;}; " + "do{ foo(1); } while(1)", "function foo(a){var b;return a;}; " + "do{ {" + "var b$$inline_1=void 0;1}}while(1)", "foo", INLINE_BLOCK); helperInlineReferenceToFunction( "function foo(a){for(var b in c)return a;}; " + "for(;1;){ foo(1); }", "function foo(a){var b;for(b in c)return a;}; " + "for(;1;){ {JSCompiler_inline_label_foo_2:{" + "var b$$inline_1=void 0;for(b$$inline_1 in c){" + "1;break JSCompiler_inline_label_foo_2" + "}}}}", "foo", INLINE_BLOCK); } public void testInlineFunctionWithInnerFunction1() { // Call with inner function expression. helperInlineReferenceToFunction( "function foo(){return function() {return true;}}; foo();", "function foo(){return function() {return true;}};" + "(function() {return true;})", "foo", INLINE_DIRECT); } public void testInlineFunctionWithInnerFunction2() { // Call with inner function expression. helperInlineReferenceToFunction( "function foo(){return function() {return true;}}; foo();", "function foo(){return function() {return true;}};" + "{(function() {return true;})}", "foo", INLINE_BLOCK); } public void testInlineFunctionWithInnerFunction3() { // Call with inner function expression. helperInlineReferenceToFunction( "function foo(){return function() {var a; return true;}}; foo();", "function foo(){return function() {var a; return true;}};" + "(function() {var a; return true;});", "foo", INLINE_DIRECT); } public void testInlineFunctionWithInnerFunction4() { // Call with inner function expression. helperInlineReferenceToFunction( "function foo(){return function() {var a; return true;}}; foo();", "function foo(){return function() {var a; return true;}};" + "{(function() {var a$$inline_0; return true;});}", "foo", INLINE_BLOCK); } public void testInlineFunctionWithInnerFunction5() { // Call with inner function statement. helperInlineReferenceToFunction( "function foo(){function x() {var a; return true;} return x}; foo();", "function foo(){function x(){var a;return true}return x};" + "{var x$$inline_0 = function(){" + "var a$$inline_1;return true};x$$inline_0}", "foo", INLINE_BLOCK); } public void testInlineReferenceInExpression1() { // Call in if condition helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() { if (foo(1)) throw 'test'; }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "if (JSCompiler_inline_result$$0) throw 'test'; }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression2() { // Call in return expression helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() { return foo(1); }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "return JSCompiler_inline_result$$0; }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression3() { // Call in switch expression helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() { switch(foo(1)) { default:break; } }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "switch(JSCompiler_inline_result$$0) { default:break; } }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression4() { // Call in hook condition helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {foo(1)?0:1 }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "JSCompiler_inline_result$$0?0:1 }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression5() { // Call in expression statement "condition" helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {foo(1)&&1 }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "JSCompiler_inline_result$$0&&1 }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression6() { // Call in expression statement after side-effect free "condition" helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {1 + foo(1) }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "1 + JSCompiler_inline_result$$0 }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression7() { // Call in expression statement "condition" helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {foo(1) && 1 }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0; " + "{JSCompiler_inline_result$$0=true;}" + "JSCompiler_inline_result$$0&&1 }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression8() { // Call in expression statement after side-effect free operator helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {1 + foo(1) }", "function foo(a){return true;}; " + "function x() { var JSCompiler_inline_result$$0;" + "{JSCompiler_inline_result$$0=true;}" + "1 + JSCompiler_inline_result$$0 }", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression9() { // Call in VAR expression. helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {var b = 1 + foo(1)}", "function foo(a){return true;}; " + "function x() { " + "var JSCompiler_inline_result$$0;" + "{JSCompiler_inline_result$$0=true;}" + "var b = 1 + JSCompiler_inline_result$$0 " + "}", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testInlineReferenceInExpression10() { // // Call in assignment expression. // helperInlineReferenceToFunction( // "/** @nosideeffects */ function foo(a){return true;}; " + // "function x() {var b; b += 1 + foo(1) }", // "function foo(a){return true;}; " + // "function x() {var b;" + // "{var JSCompiler_inline_result$$0; " + // "JSCompiler_inline_result$$0=true;}" + // "b += 1 + JSCompiler_inline_result$$0 }", // "foo", INLINE_BLOCK); // } public void testInlineReferenceInExpression11() { // Call under label helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() {a:foo(1)?0:1 }", "function foo(a){return true;}; " + "function x() {" + " a:{" + " var JSCompiler_inline_result$$0; " + " {JSCompiler_inline_result$$0=true;}" + " JSCompiler_inline_result$$0?0:1 " + " }" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression12() { helperInlineReferenceToFunction( "function foo(a){return true;}" + "function x() { 1?foo(1):1; }", "function foo(a){return true}" + "function x() {" + " if(1) {" + " {true;}" + " } else {" + " 1;" + " }" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression13() { helperInlineReferenceToFunction( "function foo(a){return true;}; " + "function x() { goo() + (1?foo(1):1) }", "function foo(a){return true;}; " + "function x() { var JSCompiler_temp_const$$0=goo();" + "var JSCompiler_temp$$1;" + "if(1) {" + " {JSCompiler_temp$$1=true;} " + "} else {" + " JSCompiler_temp$$1=1;" + "}" + "JSCompiler_temp_const$$0 + JSCompiler_temp$$1" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression14() { helperInlineReferenceToFunction( "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z.gack = foo(1) }", "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() {" + "var JSCompiler_temp_const$$0=z;" + "var JSCompiler_inline_result$$1;" + "{" + "z= {};" + "JSCompiler_inline_result$$1 = true;" + "}" + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression15() { helperInlineReferenceToFunction( "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z.gack = foo.call(this,1) }", "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() {" + "var JSCompiler_temp_const$$0=z;" + "var JSCompiler_inline_result$$1;" + "{" + "z= {};" + "JSCompiler_inline_result$$1 = true;" + "}" + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression16() { helperInlineReferenceToFunction( "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z[bar()] = foo(1) }", "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() {" + "var JSCompiler_temp_const$$1=z;" + "var JSCompiler_temp_const$$0=bar();" + "var JSCompiler_inline_result$$2;" + "{" + "z= {};" + "JSCompiler_inline_result$$2 = true;" + "}" + "JSCompiler_temp_const$$1[JSCompiler_temp_const$$0] = " + "JSCompiler_inline_result$$2;" + "}", "foo", INLINE_BLOCK, true); } public void testInlineReferenceInExpression17() { helperInlineReferenceToFunction( "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() { z.y.x.gack = foo(1) }", "var z = {};" + "function foo(a){z = {};return true;}; " + "function x() {" + "var JSCompiler_temp_const$$0=z.y.x;" + "var JSCompiler_inline_result$$1;" + "{" + "z= {};" + "JSCompiler_inline_result$$1 = true;" + "}" + "JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" + "}", "foo", INLINE_BLOCK, true); } public void testInlineWithinCalls1() { // Call in within a call helperInlineReferenceToFunction( "function foo(){return _g;}; " + "function x() {1 + foo()() }", "function foo(){return _g;}; " + "function x() { var JSCompiler_inline_result$$0;" + "{JSCompiler_inline_result$$0=_g;}" + "1 + JSCompiler_inline_result$$0() }", "foo", INLINE_BLOCK, true); } // TODO(nicksantos): Re-enable with side-effect detection. // public void testInlineWithinCalls2() { // helperInlineReferenceToFunction( // "/** @nosideeffects */ function foo(){return true;}; " + // "function x() {1 + _g(foo()) }", // "function foo(){return true;}; " + // "function x() { {var JSCompiler_inline_result$$0; " + // "JSCompiler_inline_result$$0=true;}" + // "1 + _g(JSCompiler_inline_result$$0) }", // "foo", INLINE_BLOCK, true); // } public void testInlineAssignmentToConstant() { // Call in within a call helperInlineReferenceToFunction( "function foo(){return _g;}; " + "function x(){var CONSTANT_RESULT = foo(); }", "function foo(){return _g;}; " + "function x() {" + " var JSCompiler_inline_result$$0;" + " {JSCompiler_inline_result$$0=_g;}" + " var CONSTANT_RESULT = JSCompiler_inline_result$$0;" + "}", "foo", INLINE_BLOCK, true); } public void testBug1897706() { helperInlineReferenceToFunction( "function foo(a){}; foo(x())", "function foo(a){}; {var a$$inline_0=x()}", "foo", INLINE_BLOCK); helperInlineReferenceToFunction( "function foo(a){bar()}; foo(x())", "function foo(a){bar()}; {var a$$inline_0=x();bar()}", "foo", INLINE_BLOCK); helperInlineReferenceToFunction( "function foo(a,b){bar()}; foo(x(),y())", "function foo(a,b){bar()};" + "{var a$$inline_0=x();var b$$inline_1=y();bar()}", "foo", INLINE_BLOCK); } /** * Test case * * var a = {}, b = {} * a.test = "a", b.test = "b" * c = a; * foo() { c=b; return "a" } * c.teste * */ public void helperCanInlineReferenceToFunction( final CanInlineResult expectedResult, final String code, final String fnName, final InliningMode mode) { helperCanInlineReferenceToFunction( expectedResult, code, fnName, mode, false); } public void helperCanInlineReferenceToFunction( final CanInlineResult expectedResult, final String code, final String fnName, final InliningMode mode, boolean allowDecomposition) { final Compiler compiler = new Compiler(); final FunctionInjector injector = new FunctionInjector( compiler, compiler.getUniqueNameIdSupplier(), allowDecomposition, assumeStrictThis, assumeMinimumCapture); final Node tree = parse(compiler, code); final Node fnNode = findFunction(tree, fnName); final Set unsafe = FunctionArgumentInjector.findModifiedParameters(fnNode); // can-inline tester Method tester = new Method() { @Override public boolean call(NodeTraversal t, Node n, Node parent) { CanInlineResult result = injector.canInlineReferenceToFunction( t, n, fnNode, unsafe, mode, NodeUtil.referencesThis(fnNode), NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode))); assertEquals(expectedResult, result); return true; } }; compiler.resetUniqueNameId(); TestCallback test = new TestCallback(fnName, tester); NodeTraversal.traverse(compiler, tree, test); } public void helperInlineReferenceToFunction( String code, final String expectedResult, final String fnName, final InliningMode mode) { helperInlineReferenceToFunction( code, expectedResult, fnName, mode, false); } private void validateSourceInfo(Compiler compiler, Node subtree) { (new LineNumberCheck(compiler)).setCheckSubTree(subtree); // Source information problems are reported as compiler errors. if (compiler.getErrorCount() != 0) { String msg = "Error encountered: "; for (JSError err : compiler.getErrors()) { msg += err.toString() + "\n"; } assertTrue(msg, compiler.getErrorCount() == 0); } } public void helperInlineReferenceToFunction( String code, final String expectedResult, final String fnName, final InliningMode mode, final boolean decompose) { final Compiler compiler = new Compiler(); final FunctionInjector injector = new FunctionInjector( compiler, compiler.getUniqueNameIdSupplier(), decompose, assumeStrictThis, assumeMinimumCapture); List externsInputs = Lists.newArrayList( SourceFile.fromCode("externs", "")); CompilerOptions options = new CompilerOptions(); options.setCodingConvention(new GoogleCodingConvention()); compiler.init(externsInputs, Lists.newArrayList( SourceFile.fromCode("code", code)), options); Node parseRoot = compiler.parseInputs(); Node externsRoot = parseRoot.getFirstChild(); final Node tree = parseRoot.getLastChild(); assertNotNull(tree); assertTrue(tree != externsRoot); final Node expectedRoot = parseExpected(new Compiler(), expectedResult); Node mainRoot = tree; MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED); final Node fnNode = findFunction(tree, fnName); assertNotNull(fnNode); final Set unsafe = FunctionArgumentInjector.findModifiedParameters(fnNode); assertNotNull(fnNode); // inline tester Method tester = new Method() { @Override public boolean call(NodeTraversal t, Node n, Node parent) { CanInlineResult canInline = injector.canInlineReferenceToFunction( t, n, fnNode, unsafe, mode, NodeUtil.referencesThis(fnNode), NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode))); assertTrue("canInlineReferenceToFunction should not be CAN_NOT_INLINE", CanInlineResult.NO != canInline); if (decompose) { assertTrue("canInlineReferenceToFunction " + "should be CAN_INLINE_AFTER_DECOMPOSITION", CanInlineResult.AFTER_PREPARATION == canInline); Set knownConstants = Sets.newHashSet(); injector.setKnownConstants(knownConstants); injector.maybePrepareCall(n); assertTrue("canInlineReferenceToFunction " + "should be CAN_INLINE", CanInlineResult.YES != canInline); } Node result = injector.inline(n, fnName, fnNode, mode); validateSourceInfo(compiler, result); String explanation = expectedRoot.checkTreeEquals(tree.getFirstChild()); assertNull("\nExpected: " + toSource(expectedRoot) + "\nResult: " + toSource(tree.getFirstChild()) + "\n" + explanation, explanation); return true; } }; compiler.resetUniqueNameId(); TestCallback test = new TestCallback(fnName, tester); NodeTraversal.traverse(compiler, tree, test); } interface Method { boolean call(NodeTraversal t, Node n, Node parent); } class TestCallback implements Callback { private final String callname; private final Method method; private boolean complete = false; TestCallback(String callname, Method method) { this.callname = callname; this.method = method; } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return !complete; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { Node callee; if (NodeUtil.isGet(n.getFirstChild())) { callee = n.getFirstChild().getFirstChild(); } else { callee = n.getFirstChild(); } if (callee.isName() && callee.getString().equals(callname)) { complete = method.call(t, n, parent); } } if (parent == null) { assertTrue(complete); } } } private static Node findFunction(Node n, String name) { if (n.isFunction()) { if (n.getFirstChild().getString().equals(name)) { return n; } } for (Node c : n.children()) { Node result = findFunction(c, name); if (result != null) { return result; } } return null; } private static Node prep(String js) { Compiler compiler = new Compiler(); Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); return n.getFirstChild(); } private static Node parse(Compiler compiler, String js) { Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); return n; } private static Node parseExpected(Compiler compiler, String js) { Node n = compiler.parseTestCode(js); String message = "Unexpected errors: "; JSError[] errs = compiler.getErrors(); for (int i = 0; i < errs.length; i++){ message += "\n" + errs[i].toString(); } assertEquals(message, 0, compiler.getErrorCount()); return n; } private static String toSource(Node n) { return new CodePrinter.Builder(n) .setPrettyPrint(false) .setLineBreak(false) .setSourceMap(null) .build(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FunctionTypeBuilderTest.java0000644000175000017500000001166612115204405030477 0ustar apoapo/* * 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 static com.google.javascript.rhino.testing.BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.ObjectType; import java.util.List; /** * Unit tests for {@link FunctionTypeBuilder}. * */ public class FunctionTypeBuilderTest extends CompilerTestCase { public FunctionTypeBuilderTest() { parseTypeInfo = true; enableTypeCheck(CheckLevel.WARNING); } @Override public CompilerPass getProcessor(Compiler compiler) { // By turning on type checking, the FunctionTypeBuilder will be invoked. return new CompilerPass() { @Override public void process(Node externs, Node js) {} }; } @Override public int getNumRepetitions() { return 1; } public void testValidBuiltInTypeRedefinition() throws Exception { testSame(ALL_NATIVE_EXTERN_TYPES, "", null); } public void testBuiltInTypeDifferentReturnType() throws Exception { testSame( "/**\n" + " * @constructor\n" + " * @param {*} opt_str\n" + " * @return {number}\n" + " */\n" + "function String(opt_str) {}\n", "", FunctionTypeBuilder.TYPE_REDEFINITION, "attempted re-definition of type String\n" + "found : function (new:String, *=): number\n" + "expected: function (new:String, *=): string"); } public void testBuiltInTypeDifferentNumParams() throws Exception { testSame( "/**\n" + " * @constructor\n" + " * @return {string}\n" + " */\n" + "function String() {}\n", "", FunctionTypeBuilder.TYPE_REDEFINITION, "attempted re-definition of type String\n" + "found : function (new:String): string\n" + "expected: function (new:String, *=): string"); } public void testBuiltInTypeDifferentNumParams2() throws Exception { testSame( "/**\n" + " * @constructor\n" + " * @return {string}\n" + " */\n" + "function String(opt_str, opt_nothing) {}\n", "", FunctionTypeBuilder.TYPE_REDEFINITION, "attempted re-definition of type String\n" + "found : function (new:String, ?=, ?=): string\n" + "expected: function (new:String, *=): string"); } public void testBuiltInTypeDifferentParamType() throws Exception { testSame( "/**\n" + " * @constructor\n" + " * @return {string}\n" + " */\n" + "function String(opt_str) {}\n", "", FunctionTypeBuilder.TYPE_REDEFINITION, "attempted re-definition of type String\n" + "found : function (new:String, ?=): string\n" + "expected: function (new:String, *=): string"); } public void testBadFunctionTypeDefinition() throws Exception { testSame( "/** @constructor */function Function(opt_str) {}\n", "", FunctionTypeBuilder.TYPE_REDEFINITION, "attempted re-definition of type Function\n" + "found : function (new:Function, ?=): ?\n" + "expected: function (new:Function, ...[*]): ?"); } public void testInlineJsDoc() throws Exception { testSame( "/** @return {number} */ function f(/** string */ x) { return x; }", "", TypeValidator.TYPE_MISMATCH_WARNING, "inconsistent return type\n" + "found : string\n" + "required: number"); } public void testInlineJsDoc2() throws Exception { testSame( "/** @return {T} \n @template T */ " + "function f(/** T */ x) { return x; }" + "/** @type {string} */ var x = f(1);", "", TypeValidator.TYPE_MISMATCH_WARNING, "initializing variable\n" + "found : number\n" + "required: string"); } public void testExternSubTypes() throws Exception { testSame(ALL_NATIVE_EXTERN_TYPES, "", null); List subtypes = ((ObjectType) getLastCompiler() .getTypeRegistry().getType("Error")).getConstructor().getSubTypes(); for (FunctionType type : subtypes) { String typeName = type.getInstanceType().toString(); FunctionType typeInRegistry = ((ObjectType) getLastCompiler() .getTypeRegistry().getType(typeName)).getConstructor(); assertTrue(typeInRegistry == type); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RenameVarsTest.java0000644000175000017500000005362112115204405026601 0ustar apoapo/* * Copyright 2005 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.javascript.rhino.Node; import java.util.*; /** * Tests for {@link RenameVars}. */ public class RenameVarsTest extends CompilerTestCase { private static final String DEFAULT_PREFIX = ""; private String prefix = DEFAULT_PREFIX; private VariableMap previouslyUsedMap = new VariableMap(ImmutableMap.of()); private RenameVars renameVars; private boolean withClosurePass = false; private boolean localRenamingOnly = false; private boolean preserveFunctionExpressionNames = false; private boolean useGoogleCodingConvention = true; private boolean generatePseudoNames = false; private boolean shouldShadow = false; private boolean withNormalize = false; @Override protected CodingConvention getCodingConvention() { if (useGoogleCodingConvention) { return new GoogleCodingConvention(); } else { return CodingConventions.getDefault(); } } @Override protected CompilerPass getProcessor(Compiler compiler) { CompilerPass pass; if (withClosurePass) { pass = new ClosurePassAndRenameVars(compiler); } else { pass = renameVars = new RenameVars(compiler, prefix, localRenamingOnly, preserveFunctionExpressionNames, generatePseudoNames, shouldShadow, previouslyUsedMap, null, null); } if (withNormalize) { // Don't use the standard CompilerTestCase normalization options // as renaming is a post denormalization operation, but we do still // want to run the normal normalizations on the input in some cases. pass = new NormalizePassWrapper(compiler, pass); } return pass; } @Override protected int getNumRepetitions() { return 1; } @Override protected void setUp() throws Exception { super.setUp(); previouslyUsedMap = new VariableMap(ImmutableMap.of()); prefix = DEFAULT_PREFIX; withClosurePass = false; withNormalize = false; localRenamingOnly = false; preserveFunctionExpressionNames = false; generatePseudoNames = false; shouldShadow = false; // TODO(johnlenz): Enable Normalize during these tests. } public void testRenameSimple() { test("function Foo(v1, v2) {return v1;} Foo();", "function a(b, c) {return b;} a();"); } public void testRenameGlobals() { test("var Foo; var Bar, y; function x() { Bar++; }", "var a; var b, c; function d() { b++; }"); } public void testRenameLocals() { test("(function (v1, v2) {}); (function (v3, v4) {});", "(function (a, b) {}); (function (a, b) {});"); test("function f1(v1, v2) {}; function f2(v3, v4) {};", "function c(a, b) {}; function d(a, b) {};"); } public void testRenameRedeclaredGlobals() { test("function f1(v1, v2) {f1()};" + "/** @suppress {duplicate} */" + "function f1(v3, v4) {f1()};", "function a(b, c) {a()};" + "function a(b, c) {a()};"); localRenamingOnly = true; test("function f1(v1, v2) {f1()};" + "/** @suppress {duplicate} */" + "function f1(v3, v4) {f1()};", "function f1(a, b) {f1()};" + "function f1(a, b) {f1()};"); } public void testRecursiveFunctions1() { test("var walk = function walk(node, aFunction) {" + " walk(node, aFunction);" + "};", "var a = function a(b, c) {" + " a(b, c);" + "};"); localRenamingOnly = true; test("var walk = function walk(node, aFunction) {" + " walk(node, aFunction);" + "};", "var walk = function walk(a, b) {" + " walk(a, b);" + "};"); } public void testRecursiveFunctions2() { preserveFunctionExpressionNames = true; test("var walk = function walk(node, aFunction) {" + " walk(node, aFunction);" + "};", "var c = function walk(a, b) {" + " walk(a, b);" + "};"); localRenamingOnly = true; test("var walk = function walk(node, aFunction) {" + " walk(node, aFunction);" + "};", "var walk = function walk(a, b) {" + " walk(a, b);" + "};"); } public void testRenameLocalsClashingWithGlobals() { test("function a(v1, v2) {return v1;} a();", "function a(b, c) {return b;} a();"); } public void testRenameNested() { test("function f1(v1, v2) { (function(v3, v4) {}) }", "function a(b, c) { (function(d, e) {}) }"); test("function f1(v1, v2) { function f2(v3, v4) {} }", "function a(b, c) { function d(e, f) {} }"); } public void testBleedingRecursiveFunctions1() { // On IE, bleeding functions will interfere with each other if // they are in the same scope. In the below example, we want to be // sure that a and b get separate names. test("var x = function a(x) { return x ? 1 : a(1); };" + "var y = function b(x) { return x ? 2 : b(2); };", "var c = function b(a) { return a ? 1 : b(1); };" + "var e = function d(a) { return a ? 2 : d(2); };"); } public void testBleedingRecursiveFunctions2() { test("function f() {" + " var x = function a(x) { return x ? 1 : a(1); };" + " var y = function b(x) { return x ? 2 : b(2); };" + "}", "function d() {" + " var e = function b(a) { return a ? 1 : b(1); };" + " var f = function a(c) { return c ? 2 : a(2); };" + "}"); } public void testBleedingRecursiveFunctions3() { test("function f() {" + " var x = function a(x) { return x ? 1 : a(1); };" + " var y = function b(x) { return x ? 2 : b(2); };" + " var z = function c(x) { return x ? y : c(2); };" + "}", "function f() {" + " var g = function c(a) { return a ? 1 : c(1); };" + " var d = function a(b) { return b ? 2 : a(2); };" + " var h = function b(e) { return e ? d : b(2); };" + "}"); } public void testRenameWithExterns1() { String externs = "var foo;"; test(externs, "var bar; foo(bar);", "var a; foo(a);", null, null); } public void testRenameWithExterns2() { String externs = "var a;"; test(externs, "var b = 5", "var b = 5", null, null); } public void testDoNotRenameExportedName() { test("_foo()", "_foo()"); } public void testRenameWithNameOverlap() { test("var a = 1; var b = 2; b + b;", "var a = 1; var b = 2; b + b;"); } public void testRenameWithPrefix1() { prefix = "PRE_"; test("function Foo(v1, v2) {return v1} Foo();", "function PRE_(a, b) {return a} PRE_();"); prefix = DEFAULT_PREFIX; } public void testRenameWithPrefix2() { prefix = "PRE_"; test("function Foo(v1, v2) {var v3 = v1 + v2; return v3;} Foo();", "function PRE_(a, b) {var c = a + b; return c;} PRE_();"); prefix = DEFAULT_PREFIX; } public void testRenameWithPrefix3() { prefix = "a"; test("function Foo() {return 1;}" + "function Bar() {" + " var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z," + " A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,aa,ab;" + " Foo();" + "} Bar();", "function a() {return 1;}" + "function aa() {" + " var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A," + " B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ba,ca;" + " a();" + "} aa();"); prefix = DEFAULT_PREFIX; } public void testNamingBasedOnOrderOfOccurrence() { test("var q,p,m,n,l,k; " + "(function (r) {}); try { } catch(s) {}; var t = q + q;", "var a,b,c,d,e,f; " + "(function(g) {}); try { } catch(h) {}; var i = a + a;" ); test("(function(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z," + "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$){});" + "var a4,a3,a2,a1,b4,b3,b2,b1,ab,ac,ad,fg;function foo(){};", "(function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z," + "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$){});" + "var aa,ba,ca,da,ea,fa,ga,ha,ia,ja,ka,la;function ma(){};"); } public void testStableRenameSimple() { VariableMap expectedVariableMap = makeVariableMap( "Foo", "a", "L 0", "b", "L 1", "c"); testRenameMap("function Foo(v1, v2) {return v1;} Foo();", "function a(b, c) {return b;} a();", expectedVariableMap); expectedVariableMap = makeVariableMap( "Foo", "a", "L 0", "b", "L 1", "c", "L 2", "d"); testRenameMapUsingOldMap("function Foo(v1, v2, v3) {return v1;} Foo();", "function a(b, c, d) {return b;} a();", expectedVariableMap); } public void testStableRenameGlobals() { VariableMap expectedVariableMap = makeVariableMap( "Foo", "a", "Bar", "b", "y", "c", "x", "d"); testRenameMap("var Foo; var Bar, y; function x() { Bar++; }", "var a; var b, c; function d() { b++; }", expectedVariableMap); expectedVariableMap = makeVariableMap( "Foo", "a", "Bar", "b", "y", "c", "x", "d", "Baz", "f", "L 0" , "e"); testRenameMapUsingOldMap( "var Foo, Baz; var Bar, y; function x(R) { return R + Bar++; }", "var a, f; var b, c; function d(e) { return e + b++; }", expectedVariableMap); } public void testStableRenameWithPointlesslyAnonymousFunctions() { VariableMap expectedVariableMap = makeVariableMap("L 0", "a", "L 1", "b"); testRenameMap("(function (v1, v2) {}); (function (v3, v4) {});", "(function (a, b) {}); (function (a, b) {});", expectedVariableMap); expectedVariableMap = makeVariableMap("L 0", "a", "L 1", "b", "L 2", "c"); testRenameMapUsingOldMap("(function (v0, v1, v2) {});" + "(function (v3, v4) {});", "(function (a, b, c) {});" + "(function (a, b) {});", expectedVariableMap); } public void testStableRenameLocalsClashingWithGlobals() { test("function a(v1, v2) {return v1;} a();", "function a(b, c) {return b;} a();"); previouslyUsedMap = renameVars.getVariableMap(); test("function bar(){return;}function a(v1, v2) {return v1;} a();", "function d(){return;}function a(b, c) {return b;} a();"); } public void testStableRenameNested() { VariableMap expectedVariableMap = makeVariableMap( "f1", "a", "L 0", "b", "L 1", "c", "L 2", "d", "L 3", "e"); testRenameMap("function f1(v1, v2) { (function(v3, v4) {}) }", "function a(b, c) { (function(d, e) {}) }", expectedVariableMap); expectedVariableMap = makeVariableMap( "f1", "a", "L 0", "b", "L 1", "c", "L 2", "d", "L 3", "e", "L 4", "f"); testRenameMapUsingOldMap( "function f1(v1, v2) { (function(v3, v4, v5) {}) }", "function a(b, c) { (function(d, e, f) {}) }", expectedVariableMap); } public void testStableRenameWithExterns1() { String externs = "var foo;"; test(externs, "var bar; foo(bar);", "var a; foo(a);", null, null); previouslyUsedMap = renameVars.getVariableMap(); test(externs, "var bar, baz; foo(bar, baz);", "var a, b; foo(a, b);", null, null); } public void testStableRenameWithExterns2() { String externs = "var a;"; test(externs, "var b = 5", "var b = 5", null, null); previouslyUsedMap = renameVars.getVariableMap(); test(externs, "var b = 5, catty = 9;", "var b = 5, c=9;", null, null); } public void testStableRenameWithNameOverlap() { test("var a = 1; var b = 2; b + b;", "var a = 1; var b = 2; b + b;"); previouslyUsedMap = renameVars.getVariableMap(); test("var a = 1; var c, b = 2; b + b;", "var a = 1; var c, b = 2; b + b;"); } public void testStableRenameWithAnonymousFunctions() { VariableMap expectedVariableMap = makeVariableMap("L 0", "a", "foo", "b"); testRenameMap("function foo(bar){return bar;}foo(function(h){return h;});", "function b(a){return a}b(function(a){return a;})", expectedVariableMap); expectedVariableMap = makeVariableMap("foo", "b", "L 0", "a", "L 1", "c"); testRenameMapUsingOldMap( "function foo(bar) {return bar;}foo(function(g,h) {return g+h;});", "function b(a){return a}b(function(a,c){return a+c;})", expectedVariableMap); } public void testStableRenameSimpleExternsChanges() { VariableMap expectedVariableMap = makeVariableMap( "Foo", "a", "L 0", "b", "L 1", "c"); testRenameMap("function Foo(v1, v2) {return v1;} Foo();", "function a(b, c) {return b;} a();", expectedVariableMap); expectedVariableMap = makeVariableMap("L 0", "b", "L 1", "c", "L 2", "a"); String externs = "var Foo;"; testRenameMapUsingOldMap(externs, "function Foo(v1, v2, v0) {return v1;} Foo();", "function Foo(b, c, a) {return b;} Foo();", expectedVariableMap); } public void testStableRenameSimpleLocalNameExterned() { test("function Foo(v1, v2) {return v1;} Foo();", "function a(b, c) {return b;} a();"); previouslyUsedMap = renameVars.getVariableMap(); String externs = "var b;"; test(externs, "function Foo(v1, v2) {return v1;} Foo(b);", "function a(d, c) {return d;} a(b);", null, null); } public void testStableRenameSimpleGlobalNameExterned() { test("function Foo(v1, v2) {return v1;} Foo();", "function a(b, c) {return b;} a();"); previouslyUsedMap = renameVars.getVariableMap(); String externs = "var Foo;"; test(externs, "function Foo(v1, v2, v0) {return v1;} Foo();", "function Foo(b, c, a) {return b;} Foo();", null, null); } public void testStableRenameWithPrefix1AndUnstableLocalNames() { prefix = "PRE_"; test("function Foo(v1, v2) {return v1} Foo();", "function PRE_(a, b) {return a} PRE_();"); previouslyUsedMap = renameVars.getVariableMap(); prefix = "PRE_"; test("function Foo(v0, v1, v2) {return v1} Foo();", "function PRE_(a, b, c) {return b} PRE_();"); } public void testStableRenameWithPrefix2() { prefix = "a"; test("function Foo() {return 1;}" + "function Bar() {" + " var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z," + " A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,aa,ab;" + " Foo();" + "} Bar();", "function a() {return 1;}" + "function aa() {" + " var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A," + " B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ba,ca;" + " a();" + "} aa();"); previouslyUsedMap = renameVars.getVariableMap(); prefix = "a"; test("function Foo() {return 1;}" + "function Baz() {return 1;}" + "function Bar() {" + " var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z," + " A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,aa,ab;" + " Foo();" + "} Bar();", "function a() {return 1;}" + "function ab() {return 1;}" + "function aa() {" + " var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A," + " B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,$,ba,ca;" + " a();" + "} aa();"); } public void testContrivedExampleWhereConsistentRenamingIsWorse() { previouslyUsedMap = makeVariableMap( "Foo", "LongString", "L 0", "b", "L 1", "c"); test("function Foo(v1, v2) {return v1;} Foo();", "function LongString(b, c) {return b;} LongString();"); previouslyUsedMap = renameVars.getVariableMap(); VariableMap expectedVariableMap = makeVariableMap( "Foo", "LongString", "L 0", "b", "L 1", "c"); assertVariableMapsEqual(expectedVariableMap, previouslyUsedMap); } public void testPrevUsedMapWithDuplicates() { previouslyUsedMap = makeVariableMap("Foo", "z", "Bar", "z"); try { testSame(""); fail(); } catch (java.lang.IllegalArgumentException expected) { } } public void testExportSimpleSymbolReservesName() { test("var goog, x; goog.exportSymbol('a', x);", "var a, b; a.exportSymbol('a', b);"); withClosurePass = true; test("var goog, x; goog.exportSymbol('a', x);", "var b, c; b.exportSymbol('a', c);"); } public void testExportComplexSymbolReservesName() { test("var goog, x; goog.exportSymbol('a.b', x);", "var a, b; a.exportSymbol('a.b', b);"); withClosurePass = true; test("var goog, x; goog.exportSymbol('a.b', x);", "var b, c; b.exportSymbol('a.b', c);"); } public void testExportToNonStringDoesntExplode() { withClosurePass = true; test("var goog, a, b; goog.exportSymbol(a, b);", "var a, b, c; a.exportSymbol(b, c);"); } public void testDollarSignSuperExport1() { useGoogleCodingConvention = false; // See http://code.google.com/p/closure-compiler/issues/detail?id=32 test("var x = function($super,duper,$fantastic){}", "var c = function($super, a, b){}"); localRenamingOnly = false; test("var $super = 1", "var a = 1"); useGoogleCodingConvention = true; test("var x = function($super,duper,$fantastic){}", "var c = function($super,a,b){}"); } public void testDollarSignSuperExport2() { withNormalize = true; useGoogleCodingConvention = false; // See http://code.google.com/p/closure-compiler/issues/detail?id=32 test("var x = function($super,duper,$fantastic){};" + "var y = function($super,duper){};", "var c = function($super, a, b){};" + "var d = function($super, a){};"); localRenamingOnly = false; test("var $super = 1", "var a = 1"); useGoogleCodingConvention = true; test("var x = function($super,duper,$fantastic){};" + "var y = function($super,duper){};", "var c = function($super, a, b ){};" + "var d = function($super,a){};"); } public void testPseudoNames() { generatePseudoNames = false; // See http://code.google.com/p/closure-compiler/issues/detail?id=32 test("var foo = function(a, b, c){}", "var d = function(a, b, c){}"); generatePseudoNames = true; test("var foo = function(a, b, c){}", "var $foo$$ = function($a$$, $b$$, $c$$){}"); test("var a = function(a, b, c){}", "var $a$$ = function($a$$, $b$$, $c$$){}"); } private void testRenameMapUsingOldMap(String input, String expected, VariableMap expectedMap) { previouslyUsedMap = renameVars.getVariableMap(); testRenameMap("", input, expected, expectedMap); } private void testRenameMapUsingOldMap(String externs, String input, String expected, VariableMap expectedMap) { previouslyUsedMap = renameVars.getVariableMap(); testRenameMap(externs, input, expected, expectedMap); } private void testRenameMap(String input, String expected, VariableMap expectedRenameMap) { testRenameMap("", input, expected, expectedRenameMap); } private void testRenameMap(String externs, String input, String expected, VariableMap expectedRenameMap) { test(externs, input, expected, null, null); VariableMap renameMap = renameVars.getVariableMap(); assertVariableMapsEqual(expectedRenameMap, renameMap); } private VariableMap makeVariableMap(String... keyValPairs) { Preconditions.checkArgument(keyValPairs.length % 2 == 0); ImmutableMap.Builder renameMap = ImmutableMap.builder(); for (int i = 0; i < keyValPairs.length; i += 2) { renameMap.put(keyValPairs[i], keyValPairs[i + 1]); } return new VariableMap(renameMap.build()); } private static void assertVariableMapsEqual(VariableMap a, VariableMap b) { Map ma = a.getOriginalNameToNewNameMap(); Map mb = b.getOriginalNameToNewNameMap(); assertEquals("VariableMaps not equal", ma, mb); } private class ClosurePassAndRenameVars implements CompilerPass { private final Compiler compiler; private ClosurePassAndRenameVars(Compiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { ProcessClosurePrimitives closurePass = new ProcessClosurePrimitives( compiler, null, CheckLevel.WARNING); closurePass.process(externs, root); renameVars = new RenameVars(compiler, prefix, false, false, false, false, previouslyUsedMap, null, closurePass.getExportedVariableNames()); renameVars.process(externs, root); } } private class NormalizePassWrapper implements CompilerPass { private final Compiler compiler; private CompilerPass wrappedPass; private NormalizePassWrapper(Compiler compiler, CompilerPass wrappedPass) { this.compiler = compiler; this.wrappedPass = wrappedPass; } @Override public void process(Node externs, Node root) { Normalize normalize = new Normalize(compiler, false); normalize.process(externs, root); wrappedPass.process(externs, root); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/VariableMapTest.java0000644000175000017500000001164112115204405026715 0ustar apoapo/* * 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.base.Charsets; import com.google.common.collect.ImmutableMap; import junit.framework.TestCase; import java.text.ParseException; import java.util.Arrays; import java.util.List; import java.util.Map; /** * Tests for {@link VariableMap}. * */ public class VariableMapTest extends TestCase { public void testCycle1() throws ParseException { cycleTest(ImmutableMap.of("AAA", "a", "BBB", "b")); cycleTest(ImmutableMap.of("AA:AA", "a", "BB:BB", "b")); cycleTest(ImmutableMap.of("AAA", "a:a", "BBB", "b:b")); } public void cycleTest(ImmutableMap map) throws ParseException { VariableMap in = new VariableMap(map); String serialized = new String(in.toBytes(), Charsets.UTF_8); VariableMap out = VariableMap.fromBytes(serialized.getBytes()); assertMapsEquals(in.toMap(), out.toMap()); } public void assertMapsEquals( Map expected, Map result) { assertEquals(expected.size(), result.size()); for (String key : expected.keySet()) { assertEquals(expected.get(key), result.get(key)); } } public void testToBytes() { VariableMap vm = new VariableMap(ImmutableMap.of("AAA", "a", "BBB", "b")); String serialized = new String(vm.toBytes(), Charsets.UTF_8); assertTrue(serialized.endsWith("\n")); List lines = Arrays.asList(serialized.split("\n")); assertEquals(2, lines.size()); assertTrue(lines.contains("AAA:a")); assertTrue(lines.contains("BBB:b")); } public void testFromBytes() throws ParseException { VariableMap vm = VariableMap.fromBytes("AAA:a\nBBB:b\n".getBytes()); assertEquals(2, vm.getOriginalNameToNewNameMap().size()); assertEquals("a", vm.lookupNewName("AAA")); assertEquals("b", vm.lookupNewName("BBB")); assertEquals("AAA", vm.lookupSourceName("a")); assertEquals("BBB", vm.lookupSourceName("b")); } public void testFileFormat1() { assertEqual( new VariableMap(ImmutableMap.of("x\ny", "a")).toBytes(), "x\\ny:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("x:y", "a")).toBytes(), "x\\:y:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("x\ny", "a")).toBytes(), "x\\ny:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("x\\y", "a")).toBytes(), "x\\\\y:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("\n", "a")).toBytes(), "\\n:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of(":", "a")).toBytes(), "\\::a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("\n", "a")).toBytes(), "\\n:a\n".getBytes()); assertEqual( new VariableMap(ImmutableMap.of("\\", "a")).toBytes(), "\\\\:a\n".getBytes()); } public void testFromBytesComplex1() throws ParseException { // Verify we get out what we put in. cycleTest(ImmutableMap.of("AAA[':f']", "a")); // Verify the file format is as expected. VariableMap in = new VariableMap(ImmutableMap.of("AAA[':f']", "a")); assertEqual(in.toBytes(), "AAA['\\:f']:a\n".getBytes()); } public void testFromBytesComplex2() throws ParseException { VariableMap vm = VariableMap.fromBytes("AAA['\\:f']:a\n".getBytes()); assertEquals(1, vm.getOriginalNameToNewNameMap().size()); assertEquals("a", vm.lookupNewName("AAA[':f']")); assertEquals(1, vm.getNewNameToOriginalNameMap().size()); assertEquals("AAA[':f']", vm.lookupSourceName("a")); } private void assertEqual(byte[] bytes1, byte[] bytes2) { if (bytes1 != bytes2) { assertEquals("length differs.", bytes1.length, bytes2.length); for (int i = 0; i < bytes1.length; i++) { assertEquals("byte " + i + "differs.", bytes1[i], bytes2[i]); } } } public void testReverseThrowsErrorOnDuplicate() throws ParseException { VariableMap vm = new VariableMap(ImmutableMap.of("AA", "b", "BB", "b")); try { vm.getNewNameToOriginalNameMap(); fail(); } catch (java.lang.IllegalArgumentException expected) { } } public void testReverseLookupOfNullFindsNoName() throws ParseException { VariableMap vm = new VariableMap(ImmutableMap.of("AA", "a", "BB", "b")); assertNull(vm.lookupSourceName(null)); } } ././@LongLink0000644000000000000000000000016200000000000011602 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallbackTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCall0000644000175000017500000001463212115204405031662 0ustar apoapo/* * 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.GatherSideEffectSubexpressionsCallback.GetReplacementSideEffectSubexpressions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.List; /** * Tests for {@link GatherSideEffectSubexpressionsCallback} * */ public class GatherSideEffectSubexpressionsCallbackTest extends TestCase { public void testAndOr() throws Exception { Node andNode = getSideEffectsAndNode(); checkKeepSimplifiedShortCircuitExpr(andNode, ImmutableList.of("foo&&(bar=0)")); } public void testIllegalArgumentIfNotAndOr() throws Exception { Node nameNode = Node.newString(Token.NAME, "foo"); try { checkKeepSimplifiedShortCircuitExpr(nameNode, ImmutableList.of()); fail("Expected exception"); } catch (IllegalArgumentException e) { // ignore } } public void testIllegalArgumentIfNoSideEffectAndOr() throws Exception { Node andNode = getNoSideEffectsAndNode(); try { checkKeepSimplifiedShortCircuitExpr(andNode, ImmutableList.of()); fail("Expected exception"); } catch (IllegalArgumentException e) { // ignore } } public void testHook() throws Exception { Node hook = getSideEffectsHookNode(); checkKeepSimplifiedHookExpr(hook, true, true, ImmutableList.of("foo?bar=0:baz=0")); } public void testIllegalArgumentIfNotHook() throws Exception { Node nameNode = Node.newString(Token.NAME, "foo"); try { checkKeepSimplifiedHookExpr(nameNode, true, true, ImmutableList.of()); fail("Expected exception"); } catch (IllegalArgumentException e) { // ignore } } public void testIllegalArgumentIfNoSideEffectHook() throws Exception { Node hookNode = getNoSideEffectsHookNode(); try { checkKeepSimplifiedHookExpr(hookNode, true, true, ImmutableList.of()); fail("Expected exception"); } catch (IllegalArgumentException e) { // ignore } } public void testIllegalArgumentIfHookKeepNeitherBranch() throws Exception { Node hookNode = getSideEffectsHookNode(); try { checkKeepSimplifiedHookExpr(hookNode, false, false, ImmutableList.of()); fail("Expected exception"); } catch (IllegalArgumentException e) { // ignore } } private Node getNoSideEffectsAndNode() { Node andNode = new Node(Token.AND); andNode.addChildToBack(Node.newString(Token.NAME, "foo")); andNode.addChildToBack(Node.newString(Token.NAME, "bar")); return andNode; } private Node getSideEffectsAndNode() { Node andNode = new Node(Token.AND); Node assign = new Node(Token.ASSIGN); assign.addChildToBack(Node.newString(Token.NAME, "bar")); assign.addChildToBack(Node.newNumber(0)); andNode.addChildToBack(Node.newString(Token.NAME, "foo")); andNode.addChildToBack(assign); return andNode; } private Node getNoSideEffectsHookNode() { Node hookNode = new Node(Token.HOOK); hookNode.addChildToBack(Node.newString(Token.NAME, "foo")); hookNode.addChildToBack(Node.newString(Token.NAME, "bar")); hookNode.addChildToBack(Node.newString(Token.NAME, "baz")); return hookNode; } private Node getSideEffectsHookNode() { Node hookNode = new Node(Token.HOOK); Node assign1 = new Node(Token.ASSIGN); assign1.addChildToBack(Node.newString(Token.NAME, "bar")); assign1.addChildToBack(Node.newNumber(0)); Node assign2 = new Node(Token.ASSIGN); assign2.addChildToBack(Node.newString(Token.NAME, "baz")); assign2.addChildToBack(Node.newNumber(0)); hookNode.addChildToBack(Node.newString(Token.NAME, "foo")); hookNode.addChildToBack(assign1); hookNode.addChildToBack(assign2); return hookNode; } private void checkKeepSimplifiedShortCircuitExpr(Node root, List expected) { Compiler compiler = new Compiler(); List replacements = Lists.newArrayList(); GetReplacementSideEffectSubexpressions accumulator = new GetReplacementSideEffectSubexpressions(compiler, replacements); accumulator.keepSimplifiedShortCircuitExpression(root); List actual = Lists.newArrayList(); for (Node replacement : replacements) { actual.add(compiler.toSource(replacement)); } assertEquals(expected, actual); } private void checkKeepSimplifiedHookExpr(Node root, boolean thenHasSideEffects, boolean elseHasSideEffects, List expected) { Compiler compiler = new Compiler(); List replacements = Lists.newArrayList(); GetReplacementSideEffectSubexpressions accumulator = new GetReplacementSideEffectSubexpressions(compiler, replacements); accumulator.keepSimplifiedHookExpression(root, thenHasSideEffects, elseHasSideEffects); List actual = Lists.newArrayList(); for (Node replacement : replacements) { actual.add(compiler.toSource(replacement)); } assertEquals(expected, actual); } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DeadAssignmentsEliminationTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DeadAssignmentsEliminationTest.jav0000644000175000017500000004264312115204405031641 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Tests for {@link DeadAssignmentsElimination}. * */ public class DeadAssignmentsEliminationTest extends CompilerTestCase { public DeadAssignmentsEliminationTest() { super("var extern;"); } @Override public void setUp() { super.enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node js) { NodeTraversal.traverse( compiler, js, new DeadAssignmentsElimination(compiler)); } }; } @Override protected int getNumRepetitions() { return 1; } public void testSimple() { inFunction("var a; a=1", "var a; 1"); inFunction("var a; a=1+1", "var a; 1+1"); inFunction("var a; a=foo();", "var a; foo()"); inFunction("a=1; var a; a=foo();", "1; var a; foo();"); // This should be: "var a; (function f(){})", but we don't mess with // functions with inner functions. inFunction("var a; a=function f(){}"); } public void testLoops() { inFunction("for(var a=0; a<10; a++) {}"); inFunction("var x; for(var a=0; a<10; a++) {x=a}; a(x)"); inFunction("var x; for(var a=0; x=a<10; a++) {}", "var x; for(var a=0; a<10; a++) {}"); inFunction("var x; for(var a=0; a<10; x=a) {}", "var x; for(var a=0; a<10; a) {}"); inFunction("var x; for(var a=0; a<10; x=a,a++) {}", "var x; for(var a=0; a<10; a,a++) {}"); inFunction("var x; for(var a=0; a<10; a++,x=a) {}", "var x; for(var a=0; a<10; a++,a) {}"); inFunction("var x;for(var a=0; a<10; a++) {x=1}", "var x;for(var a=0; a<10; a++) {1}"); inFunction("var x; x=1; do{x=2}while(0); x", "var x; 1; do{x=2}while(0); x"); inFunction("var x; x=1; while(1){x=2}; x"); } public void testMultiPaths() { inFunction("var x,y; if(x)y=1;", "var x,y; if(x)1;"); inFunction("var x,y; if(x)y=1; y=2; x(y)", "var x,y; if(x)1; y=2; x(y)"); inFunction("var x; switch(x) { case(1): x=1; break; } x"); inFunction("var x; switch(x) { case(1): x=1; break; }", "var x; switch(x) { case(1): 1; break; }"); } public void testUsedAsConditions() { inFunction("var x; while(x=1){}", "var x; while(1){}"); inFunction("var x; if(x=1){}", "var x; if(1){}"); inFunction("var x; do{}while(x=1)", "var x; do{}while(1)"); inFunction("var x; if(x=1==4&&1){}", "var x; if(1==4&&1) {}"); inFunction("var x; if(0&&(x=1)){}", "var x; if(0&&1){}"); inFunction("var x; if((x=2)&&(x=1)){}", "var x; if(2&&1){}"); inFunction("var x; x=2; if(0&&(x=1)){}; x"); inFunction("var x,y; if( (x=1)+(y=2) > 3){}", "var x,y; if( 1+2 > 3){}"); } public void testUsedAsConditionsInSwitchStatements() { inFunction("var x; switch(x=1){}","var x; switch(1){}"); inFunction("var x; switch(x){case(x=1):break;}", "var x; switch(x){case(1):break;}"); inFunction("var x,y; switch(y) { case (x += 1): break; case (x): break;}"); inFunction("var x,y; switch(y) { case (x = 1): break; case (2): break;}", "var x,y; switch(y) { case (1): break; case (2): break;}"); inFunction("var x,y; switch(y) { case (x+=1): break; case (x=2): break;}", "var x,y; switch(y) { case (x+1): break; case (2): break;}"); } public void testAssignmentInReturn() { inFunction("var x; return x = 1;", "var x; return 1"); inFunction("var x; return"); } public void testAssignmentSamples() { // We want this to be "var x" in these cases. inFunction("var x = 2;"); inFunction("var x = 2; x++;", "var x=2; void 0"); inFunction("var x; x=x++;", "var x;x++"); inFunction("var x; x+=1;", "var x;x+1"); } public void testAssignmentInArgs() { inFunction("var x; foo(x = 1);", "var x; foo(1);"); inFunction("var x; return foo(x = 1);", "var x; return foo(1);"); } /** * BUG #1358904 */ public void testAssignAndReadInCondition() { inFunction("var a, b; if ((a = 1) && (b = a)) {b}"); inFunction("var a, b; if ((b = a) && (a = 1)) {b}", "var a, b; if ((b = a) && (1)) {b}"); } public void testParameters() { inFunction("param1=1; param1=2; param2(param1)", "1; param1=2; param2(param1)"); inFunction("param1=param2()", "param2()"); } public void testErrorHandling() { inFunction("var x; try{ x=1 } catch(e){ x=2 }; x"); inFunction("var x; try{ x=1 } catch(e){ x=2 }", "var x;try{ 1 } catch(e) { 2 }"); inFunction("var x; try{ x=1 } finally { x=2 }; x", "var x;try{ 1 } finally{ x=2 }; x"); inFunction("var x; while(1) { try{x=1;break}finally{x} }"); inFunction("var x; try{throw 1} catch(e){x=2} finally{x}"); inFunction("var x; try{x=1;throw 1;x} finally{x=2}; x", "var x; try{1;throw 1;x} finally{x=2}; x"); } public void testDeadVarDeclarations() { // Dead assignments in VAR is _NOT_ supported yet. inFunction("var x=1;"); inFunction("var x=1; x=2; x"); } public void testGlobal() { // Doesn't do any work on global scope yet. test("var x; x=1; x=2; x=3;", "var x; x=1; x=2; x=3;"); } public void testInnerFunctions() { inFunction("var x = function() { var x; x=1; }", "var x = function() { var x; 1; }"); } public void testInnerFunctions2() { // Give up DCE if there is a inner function. inFunction("var x = 0; print(x); x = 1; var y = function(){}; y()"); } public void testSelfReAssignment() { inFunction("var x; x = x;", "var x; x"); } public void testSelfIncrement() { inFunction("var x; x = x + 1;", "var x; x + 1"); } public void testAssignmentOp() { // We have remove constant expressions that cleans this one up. inFunction("var x; x += foo()", "var x; x + foo()"); } public void testAssignmentOpUsedAsLhs() { inFunction("var x,y; y = x += foo(); print(y)", "var x,y; y = x + foo(); print(y)"); } public void testAssignmentOpUsedAsCondition() { inFunction("var x; if(x += foo()) {}", "var x; if(x + foo()) {}"); inFunction("var x; if((x += foo()) > 1) {}", "var x; if((x + foo()) > 1) {}"); // Not in a while because this happens every loop. inFunction("var x; while((x += foo()) > 1) {}"); inFunction("var x; for(;--x;){}"); inFunction("var x; for(;x--;){}"); inFunction("var x; for(;x -= 1;){}"); inFunction("var x; for(;x = 0;){}", "var x; for(;0;){}"); inFunction("var x; for(;;--x){}"); inFunction("var x; for(;;x--){}"); inFunction("var x; for(;;x -= 1){}"); inFunction("var x; for(;;x = 0){}", "var x; for(;;0){}"); inFunction("var x; for(--x;;){}", "var x; for(;;){}"); inFunction("var x; for(x--;;){}", "var x; for(;;){}"); inFunction("var x; for(x -= 1;;){}", "var x; for(x - 1;;){}"); inFunction("var x; for(x = 0;;){}", "var x; for(0;;){}"); } public void testDeadIncrement() { // TODO(user): Optimize this. inFunction("var x; x ++", "var x; void 0"); inFunction("var x; x --", "var x; void 0"); } public void testDeadButAlivePartiallyWithinTheExpression() { inFunction("var x; x = 100, print(x), x = 101;", "var x; x = 100, print(x), 101;"); inFunction("var x; x = 100, print(x), print(x), x = 101;", "var x; x = 100, print(x), print(x), 101;"); inFunction("var x; x = 100, print(x), x = 0, print(x), x = 101;", "var x; x = 100, print(x), x = 0, print(x), 101;"); } public void testMutipleDeadAssignmentsButAlivePartiallyWithinTheExpression() { inFunction("var x; x = 1, x = 2, x = 3, x = 4, x = 5," + " print(x), x = 0, print(x), x = 101;", "var x; 1, 2, 3, 4, x = 5, print(x), x = 0, print(x), 101;"); } public void testDeadPartiallyWithinTheExpression() { // Sadly, this is not covered. We don't suspect this would happen too // often. inFunction("var x; x = 100, x = 101; print(x);"); } public void testAssignmentChain() { inFunction("var a,b,c,d,e; a = b = c = d = e = 1", "var a,b,c,d,e; 1"); inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(c)", "var a,b,c,d,e; c = 1 ; print(c)"); inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a + e)", "var a,b,c,d,e; a = e = 1; print(a + e)"); inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(b + d)", "var a,b,c,d,e; b = d = 1; print(b + d)"); inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a + b + d + e)", "var a,b,c,d,e; a = b = d = e = 1; print(a + b + d + e)"); inFunction("var a,b,c,d,e; a = b = c = d = e = 1; print(a+b+c+d+e)"); } public void testAssignmentOpChain() { inFunction("var a,b,c,d,e; a = b = c += d = e = 1", "var a,b,c,d,e; c + 1"); inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(e)", "var a,b,c,d,e; c + (e = 1); print(e)"); inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(d)", "var a,b,c,d,e; c + (d = 1) ; print(d)"); inFunction("var a,b,c,d,e; a = b = c += d = e = 1; print(a)", "var a,b,c,d,e; a = c + 1; print(a)"); } public void testIncDecInSubExpressions() { inFunction("var a; a = 1, a++; a"); inFunction("var a; a = 1, ++a; a"); inFunction("var a; a = 1, a--; a"); inFunction("var a; a = 1, --a; a"); inFunction("var a; a = 1, a++, print(a)"); inFunction("var a; a = 1, ++a, print(a)"); inFunction("var a; a = 1, a--, print(a)"); inFunction("var a; a = 1, --a, print(a)"); inFunction("var a; a = 1, print(a++)"); inFunction("var a; a = 1, print(++a)"); inFunction("var a; a = 1, print(a++)"); inFunction("var a; a = 1, print(++a)"); inFunction("var a; a = 1, print(a--)"); inFunction("var a; a = 1, print(--a)"); } public void testNestedReassignments() { inFunction("var a; a = (a = 1)", "var a; 1"); inFunction("var a; a = (a *= 2)", "var a; a*2"); // Note a = (a++) is not same as a++. Only if 'a' is dead. inFunction("var a; a = (a++)", "var a; a++"); // Preferred: "var a" inFunction("var a; a = (++a)", "var a; ++a"); // Preferred: "var a" inFunction("var a; a = (b = (a = 1))", "var a; b = 1"); inFunction("var a; a = (b = (a *= 2))", "var a; b = a * 2"); inFunction("var a; a = (b = (a++))", "var a; b=a++"); inFunction("var a; a = (b = (++a))", "var a; b=++a"); // Include b as local. inFunction("var a,b; a = (b = (a = 1))", "var a,b; 1"); inFunction("var a,b; a = (b = (a *= 2))", "var a,b; a * 2"); inFunction("var a,b; a = (b = (a++))", "var a,b; a++"); // Preferred: "var a,b" inFunction("var a,b; a = (b = (++a))", "var a,b; ++a"); // Preferred: "var a,b" inFunction("var a; a += (a++)", "var a; a + a++"); inFunction("var a; a += (++a)", "var a; a+ (++a)"); // Include b as local. inFunction("var a,b; a += (b = (a = 1))", "var a,b; a + 1"); inFunction("var a,b; a += (b = (a *= 2))", "var a,b; a + (a * 2)"); inFunction("var a,b; a += (b = (a++))", "var a,b; a + a++"); inFunction("var a,b; a += (b = (++a))", "var a,b; a+(++a)"); } public void testIncrementalReassignmentInForLoops() { inFunction("for(;x+=1;x+=1) {}"); inFunction("for(;x;x+=1){}"); inFunction("for(;x+=1;){foo(x)}"); inFunction("for(;1;x+=1){foo(x)}"); } public void testIdentityAssignments() { inFunction("var x; x=x", "var x; x"); } private void inFunction(String src) { inFunction(src, src); } private void inFunction(String src, String expected) { test("function FUNC(param1, param2){" + src + "}", "function FUNC(param1, param2){" + expected + "}"); } public void testBug8730257() { inFunction( " try {" + " var sortIndices = {};" + " sortIndices = bar();" + " for (var i = 0; i < 100; i++) {" + " var sortIndex = sortIndices[i];" + " bar(sortIndex);" + " }" + " } finally {" + " bar();" + " }" ); } public void testAssignToExtern() { inFunction("extern = true;"); } public void testIssue297a() { testSame("function f(p) {" + " var x;" + " return ((x=p.id) && (x=parseInt(x.substr(1))) && x>0);" + "}; f('');"); } public void testIssue297b() { test("function f() {" + " var x;" + " return (x='') && (x = x.substr(1));" + "};", "function f() {" + " var x;" + " return (x='') && (x.substr(1));" + "};"); } public void testIssue297c() { test("function f() {" + " var x;" + " return (x=1) && (x = f(x));" + "};", "function f() {" + " var x;" + " return (x=1) && f(x);" + "};"); } public void testIssue297d() { test("function f(a) {" + " return (a=1) && (a = f(a));" + "};", "function f(a) {" + " return (a=1) && (f(a));" + "};"); } public void testIssue297e() { test("function f(a) {" + " return (a=1) - (a = g(a));" + "};", "function f(a) {" + " return (a=1) - (g(a));" + "};"); } public void testIssue297f() { test("function f(a) {" + " h((a=1) - (a = g(a)));" + "};", "function f(a) {" + " h((a=1) - (g(a)));" + "};"); } public void testIssue297g() { test("function f(a) {" + " var b = h((b=1) - (b = g(b)));" + " return b;" + "};", // The last assignment in the initializer should be eliminated "function f(a) {" + " var b = h((b=1) - (b = g(b)));" + " return b;" + "};"); } public void testIssue297h() { test("function f(a) {" + " var b = b=1;" + " return b;" + "};", // The assignment in the initializer should be eliminated "function f(a) {" + " var b = b = 1;" + " return b;" + "};"); } public void testInExpression1() { inFunction("var a; return a=(a=(a=3));", "var a; return 3;"); inFunction("var a; return a=(a=(a=a));", "var a; return a;"); inFunction("var a; return a=(a=(a=a+1)+1);", "var a; return a+1+1;"); inFunction("var a; return a=(a=(a=f(a)+1)+1);", "var a; return f(a)+1+1;"); inFunction("var a; return a=f(a=f(a=f(a)));", "var a; return f(f(f(a)));"); } public void testInExpression2() { // This can be improved. "a = 1" is dead but "a" is read in the following // expression. inFunction( "var a; a = 1; if ((a = 2) || (a = 3) || (a)) {}", "var a; a = 1; if (( 2) || (a = 3) || (a)) {}"); inFunction( "var a; (a = 1) || (a = 2)", "var a; 1 || 2"); inFunction("var a; (a = 1) || (a = 2); return a"); inFunction( "var a; a = 1; a ? a = 2 : a;", "var a; a = 1; a ? 2 : a;"); inFunction("var a; a = 1; a ? a = 2 : a; return a"); inFunction( "var a; a = 1; a ? a : a = 2;", "var a; a = 1; a ? a : 2;"); inFunction("var a; a = 1; a ? a : a =2; return a"); inFunction( "var a; (a = 1) ? a = 2 : a = 3;", "var a; 1 ? 2 : 3;"); // This can be improved. "a = 1" is dead but "a" is read in the following // expression. inFunction("var a; (a = 1) ? a = 2 : a = 3; return a"); } public void testIssue384a() { inFunction( " var a, b;\n" + " if (f(b = true) || f(b = false))\n" + " a = b;\n" + " else\n" + " a = null;\n" + " return a;"); } public void testIssue384b() { inFunction( " var a, b;\n" + " (f(b = true) || f(b = false)) ? (a = b) : (a = null);\n" + " return a;"); } public void testIssue384c() { inFunction( " var a, b;\n" + " (a ? f(b = true) : f(b = false)) && (a = b);\n" + " return a;"); } public void testIssue384d() { inFunction( " var a, b;\n" + " (f(b = true) || f(b = false)) && (a = b);\n" + " return a;"); } public void testForIn() { inFunction("var x = {}; for (var y in x) { y() }"); inFunction("var x, y, z; x = {}; z = {}; for (y in x = z) { y() }", "var x, y, z; ({}); z = {}; for (y in z) { y() }"); inFunction("var x, y, z; x = {}; z = {}; for (y[z=1] in z) { y() }", "var x, y, z; ({}); z = {}; for (y[z=1] in z) { y() }"); // "x in z" doesn't overwrite x if z is empty. // TODO(user): If you look outside of just liveness, x = {} is dead. // That probably requires value numbering or SSA to detect that case. inFunction("var x, y, z; x = {}; z = {}; for (x in z) { x() }"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SpecializeModuleTest.java0000644000175000017500000003724412115204405027777 0ustar apoapo/* * 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.common.collect.ImmutableSet; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.SpecializeModule.SpecializationState; import com.google.javascript.rhino.Node; /** * Tests for {@link SpecializeModule}. * * @author dcc@google.com (Devin Coughlin) */ public class SpecializeModuleTest extends CompilerTestCase { private static final String SHARED_EXTERNS = "var alert = function() {}"; public SpecializeModuleTest() { super(SHARED_EXTERNS); } private PassFactory inlineFunctions = new PassFactory("inlineFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineFunctions(compiler, compiler.getUniqueNameIdSupplier(), true, false, true, true, true); } }; private PassFactory removeUnusedPrototypeProperties = new PassFactory("removeUnusedPrototypeProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedPrototypeProperties(compiler, false, false); } }; private PassFactory devirtualizePrototypeMethods = new PassFactory("devirtualizePrototypeMethods", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DevirtualizePrototypeMethods(compiler); } }; @Override protected CompilerPass getProcessor(final Compiler compiler) { final SpecializeModule specializeModule = new SpecializeModule(compiler, devirtualizePrototypeMethods, inlineFunctions, removeUnusedPrototypeProperties); return new CompilerPass() { @Override public void process(Node externs, Node root) { specializeModule.process(externs, root); /* Make sure variables are declared before used */ new VarCheck(compiler).process(externs, root); } }; } @Override public void setUp() throws Exception { super.setUp(); enableNormalize(); } public void testSpecializeInline() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "var A = function() {alert(B());A()};" + "var B = function() {return 6};" + "A();", // m2 "A();" + "B();" + "B = function() {return 7};" + "A();" + "B();" ); test(modules, new String[] { // m1 "var A = function() {alert(6);A()};" + /* Specialized A */ "A();" + "var B;", // m2 "A = function() {alert(B());A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ "A();" + "B();" + "B = function() {return 7};" + "A();" + "B();" }); } public void testSpecializeCascadedInline() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "var A = function() {alert(B());A()};" + "var B = function() {return C()};" + "var C = function() {return 6};" + "A();", // m2 "B = function() {return 7};" + "A();"); test(modules, new String[] { // m1 "var A = function() {alert(6);A()};" + /* Specialized A */ "A();" + "var B, C;", // m2 "A = function() {alert(B());A()};" + /* Unspecialized A */ "B = function() {return C()};" + /* Removed from m1, so add to m2 */ "C = function() {return 6};" + /* Removed from m1, so add to m2 */ "B = function() {return 7};" + "A();" }); } public void testSpecializeInlineWithMultipleDependents() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "var A = function() {alert(B());A()};" + "var B = function() {return 6};" + "A();", // m2 "B = function() {return 7};" + "A();", // m3 "A();" ); test(modules, new String[] { // m1 "var A = function() {alert(6);A()};" + /* Specialized A */ "A();" + "var B;", // m2 "A = function() {alert(B());A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ "B = function() {return 7};" + "A();", "A = function() {alert(B());A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ "A();", }); } public void testSpecializeInlineWithNamespaces() { JSModule[] modules = createModuleStar( // m1 "var ns = {};" + /* Recursion in A() prevents inline of A*/ "ns.A = function() {alert(B());ns.A()};" + "var B = function() {return 6};" + "ns.A();", // m2 "B = function() {return 7};" + "ns.A();"); test(modules, new String[] { // m1 "var ns = {};" + "ns.A = function() {alert(6);ns.A()};" + /* Specialized A */ "ns.A();" + "var B;", // m2 "ns.A = function() {alert(B());ns.A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ "B = function() {return 7};" + "ns.A();" }); } public void testSpecializeInlineWithRegularFunctions() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "function A() {alert(B());A()}" + "function B() {return 6}" + "A();", // m2 "B = function() {return 7};" + "A();"); test(modules, new String[] { // m1 "function A() {alert(6);A()}" + /* Specialized A */ "A();" + "var B;", // m2 "A = function() {alert(B());A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ /* Start of original m2 */ "B = function() {return 7};" + "A();" }); } public void testDontSpecializeLocalNonAnonymousFunctions() { /* normalize result, but not expected */ enableNormalize(false); JSModule[] modules = createModuleStar( // m1 "(function(){var noSpecialize = " + "function() {alert(6)};noSpecialize()})()", // m2 ""); test(modules, new String[] { // m1 "(function(){var noSpecialize = " + "function() {alert(6)};noSpecialize()})()", // m2 "" }); } public void testAddDummyVarsForRemovedFunctions() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "var A = function() {alert(B() + C());A()};" + "var B = function() {return 6};" + "var C = function() {return 8};" + "A();", // m2 "" + "A();"); test(modules, new String[] { // m1 "var A = function() {alert(6 + 8);A()};" + /* Specialized A */ "A();" + "var B, C;", // m2 "A = function() {alert(B() + C());A()};" + /* Unspecialized A */ "B = function() {return 6};" + /* Removed from m1, so add to m2 */ "C = function() {return 8};" + /* Removed from m1, so add to m2 */ "A();" }); } public void testSpecializeRemoveUnusedProperties() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {this.a()};" + "Foo.prototype.b = function() {return 6};" + "Foo.prototype.c = function() {return 7};" + "var aliasA = Foo.prototype.a;" + // Prevents devirtualization of a "var x = new Foo();" + "x.a();", // m2 ""); test(modules, new String[] { // m1 "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {this.a()};" + "var aliasA = Foo.prototype.a;" + "var x = new Foo();" + "x.a();", // m2 "Foo.prototype.b = function() {return 6};" + "Foo.prototype.c = function() {return 7};" }); } public void testDontSpecializeAliasedFunctions_inline() { JSModule[] modules = createModuleStar( // m1 /* Recursion in A() prevents inline of A*/ "function A() {alert(B());A()}" + "function B() {return 6}" + "var aliasA = A;" + "A();", // m2 "B = function() {return 7};" + "B();"); test(modules, new String[] { // m1 /* Recursion in A() prevents inline of A*/ "function A() {alert(B());A()}" + "function B() {return 6}" + "var aliasA = A;" + "A();", // m2 "B = function() {return 7};" + "B();" }); } public void testDontSpecializeAliasedFunctions_remove_unused_properties() { JSModule[] modules = createModuleStar( // m1 "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {this.a()};" + "Foo.prototype.b = function() {return 6};" + "var aliasB = Foo.prototype.b;" + "Foo.prototype.c = function() {return 7};" + "Foo.prototype.d = function() {return 7};" + "var aliasA = Foo.prototype.a;" + // Prevents devirtualization of a "var x = new Foo();" + "x.a();" + "var aliasC = (new Foo).c", // m2 ""); test(modules, new String[] { // m1 "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {this.a()};" + "Foo.prototype.b = function() {return 6};" + "var aliasB = Foo.prototype.b;" + "Foo.prototype.c = function() {return 7};" + "var aliasA = Foo.prototype.a;" + // Prevents devirtualization of a "var x = new Foo();" + "x.a();" + "var aliasC = (new Foo).c", // m2 "Foo.prototype.d = function() {return 7};" }); } public void testSpecializeDevirtualizePrototypeMethods() { JSModule[] modules = createModuleStar( // m1 "/** @constructor */" + "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {this.a();return 7};" + "Foo.prototype.b = function() {this.a()};" + "var x = new Foo();" + "x.a();", // m2 ""); test(modules, new String[] { // m1 "var Foo = function(){};" + /* constructor */ "var JSCompiler_StaticMethods_a =" + "function(JSCompiler_StaticMethods_a$self) {" + "JSCompiler_StaticMethods_a(JSCompiler_StaticMethods_a$self);" + "return 7" + "};" + "var x = new Foo();" + "JSCompiler_StaticMethods_a(x);", // m2 "Foo.prototype.a = function() {this.a();return 7};" + "Foo.prototype.b = function() {this.a()};" }); } public void testSpecializeDevirtualizePrototypeMethodsWithInline() { JSModule[] modules = createModuleStar( // m1 "/** @constructor */" + "var Foo = function(){};" + /* constructor */ "Foo.prototype.a = function() {return 7};" + "var x = new Foo();" + "var z = x.a();", // m2 ""); test(modules, new String[] { // m1 "var Foo = function(){};" + /* constructor */ "var x = new Foo();" + "var z = 7;", // m2 "Foo.prototype.a = function() {return 7};" }); } /** * Tests for {@link SpecializeModule.SpecializationState}. */ public static class SpecializeModuleSpecializationStateTest extends CompilerTestCase { Compiler lastCompiler; SpecializationState lastState; @Override public CompilerPass getProcessor(final Compiler compiler) { lastCompiler = compiler; return new CompilerPass() { @Override public void process(Node externs, Node root) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); SimpleFunctionAliasAnalysis functionAliasAnalysis = new SimpleFunctionAliasAnalysis(); functionAliasAnalysis.analyze(defFinder); lastState = new SpecializationState(functionAliasAnalysis); } }; } public void testRemovedFunctions() { testSame("function F(){}\nvar G = function(a){};"); assertEquals(ImmutableSet.of(), lastState.getRemovedFunctions()); Node functionF = findFunction("F"); lastState.reportRemovedFunction(functionF, functionF.getParent()); assertEquals(ImmutableSet.of(functionF), lastState.getRemovedFunctions()); Node functionG = findFunction("F"); lastState.reportRemovedFunction(functionG, functionF.getParent()); assertEquals(ImmutableSet.of(functionF, functionG), lastState.getRemovedFunctions()); assertEquals(ImmutableSet.of(), lastState.getSpecializedFunctions()); } public void testSpecializedFunctions() { testSame("function F(){}\nvar G = function(a){};"); assertEquals(ImmutableSet.of(), lastState.getSpecializedFunctions()); Node functionF = findFunction("F"); lastState.reportSpecializedFunction(functionF); assertEquals(ImmutableSet.of(functionF), lastState.getSpecializedFunctions()); Node functionG = findFunction("F"); lastState.reportSpecializedFunction(functionG); assertEquals(ImmutableSet.of(functionF, functionG), lastState.getSpecializedFunctions()); assertEquals(ImmutableSet.of(), lastState.getRemovedFunctions()); } public void testCanFixupFunction() { testSame("function F(){}\n" + "var G = function(a){};\n" + "var ns = {};" + "ns.H = function(){};" + "var ns2 = {I : function anon1(){}};" + "(function anon2(){})();"); assertTrue(lastState.canFixupFunction(findFunction("F"))); assertTrue(lastState.canFixupFunction(findFunction("G"))); assertTrue(lastState.canFixupFunction(findFunction("ns.H"))); assertFalse(lastState.canFixupFunction(findFunction("anon1"))); assertFalse(lastState.canFixupFunction(findFunction("anon2"))); // Can't guarantee safe fixup for aliased functions testSame("function A(){}\n" + "var aliasA = A;\n"); assertFalse(lastState.canFixupFunction(findFunction("A"))); } 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; } } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineObjectLiteralsTest.java0000644000175000017500000003200312115204405030572 0ustar apoapo/* * Copyright 2011 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; /** * Verifies that valid candidates for object literals are inlined as * expected, and invalid candidates are not touched. * */ public class InlineObjectLiteralsTest extends CompilerTestCase { public InlineObjectLiteralsTest() { enableNormalize(); } @Override public void setUp() { super.enableLineNumberCheck(true); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new InlineObjectLiterals( compiler, compiler.getUniqueNameIdSupplier()); } // Test object literal -> variable inlining public void testObject0() { // Don't mess with global variables, that is the job of CollapseProperties. testSame("var a = {x:1}; f(a.x);"); } public void testObject1() { testLocal("var a = {x:x(), y:y()}; f(a.x, a.y);", "var JSCompiler_object_inline_x_0=x();" + "var JSCompiler_object_inline_y_1=y();" + "f(JSCompiler_object_inline_x_0, JSCompiler_object_inline_y_1);"); } public void testObject1a() { testLocal("var a; a = {x:x, y:y}; f(a.x, a.y);", "var JSCompiler_object_inline_x_0;" + "var JSCompiler_object_inline_y_1;" + "(JSCompiler_object_inline_x_0=x," + "JSCompiler_object_inline_y_1=y, true);" + "f(JSCompiler_object_inline_x_0, JSCompiler_object_inline_y_1);"); } public void testObject2() { testLocal("var a = {y:y}; a.x = z; f(a.x, a.y);", "var JSCompiler_object_inline_y_0 = y;" + "var JSCompiler_object_inline_x_1;" + "JSCompiler_object_inline_x_1=z;" + "f(JSCompiler_object_inline_x_1, JSCompiler_object_inline_y_0);"); } public void testObject3() { // Inlining the 'y' would cause the 'this' to be different in the // target function. testSameLocal("var a = {y:y,x:x}; a.y(); f(a.x);"); testSameLocal("var a; a = {y:y,x:x}; a.y(); f(a.x);"); } public void testObject4() { // Object literal is escaped. testSameLocal("var a = {y:y}; a.x = z; f(a.x, a.y); g(a);"); testSameLocal("var a; a = {y:y}; a.x = z; f(a.x, a.y); g(a);"); } public void testObject5() { testLocal("var a = {x:x, y:y}; var b = {a:a}; f(b.a.x, b.a.y);", "var a = {x:x, y:y};" + "var JSCompiler_object_inline_a_0=a;" + "f(JSCompiler_object_inline_a_0.x, JSCompiler_object_inline_a_0.y);"); } public void testObject6() { testLocal("for (var i = 0; i < 5; i++) { var a = {i:i,x:x}; f(a.i, a.x); }", "for (var i = 0; i < 5; i++) {" + " var JSCompiler_object_inline_i_0=i;" + " var JSCompiler_object_inline_x_1=x;" + " f(JSCompiler_object_inline_i_0,JSCompiler_object_inline_x_1)" + "}"); testLocal("if (c) { var a = {i:i,x:x}; f(a.i, a.x); }", "if (c) {" + " var JSCompiler_object_inline_i_0=i;" + " var JSCompiler_object_inline_x_1=x;" + " f(JSCompiler_object_inline_i_0,JSCompiler_object_inline_x_1)" + "}"); } public void testObject7() { testLocal("var a = {x:x, y:f()}; g(a.x);", "var JSCompiler_object_inline_x_0=x;" + "var JSCompiler_object_inline_y_1=f();" + "g(JSCompiler_object_inline_x_0)"); } public void testObject8() { testSameLocal("var a = {x:x,y:y}; var b = {x:y}; f((c?a:b).x);"); testLocal("var a; if(c) { a={x:x, y:y}; } else { a={x:y}; } f(a.x);", "var JSCompiler_object_inline_x_0;" + "var JSCompiler_object_inline_y_1;" + "if(c) JSCompiler_object_inline_x_0=x," + " JSCompiler_object_inline_y_1=y," + " true;" + "else JSCompiler_object_inline_x_0=y," + " JSCompiler_object_inline_y_1=void 0," + " true;" + "f(JSCompiler_object_inline_x_0)"); testLocal("var a = {x:x,y:y}; var b = {x:y}; c ? f(a.x) : f(b.x);", "var JSCompiler_object_inline_x_0 = x; " + "var JSCompiler_object_inline_y_1 = y; " + "var JSCompiler_object_inline_x_2 = y; " + "c ? f(JSCompiler_object_inline_x_0):f(JSCompiler_object_inline_x_2)"); } public void testObject9() { // There is a call, so no inlining testSameLocal("function f(a,b) {" + " var x = {a:a,b:b}; x.a(); return x.b;" + "}"); testLocal("function f(a,b) {" + " var x = {a:a,b:b}; g(x.a); x = {a:a,b:2}; return x.b;" + "}", "function f(a,b) {" + " var JSCompiler_object_inline_a_0 = a;" + " var JSCompiler_object_inline_b_1 = b;" + " g(JSCompiler_object_inline_a_0);" + " JSCompiler_object_inline_a_0 = a," + " JSCompiler_object_inline_b_1=2," + " true;" + " return JSCompiler_object_inline_b_1" + "}"); testLocal("function f(a,b) { " + " var x = {a:a,b:b}; g(x.a); x.b = x.c = 2; return x.b; " + "}", "function f(a,b) { " + " var JSCompiler_object_inline_a_0=a;" + " var JSCompiler_object_inline_b_1=b; " + " var JSCompiler_object_inline_c_2;" + " g(JSCompiler_object_inline_a_0);" + " JSCompiler_object_inline_b_1=JSCompiler_object_inline_c_2=2;" + " return JSCompiler_object_inline_b_1" + "}"); } public void testObject10() { testLocal("var x; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b);", "var JSCompiler_object_inline_a_0;" + "var JSCompiler_object_inline_b_1;" + "var b = f();" + "JSCompiler_object_inline_a_0=a,JSCompiler_object_inline_b_1=b,true;" + "if(JSCompiler_object_inline_a_0) g(JSCompiler_object_inline_b_1)"); testLocal("var x = {}; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b) + x.c", "var x = {}; var b = f(); x = {a:a, b:b}; if(x.a) g(x.b) + x.c"); testLocal("var x; var b = f(); x = {a:a, b:b}; x.c = c; if(x.a) g(x.b) + x.c", "var JSCompiler_object_inline_a_0;" + "var JSCompiler_object_inline_b_1;" + "var JSCompiler_object_inline_c_2;" + "var b = f();" + "JSCompiler_object_inline_a_0 = a,JSCompiler_object_inline_b_1 = b, " + " JSCompiler_object_inline_c_2=void 0,true;" + "JSCompiler_object_inline_c_2 = c;" + "if (JSCompiler_object_inline_a_0)" + " g(JSCompiler_object_inline_b_1) + JSCompiler_object_inline_c_2;"); testLocal("var x = {a:a}; if (b) x={b:b}; f(x.a||x.b);", "var JSCompiler_object_inline_a_0 = a;" + "var JSCompiler_object_inline_b_1;" + "if(b) JSCompiler_object_inline_b_1 = b," + " JSCompiler_object_inline_a_0 = void 0," + " true;" + "f(JSCompiler_object_inline_a_0 || JSCompiler_object_inline_b_1)"); testLocal("var x; var y = 5; x = {a:a, b:b, c:c}; if (b) x={b:b}; f(x.a||x.b);", "var JSCompiler_object_inline_a_0;" + "var JSCompiler_object_inline_b_1;" + "var JSCompiler_object_inline_c_2;" + "var y=5;" + "JSCompiler_object_inline_a_0=a," + "JSCompiler_object_inline_b_1=b," + "JSCompiler_object_inline_c_2=c," + "true;" + "if (b) JSCompiler_object_inline_b_1=b," + " JSCompiler_object_inline_a_0=void 0," + " JSCompiler_object_inline_c_2=void 0," + " true;" + "f(JSCompiler_object_inline_a_0||JSCompiler_object_inline_b_1)"); } public void testObject11() { testSameLocal("var x = {a:b}; (x = {a:a}).c = 5; f(x.a);"); testSameLocal("var x = {a:a}; f(x[a]); g(x[a]);"); } public void testObject12() { testLocal("var a; a = {x:1, y:2}; f(a.x, a.y2);", "var a; a = {x:1, y:2}; f(a.x, a.y2);"); } public void testObject13() { testSameLocal("var x = {a:1, b:2}; x = {a:3, b:x.a};"); } public void testObject14() { testSameLocal("var x = {a:1}; if ('a' in x) { f(); }"); testSameLocal("var x = {a:1}; for (var y in x) { f(y); }"); } public void testObject15() { testSameLocal("x = x || {}; f(x.a);"); } public void testObject16() { testLocal("function f(e) { bar(); x = {a: foo()}; var x; print(x.a); }", "function f(e) { " + " var JSCompiler_object_inline_a_0;" + " bar();" + " JSCompiler_object_inline_a_0 = foo(), true;" + " print(JSCompiler_object_inline_a_0);" + "}"); } public void testObject17() { // Note: Some day, with careful analysis, these two uses could be // disambiguated, and the second assignment could be inlined. testSameLocal( "var a = {a: function(){}};" + "a.a();" + "a = {a1: 100};" + "print(a.a1);"); } public void testObject18() { testSameLocal("var a,b; b=a={x:x, y:y}; f(b.x);"); } public void testObject19() { testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; } else { b=a={x:y}; } f(b.x);"); } public void testObject20() { testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; } else { b=a={x:y}; } f(a.x);"); } public void testObject21() { testSameLocal("var a,b; b=a={x:x, y:y};"); testSameLocal("var a,b; if(c) { b=a={x:x, y:y}; }" + "else { b=a={x:y}; } f(a.x); f(b.x)"); testSameLocal("var a, b; if(c) { if (a={x:x, y:y}) f(); } " + "else { b=a={x:y}; } f(a.x);"); testSameLocal("var a,b; b = (a = {x:x, y:x});"); testSameLocal("var a,b; a = {x:x, y:x}; b = a"); testSameLocal("var a,b; a = {x:x, y:x}; b = x || a"); testSameLocal("var a,b; a = {x:x, y:x}; b = y && a"); testSameLocal("var a,b; a = {x:x, y:x}; b = y ? a : a"); testSameLocal("var a,b; a = {x:x, y:x}; b = y , a"); testSameLocal("b = x || (a = {x:1, y:2});"); } public void testObject22() { testLocal("while(1) { var a = {y:1}; if (b) a.x = 2; f(a.y, a.x);}", "for(;1;){" + " var JSCompiler_object_inline_y_0=1;" + " var JSCompiler_object_inline_x_1;" + " if(b) JSCompiler_object_inline_x_1=2;" + " f(JSCompiler_object_inline_y_0,JSCompiler_object_inline_x_1)" + "}"); testLocal("var a; while (1) { f(a.x, a.y); a = {x:1, y:1};}", "var a; while (1) { f(a.x, a.y); a = {x:1, y:1};}"); } public void testObject23() { testLocal("function f() {\n" + " var templateData = {\n" + " linkIds: {\n" + " CHROME: 'cl',\n" + " DISMISS: 'd'\n" + " }\n" + " };\n" + " var html = templateData.linkIds.CHROME \n" + " + \":\" + templateData.linkIds.DISMISS;\n" + "}", "function f(){" + "var JSCompiler_object_inline_CHROME_1='cl';" + "var JSCompiler_object_inline_DISMISS_2='d';" + "var html=JSCompiler_object_inline_CHROME_1 +" + " ':' +JSCompiler_object_inline_DISMISS_2}"); } public void testObject24() { testLocal("function f() {\n" + " var linkIds = {\n" + " CHROME: 1,\n" + " };\n" + " var g = function () {var o = {a: linkIds};}\n" + "}", "function f(){var linkIds={CHROME:1};" + "var g=function(){var JSCompiler_object_inline_a_0=linkIds}}"); } public void testObject25() { testLocal("var a = {x:f(), y:g()}; a = {y:g(), x:f()}; f(a.x, a.y);", "var JSCompiler_object_inline_x_0=f();" + "var JSCompiler_object_inline_y_1=g();" + "JSCompiler_object_inline_y_1=g()," + " JSCompiler_object_inline_x_0=f()," + " true;" + "f(JSCompiler_object_inline_x_0,JSCompiler_object_inline_y_1)"); } public void testObject26() { testLocal("var a = {}; a.b = function() {}; new a.b.c", "var JSCompiler_object_inline_b_0;" + "JSCompiler_object_inline_b_0=function(){};" + "new JSCompiler_object_inline_b_0.c"); } public void testBug545() { testLocal("var a = {}", ""); testLocal("var a; a = {}", "true"); } public void testIssue724() { testSameLocal( "var getType; getType = {};" + "return functionToCheck && " + " getType.toString.apply(functionToCheck) === " + " '[object Function]';"); } public void testNoInlineDeletedProperties() { testSameLocal( "var foo = {bar:1};" + "delete foo.bar;" + "return foo.bar;"); } private final String LOCAL_PREFIX = "function local(){"; private final String LOCAL_POSTFIX = "}"; private void testLocal(String code, String result) { test(LOCAL_PREFIX + code + LOCAL_POSTFIX, LOCAL_PREFIX + result + LOCAL_POSTFIX); } private void testSameLocal(String code) { testLocal(code, code); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AliasExternalsTest.java0000644000175000017500000003125712115204405027456 0ustar apoapo/* * Copyright 2006 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; /** * Tests for {@link AliasExternals}. * */ public class AliasExternalsTest extends CompilerTestCase { private static String EXTERNS = // Globals "/** @const */ var window;" + "/** @const */ var document;" + "var arguments;var _USER_ID;var ActiveXObject;" + "function eval(x) {}" + // Properties "window.setTimeout;" + "window.eval;" + "props.window;props.innerHTML;props.length;props.prototype;props.length;" + // More globals "/** @noalias */ var RangeObject; " + "var /** @noalias */ RuntimeObject, SelectionObject;" + "/** @noalias */ function NoAliasFunction() {};"; // Blacklist and whitelist of globals. Assign to these before running test // if you want to factor them in to the test, otherwise they will be null. private String unaliasableGlobals; private String aliasableGlobals; public AliasExternalsTest() { super(EXTERNS); } @Override protected int getNumRepetitions() { // This pass only runs once. return 1; } @Override public void setUp() { super.enableLineNumberCheck(false); super.enableNormalize(); unaliasableGlobals = null; aliasableGlobals = null; } /** * Test standard global aliasing. */ public void testGlobalAlias() { test("window.setTimeout(function() {}, 0);" + "var doc=window.document;" + "window.alert(\"foo\");" + "window.eval(\"1\");" + "window.location.href=\"http://www.example.com\";" + "function foo() {var window = \"bar\"; return window}foo();", "var GLOBAL_window=window;" + formatPropNameDecl("setTimeout") + "GLOBAL_window[$$PROP_setTimeout](function() {}, 0);" + "var doc=GLOBAL_window.document;" + "GLOBAL_window.alert(\"foo\");" + "GLOBAL_window.eval(\"1\");" + "GLOBAL_window.location.href=\"http://www.example.com\";" + "function foo() {var window = \"bar\"; return window}foo();"); } /** * Some globals should not be aliased because they have special meaning * within the language (like arguments). */ public void testUnaliasable() { test("function foo() {" + "var x=arguments.length;" + "var y=arguments.length;" + "var z=arguments.length;" + "var w=arguments.length;" + "return x + y + z + w" + "};foo();", formatPropNameDecl("length") + "function foo() {" + "var x=arguments[$$PROP_length];" + "var y=arguments[$$PROP_length];" + "var z=arguments[$$PROP_length];" + "var w=arguments[$$PROP_length];" + "return x + y + z + w" + "};foo();"); test("var x=new ActiveXObject();" + "x.foo=\"bar\";" + "var y=new ActiveXObject();" + "y.foo=\"bar\";" + "var z=new ActiveXObject();" + "z.foo=\"bar\";", "var x=new ActiveXObject();" + "x.foo=\"bar\";" + "var y=new ActiveXObject();" + "y.foo=\"bar\";" + "var z=new ActiveXObject();" + "z.foo=\"bar\";"); test("var _a=eval('foo'),_b=eval('foo'),_c=eval('foo'),_d=eval('foo')," + "_e=eval('foo'),_f=eval('foo'),_g=eval('foo');", "var _a=eval('foo'),_b=eval('foo'),_c=eval('foo'),_d=eval('foo')," + "_e=eval('foo'),_f=eval('foo'),_g=eval('foo');"); } /** * Test using a whitelist to explicitly alias only specific * identifiers. */ public void testAliasableGlobals() { aliasableGlobals = "notused,length"; test("function foo() {" + "var x=arguments.length;" + "var y=arguments.length;" + "var z=arguments.length;" + "var w=arguments.length;" + "return x + y + z + w" + "};foo();", formatPropNameDecl("length") + "function foo() {" + "var x=arguments[$$PROP_length];" + "var y=arguments[$$PROP_length];" + "var z=arguments[$$PROP_length];" + "var w=arguments[$$PROP_length];" + "return x + y + z + w" + "};foo();"); aliasableGlobals = "notused,notlength"; test("function foo() {" + "var x=arguments.length;" + "var y=arguments.length;" + "var z=arguments.length;" + "var w=arguments.length;" + "return x + y + z + w" + "};foo();", "function foo() {" + "var x=arguments.length;" + "var y=arguments.length;" + "var z=arguments.length;" + "var w=arguments.length;" + "return x + y + z + w" + "};foo();"); } /** * Test combined usage of aliasable and unaliasable global lists. */ public void testAliasableAndUnaliasableGlobals() { // Only aliasable provided - OK aliasableGlobals = "foo,bar"; unaliasableGlobals = ""; test("var x;", "var x;"); // Only unaliasable provided - OK aliasableGlobals = ""; unaliasableGlobals = "baz,qux"; test("var x;", "var x;"); // Both provided - bad aliasableGlobals = "foo,bar"; unaliasableGlobals = "baz,qux"; try { test("var x;", "var x;"); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException ex) { // pass } } /** * Global variables that get re-assigned should not be aliased. */ public void testGlobalAssigment() { test("var x=_USER_ID+window;" + "var y=_USER_ID+window;" + "var z=_USER_ID+window;" + "var w=x+y+z;" + "_USER_ID = \"foo\";" + "window++;", "var x=_USER_ID+window;" + "var y=_USER_ID+window;" + "var z=_USER_ID+window;" + "var w=x+y+z;" + "_USER_ID = \"foo\";" + "window++"); } public void testNewOperator() { test("var x;new x(window);window;window;window;window;window", "var GLOBAL_window=window; var x;" + " new x(GLOBAL_window);GLOBAL_window;GLOBAL_window;" + " GLOBAL_window;GLOBAL_window;GLOBAL_window"); } /** * Test the standard replacement for GETPROP */ public void testGetProp() { test("function foo(a,b){return a.length > b.length;}", formatPropNameDecl("length") + "function foo(a, b){return a[$$PROP_length] > b[$$PROP_length];}"); test("Foo.prototype.bar = function() { return 'foo'; }", formatPropNameDecl("prototype") + "Foo[$$PROP_prototype].bar = function() { return 'foo'; }"); test("Foo.notreplaced = 5", "Foo.notreplaced=5"); } /** * Ops that should be ignored */ public void testIgnoredOps() { testSame("function foo() { this.length-- }"); testSame("function foo() { this.length++ }"); testSame("function foo() { this.length+=5 }"); testSame("function foo() { this.length-=5 }"); } /** * Test property setting */ public void testSetProp() { test("function foo() { this.innerHTML = 'hello!'; }", formatSetPropFn("innerHTML") + "function foo() { SETPROP_innerHTML(this, 'hello!'); }"); } /** * Test for modifying both parent and child, as all replacements * are on a single pass and modifying both involves being careful with * references. */ public void testParentChild() { test("a.length = b.length = c.length;", formatSetPropFn("length") + formatPropNameDecl("length") + "SETPROP_length(a, SETPROP_length(b, c[$$PROP_length]))"); } private static final String MODULE_SRC_ONE = "a=b.length;a=b.length;a=b.length;"; private static final String MODULE_SRC_TWO = "c=d.length;"; /** * Test that the code is placed in the first module when there are no * dependencies. */ public void testModulesWithoutDependencies() { test(createModules(MODULE_SRC_ONE, MODULE_SRC_TWO), new String[] { "var $$PROP_length=\"length\";a=b[$$PROP_length];" + "a=b[$$PROP_length];a=b[$$PROP_length];", "c=d[$$PROP_length];"}); } /** * Test that the code is placed in the first module when the second module * depends on the first. */ public void testModulesWithDependencies() { test(createModuleChain(MODULE_SRC_ONE, MODULE_SRC_TWO), new String[] { "var $$PROP_length=\"length\";a=b[$$PROP_length];" + "a=b[$$PROP_length];a=b[$$PROP_length];", "c=d[$$PROP_length];"}); } public void testPropAccessorPushedDeeper1() { test(createModuleChain("var a = \"foo\";", "var b = a.length;"), new String[] { "var a = \"foo\";", formatPropNameDecl("length") + "var b = a[$$PROP_length]" }); } public void testPropAccessorPushedDeeper2() { test(createModuleChain( "var a = \"foo\";", "var b = a.length;", "var c = a.length;"), new String[] { "var a = \"foo\";", formatPropNameDecl("length") + "var b = a[$$PROP_length]", "var c = a[$$PROP_length]" }); } public void testPropAccessorPushedDeeper3() { test(createModuleStar( "var a = \"foo\";", "var b = a.length;", "var c = a.length;"), new String[] { formatPropNameDecl("length") + "var a = \"foo\";", "var b = a[$$PROP_length]", "var c = a[$$PROP_length]" }); } public void testPropAccessorNotPushedDeeper() { test(createModuleChain("var a = \"foo\"; var b = a.length;", "var c = a.length;"), new String[] { formatPropNameDecl("length") + "var a = \"foo\"; var b = a[$$PROP_length]", "var c = a[$$PROP_length]" }); } public void testPropMutatorPushedDeeper() { test(createModuleChain("var a = [1];", "a.length = 0;"), new String[] { "var a = [1];", formatSetPropFn("length") + "SETPROP_length(a, 0);" }); } public void testPropMutatorNotPushedDeeper() { test(createModuleChain( "var a = [1]; a.length = 1;", "a.length = 0;"), new String[] { formatSetPropFn("length") + "var a = [1]; SETPROP_length(a, 1);", "SETPROP_length(a, 0);" }); } public void testGlobalAliasPushedDeeper() { test(createModuleChain( "var a = 1;", "var b = window, c = window, d = window, e = window;"), new String[] { "var a = 1;", "var GLOBAL_window = window;" + "var b = GLOBAL_window, c = GLOBAL_window, " + " d = GLOBAL_window, e = GLOBAL_window;" }); } public void testGlobalAliasNotPushedDeeper() { test(createModuleChain( "var a = 1, b = window;", "var c = window, d = window, e = window;"), new String[] { "var GLOBAL_window = window;" + "var a = 1, b = GLOBAL_window;", "var c = GLOBAL_window, " + " d = GLOBAL_window, e = GLOBAL_window;" }); } public void testNoAliasAnnotationForSingleVar() { testSame("[RangeObject, RangeObject, RangeObject]"); } public void testNoAliasAnnotationForMultiVarDeclaration() { test("[RuntimeObject, RuntimeObject, RuntimeObject," + " SelectionObject, SelectionObject, SelectionObject]", "var GLOBAL_SelectionObject = SelectionObject;" + "[RuntimeObject, RuntimeObject, RuntimeObject," + " GLOBAL_SelectionObject, GLOBAL_SelectionObject," + " GLOBAL_SelectionObject]"); } public void testNoAliasAnnotationForFunction() { testSame("[NoAliasFunction(), NoAliasFunction(), NoAliasFunction()]"); } private String formatPropNameDecl(String prop) { return "var $$PROP_" + prop + "='" + prop + "';"; } private String formatSetPropFn(String prop) { String mutatorName = "SETPROP_" + prop; String arg1 = mutatorName + "$a"; String arg2 = mutatorName + "$b"; return "function " + mutatorName + "(" + arg1 + "," + arg2 + ") {" + "return " + arg1 + "." + prop + "=" + arg2 + ";}"; } @Override protected CompilerPass getProcessor(Compiler compiler) { AliasExternals ae = new AliasExternals( compiler, compiler.getModuleGraph(), unaliasableGlobals, aliasableGlobals); ae.setRequiredUsage(1); return ae; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/regex/0000755000175000017500000000000012115204405024136 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/regex/CharRangesTest.java0000644000175000017500000001671212115204405027665 0ustar apoapo/* * Copyright 2011 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.regex; import java.util.BitSet; import java.util.Random; import com.google.javascript.jscomp.regex.CharRanges; import junit.framework.TestCase; public class CharRangesTest extends TestCase { static final long SEED = Long.parseLong(System.getProperty( "junit.random.seed", "" + System.currentTimeMillis())); public final void testAgainstRegularImplementation() { Random rnd = new Random(SEED); for (int run = 10; --run >= 0;) { // Fill with bits in the range [0x1000, 0x3000). BitSet bs = new BitSet(); for (int i = 0x1000; --i >= 0;) { bs.set(0x1000 + rnd.nextInt(0x3000)); } // Create an equivalent sparse bit set int[] members = new int[bs.cardinality()]; for (int i = -1, k = 0; k < members.length; ++k) { members[k] = i = bs.nextSetBit(i + 1); } CharRanges sbs = CharRanges.withMembers(members); // Check all bits including past the min/max bit for (int i = 0; i < 0x5000; ++i) { if (bs.get(i) != sbs.contains(i)) { fail("sbs=" + sbs + ", bs=" + bs + ", difference at bit " + i); } } } } public final void testEmptyCharRanges() { CharRanges sbs = CharRanges.EMPTY; for (int i = -1000; i < 1000; ++i) { assertFalse(sbs.contains(i)); } assertEquals("[]", sbs.toString()); } public final void testCharRangesFactories() { CharRanges isbs = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); CharRanges isbs2 = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); assertEquals("[0x0-0x1 0x4 0x9]", isbs.toString()); CharRanges esbs = CharRanges.withMembers(new int[0]); assertEquals(isbs, isbs); assertEquals(isbs, isbs2); assertFalse(isbs.equals(esbs)); assertFalse(isbs.equals(null)); assertFalse(isbs.equals(new Object())); assertEquals(isbs.hashCode(), isbs2.hashCode()); assertFalse(isbs.hashCode() == esbs.hashCode()); } public final void testRangeConstructor() { try { CharRanges.withRanges(new int[] { 1 }); fail("Mismatched ranges"); } catch (IllegalArgumentException ex) { // pass } try { CharRanges.withRanges(new int[] { 1, 4, 4, 5 }); fail("Discontiguous ranges"); } catch (IllegalArgumentException ex) { // pass } try { CharRanges.withRanges(new int[] { 4, 5, 1, 3 }); fail("Misordered ranges"); } catch (IllegalArgumentException ex) { // pass } try { CharRanges.withRanges(new int[] { 0, 0 }); fail("Empty range"); } catch (IllegalArgumentException ex) { // pass } } public final void testDupeMembers() { CharRanges sbs1 = CharRanges.withMembers(new int[] { 0, 1, 4, 9 }); assertEquals(sbs1.toString(), "[0x0-0x1 0x4 0x9]", sbs1.toString()); CharRanges sbs2 = CharRanges.withMembers(new int[] { 9, 1, 4, 1, 0 }); assertEquals(sbs2.toString(), "[0x0-0x1 0x4 0x9]", sbs2.toString()); assertEquals(sbs1, sbs2); assertEquals(sbs1.hashCode(), sbs2.hashCode()); for (int i = -10; i < 20; ++i) { assertEquals("" + i, sbs1.contains(i), sbs2.contains(i)); } } public final void testDifference() { // 1 2 3 // 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0 // b-a DD DD DDD D DDD // a AAAAAAAAA A A A A A AAA AAA A A // b BBB BBB BBB BBB B B BBB // a-b DD DD D D D D DDD DDD D D CharRanges a = CharRanges.withRanges(new int[] { 0x03, 0x0C, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1C, 0x1D, 0x1E, 0x21, 0x24, 0x27, 0x28, 0x29, 0x2A, 0x2B }); CharRanges b = CharRanges.withRanges(new int[] { 0x01, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x0F, 0x12, 0x1A, 0x1B, 0x1C, 0x1D, 0x21, 0x24 }); CharRanges empty = CharRanges.withMembers(new int[0]); assertEquals(empty, empty.union(empty)); assertEquals(a, a.union(empty)); assertEquals(b, empty.union(b)); CharRanges aSb = a.difference(b); assertEquals( "[0x4-0x5 0x9-0xa 0x12 0x14 0x16 0x18 0x1e-0x20 0x24-0x26 0x28 0x2a]", aSb.toString()); assertTrue(a.containsAll(aSb)); assertFalse(aSb.containsAll(a)); assertFalse(aSb.containsAll(b)); CharRanges bSa = b.difference(a); assertEquals( "[0x1-0x2 0xc-0xd 0xf-0x11 0x1a 0x21-0x23]", bSa.toString()); assertTrue(b.containsAll(bSa)); assertFalse(bSa.containsAll(a)); assertFalse(bSa.containsAll(b)); // Check that a and b not changed by operation assertEquals( "[0x3-0xb 0x12 0x14 0x16 0x18 0x1c 0x1e-0x20 0x24-0x26 0x28 0x2a]", a.toString()); assertEquals( "[0x1-0x3 0x6-0x8 0xb-0xd 0xf-0x11 0x1a 0x1c 0x21-0x23]", b.toString()); // 0 1 2 3 4 5 6 7 8 9 a b c d e f // m: * * * * * * * * * // s: * * * * * * * * // d: * * * * * CharRanges m = CharRanges.withMembers(0, 1, 2, 3, 6, 9, 0xa, 0xe, 0xf); CharRanges s = CharRanges.withMembers(2, 5, 6, 7, 0xa, 0xb, 0xd, 0xe); CharRanges d = m.difference(s); assertEquals("[0x0-0x1 0x3 0x9 0xf]", d.toString()); assertTrue(m.containsAll(d)); assertFalse(d.containsAll(m)); assertFalse(d.containsAll(s)); assertFalse(s.containsAll(d)); assertTrue(d.containsAll(d)); } public final void testUnion() { // 1 2 3 // 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0 // AAAAAAAAA A A A A A AAA AAA A A // BBB BBB BBB BBB B B BBB // UUUUUUUUUUUUU UUUU U U U U U UUUUUUUUU U U CharRanges a = CharRanges.withRanges(new int[] { 0x03, 0x0C, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1C, 0x1D, 0x1E, 0x21, 0x24, 0x27, 0x28, 0x29, 0x2A, 0x2B }); CharRanges b = CharRanges.withRanges(new int[] { 0x01, 0x04, 0x06, 0x09, 0x0B, 0x0E, 0x0F, 0x12, 0x1A, 0x1B, 0x1C, 0x1D, 0x21, 0x24 }); CharRanges empty = CharRanges.withMembers(new int[0]); assertEquals(empty, empty.union(empty)); assertEquals(a, a.union(empty)); assertEquals(b, empty.union(b)); CharRanges aUb = a.union(b); assertEquals( "[0x1-0xd 0xf-0x12 0x14 0x16 0x18 0x1a 0x1c 0x1e-0x26 0x28 0x2a]", aUb.toString()); assertEquals(aUb, b.union(a)); assertTrue(aUb.containsAll(a)); assertTrue(aUb.containsAll(b)); assertFalse(a.containsAll(b)); assertFalse(b.containsAll(a)); assertTrue(a.containsAll(a)); assertTrue(b.containsAll(b)); assertTrue(aUb.containsAll(aUb)); // Check that a and b not changed by operation assertEquals( "[0x3-0xb 0x12 0x14 0x16 0x18 0x1c 0x1e-0x20 0x24-0x26 0x28 0x2a]", a.toString()); assertEquals( "[0x1-0x3 0x6-0x8 0xb-0xd 0xf-0x11 0x1a 0x1c 0x21-0x23]", b.toString()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NameAnonymousFunctionsTest.java0000644000175000017500000001073512115204405031217 0ustar apoapo/* * Copyright 2006 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; /** * Unit test for {@link NameAnonymousFunctionsTest}. * */ public class NameAnonymousFunctionsTest extends CompilerTestCase { private static final String EXTERNS = "var document;"; public NameAnonymousFunctionsTest() { super(EXTERNS); } @Override public CompilerPass getProcessor(Compiler compiler) { return new NameAnonymousFunctions(compiler); } public void testSimpleVarAssignment() { test("var a = function() { return 1; }", "var a = function $a$() { return 1; }"); } public void testAssignmentToProperty() { test("var a = {}; a.b = function() { return 1; }", "var a = {}; a.b = function $a$b$() { return 1; }"); } public void testAssignmentToPrototype() { test("function a() {} a.prototype.b = function() { return 1; };", "function a() {} " + "a.prototype.b = function $a$$b$() { return 1; };"); } public void testAssignmentToPrototype2() { test("var a = {}; " + "a.b = function() {}; " + "a.b.prototype.c = function() { return 1; };", "var a = {}; " + "a.b = function $a$b$() {}; " + "a.b.prototype.c = function $a$b$$c$() { return 1; };"); } public void testAssignmentToPrototype3() { test("function a() {} a.prototype['b'] = function() { return 1; };", "function a() {} " + "a.prototype['b'] = function $a$$b$() { return 1; };"); } public void testAssignmentToPrototype4() { test("function a() {} a['prototype']['b'] = function() { return 1; };", "function a() {} " + "a['prototype']['b'] = function $a$$b$() { return 1; };"); } public void testPrototypeInitializer() { test("function a(){} a.prototype = {b: function() { return 1; }};", "function a(){} " + "a.prototype = {b: function $a$$b$() { return 1; }};"); } public void testMultiplePrototypeInitializer() { test("function a(){} a.prototype = {b: function() { return 1; }, " + "c: function() { return 2; }};", "function a(){} " + "a.prototype = {b: function $a$$b$() { return 1; }," + "c: function $a$$c$() { return 2; }};"); } public void testRecursiveObjectLiteral() { test("function a(){} a.prototype = {b: {c: function() { return 1; }}}", "function a(){}a.prototype={b:{c:function $a$$b$c$(){return 1}}}"); } public void testAssignmentToPropertyOfCallReturnValue() { test("document.getElementById('x').onClick = function() {};", "document.getElementById('x').onClick = " + "function $document$getElementById$onClick$() {};"); } public void testAssignmentToPropertyOfArrayElement() { test("var a = {}; a.b = [{}]; a.b[0].c = function() {};", "var a = {}; a.b = [{}]; a.b[0].c = function $a$b$0$c$() {};"); test("var a = {b: {'c': {}}}; a.b['c'].d = function() {};", "var a = {b: {'c': {}}}; a.b['c'].d = function $a$b$c$d$() {};"); test("var a = {b: {'c': {}}}; a.b[x()].d = function() {};", "var a = {b: {'c': {}}}; a.b[x()].d = function $a$b$x$d$() {};"); } public void testAssignmentToGetElem() { test("function f() {win['x' + this.id] = function(a){};}", "function f() {win['x' + this.id] = function $win$x$this$id$(a){};}"); } public void testGetElemWithDashes() { test("var foo = {}; foo['-'] = function() {};", "var foo = {}; foo['-'] = function $foo$__0$() {};"); } public void testWhatCausedIeToFail() { // If the function was given the name main, for some reason IE failed to // handle this case properly. That's why we give it the name $main$. FF // handled the case fine. test("var main;" + "(function() {" + " main = function() {" + " return 5;" + " };" + "})();" + "" + "main();", "var main;(function(){main=function $main$(){return 5}})();main()"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/BasicErrorManagerTest.java0000644000175000017500000000750612115204405030065 0ustar apoapo/* * Copyright 2007 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.BasicErrorManager.ErrorWithLevel; import com.google.javascript.jscomp.BasicErrorManager.LeveledJSErrorComparator; import com.google.javascript.jscomp.CheckLevel; import junit.framework.TestCase; /** * Tests {@link BasicErrorManager}. * */ public class BasicErrorManagerTest extends TestCase { private static final String NULL_SOURCE = null; private LeveledJSErrorComparator comparator = new LeveledJSErrorComparator(); static final CheckLevel E = CheckLevel.ERROR; private static final DiagnosticType FOO_TYPE = DiagnosticType.error("TEST_FOO", "Foo"); private static final DiagnosticType JOO_TYPE = DiagnosticType.error("TEST_JOO", "Joo"); public void testOrderingBothNull() throws Exception { assertEquals(0, comparator.compare(null, null)); } public void testOrderingSourceName1() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make("a", -1, -1, FOO_TYPE); assertSmaller(error(e1), error(e2)); } public void testOrderingSourceName2() throws Exception { JSError e1 = JSError.make("a", -1, -1, FOO_TYPE); JSError e2 = JSError.make("b", -1, -1, FOO_TYPE); assertSmaller(error(e1), error(e2)); } public void testOrderingLineno1() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 2, -1, FOO_TYPE); assertSmaller(error(e1), error(e2)); } public void testOrderingLineno2() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, 8, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 56, -1, FOO_TYPE); assertSmaller(error(e1), error(e2)); } public void testOrderingCheckLevel() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); assertSmaller(warning(e1), error(e2)); } public void testOrderingCharno1() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, 5, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 5, 2, FOO_TYPE); assertSmaller(error(e1), error(e2)); // CheckLevel preempts charno comparison assertSmaller(warning(e1), error(e2)); } public void testOrderingCharno2() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, 8, 7, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, 8, 5, FOO_TYPE); assertSmaller(error(e2), error(e1)); // CheckLevel preempts charno comparison assertSmaller(warning(e2), error(e1)); } public void testOrderingDescription() throws Exception { JSError e1 = JSError.make(NULL_SOURCE, -1, -1, FOO_TYPE); JSError e2 = JSError.make(NULL_SOURCE, -1, -1, JOO_TYPE); assertSmaller(error(e1), error(e2)); } private ErrorWithLevel error(JSError e) { return new ErrorWithLevel(e, CheckLevel.ERROR); } private ErrorWithLevel warning(JSError e) { return new ErrorWithLevel(e, CheckLevel.WARNING); } private void assertSmaller(ErrorWithLevel p1, ErrorWithLevel p2) { int p1p2 = comparator.compare(p1, p2); assertTrue(Integer.toString(p1p2), p1p2 < 0); int p2p1 = comparator.compare(p2, p1); assertTrue(Integer.toString(p2p1), p2p1 > 0); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ProcessTweaksTest.java0000644000175000017500000002614012115204405027327 0ustar apoapo/* * 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.common.collect.Maps; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collections; import java.util.Map; import java.util.Set; /** * @author agrieve@google.com (Andrew Grieve) */ public class ProcessTweaksTest extends CompilerTestCase { Map defaultValueOverrides; boolean stripTweaks; public ProcessTweaksTest() { super("function alert(arg) {}"); } @Override public void setUp() throws Exception { super.setUp(); defaultValueOverrides = Maps.newHashMap(); stripTweaks = false; } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ProcessTweaks processTweak = new ProcessTweaks(compiler, stripTweaks, defaultValueOverrides); processTweak.process(externs, root); if (stripTweaks) { Set emptySet = Collections.emptySet(); final StripCode stripCode = new StripCode(compiler, emptySet, emptySet, emptySet, emptySet); stripCode.enableTweakStripping(); stripCode.process(externs, root); } } }; } @Override protected int getNumRepetitions() { // Only do one repetition, so that we can make sure the first pass keeps // GlobalNamespace up to date. return 1; } public void testBasicTweak1() { testSame("goog.tweak.registerBoolean('Foo', 'Description');" + "goog.tweak.getBoolean('Foo')"); } public void testBasicTweak2() { testSame("goog.tweak.registerString('Foo', 'Description');" + "goog.tweak.getString('Foo')"); } public void testBasicTweak3() { testSame("goog.tweak.registerNumber('Foo', 'Description');" + "goog.tweak.getNumber('Foo')"); } public void testBasicTweak4() { testSame("goog.tweak.registerButton('Foo', 'Description', function() {})"); } public void testBasicTweak5() { testSame("goog.tweak.registerBoolean('A.b_7', 'Description', true, " + "{ requiresRestart:false })"); } public void testBasicTweak6() { testSame("var opts = { requiresRestart:false };" + "goog.tweak.registerBoolean('Foo', 'Description', true, opts)"); } public void testNonLiteralId1() { test("goog.tweak.registerBoolean(3, 'Description')", null, ProcessTweaks.NON_LITERAL_TWEAK_ID_ERROR); } public void testNonLiteralId2() { test("goog.tweak.getBoolean('a' + 'b')", null, ProcessTweaks.NON_LITERAL_TWEAK_ID_ERROR); } public void testNonLiteralId3() { test("var CONST = 'foo'; goog.tweak.overrideDefaultValue(CONST, 3)", null, ProcessTweaks.NON_LITERAL_TWEAK_ID_ERROR); } public void testInvalidId() { test("goog.tweak.registerBoolean('Some ID', 'a')", null, ProcessTweaks.INVALID_TWEAK_ID_ERROR); } public void testInvalidDefaultValue1() { testSame("var val = true; goog.tweak.registerBoolean('Foo', 'desc', val)", ProcessTweaks.INVALID_TWEAK_DEFAULT_VALUE_WARNING); } public void testInvalidDefaultValue2() { testSame("goog.tweak.overrideDefaultValue('Foo', 3 + 1);" + "goog.tweak.registerNumber('Foo', 'desc')", ProcessTweaks.INVALID_TWEAK_DEFAULT_VALUE_WARNING); } public void testUnknownGetString() { testSame("goog.tweak.getString('huh')", ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testUnknownGetNumber() { testSame("goog.tweak.getNumber('huh')", ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testUnknownGetBoolean() { testSame("goog.tweak.getBoolean('huh')", ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testUnknownOverride() { testSame("goog.tweak.overrideDefaultValue('huh', 'val')", ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testDuplicateTweak() { test("goog.tweak.registerBoolean('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakA', 'desc')", null, ProcessTweaks.TWEAK_MULTIPLY_REGISTERED_ERROR); } public void testOverrideAfterRegister() { test("goog.tweak.registerBoolean('TweakA', 'desc');" + "goog.tweak.overrideDefaultValue('TweakA', 'val')", null, ProcessTweaks.TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR); } public void testRegisterInNonGlobalScope() { test("function foo() {goog.tweak.registerBoolean('TweakA', 'desc');};", null, ProcessTweaks.NON_GLOBAL_TWEAK_INIT_ERROR); } public void testWrongGetter1() { testSame("goog.tweak.registerBoolean('TweakA', 'desc');" + "goog.tweak.getString('TweakA')", ProcessTweaks.TWEAK_WRONG_GETTER_TYPE_WARNING); } public void testWrongGetter2() { testSame("goog.tweak.registerString('TweakA', 'desc');" + "goog.tweak.getNumber('TweakA')", ProcessTweaks.TWEAK_WRONG_GETTER_TYPE_WARNING); } public void testWrongGetter3() { testSame("goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.getBoolean('TweakA')", ProcessTweaks.TWEAK_WRONG_GETTER_TYPE_WARNING); } public void testWithNoTweaks() { testSame("var DEF=true;var x={};x.foo={}"); } public void testStrippingWithImplicitDefaultValues() { stripTweaks = true; test("goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc');" + "goog.tweak.registerString('TweakC', 'desc');" + "alert(goog.tweak.getNumber('TweakA'));" + "alert(goog.tweak.getBoolean('TweakB'));" + "alert(goog.tweak.getString('TweakC'));", "void 0; void 0; void 0; alert(0); alert(false); alert('')"); } public void testStrippingWithExplicitDefaultValues() { stripTweaks = true; test("goog.tweak.registerNumber('TweakA', 'desc', 5);" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', '!');" + "alert(goog.tweak.getNumber('TweakA'));" + "alert(goog.tweak.getBoolean('TweakB'));" + "alert(goog.tweak.getString('TweakC'));", "void 0; void 0; void 0; alert(5); alert(true); alert('!')"); } public void testStrippingWithInCodeOverrides() { stripTweaks = true; test("goog.tweak.overrideDefaultValue('TweakA', 5);" + "goog.tweak.overrideDefaultValue('TweakB', true);" + "goog.tweak.overrideDefaultValue('TweakC', 'bar');" + "goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc');" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "alert(goog.tweak.getNumber('TweakA'));" + "alert(goog.tweak.getBoolean('TweakB'));" + "alert(goog.tweak.getString('TweakC'));", "void 0; void 0; void 0; void 0; void 0; void 0;" + "alert(5); alert(true); alert('bar');"); } public void testStrippingWithUnregisteredTweak1() { stripTweaks = true; test("alert(goog.tweak.getNumber('TweakA'));", "alert(0)", null, ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testStrippingWithUnregisteredTweak2() { stripTweaks = true; test("alert(goog.tweak.getBoolean('TweakB'))", "alert(false)", null, ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testStrippingWithUnregisteredTweak3() { stripTweaks = true; test("alert(goog.tweak.getString('TweakC'))", "alert('')", null, ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testStrippingOfManuallyRegistered1() { stripTweaks = true; test("var reg = goog.tweak.getRegistry();" + "if (reg) {" + " reg.register(new goog.tweak.BooleanSetting('foo', 'desc'));" + " reg.getEntry('foo').setDefaultValue(1);" + "}", "if (null);"); } public void testOverridesWithStripping() { stripTweaks = true; defaultValueOverrides.put("TweakA", Node.newNumber(1)); defaultValueOverrides.put("TweakB", new Node(Token.FALSE)); defaultValueOverrides.put("TweakC", Node.newString("!")); test("goog.tweak.overrideDefaultValue('TweakA', 5);" + "goog.tweak.overrideDefaultValue('TweakC', 'bar');" + "goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "alert(goog.tweak.getNumber('TweakA'));" + "alert(goog.tweak.getBoolean('TweakB'));" + "alert(goog.tweak.getString('TweakC'));", "void 0; void 0; void 0; void 0; void 0; " + "alert(1); alert(false); alert('!')"); } public void testCompilerOverridesNoStripping1() { defaultValueOverrides.put("TweakA", Node.newNumber(1)); defaultValueOverrides.put("TweakB", new Node(Token.FALSE)); defaultValueOverrides.put("TweakC", Node.newString("!")); test("goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "var a = goog.tweak.getCompilerOverrides_()", "goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "var a = { TweakA: 1, TweakB: false, TweakC: '!' };"); } public void testCompilerOverridesNoStripping2() { defaultValueOverrides.put("TweakA", Node.newNumber(1)); defaultValueOverrides.put("TweakB", new Node(Token.FALSE)); defaultValueOverrides.put("TweakC", Node.newString("!")); test("goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "var a = goog.tweak.getCompilerOverrides_();" + "var b = goog.tweak.getCompilerOverrides_()", "goog.tweak.registerNumber('TweakA', 'desc');" + "goog.tweak.registerBoolean('TweakB', 'desc', true);" + "goog.tweak.registerString('TweakC', 'desc', 'foo');" + "var a = { TweakA: 1, TweakB: false, TweakC: '!' };" + "var b = { TweakA: 1, TweakB: false, TweakC: '!' };"); } public void testUnknownCompilerOverride() { allowSourcelessWarnings(); defaultValueOverrides.put("TweakA", Node.newString("!")); testSame("var a", ProcessTweaks.UNKNOWN_TWEAK_WARNING); } public void testCompilerOverrideWithWrongType() { allowSourcelessWarnings(); defaultValueOverrides.put("TweakA", Node.newString("!")); testSame("goog.tweak.registerBoolean('TweakA', 'desc')", ProcessTweaks.INVALID_TWEAK_DEFAULT_VALUE_WARNING); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/JsMessageTest.java0000644000175000017500000000356212115204405026416 0ustar apoapo/* * 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 junit.framework.*; /** * @author anatol@google.com (Anatol Pomazau) */ public class JsMessageTest extends TestCase { public void testIsEmpty() { assertTrue(new JsMessage.Builder().build().isEmpty()); assertTrue(new JsMessage.Builder().appendStringPart("").build().isEmpty()); assertTrue(new JsMessage.Builder().appendStringPart("") .appendStringPart("").build().isEmpty()); assertFalse(new JsMessage.Builder().appendStringPart("s") .appendStringPart("").build().isEmpty()); assertFalse(new JsMessage.Builder().appendPlaceholderReference("3") .build().isEmpty()); } public void testMeaningChangesId() { String id1 = new JsMessage.Builder() .appendStringPart("foo").build().getId(); String id2 = new JsMessage.Builder() .appendStringPart("foo").setMeaning("bar").build().getId(); assertFalse(id1.equals(id2)); } public void testHashValues() { final String EMPTY = ""; final String VAL = "Hello, world"; final long ANSWER_STRING_64 = 0x43ec5d9731515874L; final long ANSWER_EMPTY_64 = 0x468d9ea2c42361aaL; assertEquals(ANSWER_STRING_64, JsMessage.Hash.hash64(VAL)); assertEquals(ANSWER_EMPTY_64, JsMessage.Hash.hash64(EMPTY)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ProcessDefinesTest.java0000644000175000017500000002554512115204405027456 0ustar apoapo/* * Copyright 2007 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.javascript.jscomp.SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR; import com.google.common.collect.Maps; import com.google.javascript.jscomp.GlobalNamespace.Name; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * @author nicksantos@google.com (Nick Santos) */ public class ProcessDefinesTest extends CompilerTestCase { public ProcessDefinesTest() { super("var externMethod;"); // ProcessDefines emits warnings if the user tries to re-define a constant, // but the constant is not defined anywhere in the binary. allowSourcelessWarnings(); } private Map overrides = Maps.newHashMap(); private GlobalNamespace namespace; @Override public void setUp() throws Exception { super.setUp(); overrides.clear(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ProcessDefinesWithInjectedNamespace(compiler); } @Override protected int getNumRepetitions() { // Only do one repetition, so that we can make sure the first pass keeps // GlobalNamespace up to date. return 1; } /** * Helper for tests that expects definitions to remain unchanged, such * that {@code definitions+js} is converted to {@code definitions+expected}. */ private void testWithPrefix(String definitions, String js, String expected) { test(definitions + js, definitions + expected); } public void testBasicDefine1() { test("/** @define {boolean} */ var DEF = true", "var DEF=true"); } public void testBasicDefine2() { test("/** @define {string} */ var DEF = 'a'", "var DEF=\"a\""); } public void testBasicDefine3() { test("/** @define {number} */ var DEF = 0", "var DEF=0"); } public void testDefineBadType() { test("/** @define {Object} */ var DEF = {}", null, ProcessDefines.INVALID_DEFINE_TYPE_ERROR); } public void testDefineWithBadValue1() { test("/** @define {boolean} */ var DEF = new Boolean(true);", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testDefineWithBadValue2() { test("/** @define {string} */ var DEF = 'x' + y;", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testDefineWithDependentValue() { test("/** @define {boolean} */ var BASE = false;\n" + "/** @define {boolean} */ var DEF = !BASE;", "var BASE=false;var DEF=!BASE"); test("var a = {};\n" + "/** @define {boolean} */ a.BASE = false;\n" + "/** @define {boolean} */ a.DEF = !a.BASE;", "var a={};a.BASE=false;a.DEF=!a.BASE"); } public void testDefineWithInvalidDependentValue() { test("var BASE = false;\n" + "/** @define {boolean} */ var DEF = !BASE;", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testOverriding1() { overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE)); overrides.put("DEF_OVERRIDE_TO_FALSE", new Node(Token.FALSE)); test( "/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = false;" + "/** @define {boolean} */ var DEF_OVERRIDE_TO_FALSE = true", "var DEF_OVERRIDE_TO_TRUE=true;var DEF_OVERRIDE_TO_FALSE=false"); } public void testOverriding2() { overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE)); String normalConst = "var DEF_OVERRIDE_TO_FALSE=true;"; testWithPrefix( normalConst, "/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = false", "var DEF_OVERRIDE_TO_TRUE=true"); } public void testOverriding3() { overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE)); test( "/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = true;", "var DEF_OVERRIDE_TO_TRUE=true"); } public void testOverridingString0() { test( "/** @define {string} */ var DEF_OVERRIDE_STRING = 'x';", "var DEF_OVERRIDE_STRING=\"x\""); } public void testOverridingString1() { test( "/** @define {string} */ var DEF_OVERRIDE_STRING = 'x' + 'y';", "var DEF_OVERRIDE_STRING=\"x\" + \"y\""); } public void testOverridingString2() { overrides.put("DEF_OVERRIDE_STRING", Node.newString("foo")); test( "/** @define {string} */ var DEF_OVERRIDE_STRING = 'x';", "var DEF_OVERRIDE_STRING=\"foo\""); } public void testOverridingString3() { overrides.put("DEF_OVERRIDE_STRING", Node.newString("foo")); test( "/** @define {string} */ var DEF_OVERRIDE_STRING = 'x' + 'y';", "var DEF_OVERRIDE_STRING=\"foo\""); } public void testMisspelledOverride() { overrides.put("DEF_BAD_OVERIDE", new Node(Token.TRUE)); test("/** @define {boolean} */ var DEF_BAD_OVERRIDE = true", "var DEF_BAD_OVERRIDE=true", null, ProcessDefines.UNKNOWN_DEFINE_WARNING); } public void testCompiledIsKnownDefine() { overrides.put("COMPILED", new Node(Token.TRUE)); testSame(""); } public void testSimpleReassign1() { test("/** @define {boolean} */ var DEF = false; DEF = true;", "var DEF=true;true"); } public void testSimpleReassign2() { test("/** @define {number|boolean} */ var DEF=false;DEF=true;DEF=3", "var DEF=3;true;3"); Name def = namespace.getNameIndex().get("DEF"); assertEquals(1, def.getRefs().size()); assertEquals(1, def.globalSets); assertNotNull(def.getDeclaration()); } public void testSimpleReassign3() { test("/** @define {boolean} */ var DEF = false;var x;x = DEF = true;", "var DEF=true;var x;x=true"); } public void testDuplicateVar() { test("/** @define {boolean} */ var DEF = false; var DEF = true;", null, VAR_MULTIPLY_DECLARED_ERROR); } public void testAssignBeforeDeclaration1() { test("DEF=false;var b=false,/** @define {boolean} */DEF=true,c=false", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testAssignBeforeDeclaration2() { overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE)); test( "DEF_OVERRIDE_TO_TRUE = 3;" + "/** @define {boolean|number} */ var DEF_OVERRIDE_TO_TRUE = false;", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testEmptyDeclaration() { test("/** @define {boolean} */ var DEF;", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testReassignAfterCall() { test("/** @define {boolean} */var DEF=true;externMethod();DEF=false", null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR); } public void testReassignAfterRef() { test("/** @define {boolean} */var DEF=true;var x = DEF;DEF=false", null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR); } public void testReassignWithExpr() { test("/** @define {boolean} */var DEF=true;var x;DEF=x=false", null, ProcessDefines.INVALID_DEFINE_INIT_ERROR); } public void testReassignAfterNonGlobalRef() { test( "/** @define {boolean} */var DEF=true;" + "var x=function(){var y=DEF}; DEF=false", "var DEF=false;var x=function(){var y=DEF};false"); Name def = namespace.getNameIndex().get("DEF"); assertEquals(2, def.getRefs().size()); assertEquals(1, def.globalSets); assertNotNull(def.getDeclaration()); } public void testReassignAfterRefInConditional() { test( "/** @define {boolean} */var DEF=true;" + "if (false) {var x=DEF} DEF=false;", null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR); } public void testAssignInNonGlobalScope() { test("/** @define {boolean} */var DEF=true;function foo() {DEF=false};", null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR); } public void testDeclareInNonGlobalScope() { test("function foo() {/** @define {boolean} */var DEF=true;};", null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR); } public void testDefineAssignmentInLoop() { test("/** @define {boolean} */var DEF=true;var x=0;while (x) {DEF=false;}", null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR); } public void testWithNoDefines() { testSame("var DEF=true;var x={};x.foo={}"); } public void testNamespacedDefine1() { test("var a = {}; /** @define {boolean} */ a.B = false; a.B = true;", "var a = {}; a.B = true; true;"); Name aDotB = namespace.getNameIndex().get("a.B"); assertEquals(1, aDotB.getRefs().size()); assertEquals(1, aDotB.globalSets); assertNotNull(aDotB.getDeclaration()); } public void testNamespacedDefine2a() { overrides.put("a.B", new Node(Token.TRUE)); test("var a = {}; /** @define {boolean} */ a.B = false;", "var a = {}; a.B = true;"); } public void testNamespacedDefine2b() { // TODO(johnlenz): We should either reject the define as invalid // or replace its value. overrides.put("a.B", new Node(Token.TRUE)); test("var a = { /** @define {boolean} */ B : false };", "var a = {B : false};", null, ProcessDefines.UNKNOWN_DEFINE_WARNING); } public void testNamespacedDefine2c() { // TODO(johnlenz): We should either reject the define as invalid // or replace its value. overrides.put("a.B", new Node(Token.TRUE)); test("var a = { /** @define {boolean} */ get B() { return false } };", "var a = {get B() { return false } };", null, ProcessDefines.UNKNOWN_DEFINE_WARNING); } public void testNamespacedDefine3() { overrides.put("a.B", new Node(Token.TRUE)); test("var a = {};", "var a = {};", null, ProcessDefines.UNKNOWN_DEFINE_WARNING); } public void testNamespacedDefine4() { overrides.put("a.B", new Node(Token.TRUE)); test("var a = {}; /** @define {boolean} */ a.B = false;", "var a = {}; a.B = true;"); } public void testOverrideAfterAlias() { test("var x; /** @define {boolean} */var DEF=true; x=DEF; DEF=false;", null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR); } private class ProcessDefinesWithInjectedNamespace implements CompilerPass { private final Compiler compiler; public ProcessDefinesWithInjectedNamespace(Compiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node js) { namespace = new GlobalNamespace(compiler, js); new ProcessDefines(compiler, overrides) .injectNamespace(namespace) .process(externs, js); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AngularPassTest.java0000644000175000017500000001122312115204405026746 0ustar apoapo/* * Copyright 2012 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; /** * Tests {@link AngularPass}. */ public class AngularPassTest extends CompilerTestCase { public AngularPassTest() { super(); enableLineNumberCheck(false); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new AngularPass(compiler); } @Override protected int getNumRepetitions() { // This pass only runs once. return 1; } @Override protected CompilerOptions getOptions() { CompilerOptions options = new CompilerOptions(); // enables angularPass. options.angularPass = true; return getOptions(options); } public void testNgInjectAddsInjectToFunctions() throws Exception { test( "/** @ngInject */" + "function fn(a, b) {}", "function fn(a, b) {}\n" + "fn['$inject']=['a', 'b']" ); testSame( "function fn(a, b) {}" ); } public void testNgInjectAddsInjectToProps() throws Exception { test( "var ns = {};\n" + "/** @ngInject */" + "ns.fn = function (a, b) {}", "var ns = {};\n" + "ns.fn = function (a, b) {}\n" + "ns.fn['$inject']=['a', 'b']" ); testSame( "var ns = {};\n" + "ns.fn = function (a, b) {}" ); } public void testNgInjectAddsInjectToNestedProps() throws Exception { test( "var ns = {}; ns.subns = {};\n" + "/** @ngInject */" + "ns.subns.fn = function (a, b) {}", "var ns = {};ns.subns = {};\n" + "ns.subns.fn = function (a, b) {}\n" + "ns.subns.fn['$inject']=['a', 'b']" ); testSame( "var ns = {};\n" + "ns.fn = function (a, b) {}" ); } public void testNgInjectAddsInjectToVars() throws Exception { test( "/** @ngInject */" + "var fn = function (a, b) {}", "var fn = function (a, b) {};\n" + "fn['$inject']=['a', 'b']" ); testSame( "var fn = function (a, b) {}" ); } public void testNgInjectAddsInjectToVarsWithChainedAssignment() throws Exception { test( "var ns = {};\n" + "/** @ngInject */" + "var fn = ns.func = function (a, b) {}", "var ns = {}; var fn = ns.func = function (a, b) {};\n" + "fn['$inject']=['a', 'b']" ); testSame( "var ns = {};\n" + "var fn = ns.func = function (a, b) {}" ); } public void testNgInjectInBlock() throws Exception { test( "(function() {" + "var ns = {};\n" + "/** @ngInject */" + "var fn = ns.func = function (a, b) {}" + "})()", "(function() {" + "var ns = {}; var fn = ns.func = function (a, b) {};\n" + "fn['$inject']=['a', 'b']" + "})()" ); testSame( "(function() {" + "var ns = {};\n" + "var fn = ns.func = function (a, b) {}" + "})()" ); } public void testNgInjectAddsToTheRightBlock() throws Exception { test( "var fn = 10;\n" + "(function() {" + "var ns = {};\n" + "/** @ngInject */" + "var fn = ns.func = function (a, b) {}" + "})()", "var fn = 10;" + "(function() {" + "var ns = {}; var fn = ns.func = function (a, b) {};\n" + "fn['$inject']=['a', 'b']" + "})()" ); } public void testNgInjectInNonBlock() throws Exception { test( "function fake(){}; var ns = {};" + "fake(" + "/** @ngInject */" + "ns.func = function (a, b) {}" + ")", null, AngularPass.INJECT_IN_NON_GLOBAL_OR_BLOCK_ERROR ); test( "/** @ngInject */(" + "function (a, b) {}" + ")", null, AngularPass.INJECT_IN_NON_GLOBAL_OR_BLOCK_ERROR ); } public void testNgInjectNonFunction() throws Exception { test( "/** @ngInject */" + "var a = 10", null, AngularPass.INJECT_NON_FUNCTION_ERROR ); test( "/** @ngInject */" + "var x", null, AngularPass.INJECT_NON_FUNCTION_ERROR ); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeOptimizationsPassTest.java0000644000175000017500000001726612115204405031725 0ustar apoapo/* * 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.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * Unit tests for PeepholeOptimizationsPass. * */ public class PeepholeOptimizationsPassTest extends CompilerTestCase { private ImmutableList currentPeepholePasses; @Override public void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { return new PeepholeOptimizationsPass(compiler, currentPeepholePasses.toArray( new AbstractPeepholeOptimization[currentPeepholePasses.size()])); } @Override protected int getNumRepetitions() { // Our tests do not require multiple passes to reach a fixed-point. return 1; } /** * PeepholeOptimizationsPass should handle the case when no peephole * optimizations are turned on. */ public void testEmptyPass() { currentPeepholePasses = ImmutableList.of(); testSame("var x; var y;"); } public void testOptimizationOrder() { /* * We need to make sure that: 1) We are only traversing the AST once 2) For * each node, we visit the optimizations in the client-supplied order * * To test this, we create two fake optimizations that each make an entry in * the visitationLog when they are passed a name node to optimize. * * Each entry is of the form nameX where 'name' is the name of the name node * visited and X is the identity of the optimization (1 or 2 in this case). * After the pass is run, we verify the correct ordering by querying the * log. * * Using a log, rather than, say, transforming nodes, allows us to ensure * not only that we are visiting each node but that our visits occur in the * right order (i.e. we need to make sure we're not traversing the entire * AST for the first optimization and then a second time for the second). */ final List visitationLog = Lists.newArrayList(); AbstractPeepholeOptimization note1Applied = new AbstractPeepholeOptimization() { @Override public Node optimizeSubtree(Node node) { if (node.isName()) { visitationLog.add(node.getString() + "1"); } return node; } }; AbstractPeepholeOptimization note2Applied = new AbstractPeepholeOptimization() { @Override public Node optimizeSubtree(Node node) { if (node.isName()) { visitationLog.add(node.getString() + "2"); } return node; } }; currentPeepholePasses = ImmutableList.< AbstractPeepholeOptimization>of(note1Applied, note2Applied); test("var x; var y", "var x; var y"); /* * We expect the optimization order to be: "x" visited by optimization1 "x" * visited by optimization2 "y" visited by optimization1 "y" visited by * optimization2 */ assertEquals(4, visitationLog.size()); assertEquals("x1", visitationLog.get(0)); assertEquals("x2", visitationLog.get(1)); assertEquals("y1", visitationLog.get(2)); assertEquals("y2", visitationLog.get(3)); } /** * A peephole optimization that, given a subtree consisting of a VAR node, * removes children of that node named "x". */ private static class RemoveNodesNamedXUnderVarOptimization extends AbstractPeepholeOptimization { @Override public Node optimizeSubtree(Node node) { if (node.isVar()) { Set nodesToRemove = Sets.newHashSet(); for (Node child : node.children()) { if ("x".equals(child.getString())) { nodesToRemove.add(child); } } for (Node childToRemove : nodesToRemove) { node.removeChild(childToRemove); reportCodeChange(); } } return node; } } /** * A peephole optimization that, given a subtree consisting of a name node * named "x" removes that node. */ private static class RemoveNodesNamedXOptimization extends AbstractPeepholeOptimization { @Override public Node optimizeSubtree(Node node) { if (node.isName() && "x".equals(node.getString())) { node.getParent().removeChild(node); reportCodeChange(); return null; } return node; } } /** * A peephole optimization that, given a subtree consisting of a name node * named "x" whose parent is a VAR node, removes the parent VAR node. */ private static class RemoveParentVarsForNodesNamedX extends AbstractPeepholeOptimization { @Override public Node optimizeSubtree(Node node) { if (node.isName() && "x".equals(node.getString())) { Node parent = node.getParent(); if (parent.isVar()) { parent.getParent().removeChild(parent); reportCodeChange(); return null; } } return node; } } /** * A peephole optimization that, given a subtree consisting of a name node * named "y", replaces it with a name node named "x"; */ private static class RenameYToX extends AbstractPeepholeOptimization { @Override public Node optimizeSubtree(Node node) { if (node.isName() && "y".equals(node.getString())) { Node replacement = Node.newString(Token.NAME, "x"); node.getParent().replaceChild(node, replacement); reportCodeChange(); return replacement; } return node; } } public void testOptimizationRemovingSubtreeChild() { currentPeepholePasses = ImmutableList.of(new RemoveNodesNamedXUnderVarOptimization()); test("var x,y;", "var y;"); test("var y,x;", "var y;"); test("var x,y,x;", "var y;"); } public void testOptimizationRemovingSubtree() { currentPeepholePasses = ImmutableList.of(new RemoveNodesNamedXOptimization()); test("var x,y;", "var y;"); test("var y,x;", "var y;"); test("var x,y,x;", "var y;"); } public void testOptimizationRemovingSubtreeParent() { currentPeepholePasses = ImmutableList.of(new RemoveParentVarsForNodesNamedX()); test("var x; var y", "var y"); } /** * Test the case where the first peephole optimization removes a node and the * second wants to remove (the now nonexistent) parent of that node. */ public void testOptimizationsRemoveParentAfterRemoveChild() { currentPeepholePasses = ImmutableList.of( new RemoveNodesNamedXOptimization(), new RemoveParentVarsForNodesNamedX()); test("var x,y; var z;", "var y; var z;"); } public void testOptimizationReplacingNode() { currentPeepholePasses = ImmutableList.of( new RenameYToX(), new RemoveParentVarsForNodesNamedX()); test("var y; var z;", "var z;"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineVariablesConstantsTest.java0000644000175000017500000001164712115204405031504 0ustar apoapo/* * Copyright 2004 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; /** * Ensures that the InlineVariables pass in constants-only mode * is functionally equivalent to the old InlineVariablesConstants pass. */ public class InlineVariablesConstantsTest extends CompilerTestCase { private boolean inlineAllStrings = false; public InlineVariablesConstantsTest() { enableNormalize(); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new InlineVariables( compiler, InlineVariables.Mode.CONSTANTS_ONLY, inlineAllStrings); } @Override public void tearDown() { inlineAllStrings = false; } public void testInlineVariablesConstants() { test("var ABC=2; var x = ABC;", "var x=2"); test("var AA = 'aa'; AA;", "'aa'"); test("var A_A=10; A_A + A_A;", "10+10"); test("var AA=1", ""); test("var AA; AA=1", "1"); test("var AA; if (false) AA=1; AA;", "if (false) 1; 1;"); testSame("var AA; if (false) AA=1; else AA=2; AA;"); test("var AA;(function () {AA=1})()", "(function () {1})()"); // Make sure that nothing explodes if there are undeclared variables. testSame("var x = AA;"); // Don't inline if it will make the output larger. testSame("var AA = '1234567890'; foo(AA); foo(AA); foo(AA);"); test("var AA = '123456789012345';AA;", "'123456789012345'"); } public void testNoInlineArraysOrRegexps() { testSame("var AA = [10,20]; AA[0]"); testSame("var AA = [10,20]; AA.push(1); AA[0]"); testSame("var AA = /x/; AA.test('1')"); testSame("/** @const */ var aa = /x/; aa.test('1')"); } public void testInlineVariablesConstantsJsDocStyle() { test("/** @const */var abc=2; var x = abc;", "var x=2"); test("/** @const */var aa = 'aa'; aa;", "'aa'"); test("/** @const */var a_a=10; a_a + a_a;", "10+10"); test("/** @const */var aa=1;", ""); test("/** @const */var aa; aa=1;", "1"); test("/** @const */var aa;(function () {aa=1})()", "(function () {1})()"); test("/** @const */var aa;(function () {aa=1})(); var z=aa", "(function () {1})(); var z=1"); testSame("/** @const */var aa;(function () {var y; aa=y})(); var z=aa"); // Don't inline if it will make the output larger. testSame("/** @const */var aa = '1234567890'; foo(aa); foo(aa); foo(aa);"); test("/** @const */var aa = '123456789012345';aa;", "'123456789012345'"); } public void testInlineConditionallyDefinedConstant1() { // Note that inlining conditionally defined constants can change the // run-time behavior of code (e.g. when y is true and x is false in the // example below). We inline them anyway because if the code author didn't // want one inlined, he/she could define it as a non-const variable instead. test("if (x) var ABC = 2; if (y) f(ABC);", "if (x); if (y) f(2);"); } public void testInlineConditionallyDefinedConstant2() { test("if (x); else var ABC = 2; if (y) f(ABC);", "if (x); else; if (y) f(2);"); } public void testInlineConditionallyDefinedConstant3() { test("if (x) { var ABC = 2; } if (y) { f(ABC); }", "if (x) {} if (y) { f(2); }"); } public void testInlineDefinedConstant() { test( "/**\n" + " * @define {string}\n" + " */\n" + "var aa = '1234567890';\n" + "foo(aa); foo(aa); foo(aa);", "foo('1234567890');foo('1234567890');foo('1234567890')"); test( "/**\n" + " * @define {string}\n" + " */\n" + "var ABC = '1234567890';\n" + "foo(ABC); foo(ABC); foo(ABC);", "foo('1234567890');foo('1234567890');foo('1234567890')"); } public void testInlineVariablesConstantsWithInlineAllStringsOn() { inlineAllStrings = true; test("var AA = '1234567890'; foo(AA); foo(AA); foo(AA);", "foo('1234567890'); foo('1234567890'); foo('1234567890')"); } public void testNoInlineWithoutConstDeclaration() { testSame("var abc = 2; var x = abc;"); } // TODO(nicksantos): enable this again once we allow constant aliasing. // public void testInlineConstantAlias() { // test("var XXX = new Foo(); var YYY = XXX; bar(YYY)", // "var XXX = new Foo(); bar(XXX)"); // } public void testNoInlineAliases() { testSame("var XXX = new Foo(); var yyy = XXX; bar(yyy)"); testSame("var xxx = new Foo(); var YYY = xxx; bar(YYY)"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/OptimizeCallsTest.java0000644000175000017500000000417112115204405027311 0ustar apoapo/* * Copyright 2011 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.rhino.Node; /** * Unit tests for {#link {@link OptimizeCalls} * */ public class OptimizeCallsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { final OptimizeCalls passes = new OptimizeCalls(compiler); passes.addPass(new OptimizeReturns(compiler)); passes.addPass(new OptimizeParameters(compiler)); passes.addPass(new RemoveUnusedVars(compiler, true, false, true)); return new CompilerPass() { @Override public void process(Node externs, Node root) { new PureFunctionIdentifier(compiler, new SimpleDefinitionFinder(compiler)).process(externs, root); passes.process(externs, root); } }; } public void testRemovingReturnCallToFunctionWithUnusedParams() { test("function foo() {var x; return x = bar(1)} foo(); function bar(x) {}", "function foo() { bar(); return;} foo(); function bar() {}"); } public void testNestingFunctionCallWithUnsedParams() { test("function f1(x) { } function f2(x) { }" + "function f3(x) { } function f4(x) { }" + "f3(f1(f2()));", "function f1() {f2()} function f2() { }" + "function f3() {f1()} " + "f3();" ); } public void testUnusedAssignOnFunctionWithUnusedParams() { test("var foo = function(a){}; function bar(){var x;x = foo} bar(); foo(1)", "var foo = function( ){}; function bar(){ } bar(); foo()"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/IgnoreCajaPropertiesTest.java0000644000175000017500000001145412115204405030613 0ustar apoapo/* * 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; /** * {@link IgnoreCajaProperties} tests. * */ public class IgnoreCajaPropertiesTest extends CompilerTestCase { private static final String EXTERNS = "var z = {}, " + "f = function(y) { z[y] = z[y] ? (z[y]+1) : 1; }, " + "x, i;"; public IgnoreCajaPropertiesTest() { super(EXTERNS); } @Override public void setUp() { super.enableLineNumberCheck(false); } @Override public int getNumRepetitions() { return 1; } public void testSimpleKey() { // Test a one-statement body. test("for (i in x) f(i);", "for (var JSCompiler_IgnoreCajaProperties_0 in x)" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " i = JSCompiler_IgnoreCajaProperties_0;" + " { f(i); }" + " }"); // Test a two-statement body. test("for (i in x) { f(i); f(i); }", "for (var JSCompiler_IgnoreCajaProperties_0 in x)" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " i = JSCompiler_IgnoreCajaProperties_0;" + " { f(i); f(i); }" + " }"); // Check that the counter's incrementing properly and // that nested loops work. test("for (i in x) for (j in y) f(i,j);", "for (var JSCompiler_IgnoreCajaProperties_1 in x)" + " if (!JSCompiler_IgnoreCajaProperties_1.match(/___$/)) {" + " i = JSCompiler_IgnoreCajaProperties_1;" + " {" + " for (var JSCompiler_IgnoreCajaProperties_0 in y)" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " j = JSCompiler_IgnoreCajaProperties_0;" + " { f(i,j); }" + " }" + " }" + " }"); } public void testPropertyKey() { test("for (z.i in x) { f(z.i); f(z.i); }", "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " z.i = JSCompiler_IgnoreCajaProperties_0;" + " { f(z.i); f(z.i); }" + " }" + "}"); } public void testFunctionPropertyKey() { // Note that both in the original code and the // rewritten code, z.j() is invoked on every // iteration of the loop. test("for (z.j().i in x) { f(z.j().i); f(z.j().i); }", "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " z.j().i = JSCompiler_IgnoreCajaProperties_0;" + " { f(z.j().i); f(z.j().i); }" + " }" + "}"); } public void testVarKey() { // Test a one-statement body. test("for (var j in x) { f(j); }", "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " var j;" + " j = JSCompiler_IgnoreCajaProperties_0;" + " { f(j); }" + " }" + "}"); // Test a two-statement body. test("for (var j in x) { f(j); f(j); }", "for (var JSCompiler_IgnoreCajaProperties_0 in x) {" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " var j;" + " j = JSCompiler_IgnoreCajaProperties_0;" + " { f(j); f(j); }" + " }" + "}"); // Test two loops. test("for (var i in x) for (var j in y) f(i,j);", "for (var JSCompiler_IgnoreCajaProperties_1 in x)" + " if (!JSCompiler_IgnoreCajaProperties_1.match(/___$/)) {" + " var i;" + " i = JSCompiler_IgnoreCajaProperties_1;" + " {" + " for (var JSCompiler_IgnoreCajaProperties_0 in y)" + " if (!JSCompiler_IgnoreCajaProperties_0.match(/___$/)) {" + " var j;" + " j = JSCompiler_IgnoreCajaProperties_0;" + " { f(i,j); }" + " }" + " }" + " }"); } public void testFourChildFor() { test("for (i = 0; i < 10; ++i) { f(i); }", "for (i = 0; i < 10; ++i) { f(i); }"); } @Override public CompilerPass getProcessor(Compiler compiler) { return new IgnoreCajaProperties(compiler); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CompilerTest.java0000644000175000017500000001334012115204405026302 0ustar apoapo/* * 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.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.List; /** * @author johnlenz@google.com (John Lenz) */ public class CompilerTest extends TestCase { // Verify the line and column information is maintained after a reset public void testCodeBuilderColumnAfterReset() { Compiler.CodeBuilder cb = new Compiler.CodeBuilder(); String js = "foo();\ngoo();"; cb.append(js); assertEquals(js, cb.toString()); assertEquals(1, cb.getLineIndex()); assertEquals(6, cb.getColumnIndex()); cb.reset(); assertTrue(cb.toString().isEmpty()); assertEquals(1, cb.getLineIndex()); assertEquals(6, cb.getColumnIndex()); } public void testCodeBuilderAppend() { Compiler.CodeBuilder cb = new Compiler.CodeBuilder(); cb.append("foo();"); assertEquals(0, cb.getLineIndex()); assertEquals(6, cb.getColumnIndex()); cb.append("goo();"); assertEquals(0, cb.getLineIndex()); assertEquals(12, cb.getColumnIndex()); // newline reset the column index cb.append("blah();\ngoo();"); assertEquals(1, cb.getLineIndex()); assertEquals(6, cb.getColumnIndex()); } public void testCyclicalDependencyInInputs() { List inputs = Lists.newArrayList( SourceFile.fromCode( "gin", "goog.provide('gin'); goog.require('tonic'); var gin = {};"), SourceFile.fromCode("tonic", "goog.provide('tonic'); goog.require('gin'); var tonic = {};"), SourceFile.fromCode( "mix", "goog.require('gin'); goog.require('tonic');")); CompilerOptions options = new CompilerOptions(); options.ideMode = true; options.setManageClosureDependencies(true); Compiler compiler = new Compiler(); compiler.init(ImmutableList.of(), inputs, options); compiler.parseInputs(); assertEquals(compiler.externAndJsRoot, compiler.jsRoot.getParent()); assertEquals(compiler.externAndJsRoot, compiler.externsRoot.getParent()); assertNotNull(compiler.externAndJsRoot); Node jsRoot = compiler.jsRoot; assertEquals(3, jsRoot.getChildCount()); } public void testLocalUndefined() throws Exception { // Some JavaScript libraries like to create a local instance of "undefined", // to ensure that other libraries don't try to overwrite it. // // Most of the time, this is OK, because normalization will rename // that variable to undefined$$1. But this won't happen if they don't // include the default externs. // // This test is just to make sure that the compiler doesn't crash. CompilerOptions options = new CompilerOptions(); CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( options); Compiler compiler = new Compiler(); SourceFile externs = SourceFile.fromCode("externs.js", ""); SourceFile input = SourceFile.fromCode("input.js", "(function (undefined) { alert(undefined); })();"); compiler.compile(externs, input, options); } public void testCommonJSProvidesAndRequire() throws Exception { List inputs = Lists.newArrayList( SourceFile.fromCode("gin.js", "require('tonic')"), SourceFile.fromCode("tonic.js", ""), SourceFile.fromCode("mix.js", "require('gin'); require('tonic');")); List entryPoints = Lists.newArrayList("module$mix"); Compiler compiler = initCompilerForCommonJS(inputs, entryPoints); JSModuleGraph graph = compiler.getModuleGraph(); assertEquals(4, graph.getModuleCount()); List result = graph.manageDependencies(entryPoints, compiler.getInputsForTesting()); assertEquals("[root]", result.get(0).getName()); assertEquals("[module$tonic]", result.get(1).getName()); assertEquals("[module$gin]", result.get(2).getName()); assertEquals("tonic.js", result.get(3).getName()); assertEquals("gin.js", result.get(4).getName()); assertEquals("mix.js", result.get(5).getName()); } public void testCommonJSMissingRequire() throws Exception { List inputs = Lists.newArrayList( SourceFile.fromCode("gin.js", "require('missing')")); Compiler compiler = initCompilerForCommonJS( inputs, ImmutableList.of("module$gin")); compiler.processAMDAndCommonJSModules(); assertEquals(1, compiler.getErrorManager().getErrorCount()); String error = compiler.getErrorManager().getErrors()[0].toString(); assertTrue( "Unexpected error: " + error, error.contains( "required entry point \"module$missing\" never provided")); } private Compiler initCompilerForCommonJS( List inputs, List entryPoints) throws Exception { CompilerOptions options = new CompilerOptions(); options.ideMode = true; options.setManageClosureDependencies(entryPoints); options.closurePass = true; options.processCommonJSModules = true; Compiler compiler = new Compiler(); compiler.init(Lists.newArrayList(), inputs, options); compiler.parseInputs(); return compiler; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/FunctionArgumentInjectorTest.java0000644000175000017500000003463512115204405031530 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.Collections; import java.util.Map; import java.util.Set; /** * Inline function tests. * @author johnlenz@google.com (John Lenz) */ public class FunctionArgumentInjectorTest extends TestCase { // TODO(johnlenz): Add unit tests for: // inject // getFunctionCallParameterMap private static final Set EMPTY_STRING_SET = Collections.emptySet(); public void testFindModifiedParameters1() { assertEquals(Sets.newHashSet(), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a){ return a==0; }"))); } public void testFindModifiedParameters2() { assertEquals(Sets.newHashSet(), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a){ b=a }"))); } public void testFindModifiedParameters3() { assertEquals(Sets.newHashSet("a"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a){ a=0 }"))); } public void testFindModifiedParameters4() { assertEquals(Sets.newHashSet("a", "b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ a=0;b=0 }"))); } public void testFindModifiedParameters5() { assertEquals(Sets.newHashSet("b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ a; if (a) b=0 }"))); } public void testFindModifiedParameters6() { assertEquals(Sets.newHashSet("a", "b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ function f(){ a;b; } }"))); } public void testFindModifiedParameters7() { assertEquals(Sets.newHashSet("b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ a; function f(){ b; } }"))); } public void testFindModifiedParameters8() { assertEquals(Sets.newHashSet("b"), FunctionArgumentInjector.findModifiedParameters( parseFunction( "function f(a,b){ "+ "a; function f(){ function g() { b; } } }"))); } public void testFindModifiedParameters9() { assertEquals(Sets.newHashSet("a", "b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ (function(){ a;b; }) }"))); } public void testFindModifiedParameters10() { assertEquals(Sets.newHashSet("b"), FunctionArgumentInjector.findModifiedParameters( parseFunction("function f(a,b){ a; (function (){ b; }) }"))); } public void testFindModifiedParameters11() { assertEquals(Sets.newHashSet("b"), FunctionArgumentInjector.findModifiedParameters( parseFunction( "function f(a,b){ "+ "a; (function(){ (function () { b; }) }) }"))); } public void testMaybeAddTempsForCallArguments1() { // Parameters with side-effects must be executed // even if they aren't referenced. testNeededTemps( "function foo(a,b){}; foo(goo(),goo());", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments2() { // Unreferenced parameters without side-effects // can be ignored. testNeededTemps( "function foo(a,b){}; foo(1,2);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments3() { // Referenced parameters without side-effects // don't need temps. testNeededTemps( "function foo(a,b){a;b;}; foo(x,y);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments4() { // Parameters referenced after side-effect must // be assigned to temps. testNeededTemps( "function foo(a,b){a;goo();b;}; foo(x,y);", "foo", Sets.newHashSet("b")); } public void testMaybeAddTempsForCallArguments5() { // Parameters referenced after out-of-scope side-effect must // be assigned to temps. testNeededTemps( "function foo(a,b){x = b; y = a;}; foo(x,y);", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments6() { // Parameter referenced after a out-of-scope side-effect must // be assigned to a temp. testNeededTemps( "function foo(a){x++;a;}; foo(x);", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments7() { // No temp needed after local side-effects. testNeededTemps( "function foo(a){var c; c=0; a;}; foo(x);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments8() { // Temp needed for side-effects to object using local name. testNeededTemps( "function foo(a){var c = {}; c.goo=0; a;}; foo(x);", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments9() { // Parameters referenced in a loop with side-effects must // be assigned to temps. testNeededTemps( "function foo(a,b){while(true){a;goo();b;}}; foo(x,y);", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments10() { // No temps for parameters referenced in a loop with no side-effects. testNeededTemps( "function foo(a,b){while(true){a;true;b;}}; foo(x,y);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments11() { // Parameters referenced in a loop with side-effects must // be assigned to temps. testNeededTemps( "function foo(a,b){do{a;b;}while(goo());}; foo(x,y);", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments12() { // Parameters referenced in a loop with side-effects must // be assigned to temps. testNeededTemps( "function foo(a,b){for(;;){a;b;goo();}}; foo(x,y);", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments13() { // Parameters referenced in a inner loop without side-effects must // be assigned to temps if the outer loop has side-effects. testNeededTemps( "function foo(a,b){for(;;){for(;;){a;b;}goo();}}; foo(x,y);", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments14() { // Parameters referenced in a loop must // be assigned to temps. testNeededTemps( "function foo(a,b){goo();for(;;){a;b;}}; foo(x,y);", "foo", Sets.newHashSet("a", "b")); } public void testMaybeAddTempsForCallArguments20() { // A long string referenced more than once should have a temp. testNeededTemps( "function foo(a){a;a;}; foo(\"blah blah\");", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments21() { // A short string referenced once should not have a temp. testNeededTemps( "function foo(a){a;a;}; foo(\"\");", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments22() { // A object literal not referenced. testNeededTemps( "function foo(a){}; foo({x:1});", "foo", EMPTY_STRING_SET); // A object literal referenced, should have a temp. testNeededTemps( "function foo(a){a;}; foo({x:1});", "foo", Sets.newHashSet("a")); // A object literal, referenced more than once, should have a temp. testNeededTemps( "function foo(a){a;a;}; foo({x:1});", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments23() { // A array literal, not referenced. testNeededTemps( "function foo(a){}; foo([1,2]);", "foo", EMPTY_STRING_SET); // A array literal, referenced once, should have a temp. testNeededTemps( "function foo(a){a;}; foo([1,2]);", "foo", Sets.newHashSet("a")); // A array literal, referenced more than once, should have a temp. testNeededTemps( "function foo(a){a;a;}; foo([1,2]);", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments24() { // A regex literal, not referenced. testNeededTemps( "function foo(a){}; foo(/mac/);", "foo", EMPTY_STRING_SET); // A regex literal, referenced once, should have a temp. testNeededTemps( "function foo(a){a;}; foo(/mac/);", "foo", Sets.newHashSet("a")); // A regex literal, referenced more than once, should have a temp. testNeededTemps( "function foo(a){a;a;}; foo(/mac/);", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments25() { // A side-effect-less constructor, not referenced. testNeededTemps( "function foo(a){}; foo(new Date());", "foo", EMPTY_STRING_SET); // A side-effect-less constructor, referenced once, should have a temp. testNeededTemps( "function foo(a){a;}; foo(new Date());", "foo", Sets.newHashSet("a")); // A side-effect-less constructor, referenced more than once, should have // a temp. testNeededTemps( "function foo(a){a;a;}; foo(new Date());", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments26() { // A constructor, not referenced. testNeededTemps( "function foo(a){}; foo(new Bar());", "foo", Sets.newHashSet("a")); // A constructor, referenced once, should have a temp. testNeededTemps( "function foo(a){a;}; foo(new Bar());", "foo", Sets.newHashSet("a")); // A constructor, referenced more than once, should have a temp. testNeededTemps( "function foo(a){a;a;}; foo(new Bar());", "foo", Sets.newHashSet("a")); } public void testMaybeAddTempsForCallArguments27() { // Ensure the correct parameter is given a temp, when there is // a this value in the call. testNeededTemps( "function foo(a,b,c){}; foo.call(this,1,goo(),2);", "foo", Sets.newHashSet("b")); } public void testMaybeAddTempsForCallArguments28() { // true/false are don't need temps testNeededTemps( "function foo(a){a;a;}; foo(true);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments29() { // true/false are don't need temps testNeededTemps( "function foo(a){a;a;}; foo(false);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments30() { // true/false are don't need temps testNeededTemps( "function foo(a){a;a;}; foo(!0);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments31() { // true/false are don't need temps testNeededTemps( "function foo(a){a;a;}; foo(!1);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArguments32() { // void 0 doesn't need a temp testNeededTemps( "function foo(a){a;a;}; foo(void 0);", "foo", EMPTY_STRING_SET); } public void testMaybeAddTempsForCallArgumentsInLoops() { // A mutable parameter referenced in loop needs a // temporary. testNeededTemps( "function foo(a){for(;;)a;}; foo(new Bar());", "foo", Sets.newHashSet("a")); testNeededTemps( "function foo(a){while(true)a;}; foo(new Bar());", "foo", Sets.newHashSet("a")); testNeededTemps( "function foo(a){do{a;}while(true)}; foo(new Bar());", "foo", Sets.newHashSet("a")); } private void testNeededTemps( String code, String fnName, Set expectedTemps) { Node n = parse(code); Node fn = findFunction(n, fnName); assertNotNull(fn); Node call = findCall(n, fnName); assertNotNull(call); Map args = FunctionArgumentInjector.getFunctionCallParameterMap( fn, call, getNameSupplier()); Set actualTemps = Sets.newHashSet(); FunctionArgumentInjector.maybeAddTempsForCallArguments( fn, args, actualTemps, new ClosureCodingConvention()); assertEquals(expectedTemps, actualTemps); } private static Supplier getNameSupplier() { return new Supplier() { int i = 0; @Override public String get() { return String.valueOf(i++); } }; } private static Node findCall(Node n, String name) { if (n.isCall()) { Node callee; if (NodeUtil.isGet(n.getFirstChild())) { callee = n.getFirstChild().getFirstChild(); Node prop = callee.getNext(); // Only "call" is support at this point. Preconditions.checkArgument(prop.isString() && prop.getString().equals("call")); } else { callee = n.getFirstChild(); } if (callee.isName() && callee.getString().equals(name)) { return n; } } for (Node c : n.children()) { Node result = findCall(c, name); if (result != null) { return result; } } return null; } private static Node findFunction(Node n, String name) { if (n.isFunction()) { if (n.getFirstChild().getString().equals(name)) { return n; } } for (Node c : n.children()) { Node result = findFunction(c, name); if (result != null) { return result; } } return null; } private static Node parseFunction(String js) { return parse(js).getFirstChild(); } private static Node parse(String js) { Compiler compiler = new Compiler(); Node n = compiler.parseTestCode(js); assertEquals(0, compiler.getErrorCount()); return n; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DenormalizeTest.java0000644000175000017500000001064212115204405027003 0ustar apoapo/* * 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.javascript.jscomp.Normalize.NormalizeStatements; import com.google.javascript.rhino.Node; /** * @author johnlenz@google.com (John Lenz) * */ public class DenormalizeTest extends CompilerTestCase { @Override public CompilerPass getProcessor(final Compiler compiler) { return new NormalizeAndDenormalizePass(compiler); } @Override protected int getNumRepetitions() { // The normalize pass is only run once. return 1; } public void testFor() { // Verify assignments are moved into the FOR init node. test("a = 0; for(; a < 2 ; a++) foo()", "for(a = 0; a < 2 ; a++) foo();"); // Verify vars are are moved into the FOR init node. test("var a = 0; for(; c < b ; c++) foo()", "for(var a = 0; c < b ; c++) foo()"); // We don't handle labels yet. testSame("var a = 0; a:for(; c < b ; c++) foo()"); testSame("var a = 0; a:b:for(; c < b ; c++) foo()"); // Verify FOR inside IFs. test("if(x){var a = 0; for(; c < b; c++) foo()}", "if(x){for(var a = 0; c < b; c++) foo()}"); // Any other expression. test("init(); for(; a < 2 ; a++) foo()", "for(init(); a < 2 ; a++) foo();"); // Other statements are left as is. test("function f(){ var a; for(; a < 2 ; a++) foo() }", "function f(){ for(var a; a < 2 ; a++) foo() }"); testSame("function f(){ return; for(; a < 2 ; a++) foo() }"); } public void testForIn() { test("var a; for(a in b) foo()", "for (var a in b) foo()"); testSame("a = 0; for(a in b) foo()"); testSame("var a = 0; for(a in b) foo()"); // We don't handle labels yet. testSame("var a; a:for(a in b) foo()"); testSame("var a; a:b:for(a in b) foo()"); // Verify FOR inside IFs. test("if(x){var a; for(a in b) foo()}", "if(x){for(var a in b) foo()}"); // Any other expression. testSame("init(); for(a in b) foo()"); // Other statements are left as is. testSame("function f(){ return; for(a in b) foo() }"); } public void testInOperatorNotInsideFor() { // in operators shouldn't be moved into for loops. // Some JavaScript interpreters (such as the NetFront Access browser // embedded in the PlayStation 3) will not parse an in operator in // a for loop, even if it's protected by parentheses. // Make sure the in operator doesn't get moved into the for loop. testSame("function f(){ var a; var i=\"length\" in a;" + "for(; a < 2 ; a++) foo() }"); // Same, but with parens around the operator. testSame("function f(){ var a; var i=(\"length\" in a);" + "for(; a < 2 ; a++) foo() }"); // Make sure Normalize yanks the variable initializer out, and // Denormalize doesn't put it back. test("function f(){" + "var b,a=0; for (var i=(\"length\" in b);a<2; a++) foo()}", "function f(){var b; var a=0;var i=(\"length\" in b);" + "for (;a<2;a++) foo()}"); } /** * Create a class to combine the Normalize and Denormalize passes. * This is needed because the enableNormalize() call on CompilerTestCase * causes normalization of the result *and* the expected string, and * we really don't want the compiler twisting the expected code around. */ public class NormalizeAndDenormalizePass implements CompilerPass { Denormalize denormalizePass; NormalizeStatements normalizePass; AbstractCompiler compiler; public NormalizeAndDenormalizePass(AbstractCompiler compiler) { this.compiler = compiler; denormalizePass = new Denormalize(compiler); normalizePass = new NormalizeStatements(compiler, false); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, normalizePass); NodeTraversal.traverse(compiler, root, denormalizePass); } } } ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ObjectPropertyStringPostprocessTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ObjectPropertyStringPostprocessTes0000644000175000017500000000270412115204405032035 0ustar apoapo/* * 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; /** * Tests for {@link ObjectPropertyStringPostprocess}. * */ public class ObjectPropertyStringPostprocessTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new ObjectPropertyStringPostprocess(compiler); } @Override protected int getNumRepetitions() { return 1; } public void testFooDotBar() { testPass("goog.global, foo.bar", "foo, 'bar'"); } public void testFooGetElemBar() { testPass("goog.global, foo[bar]", "foo, bar"); } public void testFooBar() { testPass("goog.global, foo$bar", "goog.global, 'foo$bar'"); } private void testPass(String input, String expected) { test("new JSCompiler_ObjectPropertyString(" + input + ")", "new JSCompiler_ObjectPropertyString(" + expected + ")"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ProcessClosurePrimitivesTest.java0000644000175000017500000007205712115204405031571 0ustar apoapo/* * Copyright 2006 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.rhino.Node; import com.google.javascript.jscomp.CheckLevel; import static com.google.javascript.jscomp.ProcessClosurePrimitives.BASE_CLASS_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.DUPLICATE_NAMESPACE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.EXPECTED_OBJECTLIT_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.FUNCTION_NAMESPACE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_ARGUMENT_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_PROVIDE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_STYLE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.LATE_PROVIDE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.MISSING_PROVIDE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.NULL_ARGUMENT_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.TOO_MANY_ARGUMENTS_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.XMODULE_REQUIRE_ERROR; import static com.google.javascript.jscomp.ProcessClosurePrimitives.INVALID_CSS_RENAMING_MAP; /** * Tests for {@link ProcessClosurePrimitives}. * */ public class ProcessClosurePrimitivesTest extends CompilerTestCase { private String additionalCode; private String additionalEndCode; private boolean addAdditionalNamespace; public ProcessClosurePrimitivesTest() { enableLineNumberCheck(true); } @Override protected void setUp() { additionalCode = null; additionalEndCode = null; addAdditionalNamespace = false; } @Override public CompilerPass getProcessor(final Compiler compiler) { if ((additionalCode == null) && (additionalEndCode == null)) { return new ProcessClosurePrimitives( compiler, null, CheckLevel.ERROR); } else { return new CompilerPass() { @Override public void process(Node externs, Node root) { // Process the original code. new ProcessClosurePrimitives(compiler, null, CheckLevel.OFF) .process(externs, root); // Inject additional code at the beginning. if (additionalCode != null) { SourceFile file = SourceFile.fromCode("additionalcode", additionalCode); Node scriptNode = root.getFirstChild(); Node newScriptNode = new CompilerInput(file).getAstRoot(compiler); if (addAdditionalNamespace) { newScriptNode.getFirstChild() .putBooleanProp(Node.IS_NAMESPACE, true); } while (newScriptNode.getLastChild() != null) { Node lastChild = newScriptNode.getLastChild(); newScriptNode.removeChild(lastChild); scriptNode.addChildBefore(lastChild, scriptNode.getFirstChild()); } } // Inject additional code at the end. if (additionalEndCode != null) { SourceFile file = SourceFile.fromCode("additionalendcode", additionalEndCode); Node scriptNode = root.getFirstChild(); Node newScriptNode = new CompilerInput(file).getAstRoot(compiler); if (addAdditionalNamespace) { newScriptNode.getFirstChild() .putBooleanProp(Node.IS_NAMESPACE, true); } while (newScriptNode.getFirstChild() != null) { Node firstChild = newScriptNode.getFirstChild(); newScriptNode.removeChild(firstChild); scriptNode.addChildToBack(firstChild); } } // Process the tree a second time. new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) .process(externs, root); } }; } } @Override public int getNumRepetitions() { return 1; } public void testSimpleProvides() { test("goog.provide('foo');", "var foo={};"); test("goog.provide('foo.bar');", "var foo={}; foo.bar={};"); test("goog.provide('foo.bar.baz');", "var foo={}; foo.bar={}; foo.bar.baz={};"); test("goog.provide('foo.bar.baz.boo');", "var foo={}; foo.bar={}; foo.bar.baz={}; foo.bar.baz.boo={};"); test("goog.provide('goog.bar');", "goog.bar={};"); // goog is special-cased } public void testMultipleProvides() { test("goog.provide('foo.bar'); goog.provide('foo.baz');", "var foo={}; foo.bar={}; foo.baz={};"); test("goog.provide('foo.bar.baz'); goog.provide('foo.boo.foo');", "var foo={}; foo.bar={}; foo.bar.baz={}; foo.boo={}; foo.boo.foo={};"); test("goog.provide('foo.bar.baz'); goog.provide('foo.bar.boo');", "var foo={}; foo.bar={}; foo.bar.baz={}; foo.bar.boo={};"); test("goog.provide('foo.bar.baz'); goog.provide('goog.bar.boo');", "var foo={}; foo.bar={}; foo.bar.baz={}; goog.bar={}; " + "goog.bar.boo={};"); } public void testRemovalOfProvidedObjLit() { test("goog.provide('foo'); foo = 0;", "var foo = 0;"); test("goog.provide('foo'); foo = {a: 0};", "var foo = {a: 0};"); test("goog.provide('foo'); foo = function(){};", "var foo = function(){};"); test("goog.provide('foo'); var foo = 0;", "var foo = 0;"); test("goog.provide('foo'); var foo = {a: 0};", "var foo = {a: 0};"); test("goog.provide('foo'); var foo = function(){};", "var foo = function(){};"); test("goog.provide('foo.bar.Baz'); foo.bar.Baz=function(){};", "var foo={}; foo.bar={}; foo.bar.Baz=function(){};"); test("goog.provide('foo.bar.moo'); foo.bar.moo={E:1,S:2};", "var foo={}; foo.bar={}; foo.bar.moo={E:1,S:2};"); test("goog.provide('foo.bar.moo'); foo.bar.moo={E:1}; foo.bar.moo={E:2};", "var foo={}; foo.bar={}; foo.bar.moo={E:1}; foo.bar.moo={E:2};"); } public void testProvidedDeclaredFunctionError() { test("goog.provide('foo'); function foo(){}", null, FUNCTION_NAMESPACE_ERROR); } public void testRemovalMultipleAssignment1() { test("goog.provide('foo'); foo = 0; foo = 1", "var foo = 0; foo = 1;"); } public void testRemovalMultipleAssignment2() { test("goog.provide('foo'); var foo = 0; foo = 1", "var foo = 0; foo = 1;"); } public void testRemovalMultipleAssignment3() { test("goog.provide('foo'); foo = 0; var foo = 1", "foo = 0; var foo = 1;"); } public void testRemovalMultipleAssignment4() { test("goog.provide('foo.bar'); foo.bar = 0; foo.bar = 1", "var foo = {}; foo.bar = 0; foo.bar = 1"); } public void testNoRemovalFunction1() { test("goog.provide('foo'); function f(){foo = 0}", "var foo = {}; function f(){foo = 0}"); } public void testNoRemovalFunction2() { test("goog.provide('foo'); function f(){var foo = 0}", "var foo = {}; function f(){var foo = 0}"); } public void testRemovalMultipleAssignmentInIf1() { test("goog.provide('foo'); if (true) { var foo = 0 } else { foo = 1 }", "if (true) { var foo = 0 } else { foo = 1 }"); } public void testRemovalMultipleAssignmentInIf2() { test("goog.provide('foo'); if (true) { foo = 0 } else { var foo = 1 }", "if (true) { foo = 0 } else { var foo = 1 }"); } public void testRemovalMultipleAssignmentInIf3() { test("goog.provide('foo'); if (true) { foo = 0 } else { foo = 1 }", "if (true) { var foo = 0 } else { foo = 1 }"); } public void testRemovalMultipleAssignmentInIf4() { test("goog.provide('foo.bar');" + "if (true) { foo.bar = 0 } else { foo.bar = 1 }", "var foo = {}; if (true) { foo.bar = 0 } else { foo.bar = 1 }"); } public void testMultipleDeclarationError1() { String rest = "if (true) { foo.bar = 0 } else { foo.bar = 1 }"; test("goog.provide('foo.bar');" + "var foo = {};" + rest, "var foo = {};" + "var foo = {};" + rest); } public void testMultipleDeclarationError2() { test("goog.provide('foo.bar');" + "if (true) { var foo = {}; foo.bar = 0 } else { foo.bar = 1 }", "var foo = {};" + "if (true) {" + " var foo = {}; foo.bar = 0" + "} else {" + " foo.bar = 1" + "}"); } public void testMultipleDeclarationError3() { test("goog.provide('foo.bar');" + "if (true) { foo.bar = 0 } else { var foo = {}; foo.bar = 1 }", "var foo = {};" + "if (true) {" + " foo.bar = 0" + "} else {" + " var foo = {}; foo.bar = 1" + "}"); } public void testProvideAfterDeclarationError() { test("var x = 42; goog.provide('x');", "var x = 42; var x = {}"); } public void testProvideErrorCases() { test("goog.provide();", "", NULL_ARGUMENT_ERROR); test("goog.provide(5);", "", INVALID_ARGUMENT_ERROR); test("goog.provide([]);", "", INVALID_ARGUMENT_ERROR); test("goog.provide({});", "", INVALID_ARGUMENT_ERROR); test("goog.provide('foo', 'bar');", "", TOO_MANY_ARGUMENTS_ERROR); test("goog.provide('foo'); goog.provide('foo');", "", DUPLICATE_NAMESPACE_ERROR); test("goog.provide('foo.bar'); goog.provide('foo'); goog.provide('foo');", "", DUPLICATE_NAMESPACE_ERROR); } public void testRemovalOfRequires() { test("goog.provide('foo'); goog.require('foo');", "var foo={};"); test("goog.provide('foo.bar'); goog.require('foo.bar');", "var foo={}; foo.bar={};"); test("goog.provide('foo.bar.baz'); goog.require('foo.bar.baz');", "var foo={}; foo.bar={}; foo.bar.baz={};"); test("goog.provide('foo'); var x = 3; goog.require('foo'); something();", "var foo={}; var x = 3; something();"); testSame("foo.require('foo.bar');"); } public void testRequireErrorCases() { test("goog.require();", "", NULL_ARGUMENT_ERROR); test("goog.require(5);", "", INVALID_ARGUMENT_ERROR); test("goog.require([]);", "", INVALID_ARGUMENT_ERROR); test("goog.require({});", "", INVALID_ARGUMENT_ERROR); } public void testLateProvides() { test("goog.require('foo'); goog.provide('foo');", "var foo={};", LATE_PROVIDE_ERROR); test("goog.require('foo.bar'); goog.provide('foo.bar');", "var foo={}; foo.bar={};", LATE_PROVIDE_ERROR); test("goog.provide('foo.bar'); goog.require('foo'); goog.provide('foo');", "var foo={}; foo.bar={};", LATE_PROVIDE_ERROR); } public void testMissingProvides() { test("goog.require('foo');", "", MISSING_PROVIDE_ERROR); test("goog.provide('foo'); goog.require('Foo');", "var foo={};", MISSING_PROVIDE_ERROR); test("goog.provide('foo'); goog.require('foo.bar');", "var foo={};", MISSING_PROVIDE_ERROR); test("goog.provide('foo'); var EXPERIMENT_FOO = true; " + "if (EXPERIMENT_FOO) {goog.require('foo.bar');}", "var foo={}; var EXPERIMENT_FOO = true; if (EXPERIMENT_FOO) {}", MISSING_PROVIDE_ERROR); } public void testAddDependency() { test("goog.addDependency('x.js', ['A', 'B'], []);", "0"); Compiler compiler = getLastCompiler(); assertTrue(compiler.getTypeRegistry().isForwardDeclaredType("A")); assertTrue(compiler.getTypeRegistry().isForwardDeclaredType("B")); assertFalse(compiler.getTypeRegistry().isForwardDeclaredType("C")); } public void testValidSetCssNameMapping() { test("goog.setCssNameMapping({foo:'bar',\"biz\":'baz'});", ""); CssRenamingMap map = getLastCompiler().getCssRenamingMap(); assertNotNull(map); assertEquals("bar", map.get("foo")); assertEquals("baz", map.get("biz")); } public void testValidSetCssNameMappingWithType() { test("goog.setCssNameMapping({foo:'bar',\"biz\":'baz'}, 'BY_PART');", ""); CssRenamingMap map = getLastCompiler().getCssRenamingMap(); assertNotNull(map); assertEquals("bar", map.get("foo")); assertEquals("baz", map.get("biz")); test("goog.setCssNameMapping({foo:'bar',biz:'baz','biz-foo':'baz-bar'}," + " 'BY_WHOLE');", ""); map = getLastCompiler().getCssRenamingMap(); assertNotNull(map); assertEquals("bar", map.get("foo")); assertEquals("baz", map.get("biz")); assertEquals("baz-bar", map.get("biz-foo")); } public void testSetCssNameMappingNonStringValueReturnsError() { // Make sure the argument is an object literal. test("var BAR = {foo:'bar'}; goog.setCssNameMapping(BAR);", "", EXPECTED_OBJECTLIT_ERROR); test("goog.setCssNameMapping([]);", "", EXPECTED_OBJECTLIT_ERROR); test("goog.setCssNameMapping(false);", "", EXPECTED_OBJECTLIT_ERROR); test("goog.setCssNameMapping(null);", "", EXPECTED_OBJECTLIT_ERROR); test("goog.setCssNameMapping(undefined);", "", EXPECTED_OBJECTLIT_ERROR); // Make sure all values of the object literal are string literals. test("var BAR = 'bar'; goog.setCssNameMapping({foo:BAR});", "", NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); test("goog.setCssNameMapping({foo:6});", "", NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); test("goog.setCssNameMapping({foo:false});", "", NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); test("goog.setCssNameMapping({foo:null});", "", NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); test("goog.setCssNameMapping({foo:undefined});", "", NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR); } public void testSetCssNameMappingValidity() { // Make sure that the keys don't have -'s test("goog.setCssNameMapping({'a': 'b', 'a-a': 'c'})", "", null, INVALID_CSS_RENAMING_MAP); // In full mode, we check that map(a-b)=map(a)-map(b) test("goog.setCssNameMapping({'a': 'b', 'a-a': 'c'}, 'BY_WHOLE')", "", null, INVALID_CSS_RENAMING_MAP); // Unknown mapping type test("goog.setCssNameMapping({foo:'bar'}, 'UNKNOWN');", "", INVALID_STYLE_ERROR); } public void testBadCrossModuleRequire() { test( createModuleStar( "", "goog.provide('goog.ui');", "goog.require('goog.ui');"), new String[] { "", "goog.ui = {};", "" }, null, XMODULE_REQUIRE_ERROR); } public void testGoodCrossModuleRequire1() { test( createModuleStar( "goog.provide('goog.ui');", "", "goog.require('goog.ui');"), new String[] { "goog.ui = {};", "", "", }); } public void testGoodCrossModuleRequire2() { test( createModuleStar( "", "", "goog.provide('goog.ui'); goog.require('goog.ui');"), new String[] { "", "", "goog.ui = {};", }); } // Tests providing additional code with non-overlapping var namespace. public void testSimpleAdditionalProvide() { additionalCode = "goog.provide('b.B'); b.B = {};"; test("goog.provide('a.A'); a.A = {};", "var b={};b.B={};var a={};a.A={};"); } // Same as above, but with the additional code added after the original. public void testSimpleAdditionalProvideAtEnd() { additionalEndCode = "goog.provide('b.B'); b.B = {};"; test("goog.provide('a.A'); a.A = {};", "var a={};a.A={};var b={};b.B={};"); } // Tests providing additional code with non-overlapping dotted namespace. public void testSimpleDottedAdditionalProvide() { additionalCode = "goog.provide('a.b.B'); a.b.B = {};"; test("goog.provide('c.d.D'); c.d.D = {};", "var a={};a.b={};a.b.B={};var c={};c.d={};c.d.D={};"); } // Tests providing additional code with overlapping var namespace. public void testOverlappingAdditionalProvide() { additionalCode = "goog.provide('a.B'); a.B = {};"; test("goog.provide('a.A'); a.A = {};", "var a={};a.B={};a.A={};"); } // Tests providing additional code with overlapping var namespace. public void testOverlappingAdditionalProvideAtEnd() { additionalEndCode = "goog.provide('a.B'); a.B = {};"; test("goog.provide('a.A'); a.A = {};", "var a={};a.A={};a.B={};"); } // Tests providing additional code with overlapping dotted namespace. public void testOverlappingDottedAdditionalProvide() { additionalCode = "goog.provide('a.b.B'); a.b.B = {};"; test("goog.provide('a.b.C'); a.b.C = {};", "var a={};a.b={};a.b.B={};a.b.C={};"); } // Tests that a require of additional code generates no error. public void testRequireOfAdditionalProvide() { additionalCode = "goog.provide('b.B'); b.B = {};"; test("goog.require('b.B'); goog.provide('a.A'); a.A = {};", "var b={};b.B={};var a={};a.A={};"); } // Tests that a require not in additional code generates (only) one error. public void testMissingRequireWithAdditionalProvide() { additionalCode = "goog.provide('b.B'); b.B = {};"; test("goog.require('b.C'); goog.provide('a.A'); a.A = {};", "var b={};b.B={};var a={};a.A={};", MISSING_PROVIDE_ERROR); } // Tests that a require in additional code generates no error. public void testLateRequire() { additionalEndCode = "goog.require('a.A');"; test("goog.provide('a.A'); a.A = {};", "var a={};a.A={};"); } // Tests a case where code is reordered after processing provides and then // provides are processed again. public void testReorderedProvides() { additionalCode = "a.B = {};"; // as if a.B was after a.A originally addAdditionalNamespace = true; test("goog.provide('a.A'); a.A = {};", "var a={};a.B={};a.A={};"); } // Another version of above. public void testReorderedProvides2() { additionalEndCode = "a.B = {};"; addAdditionalNamespace = true; test("goog.provide('a.A'); a.A = {};", "var a={};a.A={};a.B={};"); } // Provide a name before the definition of the class providing the // parent namespace. public void testProvideOrder1() { additionalEndCode = ""; addAdditionalNamespace = false; // TODO(johnlenz): This test confirms that the constructor (a.b) isn't // improperly removed, but this result isn't really what we want as the // reassign of a.b removes the definition of "a.b.c". test("goog.provide('a.b');" + "goog.provide('a.b.c');" + "a.b.c;" + "a.b = function(x,y) {};", "var a = {};" + "a.b = {};" + "a.b.c = {};" + "a.b.c;" + "a.b = function(x,y) {};"); } // Provide a name after the definition of the class providing the // parent namespace. public void testProvideOrder2() { additionalEndCode = ""; addAdditionalNamespace = false; // TODO(johnlenz): This test confirms that the constructor (a.b) isn't // improperly removed, but this result isn't really what we want as // namespace placeholders for a.b and a.b.c remain. test("goog.provide('a.b');" + "goog.provide('a.b.c');" + "a.b = function(x,y) {};" + "a.b.c;", "var a = {};" + "a.b = {};" + "a.b.c = {};" + "a.b = function(x,y) {};" + "a.b.c;"); } // Provide a name after the definition of the class providing the // parent namespace. public void testProvideOrder3a() { test("goog.provide('a.b');" + "a.b = function(x,y) {};" + "goog.provide('a.b.c');" + "a.b.c;", "var a = {};" + "a.b = function(x,y) {};" + "a.b.c = {};" + "a.b.c;"); } public void testProvideOrder3b() { additionalEndCode = ""; addAdditionalNamespace = false; // This tests a cleanly provided name, below a function namespace. test("goog.provide('a.b');" + "a.b = function(x,y) {};" + "goog.provide('a.b.c');" + "a.b.c;", "var a = {};" + "a.b = function(x,y) {};" + "a.b.c = {};" + "a.b.c;"); } public void testProvideOrder4a() { test("goog.provide('goog.a');" + "goog.provide('goog.a.b');" + "if (x) {" + " goog.a.b = 1;" + "} else {" + " goog.a.b = 2;" + "}", "goog.a={};" + "if(x)" + " goog.a.b=1;" + "else" + " goog.a.b=2;"); } public void testProvideOrder4b() { additionalEndCode = ""; addAdditionalNamespace = false; // This tests a cleanly provided name, below a namespace. test("goog.provide('goog.a');" + "goog.provide('goog.a.b');" + "if (x) {" + " goog.a.b = 1;" + "} else {" + " goog.a.b = 2;" + "}", "goog.a={};" + "if(x)" + " goog.a.b=1;" + "else" + " goog.a.b=2;"); } public void testInvalidProvide() { test("goog.provide('a.class');", null, INVALID_PROVIDE_ERROR); } private static final String METHOD_FORMAT = "function Foo() {} Foo.prototype.method = function() { %s };"; private static final String FOO_INHERITS = "goog.inherits(Foo, BaseFoo);"; public void testInvalidBase1() { test("goog.base(this, 'method');", null, BASE_CLASS_ERROR); } public void testInvalidBase2() { test("function Foo() {}" + "Foo.method = function() {" + " goog.base(this, 'method');" + "};", null, BASE_CLASS_ERROR); } public void testInvalidBase3() { test(String.format(METHOD_FORMAT, "goog.base();"), null, BASE_CLASS_ERROR); } public void testInvalidBase4() { test(String.format(METHOD_FORMAT, "goog.base(this, 'bar');"), null, BASE_CLASS_ERROR); } public void testInvalidBase5() { test(String.format(METHOD_FORMAT, "goog.base('foo', 'method');"), null, BASE_CLASS_ERROR); } public void testInvalidBase6() { test(String.format(METHOD_FORMAT, "goog.base.call(null, this, 'method');"), null, BASE_CLASS_ERROR); } public void testInvalidBase7() { test("function Foo() { goog.base(this); }", null, BASE_CLASS_ERROR); } public void testInvalidBase8() { test("var Foo = function() { goog.base(this); }", null, BASE_CLASS_ERROR); } public void testInvalidBase9() { test("var goog = {}; goog.Foo = function() { goog.base(this); }", null, BASE_CLASS_ERROR); } public void testValidBase1() { test(String.format(METHOD_FORMAT, "goog.base(this, 'method');"), String.format(METHOD_FORMAT, "Foo.superClass_.method.call(this)")); } public void testValidBase2() { test(String.format(METHOD_FORMAT, "goog.base(this, 'method', 1, 2);"), String.format(METHOD_FORMAT, "Foo.superClass_.method.call(this, 1, 2)")); } public void testValidBase3() { test(String.format(METHOD_FORMAT, "return goog.base(this, 'method');"), String.format(METHOD_FORMAT, "return Foo.superClass_.method.call(this)")); } public void testValidBase4() { test("function Foo() { goog.base(this, 1, 2); }" + FOO_INHERITS, "function Foo() { BaseFoo.call(this, 1, 2); } " + FOO_INHERITS); } public void testValidBase5() { test("var Foo = function() { goog.base(this, 1); };" + FOO_INHERITS, "var Foo = function() { BaseFoo.call(this, 1); }; " + FOO_INHERITS); } public void testValidBase6() { test("var goog = {}; goog.Foo = function() { goog.base(this); }; " + "goog.inherits(goog.Foo, goog.BaseFoo);", "var goog = {}; goog.Foo = function() { goog.BaseFoo.call(this); }; " + "goog.inherits(goog.Foo, goog.BaseFoo);"); } public void testImplicitAndExplicitProvide() { test("var goog = {}; " + "goog.provide('goog.foo.bar'); goog.provide('goog.foo');", "var goog = {}; goog.foo = {}; goog.foo.bar = {};"); } public void testImplicitProvideInIndependentModules() { test( createModuleStar( "", "goog.provide('apps.A');", "goog.provide('apps.B');"), new String[] { "var apps = {};", "apps.A = {};", "apps.B = {};", }); } public void testImplicitProvideInIndependentModules2() { test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo.A');", "goog.provide('apps.foo.B');"), new String[] { "var apps = {}; apps.foo = {};", "apps.foo.A = {};", "apps.foo.B = {};", }); } public void testImplicitProvideInIndependentModules3() { test( createModuleStar( "var goog = {};", "goog.provide('goog.foo.A');", "goog.provide('goog.foo.B');"), new String[] { "var goog = {}; goog.foo = {};", "goog.foo.A = {};", "goog.foo.B = {};", }); } public void testProvideInIndependentModules1() { test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo');", "goog.provide('apps.foo.B');"), new String[] { "var apps = {}; apps.foo = {};", "", "apps.foo.B = {};", }); } public void testProvideInIndependentModules2() { // TODO(nicksantos): Make this an error. test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo'); apps.foo = {};", "goog.provide('apps.foo.B');"), new String[] { "var apps = {};", "apps.foo = {};", "apps.foo.B = {};", }); } public void testProvideInIndependentModules2b() { // TODO(nicksantos): Make this an error. test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo'); apps.foo = function() {};", "goog.provide('apps.foo.B');"), new String[] { "var apps = {};", "apps.foo = function() {};", "apps.foo.B = {};", }); } public void testProvideInIndependentModules3() { test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo.B');", "goog.provide('apps.foo'); goog.require('apps.foo');"), new String[] { "var apps = {}; apps.foo = {};", "apps.foo.B = {};", "", }); } public void testProvideInIndependentModules3b() { // TODO(nicksantos): Make this an error. test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo.B');", "goog.provide('apps.foo'); apps.foo = function() {}; " + "goog.require('apps.foo');"), new String[] { "var apps = {};", "apps.foo.B = {};", "apps.foo = function() {};", }); } public void testProvideInIndependentModules4() { // Regression test for bug 261: // http://code.google.com/p/closure-compiler/issues/detail?id=261 test( createModuleStar( "goog.provide('apps');", "goog.provide('apps.foo.bar.B');", "goog.provide('apps.foo.bar.C');"), new String[] { "var apps = {};apps.foo = {};apps.foo.bar = {}", "apps.foo.bar.B = {};", "apps.foo.bar.C = {};", }); } public void testRequireOfBaseGoog() { test("goog.require('goog');", "", MISSING_PROVIDE_ERROR); } public void testSourcePositionPreservation() { test("goog.provide('foo.bar.baz');", "var foo = {};" + "foo.bar = {};" + "foo.bar.baz = {};"); Node root = getLastCompiler().getRoot(); Node fooDecl = findQualifiedNameNode("foo", root); Node fooBarDecl = findQualifiedNameNode("foo.bar", root); Node fooBarBazDecl = findQualifiedNameNode("foo.bar.baz", root); assertEquals(1, fooDecl.getLineno()); assertEquals(14, fooDecl.getCharno()); assertEquals(1, fooBarDecl.getLineno()); assertEquals(18, fooBarDecl.getCharno()); assertEquals(1, fooBarBazDecl.getLineno()); assertEquals(22, fooBarBazDecl.getCharno()); } public void testNoStubForProvidedTypedef() { test("goog.provide('x'); /** @typedef {number} */ var x;", "var x;"); } public void testNoStubForProvidedTypedef2() { test("goog.provide('x.y'); /** @typedef {number} */ x.y;", "var x = {}; x.y;"); } public void testNoStubForProvidedTypedef4() { test("goog.provide('x.y.z'); /** @typedef {number} */ x.y.z;", "var x = {}; x.y = {}; x.y.z;"); } public void testProvideRequireSameFile() { test("goog.provide('x');\ngoog.require('x');", "var x = {};"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DefinitionsRemoverTest.java0000644000175000017500000000557412115204405030355 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import java.util.List; /** * Test for {@link DefinitionsRemover}. Basically test for the simple removal * cases. More complicated cases will be tested by the clients of * {@link DefinitionsRemover}. * */ public class DefinitionsRemoverTest extends CompilerTestCase { public void testRemoveFunction() { testSame("{(function (){bar()})}"); test("{function a(){bar()}}", "{}"); test("foo(); function a(){} bar()", "foo(); bar();"); test("foo(); function a(){} function b(){} bar()", "foo(); bar();"); } public void testRemoveAssignment() { test("x = 0;", "0"); test("{x = 0}", "{0}"); test("x = 0; y = 0;", "0; 0;"); test("for (x = 0;x;x) {};", "for(0;x;x) {};"); } public void testRemoveVarAssignment() { test("var x = 0;", "0"); test("{var x = 0}", "{0}"); test("var x = 0; var y = 0;", "0;0"); test("var x = 0; var y = 0;", "0;0"); } public void testRemoveLiteral() { test("foo({ 'one' : 1 })", "foo({ })"); test("foo({ 'one' : 1 , 'two' : 2 })", "foo({ })"); } public void testRemoveFunctionExpressionName() { test("foo(function f(){})", "foo(function (){})"); } @Override protected CompilerPass getProcessor(final Compiler compiler) { // Create a pass that removes all the definitions. return new CompilerPass() { @Override public void process(Node externs, Node root) { DefinitionsGatherer g = new DefinitionsGatherer(); (new NodeTraversal(compiler, g)).traverse(root); for (Definition def : g.definitions) { def.remove(); compiler.reportCodeChange(); } } }; } /** * Gather all possible definition objects. */ private static class DefinitionsGatherer extends AbstractPostOrderCallback { final List definitions = Lists.newArrayList(); @Override public void visit(NodeTraversal t, Node n, Node parent) { Definition def = DefinitionsRemover.getDefinition(n, false); if (def != null) { definitions.add(def); } } } } ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveUnusedClassPropertiesTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveUnusedClassPropertiesTest.ja0000644000175000017500000001251312115204405031666 0ustar apoapo/* * Copyright 2011 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; /** * @author johnlenz@google.com (John Lenz) */ public class RemoveUnusedClassPropertiesTest extends CompilerTestCase { private static final String EXTERNS = "var window;\n" + "function alert(a) {}\n" + "var EXT = {};" + "EXT.ext;"; public RemoveUnusedClassPropertiesTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new RemoveUnusedClassProperties(compiler); } public void testSimple1() { // A property defined on "this" can be removed test("this.a = 2", "2"); test("x = (this.a = 2)", "x = 2"); testSame("this.a = 2; x = this.a;"); } public void testSimple2() { // A property defined on "this" can be removed, even when defined // as part of an expression test("this.a = 2, f()", "2, f()"); test("x = (this.a = 2, f())", "x = (2, f())"); test("x = (f(), this.a = 2)", "x = (f(), 2)"); } public void testSimple3() { // A property defined on an object other than "this" can not be removed. testSame("y.a = 2"); // but doesn't prevent the removal of the definition on 'this'. test("y.a = 2; this.a = 2", "y.a = 2; 2"); // Some use of the property "a" prevents the removal. testSame("y.a = 2; this.a = 1; alert(x.a)"); } public void testObjLit() { // A property defined on an object other than "this" can not be removed. testSame("({a:2})"); // but doesn't prevent the removal of the definition on 'this'. test("({a:0}); this.a = 1;", "({a:0});1"); // Some use of the property "a" prevents the removal. testSame("x = ({a:0}); this.a = 1; alert(x.a)"); } public void testExtern() { // A property defined in the externs is can not be removed. testSame("this.ext = 2"); } public void testExport() { // An exported property can not be removed. testSame("this.ext = 2; window['export'] = this.ext;"); testSame("function f() { this.ext = 2; } window['export'] = this.ext;"); } public void testAssignOp1() { // Properties defined using a compound assignment can be removed if the // result of the assignment expression is not immediately used. test("this.x += 2", "2"); testSame("x = (this.x += 2)"); testSame("this.x += 2; x = this.x;"); // But, of course, a later use prevents its removal. testSame("this.x += 2; x.x;"); } public void testAssignOp2() { // Properties defined using a compound assignment can be removed if the // result of the assignment expression is not immediately used. test("this.a += 2, f()", "2, f()"); test("x = (this.a += 2, f())", "x = (2, f())"); testSame("x = (f(), this.a += 2)"); } public void testInc1() { // Increments and Decrements are handled similarly to compound assignments // but need a placeholder value when replaced. test("this.x++", "0"); testSame("x = (this.x++)"); testSame("this.x++; x = this.x;"); test("--this.x", "0"); testSame("x = (--this.x)"); testSame("--this.x; x = this.x;"); } public void testInc2() { // Increments and Decrements are handled similarly to compound assignments // but need a placeholder value when replaced. test("this.a++, f()", "0, f()"); test("x = (this.a++, f())", "x = (0, f())"); testSame("x = (f(), this.a++)"); test("--this.a, f()", "0, f()"); test("x = (--this.a, f())", "x = (0, f())"); testSame("x = (f(), --this.a)"); } public void testJSCompiler_renameProperty() { // JSCompiler_renameProperty introduces a use of the property testSame("this.a = 2; x[JSCompiler_renameProperty('a')]"); testSame("this.a = 2; JSCompiler_renameProperty('a')"); } public void testForIn() { // This is the basic assumption that this pass makes: // it can remove properties even when the object is used in a FOR-IN loop test("this.y = 1;for (var a in x) { alert(x[a]) }", "1;for (var a in x) { alert(x[a]) }"); } public void testObjectKeys() { // This is the basic assumption that this pass makes: // it can remove properties even when the object are referenced test("this.y = 1;alert(Object.keys(this))", "1;alert(Object.keys(this))"); } public void testIssue730() { // Partial removal of properties can causes problems if the object is // sealed. // TODO(johnlenz): should we not allow partial removals? test( "function A() {this.foo = 0;}\n" + "function B() {this.a = new A();}\n" + "B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" + "new B().dostuff();\n", "function A(){0}" + "function B(){this.a=new A}" + "B.prototype.dostuff=function(){this.a.foo++;alert(\"hi\")};" + "new B().dostuff();"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckSuspiciousCodeTest.java0000644000175000017500000000636212115204405030435 0ustar apoapo/* * Copyright 2012 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; /** * Tests for CheckSuspiciousCode */ public class CheckSuspiciousCodeTest extends CompilerTestCase { public CheckSuspiciousCodeTest() { this.parseTypeInfo = true; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CombinedCompilerPass(compiler, new CheckSuspiciousCode()); } @Override protected int getNumRepetitions() { return 1; } public void test(String js, DiagnosticType error) { test(js, js, null, error); } public void testSuspiciousSemi() { final DiagnosticType e = CheckSuspiciousCode.SUSPICIOUS_SEMICOLON; final DiagnosticType ok = null; // code is 'ok', verify no warning test("if(x()) x = y;", ok); test("if(x()); x = y;", e); // I've had this bug, damned ; test("if(x()){} x = y;", ok); test("if(x()) x = y; else y=z;", ok); test("if(x()); else y=z;", e); test("if(x()){} else y=z;", ok); test("if(x()) x = y; else;", e); test("if(x()) x = y; else {}", ok); test("while(x()) x = y;", ok); test("while(x()); x = y;", e); test("while(x()){} x = y;", ok); test("while(x()); {x = y}", e); test("while(x()){} {x = y}", ok); test("for(;;) x = y;", ok); test("for(;;); x = y;", e); test("for(;;){} x = y;", ok); test("for(x in y) x = y;", ok); test("for(x in y); x = y;", e); test("for(x in y){} x = y;", ok); } private void testReportNaN(String js) { testSame(js, CheckSuspiciousCode.SUSPICIOUS_COMPARISON_WITH_NAN); } public void testComparison1() { testReportNaN("x == NaN"); testReportNaN("x != NaN"); testReportNaN("x === NaN"); testReportNaN("x !== NaN"); testReportNaN("x < NaN"); testReportNaN("x <= NaN"); testReportNaN("x > NaN"); testReportNaN("x >= NaN"); } public void testComparison2() { testReportNaN("NaN == x"); testReportNaN("NaN != x"); testReportNaN("NaN === x"); testReportNaN("NaN !== x"); testReportNaN("NaN < x"); testReportNaN("NaN <= x"); testReportNaN("NaN > x"); testReportNaN("NaN >= x"); } public void testComparison3() { testReportNaN("x == 0/0"); testReportNaN("x != 0/0"); testReportNaN("x === 0/0"); testReportNaN("x !== 0/0"); testReportNaN("x < 0/0"); testReportNaN("x <= 0/0"); testReportNaN("x > 0/0"); testReportNaN("x >= 0/0"); } public void testComparison4() { testReportNaN("0/0 == x"); testReportNaN("0/0 != x"); testReportNaN("0/0 === x"); testReportNaN("0/0 !== x"); testReportNaN("0/0 < x"); testReportNaN("0/0 <= x"); testReportNaN("0/0 > x"); testReportNaN("0/0 >= x"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/JSModuleGraphTest.java0000644000175000017500000002732012115204405027177 0ustar apoapo/* * 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.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import junit.framework.*; import java.util.*; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * Tests for {@link JSModuleGraph} * */ public class JSModuleGraphTest extends TestCase { private final JSModule A = new JSModule("A"); private final JSModule B = new JSModule("B"); private final JSModule C = new JSModule("C"); private final JSModule D = new JSModule("D"); private final JSModule E = new JSModule("E"); private final JSModule F = new JSModule("F"); private JSModuleGraph graph = null; // For resolving dependencies only. private Compiler compiler; @Override public void setUp() throws Exception { super.setUp(); B.addDependency(A); // __A__ C.addDependency(A); // / | \ D.addDependency(B); // B C | E.addDependency(B); // / \ /| | E.addDependency(C); // D E | / F.addDependency(A); // \|/ F.addDependency(C); // F F.addDependency(E); graph = new JSModuleGraph(new JSModule[] {A, B, C, D, E, F}); compiler = new Compiler(); } public void testModuleDepth() { assertEquals("A should have depth 0", 0, A.getDepth()); assertEquals("B should have depth 1", 1, B.getDepth()); assertEquals("C should have depth 1", 1, C.getDepth()); assertEquals("D should have depth 2", 2, D.getDepth()); assertEquals("E should have depth 2", 2, E.getDepth()); assertEquals("F should have depth 3", 3, F.getDepth()); } public void testDeepestCommonDep() { assertDeepestCommonDep(null, A, A); assertDeepestCommonDep(null, A, B); assertDeepestCommonDep(null, A, C); assertDeepestCommonDep(null, A, D); assertDeepestCommonDep(null, A, E); assertDeepestCommonDep(null, A, F); assertDeepestCommonDep(A, B, B); assertDeepestCommonDep(A, B, C); assertDeepestCommonDep(A, B, D); assertDeepestCommonDep(A, B, E); assertDeepestCommonDep(A, B, F); assertDeepestCommonDep(A, C, C); assertDeepestCommonDep(A, C, D); assertDeepestCommonDep(A, C, E); assertDeepestCommonDep(A, C, F); assertDeepestCommonDep(B, D, D); assertDeepestCommonDep(B, D, E); assertDeepestCommonDep(B, D, F); assertDeepestCommonDep(C, E, E); assertDeepestCommonDep(C, E, F); assertDeepestCommonDep(E, F, F); } public void testDeepestCommonDepInclusive() { assertDeepestCommonDepInclusive(A, A, A); assertDeepestCommonDepInclusive(A, A, B); assertDeepestCommonDepInclusive(A, A, C); assertDeepestCommonDepInclusive(A, A, D); assertDeepestCommonDepInclusive(A, A, E); assertDeepestCommonDepInclusive(A, A, F); assertDeepestCommonDepInclusive(B, B, B); assertDeepestCommonDepInclusive(A, B, C); assertDeepestCommonDepInclusive(B, B, D); assertDeepestCommonDepInclusive(B, B, E); assertDeepestCommonDepInclusive(B, B, F); assertDeepestCommonDepInclusive(C, C, C); assertDeepestCommonDepInclusive(A, C, D); assertDeepestCommonDepInclusive(C, C, E); assertDeepestCommonDepInclusive(C, C, F); assertDeepestCommonDepInclusive(D, D, D); assertDeepestCommonDepInclusive(B, D, E); assertDeepestCommonDepInclusive(B, D, F); assertDeepestCommonDepInclusive(E, E, E); assertDeepestCommonDepInclusive(E, E, F); assertDeepestCommonDepInclusive(F, F, F); } public void testGetTransitiveDepsDeepestFirst() { assertTransitiveDepsDeepestFirst(A); assertTransitiveDepsDeepestFirst(B, A); assertTransitiveDepsDeepestFirst(C, A); assertTransitiveDepsDeepestFirst(D, B, A); assertTransitiveDepsDeepestFirst(E, C, B, A); assertTransitiveDepsDeepestFirst(F, E, C, B, A); } public void testCoalesceDuplicateFiles() { A.add(SourceFile.fromCode("a.js", "")); B.add(SourceFile.fromCode("a.js", "")); B.add(SourceFile.fromCode("b.js", "")); C.add(SourceFile.fromCode("b.js", "")); C.add(SourceFile.fromCode("c.js", "")); E.add(SourceFile.fromCode("c.js", "")); E.add(SourceFile.fromCode("d.js", "")); graph.coalesceDuplicateFiles(); assertEquals(2, A.getInputs().size()); assertEquals("a.js", A.getInputs().get(0).getName()); assertEquals("b.js", A.getInputs().get(1).getName()); assertEquals(0, B.getInputs().size()); assertEquals(1, C.getInputs().size()); assertEquals("c.js", C.getInputs().get(0).getName()); assertEquals(1, E.getInputs().size()); assertEquals("d.js", E.getInputs().get(0).getName()); } public void testManageDependencies1() throws Exception { List inputs = setUpManageDependenciesTest(); List results = graph.manageDependencies( ImmutableList.of(), inputs); assertInputs(A, "a1", "a3"); assertInputs(B, "a2", "b2"); assertInputs(C); // no inputs assertInputs(E, "c1", "e1", "e2"); assertEquals( Lists.newArrayList("a1", "a3", "a2", "b2", "c1", "e1", "e2"), sourceNames(results)); } public void testManageDependencies2() throws Exception { List inputs = setUpManageDependenciesTest(); List results = graph.manageDependencies( ImmutableList.of("c2"), inputs); assertInputs(A, "a1", "a3"); assertInputs(B, "a2", "b2"); assertInputs(C, "c1", "c2"); assertInputs(E, "e1", "e2"); assertEquals( Lists.newArrayList("a1", "a3", "a2", "b2", "c1", "c2", "e1", "e2"), sourceNames(results)); } public void testManageDependencies3() throws Exception { List inputs = setUpManageDependenciesTest(); DependencyOptions depOptions = new DependencyOptions(); depOptions.setDependencySorting(true); depOptions.setDependencyPruning(true); depOptions.setMoocherDropping(true); depOptions.setEntryPoints(ImmutableList.of("c2")); List results = graph.manageDependencies( depOptions, inputs); // Everything gets pushed up into module c, because that's // the only one that has entry points. assertInputs(A); assertInputs(B); assertInputs(C, "a1", "c1", "c2"); assertInputs(E); assertEquals( Lists.newArrayList("a1", "c1", "c2"), sourceNames(results)); } public void testManageDependencies4() throws Exception { setUpManageDependenciesTest(); DependencyOptions depOptions = new DependencyOptions(); depOptions.setDependencySorting(true); List inputs = Lists.newArrayList(); // Add the inputs in a random order. inputs.addAll(E.getInputs()); inputs.addAll(B.getInputs()); inputs.addAll(A.getInputs()); inputs.addAll(C.getInputs()); List results = graph.manageDependencies( depOptions, inputs); assertInputs(A, "a1", "a2", "a3"); assertInputs(B, "b1", "b2"); assertInputs(C, "c1", "c2"); assertInputs(E, "e1", "e2"); assertEquals( Lists.newArrayList( "a1", "a2", "a3", "b1", "b2", "c1", "c2", "e1", "e2"), sourceNames(results)); } public void testNoFiles() throws Exception { DependencyOptions depOptions = new DependencyOptions(); depOptions.setDependencySorting(true); List inputs = Lists.newArrayList(); List results = graph.manageDependencies( depOptions, inputs); assertTrue(results.isEmpty()); } public void testToJson() throws JSONException { JSONArray modules = graph.toJson(); assertEquals(6, modules.length()); for (int i = 0; i < modules.length(); i++) { JSONObject m = modules.getJSONObject(i); assertNotNull(m.getString("name")); assertNotNull(m.getJSONArray("dependencies")); assertNotNull(m.getJSONArray("transitive-dependencies")); assertNotNull(m.getJSONArray("inputs")); } JSONObject m = modules.getJSONObject(3); assertEquals("D", m.getString("name")); assertEquals("[\"B\"]", m.getJSONArray("dependencies").toString()); assertEquals(2, m.getJSONArray("transitive-dependencies").length()); assertEquals("[]", m.getJSONArray("inputs").toString()); } private List setUpManageDependenciesTest() { List inputs = Lists.newArrayList(); A.add(code("a1", provides("a1"), requires())); A.add(code("a2", provides("a2"), requires("a1"))); A.add(code("a3", provides(), requires("a1"))); B.add(code("b1", provides("b1"), requires("a2"))); B.add(code("b2", provides(), requires("a1", "a2"))); C.add(code("c1", provides("c1"), requires("a1"))); C.add(code("c2", provides("c2"), requires("c1"))); E.add(code("e1", provides(), requires("c1"))); E.add(code("e2", provides(), requires("c1"))); inputs.addAll(A.getInputs()); inputs.addAll(B.getInputs()); inputs.addAll(C.getInputs()); inputs.addAll(E.getInputs()); for (CompilerInput input : inputs) { input.setCompiler(compiler); } return inputs; } private void assertInputs(JSModule module, String ... sourceNames) { assertEquals( Lists.newArrayList(sourceNames), sourceNames(module.getInputs())); } private List sourceNames(List inputs) { List inputNames = Lists.newArrayList(); for (CompilerInput input : inputs) { inputNames.add(input.getName()); } return inputNames; } private SourceFile code( String sourceName, List provides, List requires) { String text = ""; for (String p : provides) { text += "goog.provide('" + p + "');\n"; } for (String r : requires) { text += "goog.require('" + r + "');\n"; } return SourceFile.fromCode(sourceName, text); } private List provides(String ... strings) { return Lists.newArrayList(strings); } private List requires(String ... strings) { return Lists.newArrayList(strings); } private void assertDeepestCommonDepInclusive( JSModule expected, JSModule m1, JSModule m2) { assertDeepestCommonDepOneWay(expected, m1, m2, true); assertDeepestCommonDepOneWay(expected, m2, m1, true); } private void assertDeepestCommonDep( JSModule expected, JSModule m1, JSModule m2) { assertDeepestCommonDepOneWay(expected, m1, m2, false); assertDeepestCommonDepOneWay(expected, m2, m1, false); } private void assertDeepestCommonDepOneWay( JSModule expected, JSModule m1, JSModule m2, boolean inclusive) { JSModule actual = inclusive ? graph.getDeepestCommonDependencyInclusive(m1, m2) : graph.getDeepestCommonDependency(m1, m2); if (actual != expected) { fail(String.format( "Deepest common dep of %s and %s should be %s but was %s", m1.getName(), m2.getName(), expected == null ? "null" : expected.getName(), actual == null ? "null" : actual.getName())); } } private void assertTransitiveDepsDeepestFirst(JSModule m, JSModule... deps) { Iterable actual = graph.getTransitiveDepsDeepestFirst(m); assertEquals(Arrays.toString(deps), Arrays.toString(Iterables.toArray(actual, JSModule.class))); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExpressionDecomposerTest.java0000644000175000017500000005217112115204405030715 0ustar apoapo/* * 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.Sets; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.ExpressionDecomposer.DecompositionType; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.util.Set; import javax.annotation.Nullable; /** * Unit tests for ExpressionDecomposer * @author johnlenz@google.com (John Lenz) */ public class ExpressionDecomposerTest extends TestCase { // Note: functions "foo" and "goo" are external functions // in the helper. public void testCanExposeExpression1() { // Can't move or decompose some classes of expressions. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "while(foo());", "foo"); helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "while(x = goo()&&foo()){}", "foo"); helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "while(x += goo()&&foo()){}", "foo"); helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "do{}while(foo());", "foo"); helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "for(;foo(););", "foo"); // This case could be supported for loops without conditional continues // by moving the increment into the loop body. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "for(;;foo());", "foo"); // FOR initializer could be supported but they never occur // as they are normalized away. // This is potentially doable but a bit too complex currently. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "switch(1){case foo():;}", "foo"); } public void testCanExposeExpression2() { helperCanExposeExpression( DecompositionType.MOVABLE, "foo()", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "x = foo()", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "var x = foo()", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "if(foo()){}", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "switch(foo()){}", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "switch(foo()){}", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "function f(){ return foo();}", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "x = foo() && 1", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "x = foo() || 1", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "x = foo() ? 0 : 1", "foo"); helperCanExposeExpression( DecompositionType.MOVABLE, "(function(a){b = a})(foo())", "foo"); } public void testCanExposeExpression3() { helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "x = 0 && foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "x = 1 || foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "var x = 1 ? foo() : 0", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "goo() && foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "x = goo() && foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "x += goo() && foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "var x = goo() && foo()", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "if(goo() && foo()){}", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "switch(goo() && foo()){}", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "switch(goo() && foo()){}", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "switch(x = goo() && foo()){}", "foo"); helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "function f(){ return goo() && foo();}", "foo"); } public void testCanExposeExpression4() { // 'this' must be preserved in call. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "if (goo.a(1, foo()));", "foo"); } public void testCanExposeExpression5() { // 'this' must be preserved in call. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "if (goo['a'](foo()));", "foo"); } public void testCanExposeExpression6() { // 'this' must be preserved in call. helperCanExposeExpression( DecompositionType.UNDECOMPOSABLE, "z:if (goo.a(1, foo()));", "foo"); } public void testCanExposeExpression7() { // Verify calls to function expressions are movable. helperCanExposeFunctionExpression( DecompositionType.MOVABLE, "(function(map){descriptions_=map})(\n" + "function(){\n" + "var ret={};\n" + "ret[INIT]='a';\n" + "ret[MIGRATION_BANNER_DISMISS]='b';\n" + "return ret\n" + "}()\n" + ");", 2); } public void testCanExposeExpression8() { // Can it be decompose? helperCanExposeExpression( DecompositionType.DECOMPOSABLE, "HangoutStarter.prototype.launchHangout = function() {\n" + " var self = a.b;\n" + " var myUrl = new goog.Uri(getDomServices_(self).getDomHelper()." + "getWindow().location.href);\n" + "};", "getDomServices_"); // Verify it is properly expose the target expression. helperExposeExpression( "HangoutStarter.prototype.launchHangout = function() {\n" + " var self = a.b;\n" + " var myUrl = new goog.Uri(getDomServices_(self).getDomHelper()." + "getWindow().location.href);\n" + "};", "getDomServices_", "HangoutStarter.prototype.launchHangout = function() {" + " var self = a.b;" + " var temp_const$$0 = goog.Uri;" + " var myUrl = new temp_const$$0(getDomServices_(self)." + " getDomHelper().getWindow().location.href)}"); // Verify the results can be properly moved. helperMoveExpression( "HangoutStarter.prototype.launchHangout = function() {" + " var self = a.b;" + " var temp_const$$0 = goog.Uri;" + " var myUrl = new temp_const$$0(getDomServices_(self)." + " getDomHelper().getWindow().location.href)}", "getDomServices_", "HangoutStarter.prototype.launchHangout = function() {" + " var self=a.b;" + " var temp_const$$0=goog.Uri;" + " var result$$0=getDomServices_(self);" + " var myUrl=new temp_const$$0(result$$0.getDomHelper()." + " getWindow().location.href)}"); } public void testMoveExpression1() { // There isn't a reason to do this, but it works. helperMoveExpression("foo()", "foo", "var result$$0 = foo(); result$$0;"); } public void testMoveExpression2() { helperMoveExpression( "x = foo()", "foo", "var result$$0 = foo(); x = result$$0;"); } public void testMoveExpression3() { helperMoveExpression( "var x = foo()", "foo", "var result$$0 = foo(); var x = result$$0;"); } public void testMoveExpression4() { helperMoveExpression( "if(foo()){}", "foo", "var result$$0 = foo(); if (result$$0);"); } public void testMoveExpression5() { helperMoveExpression( "switch(foo()){}", "foo", "var result$$0 = foo(); switch(result$$0){}"); } public void testMoveExpression6() { helperMoveExpression( "switch(1 + foo()){}", "foo", "var result$$0 = foo(); switch(1 + result$$0){}"); } public void testMoveExpression7() { helperMoveExpression( "function f(){ return foo();}", "foo", "function f(){ var result$$0 = foo(); return result$$0;}"); } public void testMoveExpression8() { helperMoveExpression( "x = foo() && 1", "foo", "var result$$0 = foo(); x = result$$0 && 1"); } public void testMoveExpression9() { helperMoveExpression( "x = foo() || 1", "foo", "var result$$0 = foo(); x = result$$0 || 1"); } public void testMoveExpression10() { helperMoveExpression( "x = foo() ? 0 : 1", "foo", "var result$$0 = foo(); x = result$$0 ? 0 : 1"); } /* Decomposition tests. */ public void testExposeExpression1() { helperExposeExpression( "x = 0 && foo()", "foo", "var temp$$0; if (temp$$0 = 0) temp$$0 = foo(); x = temp$$0;"); } public void testExposeExpression2() { helperExposeExpression( "x = 1 || foo()", "foo", "var temp$$0; if (temp$$0 = 1); else temp$$0 = foo(); x = temp$$0;"); } public void testExposeExpression3() { helperExposeExpression( "var x = 1 ? foo() : 0", "foo", "var temp$$0;" + " if (1) temp$$0 = foo(); else temp$$0 = 0;var x = temp$$0;"); } public void testExposeExpression4() { helperExposeExpression( "goo() && foo()", "foo", "if (goo()) foo();"); } public void testExposeExpression5() { helperExposeExpression( "x = goo() && foo()", "foo", "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo(); x = temp$$0;"); } public void testExposeExpression6() { helperExposeExpression( "var x = 1 + (goo() && foo())", "foo", "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo();" + "var x = 1 + temp$$0;"); } public void testExposeExpression7() { helperExposeExpression( "if(goo() && foo());", "foo", "var temp$$0;" + "if (temp$$0 = goo()) temp$$0 = foo();" + "if(temp$$0);"); } public void testExposeExpression8() { helperExposeExpression( "switch(goo() && foo()){}", "foo", "var temp$$0;" + "if (temp$$0 = goo()) temp$$0 = foo();" + "switch(temp$$0){}"); } public void testExposeExpression9() { helperExposeExpression( "switch(1 + goo() + foo()){}", "foo", "var temp_const$$0 = 1 + goo();" + "switch(temp_const$$0 + foo()){}"); } public void testExposeExpression10() { helperExposeExpression( "function f(){ return goo() && foo();}", "foo", "function f(){" + "var temp$$0; if (temp$$0 = goo()) temp$$0 = foo();" + "return temp$$0;" + "}"); } public void testExposeExpression11() { // TODO(johnlenz): We really want a constant marking pass. // The value "goo" should be constant, but it isn't known to be so. helperExposeExpression( "if (goo(1, goo(2), (1 ? foo() : 0)));", "foo", "var temp_const$$1 = goo;" + "var temp_const$$0 = goo(2);" + "var temp$$2;" + "if (1) temp$$2 = foo(); else temp$$2 = 0;" + "if (temp_const$$1(1, temp_const$$0, temp$$2));"); } // Simple name on LHS of assignment-op. public void testExposePlusEquals1() { helperExposeExpression( "var x = 0; x += foo() + 1", "foo", "var x = 0; var temp_const$$0 = x;" + "x = temp_const$$0 + (foo() + 1);"); helperExposeExpression( "var x = 0; y = (x += foo()) + x", "foo", "var x = 0; var temp_const$$0 = x;" + "y = (x = temp_const$$0 + foo()) + x"); } // Structure on LHS of assignment-op. public void testExposePlusEquals2() { helperExposeExpression( "var x = {}; x.a += foo() + 1", "foo", "var x = {}; var temp_const$$0 = x;" + "var temp_const$$1 = temp_const$$0.a;" + "temp_const$$0.a = temp_const$$1 + (foo() + 1);"); helperExposeExpression( "var x = {}; y = (x.a += foo()) + x.a", "foo", "var x = {}; var temp_const$$0 = x;" + "var temp_const$$1 = temp_const$$0.a;" + "y = (temp_const$$0.a = temp_const$$1 + foo()) + x.a"); } // Constant object on LHS of assignment-op. public void testExposePlusEquals3() { helperExposeExpression( "/** @const */ var XX = {};\n" + "XX.a += foo() + 1", "foo", "var XX = {}; var temp_const$$0 = XX.a;" + "XX.a = temp_const$$0 + (foo() + 1);"); helperExposeExpression( "var XX = {}; y = (XX.a += foo()) + XX.a", "foo", "var XX = {}; var temp_const$$0 = XX.a;" + "y = (XX.a = temp_const$$0 + foo()) + XX.a"); } // Function all on LHS of assignment-op. public void testExposePlusEquals4() { helperExposeExpression( "var x = {}; goo().a += foo() + 1", "foo", "var x = {};" + "var temp_const$$0 = goo();" + "var temp_const$$1 = temp_const$$0.a;" + "temp_const$$0.a = temp_const$$1 + (foo() + 1);"); helperExposeExpression( "var x = {}; y = (goo().a += foo()) + goo().a", "foo", "var x = {};" + "var temp_const$$0 = goo();" + "var temp_const$$1 = temp_const$$0.a;" + "y = (temp_const$$0.a = temp_const$$1 + foo()) + goo().a"); } // Test multiple levels public void testExposePlusEquals5() { helperExposeExpression( "var x = {}; goo().a.b += foo() + 1", "foo", "var x = {};" + "var temp_const$$0 = goo().a;" + "var temp_const$$1 = temp_const$$0.b;" + "temp_const$$0.b = temp_const$$1 + (foo() + 1);"); helperExposeExpression( "var x = {}; y = (goo().a.b += foo()) + goo().a", "foo", "var x = {};" + "var temp_const$$0 = goo().a;" + "var temp_const$$1 = temp_const$$0.b;" + "y = (temp_const$$0.b = temp_const$$1 + foo()) + goo().a"); } public void testExposeObjectLit1() { // Validate that getter and setters methods are see as side-effect // free and that values can move past them. We don't need to be // concerned with exposing the getter or setter here but the // decomposer does not have a method of exposing properties only variables. helperMoveExpression( "var x = {get a() {}, b: foo()};", "foo", "var result$$0=foo();var x = {get a() {}, b: result$$0};"); helperMoveExpression( "var x = {set a(p) {}, b: foo()};", "foo", "var result$$0=foo();var x = {set a(p) {}, b: result$$0};"); } /** Test case helpers. */ private void helperCanExposeExpression( DecompositionType expectedResult, String code, String fnName ) { helperCanExposeExpression(expectedResult, code, fnName, null); } private void helperCanExposeFunctionExpression( DecompositionType expectedResult, String code, int call) { Compiler compiler = getCompiler(); Set knownConstants = Sets.newHashSet(); ExpressionDecomposer decomposer = new ExpressionDecomposer( compiler, compiler.getUniqueNameIdSupplier(), knownConstants); Node tree = parse(compiler, code); assertNotNull(tree); Node externsRoot = parse(compiler, "function goo() {}" + "function foo() {}"); assertNotNull(externsRoot); Node callSite = findCall(tree, null, 2); assertNotNull("Call " + call + " was not found.", callSite); compiler.resetUniqueNameId(); DecompositionType result = decomposer.canExposeExpression( callSite); assertEquals(expectedResult, result); } private void helperCanExposeExpression( DecompositionType expectedResult, String code, String fnName, Set knownConstants ) { Compiler compiler = getCompiler(); if (knownConstants == null) { knownConstants = Sets.newHashSet(); } ExpressionDecomposer decomposer = new ExpressionDecomposer( compiler, compiler.getUniqueNameIdSupplier(), knownConstants); Node tree = parse(compiler, code); assertNotNull(tree); Node externsRoot = parse(compiler, "function goo() {}" + "function foo() {}"); assertNotNull(externsRoot); Node callSite = findCall(tree, fnName); assertNotNull("Call to " + fnName + " was not found.", callSite); compiler.resetUniqueNameId(); DecompositionType result = decomposer.canExposeExpression( callSite); assertEquals(expectedResult, result); } private void helperExposeExpression( String code, String fnName, String expectedResult ) { helperExposeExpression( code, fnName, expectedResult, null); } private void validateSourceInfo(Compiler compiler, Node subtree) { (new LineNumberCheck(compiler)).setCheckSubTree(subtree); // Source information problems are reported as compiler errors. if (compiler.getErrorCount() != 0) { String msg = "Error encountered: "; for (JSError err : compiler.getErrors()) { msg += err.toString() + "\n"; } assertTrue(msg, compiler.getErrorCount() == 0); } } private void helperExposeExpression( String code, String fnName, String expectedResult, Set knownConstants ) { Compiler compiler = getCompiler(); if (knownConstants == null) { knownConstants = Sets.newHashSet(); } ExpressionDecomposer decomposer = new ExpressionDecomposer( compiler, compiler.getUniqueNameIdSupplier(), knownConstants); decomposer.setTempNamePrefix("temp"); decomposer.setResultNamePrefix("result"); Node expectedRoot = parse(compiler, expectedResult); Node tree = parse(compiler, code); assertNotNull(tree); Node callSite = findCall(tree, fnName); assertNotNull("Call to " + fnName + " was not found.", callSite); DecompositionType result = decomposer.canExposeExpression(callSite); assertTrue(result == DecompositionType.DECOMPOSABLE); compiler.resetUniqueNameId(); decomposer.exposeExpression(callSite); validateSourceInfo(compiler, tree); String explanation = expectedRoot.checkTreeEquals(tree); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(tree) + "\n" + explanation, explanation); } private void helperMoveExpression( String code, String fnName, String expectedResult ) { helperMoveExpression( code, fnName, expectedResult, null); } private void helperMoveExpression( String code, String fnName, String expectedResult, Set knownConstants ) { Compiler compiler = getCompiler(); if (knownConstants == null) { knownConstants = Sets.newHashSet(); } ExpressionDecomposer decomposer = new ExpressionDecomposer( compiler, compiler.getUniqueNameIdSupplier(), knownConstants); decomposer.setTempNamePrefix("temp"); decomposer.setResultNamePrefix("result"); Node expectedRoot = parse(compiler, expectedResult); Node tree = parse(compiler, code); assertNotNull(tree); Node callSite = findCall(tree, fnName); assertNotNull("Call to " + fnName + " was not found.", callSite); compiler.resetUniqueNameId(); decomposer.moveExpression(callSite); validateSourceInfo(compiler, tree); String explanation = expectedRoot.checkTreeEquals(tree); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(tree) + "\n" + explanation, explanation); } private static Compiler getCompiler() { Compiler compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5); options.setCodingConvention(new GoogleCodingConvention()); compiler.initOptions(options); return compiler; } private static Node findCall(Node n, String name) { return findCall(n, name, 1); } /** * @param name The name to look for. * @param call The call to look for. * @return The return the Nth CALL node to name found in a pre-order * traversal. */ private static Node findCall( Node root, @Nullable final String name, final int call) { class Find { int found = 0; Node find(Node n) { if (n.isCall()) { Node callee = n.getFirstChild(); if (name == null || (callee.isName() && callee.getString().equals(name))) { found++; if (found == call) { return n; } } } for (Node c : n.children()) { Node result = find(c); if (result != null) { return result; } } return null; } } return (new Find()).find(root); } private static Node parse(Compiler compiler, String js) { Node n = Normalize.parseAndNormalizeTestCode(compiler, js); assertEquals(0, compiler.getErrorCount()); return n; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GlobalVarReferenceMapTest.java0000644000175000017500000002210212115204405030652 0ustar apoapo/* * Copyright 2011 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.javascript.jscomp.ReferenceCollectingCallback.Reference.createRefForTest; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.Map; /** * Unit-tests for the GlobalVarReferenceMap class. * * @author bashir@google.com (Bashir Sadjad) */ public class GlobalVarReferenceMapTest extends TestCase { private final CompilerInput INPUT1 = new CompilerInput(SourceFile.fromCode("input1", ""), false); private final CompilerInput INPUT2 = new CompilerInput(SourceFile.fromCode("input2", ""), false); private final CompilerInput INPUT3 = new CompilerInput(SourceFile.fromCode("input3", ""), false); private final CompilerInput EXTERN1 = new CompilerInput(SourceFile.fromCode("extern1", ""), true); private final GlobalVarReferenceMap map = new GlobalVarReferenceMap( Lists.newArrayList(INPUT1, INPUT2, INPUT3), Lists.newArrayList(EXTERN1)); private final Map globalMap = Maps.newHashMap(); private final Node root = new Node(Token.BLOCK); private final Scope globalScope = Scope.createGlobalScope(root); private Node scriptRoot = new Node(Token.SCRIPT); // In the initial setUp we have 3 references to var1 (one in each input) and // 2 references to var2 (in first and third inputs), and 2 references to var3 // (in second input and first extern) private static final String VAR1 = "var1"; private static final String VAR2 = "var2"; private static final String VAR3 = "var3"; private final ReferenceCollection var1Refs = new ReferenceCollection(); private final ReferenceCollection var2Refs = new ReferenceCollection(); private final ReferenceCollection var3Refs = new ReferenceCollection(); private final Reference var1In1Ref = createRefForTest(INPUT1); private final Reference var1In2Ref = createRefForTest(INPUT2); private final Reference var1In3Ref = createRefForTest(INPUT3); private final Reference var2In1Ref = createRefForTest(INPUT1); private final Reference var2In3Ref = createRefForTest(INPUT3); private final Reference var3In2Ref = createRefForTest(INPUT2); private final Reference var3In1Ext = createRefForTest(EXTERN1); @Override protected void setUp() throws Exception { super.setUp(); globalScope.declare(VAR1, new Node(Token.NAME), null, INPUT1); var1Refs.references = Lists.newArrayList(var1In1Ref, var1In2Ref, var1In3Ref); globalScope.declare(VAR2, new Node(Token.NAME), null, INPUT1); var2Refs.references = Lists.newArrayList(var2In1Ref, var2In3Ref); globalScope.declare(VAR3, new Node(Token.NAME), null, EXTERN1); var3Refs.references = Lists.newArrayList(var3In1Ext, var3In2Ref); // We recreate these two ReferenceCollection to keep var1Refs and // var2Refs intact in update operations for comparison in the tests. ReferenceCollection var1TempRefs = new ReferenceCollection(); var1TempRefs.references = Lists.newArrayList(var1Refs.references); ReferenceCollection var2TempRefs = new ReferenceCollection(); var2TempRefs.references = Lists.newArrayList(var2Refs.references); ReferenceCollection var3TempRefs = new ReferenceCollection(); var3TempRefs.references = Lists.newArrayList(var3Refs.references); globalMap.put(globalScope.getVar(VAR1), var1TempRefs); globalMap.put(globalScope.getVar(VAR2), var2TempRefs); globalMap.put(globalScope.getVar(VAR3), var3TempRefs); map.updateGlobalVarReferences(globalMap, root); scriptRoot.setInputId(INPUT2.getInputId()); scriptRoot.setSourceFileForTesting(INPUT2.getName()); } /** Tests whether the global variable references are set/reset properly. */ public void testUpdateGlobalVarReferences_ResetReferences() { // First we check the original setup then reset again. for (int i = 0; i < 2; i++) { assertEquals(var1Refs.references, map.getReferences(globalScope.getVar(VAR1)).references); assertEquals(var2Refs.references, map.getReferences(globalScope.getVar(VAR2)).references); assertEquals(var3Refs.references, map.getReferences(globalScope.getVar(VAR3)).references); map.updateGlobalVarReferences(globalMap, root); } } /** Removes all variable references in second script. */ public void testUpdateGlobalVarReferences_UpdateScriptNoRef() { Map scriptMap = Maps.newHashMap(); map.updateGlobalVarReferences(scriptMap, scriptRoot); ReferenceCollection refs = map.getReferences(globalScope.getVar(VAR2)); assertEquals(var2Refs.references, refs.references); refs = map.getReferences(globalScope.getVar(VAR1)); assertEquals(2, refs.references.size()); assertEquals(var1Refs.references.get(0), refs.references.get(0)); assertEquals(var1Refs.references.get(2), refs.references.get(1)); refs = map.getReferences(globalScope.getVar(VAR3)); assertEquals(1, refs.references.size()); assertEquals(var3Refs.references.get(0), refs.references.get(0)); } /** Changes variable references in second script. */ public void testUpdateGlobalVarReferences_UpdateScriptNewRefs() { Map scriptMap = Maps.newHashMap(); ReferenceCollection newVar1Refs = new ReferenceCollection(); Reference newVar1In2Ref = createRefForTest(INPUT2); newVar1Refs.references = Lists.newArrayList(newVar1In2Ref); ReferenceCollection newVar2Refs = new ReferenceCollection(); Reference newVar2In2Ref = createRefForTest(INPUT2); newVar2Refs.references = Lists.newArrayList(newVar2In2Ref); ReferenceCollection newVar3Refs = new ReferenceCollection(); Reference newVar3In2Ref = createRefForTest(INPUT2); newVar3Refs.references = Lists.newArrayList(newVar3In2Ref); scriptMap.put(globalScope.getVar(VAR1), newVar1Refs); scriptMap.put(globalScope.getVar(VAR2), newVar2Refs); scriptMap.put(globalScope.getVar(VAR3), newVar3Refs); map.updateGlobalVarReferences(scriptMap, scriptRoot); ReferenceCollection refs = map.getReferences(globalScope.getVar(VAR1)); assertEquals(3, refs.references.size()); assertEquals(var1Refs.references.get(0), refs.references.get(0)); assertEquals(newVar1In2Ref, refs.references.get(1)); assertEquals(var1Refs.references.get(2), refs.references.get(2)); refs = map.getReferences(globalScope.getVar(VAR2)); assertEquals(3, refs.references.size()); assertEquals(var2Refs.references.get(0), refs.references.get(0)); assertEquals(newVar2In2Ref, refs.references.get(1)); assertEquals(var2Refs.references.get(1), refs.references.get(2)); refs = map.getReferences(globalScope.getVar(VAR3)); assertEquals(2, refs.references.size()); assertEquals(var3Refs.references.get(0), refs.references.get(0)); assertEquals(newVar3In2Ref, refs.references.get(1)); } /** Changes variable references in second script. */ public void testUpdateGlobalVarReferences_UpdateScriptNewVar() { Map scriptMap = Maps.newHashMap(); final String var4 = "var4"; globalScope.declare(var4, new Node(Token.NAME), null, INPUT2); ReferenceCollection newVar3Refs = new ReferenceCollection(); Reference newVar3In2Ref = createRefForTest(INPUT2); newVar3Refs.references = Lists.newArrayList(newVar3In2Ref); scriptMap.put(globalScope.getVar(var4), newVar3Refs); map.updateGlobalVarReferences(scriptMap, scriptRoot); ReferenceCollection refs = map.getReferences(globalScope.getVar(var4)); assertEquals(1, refs.references.size()); assertEquals(newVar3In2Ref, refs.references.get(0)); } public void testUpdateReferencesWithGlobalScope() { Scope newGlobalScope = Scope.createGlobalScope(root); map.updateReferencesWithGlobalScope(newGlobalScope); ReferenceCollection references = map.getReferences(globalScope.getVar(VAR1)); for (Reference ref : references) { assertEquals(newGlobalScope, ref.getScope()); } references = map.getReferences(globalScope.getVar(VAR2)); for (Reference ref : references) { assertEquals(newGlobalScope, ref.getScope()); } references = map.getReferences(globalScope.getVar(VAR3)); for (Reference ref : references) { assertEquals(newGlobalScope, ref.getScope()); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MoveFunctionDeclarationsTest.java0000644000175000017500000000264612115204405031504 0ustar apoapo/* * 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; /** * Tests for {@link MoveFunctionDeclarations} * */ public class MoveFunctionDeclarationsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new MoveFunctionDeclarations(compiler); } public void testFunctionDeclarations() { test("a; function f(){} function g(){}", "function f(){} function g(){} a"); } public void testFunctionDeclarationsInModule() { test(createModules("a; function f(){} function g(){}"), new String[] { "function f(){} function g(){} a" }); } public void testFunctionsExpression() { testSame("a; f = function(){}"); } public void testNoMoveDeepFunctionDeclarations() { testSame("a; if (a) function f(){};"); testSame("a; if (a) { function f(){} }"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CompilerTestCase.java0000644000175000017500000011246212115204405027103 0ustar apoapo/* * Copyright 2006 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; import junit.framework.TestCase; import java.io.IOException; import java.util.List; /** *

Base class for testing JS compiler classes that change * the node tree of a compiled JS input.

* *

Pulls in shared functionality from different test cases. Also supports * node tree comparison for input and output (instead of string comparison), * which makes it easier to write tests b/c you don't have to get the syntax * exactly correct to the spacing.

* */ public abstract class CompilerTestCase extends TestCase { /** Externs for the test */ private final List externsInputs; /** Whether to compare input and output as trees instead of strings */ private final boolean compareAsTree; /** Whether to parse type info from JSDoc comments */ protected boolean parseTypeInfo; /** Whether we check warnings without source information. */ private boolean allowSourcelessWarnings = false; /** True iff closure pass runs before pass being tested. */ private boolean closurePassEnabled = false; /** True iff type checking pass runs before pass being tested. */ private boolean typeCheckEnabled = false; /** Error level reported by type checker. */ private CheckLevel typeCheckLevel; /** Whether the Normalize pass runs before pass being tested. */ private boolean normalizeEnabled = false; /** Whether the expected JS strings should be normalized. */ private boolean normalizeExpected = false; /** Whether to check that all line number information is preserved. */ private boolean checkLineNumbers = true; /** Whether we expect parse warnings in the current test. */ private boolean expectParseWarningsThisTest = false; /** * An expected symbol table error. Only useful for testing the * symbol table error-handling. */ private DiagnosticType expectedSymbolTableError = null; /** * Whether the MarkNoSideEffectsCalls pass runs before the pass being tested */ private boolean markNoSideEffects = false; /** The most recently used Compiler instance. */ private Compiler lastCompiler; /** * Whether to acceptES5 source. */ private boolean acceptES5 = true; /** * Whether externs changes should be allowed for this pass. */ private boolean allowExternsChanges = false; /** * Whether the AST should be validated. */ private boolean astValidationEnabled = true; private String filename = "testcode"; /** * Constructs a test. * * @param externs Externs JS as a string * @param compareAsTree True to compare output & expected as a node tree. * 99% of the time you want to compare as a tree. There are a few * special cases where you don't, like if you want to test the code * printing of "unnatural" syntax trees. For example, * *
   * IF
   *   IF
   *     STATEMENT
   * ELSE
   *   STATEMENT
   * 
*/ protected CompilerTestCase(String externs, boolean compareAsTree) { this.externsInputs = ImmutableList.of( SourceFile.fromCode("externs", externs)); this.compareAsTree = compareAsTree; this.parseTypeInfo = false; } /** * Constructs a test. Uses AST comparison. * @param externs Externs JS as a string */ protected CompilerTestCase(String externs) { this(externs, true); } /** * Constructs a test. Uses AST comparison and no externs. */ protected CompilerTestCase() { this("", true); } @Override protected void tearDown() throws Exception { super.tearDown(); expectParseWarningsThisTest = false; } /** * Gets the compiler pass instance to use for a test. * * @param compiler The compiler * @return The pass to test */ protected abstract CompilerPass getProcessor(Compiler compiler); /** * Gets the compiler options to use for this test. Use getProcessor to * determine what passes should be run. */ protected CompilerOptions getOptions() { return getOptions(new CompilerOptions()); } /** * Gets the compiler options to use for this test. Use getProcessor to * determine what passes should be run. */ protected CompilerOptions getOptions(CompilerOptions options) { if (this.acceptES5) { options.setLanguageIn(LanguageMode.ECMASCRIPT5); } // This doesn't affect whether checkSymbols is run--it just affects // whether variable warnings are filtered. options.checkSymbols = true; options.setWarningLevel( DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.INVALID_CASTS, CheckLevel.WARNING); options.setCodingConvention(getCodingConvention()); return options; } protected CodingConvention getCodingConvention() { return new GoogleCodingConvention(); } public void setFilename(String filename) { this.filename = filename; } /** * Returns the number of times the pass should be run before results are * verified. */ protected int getNumRepetitions() { // Since most compiler passes should be idempotent, we run each pass twice // by default. return 2; } /** Expect warnings without source information. */ void allowSourcelessWarnings() { allowSourcelessWarnings = true; } /** The most recently used JSComp instance. */ Compiler getLastCompiler() { return lastCompiler; } /** * Whether to allow ECMASCRIPT5 source parsing. */ protected void enableEcmaScript5(boolean acceptES5) { this.acceptES5 = acceptES5; } /** * Whether to allow externs changes. */ protected void allowExternsChanges(boolean allowExternsChanges) { this.allowExternsChanges = allowExternsChanges; } /** * Perform type checking before running the test pass. This will check * for type errors and annotate nodes with type information. * * @param level the level of severity to report for type errors * * @see TypeCheck */ public void enableTypeCheck(CheckLevel level) { typeCheckEnabled = true; typeCheckLevel = level; } /** * Check to make sure that line numbers were preserved. */ public void enableLineNumberCheck(boolean newVal) { checkLineNumbers = newVal; } /** * Do not run type checking before running the test pass. * * @see TypeCheck */ void disableTypeCheck() { typeCheckEnabled = false; } /** * Process closure library primitives. */ // TODO(nicksantos): Fix other passes to use this when appropriate. void enableClosurePass() { closurePassEnabled = true; } /** * Perform AST normalization before running the test pass, and anti-normalize * after running it. * * @see Normalize */ protected void enableNormalize() { enableNormalize(true); } /** * Perform AST normalization before running the test pass, and anti-normalize * after running it. * * @param normalizeExpected Whether to perform normalization on the * expected JS result. * @see Normalize */ protected void enableNormalize(boolean normalizeExpected) { normalizeEnabled = true; this.normalizeExpected = normalizeExpected; } /** * Don't perform AST normalization before running the test pass. * @see Normalize */ protected void disableNormalize() { normalizeEnabled = false; } /** * Run the MarkSideEffectCalls pass before running the test pass. * * @see MarkNoSideEffectCalls */ void enableMarkNoSideEffects() { markNoSideEffects = true; } /** * Whether to allow Validate the AST after each run of the pass. */ protected void enableAstValidation(boolean validate) { astValidationEnabled = validate; } /** Whether we should ignore parse warnings for the current test method. */ protected void setExpectParseWarningsThisTest() { expectParseWarningsThisTest = true; } /** Returns a newly created TypeCheck. */ private static TypeCheck createTypeCheck(Compiler compiler, CheckLevel level) { ReverseAbstractInterpreter rai = new SemanticReverseAbstractInterpreter(compiler.getCodingConvention(), compiler.getTypeRegistry()); return new TypeCheck(compiler, rai, compiler.getTypeRegistry(), level, CheckLevel.OFF); } /** * Verifies that the compiler pass's JS output matches the expected output. * * @param js Input * @param expected Expected JS output */ public void test(String js, String expected) { test(js, expected, (DiagnosticType) null); } /** * Verifies that the compiler pass's JS output matches the expected output, * or that an expected error is encountered. * * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected */ public void test(String js, String expected, DiagnosticType error) { test(js, expected, error, null); } /** * Verifies that the compiler pass's JS output matches the expected output, * or that an expected error is encountered. * * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The content of the error expected */ public void test(String js, String expected, DiagnosticType error, DiagnosticType warning, String description) { test(externsInputs, js, expected, error, warning, description); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void test(String js, String expected, DiagnosticType error, DiagnosticType warning) { test(externsInputs, js, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param externs Externs input * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void test(String externs, String js, String expected, DiagnosticType error, DiagnosticType warning) { test(externs, js, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param externs Externs input * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ public void test(String externs, String js, String expected, DiagnosticType error, DiagnosticType warning, String description) { List externsInputs = ImmutableList.of( SourceFile.fromCode("externs", externs)); test(externsInputs, js, expected, error, warning, description); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param externs Externs inputs * @param js Input * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ public void test(List externs, String js, String expected, DiagnosticType error, DiagnosticType warning, String description) { Compiler compiler = createCompiler(); lastCompiler = compiler; CompilerOptions options = getOptions(); if (this.acceptES5) { options.setLanguageIn(LanguageMode.ECMASCRIPT5); } // Note that in this context, turning on the checkTypes option won't // actually cause the type check to run. options.checkTypes = parseTypeInfo; compiler.init(externs, ImmutableList.of( SourceFile.fromCode(filename, js)), options); BaseJSTypeTestCase.addNativeProperties(compiler.getTypeRegistry()); test(compiler, maybeCreateArray(expected), error, warning, description); } private String[] maybeCreateArray(String expected) { if (expected != null) { return new String[] { expected }; } return null; } /** * Verifies that the compiler pass's JS output matches the expected output. * * @param js Inputs * @param expected Expected JS output */ public void test(String[] js, String[] expected) { test(js, expected, null); } /** * Verifies that the compiler pass's JS output matches the expected output, * or that an expected error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected */ public void test(String[] js, String[] expected, DiagnosticType error) { test(js, expected, error, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void test(String[] js, String[] expected, DiagnosticType error, DiagnosticType warning) { test(js, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param js Inputs * @param expected Expected JS output * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ public void test(String[] js, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { Compiler compiler = createCompiler(); lastCompiler = compiler; List inputs = Lists.newArrayList(); for (int i = 0; i < js.length; i++) { inputs.add(SourceFile.fromCode("input" + i, js[i])); } compiler.init(externsInputs, inputs, getOptions()); test(compiler, expected, error, warning, description); } /** * Verifies that the compiler pass's JS output matches the expected output. * * @param modules Module inputs * @param expected Expected JS outputs (one per module) */ public void test(JSModule[] modules, String[] expected) { test(modules, expected, null); } /** * Verifies that the compiler pass's JS output matches the expected output, * or that an expected error is encountered. * * @param modules Module inputs * @param expected Expected JS outputs (one per module) * @param error Expected error, or null if no error is expected */ public void test(JSModule[] modules, String[] expected, DiagnosticType error) { test(modules, expected, error, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param modules Module inputs * @param expected Expected JS outputs (one per module) * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void test(JSModule[] modules, String[] expected, DiagnosticType error, DiagnosticType warning) { Compiler compiler = createCompiler(); lastCompiler = compiler; compiler.initModules( externsInputs, Lists.newArrayList(modules), getOptions()); test(compiler, expected, error, warning); } /** * Verifies that the compiler pass's JS output is the same as its input. * * @param js Input and output */ public void testSame(String js) { test(js, js); } /** * Verifies that the compiler pass's JS output is the same as its input * and (optionally) that an expected warning is issued. * * @param js Input and output * @param warning Expected warning, or null if no warning is expected */ public void testSame(String js, DiagnosticType warning) { test(js, js, null, warning); } /** * Verifies that the compiler pass's JS output is the same as its input * and (optionally) that an expected warning is issued. * * @param js Input and output * @param diag Expected error or warning, or null if none is expected * @param error true if diag is an error, false if it is a warning */ public void testSame(String js, DiagnosticType diag, boolean error) { if (error) { test(js, js, diag); } else { test(js, js, null, diag); } } /** * Verifies that the compiler pass's JS output is the same as its input * and (optionally) that an expected warning is issued. * * @param externs Externs input * @param js Input and output * @param warning Expected warning, or null if no warning is expected */ public void testSame(String externs, String js, DiagnosticType warning) { testSame(externs, js, warning, null); } /** * Verifies that the compiler pass's JS output is the same as its input * and (optionally) that an expected warning is issued. * * @param externs Externs input * @param js Input and output * @param diag Expected error or warning, or null if none is expected * @param error true if diag is an error, false if it is a warning */ public void testSame( String externs, String js, DiagnosticType diag, boolean error) { if (error) { test(externs, js, js, diag, null); } else { test(externs, js, js, null, diag); } } /** * Verifies that the compiler pass's JS output is the same as its input * and (optionally) that an expected warning and description is issued. * * @param externs Externs input * @param js Input and output * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ public void testSame(String externs, String js, DiagnosticType warning, String description) { List externsInputs = ImmutableList.of( SourceFile.fromCode("externs", externs)); test(externsInputs, js, js, null, warning, description); } /** * Verifies that the compiler pass's JS output is the same as its input. * * @param js Inputs and outputs */ public void testSame(String[] js) { test(js, js); } /** * Verifies that the compiler pass's JS output is the same as its input, * and emits the given error. * * @param js Inputs and outputs * @param error Expected error, or null if no error is expected */ public void testSame(String[] js, DiagnosticType error) { test(js, js, error); } /** * Verifies that the compiler pass's JS output is the same as its input, * and emits the given error and warning. * * @param js Inputs and outputs * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ public void testSame( String[] js, DiagnosticType error, DiagnosticType warning) { test(js, js, error, warning); } /** * Verifies that the compiler pass's JS output is the same as the input. * * @param modules Module inputs */ public void testSame(JSModule[] modules) { testSame(modules, null); } /** * Verifies that the compiler pass's JS output is the same as the input. * * @param modules Module inputs * @param warning A warning, or null for no expected warning. */ public void testSame(JSModule[] modules, DiagnosticType warning) { try { String[] expected = new String[modules.length]; for (int i = 0; i < modules.length; i++) { expected[i] = ""; for (CompilerInput input : modules[i].getInputs()) { expected[i] += input.getSourceFile().getCode(); } } test(modules, expected, null, warning); } catch (IOException e) { throw new RuntimeException(e); } } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ protected void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning) { test(compiler, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ private void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { CodeChangeHandler recentChange = new CodeChangeHandler(); compiler.addChangeHandler(recentChange); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); if (!expectParseWarningsThisTest) { assertTrue("Unexpected parse warnings(s): " + Joiner.on("\n").join(compiler.getWarnings()), compiler.getWarnings().length == 0); } if (astValidationEnabled) { (new AstValidator()).validateRoot(root); } Node externsRoot = root.getFirstChild(); Node mainRoot = root.getLastChild(); // Save the tree for later comparison. Node rootClone = root.cloneTree(); Node externsRootClone = rootClone.getFirstChild(); Node mainRootClone = rootClone.getLastChild(); int numRepetitions = getNumRepetitions(); ErrorManager[] errorManagers = new ErrorManager[numRepetitions]; int aggregateWarningCount = 0; List aggregateWarnings = Lists.newArrayList(); boolean hasCodeChanged = false; assertFalse("Code should not change before processing", recentChange.hasCodeChanged()); for (int i = 0; i < numRepetitions; ++i) { if (compiler.getErrorCount() == 0) { errorManagers[i] = new BlackHoleErrorManager(compiler); // Only run process closure primitives once, if asked. if (closurePassEnabled && i == 0) { recentChange.reset(); new ProcessClosurePrimitives(compiler, null, CheckLevel.ERROR) .process(null, mainRoot); hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); } // Only run the type checking pass once, if asked. // Running it twice can cause unpredictable behavior because duplicate // objects for the same type are created, and the type system // uses reference equality to compare many types. if (typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } // Only run the normalize pass once, if asked. if (normalizeEnabled && i == 0) { normalizeActualCode(compiler, externsRoot, mainRoot); } if (markNoSideEffects && i == 0) { MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); } recentChange.reset(); getProcessor(compiler).process(externsRoot, mainRoot); if (astValidationEnabled) { (new AstValidator()).validateRoot(root); } if (checkLineNumbers) { (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); } hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); aggregateWarningCount += errorManagers[i].getWarningCount(); aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings())); if (normalizeEnabled) { boolean verifyDeclaredConstants = true; new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) .process(externsRoot, mainRoot); } } } if (error == null) { assertEquals( "Unexpected error(s): " + Joiner.on("\n").join(compiler.getErrors()), 0, compiler.getErrorCount()); // Verify the symbol table. ErrorManager symbolTableErrorManager = new BlackHoleErrorManager(compiler); Node expectedRoot = null; if (expected != null) { expectedRoot = parseExpectedJs(expected); expectedRoot.detachFromParent(); } JSError[] stErrors = symbolTableErrorManager.getErrors(); if (expectedSymbolTableError != null) { assertEquals("There should be one error.", 1, stErrors.length); assertEquals(expectedSymbolTableError, stErrors[0].getType()); } else { assertEquals("Unexpected symbol table error(s): " + Joiner.on("\n").join(stErrors), 0, stErrors.length); } if (warning == null) { assertEquals( "Unexpected warning(s): " + Joiner.on("\n").join(aggregateWarnings), 0, aggregateWarningCount); } else { assertEquals("There should be one warning, repeated " + numRepetitions + " time(s).", numRepetitions, aggregateWarningCount); for (int i = 0; i < numRepetitions; ++i) { JSError[] warnings = errorManagers[i].getWarnings(); JSError actual = warnings[0]; assertEquals(warning, actual.getType()); // Make sure that source information is always provided. if (!allowSourcelessWarnings) { assertTrue("Missing source file name in warning", actual.sourceName != null && !actual.sourceName.isEmpty()); assertTrue("Missing line number in warning", -1 != actual.lineNumber); assertTrue("Missing char number in warning", -1 != actual.getCharno()); } if (description != null) { assertEquals(description, actual.description); } } } // If we ran normalize on the AST, we must also run normalize on the // clone before checking for changes. if (normalizeEnabled) { normalizeActualCode(compiler, externsRootClone, mainRootClone); } boolean codeChange = !mainRootClone.isEquivalentTo(mainRoot); boolean externsChange = !externsRootClone.isEquivalentTo(externsRoot); // Generally, externs should not be change by the compiler passes. if (externsChange && !allowExternsChanges) { String explanation = externsRootClone.checkTreeEquals(externsRoot); fail("Unexpected changes to externs" + "\nExpected: " + compiler.toSource(externsRootClone) + "\nResult: " + compiler.toSource(externsRoot) + "\n" + explanation); } if (!codeChange && !externsChange) { assertFalse( "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue("compiler.reportCodeChange() should have been called", hasCodeChanged); } if (expected != null) { if (compareAsTree) { String explanation = expectedRoot.checkTreeEquals(mainRoot); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } else if (expected != null) { assertEquals( Joiner.on("").join(expected), compiler.toSource(mainRoot)); } } // Verify normalization is not invalidated. Node normalizeCheckRootClone = root.cloneTree(); Node normalizeCheckExternsRootClone = normalizeCheckRootClone.getFirstChild(); Node normalizeCheckMainRootClone = normalizeCheckRootClone.getLastChild(); new PrepareAst(compiler).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); String explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Node structure normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); // TODO(johnlenz): enable this for most test cases. // Currently, this invalidates test for while-loops, for-loop // initializers, and other naming. However, a set of code // (Closure primitive rewrites, etc) runs before the Normalize pass, // so this can't be force on everywhere. if (normalizeEnabled) { new Normalize(compiler, true).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } } else { String errors = ""; for (JSError actualError : compiler.getErrors()) { errors += actualError.description + "\n"; } assertEquals("There should be one error. " + errors, 1, compiler.getErrorCount()); assertEquals(errors, error, compiler.getErrors()[0].getType()); if (warning != null) { String warnings = ""; for (JSError actualError : compiler.getWarnings()) { warnings += actualError.description + "\n"; } assertEquals("There should be one warning. " + warnings, 1, compiler.getWarningCount()); assertEquals(warnings, warning, compiler.getWarnings()[0].getType()); } } } private void normalizeActualCode( Compiler compiler, Node externsRoot, Node mainRoot) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } /** * Parses expected JS inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(String[] expected) { Compiler compiler = createCompiler(); List inputs = Lists.newArrayList(); for (int i = 0; i < expected.length; i++) { inputs.add(SourceFile.fromCode("expected" + i, expected[i])); } compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } return mainRoot; } protected Node parseExpectedJs(String expected) { return parseExpectedJs(new String[] {expected}); } /** * Generates a list of modules from a list of inputs, such that each module * depends on the module before it. */ static JSModule[] createModuleChain(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i - 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that each module * depends on the first module. */ static JSModule[] createModuleStar(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[0]); } return modules; } /** * Generates a list of modules from a list of inputs, such that modules * form a bush formation. In a bush formation, module 2 depends * on module 1, and all other modules depend on module 2. */ static JSModule[] createModuleBush(String ... inputs) { Preconditions.checkState(inputs.length > 2); JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i == 1 ? 0 : 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that modules * form a tree formation. In a tree formation, module N depends on * module `floor(N/2)`, So the modules form a balanced binary tree. */ static JSModule[] createModuleTree(String ... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[(i - 1) / 2]); } return modules; } /** * Generates a list of modules from a list of inputs. Does not generate any * dependencies between the modules. */ static JSModule[] createModules(String... inputs) { JSModule[] modules = new JSModule[inputs.length]; for (int i = 0; i < inputs.length; i++) { JSModule module = modules[i] = new JSModule("m" + i); module.add(SourceFile.fromCode("i" + i, inputs[i])); } return modules; } private static class BlackHoleErrorManager extends BasicErrorManager { private BlackHoleErrorManager(Compiler compiler) { compiler.setErrorManager(this); } @Override public void println(CheckLevel level, JSError error) {} @Override public void printSummary() {} } Compiler createCompiler() { Compiler compiler = new Compiler(); return compiler; } protected void setExpectedSymbolTableError(DiagnosticType type) { this.expectedSymbolTableError = type; } /** Finds the first matching qualified name node in post-traversal order. */ protected final Node findQualifiedNameNode(final String name, Node root) { final List matches = Lists.newArrayList(); NodeUtil.visitPostOrder(root, new NodeUtil.Visitor() { @Override public void visit(Node n) { if (name.equals(n.getQualifiedName())) { matches.add(n); } } }, Predicates.alwaysTrue()); return matches.get(0); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TightenTypesTest.java0000644000175000017500000006641312115204405027170 0ustar apoapo/* * 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.javascript.jscomp.ConcreteType.ConcreteFunctionType; import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.TightenTypes.ConcreteSlot; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; /** * Unit test for the TightenTypes pass. * */ public class TightenTypesTest extends CompilerTestCase { private TightenTypes tt; public TightenTypesTest() { parseTypeInfo = true; enableTypeCheck(CheckLevel.WARNING); enableNormalize(true); } @Override public CompilerPass getProcessor(Compiler compiler) { return (tt = new TightenTypes(compiler)); } @Override protected int getNumRepetitions() { return 1; } @Override protected CompilerOptions getOptions() { return new CompilerOptions(); // no missing properties check } public void testTopLevelVariables() { testSame("/** @constructor */ function Foo() {}\n" + "var a = new Foo();\n" + "var b = a;\n"); assertTrue(getType("Foo").isFunction()); assertTrue(getType("a").isInstance()); assertType("function (this:Foo): ()", getType("Foo")); assertType("Foo", getType("a")); assertType("Foo", getType("b")); testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "var a = new Foo();\n" + "a = new Bar();\n" + "var b = a;\n"); assertTrue(getType("a").isUnion()); assertType("(Bar,Foo)", getType("a")); assertType("Bar", getType("b")); } public void testNamespacedVariables() { testSame("var goog = goog || {}; goog.foo = {};\n" + "/** @constructor */ goog.foo.Foo = function() {};\n" + "goog.foo.Foo.prototype.blah = function() {};\n" + "/** @constructor */ goog.foo.Bar = function() {};\n" + "goog.foo.Bar.prototype.blah = function() {};\n" + "function bar(a) { a.blah(); }\n" + "var baz = bar;\n" + "bar(new goog.foo.Foo);\n" + "baz(new goog.foo.Bar);\n"); assertType("(goog.foo.Bar,goog.foo.Foo)", getParamType(getType("bar"), 0)); assertType("(goog.foo.Bar,goog.foo.Foo)", getParamType(getType("baz"), 0)); } public void testReturnSlot() { testSame("/** @constructor */ function Foo() {}\n" + "function bar() {\n" + " var a = new Foo();\n" + " return a;\n" + "}\n" + "var b = bar();\n"); assertType("Foo", getType("b")); } public void testParameterSlots() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "function bar(a, b) {}\n" + "bar(new Foo, new Foo);\n" + "bar(new Bar, null);\n"); assertType("(Bar,Foo)", getParamType(getType("bar"), 0)); assertType("Foo", getParamType(getType("bar"), 1)); assertNull(getParamVar(getType("bar"), 2)); } public void testAliasedFunction() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "function bar(a) {}\n" + "var baz = bar;\n" + "bar(new Foo);\n" + "baz(new Bar);\n"); assertType("(Bar,Foo)", getParamType(getType("bar"), 0)); assertType("(Bar,Foo)", getParamType(getType("baz"), 0)); } public void testCatchStatement() { testSame(BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, "/** @constructor */ function Bar() {}\n" + "function bar() { try { } catch (e) { return e; } }\n" + "/** @constructor\n@extends{Error}*/ function ID10TError() {}\n" + "var a = bar(); throw new ID10TError();\n", null, null); assertType("(Error,EvalError,ID10TError,RangeError,ReferenceError," + "SyntaxError,TypeError,URIError)", getType("a")); } public void testConstructorParameterSlots() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "/** @constructor */ function Baz(a) {}\n" + "new Baz(new Foo);\n" + "new Baz(new Bar);\n"); assertType("(Bar,Foo)", getParamType(getType("Baz"), 0)); } public void testCallSlot() { testSame("function foo() {}\n" + "function bar() {}\n" + "function baz() {}\n" + "var a = foo;\n" + "a = bar;\n" + "a();\n"); assertTrue(isCalled(getType("foo"))); assertTrue(isCalled(getType("bar"))); assertFalse(isCalled(getType("baz"))); } public void testObjectLiteralTraversal() { testSame("var foo = function() {}\n" + "function bar() { return { 'a': foo()} };\n" + "bar();"); assertTrue(isCalled(getType("foo"))); } public void testThis() { testSame("/** @constructor */ function Foo() {}\n" + "Foo.prototype.foo = function() { return this; }\n" + "var a = new Foo();\n" + "var b = a.foo();\n"); assertType("Foo", getType("a")); assertType("Foo", getType("b")); } public void testAssign() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "var a = new Foo();\n" + "var b = a = new Bar();\n"); assertType("(Bar,Foo)", getType("a")); assertType("Bar", getType("b")); } public void testComma() { testSame("/** @constructor */ function Foo() {b=new Foo()}\n" + "var b;" + "/** @constructor */ function Bar() {}\n" + "var a = (new Foo, new Bar);\n"); assertType("Bar", getType("a")); assertType("Foo", getType("b")); } public void testAnd() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "var a = (new Foo && new Bar);\n"); assertType("Bar", getType("a")); } public void testOr() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "/** @type {Foo} */ var f = new Foo();\n" + "/** @type {Bar} */ var b = new Bar();\n" + "var a = (f || b);\n"); assertType("(Bar,Foo)", getType("a")); } public void testHook() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "var a = (1+1 == 2) ? new Foo : new Bar;\n"); assertType("(Bar,Foo)", getType("a")); } public void testFunctionLiteral() { testSame("/** @constructor */ function Foo() {}\n" + "var a = (function() { return new Foo; })();\n"); assertType("Foo", getType("a")); } public void testNameLookup() { testSame("/** @constructor */ function Foo() {}\n" + "var a = new Foo;\n" + "var b = (function() { return a; })();\n"); assertType("Foo", getType("a")); assertType("Foo", getType("b")); } public void testGetProp() { testSame("/** @constructor */ function Foo() {\n" + " this.foo = new A();\n" + "}\n" + "/** @constructor */ function Bar() {\n" + " this.foo = new B();\n" + "}\n" + "/** @constructor */ function Baz() {}\n" + "/** @constructor */ function A() {}\n" + "/** @constructor */ function B() {}\n" // add the casts to make the JSType a union with null + "/** @type {Foo} */ var foo = new Foo();\n" + "/** @type {Bar} */ var bar = new Bar();\n" + "/** @type {Baz} */ var baz = new Baz();\n" // has no 'foo' + "var a = foo || bar || baz\n" + "var b = a.foo;\n"); assertType("(A,B)", getType("b")); } public void testGetPrototypeProperty() { testSame("/** @constructor */ function Foo() {};\n" + "/** @constructor */ function Bar() {};\n" + "Bar.prototype.a = new Foo();\n" + "var a = Bar.prototype.a;\n"); assertType("Foo", getType("a")); } public void testGetElem() { testSame( "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {...*} var_args\n" + " * @return {!Array}\n" + " */\n" + "function Array(var_args) {}\n", "/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "var a = [];\n" + "a[0] = new Foo;\n" + "a[1] = new Bar;\n" + "var b = a[0];\n" + "var c = [new Foo, new Bar];\n", null); assertType("Array", getType("a")); assertType("(Array,Bar,Foo)", getType("b")); assertType("Array", getType("c")); testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "/** @constructor */ function Baz() {\n" + " this.arr = [];\n" + "}\n" + "var b = new Baz;\n" + "b.arr[0] = new Foo;\n" + "b.arr[1] = new Bar;\n" + "var c = b.arr;\n"); assertType("Array", getType("c")); } public void testGetElem3() { testSame(BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, "/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Bar() {}\n" + "/** @constructor */ function Baz() {\n" + " this.arr = [];\n" + "}\n" + "function foo(anarr) {" + "}\n" + "var ar = [];\n" + "foo(ar);\n", null); assertType("Array", getType("ar")); } public void testScopeDiscovery() { testSame("function spam() {}\n" + "function foo() {}\n" + "function bar() {\n" + " return function() { foo(); };\n" + "}" + "function baz() {\n" + " return function() { bar()(); };\n" + "}" + "baz()()();\n"); assertFalse(isCalled(getType("spam"))); assertTrue(isCalled(getType("foo"))); } public void testSheqDiscovery() { testSame("function spam() {}\n" + "/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.foo1 = function() { f1(); }\n" + "Foo.prototype.foo2 = function() { f2(); }\n" + "Foo.prototype.foo3 = function() { f3(); }\n" + "function baz(a) {\n" + " a === null || a instanceof Foo ?\n" + " Foo.prototype.foo1.call(this) :\n" + " Foo.prototype.foo2.call(this);\n" + "}\n" + "function f1() {}\n" + "function f2() {}\n" + "function f3() {}\n" + "baz(3);\n"); assertFalse(isCalled(getType("spam"))); assertFalse(isCalled(getType("f3"))); assertTrue(isCalled(getType("f1"))); assertTrue(isCalled(getType("f2"))); } public void testSubclass() { testSame("/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.foo = function() { return this.bar; };\n" + "Foo.prototype.bar = function() { return new A(); };\n" + "/**\n" + " * @constructor\n" + " * @extends Foo\n" + " */\n" + "function Bar() {}\n" + "/** @override */\n" + "Bar.prototype.bar = function() { return new B(); };\n" + "/** @constructor */ function A() {}\n" + "/** @constructor */ function B() {}\n" + "var a = (new Foo()).foo()();\n" + "a = (new Bar()).foo()();\n"); ConcreteType fooType = getPropertyType(getFunctionPrototype(getType("Foo")), "foo"); assertType("(Bar,Foo)", getThisType(fooType)); assertType("(A,B)", getType("a")); testSame("/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.foo = function() { return this.bar; };\n" + "Foo.prototype.bar = function() { return new A(); };\n" + "/**\n" + " * @constructor\n" + " * @extends Foo\n" + " */\n" + "function Bar() {}\n" + "/** @override */\n" + "Bar.prototype.bar = function() { return new B(); };\n" + "/** @constructor */ function A() {}\n" + "/** @constructor */ function B() {}\n" + "var a = (new Bar()).foo()();\n"); fooType = getPropertyType(getFunctionPrototype(getType("Foo")), "foo"); assertType("Bar", getThisType(fooType)); assertType("B", getType("a")); } public void testArrayAssignments() { testSame("/** @constructor */ function Foo() {}\n" + "var a = [];\n" + "function foo() { return []; }\n" + "(a.length == 0 ? a : foo())[0] = new Foo;\n" + "var b = a[0];\n" + "var c = foo()[0];\n"); assertType("(Array,Foo)", getType("b")); assertType("(Array,Foo)", getType("c")); } public void testAllPropertyReference() { testSame("/** @constructor */ function Foo() {}\n" + "Foo.prototype.prop = function() { this.prop2(); }\n" + "Foo.prototype.prop2 = function() { b = new Foo; }\n" + "var a = new Foo;\n" + "a = [][0];\n" + "function fun(a) {\n" + " return a.prop();\n" + "}\n" + "var b;\n" + "fun(a);\n" ); assertType("Foo", getType("a")); assertType("Foo", getType("b")); } public void testCallFunction() { testSame("/** @constructor */ function Foo() { this.a = new A; }\n" + "/** @constructor \n @extends Foo */ function Bar() {\n" + " Foo.call(this);\n" + "}\n" + "/** @constructor */ function A() {};\n" + "new Bar;"); assertTrue(isCalled(getType("Foo"))); assertTrue(isCalled(getType("A"))); ConcreteType fooType = getThisType(getType("Foo")); assertType("A", getPropertyType(fooType, "a")); ConcreteType barType = getThisType(getType("Bar")); assertType("A", getPropertyType(barType, "a")); } public void testCallFunctionWithArgs() { testSame("/** @constructor */ function Foo(o) { this.a = o; }\n" + "/** @constructor \n @extends Foo */ function Bar() {\n" + " Foo.call(this, new A());\n" + "}\n" + "/** @constructor */ function A() {};\n" + "var b = new Bar;"); assertTrue(isCalled(getType("Foo"))); assertTrue(isCalled(getType("A"))); ConcreteType barType = getThisType(getType("Bar")); assertType("A", getPropertyType(barType, "a")); ConcreteType fooType = getThisType(getType("Foo")); assertType("A", getPropertyType(fooType, "a")); } public void testCallPrototypeFunction() { testSame("/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = function() { return new A; }\n" + "Foo.prototype.a = function() { return new A; };\n" + "/** @constructor \n @extends Foo */ function Bar() {}\n" + "/** @override */" + "Bar.prototype.a = function() { return new B; };\n" + "/** @constructor */ function A() {};\n" + "/** @constructor */ function B() {};\n" + "var ret = Foo.prototype.a.call(new Bar);"); assertType("A", getType("ret")); } public void testCallPrototypeFunctionWithArgs() { testSame("/** @constructor */ function Foo() { this.p = null }\n" + "Foo.prototype.set = function(arg) { this.p = arg; };\n" + "Foo.prototype.get = function() { return this.p; };\n" + "/** @constructor */ function A() {};\n" + "Foo.prototype.set.call(new Foo, new A);\n" + "var ret = Foo.prototype.get.call(new Foo);"); ConcreteType fooP = getFunctionPrototype(getType("Foo")); ConcreteFunctionType gFun = getPropertyType(fooP, "get").toFunction(); ConcreteFunctionType sFun = getPropertyType(fooP, "set").toFunction(); assertTrue(isCalled(sFun)); assertTrue(isCalled(gFun)); assertTrue(isCalled(getType("A"))); assertType("A", getType("ret")); } public void testSetTimeout() { testSame("/** @constructor */ function Window() {};\n" + "Window.prototype.setTimeout = function(f, t) {};\n" + "/** @type Window */ var window;", "/** @constructor*/ function A() {}\n" + "A.prototype.handle = function() { foo(); };\n" + "function foo() {}\n" + "window.setTimeout((new A).handle, 3);", null); assertTrue(isCalled(getType("foo"))); } public void testExternType() { testSame("/** @constructor */ function T() {};\n" + "/** @constructor */ function Ext() {};\n" + "/** @return {T} */\n" + "Ext.prototype.getT = function() {};\n" + "/** @type T */ Ext.prototype.prop;\n" + "/** @type Ext */ var ext;", "var b = ext.getT();\n" + "var p = ext.prop;", null); assertType("Ext", getType("ext")); assertType("T", getType("b")); assertType("T", getType("p")); } public void testExternSubTypes() { testSame("/** @constructor */ function A() {};\n" + "/** @constructor \n@extends A */ function B() {};\n" + "/** @constructor \n@extends A */ function C() {};\n" + "/** @constructor \n@extends B */ function D() {};\n" + "/** @constructor */ function Ext() {};\n" + "/** @type A */ Ext.prototype.a;\n" + "/** @type B */ Ext.prototype.b;\n" + "/** @type D */ Ext.prototype.d;\n" + "/** @return {A} */ Ext.prototype.getA = function() {};\n" + "/** @return {B} */ Ext.prototype.getB = function() {};\n", "var a = (new Ext).a;\n" + "var a2 = (new Ext).getA();\n" + "var b = (new Ext).b;\n" + "var b2 = (new Ext).getB();\n" + "var d = (new Ext).d;\n", null); assertType("(A,B,C,D)", getType("a")); assertType("(A,B,C,D)", getType("a2")); assertType("(B,D)", getType("b")); assertType("(B,D)", getType("b2")); assertType("D", getType("d")); } public void testExternSubTypesForObject() { testSame(BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES + "/** @constructor */ function A() {};\n" + "/** @constructor \n@extends A */ function B() {};\n" + "/** @return {Object} */ " + "Object.prototype.eval = function(code) {};\n" + "/** @type {Object} */\n" + "A.prototype.a;\n" + "/** @return {Object} */\n" + "A.prototype.b = function(){};\n", "var a = (new A).b()", null, null); assertType("(A,ActiveXObject,Array,B,Boolean,Date,Error,EvalError," + "Function,Number,Object," + "RangeError,ReferenceError,RegExp,String,SyntaxError," + "TypeError,URIError)", getType("a")); } public void testImplicitPropCall() { testSame("/** @constructor */ function Window() {};\n" + "/** @param {function()} f \n@param {number} d */\n" + "Window.prototype.setTimeout = function(f, d) {};", "function foo() {};\n" + "(new Window).setTimeout(foo, 20);", null); assertTrue(isCalled(getType("foo"))); } public void testImplicitPropCallWithArgs() { testSame("/** @constructor */ function Window() {};\n" + "/** @constructor */ function EventListener() {};\n" + "/** @param {string} t\n" + " * @param {EventListener|function(Event)} f */\n" + "Window.prototype.addEventListener = function(t, f) {};\n" + "/** @constructor */ function Event() {};", "function foo(evt) {};\n" + "(new Window).addEventListener('click', foo);", null); assertTrue(isCalled(getType("foo"))); assertType("Event", getParamType(getType("foo"), 0)); } public void testUntypedImplicitCallFromProperty() { testSame("/** @constructor */ function Element() {};\n" + "/** @type {?function(Event)} */Element.prototype.onclick;\n" + "/** @constructor */ function Event() {};" + "/** @return {Event} */ Event.prototype.erv;", " function foo(evt) { return bar(evt); };\n" + "function bar(a) { return a.type() }\n" + "/** @type Object */ var ar = new Element;\n" + "ar.onclick = foo;", null); assertTrue(isCalled(getType("foo"))); assertTrue(isCalled(getType("bar"))); assertType("Event", getParamType(getType("foo"), 0)); assertType("Event", getParamType(getType("bar"), 0)); assertType("Element", getThisType(getType("foo").toFunction())); } public void testImplicitCallFromProperty() { testSame("/** @constructor */ function Element() {};\n" + "/** @type {function(this:Element,Event)} */\n" + "Element.prototype.onclick;\n" + "/** @constructor */ function Event() {};", "function foo(evt) {};\n" + "(new Element).onclick = foo;", null); assertTrue(isCalled(getType("foo"))); assertType("Event", getParamType(getType("foo"), 0)); assertType("Element", getThisType(getType("foo").toFunction())); } public void testImplicitCallFromPropertyOfUnion() { testSame("/** @constructor */ function Element() {};\n" + "/** @type {function(this:Element,Event)} */\n" + "Element.prototype.onclick;\n" + "/** @constructor */ function Event() {};", "function foo(evt) {};\n" + "(new Element).onclick = foo;", null); assertTrue(isCalled(getType("foo"))); assertType("Event", getParamType(getType("foo"), 0)); assertType("Element", getThisType(getType("foo").toFunction())); } public void testImplicitCallFromPropertyOfAllType() { testSame("/** @constructor */ function Element() {};\n" + "/** @type {function(this:Element,Event)} */\n" + "Element.prototype.onclick;\n" + "/** @constructor */ function Event() {};", "function foo(evt) {};\n" + "var elems = [];\n" + "var elem = elems[0];\n" // assign it the all type + "elem.onclick = foo;", null); assertTrue(isCalled(getType("foo"))); assertType("Event", getParamType(getType("foo"), 0)); assertType("Element", getThisType(getType("foo").toFunction())); } public void testRestrictToCast() { testSame("/** @constructor */ function Foo() {};\n" + "var a = [];\n" + "var foo = (/** @type {Foo} */ a[0]);\n" + "var u = a[0];\n" + "new Foo"); assertType("Foo", getType("foo")); assertType("(Array,Foo)", getType("u")); } public void testRestrictToInterfaceCast() { testSame("/** @constructor \n @implements Int */ function Foo() {};\n" + "/** @interface */ function Int() {};\n" + "var a = [];\n" + "var foo = (/** @type {Int} */ a[0]);\n" + "new Foo"); assertType("Foo", getType("foo")); } public void testRestrictToCastWithNonInstantiatedTypes() { testSame( "/** @constructor */ function Super() {}\n" + "/** @constructor \n @extends {Super} */ function Foo() {};\n" + "Foo.prototype.blah = function() { foofunc() };\n" + "/** @constructor \n @extends {Super} */ function Bar() {};\n" + "Bar.prototype.blah = function() { barfunc() };\n" + "function barfunc() {}\n" + "function foofunc() {}\n" + "var a = [];\n" + "var u = /** @type {Super} */ (a[0]);\n" + "u.blah()\n" + "new Foo"); assertTrue(isCalled(getType("foofunc"))); assertFalse(isCalled(getType("barfunc"))); assertType("Array", getType("a")); } public void testFunctionToString() { testSame("/** @constructor */ function Foo() {}\n" + "/** @constructor \n * @extends Foo */\n" + "function Bar() { Foo.call(this); }\n" + "var a = function(a) { return new Foo; };\n;" + "a(new Foo);\n" + "a(new Bar);\n" + "new Bar;"); assertType("function ((Bar,Foo)): Foo", getType("a")); assertType("function (this:(Bar,Foo)): ()", getType("Foo")); assertType("function (this:Bar): ()", getType("Bar")); } private void assertType(String expected, ConcreteType type) { assertEquals(expected, type.toString()); } /** Returns the type of the given variable in the top-most scope. */ private ConcreteType getType(String var) { assertNotNull(tt.getTopScope().getSlot(var)); return tt.getTopScope().getSlot(var).getType(); } /** Returns the variable for the given parameter of the given function. */ private ConcreteSlot getParamVar(ConcreteType funType, int param) { assertTrue(funType.isFunction()); return (ConcreteSlot) ((ConcreteFunctionType) funType).getParameterSlot(param); } /** Returns the type of the given parameter of the given function. */ private ConcreteType getParamType(ConcreteType funType, int param) { ConcreteSlot paramVar = getParamVar(funType, param); return (paramVar != null) ? paramVar.getType() : ConcreteType.NONE; } /** Returns the variable for the this variable of the given function. */ private ConcreteSlot getThisSlot(ConcreteType funType) { assertTrue(funType.isFunction()); return (ConcreteSlot) ((ConcreteFunctionType) funType).getThisSlot(); } /** Returns the type of the this variable of the given function. */ private ConcreteType getThisType(ConcreteType funType) { return getThisSlot(funType).getType(); } /** Returns the prototype type of the given function. */ private ConcreteType getFunctionPrototype(ConcreteType funType) { assertTrue(funType.isFunction()); return ((ConcreteFunctionType) funType).getPrototypeType(); } /** * Returns the variable for the property with the give name on the given * instance type. */ private ConcreteSlot getPropertyVar(ConcreteType instType, String name) { assertTrue(instType.isInstance()); return (ConcreteSlot) ((ConcreteInstanceType) instType).getPropertySlot(name); } /** Returns the type of the property with the give name on the given type. */ private ConcreteType getPropertyType(ConcreteType instType, String name) { return getPropertyVar(instType, name).getType(); } /** Returns whether the given function is called. */ private boolean isCalled(ConcreteType funType) { assertTrue(funType.isFunction()); ConcreteSlot callVar = (ConcreteSlot) ((ConcreteFunctionType) funType).getCallSlot(); return !callVar.getType().isNone(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineFunctionsTest.java0000644000175000017500000021465312115204405027651 0ustar apoapo/* * 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; /** * Inline function tests. * @author johnlenz@google.com (john lenz) */ public class InlineFunctionsTest extends CompilerTestCase { boolean allowGlobalFunctionInlining = true; boolean allowBlockInlining = true; final boolean allowExpressionDecomposition = true; final boolean allowFunctionExpressionInlining = true; final boolean allowLocalFunctionInlining = true; boolean assumeStrictThis = false; boolean assumeMinimumCapture = false; public InlineFunctionsTest() { this.enableNormalize(); this.enableMarkNoSideEffects(); } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); allowGlobalFunctionInlining = true; allowBlockInlining = true; assumeStrictThis = false; assumeMinimumCapture = false; } @Override protected CompilerPass getProcessor(Compiler compiler) { compiler.resetUniqueNameId(); return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), allowGlobalFunctionInlining, allowLocalFunctionInlining, allowBlockInlining, assumeStrictThis, assumeMinimumCapture); } /** * Returns the number of times the pass should be run before results are * verified. */ @Override protected int getNumRepetitions() { // Some inlining can only be done in multiple passes. return 3; } public void testInlineEmptyFunction1() { // Empty function, no params. test("function foo(){}" + "foo();", "void 0;"); } public void testInlineEmptyFunction2() { // Empty function, params with no side-effects. test("function foo(){}" + "foo(1, new Date, function(){});", "void 0;"); } public void testInlineEmptyFunction3() { // Empty function, multiple references. test("function foo(){}" + "foo();foo();foo();", "void 0;void 0;void 0"); } public void testInlineEmptyFunction4() { // Empty function, params with side-effects forces block inlining. test("function foo(){}" + "foo(x());", "{var JSCompiler_inline_anon_param_0=x();}"); } public void testInlineEmptyFunction5() { // Empty function, call params with side-effects in expression can not // be inlined. allowBlockInlining = false; testSame("function foo(){}" + "foo(x());"); } public void testInlineFunctions1() { // As simple a test as we can get. test("function foo(){ return 4 }" + "foo();", "4"); } public void testInlineFunctions2() { // inline simple constants // NOTE: CD is not inlined. test("var t;var AB=function(){return 4};" + "function BC(){return 6;}" + "CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();", "var t;CD=function(x){return x+5};x=CD(3);y=4;z=6" ); } public void testInlineFunctions3() { // inline simple constants test("var t;var AB=function(){return 4};" + "function BC(){return 6;}" + "var CD=function(x){return x + 5};x=CD(3);y=AB();z=BC();", "var t;x=3+5;y=4;z=6"); } public void testInlineFunctions4() { // don't inline if there are multiple definitions (need DFA for that). test("var t; var AB = function() { return 4 }; " + "function BC() { return 6; }" + "CD = 0;" + "CD = function(x) { return x + 5 }; x = CD(3); y = AB(); z = BC();", "var t;CD=0;CD=function(x){return x+5};x=CD(3);y=4;z=6"); } public void testInlineFunctions5() { // inline additions test("var FOO_FN=function(x,y) { return \"de\" + x + \"nu\" + y };" + "var a = FOO_FN(\"ez\", \"ts\")", "var a=\"de\"+\"ez\"+\"nu\"+\"ts\""); } public void testInlineFunctions6() { // more complex inlines test("function BAR_FN(x, y, z) { return z(foo(x + y)) }" + "alert(BAR_FN(1, 2, baz))", "alert(baz(foo(1+2)))"); } public void testInlineFunctions7() { // inlines appearing multiple times test("function FN(x,y,z){return x+x+y}" + "var b=FN(1,2,3)", "var b=1+1+2"); } public void testInlineFunctions8() { // check correct parenthesization test("function MUL(x,y){return x*y}function ADD(x,y){return x+y}" + "var a=1+MUL(2,3);var b=2*ADD(3,4)", "var a=1+2*3;var b=2*(3+4)"); } public void testInlineFunctions9() { // don't inline if the input parameter is modified. test("function INC(x){return x++}" + "var y=INC(i)", "var y;{var x$$inline_0=i;" + "y=x$$inline_0++}"); } public void testInlineFunctions10() { test("function INC(x){return x++}" + "var y=INC(i);y=INC(i)", "var y;" + "{var x$$inline_0=i;" + "y=x$$inline_0++}" + "{var x$$inline_2=i;" + "y=x$$inline_2++}"); } public void testInlineFunctions11() { test("function f(x){return x}" + "var y=f(i)", "var y=i"); } public void testInlineFunctions12() { // don't inline if the input parameter has side-effects. allowBlockInlining = false; test("function f(x){return x}" + "var y=f(i)", "var y=i"); testSame("function f(x){return x}" + "var y=f(i++)"); } public void testInlineFunctions13() { // inline as block if the input parameter has side-effects. test("function f(x){return x}" + "var y=f(i++)", "var y;{var x$$inline_0=i++;y=x$$inline_0}"); } public void testInlineFunctions14() { // don't remove functions that are referenced on other ways test("function FOO(x){return x}var BAR=function(y){return y}" + ";b=FOO;a(BAR);x=FOO(1);y=BAR(2)", "function FOO(x){return x}var BAR=function(y){return y}" + ";b=FOO;a(BAR);x=1;y=2"); } public void testInlineFunctions15a() { // closure factories: do inline into global scope. test("function foo(){return function(a){return a+1}}" + "var b=function(){return c};" + "var d=b()+foo()", "var d=c+function(a){return a+1}"); } public void testInlineFunctions15b() { assumeMinimumCapture = false; // closure factories: don't inline closure with locals into global scope. test("function foo(){var x;return function(a){return a+1}}" + "var b=function(){return c};" + "var d=b()+foo()", "function foo(){var x;return function(a){return a+1}}" + "var d=c+foo()"); assumeMinimumCapture = true; test("function foo(){var x;return function(a){return a+1}}" + "var b=function(){return c};" + "var d=b()+foo()", "var JSCompiler_temp_const$$0 = c;\n" + "var JSCompiler_inline_result$$1;\n" + "{\n" + "var x$$inline_2;\n" + "JSCompiler_inline_result$$1 = " + " function(a$$inline_3){ return a$$inline_3+1 };\n" + "}" + "var d=JSCompiler_temp_const$$0 + JSCompiler_inline_result$$1"); } public void testInlineFunctions15c() { assumeMinimumCapture = false; // closure factories: don't inline into non-global scope. test("function foo(){return function(a){return a+1}}" + "var b=function(){return c};" + "function _x(){ var d=b()+foo() }", "function foo(){return function(a){return a+1}}" + "function _x(){ var d=c+foo() }"); assumeMinimumCapture = true; // closure factories: don't inline into non-global scope. test("function foo(){return function(a){return a+1}}" + "var b=function(){return c};" + "function _x(){ var d=b()+foo() }", "function _x(){var d=c+function(a){return a+1}}"); } public void testInlineFunctions15d() { assumeMinimumCapture = false; // closure factories: don't inline functions with vars. test("function foo(){var x; return function(a){return a+1}}" + "var b=function(){return c};" + "function _x(){ var d=b()+foo() }", "function foo(){var x; return function(a){return a+1}}" + "function _x(){ var d=c+foo() }"); assumeMinimumCapture = true; // closure factories: don't inline functions with vars. test("function foo(){var x; return function(a){return a+1}}" + "var b=function(){return c};" + "function _x(){ var d=b()+foo() }", "function _x() { \n" + " var JSCompiler_temp_const$$0 = c;\n" + " var JSCompiler_inline_result$$1;\n" + " {\n" + " var x$$inline_2;\n" + " JSCompiler_inline_result$$1 = " + " function(a$$inline_3) {return a$$inline_3+1};\n" + " }\n" + " var d = JSCompiler_temp_const$$0+JSCompiler_inline_result$$1\n" + "}"); } public void testInlineFunctions16a() { assumeMinimumCapture = false; testSame("function foo(b){return window.bar(function(){c(b)})}" + "var d=foo(e)"); assumeMinimumCapture = true; test( "function foo(b){return window.bar(function(){c(b)})}" + "var d=foo(e)", "var d;{var b$$inline_0=e;" + "d=window.bar(function(){c(b$$inline_0)})}"); } public void testInlineFunctions16b() { test("function foo(){return window.bar(function(){c()})}" + "var d=foo(e)", "var d=window.bar(function(){c()})"); } public void testInlineFunctions17() { // don't inline recursive functions testSame("function foo(x){return x*x+foo(3)}var bar=foo(4)"); } public void testInlineFunctions18() { // TRICKY ... test nested inlines allowBlockInlining = false; test("function foo(a, b){return a+b}" + "function bar(d){return c}" + "var d=foo(bar(1),e)", "var d=c+e"); } public void testInlineFunctions19() { // TRICKY ... test nested inlines // with block inlining possible test("function foo(a, b){return a+b}" + "function bar(d){return c}" + "var d=foo(bar(1),e)", "var d;{d=c+e}"); } public void testInlineFunctions20() { // Make sure both orderings work allowBlockInlining = false; test("function foo(a, b){return a+b}" + "function bar(d){return c}" + "var d=bar(foo(1,e));", "var d=c"); } public void testInlineFunctions21() { // with block inlining possible test("function foo(a, b){return a+b}" + "function bar(d){return c}" + "var d=bar(foo(1,e))", "var d;{d=c}"); } public void testInlineFunctions22() { // Another tricky case ... test nested compiler inlines test("function plex(a){if(a) return 0;else return 1;}" + "function foo(a, b){return bar(a+b)}" + "function bar(d){return plex(d)}" + "var d=foo(1,2)", "var d;{JSCompiler_inline_label_plex_1:{" + "if(1+2){" + "d=0;break JSCompiler_inline_label_plex_1}" + "else{" + "d=1;break JSCompiler_inline_label_plex_1}d=void 0}}"); } public void testInlineFunctions23() { // Test both orderings again test("function complex(a){if(a) return 0;else return 1;}" + "function bar(d){return complex(d)}" + "function foo(a, b){return bar(a+b)}" + "var d=foo(1,2)", "var d;{JSCompiler_inline_label_complex_1:{" + "if(1+2){" + "d=0;break JSCompiler_inline_label_complex_1" + "}else{" + "d=1;break JSCompiler_inline_label_complex_1" + "}d=void 0}}"); } public void testInlineFunctions24() { // Don't inline functions with 'arguments' or 'this' testSame("function foo(x){return this}foo(1)"); } public void testInlineFunctions25() { testSame("function foo(){return arguments[0]}foo()"); } public void testInlineFunctions26() { // Don't inline external functions testSame("function _foo(x){return x}_foo(1)"); } public void testInlineFunctions27() { test("var window = {}; function foo(){window.bar++; return 3;}" + "var x = {y: 1, z: foo(2)};", "var window={};" + "var JSCompiler_inline_result$$0;" + "{" + " window.bar++;" + " JSCompiler_inline_result$$0 = 3;" + "}" + "var x = {y: 1, z: JSCompiler_inline_result$$0};"); } public void testInlineFunctions28() { test("var window = {}; function foo(){window.bar++; return 3;}" + "var x = {y: alert(), z: foo(2)};", "var window = {};" + "var JSCompiler_temp_const$$0 = alert();" + "var JSCompiler_inline_result$$1;" + "{" + " window.bar++;" + " JSCompiler_inline_result$$1 = 3;}" + "var x = {" + " y: JSCompiler_temp_const$$0," + " z: JSCompiler_inline_result$$1" + "};"); } public void testInlineFunctions29() { test("var window = {}; function foo(){window.bar++; return 3;}" + "var x = {a: alert(), b: alert2(), c: foo(2)};", "var window = {};" + "var JSCompiler_temp_const$$1 = alert();" + "var JSCompiler_temp_const$$0 = alert2();" + "var JSCompiler_inline_result$$2;" + "{" + " window.bar++;" + " JSCompiler_inline_result$$2 = 3;}" + "var x = {" + " a: JSCompiler_temp_const$$1," + " b: JSCompiler_temp_const$$0," + " c: JSCompiler_inline_result$$2" + "};"); } public void testInlineFunctions30() { // As simple a test as we can get. testSame("function foo(){ return eval() }" + "foo();"); } public void testInlineFunctions31() { // Don't introduce a duplicate label in the same scope test("function foo(){ lab:{4;} }" + "lab:{foo();}", "lab:{{JSCompiler_inline_label_0:{4}}}"); } public void testMixedModeInlining1() { // Base line tests, direct inlining test("function foo(){return 1}" + "foo();", "1;"); } public void testMixedModeInlining2() { // Base line tests, block inlining. Block inlining is needed by // possible-side-effect parameter. test("function foo(){return 1}" + "foo(x());", "{var JSCompiler_inline_anon_param_0=x();1}"); } public void testMixedModeInlining3() { // Inline using both modes. test("function foo(){return 1}" + "foo();foo(x());", "1;{var JSCompiler_inline_anon_param_0=x();1}"); } public void testMixedModeInlining4() { // Inline using both modes. Alternating. Second call of each type has // side-effect-less parameter, this is thrown away. test("function foo(){return 1}" + "foo();foo(x());" + "foo(1);foo(1,x());", "1;{var JSCompiler_inline_anon_param_0=x();1}" + "1;{var JSCompiler_inline_anon_param_4=x();1}"); } public void testMixedModeInliningCosting1() { // Inline using both modes. Costing estimates. // Base line. test( "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + "foo(1,2);" + "foo(2,3)", "1+2+1+2+4+5+6+7+8+9+1+2+3+4+5;" + "2+3+2+3+4+5+6+7+8+9+1+2+3+4+5"); } public void testMixedModeInliningCosting2() { // Don't inline here because the function definition can not be eliminated. // TODO(johnlenz): Should we add constant removing to the unit test? testSame( "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + "foo(1,2);" + "foo(2,3,x())"); } public void testMixedModeInliningCosting3() { // Do inline here because the function definition can be eliminated. test( "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+10}" + "foo(1,2);" + "foo(2,3,x())", "1+2+1+2+4+5+6+7+8+9+1+2+3+10;" + "{var JSCompiler_inline_anon_param_2=x();" + "2+3+2+3+4+5+6+7+8+9+1+2+3+10}"); } public void testMixedModeInliningCosting4() { // Threshold test. testSame( "function foo(a,b){return a+b+a+b+4+5+6+7+8+9+1+2+3+4+101}" + "foo(1,2);" + "foo(2,3,x())"); } public void testNoInlineIfParametersModified1() { // Assignment test("function f(x){return x=1}f(undefined)", "{var x$$inline_0=undefined;" + "x$$inline_0=1}"); } public void testNoInlineIfParametersModified2() { test("function f(x){return (x)=1;}f(2)", "{var x$$inline_0=2;" + "x$$inline_0=1}"); } public void testNoInlineIfParametersModified3() { // Assignment variant. test("function f(x){return x*=2}f(2)", "{var x$$inline_0=2;" + "x$$inline_0*=2}"); } public void testNoInlineIfParametersModified4() { // Assignment in if. test("function f(x){return x?(x=2):0}f(2)", "{var x$$inline_0=2;" + "x$$inline_0?(" + "x$$inline_0=2):0}"); } public void testNoInlineIfParametersModified5() { // Assignment in if, multiple params test("function f(x,y){return x?(y=2):0}f(2,undefined)", "{var y$$inline_1=undefined;2?(" + "y$$inline_1=2):0}"); } public void testNoInlineIfParametersModified6() { test("function f(x,y){return x?(y=2):0}f(2)", "{var y$$inline_1=void 0;2?(" + "y$$inline_1=2):0}"); } public void testNoInlineIfParametersModified7() { // Increment test("function f(a){return++a<++a}f(1)", "{var a$$inline_0=1;" + "++a$$inline_0<" + "++a$$inline_0}"); } public void testNoInlineIfParametersModified8() { // OK, object parameter modified. test("function f(a){return a.x=2}f(o)", "o.x=2"); } public void testNoInlineIfParametersModified9() { // OK, array parameter modified. test("function f(a){return a[2]=2}f(o)", "o[2]=2"); } public void testInlineNeverPartialSubtitution1() { test("function f(z){return x.y.z;}f(1)", "x.y.z"); } public void testInlineNeverPartialSubtitution2() { test("function f(z){return x.y[z];}f(a)", "x.y[a]"); } public void testInlineNeverMutateConstants() { test("function f(x){return x=1}f(undefined)", "{var x$$inline_0=undefined;" + "x$$inline_0=1}"); } public void testInlineNeverOverrideNewValues() { test("function f(a){return++a<++a}f(1)", "{var a$$inline_0=1;" + "++a$$inline_0<++a$$inline_0}"); } public void testInlineMutableArgsReferencedOnce() { test("function foo(x){return x;}foo([])", "[]"); } public void testNoInlineMutableArgs1() { allowBlockInlining = false; testSame("function foo(x){return x+x} foo([])"); } public void testNoInlineMutableArgs2() { allowBlockInlining = false; testSame("function foo(x){return x+x} foo(new Date)"); } public void testNoInlineMutableArgs3() { allowBlockInlining = false; testSame("function foo(x){return x+x} foo(true&&new Date)"); } public void testNoInlineMutableArgs4() { allowBlockInlining = false; testSame("function foo(x){return x+x} foo({})"); } public void testInlineBlockMutableArgs1() { test("function foo(x){x+x}foo([])", "{var x$$inline_0=[];" + "x$$inline_0+x$$inline_0}"); } public void testInlineBlockMutableArgs2() { test("function foo(x){x+x}foo(new Date)", "{var x$$inline_0=new Date;" + "x$$inline_0+x$$inline_0}"); } public void testInlineBlockMutableArgs3() { test("function foo(x){x+x}foo(true&&new Date)", "{var x$$inline_0=true&&new Date;" + "x$$inline_0+x$$inline_0}"); } public void testInlineBlockMutableArgs4() { test("function foo(x){x+x}foo({})", "{var x$$inline_0={};" + "x$$inline_0+x$$inline_0}"); } public void testShadowVariables1() { // The Normalize pass now guarantees that that globals are never shadowed // by locals. // "foo" is inlined here as its parameter "a" doesn't conflict. // "bar" is assigned a new name. test("var a=0;" + "function foo(a){return 3+a}" + "function bar(){var a=foo(4)}" + "bar();", "var a=0;" + "{var a$$inline_0=3+4}"); } public void testShadowVariables2() { // "foo" is inlined here as its parameter "a" doesn't conflict. // "bar" is inlined as its uses global "a", and does introduce any new // globals. test("var a=0;" + "function foo(a){return 3+a}" + "function bar(){a=foo(4)}" + "bar()", "var a=0;" + "{a=3+4}"); } public void testShadowVariables3() { // "foo" is inlined into exported "_bar", aliasing foo's "a". test("var a=0;" + "function foo(){var a=2;return 3+a}" + "function _bar(){a=foo()}", "var a=0;" + "function _bar(){{var a$$inline_0=2;" + "a=3+a$$inline_0}}"); } public void testShadowVariables4() { // "foo" is inlined. // block access to global "a". test("var a=0;" + "function foo(){return 3+a}" + "function _bar(a){a=foo(4)+a}", "var a=0;function _bar(a$$1){" + "a$$1=" + "3+a+a$$1}"); } public void testShadowVariables5() { // Can't yet inline multiple statements functions into expressions // (though some are possible using the COMMA operator). allowBlockInlining = false; testSame("var a=0;" + "function foo(){var a=4;return 3+a}" + "function _bar(a){a=foo(4)+a}"); } public void testShadowVariables6() { test("var a=0;" + "function foo(){var a=4;return 3+a}" + "function _bar(a){a=foo(4)}", "var a=0;function _bar(a$$2){{" + "var a$$inline_0=4;" + "a$$2=3+a$$inline_0}}"); } public void testShadowVariables7() { assumeMinimumCapture = false; test("var a=3;" + "function foo(){return a}" + "(function(){var a=5;(function(){foo()})()})()", "var a=3;" + "{var a$$inline_0=5;{a}}" ); assumeMinimumCapture = true; test("var a=3;" + "function foo(){return a}" + "(function(){var a=5;(function(){foo()})()})()", "var a=3;" + "{var a$$inline_1=5;{a}}" ); } public void testShadowVariables8() { // this should be inlined test("var a=0;" + "function foo(){return 3}" + "function _bar(){var a=foo()}", "var a=0;" + "function _bar(){var a=3}"); } public void testShadowVariables9() { // this should be inlined too [even if the global is not declared] test("function foo(){return 3}" + "function _bar(){var a=foo()}", "function _bar(){var a=3}"); } public void testShadowVariables10() { // callee var must be renamed. test("var a;function foo(){return a}" + "function _bar(){var a=foo()}", "var a;function _bar(){var a$$1=a}"); } public void testShadowVariables11() { // The call has a local variable // which collides with the function being inlined test("var a=0;var b=1;" + "function foo(){return a+a}" + "function _bar(){var a=foo();alert(a)}", "var a=0;var b=1;" + "function _bar(){var a$$1=a+a;" + "alert(a$$1)}" ); } public void testShadowVariables12() { // 2 globals colliding test("var a=0;var b=1;" + "function foo(){return a+b}" + "function _bar(){var a=foo(),b;alert(a)}", "var a=0;var b=1;" + "function _bar(){var a$$1=a+b," + "b$$1;" + "alert(a$$1)}"); } public void testShadowVariables13() { // The only change is to remove the collision test("var a=0;var b=1;" + "function foo(){return a+a}" + "function _bar(){var c=foo();alert(c)}", "var a=0;var b=1;" + "function _bar(){var c=a+a;alert(c)}"); } public void testShadowVariables14() { // There is a collision even though it is not read. test("var a=0;var b=1;" + "function foo(){return a+b}" + "function _bar(){var c=foo(),b;alert(c)}", "var a=0;var b=1;" + "function _bar(){var c=a+b," + "b$$1;alert(c)}"); } public void testShadowVariables15() { // Both parent and child reference a global test("var a=0;var b=1;" + "function foo(){return a+a}" + "function _bar(){var c=foo();alert(c+a)}", "var a=0;var b=1;" + "function _bar(){var c=a+a;alert(c+a)}"); } public void testShadowVariables16() { assumeMinimumCapture = false; // Inline functions defined as a child of the CALL node. test("var a=3;" + "function foo(){return a}" + "(function(){var a=5;(function(){foo()})()})()", "var a=3;" + "{var a$$inline_0=5;{a}}" ); assumeMinimumCapture = true; // Inline functions defined as a child of the CALL node. test("var a=3;" + "function foo(){return a}" + "(function(){var a=5;(function(){foo()})()})()", "var a=3;" + "{var a$$inline_1=5;{a}}" ); } public void testShadowVariables17() { test("var a=0;" + "function bar(){return a+a}" + "function foo(){return bar()}" + "function _goo(){var a=2;var x=foo();}", "var a=0;" + "function _goo(){var a$$1=2;var x=a+a}"); } public void testShadowVariables18() { test("var a=0;" + "function bar(){return a+a}" + "function foo(){var a=3;return bar()}" + "function _goo(){var a=2;var x=foo();}", "var a=0;" + "function _goo(){var a$$2=2;var x;" + "{var a$$inline_0=3;x=a+a}}"); } public void testCostBasedInlining1() { testSame( "function foo(a){return a}" + "foo=new Function(\"return 1\");" + "foo(1)"); } public void testCostBasedInlining2() { // Baseline complexity tests. // Single call, function not removed. test( "function foo(a){return a}" + "var b=foo;" + "function _t1(){return foo(1)}", "function foo(a){return a}" + "var b=foo;" + "function _t1(){return 1}"); } public void testCostBasedInlining3() { // Two calls, function not removed. test( "function foo(a,b){return a+b}" + "var b=foo;" + "function _t1(){return foo(1,2)}" + "function _t2(){return foo(2,3)}", "function foo(a,b){return a+b}" + "var b=foo;" + "function _t1(){return 1+2}" + "function _t2(){return 2+3}"); } public void testCostBasedInlining4() { // Two calls, function not removed. // Here there isn't enough savings to justify inlining. testSame( "function foo(a,b){return a+b+a+b}" + "var b=foo;" + "function _t1(){return foo(1,2)}" + "function _t2(){return foo(2,3)}"); } public void testCostBasedInlining5() { // Here there is enough savings to justify inlining. test( "function foo(a,b){return a+b+a+b}" + "function _t1(){return foo(1,2)}" + "function _t2(){return foo(2,3)}", "function _t1(){return 1+2+1+2}" + "function _t2(){return 2+3+2+3}"); } public void testCostBasedInlining6() { // Here we have a threshold test. // Do inline here: test( "function foo(a,b){return a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2+3+4+5}" + "function _t1(){return foo(1,2)}" + "function _t2(){return foo(2,3)}", "function _t1(){return 1+2+1+2+1+2+1+2+4+5+6+7+8+9+1+2+3+4+5}" + "function _t2(){return 2+3+2+3+2+3+2+3+4+5+6+7+8+9+1+2+3+4+5}"); } public void testCostBasedInlining7() { // Don't inline here (not enough savings): testSame( "function foo(a,b){" + " return a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2+3+4+5+6}" + "function _t1(){return foo(1,2)}" + "function _t2(){return foo(2,3)}"); } public void testCostBasedInlining8() { // Verify multiple references in the same statement: // Here "f" is not known to be removable, as it is a used as parameter // and is not known to be side-effect free. The first call to f() can // not be inlined on the first pass (as the call to f() as a parameter // prevents this). However, the call to f() would be inlinable, if it // is small enough to be inlined without removing the function declaration. // but it is not in this first test. allowBlockInlining = false; testSame("function f(a){return 1 + a + a;}" + "var a = f(f(1));"); } public void testCostBasedInlining9() { // Here both direct and block inlining is used. The call to f as a // parameter is inlined directly, which the call to f with f as a parameter // is inlined using block inlining. test("function f(a){return 1 + a + a;}" + "var a = f(f(1));", "var a;" + "{var a$$inline_0=1+1+1;" + "a=1+a$$inline_0+a$$inline_0}"); } public void testCostBasedInlining10() { // But it is small enough here, and on the second iteration, the remaining // call to f() is inlined, as there is no longer a possible side-effect-ing // parameter. allowBlockInlining = false; test("function f(a){return a + a;}" + "var a = f(f(1));", "var a= 1+1+(1+1);"); } public void testCostBasedInlining11() { // With block inlining test("function f(a){return a + a;}" + "var a = f(f(1))", "var a;" + "{var a$$inline_0=1+1;" + "a=a$$inline_0+a$$inline_0}"); } public void testCostBasedInlining12() { test("function f(a){return 1 + a + a;}" + "var a = f(1) + f(2);", "var a=1+1+1+(1+2+2)"); } public void testCostBasedInliningComplex1() { testSame( "function foo(a){a()}" + "foo=new Function(\"return 1\");" + "foo(1)"); } public void testCostBasedInliningComplex2() { // Baseline complexity tests. // Single call, function not removed. test( "function foo(a){a()}" + "var b=foo;" + "function _t1(){foo(x)}", "function foo(a){a()}" + "var b=foo;" + "function _t1(){{x()}}"); } public void testCostBasedInliningComplex3() { // Two calls, function not removed. test( "function foo(a,b){a+b}" + "var b=foo;" + "function _t1(){foo(1,2)}" + "function _t2(){foo(2,3)}", "function foo(a,b){a+b}" + "var b=foo;" + "function _t1(){{1+2}}" + "function _t2(){{2+3}}"); } public void testCostBasedInliningComplex4() { // Two calls, function not removed. // Here there isn't enough savings to justify inlining. testSame( "function foo(a,b){a+b+a+b}" + "var b=foo;" + "function _t1(){foo(1,2)}" + "function _t2(){foo(2,3)}"); } public void testCostBasedInliningComplex5() { // Here there is enough savings to justify inlining. test( "function foo(a,b){a+b+a+b}" + "function _t1(){foo(1,2)}" + "function _t2(){foo(2,3)}", "function _t1(){{1+2+1+2}}" + "function _t2(){{2+3+2+3}}"); } public void testCostBasedInliningComplex6() { // Here we have a threshold test. // Do inline here: test( "function foo(a,b){a+b+a+b+a+b+a+b+4+5+6+7+8+9+1}" + "function _t1(){foo(1,2)}" + "function _t2(){foo(2,3)}", "function _t1(){{1+2+1+2+1+2+1+2+4+5+6+7+8+9+1}}" + "function _t2(){{2+3+2+3+2+3+2+3+4+5+6+7+8+9+1}}"); } public void testCostBasedInliningComplex7() { // Don't inline here (not enough savings): testSame( "function foo(a,b){a+b+a+b+a+b+a+b+4+5+6+7+8+9+1+2}" + "function _t1(){foo(1,2)}" + "function _t2(){foo(2,3)}"); } public void testCostBasedInliningComplex8() { // Verify multiple references in the same statement. testSame("function _f(a){1+a+a}" + "a=_f(1)+_f(1)"); } public void testCostBasedInliningComplex9() { test("function f(a){1 + a + a;}" + "f(1);f(2);", "{1+1+1}{1+2+2}"); } public void testDoubleInlining1() { allowBlockInlining = false; test("var foo = function(a) { return getWindow(a); };" + "var bar = function(b) { return b; };" + "foo(bar(x));", "getWindow(x)"); } public void testDoubleInlining2() { test("var foo = function(a) { return getWindow(a); };" + "var bar = function(b) { return b; };" + "foo(bar(x));", "{getWindow(x)}"); } public void testNoInlineOfNonGlobalFunction1() { test("var g;function _f(){function g(){return 0}}" + "function _h(){return g()}", "var g;function _f(){}" + "function _h(){return g()}"); } public void testNoInlineOfNonGlobalFunction2() { test("var g;function _f(){var g=function(){return 0}}" + "function _h(){return g()}", "var g;function _f(){}" + "function _h(){return g()}"); } public void testNoInlineOfNonGlobalFunction3() { test("var g;function _f(){var g=function(){return 0}}" + "function _h(){return g()}", "var g;function _f(){}" + "function _h(){return g()}"); } public void testNoInlineOfNonGlobalFunction4() { test("var g;function _f(){function g(){return 0}}" + "function _h(){return g()}", "var g;function _f(){}" + "function _h(){return g()}"); } public void testNoInlineMaskedFunction() { // Normalization makes this test of marginal value. // The unreferenced function is removed. test("var g=function(){return 0};" + "function _f(g){return g()}", "function _f(g$$1){return g$$1()}"); } public void testNoInlineNonFunction() { testSame("var g=3;function _f(){return g()}"); } public void testInlineCall() { test("function f(g) { return g.h(); } f('x');", "\"x\".h()"); } public void testInlineFunctionWithArgsMismatch1() { test("function f(g) { return g; } f();", "void 0"); } public void testInlineFunctionWithArgsMismatch2() { test("function f() { return 0; } f(1);", "0"); } public void testInlineFunctionWithArgsMismatch3() { test("function f(one, two, three) { return one + two + three; } f(1);", "1+void 0+void 0"); } public void testInlineFunctionWithArgsMismatch4() { test("function f(one, two, three) { return one + two + three; }" + "f(1,2,3,4,5);", "1+2+3"); } public void testArgumentsWithSideEffectsNeverInlined1() { allowBlockInlining = false; testSame("function f(){return 0} f(new goo());"); } public void testArgumentsWithSideEffectsNeverInlined2() { allowBlockInlining = false; testSame("function f(g,h){return h+g}f(g(),h());"); } public void testOneSideEffectCallDoesNotRuinOthers() { allowBlockInlining = false; test("function f(){return 0}f(new goo());f()", "function f(){return 0}f(new goo());0"); } public void testComplexInlineNoResultNoParamCall1() { test("function f(){a()}f()", "{a()}"); } public void testComplexInlineNoResultNoParamCall2() { test("function f(){if (true){return;}else;} f();", "{JSCompiler_inline_label_f_0:{" + "if(true)break JSCompiler_inline_label_f_0;else;}}"); } public void testComplexInlineNoResultNoParamCall3() { // We now allow vars in the global space. // Don't inline into vars into global scope. // testSame("function f(){a();b();var z=1+1}f()"); // But do inline into functions test("function f(){a();b();var z=1+1}function _foo(){f()}", "function _foo(){{a();b();var z$$inline_0=1+1}}"); } public void testComplexInlineNoResultWithParamCall1() { test("function f(x){a(x)}f(1)", "{a(1)}"); } public void testComplexInlineNoResultWithParamCall2() { test("function f(x,y){a(x)}var b=1;f(1,b)", "var b=1;{a(1)}"); } public void testComplexInlineNoResultWithParamCall3() { test("function f(x,y){if (x) y(); return true;}var b=1;f(1,b)", "var b=1;{if(1)b();true}"); } public void testComplexInline1() { test("function f(){if (true){return;}else;} z=f();", "{JSCompiler_inline_label_f_0:" + "{if(true){z=void 0;" + "break JSCompiler_inline_label_f_0}else;z=void 0}}"); } public void testComplexInline2() { test("function f(){if (true){return;}else return;} z=f();", "{JSCompiler_inline_label_f_0:{if(true){z=void 0;" + "break JSCompiler_inline_label_f_0}else{z=void 0;" + "break JSCompiler_inline_label_f_0}z=void 0}}"); } public void testComplexInline3() { test("function f(){if (true){return 1;}else return 0;} z=f();", "{JSCompiler_inline_label_f_0:{if(true){z=1;" + "break JSCompiler_inline_label_f_0}else{z=0;" + "break JSCompiler_inline_label_f_0}z=void 0}}"); } public void testComplexInline4() { test("function f(x){a(x)} z = f(1)", "{a(1);z=void 0}"); } public void testComplexInline5() { test("function f(x,y){a(x)}var b=1;z=f(1,b)", "var b=1;{a(1);z=void 0}"); } public void testComplexInline6() { test("function f(x,y){if (x) y(); return true;}var b=1;z=f(1,b)", "var b=1;{if(1)b();z=true}"); } public void testComplexInline7() { test("function f(x,y){if (x) return y(); else return true;}" + "var b=1;z=f(1,b)", "var b=1;{JSCompiler_inline_label_f_2:{if(1){z=b();" + "break JSCompiler_inline_label_f_2}else{z=true;" + "break JSCompiler_inline_label_f_2}z=void 0}}"); } public void testComplexInline8() { test("function f(x){a(x)}var z=f(1)", "var z;{a(1);z=void 0}"); } public void testComplexInlineVars1() { test("function f(){if (true){return;}else;}var z=f();", "var z;{JSCompiler_inline_label_f_0:{" + "if(true){z=void 0;break JSCompiler_inline_label_f_0}else;z=void 0}}"); } public void testComplexInlineVars2() { test("function f(){if (true){return;}else return;}var z=f();", "var z;{JSCompiler_inline_label_f_0:{" + "if(true){z=void 0;break JSCompiler_inline_label_f_0" + "}else{" + "z=void 0;break JSCompiler_inline_label_f_0}z=void 0}}"); } public void testComplexInlineVars3() { test("function f(){if (true){return 1;}else return 0;}var z=f();", "var z;{JSCompiler_inline_label_f_0:{if(true){" + "z=1;break JSCompiler_inline_label_f_0" + "}else{" + "z=0;break JSCompiler_inline_label_f_0}z=void 0}}"); } public void testComplexInlineVars4() { test("function f(x){a(x)}var z = f(1)", "var z;{a(1);z=void 0}"); } public void testComplexInlineVars5() { test("function f(x,y){a(x)}var b=1;var z=f(1,b)", "var b=1;var z;{a(1);z=void 0}"); } public void testComplexInlineVars6() { test("function f(x,y){if (x) y(); return true;}var b=1;var z=f(1,b)", "var b=1;var z;{if(1)b();z=true}"); } public void testComplexInlineVars7() { test("function f(x,y){if (x) return y(); else return true;}" + "var b=1;var z=f(1,b)", "var b=1;var z;" + "{JSCompiler_inline_label_f_2:{if(1){z=b();" + "break JSCompiler_inline_label_f_2" + "}else{" + "z=true;break JSCompiler_inline_label_f_2}z=void 0}}"); } public void testComplexInlineVars8() { test("function f(x){a(x)}var x;var z=f(1)", "var x;var z;{a(1);z=void 0}"); } public void testComplexInlineVars9() { test("function f(x){a(x)}var x;var z=f(1);var y", "var x;var z;{a(1);z=void 0}var y"); } public void testComplexInlineVars10() { test("function f(x){a(x)}var x=blah();var z=f(1);var y=blah();", "var x=blah();var z;{a(1);z=void 0}var y=blah()"); } public void testComplexInlineVars11() { test("function f(x){a(x)}var x=blah();var z=f(1);var y;", "var x=blah();var z;{a(1);z=void 0}var y"); } public void testComplexInlineVars12() { test("function f(x){a(x)}var x;var z=f(1);var y=blah();", "var x;var z;{a(1);z=void 0}var y=blah()"); } public void testComplexInlineInExpresssions1() { test("function f(){a()}var z=f()", "var z;{a();z=void 0}"); } public void testComplexInlineInExpresssions2() { test("function f(){a()}c=z=f()", "var JSCompiler_inline_result$$0;" + "{a();JSCompiler_inline_result$$0=void 0;}" + "c=z=JSCompiler_inline_result$$0"); } public void testComplexInlineInExpresssions3() { test("function f(){a()}c=z=f()", "var JSCompiler_inline_result$$0;" + "{a();JSCompiler_inline_result$$0=void 0;}" + "c=z=JSCompiler_inline_result$$0"); } public void testComplexInlineInExpresssions4() { test("function f(){a()}if(z=f());", "var JSCompiler_inline_result$$0;" + "{a();JSCompiler_inline_result$$0=void 0;}" + "if(z=JSCompiler_inline_result$$0);"); } public void testComplexInlineInExpresssions5() { test("function f(){a()}if(z.y=f());", "var JSCompiler_temp_const$$0=z;" + "var JSCompiler_inline_result$$1;" + "{a();JSCompiler_inline_result$$1=void 0;}" + "if(JSCompiler_temp_const$$0.y=JSCompiler_inline_result$$1);"); } public void testComplexNoInline1() { testSame("function f(){a()}while(z=f())continue"); } public void testComplexNoInline2() { testSame("function f(){a()}do;while(z=f())"); } public void testComplexSample() { String result = "" + "{{" + "var styleSheet$$inline_2=null;" + "if(goog$userAgent$IE)" + "styleSheet$$inline_2=0;" + "else " + "var head$$inline_3=0;" + "{" + "var element$$inline_4=" + "styleSheet$$inline_2;" + "var stylesString$$inline_5=a;" + "if(goog$userAgent$IE)" + "element$$inline_4.cssText=" + "stylesString$$inline_5;" + "else " + "{" + "var propToSet$$inline_6=" + "\"innerText\";" + "element$$inline_4[" + "propToSet$$inline_6]=" + "stylesString$$inline_5" + "}" + "}" + "styleSheet$$inline_2" + "}}"; test("var foo = function(stylesString, opt_element) { " + "var styleSheet = null;" + "if (goog$userAgent$IE)" + "styleSheet = 0;" + "else " + "var head = 0;" + "" + "goo$zoo(styleSheet, stylesString);" + "return styleSheet;" + " };\n " + "var goo$zoo = function(element, stylesString) {" + "if (goog$userAgent$IE)" + "element.cssText = stylesString;" + "else {" + "var propToSet = 'innerText';" + "element[propToSet] = stylesString;" + "}" + "};" + "(function(){foo(a,b);})();", result); } public void testComplexSampleNoInline() { testSame( "foo=function(stylesString,opt_element){" + "var styleSheet=null;" + "if(goog$userAgent$IE)" + "styleSheet=0;" + "else " + "var head=0;" + "" + "goo$zoo(styleSheet,stylesString);" + "return styleSheet" + "};" + "goo$zoo=function(element,stylesString){" + "if(goog$userAgent$IE)" + "element.cssText=stylesString;" + "else{" + "var propToSet=goog$userAgent$WEBKIT?\"innerText\":\"innerHTML\";" + "element[propToSet]=stylesString" + "}" + "}"); } // Test redefinition of parameter name. public void testComplexNoVarSub() { test( "function foo(x){" + "var x;" + "y=x" + "}" + "foo(1)", "{y=1}" ); } public void testComplexFunctionWithFunctionDefinition1() { test("function f(){call(function(){return})}f()", "{call(function(){return})}"); } public void testComplexFunctionWithFunctionDefinition2() { assumeMinimumCapture = false; // Don't inline if local names might be captured. testSame("function f(a){call(function(){return})}f()"); assumeMinimumCapture = true; test("(function(){" + "var f = function(a){call(function(){return a})};f()})()", "{{var a$$inline_0=void 0;call(function(){return a$$inline_0})}}"); } public void testComplexFunctionWithFunctionDefinition2a() { assumeMinimumCapture = false; // Don't inline if local names might be captured. testSame("(function(){" + "var f = function(a){call(function(){return a})};f()})()"); assumeMinimumCapture = true; test("(function(){" + "var f = function(a){call(function(){return a})};f()})()", "{{var a$$inline_0=void 0;call(function(){return a$$inline_0})}}"); } public void testComplexFunctionWithFunctionDefinition3() { assumeMinimumCapture = false; // Don't inline if local names might need to be captured. testSame("function f(){var a; call(function(){return a})}f()"); assumeMinimumCapture = true; test("function f(){var a; call(function(){return a})}f()", "{var a$$inline_0;call(function(){return a$$inline_0})}"); } public void testDecomposePlusEquals() { test("function f(){a=1;return 1} var x = 1; x += f()", "var x = 1;" + "var JSCompiler_temp_const$$0 = x;" + "var JSCompiler_inline_result$$1;" + "{a=1;" + " JSCompiler_inline_result$$1=1}" + "x = JSCompiler_temp_const$$0 + JSCompiler_inline_result$$1;"); } public void testDecomposeFunctionExpressionInCall() { test( "(function(map){descriptions_=map})(\n" + "function(){\n" + "var ret={};\n" + "ret[ONE]='a';\n" + "ret[TWO]='b';\n" + "return ret\n" + "}()\n" + ");", "var JSCompiler_inline_result$$0;" + "{" + "var ret$$inline_1={};\n" + "ret$$inline_1[ONE]='a';\n" + "ret$$inline_1[TWO]='b';\n" + "JSCompiler_inline_result$$0 = ret$$inline_1;\n" + "}" + "{" + "descriptions_=JSCompiler_inline_result$$0;" + "}" ); } public void testInlineConstructor1() { test("function f() {} function _g() {f.call(this)}", "function _g() {void 0}"); } public void testInlineConstructor2() { test("function f() {} f.prototype.a = 0; function _g() {f.call(this)}", "function f() {} f.prototype.a = 0; function _g() {void 0}"); } public void testInlineConstructor3() { test("function f() {x.call(this)} f.prototype.a = 0;" + "function _g() {f.call(this)}", "function f() {x.call(this)} f.prototype.a = 0;" + "function _g() {{x.call(this)}}"); } public void testInlineConstructor4() { test("function f() {x.call(this)} f.prototype.a = 0;" + "function _g() {var t = f.call(this)}", "function f() {x.call(this)} f.prototype.a = 0;" + "function _g() {var t; {x.call(this); t = void 0}}"); } public void testFunctionExpressionInlining1() { test("(function(){})()", "void 0"); } public void testFunctionExpressionInlining2() { test("(function(){foo()})()", "{foo()}"); } public void testFunctionExpressionInlining3() { test("var a = (function(){return foo()})()", "var a = foo()"); } public void testFunctionExpressionInlining4() { test("var a; a = 1 + (function(){return foo()})()", "var a; a = 1 + foo()"); } public void testFunctionExpressionCallInlining1() { test("(function(){}).call(this)", "void 0"); } public void testFunctionExpressionCallInlining2() { test("(function(){foo(this)}).call(this)", "{foo(this)}"); } public void testFunctionExpressionCallInlining3() { test("var a = (function(){return foo(this)}).call(this)", "var a = foo(this)"); } public void testFunctionExpressionCallInlining4() { test("var a; a = 1 + (function(){return foo(this)}).call(this)", "var a; a = 1 + foo(this)"); } public void testFunctionExpressionCallInlining5() { test("a:(function(){return foo()})()", "a:foo()"); } public void testFunctionExpressionCallInlining6() { test("a:(function(){return foo()}).call(this)", "a:foo()"); } public void testFunctionExpressionCallInlining7() { test("a:(function(){})()", "a:void 0"); } public void testFunctionExpressionCallInlining8() { test("a:(function(){}).call(this)", "a:void 0"); } public void testFunctionExpressionCallInlining9() { // ... with unused recursive name. test("(function foo(){})()", "void 0"); } public void testFunctionExpressionCallInlining10() { // ... with unused recursive name. test("(function foo(){}).call(this)", "void 0"); } public void testFunctionExpressionCallInlining11a() { // Inline functions that return inner functions. test("((function(){return function(){foo()}})())();", "{foo()}"); } public void testFunctionExpressionCallInlining11b() { assumeMinimumCapture = false; // Can't inline functions that return inner functions and have local names. testSame("((function(){var a; return function(){foo()}})())();"); assumeMinimumCapture = true; test( "((function(){var a; return function(){foo()}})())();", "var JSCompiler_inline_result$$0;" + "{var a$$inline_1;" + "JSCompiler_inline_result$$0=function(){foo()};}" + "JSCompiler_inline_result$$0()"); } public void testFunctionExpressionCallInlining11c() { // TODO(johnlenz): Can inline, not temps needed. assumeMinimumCapture = false; testSame("function _x() {" + " ((function(){return function(){foo()}})())();" + "}"); assumeMinimumCapture = true; test( "function _x() {" + " ((function(){return function(){foo()}})())();" + "}", "function _x() {" + " {foo()}" + "}"); } public void testFunctionExpressionCallInlining11d() { // TODO(johnlenz): Can inline into a function containing eval, if // no names are introduced. assumeMinimumCapture = false; testSame("function _x() {" + " eval();" + " ((function(){return function(){foo()}})())();" + "}"); assumeMinimumCapture = true; test( "function _x() {" + " eval();" + " ((function(){return function(){foo()}})())();" + "}", "function _x() {" + " eval();" + " {foo()}" + "}"); } public void testFunctionExpressionCallInlining11e() { // No, don't inline into a function containing eval, // if temps are introduced. assumeMinimumCapture = false; testSame("function _x() {" + " eval();" + " ((function(a){return function(){foo()}})())();" + "}"); assumeMinimumCapture = true; test("function _x() {" + " eval();" + " ((function(a){return function(){foo()}})())();" + "}", "function _x() {" + " eval();" + " {foo();}" + "}"); } public void testFunctionExpressionCallInlining12() { // Can't inline functions that recurse. testSame("(function foo(){foo()})()"); } public void testFunctionExpressionOmega() { // ... with unused recursive name. test("(function (f){f(f)})(function(f){f(f)})", "{var f$$inline_0=function(f$$1){f$$1(f$$1)};" + "{{f$$inline_0(f$$inline_0)}}}"); } public void testLocalFunctionInlining1() { test("function _f(){ function g() {} g() }", "function _f(){ void 0 }"); } public void testLocalFunctionInlining2() { test("function _f(){ function g() {foo(); bar();} g() }", "function _f(){ {foo(); bar();} }"); } public void testLocalFunctionInlining3() { test("function _f(){ function g() {foo(); bar();} g() }", "function _f(){ {foo(); bar();} }"); } public void testLocalFunctionInlining4() { test("function _f(){ function g() {return 1} return g() }", "function _f(){ return 1 }"); } public void testLocalFunctionInlining5() { testSame("function _f(){ function g() {this;} g() }"); } public void testLocalFunctionInlining6() { testSame("function _f(){ function g() {this;} return g; }"); } public void testLocalFunctionInliningOnly1() { this.allowGlobalFunctionInlining = true; test("function f(){} f()", "void 0;"); this.allowGlobalFunctionInlining = false; testSame("function f(){} f()"); } public void testLocalFunctionInliningOnly2() { this.allowGlobalFunctionInlining = false; testSame("function f(){} f()"); test("function f(){ function g() {return 1} return g() }; f();", "function f(){ return 1 }; f();"); } public void testLocalFunctionInliningOnly3() { this.allowGlobalFunctionInlining = false; testSame("function f(){} f()"); test("(function(){ function g() {return 1} return g() })();", "(function(){ return 1 })();"); } public void testLocalFunctionInliningOnly4() { this.allowGlobalFunctionInlining = false; testSame("function f(){} f()"); test("(function(){ return (function() {return 1})() })();", "(function(){ return 1 })();"); } public void testInlineWithThis1() { assumeStrictThis = false; // If no "this" is provided it might need to be coerced to the global // "this". testSame("function f(){} f.call();"); testSame("function f(){this} f.call();"); assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(){} f.call();", "{}"); test("function f(){this} f.call();", "{void 0;}"); } public void testInlineWithThis2() { // "this" can always be replaced with "this" assumeStrictThis = false; test("function f(){} f.call(this);", "void 0"); assumeStrictThis = true; test("function f(){} f.call(this);", "void 0"); } public void testInlineWithThis3() { assumeStrictThis = false; // If no "this" is provided it might need to be coerced to the global // "this". testSame("function f(){} f.call([]);"); assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(){} f.call([]);", "{}"); } public void testInlineWithThis4() { assumeStrictThis = false; // If no "this" is provided it might need to be coerced to the global // "this". testSame("function f(){} f.call(new g);"); assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(){} f.call(new g);", "{var JSCompiler_inline_this_0=new g}"); } public void testInlineWithThis5() { assumeStrictThis = false; // If no "this" is provided it might need to be coerced to the global // "this". testSame("function f(){} f.call(g());"); assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(){} f.call(g());", "{var JSCompiler_inline_this_0=g()}"); } public void testInlineWithThis6() { assumeStrictThis = false; // If no "this" is provided it might need to be coerced to the global // "this". testSame("function f(){this} f.call(new g);"); assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(){this} f.call(new g);", "{var JSCompiler_inline_this_0=new g;JSCompiler_inline_this_0}"); } public void testInlineWithThis7() { assumeStrictThis = true; // In strict mode, "this" is never coerced so we can use the provided value. test("function f(a){a=1;this} f.call();", "{var a$$inline_0=void 0; a$$inline_0=1; void 0;}"); test("function f(a){a=1;this} f.call(x, x);", "{var a$$inline_0=x; a$$inline_0=1; x;}"); } // http://en.wikipedia.org/wiki/Fixed_point_combinator#Y_combinator public void testFunctionExpressionYCombinator() { assumeMinimumCapture = false; testSame( "var factorial = ((function(M) {\n" + " return ((function(f) {\n" + " return M(function(arg) {\n" + " return (f(f))(arg);\n" + " })\n" + " })\n" + " (function(f) {\n" + " return M(function(arg) {\n" + " return (f(f))(arg);\n" + " })\n" + " }));\n" + " })\n" + " (function(f) {\n" + " return function(n) {\n" + " if (n === 0)\n" + " return 1;\n" + " else\n" + " return n * f(n - 1);\n" + " };\n" + " }));\n" + "\n" + "factorial(5)\n"); assumeMinimumCapture = true; test( "var factorial = ((function(M) {\n" + " return ((function(f) {\n" + " return M(function(arg) {\n" + " return (f(f))(arg);\n" + " })\n" + " })\n" + " (function(f) {\n" + " return M(function(arg) {\n" + " return (f(f))(arg);\n" + " })\n" + " }));\n" + " })\n" + " (function(f) {\n" + " return function(n) {\n" + " if (n === 0)\n" + " return 1;\n" + " else\n" + " return n * f(n - 1);\n" + " };\n" + " }));\n" + "\n" + "factorial(5)\n", "var factorial;\n" + "{\n" + "var M$$inline_4 = function(f$$2) {\n" + " return function(n){if(n===0)return 1;else return n*f$$2(n-1)}\n" + "};\n" + "{\n" + "var f$$inline_0=function(f$$inline_7){\n" + " return M$$inline_4(\n" + " function(arg$$inline_8){\n" + " return f$$inline_7(f$$inline_7)(arg$$inline_8)\n" + " })\n" + "};\n" + "factorial=M$$inline_4(\n" + " function(arg$$inline_1){\n" + " return f$$inline_0(f$$inline_0)(arg$$inline_1)\n" + "});\n" + "}\n" + "}" + "factorial(5)"); } public void testRenamePropertyFunction() { testSame("function JSCompiler_renameProperty(x) {return x} " + "JSCompiler_renameProperty('foo')"); } public void testReplacePropertyFunction() { // baseline: an alias doesn't prevents declaration removal, but not // inlining. test("function f(x) {return x} " + "foo(window, f); f(1)", "function f(x) {return x} " + "foo(window, f); 1"); // a reference passed to JSCompiler_ObjectPropertyString prevents inlining // as well. testSame("function f(x) {return x} " + "new JSCompiler_ObjectPropertyString(window, f); f(1)"); } public void testInlineWithClosureContainingThis() { test("(function (){return f(function(){return this})})();", "f(function(){return this})"); } public void testIssue5159924a() { test("function f() { if (x()) return y() }\n" + "while(1){ var m = f() || z() }", "for(;1;) {" + " var JSCompiler_inline_result$$0;" + " {" + " JSCompiler_inline_label_f_1: {" + " if(x()) {" + " JSCompiler_inline_result$$0 = y();" + " break JSCompiler_inline_label_f_1" + " }" + " JSCompiler_inline_result$$0 = void 0;" + " }" + " }" + " var m=JSCompiler_inline_result$$0 || z()" + "}"); } public void testIssue5159924b() { test("function f() { if (x()) return y() }\n" + "while(1){ var m = f() }", "for(;1;){" + " var m;" + " {" + " JSCompiler_inline_label_f_0: { " + " if(x()) {" + " m = y();" + " break JSCompiler_inline_label_f_0" + " }" + " m = void 0" + " }" + " }" + "}"); } public void testInlineObject() { new StringCompare().testInlineObject(); } private static class StringCompare extends CompilerTestCase { private boolean allowGlobalFunctionInlining = true; StringCompare() { super("", false); this.enableNormalize(); this.enableMarkNoSideEffects(); } @Override public void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); allowGlobalFunctionInlining = true; } @Override protected CompilerPass getProcessor(Compiler compiler) { compiler.resetUniqueNameId(); return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), allowGlobalFunctionInlining, true, // allowLocalFunctionInlining true, // allowBlockInlining true, // assumeStrictThis true); // assumeMinimumCapture } public void testInlineObject() { allowGlobalFunctionInlining = false; // TODO(johnlenz): normalize the AST so an AST comparison can be done. // As is, the expected AST does not match the actual correct result: // The AST matches "g.a()" with a FREE_CALL annotation, but this as // expected string would fail as it won't be mark as a free call. // "(0,g.a)()" matches the output, but not the resulting AST. test("function inner(){function f(){return g.a}(f())()}", "function inner(){(0,g.a)()}"); } } public void testBug4944818() { test( "var getDomServices_ = function(self) {\n" + " if (!self.domServices_) {\n" + " self.domServices_ = goog$component$DomServices.get(" + " self.appContext_);\n" + " }\n" + "\n" + " return self.domServices_;\n" + "};\n" + "\n" + "var getOwnerWin_ = function(self) {\n" + " return getDomServices_(self).getDomHelper().getWindow();\n" + "};\n" + "\n" + "HangoutStarter.prototype.launchHangout = function() {\n" + " var self = a.b;\n" + " var myUrl = new goog.Uri(getOwnerWin_(self).location.href);\n" + "};", "HangoutStarter.prototype.launchHangout = function() { " + " var self$$2 = a.b;" + " var JSCompiler_temp_const$$0 = goog.Uri;" + " var JSCompiler_inline_result$$1;" + " {" + " var self$$inline_2 = self$$2;" + " if (!self$$inline_2.domServices_) {" + " self$$inline_2.domServices_ = goog$component$DomServices.get(" + " self$$inline_2.appContext_);" + " }" + " JSCompiler_inline_result$$1=self$$inline_2.domServices_;" + " }" + " var myUrl = new JSCompiler_temp_const$$0(" + " JSCompiler_inline_result$$1.getDomHelper()." + " getWindow().location.href)" + "}"); } public void testIssue423() { assumeMinimumCapture = false; test( "(function($) {\n" + " $.fn.multicheck = function(options) {\n" + " initialize.call(this, options);\n" + " };\n" + "\n" + " function initialize(options) {\n" + " options.checkboxes = $(this).siblings(':checkbox');\n" + " preload_check_all.call(this);\n" + " }\n" + "\n" + " function preload_check_all() {\n" + " $(this).data('checkboxes');\n" + " }\n" + "})(jQuery)", "(function($){" + " $.fn.multicheck=function(options$$1){" + " {" + " options$$1.checkboxes=$(this).siblings(\":checkbox\");" + " {" + " $(this).data(\"checkboxes\")" + " }" + " }" + " }" + "})(jQuery)"); assumeMinimumCapture = true; test( "(function($) {\n" + " $.fn.multicheck = function(options) {\n" + " initialize.call(this, options);\n" + " };\n" + "\n" + " function initialize(options) {\n" + " options.checkboxes = $(this).siblings(':checkbox');\n" + " preload_check_all.call(this);\n" + " }\n" + "\n" + " function preload_check_all() {\n" + " $(this).data('checkboxes');\n" + " }\n" + "})(jQuery)", "{var $$$inline_0=jQuery;\n" + "$$$inline_0.fn.multicheck=function(options$$inline_4){\n" + " {options$$inline_4.checkboxes=" + "$$$inline_0(this).siblings(\":checkbox\");\n" + " {$$$inline_0(this).data(\"checkboxes\")}" + " }\n" + "}\n" + "}"); } public void testIssue728() { String f = "var f = function() { return false; };"; StringBuilder calls = new StringBuilder(); StringBuilder folded = new StringBuilder(); for (int i = 0; i < 30; i++) { calls.append("if (!f()) alert('x');"); folded.append("if (!false) alert('x');"); } test(f + calls, folded.toString()); } public void testAnonymous1() { assumeMinimumCapture = false; test("(function(){var a=10;(function(){var b=a;a++;alert(b)})()})();", "{var a$$inline_0=10;" + "{var b$$inline_1=a$$inline_0;" + "a$$inline_0++;alert(b$$inline_1)}}"); assumeMinimumCapture = true; test("(function(){var a=10;(function(){var b=a;a++;alert(b)})()})();", "{var a$$inline_2=10;" + "{var b$$inline_0=a$$inline_2;" + "a$$inline_2++;alert(b$$inline_0)}}"); } public void testAnonymous2() { testSame("(function(){eval();(function(){var b=a;a++;alert(b)})()})();"); } public void testAnonymous3() { // Introducing a new value into is tricky assumeMinimumCapture = false; testSame("(function(){var a=10;(function(){arguments;})()})();"); assumeMinimumCapture = true; test("(function(){var a=10;(function(){arguments;})()})();", "{var a$$inline_0=10;(function(){arguments;})();}"); test("(function(){(function(){arguments;})()})();", "{(function(){arguments;})()}"); } public void testLoopWithFunctionWithFunction() { assumeMinimumCapture = true; test("function _testLocalVariableInLoop_() {\n" + " var result = 0;\n" + " function foo() {\n" + " var arr = [1, 2, 3, 4, 5];\n" + " for (var i = 0, l = arr.length; i < l; i++) {\n" + " var j = arr[i];\n" + // don't inline this function, because the correct behavior depends // captured values. " (function() {\n" + " var k = j;\n" + " setTimeout(function() { result += k; }, 5 * i);\n" + " })();\n" + " }\n" + " }\n" + " foo();\n" + "}", "function _testLocalVariableInLoop_(){\n" + " var result=0;\n" + " {" + " var arr$$inline_0=[1,2,3,4,5];\n" + " var i$$inline_1=0;\n" + " var l$$inline_2=arr$$inline_0.length;\n" + " for(;i$$inline_1of(), ImmutableList.of(foo, bar, foo2), options); this.provider = compiler; } public void testExcerptOneLine() throws Exception { assertEquals("foo:first line", provider.getSourceLine("foo", 1)); assertEquals("foo:second line", provider.getSourceLine("foo", 2)); assertEquals("foo:third line", provider.getSourceLine("foo", 3)); assertEquals("bar:first line", provider.getSourceLine("bar", 1)); assertEquals("bar:second line", provider.getSourceLine("bar", 2)); assertEquals("bar:third line", provider.getSourceLine("bar", 3)); assertEquals("bar:fourth line", provider.getSourceLine("bar", 4)); } public void testExcerptLineFromInexistantSource() throws Exception { assertEquals(null, provider.getSourceLine("inexistant", 1)); assertEquals(null, provider.getSourceLine("inexistant", 7)); assertEquals(null, provider.getSourceLine("inexistant", 90)); } public void testExcerptInexistantLine() throws Exception { assertEquals(null, provider.getSourceLine("foo", 0)); assertEquals(null, provider.getSourceLine("foo", 4)); assertEquals(null, provider.getSourceLine("bar", 0)); assertEquals(null, provider.getSourceLine("bar", 5)); } public void testExceptNoNewLine() throws Exception { assertEquals("foo2:first line", provider.getSourceLine("foo2", 1)); assertEquals("foo2:second line", provider.getSourceLine("foo2", 2)); assertEquals("foo2:third line", provider.getSourceLine("foo2", 3)); assertEquals(null, provider.getSourceLine("foo2", 4)); } public void testExcerptRegion() throws Exception { assertRegionWellFormed("foo", 1); assertRegionWellFormed("foo", 2); assertRegionWellFormed("foo", 3); assertRegionWellFormed("bar", 1); assertRegionWellFormed("bar", 2); assertRegionWellFormed("bar", 3); assertRegionWellFormed("bar", 4); } public void testExcerptRegionFromInexistantSource() throws Exception { assertEquals(null, provider.getSourceRegion("inexistant", 0)); assertEquals(null, provider.getSourceRegion("inexistant", 6)); assertEquals(null, provider.getSourceRegion("inexistant", 90)); } public void testExcerptInexistantRegion() throws Exception { assertEquals(null, provider.getSourceRegion("foo", 0)); assertEquals(null, provider.getSourceRegion("foo", 4)); assertEquals(null, provider.getSourceRegion("bar", 0)); assertEquals(null, provider.getSourceRegion("bar", 5)); } /** * Asserts that a region is 'well formed': it must not be an empty and * cannot start or finish by a carriage return. In addition, it must * contain the line whose region we are taking. */ private void assertRegionWellFormed(String sourceName, int lineNumber) { Region region = provider.getSourceRegion(sourceName, lineNumber); assertNotNull(region); String sourceRegion = region.getSourceExcerpt(); assertNotNull(sourceRegion); if (lineNumber == 1) { assertEquals(1, region.getBeginningLineNumber()); } else { assertTrue(region.getBeginningLineNumber() <= lineNumber); } assertTrue(lineNumber <= region.getEndingLineNumber()); assertNotSame(sourceRegion, 0, sourceRegion.length()); assertNotSame(sourceRegion, '\n', sourceRegion.charAt(0)); assertNotSame(sourceRegion, '\n', sourceRegion.charAt(sourceRegion.length() - 1)); String line = provider.getSourceLine(sourceName, lineNumber); assertTrue(sourceRegion, sourceRegion.contains(line)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/MakeDeclaredNamesUniqueTest.java0000644000175000017500000002676612115204405031224 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.MakeDeclaredNamesUnique.InlineRenamer; import com.google.javascript.rhino.Node; /** * @author johnlenz@google.com (John Lenz) */ public class MakeDeclaredNamesUniqueTest extends CompilerTestCase { private boolean useDefaultRenamer = false; private boolean invert = false; private boolean removeConst = false; private final String localNamePrefix = "unique_"; @Override public CompilerPass getProcessor(final Compiler compiler) { if (!invert) { return new CompilerPass() { @Override public void process(Node externs, Node root) { compiler.resetUniqueNameId(); MakeDeclaredNamesUnique renamer = null; if (useDefaultRenamer) { renamer = new MakeDeclaredNamesUnique(); } else { renamer = new MakeDeclaredNamesUnique( new InlineRenamer( compiler.getCodingConvention(), compiler.getUniqueNameIdSupplier(), localNamePrefix, removeConst)); } NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), renamer); } }; } else { return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); } } @Override protected int getNumRepetitions() { // The normalize pass is only run once. return 1; } @Override public void setUp() { removeConst = false; invert = false; useDefaultRenamer = false; } private void testWithInversion(String original, String expected) { invert = false; test(original, expected); invert = true; test(expected, original); invert = false; } private void testSameWithInversion(String externs, String original) { invert = false; testSame(externs, original, null); invert = true; testSame(externs, original, null); invert = false; } private void testSameWithInversion(String original) { testSameWithInversion("", original); } private String wrapInFunction(String s) { return "function f(){" + s + "}"; } private void testInFunction(String original, String expected) { test(wrapInFunction(original), wrapInFunction(expected)); } public void testMakeLocalNamesUniqueWithContext1() { // Set the test type this.useDefaultRenamer = true; invert = true; test( "var a;function foo(){var a$$inline_1; a = 1}", "var a;function foo(){var a$$0; a = 1}"); test( "var a;function foo(){var a$$inline_1;}", "var a;function foo(){var a;}"); } public void testMakeLocalNamesUniqueWithContext2() { // Set the test type this.useDefaultRenamer = true; // Verify global names are untouched. testSameWithInversion("var a;"); // Verify global names are untouched. testSameWithInversion("a;"); // Local names are made unique. testWithInversion( "var a;function foo(a){var b;a}", "var a;function foo(a$$1){var b;a$$1}"); testWithInversion( "var a;function foo(){var b;a}function boo(){var b;a}", "var a;function foo(){var b;a}function boo(){var b$$1;a}"); testWithInversion( "function foo(a){var b}" + "function boo(a){var b}", "function foo(a){var b}" + "function boo(a$$1){var b$$1}"); // Verify functions expressions are renamed. testWithInversion( "var a = function foo(){foo()};var b = function foo(){foo()};", "var a = function foo(){foo()};var b = function foo$$1(){foo$$1()};"); // Verify catch exceptions names are made unique testWithInversion( "try { } catch(e) {e;}", "try { } catch(e) {e;}"); // Inversion does not handle exceptions correctly. test( "try { } catch(e) {e;}; try { } catch(e) {e;}", "try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}"); test( "try { } catch(e) {e; try { } catch(e) {e;}};", "try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; "); } public void testMakeLocalNamesUniqueWithContext3() { // Set the test type this.useDefaultRenamer = true; String externs = "var extern1 = {};"; // Verify global names are untouched. testSameWithInversion(externs, "var extern1 = extern1 || {};"); // Verify global names are untouched. testSame(externs, "var extern1 = extern1 || {};", null); } public void testMakeLocalNamesUniqueWithContext4() { // Set the test type this.useDefaultRenamer = true; // Inversion does not handle exceptions correctly. testInFunction( "var e; try { } catch(e) {e;}; try { } catch(e) {e;}", "var e; try { } catch(e$$1) {e$$1;}; try { } catch(e$$2) {e$$2;}"); testInFunction( "var e; try { } catch(e) {e; try { } catch(e) {e;}}", "var e; try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} }"); testInFunction( "try { } catch(e) {e;}; try { } catch(e) {e;} var e;", "try { } catch(e$$1) {e$$1;}; try { } catch(e$$2) {e$$2;} var e;"); testInFunction( "try { } catch(e) {e; try { } catch(e) {e;}} var e;", "try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} } var e;"); invert = true; testInFunction( "var e; try { } catch(e$$0) {e$$0;}; try { } catch(e$$1) {e$$1;}", "var e; try { } catch(e$$2) {e$$2;}; try { } catch(e$$0) {e$$0;}"); testInFunction( "var e; try { } catch(e$$1) {e$$1; try { } catch(e$$2) {e$$2;} };", "var e; try { } catch(e$$0) {e$$0; try { } catch(e$$1) {e$$1;} };"); testInFunction( "try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;};var e$$2;", "try { } catch(e) {e;}; try { } catch(e$$0) {e$$0;};var e$$1;"); testInFunction( "try { } catch(e) {e; try { } catch(e$$1) {e$$1;} };var e$$2", "try { } catch(e) {e; try { } catch(e$$0) {e$$0;} };var e$$1"); } public void testMakeLocalNamesUniqueWithContext5() { // Set the test type this.useDefaultRenamer = true; testWithInversion( "function f(){var f; f = 1}", "function f(){var f$$1; f$$1 = 1}"); testWithInversion( "function f(f){f = 1}", "function f(f$$1){f$$1 = 1}"); testWithInversion( "function f(f){var f; f = 1}", "function f(f$$1){var f$$1; f$$1 = 1}"); test( "var fn = function f(){var f; f = 1}", "var fn = function f(){var f$$1; f$$1 = 1}"); test( "var fn = function f(f){f = 1}", "var fn = function f(f$$1){f$$1 = 1}"); test( "var fn = function f(f){var f; f = 1}", "var fn = function f(f$$1){var f$$1; f$$1 = 1}"); } public void testArguments() { // Set the test type this.useDefaultRenamer = true; // Don't distinguish between "arguments", it can't be made unique. testSameWithInversion( "function foo(){var arguments;function bar(){var arguments;}}"); invert = true; // Don't introduce new references to arguments, it is special. test( "function foo(){var arguments$$1;}", "function foo(){var arguments$$0;}"); } public void testMakeLocalNamesUniqueWithoutContext() { // Set the test type this.useDefaultRenamer = false; test("var a;", "var a$$unique_0"); // Verify undeclared names are untouched. testSame("a;"); // Local names are made unique. test("var a;" + "function foo(a){var b;a}", "var a$$unique_0;" + "function foo$$unique_1(a$$unique_2){var b$$unique_3;a$$unique_2}"); test("var a;" + "function foo(){var b;a}" + "function boo(){var b;a}", "var a$$unique_0;" + "function foo$$unique_1(){var b$$unique_3;a$$unique_0}" + "function boo$$unique_2(){var b$$unique_4;a$$unique_0}"); // Verify function expressions are renamed. test("var a = function foo(){foo()};", "var a$$unique_0 = function foo$$unique_1(){foo$$unique_1()};"); // Verify catch exceptions names are made unique test("try { } catch(e) {e;}", "try { } catch(e$$unique_0) {e$$unique_0;}"); test("try { } catch(e) {e;};" + "try { } catch(e) {e;}", "try { } catch(e$$unique_0) {e$$unique_0;};" + "try { } catch(e$$unique_1) {e$$unique_1;}"); test("try { } catch(e) {e; " + "try { } catch(e) {e;}};", "try { } catch(e$$unique_0) {e$$unique_0; " + "try { } catch(e$$unique_1) {e$$unique_1;} }; "); } public void testMakeLocalNamesUniqueWithoutContext2() { // Set the test type this.useDefaultRenamer = false; test("var _a;", "var JSCompiler__a$$unique_0"); test("var _a = function _b(_c) { var _d; };", "var JSCompiler__a$$unique_0 = function JSCompiler__b$$unique_1(" + "JSCompiler__c$$unique_2) { var JSCompiler__d$$unique_3; };"); } public void testOnlyInversion() { invert = true; test("function f(a, a$$1) {}", "function f(a, a$$0) {}"); test("function f(a$$1, b$$2) {}", "function f(a, b) {}"); test("function f(a$$1, a$$2) {}", "function f(a, a$$0) {}"); testSame("try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}"); testSame("try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; "); testSame("var a$$1;"); testSame("function f() { var $$; }"); test("var CONST = 3; var b = CONST;", "var CONST = 3; var b = CONST;"); test("function f() {var CONST = 3; var ACONST$$1 = 2;}", "function f() {var CONST = 3; var ACONST = 2;}"); } public void testOnlyInversion2() { invert = true; test("function f() {try { } catch(e) {e;}; try { } catch(e$$0) {e$$0;}}", "function f() {try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}}"); } public void testOnlyInversion3() { invert = true; test( "function x1() {" + " var a$$1;" + " function x2() {" + " var a$$2;" + " }" + " function x3() {" + " var a$$3;" + " }" + "}", "function x1() {" + " var a$$0;" + " function x2() {" + " var a;" + " }" + " function x3() {" + " var a;" + " }" + "}"); } public void testOnlyInversion4() { invert = true; test( "function x1() {" + " var a$$0;" + " function x2() {" + " var a;a$$0++" + " }" + "}", "function x1() {" + " var a$$1;" + " function x2() {" + " var a;a$$1++" + " }" + "}"); } public void testConstRemovingRename1() { removeConst = true; test("(function () {var CONST = 3; var ACONST$$1 = 2;})", "(function () {var CONST$$unique_0 = 3; var ACONST$$unique_1 = 2;})"); } public void testConstRemovingRename2() { removeConst = true; test("var CONST = 3; var b = CONST;", "var CONST$$unique_0 = 3; var b$$unique_1 = CONST$$unique_0;"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CompilerOptionsTest.java0000644000175000017500000000323412115204405027657 0ustar apoapo/* * 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.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.Map; /** * Tests for {@link CompilerOptions}. * @author nicksantos@google.com (Nick Santos) */ public class CompilerOptionsTest extends TestCase { public void testDefines() throws Exception { CompilerOptions options = new CompilerOptions(); options.setDefineToBooleanLiteral("trueVar", true); options.setDefineToBooleanLiteral("falseVar", false); options.setDefineToNumberLiteral("threeVar", 3); options.setDefineToStringLiteral("strVar", "str"); Map actual = options.getDefineReplacements(); assertEquivalent(new Node(Token.TRUE), actual.get("trueVar")); assertEquivalent(new Node(Token.FALSE), actual.get("falseVar")); assertEquivalent(Node.newNumber(3), actual.get("threeVar")); assertEquivalent(Node.newString("str"), actual.get("strVar")); } public void assertEquivalent(Node a, Node b) { assertTrue(a.isEquivalentTo(b)); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NameAnonymousFunctionsMappedTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/NameAnonymousFunctionsMappedTest.j0000644000175000017500000001367612115204405031665 0ustar apoapo/* * 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.ImmutableMap; /** * Test cases for {@link NameAnonymousFunctionsMapped}. * */ public class NameAnonymousFunctionsMappedTest extends CompilerTestCase { private static final String EXTERNS = "var document;"; private NameAnonymousFunctionsMapped pass; private VariableMap previous; public NameAnonymousFunctionsMappedTest() { super(EXTERNS); } @Override protected int getNumRepetitions() { return 1; } @Override protected void setUp() throws Exception { super.setUp(); previous = null; } @Override public CompilerPass getProcessor(Compiler compiler) { return pass = new NameAnonymousFunctionsMapped(compiler, previous); } private void assertMapping(String... pairs) { VariableMap functionMap = pass.getFunctionMap(); assertTrue(pairs.length % 2 == 0); for (int i = 0; i < pairs.length; i += 2) { String s = functionMap.lookupSourceName(pairs[i]); assertEquals(pairs[i + 1], s); } assertEquals(pairs.length / 2, functionMap.getNewNameToOriginalNameMap().size()); } public void testSimpleVarAssignment1() { test("var a = function() { return 1; }", "var a = function $() { return 1; }"); assertMapping("$", "a"); } public void testSimpleVarAssignment2() { previous = VariableMap.fromMap(ImmutableMap.of( "a", "previous")); test("var a = function() { return 1; }", "var a = function previous() { return 1; }"); assertMapping("previous", "a"); } public void testSimpleVarAssignment3() { previous = VariableMap.fromMap(ImmutableMap.of( "unused", "$")); test("var fn = function() { return 1; }", "var fn = function $a() { return 1; }"); assertMapping("$a", "fn"); } public void testAssignmentToProperty() { test("var a = {}; a.b = function() { return 1; }", "var a = {}; a.b = function $() { return 1; }"); assertMapping("$", "a.b"); } public void testAssignmentToPrototype() { test("function a() {} a.prototype.b = function() { return 1; };", "function a() {} " + "a.prototype.b = function $() { return 1; };"); assertMapping("$", "a.prototype.b"); } public void testAssignmentToPrototype2() { test("var a = {}; " + "a.b = function() {}; " + "a.b.prototype.c = function() { return 1; };", "var a = {}; " + "a.b = function $() {}; " + "a.b.prototype.c = function $a() { return 1; };"); assertMapping("$", "a.b", "$a", "a.b.prototype.c"); } public void testAssignmentToPrototype3() { test("function a() {} a.prototype['XXX'] = function() { return 1; };", "function a() {} " + "a.prototype['XXX'] = function $() { return 1; };"); assertMapping("$", "a.prototype[\"XXX\"]"); test("function a() {} a.prototype['\\n'] = function() { return 1; };", "function a() {} " + "a.prototype['\\n'] = function $() { return 1; };"); assertMapping("$", "a.prototype[\"\\n\"]"); } public void testAssignmentToPrototype4() { test("var Y = 1; function a() {} " + "a.prototype[Y] = function() { return 1; };", "var Y = 1; function a() {} " + "a.prototype[Y] = function $() { return 1; };"); assertMapping("$", "a.prototype[Y]"); } public void testAssignmentToPrototype5() { test("function a() {} a['prototype'].b = function() { return 1; };", "function a() {} " + "a['prototype'].b = function $() { return 1; };"); assertMapping("$", "a[\"prototype\"].b"); } public void testPrototypeInitializer() { test("function a(){} a.prototype = {b: function() { return 1; }};", "function a(){} " + "a.prototype = {b: function $() { return 1; }};"); assertMapping("$", "a.prototype.b"); } public void testAssignmentToPropertyOfCallReturnValue() { test("document.getElementById('x').onClick = function() {};", "document.getElementById('x').onClick = " + "function $() {};"); assertMapping("$", "document.getElementById(\"x\").onClick"); } public void testAssignmentToPropertyOfArrayElement() { test("var a = {}; a.b = [{}]; a.b[0].c = function() {};", "var a = {}; a.b = [{}]; a.b[0].c = function $() {};"); assertMapping("$", "a.b[0].c"); test("var a = {b: {'c': {}}}; a.b['c'].d = function() {};", "var a = {b: {'c': {}}}; a.b['c'].d = function $() {};"); assertMapping("$", "a.b[\"c\"].d"); test("var a = {b: {'c': {}}}; a.b[x()].d = function() {};", "var a = {b: {'c': {}}}; a.b[x()].d = function $() {};"); assertMapping("$", "a.b[x()].d"); } public void testAssignmentToGetElem() { test("function f() { win['x' + this.id] = function(a){}; }", "function f() { win['x' + this.id] = function $(a){}; }"); // TODO - could probably do a better job encoding these assertMapping("$", "win[\"x\"+this.id]"); } public void testGetElemWithDashes() { test("var foo = {}; foo['-'] = function() {};", "var foo = {}; foo['-'] = function $() {};"); assertMapping("$", "foo[\"-\"]"); } public void testDuplicateNames() { test("var a = function() { return 1; };a = function() { return 2; }", "var a = function $() { return 1; };a = function $() { return 2; }"); assertMapping("$", "a"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ControlFlowAnalysisTest.java0000644000175000017500000016511612115204405030515 0ustar apoapo/* * 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.ControlFlowGraph.Branch; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * Tests {@link ControlFlowAnalysis}. * */ public class ControlFlowAnalysisTest extends TestCase { /** * Given an input in JavaScript, test if the control flow analysis * creates the proper control flow graph by comparing the expected * Dot file output. * * @param input Input JavaScript. * @param expected Expected Graphviz Dot file. */ private void testCfg(String input, String expected) { testCfg(input, expected, true); } /** * Gets all the edges of the graph. */ private static List> getAllEdges( ControlFlowGraph cfg) { List> edges = Lists.newArrayList(); for (DiGraphNode n : cfg.getDirectedGraphNodes()) { for (DiGraphEdge e : cfg.getOutEdges(n.getValue())) { edges.add(e); } } return edges; } /** * Gets all the control flow edges from some node with the first token to * some node with the second token. */ private static List> getAllEdges( ControlFlowGraph cfg, int startToken, int endToken) { List> edges = getAllEdges(cfg); Iterator> it = edges.iterator(); while (it.hasNext()) { DiGraphEdge edge = it.next(); Node startNode = edge.getSource().getValue(); Node endNode = edge.getDestination().getValue(); if (startNode == null || endNode == null || startNode.getType() != startToken || endNode.getType() != endToken) { it.remove(); } } return edges; } /** * Gets all the control flow edges of the given type from some node with the * first token to some node with the second token. */ private static List> getAllEdges( ControlFlowGraph cfg, int startToken, int endToken, Branch type) { List> edges = getAllEdges(cfg, startToken, endToken); Iterator> it = edges.iterator(); while (it.hasNext()) { if (type != it.next().getValue()) { it.remove(); } } return edges; } private static boolean isAncestor(Node n, Node maybeDescendent) { for (Node current = n.getFirstChild(); current != null; current = current.getNext()) { if (current == maybeDescendent || isAncestor(current, maybeDescendent)) { return true; } } return false; } /** * Gets all the control flow edges of the given type from some node with * the first token to some node with the second token. * This edge must flow from a parent to one of its descendants. */ private static List> getAllDownEdges( ControlFlowGraph cfg, int startToken, int endToken, Branch type) { List> edges = getAllEdges(cfg, startToken, endToken, type); Iterator> it = edges.iterator(); while (it.hasNext()) { DiGraphEdge edge = it.next(); Node source = edge.getSource().getValue(); Node dest = edge.getDestination().getValue(); if (!isAncestor(source, dest)) { it.remove(); } } return edges; } /** * Assert that there exists a control flow edge of the given type * from some node with the first token to some node with the second token. */ private static void assertNoEdge(ControlFlowGraph cfg, int startToken, int endToken) { assertEquals(0, getAllEdges(cfg, startToken, endToken).size()); } /** * Assert that there exists a control flow edge of the given type * from some node with the first token to some node with the second token. * This edge must flow from a parent to one of its descendants. */ private static void assertDownEdge(ControlFlowGraph cfg, int startToken, int endToken, Branch type) { assertTrue("No down edge found", 0 != getAllDownEdges(cfg, startToken, endToken, type).size()); } /** * Assert that there exists a control flow edge of the given type * from some node with the first token to some node with the second token. * This edge must flow from a node to one of its ancestors. */ private static void assertUpEdge(ControlFlowGraph cfg, int startToken, int endToken, Branch type) { assertTrue("No up edge found", 0 != getAllDownEdges(cfg, endToken, startToken, type).size()); } /** * Assert that there exists a control flow edge of the given type * from some node with the first token to some node with the second token. * This edge must flow between two nodes that are not in the same subtree. */ private static void assertCrossEdge(ControlFlowGraph cfg, int startToken, int endToken, Branch type) { int numDownEdges = getAllDownEdges(cfg, startToken, endToken, type).size(); int numUpEdges = getAllDownEdges(cfg, endToken, startToken, type).size(); int numEdges = getAllEdges(cfg, startToken, endToken, type).size(); assertTrue("No cross edges found", numDownEdges + numUpEdges < numEdges); } /** * Assert that there exists a control flow edge of the given type * from some node with the first token to the return node. */ private static void assertReturnEdge(ControlFlowGraph cfg, int startToken) { List> edges = getAllEdges(cfg); for (DiGraphEdge edge : edges) { Node source = edge.getSource().getValue(); DiGraphNode dest = edge.getDestination(); if (source.getType() == startToken && cfg.isImplicitReturn(dest)) { return; } } fail("No return edge found"); } /** * Assert that there exists no control flow edge of the given type * from some node with the first token to the return node. */ private static void assertNoReturnEdge(ControlFlowGraph cfg, int startToken) { List> edges = getAllEdges(cfg); for (DiGraphEdge edge : edges) { Node source = edge.getSource().getValue(); DiGraphNode dest = edge.getDestination(); if (source.getType() == startToken) { assertTrue("Token " + startToken + " should not have an out going" + " edge to the implicit return", !cfg.isImplicitReturn(dest)); return; } } } /** * Given an input in JavaScript, get a control flow graph for it. * * @param input Input JavaScript. */ private ControlFlowGraph createCfg(String input, boolean runSynBlockPass) { Compiler compiler = new Compiler(); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, true, true); Node root = compiler.parseSyntheticCode("cfgtest", input); if (runSynBlockPass) { CreateSyntheticBlocks pass = new CreateSyntheticBlocks( compiler, "START", "END"); pass.process(null, root); } cfa.process(null, root); return cfa.getCfg(); } private ControlFlowGraph createCfg(String input) { return createCfg(input, false); } /** * Given an input in JavaScript, test if the control flow analysis * creates the proper control flow graph by comparing the expected * Dot file output. * * @param input Input JavaScript. * @param expected Expected Graphviz Dot file. * @param shouldTraverseFunctions Whether to traverse functions when * constructing the CFG (true by default). Passed in to the * constructor of {@link ControlFlowAnalysis}. */ private void testCfg(String input, String expected, boolean shouldTraverseFunctions) { Compiler compiler = new Compiler(); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, shouldTraverseFunctions, true); Node root = compiler.parseSyntheticCode("cfgtest", input); cfa.process(null, root); ControlFlowGraph cfg = cfa.getCfg(); try { assertEquals(expected, DotFormatter.toDot(root, cfg)); } catch (java.io.IOException e) { fail("Tests failed with IOExceptions"); } } public void testSimpleStatements() { String src = "var a; a = a; a = a"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.SCRIPT, Token.VAR, Branch.UNCOND); assertCrossEdge(cfg, Token.VAR, Token.EXPR_RESULT, Branch.UNCOND); assertCrossEdge(cfg, Token.EXPR_RESULT, Token.EXPR_RESULT, Branch.UNCOND); } // Test a simple IF control flow. public void testSimpleIf() { String src = "var x; if (x) { x() } else { x() };"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.SCRIPT, Token.VAR, Branch.UNCOND); assertCrossEdge(cfg, Token.VAR, Token.IF, Branch.UNCOND); assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_TRUE); assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); assertNoEdge(cfg, Token.EXPR_RESULT, Token.CALL); assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_FALSE); assertReturnEdge(cfg, Token.EMPTY); } public void testBreakingBlock() { // BUG #1382217 String src = "X: { while(1) { break } }"; ControlFlowGraph cfg = createCfg(src); assertUpEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); } public void testBreakingTryBlock() { String src = "a: try { break a; } finally {} if(x) {}"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); src = "a: try {} finally {break a;} if(x) {}"; cfg = createCfg(src); assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); src = "a: try {} catch(e) {break a;} if(x) {}"; cfg = createCfg(src); assertCrossEdge(cfg, Token.BREAK, Token.IF, Branch.UNCOND); } public void testWithStatement() { String src = "var x, y; with(x) { y() }"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.WITH, Token.BLOCK, Branch.UNCOND); assertNoEdge(cfg, Token.WITH, Token.NAME); assertNoEdge(cfg, Token.NAME, Token.BLOCK); assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); assertReturnEdge(cfg, Token.EXPR_RESULT); } // Test a simple WHILE control flow with BREAKs. public void testSimpleWhile() { String src = "var x; while (x) { x(); if (x) { break; } x() }"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_TRUE); assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); assertDownEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_TRUE); assertReturnEdge(cfg, Token.BREAK); } public void testSimpleSwitch() { String src = "var x; switch(x){ case(1): x(); case('x'): x(); break" + "; default: x();}"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.VAR, Token.SWITCH, Branch.UNCOND); assertNoEdge(cfg, Token.SWITCH, Token.NAME); // Transfer between cases and default. assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); assertCrossEdge(cfg, Token.CASE, Token.CASE, Branch.ON_FALSE); assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); // Within each case. assertDownEdge(cfg, Token.CASE, Token.BLOCK, Branch.ON_TRUE); assertDownEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.UNCOND); assertNoEdge(cfg, Token.EXPR_RESULT, Token.CALL); assertNoEdge(cfg, Token.CALL, Token.NAME); } public void testSimpleNoDefault() { String src = "var x; switch(x){ case(1): break; } x();"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.CASE, Token.EXPR_RESULT, Branch.ON_FALSE); } public void testSwitchDefaultFirst() { // DEFAULT appears first. But it is should evaluated last. String src = "var x; switch(x){ default: break; case 1: break; }"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); } public void testSwitchDefaultInMiddle() { // DEFAULT appears in the middle. But it is should evaluated last. String src = "var x; switch(x){ case 1: break; default: break; " + "case 2: break; }"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.SWITCH, Token.CASE, Branch.UNCOND); assertCrossEdge(cfg, Token.CASE, Token.CASE, Branch.ON_FALSE); assertCrossEdge(cfg, Token.CASE, Token.DEFAULT_CASE, Branch.ON_FALSE); } public void testSwitchEmpty() { // DEFAULT appears first. But it is should evaluated last. String src = "var x; switch(x){}; x()"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.SWITCH, Token.EMPTY, Branch.UNCOND); assertCrossEdge(cfg, Token.EMPTY, Token.EXPR_RESULT, Branch.UNCOND); } public void testReturnThrowingException() { String src = "function f() {try { return a(); } catch (e) {e()}}"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.ON_EX); assertDownEdge(cfg, Token.BLOCK, Token.CATCH, Branch.UNCOND); } // Test a simple FOR loop. public void testSimpleFor() { String src = "var a; for (var x = 0; x < 100; x++) { a(); }"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"VAR\"];\n" + " node1 -> node3 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 [label=\"FOR\"];\n" + " node0 -> node4 [weight=1];\n" + " node4 -> node3 [weight=1];\n" + " node5 [label=\"NAME\"];\n" + " node3 -> node5 [weight=1];\n" + " node6 [label=\"NUMBER\"];\n" + " node5 -> node6 [weight=1];\n" + " node3 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 [label=\"LT\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"NAME\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"NUMBER\"];\n" + " node7 -> node9 [weight=1];\n" + " node10 [label=\"INC\"];\n" + " node4 -> node10 [weight=1];\n" + " node11 [label=\"NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node10 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 [label=\"BLOCK\"];\n" + " node4 -> node12 [weight=1];\n" + " node13 [label=\"EXPR_RESULT\"];\n" + " node12 -> node13 [weight=1];\n" + " node14 [label=\"CALL\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"NAME\"];\n" + " node14 -> node15 [weight=1];\n" + " node13 -> node10 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 -> node13 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> RETURN " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node12 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testSimpleForWithContinue() { String src = "var a; for (var x = 0; x < 100; x++) {a();continue;a()}"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"VAR\"];\n" + " node1 -> node3 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 [label=\"FOR\"];\n" + " node0 -> node4 [weight=1];\n" + " node4 -> node3 [weight=1];\n" + " node5 [label=\"NAME\"];\n" + " node3 -> node5 [weight=1];\n" + " node6 [label=\"NUMBER\"];\n" + " node5 -> node6 [weight=1];\n" + " node3 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 [label=\"LT\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"NAME\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"NUMBER\"];\n" + " node7 -> node9 [weight=1];\n" + " node10 [label=\"INC\"];\n" + " node4 -> node10 [weight=1];\n" + " node11 [label=\"NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node10 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 [label=\"BLOCK\"];\n" + " node4 -> node12 [weight=1];\n" + " node13 [label=\"EXPR_RESULT\"];\n" + " node12 -> node13 [weight=1];\n" + " node14 [label=\"CALL\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"NAME\"];\n" + " node14 -> node15 [weight=1];\n" + " node16 [label=\"CONTINUE\"];\n" + " node13 -> node16 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 -> node16 [weight=1];\n" + " node16 -> node10 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node17 [label=\"EXPR_RESULT\"];\n" + " node12 -> node17 [weight=1];\n" + " node18 [label=\"CALL\"];\n" + " node17 -> node18 [weight=1];\n" + " node19 [label=\"NAME\"];\n" + " node18 -> node19 [weight=1];\n" + " node17 -> node10 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 -> node13 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> RETURN " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node12 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testNestedFor() { // This is tricky as the inner FOR branches to "x++" ON_FALSE. String src = "var a,b;a();for(var x=0;x<100;x++){for(var y=0;y<100;y++){" + "continue;b();}}"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"NAME\"];\n" + " node1 -> node3 [weight=1];\n" + " node4 [label=\"EXPR_RESULT\"];\n" + " node1 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node4 [weight=1];\n" + " node5 [label=\"CALL\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"NAME\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"VAR\"];\n" + " node4 -> node7 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 [label=\"FOR\"];\n" + " node0 -> node8 [weight=1];\n" + " node8 -> node7 [weight=1];\n" + " node9 [label=\"NAME\"];\n" + " node7 -> node9 [weight=1];\n" + " node10 [label=\"NUMBER\"];\n" + " node9 -> node10 [weight=1];\n" + " node7 -> node8 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node11 [label=\"LT\"];\n" + " node8 -> node11 [weight=1];\n" + " node12 [label=\"NAME\"];\n" + " node11 -> node12 [weight=1];\n" + " node13 [label=\"NUMBER\"];\n" + " node11 -> node13 [weight=1];\n" + " node14 [label=\"INC\"];\n" + " node8 -> node14 [weight=1];\n" + " node15 [label=\"NAME\"];\n" + " node14 -> node15 [weight=1];\n" + " node14 -> node8 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node16 [label=\"BLOCK\"];\n" + " node8 -> node16 [weight=1];\n" + " node17 [label=\"FOR\"];\n" + " node16 -> node17 [weight=1];\n" + " node18 [label=\"VAR\"];\n" + " node17 -> node18 [weight=1];\n" + " node19 [label=\"NAME\"];\n" + " node18 -> node19 [weight=1];\n" + " node20 [label=\"NUMBER\"];\n" + " node19 -> node20 [weight=1];\n" + " node18 -> node17 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node21 [label=\"LT\"];\n" + " node17 -> node21 [weight=1];\n" + " node22 [label=\"NAME\"];\n" + " node21 -> node22 [weight=1];\n" + " node23 [label=\"NUMBER\"];\n" + " node21 -> node23 [weight=1];\n" + " node24 [label=\"INC\"];\n" + " node17 -> node24 [weight=1];\n" + " node25 [label=\"NAME\"];\n" + " node24 -> node25 [weight=1];\n" + " node24 -> node17 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node26 [label=\"BLOCK\"];\n" + " node17 -> node26 [weight=1];\n" + " node27 [label=\"CONTINUE\"];\n" + " node26 -> node27 [weight=1];\n" + " node27 -> node24 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node28 [label=\"EXPR_RESULT\"];\n" + " node26 -> node28 [weight=1];\n" + " node29 [label=\"CALL\"];\n" + " node28 -> node29 [weight=1];\n" + " node30 [label=\"NAME\"];\n" + " node29 -> node30 [weight=1];\n" + " node28 -> node24 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node26 -> node27 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node17 -> node14 " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node17 -> node26 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node16 -> node18 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 -> RETURN " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 -> node16 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testNestedDoWithBreak() { // The BREAK branches to a() with UNCOND. String src = "var a;do{do{break}while(a);do{a()}while(a)}while(a);"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"BLOCK\"];\n" + " node1 -> node3 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 [label=\"DO\"];\n" + " node0 -> node4 [weight=1];\n" + " node4 -> node3 [weight=1];\n" + " node5 [label=\"DO\"];\n" + " node3 -> node5 [weight=1];\n" + " node6 [label=\"BLOCK\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"BREAK\"];\n" + " node6 -> node7 [weight=1];\n" + " node8 [label=\"BLOCK\"];\n" + " node7 -> node8 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node6 -> node7 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 [label=\"NAME\"];\n" + " node5 -> node9 [weight=1];\n" + " node5 -> node6 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node5 -> node8 " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node10 [label=\"DO\"];\n" + " node3 -> node10 [weight=1];\n" + " node10 -> node8 [weight=1];\n" + " node11 [label=\"EXPR_RESULT\"];\n" + " node8 -> node11 [weight=1];\n" + " node12 [label=\"CALL\"];\n" + " node11 -> node12 [weight=1];\n" + " node13 [label=\"NAME\"];\n" + " node12 -> node13 [weight=1];\n" + " node11 -> node10 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 -> node11 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node14 [label=\"NAME\"];\n" + " node10 -> node14 [weight=1];\n" + " node10 -> node4 " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node10 -> node8 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node6 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node15 [label=\"NAME\"];\n" + " node4 -> node15 [weight=1];\n" + " node4 -> RETURN " + "[label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node3 " + "[label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testForIn() { String src = "var a,b;for(a in b){a()};"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"NAME\"];\n" + " node1 -> node3 [weight=1];\n" + " node4 [label=\"NAME\"];\n" + " node1 -> node4 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node5 [label=\"FOR\"];\n" + " node0 -> node5 [weight=1];\n" + " node6 [label=\"NAME\"];\n" + " node5 -> node6 [weight=1];\n" + " node5 -> node4 [weight=1];\n" + " node4 -> node5 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 [label=\"BLOCK\"];\n" + " node5 -> node7 [weight=1];\n" + " node8 [label=\"EXPR_RESULT\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"CALL\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"NAME\"];\n" + " node9 -> node10 [weight=1];\n" + " node8 -> node5 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 -> node8 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node11 [label=\"EMPTY\"];\n" + " node5 -> node11 [label=\"ON_FALSE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node5 -> node7 [label=\"ON_TRUE\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node11 [weight=1];\n" + " node11 -> RETURN [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testThrow() { String src = "function f() { throw 1; f() }"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"FUNCTION\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"PARAM_LIST\"];\n" + " node1 -> node3 [weight=1];\n" + " node4 [label=\"BLOCK\"];\n" + " node1 -> node4 [weight=1];\n" + " node5 [label=\"THROW\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"NUMBER\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"EXPR_RESULT\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"CALL\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"NAME\"];\n" + " node8 -> node9 [weight=1];\n" + " node7 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node5 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } // Test a simple FUNCTION. public void testSimpleFunction() { String src = "function f() { f() } f()"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"FUNCTION\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"PARAM_LIST\"];\n" + " node1 -> node3 [weight=1];\n" + " node4 [label=\"BLOCK\"];\n" + " node1 -> node4 [weight=1];\n" + " node5 [label=\"EXPR_RESULT\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"CALL\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"NAME\"];\n" + " node6 -> node7 [weight=1];\n" + " node5 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node5 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node4 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 [label=\"EXPR_RESULT\"];\n" + " node0 -> node8 [weight=1];\n" + " node9 [label=\"CALL\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"NAME\"];\n" + " node9 -> node10 [weight=1];\n" + " node8 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node8 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testSimpleCatch() { String src = "try{ throw x; x(); x['stuff']; x.x; x} catch (e) { e() }"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"TRY\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"BLOCK\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"THROW\"];\n" + " node2 -> node3 [weight=1];\n" + " node4 [label=\"NAME\"];\n" + " node3 -> node4 [weight=1];\n" + " node5 [label=\"BLOCK\"];\n" + " node3 -> node5 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node6 [label=\"EXPR_RESULT\"];\n" + " node2 -> node6 [weight=1];\n" + " node7 [label=\"CALL\"];\n" + " node6 -> node7 [weight=1];\n" + " node8 [label=\"NAME\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"EXPR_RESULT\"];\n" + " node6 -> node5 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node6 -> node9 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node9 [weight=1];\n" + " node10 [label=\"GETELEM\"];\n" + " node9 -> node10 [weight=1];\n" + " node11 [label=\"NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node12 [label=\"STRING\"];\n" + " node10 -> node12 [weight=1];\n" + " node13 [label=\"EXPR_RESULT\"];\n" + " node9 -> node13 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 -> node5 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node13 [weight=1];\n" + " node14 [label=\"GETPROP\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"NAME\"];\n" + " node14 -> node15 [weight=1];\n" + " node16 [label=\"STRING\"];\n" + " node14 -> node16 [weight=1];\n" + " node17 [label=\"EXPR_RESULT\"];\n" + " node13 -> node17 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node13 -> node5 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node17 [weight=1];\n" + " node18 [label=\"NAME\"];\n" + " node17 -> node18 [weight=1];\n" + " node17 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node3 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node5 [weight=1];\n" + " node19 [label=\"CATCH\"];\n" + " node5 -> node19 [weight=1];\n" + " node20 [label=\"NAME\"];\n" + " node19 -> node20 [weight=1];\n" + " node21 [label=\"BLOCK\"];\n" + " node19 -> node21 [weight=1];\n" + " node22 [label=\"EXPR_RESULT\"];\n" + " node21 -> node22 [weight=1];\n" + " node23 [label=\"CALL\"];\n" + " node22 -> node23 [weight=1];\n" + " node24 [label=\"NAME\"];\n" + " node23 -> node24 [weight=1];\n" + " node22 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node21 -> node22 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node19 -> node21 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node5 -> node19 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node2 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testFunctionWithinTry() { // Make sure we don't search for the handler outside of the function. String src = "try { function f() {throw 1;} } catch (e) { }"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"TRY\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"BLOCK\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"FUNCTION\"];\n" + " node2 -> node3 [weight=1];\n" + " node4 [label=\"NAME\"];\n" + " node3 -> node4 [weight=1];\n" + " node5 [label=\"PARAM_LIST\"];\n" + " node3 -> node5 [weight=1];\n" + " node6 [label=\"BLOCK\"];\n" + " node3 -> node6 [weight=1];\n" + " node7 [label=\"THROW\"];\n" + " node6 -> node7 [weight=1];\n" + " node8 [label=\"NUMBER\"];\n" + " node7 -> node8 [weight=1];\n" + " node6 -> node7 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node6 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 [label=\"BLOCK\"];\n" + " node1 -> node9 [weight=1];\n" + " node10 [label=\"CATCH\"];\n" + " node9 -> node10 [weight=1];\n" + " node11 [label=\"NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node12 [label=\"BLOCK\"];\n" + " node10 -> node12 [weight=1];\n" + " node12 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node10 -> node12 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 -> node10 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node2 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testNestedCatch() { // Make sure we are going to the right handler. String src = "try{try{throw 1;}catch(e){throw 2}}catch(f){}"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"TRY\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"BLOCK\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"TRY\"];\n" + " node2 -> node3 [weight=1];\n" + " node4 [label=\"BLOCK\"];\n" + " node3 -> node4 [weight=1];\n" + " node5 [label=\"THROW\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"NUMBER\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"BLOCK\"];\n" + " node5 -> node7 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node5 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node7 [weight=1];\n" + " node8 [label=\"CATCH\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"NAME\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"BLOCK\"];\n" + " node8 -> node10 [weight=1];\n" + " node11 [label=\"THROW\"];\n" + " node10 -> node11 [weight=1];\n" + " node12 [label=\"NUMBER\"];\n" + " node11 -> node12 [weight=1];\n" + " node13 [label=\"BLOCK\"];\n" + " node11 -> node13 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node10 -> node11 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 -> node10 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 -> node8 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node4 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node3 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node13 [weight=1];\n" + " node14 [label=\"CATCH\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"NAME\"];\n" + " node14 -> node15 [weight=1];\n" + " node16 [label=\"BLOCK\"];\n" + " node14 -> node16 [weight=1];\n" + " node16 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node14 -> node16 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node13 -> node14 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node2 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testSimpleFinally() { String src = "try{var x; foo()}finally{}"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.TRY, Token.BLOCK, Branch.UNCOND); assertDownEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); // VAR to FINALLY. assertCrossEdge(cfg, Token.EXPR_RESULT, Token.BLOCK, Branch.UNCOND); // No CATCH to FINALLY. assertNoEdge(cfg, Token.BLOCK, Token.BLOCK); } public void testSimpleCatchFinally() { // Make sure we are going to the right handler. String src = "try{ if(a){throw 1}else{a} } catch(e){a}finally{a}"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"TRY\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"BLOCK\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"IF\"];\n" + " node2 -> node3 [weight=1];\n" + " node4 [label=\"NAME\"];\n" + " node3 -> node4 [weight=1];\n" + " node5 [label=\"BLOCK\"];\n" + " node3 -> node5 [weight=1];\n" + " node6 [label=\"THROW\"];\n" + " node5 -> node6 [weight=1];\n" + " node7 [label=\"NUMBER\"];\n" + " node6 -> node7 [weight=1];\n" + " node8 [label=\"BLOCK\"];\n" + " node6 -> node8 [label=\"ON_EX\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node5 -> node6 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 [label=\"BLOCK\"];\n" + " node3 -> node9 [weight=1];\n" + " node10 [label=\"EXPR_RESULT\"];\n" + " node9 -> node10 [weight=1];\n" + " node11 [label=\"NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node12 [label=\"BLOCK\"];\n" + " node10 -> node12 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node9 -> node10 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node5 [label=\"ON_TRUE\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node3 -> node9 [label=\"ON_FALSE\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node2 -> node3 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node8 [weight=1];\n" + " node13 [label=\"CATCH\"];\n" + " node8 -> node13 [weight=1];\n" + " node14 [label=\"NAME\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"BLOCK\"];\n" + " node13 -> node15 [weight=1];\n" + " node16 [label=\"EXPR_RESULT\"];\n" + " node15 -> node16 [weight=1];\n" + " node17 [label=\"NAME\"];\n" + " node16 -> node17 [weight=1];\n" + " node16 -> node12 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node15 -> node16 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node13 -> node15 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node8 -> node13 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node12 [weight=1];\n" + " node18 [label=\"EXPR_RESULT\"];\n" + " node12 -> node18 [weight=1];\n" + " node19 [label=\"NAME\"];\n" + " node18 -> node19 [weight=1];\n" + " node18 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node12 -> node18 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node1 -> node2 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testComplicatedFinally2() { // Now the most nasty case..... String src = "while(1){try{" + "if(a){a;continue;}else if(b){b;break;} else if(c) throw 1; else a}" + "catch(e){}finally{c()}bar}foo"; ControlFlowGraph cfg = createCfg(src); // Focus only on the ON_EX edges. assertCrossEdge(cfg, Token.CONTINUE, Token.BLOCK, Branch.UNCOND); assertCrossEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); assertCrossEdge(cfg, Token.THROW, Token.BLOCK, Branch.ON_EX); } public void testDeepNestedBreakwithFinally() { String src = "X:while(1){try{while(2){try{var a;break X;}" + "finally{}}}finally{}}"; ControlFlowGraph cfg = createCfg(src); assertDownEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_TRUE); assertDownEdge(cfg, Token.BLOCK, Token.TRY, Branch.UNCOND); assertDownEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); // BREAK to FINALLY. assertCrossEdge(cfg, Token.BREAK, Token.BLOCK, Branch.UNCOND); // FINALLY to FINALLY. assertCrossEdge(cfg, Token.BLOCK, Token.BLOCK, Branch.ON_EX); assertCrossEdge(cfg, Token.WHILE, Token.BLOCK, Branch.ON_FALSE); assertReturnEdge(cfg, Token.BLOCK); } public void testDeepNestedFinally() { String src = "try{try{try{throw 1}" + "finally{1;var a}}finally{2;if(a);}}finally{3;a()}"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.THROW, Token.BLOCK, Branch.ON_EX); assertCrossEdge(cfg, Token.VAR, Token.BLOCK, Branch.UNCOND); assertCrossEdge(cfg, Token.IF, Token.BLOCK, Branch.ON_EX); } public void testReturn() { String src = "function f() { return; }"; ControlFlowGraph cfg = createCfg(src); assertReturnEdge(cfg, Token.RETURN); } public void testReturnInFinally() { String src = "function f(x){ try{} finally {return x;} }"; ControlFlowGraph cfg = createCfg(src); assertReturnEdge(cfg, Token.RETURN); } public void testReturnInFinally2() { String src = "function f(x){" + " try{ try{}finally{var dummy; return x;} } finally {} }"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.VAR, Token.RETURN, Branch.UNCOND); assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.UNCOND); assertReturnEdge(cfg, Token.BLOCK); assertNoReturnEdge(cfg, Token.RETURN); } public void testReturnInTry() { String src = "function f(x){ try{x; return x()} finally {} var y;}"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.EXPR_RESULT, Token.RETURN, Branch.UNCOND); assertCrossEdge(cfg, Token.RETURN, Token.BLOCK, Branch.UNCOND); assertCrossEdge(cfg, Token.BLOCK, Token.VAR, Branch.UNCOND); assertReturnEdge(cfg, Token.VAR); assertReturnEdge(cfg, Token.BLOCK); assertNoReturnEdge(cfg, Token.RETURN); } public void testOptionNotToTraverseFunctions() { String src = "var x = 1; function f() { x = null; }"; String expectedWhenNotTraversingFunctions = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"NUMBER\"];\n" + " node2 -> node3 [weight=1];\n" + " node1 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 [label=\"FUNCTION\"];\n" + " node0 -> node4 [weight=1];\n" + " node5 [label=\"NAME\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"PARAM_LIST\"];\n" + " node4 -> node6 [weight=1];\n" + " node7 [label=\"BLOCK\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"EXPR_RESULT\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"ASSIGN\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"NAME\"];\n" + " node9 -> node10 [weight=1];\n" + " node11 [label=\"NULL\"];\n" + " node9 -> node11 [weight=1];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"VAR\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"NAME\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"NUMBER\"];\n" + " node2 -> node3 [weight=1];\n" + " node1 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 [label=\"FUNCTION\"];\n" + " node0 -> node4 [weight=1];\n" + " node5 [label=\"NAME\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"PARAM_LIST\"];\n" + " node4 -> node6 [weight=1];\n" + " node7 [label=\"BLOCK\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"EXPR_RESULT\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"ASSIGN\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"NAME\"];\n" + " node9 -> node10 [weight=1];\n" + " node11 [label=\"NULL\"];\n" + " node9 -> node11 [weight=1];\n" + " node8 -> RETURN " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node7 -> node8 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node4 -> node7 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 " + "[label=\"UNCOND\", fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); testCfg(src, expectedWhenNotTraversingFunctions, false); } public void testInstanceOf() { String src = "try { x instanceof 'x' } catch (e) { }"; ControlFlowGraph cfg = createCfg(src, true); assertCrossEdge(cfg, Token.EXPR_RESULT, Token.BLOCK, Branch.ON_EX); } public void testSynBlock() { String src = "START(); var x; END(); var y;"; ControlFlowGraph cfg = createCfg(src, true); assertCrossEdge(cfg, Token.BLOCK, Token.EXPR_RESULT, Branch.SYN_BLOCK); } public void testPartialTraversalOfScope() { Compiler compiler = new Compiler(); ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, true, true); Node script1 = compiler.parseSyntheticCode("cfgtest", "var foo;"); Node script2 = compiler.parseSyntheticCode("cfgtest2", "var bar;"); // Create a parent node for the scripts new Node(Token.BLOCK, script1, script2); cfa.process(null, script1); ControlFlowGraph cfg = cfa.getCfg(); assertNotNull(cfg.getNode(script1)); assertNull(cfg.getNode(script2)); } public void testForLoopOrder() { assertNodeOrder( createCfg("for (var i = 0; i < 5; i++) { var x = 3; } if (true) {}"), Lists.newArrayList( Token.SCRIPT, Token.VAR, Token.FOR, Token.BLOCK, Token.VAR, Token.INC /* i++ */, Token.IF, Token.BLOCK)); } public void testLabelledForInLoopOrder() { assertNodeOrder( createCfg("var i = 0; var y = {}; " + "label: for (var x in y) { " + " if (x) { break label; } else { i++ } x(); }"), Lists.newArrayList( Token.SCRIPT, Token.VAR, Token.VAR, Token.NAME, Token.FOR, Token.BLOCK, Token.IF, Token.BLOCK, Token.BREAK, Token.BLOCK, Token.EXPR_RESULT, Token.EXPR_RESULT)); } public void testLocalFunctionOrder() { ControlFlowGraph cfg = createCfg("function f() { while (x) { x++; } } var x = 3;"); assertNodeOrder( cfg, Lists.newArrayList( Token.SCRIPT, Token.VAR, Token.FUNCTION, Token.BLOCK, Token.WHILE, Token.BLOCK, Token.EXPR_RESULT)); } public void testDoWhileOrder() { assertNodeOrder( createCfg("do { var x = 3; } while (true); void x;"), Lists.newArrayList( Token.SCRIPT, Token.BLOCK, Token.VAR, Token.DO, Token.EXPR_RESULT)); } public void testBreakInFinally1() { String src = "f = function() {\n" + " var action;\n" + " a: {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " break a\n" + // Remove this... " }\n" + " }\n" + " alert(action)\n" + // but not this. "};"; String expected = "digraph AST {\n" + " node [color=lightblue2, style=filled];\n" + " node0 [label=\"SCRIPT\"];\n" + " node1 [label=\"EXPR_RESULT\"];\n" + " node0 -> node1 [weight=1];\n" + " node2 [label=\"ASSIGN\"];\n" + " node1 -> node2 [weight=1];\n" + " node3 [label=\"NAME\"];\n" + " node2 -> node3 [weight=1];\n" + " node4 [label=\"FUNCTION\"];\n" + " node2 -> node4 [weight=1];\n" + " node5 [label=\"NAME\"];\n" + " node4 -> node5 [weight=1];\n" + " node6 [label=\"PARAM_LIST\"];\n" + " node4 -> node6 [weight=1];\n" + " node7 [label=\"BLOCK\"];\n" + " node4 -> node7 [weight=1];\n" + " node8 [label=\"VAR\"];\n" + " node7 -> node8 [weight=1];\n" + " node9 [label=\"NAME\"];\n" + " node8 -> node9 [weight=1];\n" + " node10 [label=\"LABEL\"];\n" + " node7 -> node10 [weight=1];\n" + " node11 [label=\"LABEL_NAME\"];\n" + " node10 -> node11 [weight=1];\n" + " node12 [label=\"BLOCK\"];\n" + " node10 -> node12 [weight=1];\n" + " node13 [label=\"VAR\"];\n" + " node12 -> node13 [weight=1];\n" + " node14 [label=\"NAME\"];\n" + " node13 -> node14 [weight=1];\n" + " node15 [label=\"NULL\"];\n" + " node14 -> node15 [weight=1];\n" + " node16 [label=\"TRY\"];\n" + " node12 -> node16 [weight=1];\n" + " node17 [label=\"BLOCK\"];\n" + " node16 -> node17 [weight=1];\n" + " node18 [label=\"EXPR_RESULT\"];\n" + " node17 -> node18 [weight=1];\n" + " node19 [label=\"ASSIGN\"];\n" + " node18 -> node19 [weight=1];\n" + " node20 [label=\"NAME\"];\n" + " node19 -> node20 [weight=1];\n" + " node21 [label=\"NEW\"];\n" + " node19 -> node21 [weight=1];\n" + " node22 [label=\"NAME\"];\n" + " node21 -> node22 [weight=1];\n" + " node23 [label=\"BLOCK\"];\n" + " node16 -> node23 [weight=1];\n" + " node24 [label=\"BLOCK\"];\n" + " node16 -> node24 [weight=1];\n" + " node25 [label=\"EXPR_RESULT\"];\n" + " node24 -> node25 [weight=1];\n" + " node26 [label=\"ASSIGN\"];\n" + " node25 -> node26 [weight=1];\n" + " node27 [label=\"NAME\"];\n" + " node26 -> node27 [weight=1];\n" + " node28 [label=\"NAME\"];\n" + " node26 -> node28 [weight=1];\n" + " node29 [label=\"BREAK\"];\n" + " node24 -> node29 [weight=1];\n" + " node30 [label=\"LABEL_NAME\"];\n" + " node29 -> node30 [weight=1];\n" + " node31 [label=\"EXPR_RESULT\"];\n" + " node7 -> node31 [weight=1];\n" + " node32 [label=\"CALL\"];\n" + " node31 -> node32 [weight=1];\n" + " node33 [label=\"NAME\"];\n" + " node32 -> node33 [weight=1];\n" + " node34 [label=\"NAME\"];\n" + " node32 -> node34 [weight=1];\n" + " node1 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n"; testCfg(src, expected); } public void testBreakInFinally2() { String src = "var action;\n" + "a: {\n" + " var proto = null;\n" + " try {\n" + " proto = new Proto\n" + " } finally {\n" + " action = proto;\n" + " break a\n" + " }\n" + "}\n" + "alert(action)\n"; ControlFlowGraph cfg = createCfg(src); assertCrossEdge(cfg, Token.BREAK, Token.EXPR_RESULT, Branch.UNCOND); assertNoEdge(cfg, Token.BREAK, Token.BLOCK); } /** * Asserts the priority order of CFG nodes. * * Checks that the node type of the highest-priority node matches the * first element of the list, the type of the second node matches the * second element of the list, and so on. * * @param cfg The control flow graph. * @param nodeTypes The expected node types, in order. */ private void assertNodeOrder(ControlFlowGraph cfg, List nodeTypes) { List> cfgNodes = Lists.newArrayList(cfg.getDirectedGraphNodes()); Collections.sort(cfgNodes, cfg.getOptionalNodeComparator(true)); // IMPLICIT RETURN must always be last. Node implicitReturn = cfgNodes.remove(cfgNodes.size() - 1).getValue(); assertNull(implicitReturn == null ? "null" : implicitReturn.toStringTree(), implicitReturn); assertEquals("Wrong number of CFG nodes", nodeTypes.size(), cfgNodes.size()); for (int i = 0; i < cfgNodes.size(); i++) { int expectedType = nodeTypes.get(i); int actualType = cfgNodes.get(i).getValue().getType(); assertEquals( "node type mismatch at " + i + ".\n" + "found : " + Token.name(actualType) + "\n" + "required: " + Token.name(expectedType) + "\n", expectedType, actualType); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/0000755000175000017500000000000012115204405024125 5ustar apoapoclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/GraphReachabilityTest.java0000644000175000017500000000527312115204405031221 0ustar apoapo/* * 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.graph; import com.google.javascript.jscomp.graph.GraphReachability; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.jscomp.graph.DiGraph; import junit.framework.TestCase; /** * Tests for {@link GraphReachability}. * */ public class GraphReachabilityTest extends TestCase { GraphReachability reachability = null; DiGraph graph = null; public void testSimple() { graph = LinkedDirectedGraph.create(); graph.createNode("A"); reachability = new GraphReachability(graph); reachability.compute("A"); assertReachable("A"); graph.createNode("B"); reachability = new GraphReachability(graph); reachability.compute("A"); assertReachable("A"); assertNotReachable("B"); graph.connect("A", "--->", "B"); reachability = new GraphReachability(graph); reachability.compute("B"); assertNotReachable("A"); assertReachable("B"); graph.connect("B", "--->", "A"); reachability = new GraphReachability(graph); reachability.compute("B"); assertReachable("A"); assertReachable("B"); graph.createNode("C"); reachability = new GraphReachability(graph); reachability.compute("A"); assertReachable("A"); assertReachable("B"); assertNotReachable("C"); graph.createNode("D"); graph.connect("C", "--->", "D"); reachability = new GraphReachability(graph); reachability.compute("A"); assertReachable("A"); assertReachable("B"); assertNotReachable("C"); assertNotReachable("D"); reachability.recompute("C"); assertReachable("C"); assertReachable("D"); } public void assertReachable(String s) { assertSame(s + " should be reachable", graph.getNode(s).getAnnotation(), GraphReachability.REACHABLE); } public void assertNotReachable(String s) { assertNotSame(s + " should not be reachable", graph.getNode(s).getAnnotation(), GraphReachability.REACHABLE); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/GraphPrunerTest.java0000644000175000017500000000754412115204405030077 0ustar apoapo/* * Copyright 2011 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.graph; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import junit.framework.TestCase; /** * @author nicksantos@google.com (Nick Santos) */ public class GraphPrunerTest extends TestCase { public void testThreeNodesConnected() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.connect("A", "--", "B"); graph.connect("B", "--", "C"); DiGraph pruned = new GraphPruner(graph).prune( Predicates.in(Lists.newArrayList("A", "C"))); assertEquals(2, pruned.getNodes().size()); assertTrue(pruned.isConnectedInDirection("A", "C")); } public void testThreeNodesDisconnected() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.connect("A", "--", "B"); graph.connect("C", "--", "B"); DiGraph pruned = new GraphPruner(graph).prune( Predicates.in(Lists.newArrayList("A", "C"))); assertEquals(2, pruned.getNodes().size()); assertFalse(pruned.isConnectedInDirection("A", "C")); } public void testFourNodesConnected1() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.createNode("D"); graph.connect("A", "--", "C"); graph.connect("B", "--", "C"); graph.connect("C", "--", "D"); graph.connect("A", "--", "D"); DiGraph pruned = new GraphPruner(graph).prune( Predicates.not(Predicates.equalTo("C"))); assertEquals(3, pruned.getNodes().size()); assertTrue(pruned.isConnectedInDirection("A", "D")); assertTrue(pruned.isConnectedInDirection("B", "D")); assertFalse(pruned.isConnectedInDirection("A", "B")); } public void testFourNodesConnected2() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.createNode("D"); graph.connect("A", "--", "B"); graph.connect("B", "--", "C"); graph.connect("C", "--", "D"); DiGraph pruned = new GraphPruner(graph).prune( Predicates.not(Predicates.in(Lists.newArrayList("B", "C")))); assertEquals(2, pruned.getNodes().size()); assertTrue(pruned.isConnectedInDirection("A", "D")); } public void testFiveNodesConnected() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.createNode("D"); graph.createNode("E"); graph.connect("A", "--", "B"); graph.connect("B", "--", "C"); graph.connect("C", "--", "D"); graph.connect("D", "--", "E"); graph.connect("D", "--", "B"); DiGraph pruned = new GraphPruner(graph).prune( Predicates.not(Predicates.in(Lists.newArrayList("B", "C", "D")))); assertEquals(2, pruned.getNodes().size()); assertTrue(pruned.isConnectedInDirection("A", "E")); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/GraphColoringTest.java0000644000175000017500000001643612115204405030400 0ustar apoapo/* * 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.graph; import com.google.javascript.jscomp.graph.Graph; import com.google.javascript.jscomp.graph.GraphColoring; import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.Graph.GraphEdge; import com.google.javascript.jscomp.graph.GraphColoring.Color; import com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring; import junit.framework.TestCase; import java.util.Comparator; /** * Tests for {@link GraphColoring}. * */ public class GraphColoringTest extends TestCase { public void testNoEdge() { Graph graph = LinkedUndirectedGraph.create(); for (int i = 0; i < 5; i++) { graph.createNode("Node " + i); // All node with same color. GraphColoring coloring = new GreedyGraphColoring(graph); assertEquals(1, coloring.color()); validateColoring(graph); for (int j = 0; j < i; j++) { assertEquals("Node 0", coloring.getPartitionSuperNode("Node 0")); } } } public void testTwoNodesConnected() { Graph graph = LinkedUndirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.connect("A", "--", "B"); GraphColoring coloring = new GreedyGraphColoring(graph); assertEquals(2, coloring.color()); validateColoring(graph); assertEquals("A", coloring.getPartitionSuperNode("A")); assertEquals("B", coloring.getPartitionSuperNode("B")); } public void testGreedy() { Graph graph = LinkedUndirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.createNode("D"); graph.connect("A", "--", "C"); graph.connect("B", "--", "C"); graph.connect("B", "--", "D"); GraphColoring coloring = new GreedyGraphColoring(graph); assertEquals(2, coloring.color()); validateColoring(graph); assertEquals("A", coloring.getPartitionSuperNode("A")); assertEquals("A", coloring.getPartitionSuperNode("B")); assertEquals("C", coloring.getPartitionSuperNode("C")); } public void testFullyConnected() { final int count = 100; Graph graph = LinkedUndirectedGraph.create(); for (int i = 0; i < count; i++) { graph.createNode("Node " + i); for (int j = 0; j < count; j++) { graph.createNode("Node " + j); if (i != j) { graph.connect("Node " + i, null, "Node " + j); } } } GraphColoring coloring = new GreedyGraphColoring(graph); assertEquals(count, coloring.color()); validateColoring(graph); for (int i = 0; i < count; i++) { assertEquals("Node " + i, coloring.getPartitionSuperNode("Node " + i)); } } public void testAllConnectedToOneNode() { final int count = 10; Graph graph = LinkedUndirectedGraph.create(); graph.createNode("Center"); for (int i = 0; i < count; i++) { graph.createNode("Node " + i); graph.connect("Center", null, "Node " + i); } GraphColoring coloring = new GreedyGraphColoring(graph); assertEquals(2, coloring.color()); validateColoring(graph); assertEquals("Center", coloring.getPartitionSuperNode("Center")); for (int i = 0; i < count; i++) { assertEquals("Node 0", coloring.getPartitionSuperNode("Node " + i)); } } public void testTwoFullyConnected() { final int count = 100; // A graph with two disconnected disjunct cliques. Graph graph = LinkedUndirectedGraph.create(); for (int i = 0; i < count; i++) { graph.createNode("Node Left " + i); graph.createNode("Node Right " + i); for (int j = 0; j < count; j++) { graph.createNode("Node Left " + j); graph.createNode("Node Right " + j); if (i != j) { graph.connect("Node Left " + i, null, "Node Left " + j); graph.connect("Node Right " + i, null, "Node Right " + j); } } } assertEquals(count, new GreedyGraphColoring(graph).color()); validateColoring(graph); // Connect the two cliques. for (int i = 0; i < count; i++) { graph.connect("Node Left " + i, null, "Node Right " + i); } // Think of two exactly same graph with the same coloring side by side. // If we circularly shift the colors of one of the graph by 1, we can // connect the isomorphic nodes and still have a valid coloring in the // resulting graph. assertEquals(count, new GreedyGraphColoring(graph).color()); validateColoring(graph); } public void testDeterministic() { // A pentagon. Graph graph = LinkedUndirectedGraph.create(); graph.createNode("A"); graph.createNode("B"); graph.createNode("C"); graph.createNode("D"); graph.createNode("E"); graph.connect("A", "-->", "B"); graph.connect("B", "-->", "C"); graph.connect("C", "-->", "D"); graph.connect("D", "-->", "E"); graph.connect("E", "-->", "A"); Comparator lexicographic = new Comparator() { @Override public int compare(String o1, String o2) { return o1.toString().compareTo(o2.toString()); } }; GraphColoring coloring = new GreedyGraphColoring(graph, lexicographic); assertEquals(3, coloring.color()); validateColoring(graph); assertEquals("A", coloring.getPartitionSuperNode("A")); assertEquals("A", coloring.getPartitionSuperNode("C")); Comparator biasD = new Comparator() { @Override public int compare(String o1, String o2) { return o1.replaceAll("D", "@").compareTo(o2.replaceAll("D", "@")); } }; coloring = new GreedyGraphColoring(graph, biasD); assertEquals(3, coloring.color()); validateColoring(graph); assertEquals("A", coloring.getPartitionSuperNode("A")); assertFalse("A".equals(coloring.getPartitionSuperNode("C"))); } /** * Validate that each node has been colored and connected nodes have different * coloring. */ private static void validateColoring(Graph graph) { for (GraphNode node : graph.getNodes()) { assertTrue(node.getAnnotation() != null); } for (GraphEdge edge : graph.getEdges()) { Color c1 = edge.getNodeA().getAnnotation(); Color c2 = edge.getNodeB().getAnnotation(); assertTrue(c1 != null); assertTrue(c2 != null); assertTrue(!c1.equals(c2)); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/GraphTest.java0000644000175000017500000003100312115204405026666 0ustar apoapo/* * 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.graph; import com.google.javascript.jscomp.graph.Graph; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; import com.google.javascript.jscomp.graph.Annotatable; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.SubGraph; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.Graph.GraphEdge; import com.google.javascript.jscomp.graph.UndiGraph; import junit.framework.TestCase; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests for the graph data structure. * */ public class GraphTest extends TestCase { public void testDirectedSimple() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.connect("a", "->", "b"); assertTrue(graph.hasNode("a")); assertTrue(graph.hasNode("b")); assertTrue(graph.hasNode("c")); assertFalse(graph.hasNode("d")); assertTrue(graph.isConnected("a", "b")); assertTrue(graph.isConnected("b", "a")); assertFalse(graph.isConnected("a", "c")); assertFalse(graph.isConnected("b", "c")); assertFalse(graph.isConnected("c", "a")); assertFalse(graph.isConnected("c", "b")); assertFalse(graph.isConnected("a", "a")); assertFalse(graph.isConnected("b", "b")); assertFalse(graph.isConnected("b", "c")); assertTrue(graph.isConnectedInDirection("a", "b")); assertFalse(graph.isConnectedInDirection("b", "a")); assertFalse(graph.isConnectedInDirection("a", "c")); assertFalse(graph.isConnectedInDirection("b", "c")); assertFalse(graph.isConnectedInDirection("c", "a")); assertFalse(graph.isConnectedInDirection("c", "b")); // Removal. graph.disconnect("a", "b"); assertFalse(graph.isConnected("a", "b")); assertFalse(graph.isConnected("b", "a")); // Disconnect both ways. graph.connect("a", "->", "b"); graph.connect("b", "->", "a"); graph.disconnect("a", "b"); assertFalse(graph.isConnected("a", "b")); assertFalse(graph.isConnected("b", "a")); // Disconnect one way. graph.connect("a", "->", "b"); graph.connect("b", "->", "a"); graph.disconnectInDirection("a", "b"); assertTrue(graph.isConnected("b", "a")); assertTrue(graph.isConnected("a", "b")); assertFalse(graph.isConnectedInDirection("a", "b")); assertTrue(graph.isConnectedInDirection("b", "a")); } public void testUndirectedSimple() { UndiGraph graph = LinkedUndirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.connect("a", "--", "b"); assertTrue(graph.hasNode("a")); assertTrue(graph.hasNode("b")); assertTrue(graph.hasNode("c")); assertFalse(graph.hasNode("d")); assertTrue(graph.isConnected("a", "b")); assertTrue(graph.isConnected("b", "a")); assertFalse(graph.isConnected("a", "c")); assertFalse(graph.isConnected("b", "c")); assertFalse(graph.isConnected("c", "a")); assertFalse(graph.isConnected("c", "b")); assertFalse(graph.isConnected("a", "a")); assertFalse(graph.isConnected("b", "b")); assertFalse(graph.isConnected("b", "c")); // Removal. graph.disconnect("a", "b"); assertFalse(graph.isConnected("a", "b")); assertFalse(graph.isConnected("b", "a")); } public void testDirectedSelfLoop() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.connect("a", "->", "a"); assertTrue(graph.isConnected("a", "a")); assertFalse(graph.isConnected("a", "b")); assertFalse(graph.isConnected("b", "a")); assertTrue(graph.isConnectedInDirection("a", "a")); assertFalse(graph.isConnectedInDirection("a", "b")); assertFalse(graph.isConnectedInDirection("b", "a")); // Removal. graph.disconnect("a", "a"); assertFalse(graph.isConnected("a", "a")); // Disconnect both ways. graph.connect("a", "->", "a"); graph.disconnect("a", "a"); assertFalse(graph.isConnected("a", "a")); assertFalse(graph.isConnected("a", "a")); // Disconnect one way. graph.connect("a", "->", "a"); graph.disconnectInDirection("a", "a"); assertFalse(graph.isConnected("a", "a")); } public void testUndirectedSelfLoop() { UndiGraph graph = LinkedUndirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.connect("a", "--", "a"); assertTrue(graph.isConnected("a", "a")); assertFalse(graph.isConnected("a", "b")); assertFalse(graph.isConnected("b", "a")); // Removal. graph.disconnect("a", "a"); assertFalse(graph.isConnected("a", "a")); } public void testDirectedInAndOutEdges() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.createNode("d"); graph.connect("a", "->", "b"); graph.connect("a", "-->", "b"); graph.connect("a", "--->", "b"); graph.connect("a", "->", "c"); graph.connect("c", "->", "d"); assertSetEquals(graph.getDirectedSuccNodes("a"), "b", "c"); assertSetEquals(graph.getDirectedPredNodes("b"), "a"); assertSetEquals(graph.getDirectedPredNodes("c"), "a"); assertListCount(graph.getDirectedSuccNodes("a"), "b", 3); // Removal. graph.disconnect("a", "b"); assertFalse(graph.isConnected("a", "b")); } public void testUndirectedNeighbors() { UndiGraph graph = LinkedUndirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.createNode("d"); graph.connect("a", "-", "b"); graph.connect("a", "--", "b"); graph.connect("a", "---", "b"); graph.connect("a", "-", "c"); graph.connect("c", "-", "d"); assertSetEquals(graph.getNeighborNodes("a"), "b", "c"); assertSetEquals(graph.getNeighborNodes("b"), "a"); assertSetEquals(graph.getNeighborNodes("c"), "a", "d"); assertListCount(graph.getNeighborNodes("a"), "b", 3); // Removal. graph.disconnect("a", "b"); assertFalse(graph.isConnected("a", "b")); } public void testDirectedGetFirstEdge() { DiGraph graph = LinkedDirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.connect("a", "-", "b"); assertEquals(graph.getFirstEdge("a", "b").getValue(), "-"); assertEquals(graph.getFirstEdge("b", "a").getValue(), "-"); assertNull(graph.getFirstEdge("a", "c")); } public void testUndirectedGetFirstEdge() { UndiGraph graph = LinkedUndirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.connect("a", "-", "b"); assertEquals(graph.getFirstEdge("a", "b").getValue(), "-"); assertEquals(graph.getFirstEdge("b", "a").getValue(), "-"); assertNull(graph.getFirstEdge("a", "c")); } public void testNodeAnnotations() { Graph graph = LinkedUndirectedGraph.create(); GraphNode a = graph.createNode("a"); GraphNode b = graph.createNode("b"); checkAnnotations(graph, a, b); } public void testEdgeAnnotations() { Graph graph = LinkedUndirectedGraph.create(); graph.createNode("1"); graph.createNode("2"); graph.createNode("3"); graph.connect("1", "a", "2"); graph.connect("2", "b", "3"); GraphEdge a = graph.getEdges("1", "2").get(0); GraphEdge b = graph.getEdges("2", "3").get(0); checkAnnotations(graph, a, b); } private static void checkAnnotations( Graph graph, Annotatable a, Annotatable b) { final Annotation A = new Annotation() {}; final Annotation B = new Annotation() {}; // Initially null. assertNull(a.getAnnotation()); assertNull(b.getAnnotation()); // Test basic setting. a.setAnnotation(A); b.setAnnotation(B); assertSame(A, a.getAnnotation()); assertSame(B, b.getAnnotation()); // Test clearing. graph.clearEdgeAnnotations(); graph.clearNodeAnnotations(); assertNull(a.getAnnotation()); assertNull(b.getAnnotation()); a.setAnnotation(A); b.setAnnotation(B); // Pushing clears. graph.pushEdgeAnnotations(); graph.pushNodeAnnotations(); assertNull(a.getAnnotation()); assertNull(b.getAnnotation()); a.setAnnotation(B); b.setAnnotation(B); graph.pushEdgeAnnotations(); graph.pushNodeAnnotations(); a.setAnnotation(B); b.setAnnotation(A); // Test restoring then restoring old values with pop. assertSame(B, a.getAnnotation()); assertSame(A, b.getAnnotation()); graph.popEdgeAnnotations(); graph.popNodeAnnotations(); assertSame(B, a.getAnnotation()); assertSame(B, b.getAnnotation()); graph.popEdgeAnnotations(); graph.popNodeAnnotations(); assertSame(A, a.getAnnotation()); assertSame(B, b.getAnnotation()); } public void testDegree() { testDirectedDegree(LinkedDirectedGraph.create()); testDirectedDegree(LinkedUndirectedGraph.create()); } public void testDirectedDegree(Graph graph) { graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.createNode("d"); assertEquals(0, graph.getNodeDegree("a")); graph.connect("a", "-", "b"); assertEquals(1, graph.getNodeDegree("a")); graph.connect("b", "-", "c"); assertEquals(1, graph.getNodeDegree("a")); graph.connect("a", "-", "c"); assertEquals(2, graph.getNodeDegree("a")); graph.connect("d", "-", "a"); assertEquals(3, graph.getNodeDegree("a")); } public void testDirectedConnectIfNotFound() { testDirectedConnectIfNotFound( LinkedDirectedGraph.create()); testDirectedConnectIfNotFound( LinkedUndirectedGraph.create()); } public void testDirectedConnectIfNotFound(Graph graph) { graph.createNode("a"); graph.createNode("b"); graph.connectIfNotFound("a", "-", "b"); assertEquals(1, graph.getNodeDegree("a")); graph.connectIfNotFound("a", "-", "b"); assertEquals(1, graph.getNodeDegree("a")); graph.connectIfNotFound("a", null, "b"); assertEquals(2, graph.getNodeDegree("a")); graph.connectIfNotFound("a", null, "b"); assertEquals(2, graph.getNodeDegree("a")); } public void testSimpleSubGraph() { UndiGraph graph = LinkedUndirectedGraph.create(); graph.createNode("a"); graph.createNode("b"); graph.createNode("c"); graph.connect("a", "--", "b"); SubGraph subGraph = graph.newSubGraph(); subGraph.addNode("a"); subGraph.addNode("b"); try { subGraph.addNode("d"); fail("SubGraph should not allow add for node that is not in graph."); } catch (IllegalArgumentException e) { // exception expected } assertFalse(subGraph.isIndependentOf("a")); assertFalse(subGraph.isIndependentOf("b")); assertTrue(subGraph.isIndependentOf("c")); } private > void assertListCount( List list, String target, int count) { for (GraphNode node : list) { if (node.getValue().equals(target)) { count--; } } assertTrue(count == 0); } private > void assertSetEquals( List list, String ... targets) { Set set = new HashSet(); for (GraphNode node : list) { set.add(node.getValue()); } Set otherSet = new HashSet(); for (String target : targets) { otherSet.add(target); } assertTrue(otherSet.equals(set)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/graph/StandardUnionFindTest.java0000644000175000017500000001563012115204405031207 0ustar apoapo/* * 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.graph; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableSet; import junit.framework.Assert; import junit.framework.TestCase; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Set; /** * Unit test for the {@link StandardUnionFind} data structure. * */ public class StandardUnionFindTest extends TestCase { private StandardUnionFind union; @Override protected void setUp() { union = new StandardUnionFind(); } public void testEmpty() { assertEquals(0, union.allEquivalenceClasses().size()); } public void testAdd() { union.add("foo"); union.add("bar"); assertTrue(null != union.find("foo")); assertEquals(2, union.allEquivalenceClasses().size()); } public void testUnion() { union.union("A", "B"); union.union("C", "D"); assertEquals(union.find("A"), union.find("B")); assertEquals(union.find("C"), union.find("D")); assertFalse(union.find("A").equals(union.find("D"))); } public void testSetSize() { union.union("A", "B"); union.union("B", "C"); union.union("D", "E"); union.union("F", "F"); assertEquals(3, union.findAll("A").size()); assertEquals(3, union.findAll("B").size()); assertEquals(3, union.findAll("C").size()); assertEquals(2, union.findAll("D").size()); assertEquals(1, union.findAll("F").size()); } public void testFind() { union.add("A"); union.add("B"); assertEquals("A", union.find("A")); assertEquals("B", union.find("B")); union.union("A", "B"); assertEquals(union.find("A"), union.find("B")); try { union.find("Z"); fail("find() on unknown element should not be allowed."); } catch (IllegalArgumentException e) { } } public void testAllEquivalenceClasses() { union.union("A", "B"); union.union("A", "B"); union.union("B", "A"); union.union("B", "C"); union.union("D", "E"); union.union("F", "F"); Collection> classes = union.allEquivalenceClasses(); assertEquals(3, classes.size()); assertContentsAnyOrder(classes, ImmutableSet.of("A", "B", "C"), ImmutableSet.of("D", "E"), ImmutableSet.of("F")); } public void testFindAll() { union.union("A", "B"); union.union("A", "B"); union.union("B", "A"); union.union("D", "E"); union.union("F", "F"); Set aSet = union.findAll("A"); assertEquals(2, aSet.size()); assertTrue(aSet.contains("A")); assertTrue(aSet.contains("B")); assertFalse(aSet.contains("C")); assertFalse(aSet.contains("D")); assertFalse(aSet.contains("E")); assertFalse(aSet.contains("F")); union.union("B", "C"); assertTrue(aSet.contains("C")); assertEquals(3, aSet.size()); try { union.findAll("Z"); fail("findAll() on unknown element should not be allowed."); } catch (IllegalArgumentException e) { } } public void testFindAllIterator() { union.union("A", "B"); union.union("B", "C"); union.union("A", "B"); union.union("D", "E"); Set aSet = union.findAll("A"); Iterator aIter = aSet.iterator(); assertTrue(aIter.hasNext()); assertEquals("A", aIter.next()); assertEquals("B", aIter.next()); assertEquals("C", aIter.next()); assertFalse(aIter.hasNext()); Set dSet = union.findAll("D"); Iterator dIter = dSet.iterator(); assertTrue(dIter.hasNext()); assertEquals("D", dIter.next()); assertEquals("E", dIter.next()); assertFalse(dIter.hasNext()); } public void testFindAllSize() { union.union("A", "B"); union.union("B", "C"); assertEquals(3, union.findAll("A").size()); assertEquals(3, union.findAll("B").size()); assertEquals(3, union.findAll("C").size()); union.union("D", "E"); assertEquals(3, union.findAll("C").size()); assertEquals(2, union.findAll("D").size()); union.union("B", "E"); assertEquals(5, union.findAll("C").size()); assertEquals(5, union.findAll("D").size()); } public void testElements(){ union.union("A", "B"); union.union("B", "C"); union.union("A", "B"); union.union("D", "E"); Set elements = union.elements(); assertEquals(ImmutableSet.of("A", "B", "C", "D", "E"), elements); assertFalse(elements.contains("F")); } public void testCopy() { union.union("A", "B"); union.union("B", "Z"); union.union("X", "Y"); UnionFind copy = new StandardUnionFind(union); assertContentsAnyOrder(copy.findAll("Z"), "A", "B", "Z"); assertContentsAnyOrder(copy.findAll("X"), "X", "Y"); } public void testChangesToCopyDontAffectOriginal() { union.union("A", "B"); union.union("X", "Y"); union.union("A", "C"); UnionFind copy = new StandardUnionFind(union); copy.union("A", "D"); assertContentsAnyOrder(copy.findAll("D"), "A", "B", "C", "D"); assertContentsAnyOrder(union.findAll("A"), "A", "B", "C"); assertContentsAnyOrder(copy.findAll("X"), "X", "Y"); try { union.findAll("D"); fail("D has been inserted to the original collection"); } catch (IllegalArgumentException e) { // Expected. } } public void testCheckEquivalent() { union.union("A", "B"); union.add("C"); assertTrue(union.areEquivalent("A", "B")); assertFalse(union.areEquivalent("C", "A")); assertFalse(union.areEquivalent("C", "B")); try { union.areEquivalent("A", "F"); } catch (IllegalArgumentException e) { // Expected. } } /** * Asserts that {@code actual} contains precisely the elements * {@code expected}, in any order. Both collections may contain * duplicates, and this method will only pass if the quantities are * exactly the same. */ private static void assertContentsAnyOrder( String message, Iterable actual, Object... expected) { Assert.assertEquals(message, HashMultiset.create(Arrays.asList(expected)), HashMultiset.create(actual)); } /** * Variant of {@link #assertContentsAnyOrder(String,Iterable,Object...)} * using a generic message. */ private static void assertContentsAnyOrder( Iterable actual, Object... expected) { assertContentsAnyOrder((String) null, actual, expected); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PhaseOptimizerTest.java0000644000175000017500000001566512115204405027507 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.CompilerOptions.TracerMode; import com.google.javascript.jscomp.PhaseOptimizer.Loop; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; import java.util.List; import java.util.Random; /** * Tests for {@link PhaseOptimizer}. * @author nicksantos@google.com (Nick Santos) */ public class PhaseOptimizerTest extends TestCase { private final List passesRun = Lists.newArrayList(); private PhaseOptimizer optimizer; private Compiler compiler; private PerformanceTracker tracker; @Override public void setUp() { passesRun.clear(); compiler = new Compiler(); compiler.initCompilerOptionsIfTesting(); tracker = new PerformanceTracker( new Node(Token.BLOCK), TracerMode.TIMING_ONLY); optimizer = new PhaseOptimizer(compiler, tracker, null); } public void testOneRun() { addOneTimePass("x"); assertPasses("x"); } public void testLoop1() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 0); assertPasses("x"); } public void testLoop2() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 3); assertPasses("x", "x", "x", "x"); } public void testLoop3() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 3); addLoopedPass(loop, "y", 1); assertPasses("x", "y", "x", "y", "x", "x", "y"); } public void testNotInfiniteLoop() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", PhaseOptimizer.MAX_LOOPS - 1); optimizer.process(null, null); assertEquals("There should be no errors.", 0, compiler.getErrorCount()); } public void testInfiniteLoop() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", PhaseOptimizer.MAX_LOOPS + 1); try { optimizer.process(null, null); fail("Expected RuntimeException"); } catch (RuntimeException e) { assertTrue(e.getMessage().contains(PhaseOptimizer.OPTIMIZE_LOOP_ERROR)); } } public void testCombined() { addOneTimePass("a"); Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 3); addLoopedPass(loop, "y", 1); addOneTimePass("z"); assertPasses("a", "x", "y", "x", "y", "x", "x", "y", "z"); } public void testSanityCheck() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 1); addOneTimePass("z"); optimizer.setSanityCheck( createPassFactory("sanity", createPass("sanity", 0), false)); assertPasses("x", "sanity", "x", "sanity", "z", "sanity"); } public void testConsumption1() { optimizer.consume( Lists.newArrayList( createPassFactory("a", 0, true), createPassFactory("b", 1, false), createPassFactory("c", 2, false), createPassFactory("d", 1, false), createPassFactory("e", 1, true), createPassFactory("f", 0, true))); assertPasses("a", "b", "c", "d", "b", "c", "d", "c", "b", "d", "e", "f"); } public void testConsumption2() { optimizer.consume( Lists.newArrayList( createPassFactory("a", 2, false), createPassFactory("b", 1, true), createPassFactory("c", 1, false))); assertPasses("a", "a", "a", "b", "c", "c"); } public void testConsumption3() { optimizer.consume( Lists.newArrayList( createPassFactory("a", 2, true), createPassFactory("b", 0, false), createPassFactory("c", 0, false))); assertPasses("a", "b", "c"); } public void testDuplicateLoop() { Loop loop = optimizer.addFixedPointLoop(); addLoopedPass(loop, "x", 1); try { addLoopedPass(loop, "x", 1); fail("Expected exception"); } catch (IllegalArgumentException e) {} } public void testPassOrdering() { Loop loop = optimizer.addFixedPointLoop(); List optimalOrder = Lists.newArrayList( PhaseOptimizer.OPTIMAL_ORDER); Random random = new Random(); while (optimalOrder.size() > 0) { addLoopedPass( loop, optimalOrder.remove(random.nextInt(optimalOrder.size())), 0); } optimizer.process(null, null); assertEquals(PhaseOptimizer.OPTIMAL_ORDER, passesRun); } public void testProgress() { final List progressList = Lists.newArrayList(); compiler = new Compiler() { @Override void setProgress(double p, String name) { progressList.add(p); } }; compiler.initCompilerOptionsIfTesting(); optimizer = new PhaseOptimizer(compiler, null, new PhaseOptimizer.ProgressRange(0, 100)); addOneTimePass("x1"); addOneTimePass("x2"); addOneTimePass("x3"); addOneTimePass("x4"); optimizer.process(null, null); assertEquals(4, progressList.size()); assertEquals(25, Math.round(progressList.get(0))); assertEquals(50, Math.round(progressList.get(1))); assertEquals(75, Math.round(progressList.get(2))); assertEquals(100, Math.round(progressList.get(3))); } public void assertPasses(String ... names) { optimizer.process(null, null); assertEquals(Lists.newArrayList(names), passesRun); } private void addOneTimePass(String name) { optimizer.addOneTimePass( createPassFactory(name, 0, true)); } private void addLoopedPass(Loop loop, String name, int numChanges) { loop.addLoopedPass( createPassFactory(name, numChanges, false)); } private PassFactory createPassFactory( String name, int numChanges, boolean isOneTime) { return createPassFactory(name, createPass(name, numChanges), isOneTime); } private PassFactory createPassFactory( String name, final CompilerPass pass, boolean isOneTime) { return new PassFactory(name, isOneTime) { @Override protected CompilerPass create(AbstractCompiler compiler) { return pass; } }; } private CompilerPass createPass(final String name, int numChanges) { final int[] numChangesClosure = new int[] {numChanges}; return new CompilerPass() { @Override public void process(Node externs, Node root) { passesRun.add(name); if (numChangesClosure[0] > 0) { compiler.reportCodeChange(); numChangesClosure[0] = numChangesClosure[0] - 1; } } }; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TypeCheckFunctionCheckTest.java0000644000175000017500000001471012115204405031055 0ustar apoapo/* * 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 static com.google.javascript.jscomp.TypeCheck.WRONG_ARGUMENT_COUNT; import static com.google.javascript.jscomp.FunctionTypeBuilder.OPTIONAL_ARG_AT_END; import static com.google.javascript.jscomp.FunctionTypeBuilder.VAR_ARGS_MUST_BE_LAST; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.rhino.Node; /** * Tests for function and method arity checking in TypeCheck. * @author nicksantos@google.com (Nick Santos) */ public class TypeCheckFunctionCheckTest extends CompilerTestCase { private CodingConvention convention = null; public TypeCheckFunctionCheckTest() { parseTypeInfo = true; enableTypeCheck(CheckLevel.ERROR); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) {} }; } @Override protected CodingConvention getCodingConvention() { return convention; } @Override protected int getNumRepetitions() { // TypeCheck will only run once, regardless of what this returns. // We return 1 so that the framework only expects 1 warning. return 1; } @Override public void setUp() throws Exception { super.setUp(); convention = new GoogleCodingConvention(); } public void testFunctionAritySimple() { assertOk("", ""); assertOk("a", "'a'"); assertOk("a,b", "10, 20"); } public void testFunctionArityWithOptionalArgs() { assertOk("a,b,opt_c", "1,2"); assertOk("a,b,opt_c", "1,2,3"); assertOk("a,opt_b,opt_c", "1"); } public void testFunctionArityWithVarArgs() { assertOk("var_args", ""); assertOk("var_args", "1,2"); assertOk("a,b,var_args", "1,2"); assertOk("a,b,var_args", "1,2,3"); assertOk("a,b,var_args", "1,2,3,4,5"); assertOk("a,opt_b,var_args", "1"); assertOk("a,opt_b,var_args", "1,2"); assertOk("a,opt_b,var_args", "1,2,3"); assertOk("a,opt_b,var_args", "1,2,3,4,5"); } public void testWrongNumberOfArgs() { assertWarning("a,b,opt_c", "1", WRONG_ARGUMENT_COUNT); assertWarning("a,b,opt_c", "1,2,3,4", WRONG_ARGUMENT_COUNT); assertWarning("a,b", "1, 2, 3", WRONG_ARGUMENT_COUNT); assertWarning("", "1, 2, 3", WRONG_ARGUMENT_COUNT); assertWarning("a,b,c,d", "1, 2, 3", WRONG_ARGUMENT_COUNT); assertWarning("a,b,var_args", "1", WRONG_ARGUMENT_COUNT); assertWarning("a,b,opt_c,var_args", "1", WRONG_ARGUMENT_COUNT); } public void testVarArgsLast() { assertWarning("a,b,var_args,c", "1,2,3,4", VAR_ARGS_MUST_BE_LAST); } public void testOptArgsLast() { assertWarning("a,b,opt_d,c", "1, 2, 3", OPTIONAL_ARG_AT_END); assertWarning("a,b,opt_d,c", "1, 2", OPTIONAL_ARG_AT_END); } public void testFunctionsWithJsDoc1() { testSame("/** @param {*=} c */ function foo(a,b,c) {} foo(1,2);"); } public void testFunctionsWithJsDoc2() { testSame("/** @param {*=} c */ function foo(a,b,c) {} foo(1,2,3);"); } public void testFunctionsWithJsDoc3() { testSame("/** @param {*=} c \n * @param {*=} b */ " + "function foo(a,b,c) {} foo(1);"); } public void testFunctionsWithJsDoc4() { testSame("/** @param {...*} a */ var foo = function(a) {}; foo();"); } public void testFunctionsWithJsDoc5() { testSame("/** @param {...*} a */ var foo = function(a) {}; foo(1,2);"); } public void testFunctionsWithJsDoc6() { testSame("/** @param {...*} b */ var foo = function(a, b) {}; foo();", WRONG_ARGUMENT_COUNT); } public void testFunctionsWithJsDoc7() { String fooDfn = "/** @param {*} [b] */ var foo = function(b) {};"; testSame(fooDfn + "foo();"); testSame(fooDfn + "foo(1);"); testSame(fooDfn + "foo(1, 2);", WRONG_ARGUMENT_COUNT); } public void testFunctionWithDefaultCodingConvention() { convention = CodingConventions.getDefault(); testSame("var foo = function(x) {}; foo(1, 2);", WRONG_ARGUMENT_COUNT); testSame("var foo = function(opt_x) {}; foo(1, 2);", WRONG_ARGUMENT_COUNT); testSame("var foo = function(var_args) {}; foo(1, 2);", WRONG_ARGUMENT_COUNT); } public void testMethodCalls() { final String METHOD_DEFS = "/** @constructor */\n" + "function Foo() {}" + // Methods defined in a separate functions and then added via assignment "function twoArg(arg1, arg2) {};" + "Foo.prototype.prototypeMethod = twoArg;" + "Foo.staticMethod = twoArg;" + // Constructor that specifies a return type "/**\n * @constructor\n * @return {Bar}\n */\n" + "function Bar() {}"; // Prototype method with too many arguments. testSame(METHOD_DEFS + "var f = new Foo();f.prototypeMethod(1, 2, 3);", TypeCheck.WRONG_ARGUMENT_COUNT); // Prototype method with too few arguments. testSame(METHOD_DEFS + "var f = new Foo();f.prototypeMethod(1);", TypeCheck.WRONG_ARGUMENT_COUNT); // Static method with too many arguments. testSame(METHOD_DEFS + "Foo.staticMethod(1, 2, 3);", TypeCheck.WRONG_ARGUMENT_COUNT); // Static method with too few arguments. testSame(METHOD_DEFS + "Foo.staticMethod(1);", TypeCheck.WRONG_ARGUMENT_COUNT); // Constructor calls require new keyword testSame(METHOD_DEFS + "Bar();", TypeCheck.CONSTRUCTOR_NOT_CALLABLE); // Extern constructor calls require new keyword testSame(METHOD_DEFS, "Foo();", TypeCheck.CONSTRUCTOR_NOT_CALLABLE); // Extern constructor call without new keyword testSame(METHOD_DEFS, "Bar();", null); } public void assertOk(String params, String arguments) { assertWarning(params, arguments, null); } public void assertWarning(String params, String arguments, DiagnosticType type) { testSame("function foo(" + params + ") {} foo(" + arguments + ");", type); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CrossModuleCodeMotionTest.java0000644000175000017500000004435312115204405030760 0ustar apoapo/* * 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; /** * Tests for {@link CrossModuleCodeMotion}. * */ public class CrossModuleCodeMotionTest extends CompilerTestCase { private static final String EXTERNS = "alert"; public CrossModuleCodeMotionTest() { super(EXTERNS); } @Override public void setUp() { super.enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(Compiler compiler) { return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); } public void testFunctionMovement1() { // This tests lots of things: // 1) f1 is declared in m1, and used in m2. Move it to m2 // 2) f2 is declared in m1, and used in m3 twice. Move it to m3 // 3) f3 is declared in m1, and used in m2+m3. It stays put // 4) g declared in m1 and never used. It stays put // 5) h declared in m2 and never used. It stays put // 6) f4 declared in m1 and used in m2 as var. It moves to m2 JSModule[] modules = createModuleStar( // m1 "function f1(a) { alert(a); }" + "function f2(a) { alert(a); }" + "function f3(a) { alert(a); }" + "function f4() { alert(1); }" + "function g() { alert('ciao'); }", // m2 "f1('hi'); f3('bye'); var a = f4;" + "function h(a) { alert('h:' + a); }", // m3 "f2('hi'); f2('hi'); f3('bye');"); test(modules, new String[] { // m1 "function f3(a) { alert(a); }" + "function g() { alert('ciao'); }", // m2 "function f4() { alert(1); }" + "function f1(a) { alert(a); }" + "f1('hi'); f3('bye'); var a = f4;" + "function h(a) { alert('h:' + a); }", // m3 "function f2(a) { alert(a); }" + "f2('hi'); f2('hi'); f3('bye');", }); } public void testFunctionMovement2() { // having f declared as a local variable should block the migration to m2 JSModule[] modules = createModuleStar( // m1 "function f(a) { alert(a); }" + "function g() {var f = 1; f++}", // m2 "f(1);"); test(modules, new String[] { // m1 "function g() {var f = 1; f++}", // m2 "function f(a) { alert(a); }" + "f(1);", }); } public void testFunctionMovement3() { // having f declared as a arg should block the migration to m2 JSModule[] modules = createModuleStar( // m1 "function f(a) { alert(a); }" + "function g(f) {f++}", // m2 "f(1);"); test(modules, new String[] { // m1 "function g(f) {f++}", // m2 "function f(a) { alert(a); }" + "f(1);", }); } public void testFunctionMovement4() { // Try out moving a function which returns a closure JSModule[] modules = createModuleStar( // m1 "function f(){return function(a){}}", // m2 "var a = f();" ); test(modules, new String[] { // m1 "", // m2 "function f(){return function(a){}}" + "var a = f();", }); } public void testFunctionMovement5() { // Try moving a recursive function [using factorials for kicks] JSModule[] modules = createModuleStar( // m1 "function f(n){return (n<1)?1:f(n-1)}", // m2 "var a = f(4);" ); test(modules, new String[] { // m1 "", // m2 "function f(n){return (n<1)?1:f(n-1)}" + "var a = f(4);", }); } public void testFunctionMovement5b() { // Try moving a recursive function declared differently. JSModule[] modules = createModuleStar( // m1 "var f = function(n){return (n<1)?1:f(n-1)};", // m2 "var a = f(4);" ); test(modules, new String[] { // m1 "", // m2 "var f = function(n){return (n<1)?1:f(n-1)};" + "var a = f(4);", }); } public void testFunctionMovement6() { // Try out moving to the common ancestor JSModule[] modules = createModuleChain( // m1 "function f(){return 1}", // m2 "var a = f();", // m3 "var b = f();" ); test(modules, new String[] { // m1 "", // m2 "function f(){return 1}" + "var a = f();", // m3 "var b = f();", }); } public void testFunctionMovement7() { // Try out moving to the common ancestor with deeper ancestry chain JSModule[] modules = createModules( // m1 "function f(){return 1}", // m2 "", // m3 "var a = f();", // m4 "var b = f();", // m5 "var c = f();" ); modules[1].addDependency(modules[0]); modules[2].addDependency(modules[1]); modules[3].addDependency(modules[1]); modules[4].addDependency(modules[1]); test(modules, new String[] { // m1 "", // m2 "function f(){return 1}", // m3 "var a = f();", // m4 "var b = f();", // m5 "var c = f();", }); } public void testFunctionMovement8() { // Check what happens with named functions JSModule[] modules = createModuleChain( // m1 "var v = function f(){return 1}", // m2 "v();" ); test(modules, new String[] { // m1 "", // m2 "var v = function f(){return 1};" + "v();", }); } public void testFunctionNonMovement1() { // This tests lots of things: // 1) we can't move it if it is a class with non-const attributes accessed // 2) if it's in an if statement, we can't move it // 3) if it's in an while statement, we can't move it [with some extra // block elements] testSame(createModuleStar( // m1 "function f(){};f.prototype.bar=new f;" + "if(a)function f2(){}" + "{{while(a)function f3(){}}}", // m2 "var a = new f();f2();f3();")); } public void testFunctionNonMovement2() { // A generic case where 2 modules depend on the first one. But it's the // common ancestor, so we can't move. testSame(createModuleStar( // m1 "function f(){return 1}", // m2 "var a = f();", // m3 "var b = f();")); } public void testClassMovement1() { test(createModuleStar( // m1 "function f(){} f.prototype.bar=function (){};", // m2 "var a = new f();"), new String[] { "", "function f(){} f.prototype.bar=function (){};" + "var a = new f();" }); } public void testClassMovement2() { // NOTE: this is the result of two iterations test(createModuleChain( // m1 "function f(){} f.prototype.bar=3; f.prototype.baz=5;", // m2 "f.prototype.baq = 7;", // m3 "f.prototype.baz = 9;", // m4 "var a = new f();"), new String[] { // m1 "", // m2 "", // m3 "function f(){} f.prototype.bar=3; f.prototype.baz=5;" + "f.prototype.baq = 7;" + "f.prototype.baz = 9;", // m4 "var a = new f();" }); } public void testClassMovement3() { // NOTE: this is the result of two iterations test(createModuleChain( // m1 "var f = function() {}; f.prototype.bar=3; f.prototype.baz=5;", // m2 "f = 7;", // m3 "f = 9;", // m4 "f = 11;"), new String[] { // m1 "", // m2 "", // m3 "var f = function() {}; f.prototype.bar=3; f.prototype.baz=5;" + "f = 7;" + "f = 9;", // m4 "f = 11;" }); } public void testClassMovement4() { testSame(createModuleStar( // m1 "function f(){} f.prototype.bar=3; f.prototype.baz=5;", // m2 "f.prototype.baq = 7;", // m3 "var a = new f();")); } public void testClassMovement5() { JSModule[] modules = createModules( // m1 "function f(){} f.prototype.bar=3; f.prototype.baz=5;", // m2 "", // m3 "f.prototype.baq = 7;", // m4 "var a = new f();"); modules[1].addDependency(modules[0]); modules[2].addDependency(modules[1]); modules[3].addDependency(modules[1]); test(modules, new String[] { // m1 "", // m2 "function f(){} f.prototype.bar=3; f.prototype.baz=5;", // m3 "f.prototype.baq = 7;", // m4 + "var a = new f();" }); } public void testClassMovement6() { test(createModuleChain( // m1 "function Foo(){} function Bar(){} goog.inherits(Bar, Foo);" + "new Foo();", // m2 "new Bar();"), new String[] { // m1 "function Foo(){} new Foo();", // m2 "function Bar(){} goog.inherits(Bar, Foo); new Bar();" }); } public void testClassMovement7() { testSame(createModuleChain( // m1 "function Foo(){} function Bar(){} goog.inherits(Bar, Foo);" + "new Bar();", // m2 "new Foo();")); } public void testStubMethodMovement1() { test(createModuleChain( // m1 "function Foo(){} " + "Foo.prototype.bar = JSCompiler_stubMethod(x);", // m2 "new Foo();"), new String[] { // m1 "", "function Foo(){} " + "Foo.prototype.bar = JSCompiler_stubMethod(x);" + "new Foo();" }); } public void testStubMethodMovement2() { test(createModuleChain( // m1 "function Foo(){} " + "Foo.prototype.bar = JSCompiler_unstubMethod(x);", // m2 "new Foo();"), new String[] { // m1 "", "function Foo(){} " + "Foo.prototype.bar = JSCompiler_unstubMethod(x);" + "new Foo();" }); } public void testNoMoveSideEffectProperty() { testSame(createModuleChain( // m1 "function Foo(){} " + "Foo.prototype.bar = createSomething();", // m2 "new Foo();")); } public void testAssignMovement() { test(createModuleChain( // m1 "var f = 3;" + "f = 5;", // m2 "var h = f;"), new String[] { // m1 "", // m2 "var f = 3;" + "f = 5;" + "var h = f;" }); // don't move nested assigns testSame(createModuleChain( // m1 "var f = 3;" + "var g = f = 5;", // m2 "var h = f;")); } public void testNoClassMovement2() { test(createModuleChain( // m1 "var f = {};" + "f.h = 5;", // m2 "var h = f;"), new String[] { // m1 "", // m2 "var f = {};" + "f.h = 5;" + "var h = f;" }); // don't move nested getprop assigns testSame(createModuleChain( // m1 "var f = {};" + "var g = f.h = 5;", // m2 "var h = f;")); } public void testLiteralMovement1() { test(createModuleChain( // m1 "var f = {'hi': 'mom', 'bye': function() {}};", // m2 "var h = f;"), new String[] { // m1 "", // m2 "var f = {'hi': 'mom', 'bye': function() {}};" + "var h = f;" }); } public void testLiteralMovement2() { testSame(createModuleChain( // m1 "var f = {'hi': 'mom', 'bye': goog.nullFunction};", // m2 "var h = f;")); } public void testLiteralMovement3() { test(createModuleChain( // m1 "var f = ['hi', function() {}];", // m2 "var h = f;"), new String[] { // m1 "", // m2 "var f = ['hi', function() {}];" + "var h = f;" }); } public void testLiteralMovement4() { testSame(createModuleChain( // m1 "var f = ['hi', goog.nullFunction];", // m2 "var h = f;")); } public void testVarMovement1() { // test moving a variable JSModule[] modules = createModuleStar( // m1 "var a = 0;", // m2 "var x = a;" ); test(modules, new String[] { // m1 "", // m2 "var a = 0;" + "var x = a;", }); } public void testVarMovement2() { // Test moving 1 variable out of the block JSModule[] modules = createModuleStar( // m1 "var a = 0; var b = 1; var c = 2;", // m2 "var x = b;" ); test(modules, new String[] { // m1 "var a = 0; var c = 2;", // m2 "var b = 1;" + "var x = b;" }); } public void testVarMovement3() { // Test moving all variables out of the block JSModule[] modules = createModuleStar( // m1 "var a = 0; var b = 1;", // m2 "var x = a + b;" ); test(modules, new String[] { // m1 "", // m2 "var b = 1;" + "var a = 0;" + "var x = a + b;" }); } public void testVarMovement4() { // Test moving a function JSModule[] modules = createModuleStar( // m1 "var a = function(){alert(1)};", // m2 "var x = a;" ); test(modules, new String[] { // m1 "", // m2 "var a = function(){alert(1)};" + "var x = a;" }); } public void testVarMovement5() { // Don't move a function outside of scope testSame(createModuleStar( // m1 "var a = alert;", // m2 "var x = a;")); } public void testVarMovement6() { // Test moving a var with no assigned value JSModule[] modules = createModuleStar( // m1 "var a;", // m2 "var x = a;" ); test(modules, new String[] { // m1 "", // m2 "var a;" + "var x = a;" }); } public void testVarMovement7() { // Don't move a variable higher in the dependency tree testSame(createModuleStar( // m1 "function f() {g();}", // m2 "function g(){};")); } public void testVarMovement8() { JSModule[] modules = createModuleBush( // m1 "var a = 0;", // m2 -> m1 "", // m3 -> m2 "var x = a;", // m4 -> m2 "var y = a;" ); test(modules, new String[] { // m1 "", // m2 "var a = 0;", // m3 "var x = a;", // m4 "var y = a;" }); } public void testVarMovement9() { JSModule[] modules = createModuleTree( // m1 "var a = 0; var b = 1; var c = 3;", // m2 -> m1 "", // m3 -> m1 "", // m4 -> m2 "a;", // m5 -> m2 "a;c;", // m6 -> m3 "b;", // m7 -> m4 "b;c;" ); test(modules, new String[] { // m1 "var c = 3;", // m2 "var a = 0;", // m3 "var b = 1;", // m4 "a;", // m5 "a;c;", // m6 "b;", // m7 "b;c;" }); } public void testClone1() { test(createModuleChain( // m1 "function f(){} f.prototype.clone = function() { return new f };", // m2 "var a = (new f).clone();"), new String[] { // m1 "", "function f(){} f.prototype.clone = function() { return new f() };" + // m2 "var a = (new f).clone();" }); } public void testClone2() { test(createModuleChain( // m1 "function f(){}" + "f.prototype.cloneFun = function() {" + " return function() {new f}" + "};", // m2 "var a = (new f).cloneFun();"), new String[] { // m1 "", "function f(){}" + "f.prototype.cloneFun = function() {" + " return function() {new f}" + "};" + // m2 "var a = (new f).cloneFun();" }); } public void testBug4118005() { testSame(createModuleChain( // m1 "var m = 1;\n" + "(function () {\n" + " var x = 1;\n" + " m = function() { return x };\n" + "})();\n", // m2 "m();")); } public void testEmptyModule() { // When the dest module is empty, it might try to move the code to the // one of the modules that the empty module depends on. In some cases // this might ended up to be the same module as the definition of the code. // When that happens, CrossModuleCodeMotion might report a code change // while nothing is moved. This should not be a problem if we know all // modules are non-empty. JSModule m1 = new JSModule("m1"); m1.add(SourceFile.fromCode("m1", "function x() {}")); JSModule empty = new JSModule("empty"); empty.addDependency(m1); JSModule m2 = new JSModule("m2"); m2.add(SourceFile.fromCode("m2", "x()")); m2.addDependency(empty); JSModule m3 = new JSModule("m3"); m3.add(SourceFile.fromCode("m3", "x()")); m3.addDependency(empty); test(new JSModule[] {m1,empty,m2,m3}, new String[] { "", "function x() {}", "x()", "x()" }); } } ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeReplaceKnownMethodsTest.ja0000644000175000017500000002634112115204405031604 0ustar apoapo/* * Copyright 2011 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; /** * Unit tests for {#link {@link PeepholeReplaceKnownMethods} * */ public class PeepholeReplaceKnownMethodsTest extends CompilerTestCase { private boolean late = true; public PeepholeReplaceKnownMethodsTest() { super(""); } @Override public void setUp() { enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(final Compiler compiler) { CompilerPass peepholePass = new PeepholeOptimizationsPass(compiler, new PeepholeReplaceKnownMethods(late)); return peepholePass; } public void testStringIndexOf() { fold("x = 'abcdef'.indexOf('b')", "x = 1"); fold("x = 'abcdefbe'.indexOf('b', 2)", "x = 6"); fold("x = 'abcdef'.indexOf('bcd')", "x = 1"); fold("x = 'abcdefsdfasdfbcdassd'.indexOf('bcd', 4)", "x = 13"); fold("x = 'abcdef'.lastIndexOf('b')", "x = 1"); fold("x = 'abcdefbe'.lastIndexOf('b')", "x = 6"); fold("x = 'abcdefbe'.lastIndexOf('b', 5)", "x = 1"); // Both elements must be strings. Don't do anything if either one is not // string. fold("x = 'abc1def'.indexOf(1)", "x = 3"); fold("x = 'abcNaNdef'.indexOf(NaN)", "x = 3"); fold("x = 'abcundefineddef'.indexOf(undefined)", "x = 3"); fold("x = 'abcnulldef'.indexOf(null)", "x = 3"); fold("x = 'abctruedef'.indexOf(true)", "x = 3"); // The following test case fails with JSC_PARSE_ERROR. Hence omitted. // foldSame("x = 1.indexOf('bcd');"); foldSame("x = NaN.indexOf('bcd')"); foldSame("x = undefined.indexOf('bcd')"); foldSame("x = null.indexOf('bcd')"); foldSame("x = true.indexOf('bcd')"); foldSame("x = false.indexOf('bcd')"); // Avoid dealing with regex or other types. foldSame("x = 'abcdef'.indexOf(/b./)"); foldSame("x = 'abcdef'.indexOf({a:2})"); foldSame("x = 'abcdef'.indexOf([1,2])"); } public void testStringJoinAddSparse() { fold("x = [,,'a'].join(',')", "x = ',,a'"); } public void testNoStringJoin() { foldSame("x = [].join(',',2)"); foldSame("x = [].join(f)"); } public void testStringJoinAdd() { fold("x = ['a', 'b', 'c'].join('')", "x = \"abc\""); fold("x = [].join(',')", "x = \"\""); fold("x = ['a'].join(',')", "x = \"a\""); fold("x = ['a', 'b', 'c'].join(',')", "x = \"a,b,c\""); fold("x = ['a', foo, 'b', 'c'].join(',')", "x = [\"a\",foo,\"b,c\"].join()"); fold("x = [foo, 'a', 'b', 'c'].join(',')", "x = [foo,\"a,b,c\"].join()"); fold("x = ['a', 'b', 'c', foo].join(',')", "x = [\"a,b,c\",foo].join()"); // Works with numbers fold("x = ['a=', 5].join('')", "x = \"a=5\""); fold("x = ['a', '5'].join(7)", "x = \"a75\""); // Works on boolean fold("x = ['a=', false].join('')", "x = \"a=false\""); fold("x = ['a', '5'].join(true)", "x = \"atrue5\""); fold("x = ['a', '5'].join(false)", "x = \"afalse5\""); // Only optimize if it's a size win. fold("x = ['a', '5', 'c'].join('a very very very long chain')", "x = [\"a\",\"5\",\"c\"].join(\"a very very very long chain\")"); // TODO(user): Its possible to fold this better. foldSame("x = ['', foo].join('-')"); foldSame("x = ['', foo, ''].join()"); fold("x = ['', '', foo, ''].join(',')", "x = [',', foo, ''].join()"); fold("x = ['', '', foo, '', ''].join(',')", "x = [',', foo, ','].join()"); fold("x = ['', '', foo, '', '', bar].join(',')", "x = [',', foo, ',', bar].join()"); fold("x = [1,2,3].join('abcdef')", "x = '1abcdef2abcdef3'"); fold("x = [1,2].join()", "x = '1,2'"); fold("x = [null,undefined,''].join(',')", "x = ',,'"); fold("x = [null,undefined,0].join(',')", "x = ',,0'"); // This can be folded but we don't currently. foldSame("x = [[1,2],[3,4]].join()"); // would like: "x = '1,2,3,4'" } public void testStringJoinAdd_b1992789() { fold("x = ['a'].join('')", "x = \"a\""); fold("x = [foo()].join('')", "x = '' + foo()"); fold("[foo()].join('')", "'' + foo()"); } public void testFoldStringSubstr() { fold("x = 'abcde'.substr(0,2)", "x = 'ab'"); fold("x = 'abcde'.substr(1,2)", "x = 'bc'"); fold("x = 'abcde'['substr'](1,3)", "x = 'bcd'"); fold("x = 'abcde'.substr(2)", "x = 'cde'"); // we should be leaving negative indexes alone for now foldSame("x = 'abcde'.substr(-1)"); foldSame("x = 'abcde'.substr(1, -2)"); foldSame("x = 'abcde'.substr(1, 2, 3)"); foldSame("x = 'a'.substr(0, 2)"); } public void testFoldStringSubstring() { fold("x = 'abcde'.substring(0,2)", "x = 'ab'"); fold("x = 'abcde'.substring(1,2)", "x = 'b'"); fold("x = 'abcde'['substring'](1,3)", "x = 'bc'"); fold("x = 'abcde'.substring(2)", "x = 'cde'"); // we should be leaving negative indexes alone for now foldSame("x = 'abcde'.substring(-1)"); foldSame("x = 'abcde'.substring(1, -2)"); foldSame("x = 'abcde'.substring(1, 2, 3)"); foldSame("x = 'a'.substring(0, 2)"); } public void testFoldStringCharAt() { fold("x = 'abcde'.charAt(0)", "x = 'a'"); fold("x = 'abcde'.charAt(1)", "x = 'b'"); fold("x = 'abcde'.charAt(2)", "x = 'c'"); fold("x = 'abcde'.charAt(3)", "x = 'd'"); fold("x = 'abcde'.charAt(4)", "x = 'e'"); foldSame("x = 'abcde'.charAt(5)"); // or x = '' foldSame("x = 'abcde'.charAt(-1)"); // or x = '' foldSame("x = 'abcde'.charAt(y)"); foldSame("x = 'abcde'.charAt()"); // or x = 'a' foldSame("x = 'abcde'.charAt(0, ++z)"); // or (++z, 'a') foldSame("x = 'abcde'.charAt(null)"); // or x = 'a' foldSame("x = 'abcde'.charAt(true)"); // or x = 'b' fold("x = '\\ud834\udd1e'.charAt(0)", "x = '\\ud834'"); fold("x = '\\ud834\udd1e'.charAt(1)", "x = '\\udd1e'"); } public void testFoldStringCharCodeAt() { fold("x = 'abcde'.charCodeAt(0)", "x = 97"); fold("x = 'abcde'.charCodeAt(1)", "x = 98"); fold("x = 'abcde'.charCodeAt(2)", "x = 99"); fold("x = 'abcde'.charCodeAt(3)", "x = 100"); fold("x = 'abcde'.charCodeAt(4)", "x = 101"); foldSame("x = 'abcde'.charCodeAt(5)"); // or x = (0/0) foldSame("x = 'abcde'.charCodeAt(-1)"); // or x = (0/0) foldSame("x = 'abcde'.charCodeAt(y)"); foldSame("x = 'abcde'.charCodeAt()"); // or x = 97 foldSame("x = 'abcde'.charCodeAt(0, ++z)"); // or (++z, 97) foldSame("x = 'abcde'.charCodeAt(null)"); // or x = 97 foldSame("x = 'abcde'.charCodeAt(true)"); // or x = 98 fold("x = '\\ud834\udd1e'.charCodeAt(0)", "x = 55348"); fold("x = '\\ud834\udd1e'.charCodeAt(1)", "x = 56606"); } public void testFoldStringSplit() { late = false; fold("x = 'abcde'.split('foo')", "x = ['abcde']"); fold("x = 'abcde'.split()", "x = ['abcde']"); fold("x = 'abcde'.split(null)", "x = ['abcde']"); fold("x = 'a b c d e'.split(' ')", "x = ['a','b','c','d','e']"); fold("x = 'a b c d e'.split(' ', 0)", "x = []"); fold("x = 'abcde'.split('cd')", "x = ['ab','e']"); fold("x = 'a b c d e'.split(' ', 1)", "x = ['a']"); fold("x = 'a b c d e'.split(' ', 3)", "x = ['a','b','c']"); fold("x = 'a b c d e'.split(null, 1)", "x = ['a b c d e']"); fold("x = 'aaaaa'.split('a')", "x = ['', '', '', '', '', '']"); fold("x = 'xyx'.split('x')", "x = ['', 'y', '']"); // Empty separator fold("x = 'abcde'.split('')", "x = ['a','b','c','d','e']"); fold("x = 'abcde'.split('', 3)", "x = ['a','b','c']"); // Empty separator AND empty string fold("x = ''.split('')", "x = []"); // Separator equals string fold("x = 'aaa'.split('aaa')", "x = ['','']"); fold("x = ' '.split(' ')", "x = ['','']"); foldSame("x = 'abcde'.split(/ /)"); foldSame("x = 'abcde'.split(' ', -1)"); late = true; foldSame("x = 'a b c d e'.split(' ')"); } public void testJoinBug() { fold("var x = [].join();", "var x = '';"); fold("var x = [x].join();", "var x = '' + x;"); foldSame("var x = [x,y].join();"); foldSame("var x = [x,y,z].join();"); foldSame("shape['matrix'] = [\n" + " Number(headingCos2).toFixed(4),\n" + " Number(-headingSin2).toFixed(4),\n" + " Number(headingSin2 * yScale).toFixed(4),\n" + " Number(headingCos2 * yScale).toFixed(4),\n" + " 0,\n" + " 0\n" + " ].join()"); } public void testToUpper() { fold("'a'.toUpperCase()", "'A'"); fold("'A'.toUpperCase()", "'A'"); fold("'aBcDe'.toUpperCase()", "'ABCDE'"); } public void testToLower() { fold("'A'.toLowerCase()", "'a'"); fold("'a'.toLowerCase()", "'a'"); fold("'aBcDe'.toLowerCase()", "'abcde'"); } public void testFoldParseNumbers() { enableNormalize(); enableEcmaScript5(true); fold("x = parseInt('123')", "x = 123"); fold("x = parseInt(' 123')", "x = 123"); fold("x = parseInt('123', 10)", "x = 123"); fold("x = parseInt('0xA')", "x = 10"); fold("x = parseInt('0xA', 16)", "x = 10"); fold("x = parseInt('07', 8)", "x = 7"); fold("x = parseInt('08')", "x = 8"); fold("x = parseInt('0')", "x = 0"); fold("x = parseFloat('0')", "x = 0"); fold("x = parseFloat('1.23')", "x = 1.23"); fold("x = parseFloat('1.2300')", "x = 1.23"); fold("x = parseFloat(' 0.3333')", "x = 0.3333"); fold("x = parseFloat('0100')", "x = 100"); fold("x = parseFloat('0100.000')", "x = 100"); //Mozilla Dev Center test cases fold("x = parseInt(' 0xF', 16)", "x = 15"); fold("x = parseInt(' F', 16)", "x = 15"); fold("x = parseInt('17', 8)", "x = 15"); fold("x = parseInt('015', 10)", "x = 15"); fold("x = parseInt('1111', 2)", "x = 15"); fold("x = parseInt('12', 13)", "x = 15"); fold("x = parseInt(021, 8)", "x = 15"); fold("x = parseInt(15.99, 10)", "x = 15"); fold("x = parseFloat('3.14')", "x = 3.14"); fold("x = parseFloat(3.14)", "x = 3.14"); //Valid calls - unable to fold foldSame("x = parseInt('FXX123', 16)"); foldSame("x = parseInt('15*3', 10)"); foldSame("x = parseInt('15e2', 10)"); foldSame("x = parseInt('15px', 10)"); foldSame("x = parseInt('-0x08')"); foldSame("x = parseInt('1', -1)"); foldSame("x = parseFloat('3.14more non-digit characters')"); foldSame("x = parseFloat('314e-2')"); foldSame("x = parseFloat('0.0314E+2')"); foldSame("x = parseFloat('3.333333333333333333333333')"); //Invalid calls foldSame("x = parseInt('0xa', 10)"); foldSame("x = parseInt('')"); enableEcmaScript5(false); foldSame("x = parseInt('08')"); } @Override protected int getNumRepetitions() { // Reduce this to 2 if we get better expression evaluators. return 2; } private void foldSame(String js) { testSame(js); } private void fold(String js, String expected) { test(js, expected); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SanityCheckTest.java0000644000175000017500000000652412115204405026743 0ustar apoapo/* * 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.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * @author nicksantos@google.com (Nick Santos) */ public class SanityCheckTest extends CompilerTestCase { private CompilerPass otherPass = null; public SanityCheckTest() { super("", false); } @Override public void setUp() { otherPass = null; } @Override protected int getNumRepetitions() { return 1; } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { otherPass.process(externs, root); (new SanityCheck(compiler)).process(externs, root); } }; } public void testUnnormalizeNodeTypes() throws Exception { otherPass = new CompilerPass() { @Override public void process(Node externs, Node root) { getLastCompiler().reportCodeChange(); root.getFirstChild().addChildToBack( new Node(Token.IF, new Node(Token.TRUE), new Node(Token.EMPTY))); } }; boolean exceptionCaught = false; try { test("var x = 3;", "var x=3;0;0"); } catch (IllegalStateException e) { assertEquals("Expected BLOCK but was EMPTY Reference node EMPTY", e.getMessage()); exceptionCaught = true; } assertTrue(exceptionCaught); } public void testUnnormalized() throws Exception { otherPass = new CompilerPass() { @Override public void process(Node externs, Node root) { getLastCompiler().setLifeCycleStage(LifeCycleStage.NORMALIZED); } }; boolean exceptionCaught = false; try { test("while(1){}", "while(1){}"); } catch (RuntimeException e) { assertTrue(e.getMessage().contains( "Normalize constraints violated:\nWHILE node")); exceptionCaught = true; } assertTrue(exceptionCaught); } public void testConstantAnnotationMismatch() throws Exception { otherPass = new CompilerPass() { @Override public void process(Node externs, Node root) { getLastCompiler().reportCodeChange(); Node name = Node.newString(Token.NAME, "x"); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); root.getFirstChild().addChildToBack(new Node(Token.EXPR_RESULT, name)); getLastCompiler().setLifeCycleStage(LifeCycleStage.NORMALIZED); } }; boolean exceptionCaught = false; try { test("var x;", "var x; x;"); } catch (RuntimeException e) { assertTrue(e.getMessage().contains( "The name x is not consistently annotated as constant.")); exceptionCaught = true; } assertTrue(exceptionCaught); } } ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveUnusedPrototypePropertiesTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RemoveUnusedPrototypePropertiesTes0000644000175000017500000004320412115204405032052 0ustar apoapo/* * Copyright 2006 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; /** * Tests for {@link RemoveUnusedPrototypeProperties}. * * @author nicksantos@google.com (Nick Santos) */ public class RemoveUnusedPrototypePropertiesTest extends CompilerTestCase { private static final String EXTERNS = "IFoo.prototype.bar; var mExtern; mExtern.bExtern; mExtern['cExtern'];"; private boolean canRemoveExterns = false; private boolean anchorUnusedVars = false; public RemoveUnusedPrototypePropertiesTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new RemoveUnusedPrototypeProperties(compiler, canRemoveExterns, anchorUnusedVars); } @Override public void setUp() { anchorUnusedVars = false; canRemoveExterns = false; } public void testAnalyzePrototypeProperties() { // Basic removal for prototype properties test("function e(){}" + "e.prototype.a = function(){};" + "e.prototype.b = function(){};" + "var x = new e; x.a()", "function e(){}" + "e.prototype.a = function(){};" + "var x = new e; x.a()"); // Basic removal for prototype replacement test("function e(){}" + "e.prototype = {a: function(){}, b: function(){}};" + "var x=new e; x.a()", "function e(){}" + "e.prototype = {a: function(){}};" + "var x = new e; x.a()"); // Unused properties that were referenced in the externs file should not be // removed test("function e(){}" + "e.prototype.a = function(){};" + "e.prototype.bExtern = function(){};" + "var x = new e;x.a()", "function e(){}" + "e.prototype.a = function(){};" + "e.prototype.bExtern = function(){};" + "var x = new e; x.a()"); test("function e(){}" + "e.prototype = {a: function(){}, bExtern: function(){}};" + "var x = new e; x.a()", "function e(){}" + "e.prototype = {a: function(){}, bExtern: function(){}};" + "var x = new e; x.a()"); } public void testAliasing1() { // Aliasing a property is not enough for it to count as used test("function e(){}" + "e.prototype.method1 = function(){};" + "e.prototype.method2 = function(){};" + // aliases "e.prototype.alias1 = e.prototype.method1;" + "e.prototype.alias2 = e.prototype.method2;" + "var x = new e; x.method1()", "function e(){}" + "e.prototype.method1 = function(){};" + "var x = new e; x.method1()"); // Using an alias should keep it test("function e(){}" + "e.prototype.method1 = function(){};" + "e.prototype.method2 = function(){};" + // aliases "e.prototype.alias1 = e.prototype.method1;" + "e.prototype.alias2 = e.prototype.method2;" + "var x=new e; x.alias1()", "function e(){}" + "e.prototype.method1 = function(){};" + "e.prototype.alias1 = e.prototype.method1;" + "var x = new e; x.alias1()"); } public void testAliasing2() { // Aliasing a property is not enough for it to count as used test("function e(){}" + "e.prototype.method1 = function(){};" + // aliases "e.prototype.alias1 = e.prototype.method1;" + "(new e).method1()", "function e(){}" + "e.prototype.method1 = function(){};" + "(new e).method1()"); // Using an alias should keep it test("function e(){}" + "e.prototype.method1 = function(){};" + // aliases "e.prototype.alias1 = e.prototype.method1;" + "(new e).alias1()", "function e(){}" + "e.prototype.method1 = function(){};" + "e.prototype.alias1 = e.prototype.method1;" + "(new e).alias1()"); } public void testAliasing3() { // Aliasing a property is not enough for it to count as used test("function e(){}" + "e.prototype.method1 = function(){};" + "e.prototype.method2 = function(){};" + // aliases "e.prototype['alias1'] = e.prototype.method1;" + "e.prototype['alias2'] = e.prototype.method2;", "function e(){}" + "e.prototype.method1=function(){};" + "e.prototype.method2=function(){};" + "e.prototype[\"alias1\"]=e.prototype.method1;" + "e.prototype[\"alias2\"]=e.prototype.method2;"); } public void testAliasing4() { // Aliasing a property is not enough for it to count as used test("function e(){}" + "e.prototype['alias1'] = e.prototype.method1 = function(){};" + "e.prototype['alias2'] = e.prototype.method2 = function(){};", "function e(){}" + "e.prototype[\"alias1\"]=e.prototype.method1=function(){};" + "e.prototype[\"alias2\"]=e.prototype.method2=function(){};"); } public void testAliasing5() { // An exported alias must preserved any referenced values in the // referenced function. test("function e(){}" + "e.prototype.method1 = function(){this.method2()};" + "e.prototype.method2 = function(){};" + // aliases "e.prototype['alias1'] = e.prototype.method1;", "function e(){}" + "e.prototype.method1=function(){this.method2()};" + "e.prototype.method2=function(){};" + "e.prototype[\"alias1\"]=e.prototype.method1;"); } public void testAliasing6() { // An exported alias must preserved any referenced values in the // referenced function. test("function e(){}" + "e.prototype.method1 = function(){this.method2()};" + "e.prototype.method2 = function(){};" + // aliases "window['alias1'] = e.prototype.method1;", "function e(){}" + "e.prototype.method1=function(){this.method2()};" + "e.prototype.method2=function(){};" + "window['alias1']=e.prototype.method1;"); } public void testAliasing7() { // An exported alias must preserved any referenced values in the // referenced function. testSame("function e(){}" + "e.prototype['alias1'] = e.prototype.method1 = " + "function(){this.method2()};" + "e.prototype.method2 = function(){};"); } public void testStatementRestriction() { test("function e(){}" + "var x = e.prototype.method1 = function(){};" + "var y = new e; x()", "function e(){}" + "var x = e.prototype.method1 = function(){};" + "var y = new e; x()"); } public void testExportedMethodsByNamingConvention() { String classAndItsMethodAliasedAsExtern = "function Foo() {}" + "Foo.prototype.method = function() {};" + // not removed "Foo.prototype.unused = function() {};" + // removed "var _externInstance = new Foo();" + "Foo.prototype._externMethod = Foo.prototype.method"; // aliased here String compiled = "function Foo(){}" + "Foo.prototype.method = function(){};" + "var _externInstance = new Foo;" + "Foo.prototype._externMethod = Foo.prototype.method"; test(classAndItsMethodAliasedAsExtern, compiled); } public void testMethodsFromExternsFileNotExported() { canRemoveExterns = true; String classAndItsMethodAliasedAsExtern = "function Foo() {}" + "Foo.prototype.bar_ = function() {};" + "Foo.prototype.unused = function() {};" + "var instance = new Foo;" + "Foo.prototype.bar = Foo.prototype.bar_"; String compiled = "function Foo(){}" + "var instance = new Foo;"; test(classAndItsMethodAliasedAsExtern, compiled); } public void testExportedMethodsByNamingConventionAlwaysExported() { canRemoveExterns = true; String classAndItsMethodAliasedAsExtern = "function Foo() {}" + "Foo.prototype.method = function() {};" + // not removed "Foo.prototype.unused = function() {};" + // removed "var _externInstance = new Foo();" + "Foo.prototype._externMethod = Foo.prototype.method"; // aliased here String compiled = "function Foo(){}" + "Foo.prototype.method = function(){};" + "var _externInstance = new Foo;" + "Foo.prototype._externMethod = Foo.prototype.method"; test(classAndItsMethodAliasedAsExtern, compiled); } public void testExternMethodsFromExternsFile() { String classAndItsMethodAliasedAsExtern = "function Foo() {}" + "Foo.prototype.bar_ = function() {};" + // not removed "Foo.prototype.unused = function() {};" + // removed "var instance = new Foo;" + "Foo.prototype.bar = Foo.prototype.bar_"; // aliased here String compiled = "function Foo(){}" + "Foo.prototype.bar_ = function(){};" + "var instance = new Foo;" + "Foo.prototype.bar = Foo.prototype.bar_"; test(classAndItsMethodAliasedAsExtern, compiled); } public void testPropertyReferenceGraph() { // test a prototype property graph that looks like so: // b -> a, c -> b, c -> a, d -> c, e -> a, e -> f String constructor = "function Foo() {}"; String defA = "Foo.prototype.a = function() { Foo.superClass_.a.call(this); };"; String defB = "Foo.prototype.b = function() { this.a(); };"; String defC = "Foo.prototype.c = function() { " + "Foo.superClass_.c.call(this); this.b(); this.a(); };"; String defD = "Foo.prototype.d = function() { this.c(); };"; String defE = "Foo.prototype.e = function() { this.a(); this.f(); };"; String defF = "Foo.prototype.f = function() { };"; String fullClassDef = constructor + defA + defB + defC + defD + defE + defF; // ensure that all prototypes are compiled out if none are used test(fullClassDef, ""); // make sure that the right prototypes are called for each use String callA = "(new Foo()).a();"; String callB = "(new Foo()).b();"; String callC = "(new Foo()).c();"; String callD = "(new Foo()).d();"; String callE = "(new Foo()).e();"; String callF = "(new Foo()).f();"; test(fullClassDef + callA, constructor + defA + callA); test(fullClassDef + callB, constructor + defA + defB + callB); test(fullClassDef + callC, constructor + defA + defB + defC + callC); test(fullClassDef + callD, constructor + defA + defB + defC + defD + callD); test(fullClassDef + callE, constructor + defA + defE + defF + callE); test(fullClassDef + callF, constructor + defF + callF); test(fullClassDef + callA + callC, constructor + defA + defB + defC + callA + callC); test(fullClassDef + callB + callC, constructor + defA + defB + defC + callB + callC); test(fullClassDef + callA + callB + callC, constructor + defA + defB + defC + callA + callB + callC); } public void testPropertiesDefinedWithGetElem() { testSame("function Foo() {} Foo.prototype['elem'] = function() {};"); testSame("function Foo() {} Foo.prototype[1 + 1] = function() {};"); } public void testNeverRemoveImplicitlyUsedProperties() { testSame("function Foo() {} " + "Foo.prototype.length = 3; " + "Foo.prototype.toString = function() { return 'Foo'; }; " + "Foo.prototype.valueOf = function() { return 'Foo'; }; "); } public void testPropertyDefinedInBranch() { test("function Foo() {} if (true) Foo.prototype.baz = function() {};", "if (true);"); test("function Foo() {} while (true) Foo.prototype.baz = function() {};", "while (true);"); test("function Foo() {} for (;;) Foo.prototype.baz = function() {};", "for (;;);"); test("function Foo() {} do Foo.prototype.baz = function() {}; while(true);", "do; while(true);"); } public void testUsingAnonymousObjectsToDefeatRemoval() { String constructor = "function Foo() {}"; String declaration = constructor + "Foo.prototype.baz = 3;"; test(declaration, ""); testSame(declaration + "var x = {}; x.baz = 5;"); testSame(declaration + "var x = {baz: 5};"); test(declaration + "var x = {'baz': 5};", "var x = {'baz': 5};"); } public void testGlobalFunctionsInGraph() { test( "var x = function() { (new Foo).baz(); };" + "var y = function() { x(); };" + "function Foo() {}" + "Foo.prototype.baz = function() { y(); };", ""); } public void testGlobalFunctionsInGraph2() { // In this example, Foo.prototype.baz is a global reference to // Foo, and Foo has a reference to baz. So everything stays in. // TODO(nicksantos): We should be able to make the graph more fine-grained // here. Instead of Foo.prototype.bar creating a global reference to Foo, // it should create a reference from .bar to Foo. That will let us // compile this away to nothing. testSame( "var x = function() { (new Foo).baz(); };" + "var y = function() { x(); };" + "function Foo() { this.baz(); }" + "Foo.prototype.baz = function() { y(); };"); } public void testGlobalFunctionsInGraph3() { test( "var x = function() { (new Foo).baz(); };" + "var y = function() { x(); };" + "function Foo() { this.baz(); }" + "Foo.prototype.baz = function() { x(); };", "var x = function() { (new Foo).baz(); };" + "function Foo() { this.baz(); }" + "Foo.prototype.baz = function() { x(); };"); } public void testGlobalFunctionsInGraph4() { test( "var x = function() { (new Foo).baz(); };" + "var y = function() { x(); };" + "function Foo() { Foo.prototype.baz = function() { y(); }; }", ""); } public void testGlobalFunctionsInGraph5() { test( "function Foo() {}" + "Foo.prototype.methodA = function() {};" + "function x() { (new Foo).methodA(); }" + "Foo.prototype.methodB = function() { x(); };", ""); anchorUnusedVars = true; test( "function Foo() {}" + "Foo.prototype.methodA = function() {};" + "function x() { (new Foo).methodA(); }" + "Foo.prototype.methodB = function() { x(); };", "function Foo() {}" + "Foo.prototype.methodA = function() {};" + "function x() { (new Foo).methodA(); }"); } public void testGlobalFunctionsInGraph6() { testSame( "function Foo() {}" + "Foo.prototype.methodA = function() {};" + "function x() { (new Foo).methodA(); }" + "Foo.prototype.methodB = function() { x(); };" + "(new Foo).methodB();"); } public void testGlobalFunctionsInGraph7() { testSame( "function Foo() {}" + "Foo.prototype.methodA = function() {};" + "this.methodA();"); } public void testGetterBaseline() { anchorUnusedVars = true; test( "function Foo() {}" + "Foo.prototype = { " + " methodA: function() {}," + " methodB: function() { x(); }" + "};" + "function x() { (new Foo).methodA(); }", "function Foo() {}" + "Foo.prototype = { " + " methodA: function() {}" + "};" + "function x() { (new Foo).methodA(); }"); } public void testGetter1() { test( "function Foo() {}" + "Foo.prototype = { " + " get methodA() {}," + " get methodB() { x(); }" + "};" + "function x() { (new Foo).methodA; }", "function Foo() {}" + "Foo.prototype = {};"); anchorUnusedVars = true; test( "function Foo() {}" + "Foo.prototype = { " + " get methodA() {}," + " get methodB() { x(); }" + "};" + "function x() { (new Foo).methodA; }", "function Foo() {}" + "Foo.prototype = { " + " get methodA() {}" + "};" + "function x() { (new Foo).methodA; }"); } public void testGetter2() { anchorUnusedVars = true; test( "function Foo() {}" + "Foo.prototype = { " + " get methodA() {}," + " set methodA(a) {}," + " get methodB() { x(); }," + " set methodB(a) { x(); }" + "};" + "function x() { (new Foo).methodA; }", "function Foo() {}" + "Foo.prototype = { " + " get methodA() {}," + " set methodA(a) {}" + "};" + "function x() { (new Foo).methodA; }"); } public void testHook1() throws Exception { test( "/** @constructor */ function Foo() {}" + "Foo.prototype.method1 = Math.random() ?" + " function() { this.method2(); } : function() { this.method3(); };" + "Foo.prototype.method2 = function() {};" + "Foo.prototype.method3 = function() {};", ""); } public void testHook2() throws Exception { testSame( "/** @constructor */ function Foo() {}" + "Foo.prototype.method1 = Math.random() ?" + " function() { this.method2(); } : function() { this.method3(); };" + "Foo.prototype.method2 = function() {};" + "Foo.prototype.method3 = function() {};" + "(new Foo()).method1();"); } } ././@LongLink0000644000000000000000000000015600000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SemanticReverseAbstractInterpreterTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SemanticReverseAbstractInterpreter0000644000175000017500000005057012115204405031765 0ustar apoapo/* * Copyright 2007 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.Sets; import com.google.javascript.jscomp.type.FlowScope; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import java.util.Arrays; import java.util.Collection; public class SemanticReverseAbstractInterpreterTest extends CompilerTypeTestCase { private CodingConvention codingConvention = new GoogleCodingConvention(); private ReverseAbstractInterpreter interpreter; private Scope functionScope; @Override protected void setUp() throws Exception { super.setUp(); interpreter = new SemanticReverseAbstractInterpreter( codingConvention, registry); } public FlowScope newScope() { Scope globalScope = Scope.createGlobalScope(new Node(Token.EMPTY)); functionScope = new Scope(globalScope, new Node(Token.EMPTY)); return LinkedFlowScope.createEntryLattice(functionScope); } /** * Tests reverse interpretation of a NAME expression. */ public void testNameCondition() throws Exception { FlowScope blind = newScope(); Node condition = createVar(blind, "a", createNullableType(STRING_TYPE)); // true outcome. FlowScope informedTrue = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, true); assertTypeEquals(STRING_TYPE, getVarType(informedTrue, "a")); // false outcome. FlowScope informedFalse = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, false); assertTypeEquals(createNullableType(STRING_TYPE), getVarType(informedFalse, "a")); } /** * Tests reverse interpretation of a NOT(NAME) expression. */ public void testNegatedNameCondition() throws Exception { FlowScope blind = newScope(); Node a = createVar(blind, "a", createNullableType(STRING_TYPE)); Node condition = new Node(Token.NOT); condition.addChildToBack(a); // true outcome. FlowScope informedTrue = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, true); assertTypeEquals(createNullableType(STRING_TYPE), getVarType(informedTrue, "a")); // false outcome. FlowScope informedFalse = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, false); assertTypeEquals(STRING_TYPE, getVarType(informedFalse, "a")); } /** * Tests reverse interpretation of a ASSIGN expression. */ @SuppressWarnings("unchecked") public void testAssignCondition1() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.ASSIGN, createVar(blind, "a", createNullableType(OBJECT_TYPE)), createVar(blind, "b", createNullableType(OBJECT_TYPE)), Sets.newHashSet( new TypedName("a", OBJECT_TYPE), new TypedName("b", OBJECT_TYPE)), Sets.newHashSet( new TypedName("a", NULL_TYPE), new TypedName("b", NULL_TYPE))); } /** * Tests reverse interpretation of a SHEQ(NAME, NUMBER) expression. */ @SuppressWarnings("unchecked") public void testSheqCondition1() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), createNumber(56), Sets.newHashSet(new TypedName("a", NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE)))); } /** * Tests reverse interpretation of a SHEQ(NUMBER, NAME) expression. */ @SuppressWarnings("unchecked") public void testSheqCondition2() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createNumber(56), createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE)))); } /** * Tests reverse interpretation of a SHEQ(NAME, NAME) expression. */ @SuppressWarnings("unchecked") public void testSheqCondition3() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createVar(blind, "b", createUnionType(STRING_TYPE, BOOLEAN_TYPE)), createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", STRING_TYPE), new TypedName("b", STRING_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE)), new TypedName("b", createUnionType(STRING_TYPE, BOOLEAN_TYPE)))); } @SuppressWarnings("unchecked") public void testSheqCondition4() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(VOID_TYPE)), Sets.newHashSet(new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE)), Sets.newHashSet(new TypedName("a", STRING_TYPE), new TypedName("b", VOID_TYPE))); } @SuppressWarnings("unchecked") public void testSheqCondition5() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createVar(blind, "a", createUnionType(NULL_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(VOID_TYPE)), Sets.newHashSet(new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE)), Sets.newHashSet(new TypedName("a", NULL_TYPE), new TypedName("b", VOID_TYPE))); } @SuppressWarnings("unchecked") public void testSheqCondition6() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHEQ, createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(NUMBER_TYPE, VOID_TYPE)), Sets.newHashSet( new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE)), Sets.newHashSet( new TypedName("a", createUnionType(STRING_TYPE, VOID_TYPE)), new TypedName("b", createUnionType(NUMBER_TYPE, VOID_TYPE)))); } /** * Tests reverse interpretation of a SHNE(NAME, NUMBER) expression. */ @SuppressWarnings("unchecked") public void testShneCondition1() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), createNumber(56), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE))), Sets.newHashSet(new TypedName("a", NUMBER_TYPE))); } /** * Tests reverse interpretation of a SHNE(NUMBER, NAME) expression. */ @SuppressWarnings("unchecked") public void testShneCondition2() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createNumber(56), createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE))), Sets.newHashSet(new TypedName("a", NUMBER_TYPE))); } /** * Tests reverse interpretation of a SHNE(NAME, NAME) expression. */ @SuppressWarnings("unchecked") public void testShneCondition3() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createVar(blind, "b", createUnionType(STRING_TYPE, BOOLEAN_TYPE)), createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE)), new TypedName("b", createUnionType(STRING_TYPE, BOOLEAN_TYPE))), Sets.newHashSet(new TypedName("a", STRING_TYPE), new TypedName("b", STRING_TYPE))); } @SuppressWarnings("unchecked") public void testShneCondition4() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(VOID_TYPE)), Sets.newHashSet(new TypedName("a", STRING_TYPE), new TypedName("b", VOID_TYPE)), Sets.newHashSet(new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE))); } @SuppressWarnings("unchecked") public void testShneCondition5() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createVar(blind, "a", createUnionType(NULL_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(NULL_TYPE)), Sets.newHashSet(new TypedName("a", VOID_TYPE), new TypedName("b", NULL_TYPE)), Sets.newHashSet(new TypedName("a", NULL_TYPE), new TypedName("b", NULL_TYPE))); } @SuppressWarnings("unchecked") public void testShneCondition6() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.SHNE, createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(NUMBER_TYPE, VOID_TYPE)), Sets.newHashSet( new TypedName("a", createUnionType(STRING_TYPE, VOID_TYPE)), new TypedName("b", createUnionType(NUMBER_TYPE, VOID_TYPE))), Sets.newHashSet( new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE))); } /** * Tests reverse interpretation of a EQ(NAME, NULL) expression. */ @SuppressWarnings("unchecked") public void testEqCondition1() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.EQ, createVar(blind, "a", createUnionType(BOOLEAN_TYPE, VOID_TYPE)), createNull(), Sets.newHashSet(new TypedName("a", VOID_TYPE)), Sets.newHashSet(new TypedName("a", BOOLEAN_TYPE))); } /** * Tests reverse interpretation of a NE(NULL, NAME) expression. */ @SuppressWarnings("unchecked") public void testEqCondition2() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.NE, createNull(), createVar(blind, "a", createUnionType(BOOLEAN_TYPE, VOID_TYPE)), Sets.newHashSet(new TypedName("a", BOOLEAN_TYPE)), Sets.newHashSet(new TypedName("a", VOID_TYPE))); } /** * Tests reverse interpretation of a EQ(NAME, NULL) expression. */ @SuppressWarnings("unchecked") public void testEqCondition3() throws Exception { FlowScope blind = newScope(); // (number,undefined,null) JSType nullableOptionalNumber = createUnionType(NULL_TYPE, VOID_TYPE, NUMBER_TYPE); // (null,undefined) JSType nullUndefined = createUnionType(VOID_TYPE, NULL_TYPE); testBinop(blind, Token.EQ, createVar(blind, "a", nullableOptionalNumber), createNull(), Sets.newHashSet(new TypedName("a", nullUndefined)), Sets.newHashSet(new TypedName("a", NUMBER_TYPE))); } /** * Tests reverse interpretation of two undefineds. */ @SuppressWarnings("unchecked") public void testEqCondition4() throws Exception { FlowScope blind = newScope(); testBinop(blind, Token.EQ, createVar(blind, "a", VOID_TYPE), createVar(blind, "b", VOID_TYPE), Sets.newHashSet( new TypedName("a", VOID_TYPE), new TypedName("b", VOID_TYPE)), Sets.newHashSet( new TypedName("a", NO_TYPE), new TypedName("b", NO_TYPE))); } /** * Tests reverse interpretation of a COMPARE(NAME, NUMBER) expression, * where COMPARE can be LE, LT, GE or GT. */ @SuppressWarnings("unchecked") public void testInequalitiesCondition1() { for (int op : Arrays.asList(Token.LT, Token.GT, Token.LE, Token.GE)) { FlowScope blind = newScope(); testBinop(blind, op, createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), createNumber(8), Sets.newHashSet( new TypedName("a", STRING_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, VOID_TYPE)))); } } /** * Tests reverse interpretation of a COMPARE(NAME, NAME) expression, * where COMPARE can be LE, LT, GE or GT. */ @SuppressWarnings("unchecked") public void testInequalitiesCondition2() { for (int op : Arrays.asList(Token.LT, Token.GT, Token.LE, Token.GE)) { FlowScope blind = newScope(); testBinop(blind, op, createVar(blind, "a", createUnionType(STRING_TYPE, NUMBER_TYPE, VOID_TYPE)), createVar(blind, "b", createUnionType(NUMBER_TYPE, NULL_TYPE)), Sets.newHashSet( new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE)), new TypedName("b", createUnionType(NUMBER_TYPE, NULL_TYPE))), Sets.newHashSet( new TypedName("a", createUnionType(STRING_TYPE, NUMBER_TYPE, VOID_TYPE)), new TypedName("b", createUnionType(NUMBER_TYPE, NULL_TYPE)))); } } /** * Tests reverse interpretation of a COMPARE(NUMBER-untyped, NAME) expression, * where COMPARE can be LE, LT, GE or GT. */ @SuppressWarnings("unchecked") public void testInequalitiesCondition3() { for (int op : Arrays.asList(Token.LT, Token.GT, Token.LE, Token.GE)) { FlowScope blind = newScope(); testBinop(blind, op, createUntypedNumber(8), createVar(blind, "a", createUnionType(STRING_TYPE, VOID_TYPE)), Sets.newHashSet( new TypedName("a", STRING_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(STRING_TYPE, VOID_TYPE)))); } } @SuppressWarnings("unchecked") public void testAnd() { FlowScope blind = newScope(); testBinop(blind, Token.AND, createVar(blind, "b", createUnionType(STRING_TYPE, NULL_TYPE)), createVar(blind, "a", createUnionType(NUMBER_TYPE, VOID_TYPE)), Sets.newHashSet(new TypedName("a", NUMBER_TYPE), new TypedName("b", STRING_TYPE)), Sets.newHashSet(new TypedName("a", createUnionType(NUMBER_TYPE, VOID_TYPE)), new TypedName("b", createUnionType(STRING_TYPE, NULL_TYPE)))); } @SuppressWarnings("unchecked") public void testTypeof1() { FlowScope blind = newScope(); testBinop(blind, Token.EQ, new Node(Token.TYPEOF, createVar(blind, "a", OBJECT_TYPE)), Node.newString("function"), Sets.newHashSet( new TypedName("a", U2U_CONSTRUCTOR_TYPE)), Sets.newHashSet( new TypedName("a", OBJECT_TYPE))); } @SuppressWarnings("unchecked") public void testTypeof2() { FlowScope blind = newScope(); testBinop(blind, Token.EQ, new Node(Token.TYPEOF, createVar(blind, "a", ALL_TYPE)), Node.newString("function"), Sets.newHashSet( new TypedName("a", U2U_CONSTRUCTOR_TYPE)), Sets.newHashSet( new TypedName("a", ALL_TYPE))); } @SuppressWarnings("unchecked") public void testTypeof3() { FlowScope blind = newScope(); testBinop(blind, Token.EQ, new Node(Token.TYPEOF, createVar( blind, "a", OBJECT_NUMBER_STRING_BOOLEAN)), Node.newString("function"), Sets.newHashSet( new TypedName("a", U2U_CONSTRUCTOR_TYPE)), Sets.newHashSet( new TypedName("a", OBJECT_NUMBER_STRING_BOOLEAN))); } @SuppressWarnings("unchecked") public void testTypeof4() { FlowScope blind = newScope(); testBinop(blind, Token.EQ, new Node(Token.TYPEOF, createVar( blind, "a", createUnionType( U2U_CONSTRUCTOR_TYPE,NUMBER_STRING_BOOLEAN))), Node.newString("function"), Sets.newHashSet( new TypedName("a", U2U_CONSTRUCTOR_TYPE)), Sets.newHashSet( new TypedName("a", NUMBER_STRING_BOOLEAN))); } @SuppressWarnings("unchecked") public void testInstanceOf() { FlowScope blind = newScope(); testBinop(blind, Token.INSTANCEOF, createVar(blind, "x", UNKNOWN_TYPE), createVar(blind, "s", STRING_OBJECT_FUNCTION_TYPE), Sets.newHashSet( new TypedName("x", STRING_OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE)), Sets.newHashSet( new TypedName("s", STRING_OBJECT_FUNCTION_TYPE))); } @SuppressWarnings("unchecked") public void testInstanceOf2() { FlowScope blind = newScope(); testBinop(blind, Token.INSTANCEOF, createVar(blind, "x", createUnionType(STRING_OBJECT_TYPE, NUMBER_OBJECT_TYPE)), createVar(blind, "s", STRING_OBJECT_FUNCTION_TYPE), Sets.newHashSet( new TypedName("x", STRING_OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE)), Sets.newHashSet( new TypedName("x", NUMBER_OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE))); } @SuppressWarnings("unchecked") public void testInstanceOf3() { FlowScope blind = newScope(); testBinop(blind, Token.INSTANCEOF, createVar(blind, "x", OBJECT_TYPE), createVar(blind, "s", STRING_OBJECT_FUNCTION_TYPE), Sets.newHashSet( new TypedName("x", STRING_OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE)), Sets.newHashSet( new TypedName("x", OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE))); } @SuppressWarnings("unchecked") public void testInstanceOf4() { FlowScope blind = newScope(); testBinop(blind, Token.INSTANCEOF, createVar(blind, "x", ALL_TYPE), createVar(blind, "s", STRING_OBJECT_FUNCTION_TYPE), Sets.newHashSet( new TypedName("x", STRING_OBJECT_TYPE), new TypedName("s", STRING_OBJECT_FUNCTION_TYPE)), Sets.newHashSet( new TypedName("s", STRING_OBJECT_FUNCTION_TYPE))); } private void testBinop(FlowScope blind, int binop, Node left, Node right, Collection trueOutcome, Collection falseOutcome) { Node condition = new Node(binop); condition.addChildToBack(left); condition.addChildToBack(right); // true outcome. FlowScope informedTrue = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, true); for (TypedName p : trueOutcome) { assertTypeEquals(p.name, p.type, getVarType(informedTrue, p.name)); } // false outcome. FlowScope informedFalse = interpreter. getPreciserScopeKnowingConditionOutcome(condition, blind, false); for (TypedName p : falseOutcome) { assertTypeEquals(p.type, getVarType(informedFalse, p.name)); } } private Node createNull() { Node n = new Node(Token.NULL); n.setJSType(NULL_TYPE); return n; } private Node createNumber(int n) { Node number = createUntypedNumber(n); number.setJSType(NUMBER_TYPE); return number; } private Node createUntypedNumber(int n) { return Node.newNumber(n); } private JSType getVarType(FlowScope scope, String name) { return scope.getSlot(name).getType(); } private Node createVar(FlowScope scope, String name, JSType type) { Node n = Node.newString(Token.NAME, name); functionScope.declare(name, n, null, null); ((LinkedFlowScope) scope).inferSlotType(name, type); n.setJSType(type); return n; } private static class TypedName { private final String name; private final JSType type; private TypedName(String name, JSType type) { this.name = name; this.type = type; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RescopeGlobalSymbolsTest.java0000644000175000017500000001453512115204405030631 0ustar apoapo/* * Copyright 2011 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; /** * Unit tests for {@link RescopeGlobalSymbols} * */ public class RescopeGlobalSymbolsTest extends CompilerTestCase { private final String namespace = "_"; public RescopeGlobalSymbolsTest() { } @Override protected CompilerPass getProcessor(Compiler compiler) { return new RescopeGlobalSymbols(compiler, namespace, false); } @Override protected int getNumRepetitions() { return 1; } public void testVarDeclarations() { test("var a = 1;", "_.a = 1;"); test("var a = 1, b = 2, c = 3;", "_.a = 1; _.b = 2; _.c = 3;"); test("var a = 'str', b = 1, c = { foo: 'bar' }, d = function() {};", "_.a = 'str'; _.b = 1; _.c = { foo: 'bar' }; _.d = function() {};"); test("if(1){var x = 1;}", "if(1){_.x = 1;}"); test("var x;", ""); test("var a, b = 1;", "_.b = 1"); } public void testForLoops() { test("for (var i = 0; i < 1000; i++);", "for (_.i = 0; _.i < 1000; _.i++);"); test("for (var i = 0, c = 2; i < 1000; i++);", "for (_.i = 0, _.c = 2; _.i < 1000; _.i++);"); test("for (var i = 0, c = 2, d = 3; i < 1000; i++);", "for (_.i = 0, _.c = 2, _.d = 3; _.i < 1000; _.i++);"); test("for (var i = 0, c = 2, d = 3, e = 4; i < 1000; i++);", "for (_.i = 0, _.c = 2, _.d = 3, _.e = 4; _.i < 1000; _.i++);"); test("for (var i = 0; i < 1000;)i++;", "for (_.i = 0; _.i < 1000;)_.i++;"); test("for (var i = 0,b; i < 1000;)i++;b++", "for (_.i = 0,_.b; _.i < 1000;)_.i++;_.b++"); test("var o={};for (var i in o)i++;", "_.o={};for (_.i in _.o)_.i++;"); } public void testFunctionStatements() { test("function test(){}", "_.test=function (){}"); test("if(1)function test(){}", "if(1)_.test=function (){}"); new StringCompare().testFreeCallSemantics(); } public void testDeeperScopes() { test("var a = function(b){return b}", "_.a = function(b){return b}"); test("var a = function(b){var a; return a+b}", "_.a = function(b){var a; return a+b}"); test("var a = function(a,b){return a+b}", "_.a = function(a,b){return a+b}"); test("var x=1,a = function(b){var a; return a+b+x}", "_.x=1;_.a = function(b){var a; return a+b+_.x}"); test("var x=1,a = function(b){return function(){var a;return a+b+x}}", "_.x=1;_.a = function(b){return function(){var a; return a+b+_.x}}"); } public void testTryCatch() { test("try{var a = 1}catch(e){throw e}", "try{_.a = 1}catch(e){throw e}"); } public void testShadow() { test("var _ = 1; (function () { _ = 2 })()", "_._ = 1; (function () { _._ = 2 })()"); test("function foo() { var _ = {}; _.foo = foo; _.bar = 1; }", "_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1}"); test("function foo() { var _ = {}; _.foo = foo; _.bar = 1; " + "(function() { var _ = 0;})() }", "_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; " + "(function() { var _$ = 0;})() }"); test("function foo() { var _ = {}; _.foo = foo; _.bar = 1; " + "var _$ = 1; }", "_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; " + "var _$$ = 1; }"); test("function foo() { var _ = {}; _.foo = foo; _.bar = 1; " + "var _$ = 1; (function() { _ = _$ })() }", "_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; " + "var _$$ = 1; (function() { _$ = _$$ })() }"); test("function foo() { var _ = {}; _.foo = foo; _.bar = 1; " + "var _$ = 1, _$$ = 2 (function() { _ = _$ = _$$; " + "var _$, _$$$ })() }", "_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; " + "var _$$ = 1, _$$$ = 2 (function() { _$ = _$$ = _$$$; " + "var _$$, _$$$$ })() }"); test("function foo() { var _a = 1;}", "_.foo = function () { var _a = 1;}"); // We accept this unnecessary renaming as acceptable to simplify pattern // matching in the traversal. test("function foo() { var _$a = 1;}", "_.foo = function () { var _$a$ = 1;}"); } public void testExterns() { test("var document;", "document", "window.document", null, null); test("var document;", "document.getElementsByTagName('test')", "window.document.getElementsByTagName('test')", null, null); test("var document;", "window.document.getElementsByTagName('test')", "window.document.getElementsByTagName('test')", null, null); test("var document;document.getElementsByTagName", "document.getElementsByTagName('test')", "window.document.getElementsByTagName('test')", null, null); test("var document,navigator", "document.navigator;navigator", "window.document.navigator;window.navigator", null, null); test("var iframes", "function test() { iframes.resize(); }", "_.test = function() { window.iframes.resize(); }", null, null); test("var iframes", "var foo = iframes;", "_.foo = window.iframes;", null, null); // Special names. test("var arguments, window, eval;", "arguments;window;eval;", "arguments;window;eval;", null, null); // Actually not an extern. test("", "document", "window.document", null, null); } private class StringCompare extends CompilerTestCase { StringCompare() { super("", false); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new RescopeGlobalSymbols(compiler, namespace, false); } public void testFreeCallSemantics() { test("function x(){};var y=function(){var val=x()||{}}", "_.x=function(){};_.y=function(){var val=(0,_.x)()||{}}"); test("function x(){x()}", "_.x=function(){(0,_.x)()}"); } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PrepareAstTest.java0000644000175000017500000000500012115204405026570 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Tests for PrepareAst. * @author nicksantos@google.com (Nick Santos) */ public class PrepareAstTest extends CompilerTestCase { public PrepareAstTest() { super.enableLineNumberCheck(true); } @Override public CompilerPass getProcessor(Compiler compiler) { return null; // unused } public void testJsDocNormalization() throws Exception { Node root = parseExpectedJs( "var x = {/** @return {number} */ a: function() {}," + " c: /** @type {string} */ ('d')};"); Node objlit = root.getFirstChild().getFirstChild().getFirstChild() .getFirstChild(); assertEquals(Token.OBJECTLIT, objlit.getType()); Node firstKey = objlit.getFirstChild(); Node firstVal = firstKey.getFirstChild(); Node secondKey = firstKey.getNext(); Node secondVal = secondKey.getFirstChild(); assertNotNull(firstKey.getJSDocInfo()); assertNotNull(firstVal.getJSDocInfo()); assertNull(secondKey.getJSDocInfo()); assertNotNull(secondVal.getJSDocInfo()); } public void testFreeCall1() throws Exception { Node root = parseExpectedJs("foo();"); Node script = root.getFirstChild(); Preconditions.checkState(script.isScript()); Node firstExpr = script.getFirstChild(); Node call = firstExpr.getFirstChild(); Preconditions.checkState(call.isCall()); assertTrue(call.getBooleanProp(Node.FREE_CALL)); } public void testFreeCall2() throws Exception { Node root = parseExpectedJs("x.foo();"); Node script = root.getFirstChild(); Preconditions.checkState(script.isScript()); Node firstExpr = script.getFirstChild(); Node call = firstExpr.getFirstChild(); Preconditions.checkState(call.isCall()); assertFalse(call.getBooleanProp(Node.FREE_CALL)); } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CollapseVariableDeclarationsTest.j0000644000175000017500000000561212115204405031604 0ustar apoapo/* * Copyright 2006 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; /** * Tests for variable declaration collapsing. * */ public class CollapseVariableDeclarationsTest extends CompilerTestCase { public void testCollapsing() throws Exception { // Basic collapsing test("var a;var b;", "var a,b;"); // With initial values test("var a = 1;var b = 1;", "var a=1,b=1;"); // Already collapsed test("var a, b;", "var a,b;"); // Already collapsed with values test("var a = 1, b = 1;", "var a=1,b=1;"); // Some already collapsed test("var a;var b, c;var d;", "var a,b,c,d;"); // Some already collapsed with values test("var a = 1;var b = 2, c = 3;var d = 4;", "var a=1,b=2,c=3,d=4;"); } public void testIssue820() throws Exception { // Don't redeclare function parameters, this is incompatible with // strict mode. testSame("function f(a){ var b=1; a=2; var c; }"); } public void testIfElseVarDeclarations() throws Exception { testSame("if (x) var a = 1; else var b = 2;"); } public void testAggressiveRedeclaration() { test("var x = 2; foo(x); x = 3; var y = 2;", "var x = 2; foo(x); var x = 3, y = 2;"); test("var x = 2; foo(x); x = 3; x = 1; var y = 2;", "var x = 2; foo(x); var x = 3, x = 1, y = 2;"); test("var x = 2; foo(x); x = 3; x = 1; var y = 2; var z = 4", "var x = 2; foo(x); var x = 3, x = 1, y = 2, z = 4"); test("var x = 2; foo(x); x = 3; x = 1; var y = 2; var z = 4; x = 5", "var x = 2; foo(x); var x = 3, x = 1, y = 2, z = 4, x = 5"); } public void testAggressiveRedeclarationInFor() { testSame("for(var x = 1; x = 2; x = 3) {x = 4}"); testSame("for(var x = 1; y = 2; z = 3) {var a = 4}"); testSame("var x; for(x = 1; x = 2; z = 3) {x = 4}"); } public void testIssue397() { test("var x; var y = 3; x = 5;", "var x, y = 3; x = 5;"); testSame("var x; x = 5; var z = 7;"); test("var x; var y = 3; x = 5; var z = 7;", "var x, y = 3; x = 5; var z = 7;"); test("var a = 1; var x; var y = 3; x = 5;", "var a = 1, x, y = 3; x = 5;"); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CollapseVariableDeclarations(compiler); } } ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SimpleFunctionAliasAnalysisTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SimpleFunctionAliasAnalysisTest.ja0000644000175000017500000001363612115204405031626 0ustar apoapo/* * 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; } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineSimpleMethodsTest.java0000644000175000017500000002331012115204405030442 0ustar apoapo/* * Copyright 2007 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; public class InlineSimpleMethodsTest extends CompilerTestCase { public InlineSimpleMethodsTest() { super("", false); } @Override protected void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(true); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new InlineSimpleMethods(compiler); } /** * Helper for tests that expects definitions to remain unchanged, such * that {@code definitions+js} is converted to {@code definitions+expected}. */ private void testWithPrefix(String definitions, String js, String expected) { test(definitions + js, definitions + expected); } public void testSimpleInline1() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "var x=(new Foo).bar();var y=(new Foo).bar();", "var x=(new Foo).baz;var y=(new Foo).baz"); } public void testSimpleInline2() { testWithPrefix("function Foo(){}" + "Foo.prototype={bar:function(){return this.baz}};", "var x=(new Foo).bar();var y=(new Foo).bar();", "var x=(new Foo).baz;var y=(new Foo).baz"); } public void testSimpleGetterInline1() { // TODO(johnlenz): Support this case. testSame("function Foo(){}" + "Foo.prototype={get bar(){return this.baz}};" + "var x=(new Foo).bar;var y=(new Foo).bar"); // Verify we are not confusing calling the result of an ES5 getter // with call the getter. testSame("function Foo(){}" + "Foo.prototype={get bar(){return this.baz}};" + "var x=(new Foo).bar();var y=(new Foo).bar()"); } public void testSimpleSetterInline1() { // Verify 'get' and 'set' are not confused. testSame("function Foo(){}" + "Foo.prototype={set bar(a){return this.baz}};" + "var x=(new Foo).bar;var y=(new Foo).bar"); testSame("function Foo(){}" + "Foo.prototype={set bar(a){return this.baz}};" + "var x=(new Foo).bar();var y=(new Foo).bar()"); } public void testSelfInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "Foo.prototype.meth=function(){this.bar();}", "Foo.prototype.meth=function(){this.baz}"); } public void testCallWithArgs() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "var x=(new Foo).bar(3,new Foo)", "var x=(new Foo).bar(3,new Foo)"); } public void testCallWithConstArgs() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(a){return this.baz};", "var x=(new Foo).bar(3, 4)", "var x=(new Foo).baz"); } public void testNestedProperties() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz.ooka};", "(new Foo).bar()", "(new Foo).baz.ooka"); } public void testSkipComplexMethods() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};" + "Foo.prototype.condy=function(){return this.baz?this.baz:1};", "var x=(new Foo).argy()", "var x=(new Foo).argy()"); } public void testSkipConflictingMethods() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};" + "Foo.prototype.bar=function(){return this.bazz};", "var x=(new Foo).bar()", "var x=(new Foo).bar()"); } public void testSameNamesDifferentDefinitions() { testWithPrefix("function A(){}" + "A.prototype.g=function(){return this.a};" + "function B(){}" + "B.prototype.g=function(){return this.b};", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g();", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g()"); } public void testSameNamesSameDefinitions() { testWithPrefix("function A(){}" + "A.prototype.g=function(){return this.a};" + "function B(){}" + "B.prototype.g=function(){return this.a};", "var x=(new A).g();" + "var y=(new B).g();" + "var a=new A;" + "var ag=a.g();", "var x=(new A).a;" + "var y=(new B).a;" + "var a=new A;" + "var ag=a.a"); } public void testConfusingNames() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return this.baz};", "function bar(){var bar=function(){};bar()}", "function bar(){var bar=function(){};bar()}"); } public void testConstantInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return 3};", "var f=new Foo;var x=f.bar()", "var f=new Foo;var x=3"); } public void testConstantArrayInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return[3,4]};", "var f=new Foo;var x=f.bar()", "var f=new Foo;var x=[3,4]"); } public void testConstantInlineWithSideEffects() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){return 3};", "var x=(new Foo).bar()", "var x=(new Foo).bar()"); } public void testEmptyMethodInline() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(a){};", "var x=new Foo; x.bar();", "var x=new Foo"); } public void testEmptyMethodInlineWithSideEffects() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "(new Foo).bar();var y=new Foo;y.bar(new Foo)", "(new Foo).bar();var y=new Foo;y.bar(new Foo)"); } public void testEmptyMethodInlineInAssign1() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "var x=new Foo;var y=x.bar()", "var x=new Foo;var y=void 0"); } public void testEmptyMethodInlineInAssign2() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){};", "var x=new Foo;var y=x.bar().toString()", "var x=new Foo;var y=(void 0).toString()"); } public void testNormalMethod() { testWithPrefix("function Foo(){}" + "Foo.prototype.bar=function(){var x=1};", "var x=new Foo;x.bar()", "var x=new Foo;x.bar()"); } public void testNoInlineOfExternMethods1() { testSame("var external={};external.charAt;", "external.charAt()", (DiagnosticType) null); } public void testNoInlineOfExternMethods2() { testSame("var external={};external.charAt=function(){};", "external.charAt()", (DiagnosticType) null); } public void testNoInlineOfExternMethods3() { testSame("var external={};external.bar=function(){};", "function Foo(){}Foo.prototype.bar=function(){};(new Foo).bar()", (DiagnosticType) null); } public void testNoInlineOfDangerousProperty() { testSame("function Foo(){this.bar=3}" + "Foo.prototype.bar=function(){};" + "var x=new Foo;var y=x.bar()"); } // Don't warn about argument naming conventions (this is done in another pass) // opt_ parameters must not be followed by non-optional parameters. // var_args must be last public void testNoWarn() { testSame("function Foo(){}" + "Foo.prototype.bar=function(opt_a,b){var x=1};" + "var x=new Foo;x.bar()"); testSame("function Foo(){}" + "Foo.prototype.bar=function(var_args,b){var x=1};" + "var x=new Foo;x.bar()"); } public void testObjectLit() { testSame("Foo.prototype.bar=function(){return this.baz_};" + "var blah={bar:function(){}};" + "(new Foo).bar()"); } public void testObjectLit2() { testSame("var blah={bar:function(){}};" + "(new Foo).bar()"); } public void testObjectLitExtern() { String externs = "window.bridge={_sip:function(){}};"; testSame(externs, "window.bridge._sip()", null); } public void testExternFunction() { String externs = "function emptyFunction() {}"; testSame(externs, "function Foo(){this.empty=emptyFunction}" + "(new Foo).empty()", null); } public void testIssue2508576_1() { // Method defined by an extern should be left alone. String externs = "function alert(a) {}"; testSame(externs, "({a:alert,b:alert}).a(\"a\")", null); } public void testIssue2508576_2() { // Anonymous object definition with a side-effect should be left alone. testSame("({a:function(){},b:x()}).a(\"a\")"); } public void testIssue2508576_3() { // Anonymous object definition without side-effect should be removed. test("({a:function(){},b:alert}).a(\"a\")", ""); } public void testAnonymousGet() { // Anonymous object definition without side-effect should be removed. testSame("({get a(){return function(){}},b:alert}).a(\"a\")"); testSame("({get a(){},b:alert}).a(\"a\")"); testSame("({get a(){},b:alert}).a"); } public void testAnonymousSet() { // Anonymous object definition without side-effect should be removed. testSame("({set a(b){return function(){}},b:alert}).a(\"a\")"); testSame("({set a(b){},b:alert}).a(\"a\")"); testSame("({set a(b){},b:alert}).a"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/InlineVariablesTest.java0000644000175000017500000007300412115204405027602 0ustar apoapo/* * 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; /** * Verifies that valid candidates for inlining are inlined, but * that no dangerous inlining occurs. * * @author kushal@google.com (Kushal Dave) */ public class InlineVariablesTest extends CompilerTestCase { private boolean inlineAllStrings = false; private boolean inlineLocalsOnly = false; public InlineVariablesTest() { enableNormalize(); } @Override public void setUp() { super.enableLineNumberCheck(true); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new InlineVariables( compiler, (inlineLocalsOnly) ? InlineVariables.Mode.LOCALS_ONLY : InlineVariables.Mode.ALL, inlineAllStrings); } @Override public void tearDown() { inlineAllStrings = false; inlineLocalsOnly = false; } // Test respect for scopes and blocks public void testInlineGlobal() { test("var x = 1; var z = x;", "var z = 1;"); } public void testNoInlineExportedName() { testSame("var _x = 1; var z = _x;"); } public void testNoInlineExportedName2() { testSame("var f = function() {}; var _x = f;" + "var y = function() { _x(); }; var _y = f;"); } public void testDoNotInlineIncrement() { testSame("var x = 1; x++;"); } public void testDoNotInlineDecrement() { testSame("var x = 1; x--;"); } public void testDoNotInlineIntoLhsOfAssign() { testSame("var x = 1; x += 3;"); } public void testInlineIntoRhsOfAssign() { test("var x = 1; var y = x;", "var y = 1;"); } public void testInlineInFunction() { test("function baz() { var x = 1; var z = x; }", "function baz() { var z = 1; }"); } public void testInlineInFunction2() { test("function baz() { " + "var a = new obj();"+ "result = a;" + "}", "function baz() { " + "result = new obj()" + "}"); } public void testInlineInFunction3() { testSame( "function baz() { " + "var a = new obj();" + "(function(){a;})();" + "result = a;" + "}"); } public void testInlineInFunction4() { testSame( "function baz() { " + "var a = new obj();" + "foo.result = a;" + "}"); } public void testInlineInFunction5() { testSame( "function baz() { " + "var a = (foo = new obj());" + "foo.x();" + "result = a;" + "}"); } public void testInlineAcrossModules() { // TODO(kushal): Make decision about overlap with CrossModuleCodeMotion test(createModules("var a = 2;", "var b = a;"), new String[] { "", "var b = 2;" }); } public void testDoNotExitConditional1() { testSame("if (true) { var x = 1; } var z = x;"); } public void testDoNotExitConditional2() { testSame("if (true) var x = 1; var z = x;"); } public void testDoNotExitConditional3() { testSame("var x; if (true) x=1; var z = x;"); } public void testDoNotExitLoop() { testSame("while (z) { var x = 3; } var y = x;"); } public void testDoNotExitForLoop() { test("for (var i = 1; false; false) var z = i;", "for (;false;false) var z = 1;"); testSame("for (; false; false) var i = 1; var z = i;"); testSame("for (var i in {}); var z = i;"); } public void testDoNotEnterSubscope() { testSame( "var x = function() {" + " var self = this; " + " return function() { var y = self; };" + "}"); testSame( "var x = function() {" + " var y = [1]; " + " return function() { var z = y; };" + "}"); } public void testDoNotExitTry() { testSame("try { var x = y; } catch (e) {} var z = y; "); testSame("try { throw e; var x = 1; } catch (e) {} var z = x; "); } public void testDoNotEnterCatch() { testSame("try { } catch (e) { var z = e; } "); } public void testDoNotEnterFinally() { testSame("try { throw e; var x = 1; } catch (e) {} " + "finally { var z = x; } "); } public void testInsideIfConditional() { test("var a = foo(); if (a) { alert(3); }", "if (foo()) { alert(3); }"); test("var a; a = foo(); if (a) { alert(3); }", "if (foo()) { alert(3); }"); } public void testOnlyReadAtInitialization() { test("var a; a = foo();", "foo();"); test("var a; if (a = foo()) { alert(3); }", "if (foo()) { alert(3); }"); test("var a; switch (a = foo()) {}", "switch(foo()) {}"); test("var a; function f(){ return a = foo(); }", "function f(){ return foo(); }"); test("function f(){ var a; return a = foo(); }", "function f(){ return foo(); }"); test("var a; with (a = foo()) { alert(3); }", "with (foo()) { alert(3); }"); test("var a; b = (a = foo());", "b = foo();"); test("var a; while(a = foo()) { alert(3); }", "while(foo()) { alert(3); }"); test("var a; for(;a = foo();) { alert(3); }", "for(;foo();) { alert(3); }"); test("var a; do {} while(a = foo()) { alert(3); }", "do {} while(foo()) { alert(3); }"); } public void testImmutableWithSingleReferenceAfterInitialzation() { test("var a; a = 1;", "1;"); test("var a; if (a = 1) { alert(3); }", "if (1) { alert(3); }"); test("var a; switch (a = 1) {}", "switch(1) {}"); test("var a; function f(){ return a = 1; }", "function f(){ return 1; }"); test("function f(){ var a; return a = 1; }", "function f(){ return 1; }"); test("var a; with (a = 1) { alert(3); }", "with (1) { alert(3); }"); test("var a; b = (a = 1);", "b = 1;"); test("var a; while(a = 1) { alert(3); }", "while(1) { alert(3); }"); test("var a; for(;a = 1;) { alert(3); }", "for(;1;) { alert(3); }"); test("var a; do {} while(a = 1) { alert(3); }", "do {} while(1) { alert(3); }"); } public void testSingleReferenceAfterInitialzation() { test("var a; a = foo();a;", "foo();"); testSame("var a; if (a = foo()) { alert(3); } a;"); testSame("var a; switch (a = foo()) {} a;"); testSame("var a; function f(){ return a = foo(); } a;"); testSame("function f(){ var a; return a = foo(); a;}"); testSame("var a; with (a = foo()) { alert(3); } a;"); testSame("var a; b = (a = foo()); a;"); testSame("var a; while(a = foo()) { alert(3); } a;"); testSame("var a; for(;a = foo();) { alert(3); } a;"); testSame("var a; do {} while(a = foo()) { alert(3); } a;"); } public void testInsideIfBranch() { testSame("var a = foo(); if (1) { alert(a); }"); } public void testInsideAndConditional() { test("var a = foo(); a && alert(3);", "foo() && alert(3);"); } public void testInsideAndBranch() { testSame("var a = foo(); 1 && alert(a);"); } public void testInsideOrBranch() { testSame("var a = foo(); 1 || alert(a);"); } public void testInsideHookBranch() { testSame("var a = foo(); 1 ? alert(a) : alert(3)"); } public void testInsideHookConditional() { test("var a = foo(); a ? alert(1) : alert(3)", "foo() ? alert(1) : alert(3)"); } public void testInsideOrBranchInsideIfConditional() { testSame("var a = foo(); if (x || a) {}"); } public void testInsideOrBranchInsideIfConditionalWithConstant() { // We don't inline non-immutable constants into branches. testSame("var a = [false]; if (x || a) {}"); } public void testCrossFunctionsAsLeftLeaves() { // Ensures getNext() understands how to walk past a function leaf test( new String[] { "var x = function() {};", "", "function cow() {} var z = x;"}, new String[] { "", "", "function cow() {} var z = function() {};" }); test( new String[] { "var x = function() {};", "", "var cow = function() {}; var z = x;"}, new String[] { "", "", "var cow = function() {}; var z = function() {};" }); testSame( new String[] { "var x = a;", "", "(function() { a++; })(); var z = x;"}); test( new String[] { "var x = a;", "", "function cow() { a++; }; cow(); var z = x;"}, new String[] { "var x = a;", "", ";(function cow(){ a++; })(); var z = x;"}); testSame( new String[] { "var x = a;", "", "cow(); var z = x; function cow() { a++; };"}); } // Test movement of constant values public void testDoCrossFunction() { // We know foo() does not affect x because we require that x is only // referenced twice. test("var x = 1; foo(); var z = x;", "foo(); var z = 1;"); } public void testDoNotCrossReferencingFunction() { testSame( "var f = function() { var z = x; };" + "var x = 1;" + "f();" + "var z = x;" + "f();"); } // Test tricky declarations and references public void testChainedAssignment() { test("var a = 2, b = 2; var c = b;", "var a = 2; var c = 2;"); test("var a = 2, b = 2; var c = a;", "var b = 2; var c = 2;"); test("var a = b = 2; var f = 3; var c = a;", "var f = 3; var c = b = 2;"); testSame("var a = b = 2; var c = b;"); } public void testForIn() { testSame("for (var i in j) { var c = i; }"); testSame("var i = 0; for (i in j) ;"); testSame("var i = 0; for (i in j) { var c = i; }"); testSame("i = 0; for (var i in j) { var c = i; }"); testSame("var j = {'key':'value'}; for (var i in j) {print(i)};"); } // Test movement of values that have (may) side effects public void testDoCrossNewVariables() { test("var x = foo(); var z = x;", "var z = foo();"); } public void testDoNotCrossFunctionCalls() { testSame("var x = foo(); bar(); var z = x;"); } // Test movement of values that are complex but lack side effects public void testDoNotCrossAssignment() { testSame("var x = {}; var y = x.a; x.a = 1; var z = y;"); testSame("var a = this.id; foo(this.id = 3, a);"); } public void testDoNotCrossDelete() { testSame("var x = {}; var y = x.a; delete x.a; var z = y;"); } public void testDoNotCrossAssignmentPlus() { testSame("var a = b; b += 2; var c = a;"); } public void testDoNotCrossIncrement() { testSame("var a = b.c; b.c++; var d = a;"); } public void testDoNotCrossConstructor() { testSame("var a = b; new Foo(); var c = a;"); } public void testDoCrossVar() { // Assumes we do not rely on undefined variables (not technically correct!) test("var a = b; var b = 3; alert(a)", "alert(3);"); } public void testOverlappingInlines() { String source = "a = function(el, x, opt_y) { " + " var cur = bar(el); " + " opt_y = x.y; " + " x = x.x; " + " var dx = x - cur.x; " + " var dy = opt_y - cur.y;" + " foo(el, el.offsetLeft + dx, el.offsetTop + dy); " + "};"; String expected = "a = function(el, x, opt_y) { " + " var cur = bar(el); " + " opt_y = x.y; " + " x = x.x; " + " foo(el, el.offsetLeft + (x - cur.x)," + " el.offsetTop + (opt_y - cur.y)); " + "};"; test(source, expected); } public void testOverlappingInlineFunctions() { String source = "a = function() { " + " var b = function(args) {var n;}; " + " var c = function(args) {}; " + " d(b,c); " + "};"; String expected = "a = function() { " + " d(function(args){var n;}, function(args){}); " + "};"; test(source, expected); } public void testInlineIntoLoops() { test("var x = true; while (true) alert(x);", "while (true) alert(true);"); test("var x = true; while (true) for (var i in {}) alert(x);", "while (true) for (var i in {}) alert(true);"); testSame("var x = [true]; while (true) alert(x);"); } public void testInlineIntoFunction() { test("var x = false; var f = function() { alert(x); };", "var f = function() { alert(false); };"); testSame("var x = [false]; var f = function() { alert(x); };"); } public void testNoInlineIntoNamedFunction() { testSame("f(); var x = false; function f() { alert(x); };"); } public void testInlineIntoNestedNonHoistedNamedFunctions() { test("f(); var x = false; if (false) function f() { alert(x); };", "f(); if (false) function f() { alert(false); };"); } public void testNoInlineIntoNestedNamedFunctions() { testSame("f(); var x = false; function f() { if (false) { alert(x); } };"); } public void testNoInlineMutatedVariable() { testSame("var x = false; if (true) { var y = x; x = true; }"); } public void testInlineImmutableMultipleTimes() { test("var x = null; var y = x, z = x;", "var y = null, z = null;"); test("var x = 3; var y = x, z = x;", "var y = 3, z = 3;"); } public void testNoInlineStringMultipleTimesIfNotWorthwhile() { testSame("var x = 'abcdefghijklmnopqrstuvwxyz'; var y = x, z = x;"); } public void testInlineStringMultipleTimesWhenAliasingAllStrings() { inlineAllStrings = true; test("var x = 'abcdefghijklmnopqrstuvwxyz'; var y = x, z = x;", "var y = 'abcdefghijklmnopqrstuvwxyz', " + " z = 'abcdefghijklmnopqrstuvwxyz';"); } public void testNoInlineBackwards() { testSame("var y = x; var x = null;"); } public void testNoInlineOutOfBranch() { testSame("if (true) var x = null; var y = x;"); } public void testInterferingInlines() { test("var a = 3; var f = function() { var x = a; alert(x); };", "var f = function() { alert(3); };"); } public void testInlineIntoTryCatch() { test("var a = true; " + "try { var b = a; } " + "catch (e) { var c = a + b; var d = true; } " + "finally { var f = a + b + c + d; }", "try { var b = true; } " + "catch (e) { var c = true + b; var d = true; } " + "finally { var f = true + b + c + d; }"); } // Make sure that we still inline constants that are not provably // written before they're read. public void testInlineConstants() { test("function foo() { return XXX; } var XXX = true;", "function foo() { return true; }"); } public void testInlineStringWhenWorthwhile() { test("var x = 'a'; foo(x, x, x);", "foo('a', 'a', 'a');"); } public void testInlineConstantAlias() { test("var XXX = new Foo(); q(XXX); var YYY = XXX; bar(YYY)", "var XXX = new Foo(); q(XXX); bar(XXX)"); } public void testInlineConstantAliasWithAnnotation() { test("/** @const */ var xxx = new Foo(); q(xxx); var YYY = xxx; bar(YYY)", "/** @const */ var xxx = new Foo(); q(xxx); bar(xxx)"); } public void testInlineConstantAliasWithNonConstant() { test("var XXX = new Foo(); q(XXX); var y = XXX; bar(y); baz(y)", "var XXX = new Foo(); q(XXX); bar(XXX); baz(XXX)"); } public void testCascadingInlines() { test("var XXX = 4; " + "function f() { var YYY = XXX; bar(YYY); baz(YYY); }", "function f() { bar(4); baz(4); }"); } public void testNoInlineGetpropIntoCall() { test("var a = b; a();", "b();"); test("var a = b.c; f(a);", "f(b.c);"); testSame("var a = b.c; a();"); } public void testInlineFunctionDeclaration() { test("var f = function () {}; var a = f;", "var a = function () {};"); test("var f = function () {}; foo(); var a = f;", "foo(); var a = function () {};"); test("var f = function () {}; foo(f);", "foo(function () {});"); testSame("var f = function () {}; function g() {var a = f;}"); testSame("var f = function () {}; function g() {h(f);}"); } public void test2388531() { testSame("var f = function () {};" + "var g = function () {};" + "goog.inherits(f, g);"); testSame("var f = function () {};" + "var g = function () {};" + "goog$inherits(f, g);"); } public void testRecursiveFunction1() { testSame("var x = 0; (function x() { return x ? x() : 3; })();"); } public void testRecursiveFunction2() { testSame("function y() { return y(); }"); } public void testUnreferencedBleedingFunction() { testSame("var x = function y() {}"); } public void testReferencedBleedingFunction() { testSame("var x = function y() { return y(); }"); } public void testInlineAliases1() { test("var x = this.foo(); this.bar(); var y = x; this.baz(y);", "var x = this.foo(); this.bar(); this.baz(x);"); } public void testInlineAliases1b() { test("var x = this.foo(); this.bar(); var y; y = x; this.baz(y);", "var x = this.foo(); this.bar(); x; this.baz(x);"); } public void testInlineAliases1c() { test("var x; x = this.foo(); this.bar(); var y = x; this.baz(y);", "var x; x = this.foo(); this.bar(); this.baz(x);"); } public void testInlineAliases1d() { test("var x; x = this.foo(); this.bar(); var y; y = x; this.baz(y);", "var x; x = this.foo(); this.bar(); x; this.baz(x);"); } public void testInlineAliases2() { test("var x = this.foo(); this.bar(); " + "function f() { var y = x; this.baz(y); }", "var x = this.foo(); this.bar(); function f() { this.baz(x); }"); } public void testInlineAliases2b() { test("var x = this.foo(); this.bar(); " + "function f() { var y; y = x; this.baz(y); }", "var x = this.foo(); this.bar(); function f() { this.baz(x); }"); } public void testInlineAliases2c() { test("var x; x = this.foo(); this.bar(); " + "function f() { var y = x; this.baz(y); }", "var x; x = this.foo(); this.bar(); function f() { this.baz(x); }"); } public void testInlineAliases2d() { test("var x; x = this.foo(); this.bar(); " + "function f() { var y; y = x; this.baz(y); }", "var x; x = this.foo(); this.bar(); function f() { this.baz(x); }"); } public void testInlineAliasesInLoop() { test( "function f() { " + " var x = extern();" + " for (var i = 0; i < 5; i++) {" + " (function() {" + " var y = x; window.setTimeout(function() { extern(y); }, 0);" + " })();" + " }" + "}", "function f() { " + " var x = extern();" + " for (var i = 0; i < 5; i++) {" + " (function() {" + " window.setTimeout(function() { extern(x); }, 0);" + " })();" + " }" + "}"); } public void testNoInlineAliasesInLoop() { testSame( "function f() { " + " for (var i = 0; i < 5; i++) {" + " var x = extern();" + " (function() {" + " var y = x; window.setTimeout(function() { extern(y); }, 0);" + " })();" + " }" + "}"); } public void testNoInlineAliases1() { testSame( "var x = this.foo(); this.bar(); var y = x; x = 3; this.baz(y);"); } public void testNoInlineAliases1b() { testSame( "var x = this.foo(); this.bar(); var y; y = x; x = 3; this.baz(y);"); } public void testNoInlineAliases2() { testSame( "var x = this.foo(); this.bar(); var y = x; y = 3; this.baz(y); "); } public void testNoInlineAliases2b() { testSame( "var x = this.foo(); this.bar(); var y; y = x; y = 3; this.baz(y); "); } public void testNoInlineAliases3() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y = x; g(); this.baz(y); } " + "function g() { x = 3; }"); } public void testNoInlineAliases3b() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y; y = x; g(); this.baz(y); } " + "function g() { x = 3; }"); } public void testNoInlineAliases4() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y = x; y = 3; this.baz(y); }"); } public void testNoInlineAliases4b() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y; y = x; y = 3; this.baz(y); }"); } public void testNoInlineAliases5() { testSame( "var x = this.foo(); this.bar(); var y = x; this.bing();" + "this.baz(y); x = 3;"); } public void testNoInlineAliases5b() { testSame( "var x = this.foo(); this.bar(); var y; y = x; this.bing();" + "this.baz(y); x = 3;"); } public void testNoInlineAliases6() { testSame( "var x = this.foo(); this.bar(); var y = x; this.bing();" + "this.baz(y); y = 3;"); } public void testNoInlineAliases6b() { testSame( "var x = this.foo(); this.bar(); var y; y = x; this.bing();" + "this.baz(y); y = 3;"); } public void testNoInlineAliases7() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y = x; this.bing(); this.baz(y); x = 3; }"); } public void testNoInlineAliases7b() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y; y = x; this.bing(); this.baz(y); x = 3; }"); } public void testNoInlineAliases8() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y = x; this.baz(y); y = 3; }"); } public void testNoInlineAliases8b() { testSame( "var x = this.foo(); this.bar(); " + "function f() { var y; y = x; this.baz(y); y = 3; }"); } public void testSideEffectOrder() { // z can not be changed by the call to y, so x can be inlined. String EXTERNS = "var z; function f(){}"; test(EXTERNS, "var x = f(y.a, y); z = x;", "z = f(y.a, y);", null, null); // z.b can be changed by the call to y, so x can not be inlined. testSame(EXTERNS, "var x = f(y.a, y); z.b = x;", null, null); } public void testInlineParameterAlias1() { test( "function f(x) {" + " var y = x;" + " g();" + " y;y;" + "}", "function f(x) {" + " g();" + " x;x;" + "}" ); } public void testInlineParameterAlias2() { test( "function f(x) {" + " var y; y = x;" + " g();" + " y;y;" + "}", "function f(x) {" + " x;" + " g();" + " x;x;" + "}" ); } public void testInlineFunctionAlias1a() { test( "function f(x) {}" + "var y = f;" + "g();" + "y();y();", "var y = function f(x) {};" + "g();" + "y();y();" ); } public void testInlineFunctionAlias1b() { test( "function f(x) {};" + "f;var y = f;" + "g();" + "y();y();", "function f(x) {};" + "f;g();" + "f();f();" ); } public void testInlineFunctionAlias2a() { test( "function f(x) {}" + "var y; y = f;" + "g();" + "y();y();", "var y; y = function f(x) {};" + "g();" + "y();y();" ); } public void testInlineFunctionAlias2b() { test( "function f(x) {};" + "f; var y; y = f;" + "g();" + "y();y();", "function f(x) {};" + "f; f;" + "g();" + "f();f();" ); } public void testInlineCatchAlias1() { test( "try {" + "} catch (e) {" + " var y = e;" + " g();" + " y;y;" + "}", "try {" + "} catch (e) {" + " g();" + " e;e;" + "}" ); } public void testInlineCatchAlias2() { test( "try {" + "} catch (e) {" + " var y; y = e;" + " g();" + " y;y;" + "}", "try {" + "} catch (e) {" + " e;" + " g();" + " e;e;" + "}" ); } public void testLocalsOnly1() { inlineLocalsOnly = true; test( "var x=1; x; function f() {var x = 1; x;}", "var x=1; x; function f() {1;}"); } public void testLocalsOnly2() { inlineLocalsOnly = true; test( "/** @const */\n" + "var X=1; X;\n" + "function f() {\n" + " /** @const */\n" + " var X = 1; X;\n" + "}", "var X=1; X; function f() {1;}"); } public void testInlineUndefined1() { test("var x; x;", "void 0;"); } public void testInlineUndefined2() { testSame("var x; x++;"); } public void testInlineUndefined3() { testSame("var x; var x;"); } public void testInlineUndefined4() { test("var x; x; x;", "void 0; void 0;"); } public void testInlineUndefined5() { test("var x; for(x in a) {}", "var x; for(x in a) {}"); } public void testIssue90() { test("var x; x && alert(1)", "void 0 && alert(1)"); } public void testRenamePropertyFunction() { testSame("var JSCompiler_renameProperty; " + "JSCompiler_renameProperty('foo')"); } public void testThisAlias() { test("function f() { var a = this; a.y(); a.z(); }", "function f() { this.y(); this.z(); }"); } public void testThisEscapedAlias() { testSame( "function f() { var a = this; var g = function() { a.y(); }; a.z(); }"); } public void testInlineNamedFunction() { test("function f() {} f();", "(function f(){})()"); } public void testIssue378ModifiedArguments1() { testSame( "function g(callback) {\n" + " var f = callback;\n" + " arguments[0] = this;\n" + " f.apply(this, arguments);\n" + "}"); } public void testIssue378ModifiedArguments2() { testSame( "function g(callback) {\n" + " /** @const */\n" + " var f = callback;\n" + " arguments[0] = this;\n" + " f.apply(this, arguments);\n" + "}"); } public void testIssue378EscapedArguments1() { testSame( "function g(callback) {\n" + " var f = callback;\n" + " h(arguments,this);\n" + " f.apply(this, arguments);\n" + "}\n" + "function h(a,b) {\n" + " a[0] = b;" + "}"); } public void testIssue378EscapedArguments2() { testSame( "function g(callback) {\n" + " /** @const */\n" + " var f = callback;\n" + " h(arguments,this);\n" + " f.apply(this);\n" + "}\n" + "function h(a,b) {\n" + " a[0] = b;" + "}"); } public void testIssue378EscapedArguments3() { test( "function g(callback) {\n" + " var f = callback;\n" + " f.apply(this, arguments);\n" + "}\n", "function g(callback) {\n" + " callback.apply(this, arguments);\n" + "}\n"); } public void testIssue378EscapedArguments4() { testSame( "function g(callback) {\n" + " var f = callback;\n" + " h(arguments[0],this);\n" + " f.apply(this, arguments);\n" + "}\n" + "function h(a,b) {\n" + " a[0] = b;" + "}"); } public void testIssue378ArgumentsRead1() { test( "function g(callback) {\n" + " var f = callback;\n" + " var g = arguments[0];\n" + " f.apply(this, arguments);\n" + "}", "function g(callback) {\n" + " var g = arguments[0];\n" + " callback.apply(this, arguments);\n" + "}"); } public void testIssue378ArgumentsRead2() { test( "function g(callback) {\n" + " var f = callback;\n" + " h(arguments[0],this);\n" + " f.apply(this, arguments[0]);\n" + "}\n" + "function h(a,b) {\n" + " a[0] = b;" + "}", "function g(callback) {\n" + " h(arguments[0],this);\n" + " callback.apply(this, arguments[0]);\n" + "}\n" + "function h(a,b) {\n" + " a[0] = b;" + "}"); } public void testArgumentsModifiedInOuterFunction() { test( "function g(callback) {\n" + " var f = callback;\n" + " arguments[0] = this;\n" + " f.apply(this, arguments);\n" + " function inner(callback) {" + " var x = callback;\n" + " x.apply(this);\n" + " }" + "}", "function g(callback) {\n" + " var f = callback;\n" + " arguments[0] = this;\n" + " f.apply(this, arguments);\n" + " function inner(callback) {" + " callback.apply(this);\n" + " }" + "}"); } public void testArgumentsModifiedInInnerFunction() { test( "function g(callback) {\n" + " var f = callback;\n" + " f.apply(this, arguments);\n" + " function inner(callback) {" + " var x = callback;\n" + " arguments[0] = this;\n" + " x.apply(this);\n" + " }" + "}", "function g(callback) {\n" + " callback.apply(this, arguments);\n" + " function inner(callback) {" + " var x = callback;\n" + " arguments[0] = this;\n" + " x.apply(this);\n" + " }" + "}"); } public void testNoInlineRedeclaredExterns() { String externs = "var test = 1;"; String code = "/** @suppress {duplicate} */ var test = 2;alert(test);"; test(externs, code, code, null, null); } public void testBug6598844() { testSame( "function F() { this.a = 0; }" + "F.prototype.inc = function() { this.a++; return 10; };" + "F.prototype.bar = function() { var val = inc(); this.a += val; };"); } } ././@LongLink0000644000000000000000000000015600000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeCollectPropertyAssignmentsTest.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments0000644000175000017500000001443512115204405032006 0ustar apoapo/* * 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; public class PeepholeCollectPropertyAssignmentsTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new PeepholeOptimizationsPass( compiler, new PeepholeCollectPropertyAssignments()); } public final void testArrayOptimization1() { test("var a = []; a[0] = 1; a[1] = 2; a[2] = 3;", "var a = [1, 2, 3];"); } public final void testArrayOptimization2() { test("var a; a = []; a[0] = 1; a[1] = 2; a[2] = 3;", "var a; a = [1, 2, 3];"); } public final void testArrayOptimization3() { testSame("var a; a.b = []; a.b[0] = 1; a.b[1] = 2; a.b[2] = 3;"); } public final void testCompoundAssignment() { testSame("var x, a; a = []; a[0] *= x;"); } public final void testNegativeArrayIndex1() { testSame("var a = []; a[-1] = 1;"); } public final void testNegativeArrayIndex2() { testSame("var a; a = []; a[-1] = 1;"); } public final void testFractionalArrayIndex1() { testSame("var a = []; a[0.5] = 1;"); } public final void testFractionalArrayIndex2() { testSame("var a; a = []; a[0.5] = 1;"); } public final void testArrayOptimizationOfPartiallyBuiltArray1() { test("var a = [1, 2]; a[2] = 3;", "var a = [1, 2, 3];"); } public final void testArrayOptimizationOfPartiallyBuiltArray2() { test("var a; a = [1, 2]; a[2] = 3;", "var a; a = [1, 2, 3];"); } public final void testArrayOptimizationWithAHole1() { test("var a = []; a[0] = 1; a[1] = 2; a[3] = 4;", "var a = [1, 2, , 4];"); } public final void testArrayOptimizationWithAHole2() { test("var a; a = []; a[0] = 1; a[1] = 2; a[3] = 4;", "var a; a = [1, 2, , 4];"); } public final void testEarlyUsage1() { testSame( "function c() {return sum(a)};" + "var a = [1,2,3];" + "a[4] = c();"); } public final void testEarlyUsage2() { testSame( "function c() {return sum(a)};" + "var a; a = [1,2,3];" + "a[4] = c();"); } public final void testArrayTooSparseOptimization1() { test("var a = []; a[0] = 1; a[1] = 2; a[100] = 4;", "var a = [1, 2]; a[100] = 4;"); } public final void testArrayTooSparseOptimization2() { test("var a; a = []; a[0] = 1; a[1] = 2; a[100] = 4;", "var a; a = [1, 2]; a[100] = 4;"); } public final void testArrayOutOfOrder() { test("var a = []; a[1] = 1; a[0] = 0;", "var a = [0, 1];"); test("var a; a = []; a[1] = 1; a[0] = 0;", "var a; a = [0, 1];"); // We cannot change the order of side-effects. // The below should not be // var x = 0; var a = [x++, x++] // since that would produce // var a = [0, 1], x = 2; // instead of // var a = [1, 0], x = 2; testSame("var x = 0; var a = []; a[1] = x++; a[0] = x++;"); testSame("var x; x = 0; var a = []; a[1] = x++; a[0] = x++;"); } public final void testMultipleNames1() { test("var b = []; b[0] = 2; var a = []; a[0] = 1;", "var b = [2]; var a = [1];"); } public final void testMultipleNames2() { test("var b; b = []; b[0] = 2; var a = []; a[0] = 1;", "var b; b = [2]; var a = [1];"); } public final void testArrayReassignedInValue1() { test("var a = []; a[0] = 1; a[1] = (a = []); a[3] = 4;", "var a = [1]; a[1] = (a = []); a[3] = 4;"); } public final void testArrayReassignedInValue2() { test("var a; a = []; a[0] = 1; a[1] = (a = []); a[3] = 4;", "var a; a = [1]; a[1] = (a = []); a[3] = 4;"); } public final void testArrayReassignedInSubsequentVar1() { testSame("var a = []; a[0] = a = []; a[1] = 2;"); } public final void testArrayReassignedInSubsequentVar2() { testSame("var a; a = []; a[0] = a = []; a[1] = 2;"); } public final void testForwardReference1() { test("var a; a = []; a[0] = 1; a[1] = a;", "var a; a = [1]; a[1] = a;"); } public final void testForwardReference2() { test("var a; a = []; a[0] = 1; a[1] = a;", "var a; a = [1]; a[1] = a;"); } public final void testObjectOptimization1() { test("var o = {}; o.x = 0; o['y'] = 1; o[2] = 2;", "var o = { x: 0, \"y\": 1, \"2\": 2 };"); } public final void testObjectOptimization2() { test("var o; o = {}; o.x = 0; o['y'] = 1; o[2] = 2;", "var o; o = { x: 0, \"y\": 1, \"2\": 2 };"); } public final void testObjectReassignedInValue1() { test("var o = {}; o.x = 1; o.y = (o = {}); o.z = 4;", "var o = {x:1}; o.y = (o = {}); o.z = 4;"); } public final void testObjectReassignedInValue2() { test("var o; o = {}; o.x = 1; o.y = (o = {}); o.z = 4;", "var o; o = {x:1}; o.y = (o = {}); o.z = 4;"); } public final void testObjectFunctionRollup1() { test("var o; o = {};" + "o.x = function() {};", "var o; o = {x:function () {}};"); } public final void testObjectFunctionRollup2() { testSame( "var o; o = {};" + "o.x = (function() {return o})();"); } public final void testObjectFunctionRollup3() { test("var o; o = {};" + "o.x = function() {return o};", "var o; o = {x:function () {return o}};"); } public final void testObjectFunctionRollup4() { testSame( "function f() {return o};" + "var o; o = {};" + "o.x = f();"); } public final void testObjectFunctionRollup5() { test("var o; o = {};" + "o.x = function() {return o};" + "o.y = [function() {return o}];" + "o.z = {a:function() {return o}};", "var o; o = {" + "x:function () {return o}, " + "y:[function () {return o}], " + "z:{a:function () {return o}}};"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SourceMapTest.java0000644000175000017500000001022712115204405026427 0ustar apoapo/* * 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.debugging.sourcemap.SourceMapConsumer; import com.google.debugging.sourcemap.SourceMapConsumerV2; import com.google.debugging.sourcemap.SourceMapTestCase; import com.google.javascript.jscomp.SourceMap.Format; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * * @author johnlenz@google.com (John Lenz) */ public class SourceMapTest extends SourceMapTestCase { public SourceMapTest() { } private List mappings; public void testPrefixReplacement1() throws IOException { mappings = new ArrayList(); // mapping can be used to remove a prefix mappings.add( new SourceMap.LocationMapping("pre/","") ); checkSourceMap2("", "pre/file1", "", "pre/file2" , "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"\"],\n" + "\"mappings\":[],\n" + "\"sources\":[\"file1\",\"file2\"],\n" + "\"names\":[]\n" + "}\n"); } public void testPrefixReplacement2() throws IOException { mappings = new ArrayList(); // mapping can be used to replace a prefix mappings.add( new SourceMap.LocationMapping("pre/file","src") ); checkSourceMap2("", "pre/file1", "", "pre/file2" , "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"\"],\n" + "\"mappings\":[],\n" + "\"sources\":[\"src1\",\"src2\"],\n" + "\"names\":[]\n" + "}\n"); } public void testPrefixReplacement3() throws IOException { mappings = new ArrayList(); // multiple mappings can be applied mappings.add( new SourceMap.LocationMapping("file1","x") ); mappings.add( new SourceMap.LocationMapping("file2","y") ); checkSourceMap2("", "file1", "", "file2" , "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"\"],\n" + "\"mappings\":[],\n" + "\"sources\":[\"x\",\"y\"],\n" + "\"names\":[]\n" + "}\n"); } public void testPrefixReplacement4() throws IOException { mappings = new ArrayList(); // first match wins mappings.add( new SourceMap.LocationMapping("file1","x") ); mappings.add( new SourceMap.LocationMapping("file","y") ); checkSourceMap2("", "file1", "", "file2" , "{\n" + "\"version\":2,\n" + "\"file\":\"testcode\",\n" + "\"lineCount\":1,\n" + "\"lineMaps\":[\"\"],\n" + "\"mappings\":[],\n" + "\"sources\":[\"x\",\"y2\"],\n" + "\"names\":[]\n" + "}\n"); } @Override protected CompilerOptions getCompilerOptions() { CompilerOptions options = super.getCompilerOptions(); if (mappings != null) { options.sourceMapLocationMappings = mappings; } return options; } @Override public void setUp() { super.setUp(); } private void checkSourceMap2( String js1, String file1, String js2, String file2, String expectedMap) throws IOException { RunResult result = compile(js1, file1, js2, file2); assertEquals(expectedMap, result.sourceMapFileContent); assertEquals(result.sourceMapFileContent, getSourceMap(result)); } @Override protected Format getSourceMapFormat() { return Format.V2; } @Override protected SourceMapConsumer getSourceMapConsumer() { return new SourceMapConsumerV2(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CoalesceVariableNamesTest.java0000644000175000017500000003473712115204405030715 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Unit tests for {@link CoalesceVariableNames} * */ public class CoalesceVariableNamesTest extends CompilerTestCase { // The spacing in this file is not exactly standard but it greatly helps // picking out which variable names are merged. private boolean usePseudoName = false; @Override protected int getNumRepetitions() { return 1; } @Override public void setUp() { super.enableLineNumberCheck(true); usePseudoName = false; } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, new CoalesceVariableNames(compiler, usePseudoName)); } }; } public void testSimple() { inFunction("var x; var y; x=1; x; y=1; y; return y", "var x; x=1; x; x=1; x; return x"); inFunction("var x,y; x=1; x; y=1; y", "var x ; x=1; x; x=1; x"); inFunction("var x,y; x=1; y=2; y; x"); inFunction("y=0; var x, y; y; x=0; x", "y=0; var y ; y; y=0;y"); inFunction("var x,y; x=1; y=x; y", "var x ; x=1; x=x; x"); inFunction("var x,y; x=1; y=x+1; y", "var x ; x=1; x=x+1; x"); inFunction("x=1; x; y=2; y; var x; var y", "x=1; x; x=2; x; var x"); inFunction("var x=1; var y=x+1; return y", "var x=1; x=x+1; return x"); inFunction("var x=1; var y=0; x+=1; y"); inFunction("var x=1; x+=1; var y=0; y", "var x=1; x+=1; x=0; x"); inFunction("var x=1; foo(bar(x+=1)); var y=0; y", "var x=1; foo(bar(x+=1)); x=0; x"); inFunction("var y, x=1; f(x+=1, y)"); inFunction("var x; var y; y += 1, y, x = 1; x"); } public void testMergeThreeVarNames() { inFunction("var x,y,z; x=1; x; y=1; y; z=1; z", "var x ; x=1; x; x=1; x; x=1; x"); } public void testDifferentBlock() { inFunction("if(1) { var x = 0; x } else { var y = 0; y }", "if(1) { var x = 0; x } else { x = 0; x }"); } public void testLoops() { inFunction("var x; while(1) { x; x = 1; var y = 1; y }"); inFunction("var y = 1; y; while(1) { var x = 1; x }", "var y = 1; y; while(1) { y = 1; y }"); } public void testEscaped() { inFunction("var x = 1; x; function f() { x }; var y = 0; y; f()"); } public void testFor() { inFunction("var x = 1; x; for (;;) var y; y = 1; y", "var x = 1; x; for (;;) ; x = 1; x"); } public void testForIn() { // We lose some precision here, unless we have "branched-backward-dataflow". inFunction("var x = 1, k; x; ; for (var y in k) { y }", "var x = 1, k; x; ; for (var y in k) { y }"); inFunction("var x = 1, k; x; y = 1; for (var y in k) { y }", "var x = 1, k; x; x = 1; for ( x in k) { x }"); } public void testLoopInductionVar() { inFunction( "for(var x = 0; x < 10; x++){}" + "for(var y = 0; y < 10; y++){}" + "for(var z = 0; z < 10; z++){}", "for(var x = 0; x < 10; x++){}" + "for(x = 0; x < 10; x++){}" + "for(x = 0; x < 10; x++){}"); inFunction( "for(var x = 0; x < 10; x++){z}" + "for(var y = 0, z = 0; y < 10; y++){z}", "for(var x = 0; x < 10; x++){z}" + "for(var x = 0, z = 0; x < 10; x++){z}"); inFunction("var x = 1; x; for (var y; y=1; ) {y}", "var x = 1; x; for ( ; x=1; ) {x}"); inFunction("var x = 1; x; y = 1; while(y) var y; y", "var x = 1; x; x = 1; while(x); x"); inFunction("var x = 1; x; f:var y; y=1", "var x = 1; x; x=1"); } public void testSwitchCase() { inFunction("var x = 1; switch(x) { case 1: var y; case 2: } y = 1; y", "var x = 1; switch(x) { case 1: case 2: } x = 1; x"); } public void testDuplicatedVar() { // Is there a shorter version without multiple declarations? inFunction("z = 1; var x = 0; x; z; var y = 2, z = 1; y; z;", "z = 1; var x = 0; x; z; var x = 2, z = 1; x; z;"); } public void testTryCatch() { inFunction("try {} catch (e) { } var x = 4; x;", "try {} catch (e) { } var x = 4; x;"); inFunction("var x = 4; x; try {} catch (e) { }", "var x = 4; x; try {} catch (e) { }"); } public void testDeadAssignment() { inFunction("var x = 6; var y; y = 4 ; x"); inFunction("var y = 3; var y; y += 4; x"); inFunction("var y = 3; var y; y ++ ; x"); inFunction("y = 3; var x; var y = 1 ; x"); } public void testParameter() { test("function FUNC(param) {var x = 0; x}", "function FUNC(param) {param = 0; param}"); } public void testParameter2() { // Make sure two formal parameter name never merges. test("function FUNC(x,y) {x = 0; x; y = 0; y}"); test("function FUNC(x,y,z) {x = 0; x; y = 0; z = 0; z}"); } public void testParameter3() { // Make sure the formal parameter declaration is consider a def. test("function FUNC(x) {var y; y = 0; x; y}"); } public void testParameter4() { // Make sure that we do not merge two-arg functions because of the // IE sort bug (see comments in computeEscaped) test("function FUNC(x, y) {var a,b; y; a=0; a; x; b=0; b}", "function FUNC(x, y) {var a; y; a=0; a; x; a=0; a}"); } public void testParameter4b() { // Merge parameters test("function FUNC(x, y, z) {var a,b; y; a=0; a; x; b=0; b}", "function FUNC(x, y, z) { y; y=0; y; x; x=0; x}"); } public void testLiveRangeChangeWithinCfgNode() { inFunction("var x, y; x = 1, y = 2, y, x"); inFunction("var x, y; x = 1,x; y"); // We lose some precisions within the node itself. inFunction("var x; var y; y = 1, y, x = 1; x"); inFunction("var x; var y; y = 1; y, x = 1; x", "var x; x = 1; x, x = 1; x"); inFunction("var x, y; y = 1, x = 1, x, y += 1, y"); inFunction("var x, y; y = 1, x = 1, x, y ++, y"); } public void testLiveRangeChangeWithinCfgNode2() { inFunction("var x; var y; var a; var b;" + "y = 1, a = 1, y, a, x = 1, b = 1; x; b"); inFunction("var x; var y; var a; var b;" + "y = 1, a = 1, y, a, x = 1; x; b = 1; b", "var x; var y; var a; " + "y = 1, a = 1, y, a, x = 1; x; x = 1; x"); inFunction("var x; var y; var a; var b;" + "y = 1, a = 1, y, x = 1; a; x; b = 1; b", "var x; var y; var a; " + "y = 1, a = 1, y, x = 1; a; x; x = 1; x"); } public void testFunctionNameReuse() { // TODO(user): Figure out why this increase code size most of the time. // inFunction("function x() {}; x(); var y = 1; y", // "function x() {}; x(); x = 1; x"); // inFunction("x(); var y = 1; y; function x() {}", // "x(); x = 1; x; function x() {}"); // inFunction("x(); var y = 1; function x() {}; y", // "x(); x = 1; function x() {}; x"); // // Can't merge because of possible escape. // inFunction("function x() {return x}; x(); var y = 1; y", // "function x() {return x}; x(); var y = 1; y"); // // inFunction("var y = 1; y; x; function x() {}", // "var y = 1; y; x; function x() {}"); // inFunction("var y = 1; y; function x() {}; x", // "var y = 1; y; function x() {}; x"); // inFunction("var y = 1; y; function x() {}; x = 1; x", // "var y = 1; y; function x() {}; y = 1; y"); // inFunction("var y = 1; y; x = 1; function x() {}; x", // "var y = 1; y; y = 1; function x() {}; y"); } public void testBug1401831() { // Verify that we don't wrongly merge "opt_a2" and "i" without considering // arguments[0] aliasing it. String src = "function f(opt_a2) {" + " var buffer;" + " if (opt_a2) {" + " for(var i = 0; i < arguments.length; i++) {" + " buffer += arguments[i];" + " }" + " }" + " return buffer;" + "}"; test(src, src); } public void testDeterministic() { // Make the variable interference graph a pentagon. // a - b // / \ // e c // \ / // d // The coloring partitioning would be: // a = { a, c } // b = { b, d } // e = { e } inFunction("var a,b,c,d,e;" + " a=1; b=1; a; b;" + " b=1; c=1; b; c;" + " c=1; d=1; c; d;" + " d=1; e=1; d; e;" + " e=1; a=1; e; a;", "var a,b, e;" + " a=1; b=1; a; b;" + " b=1; a=1; b; a;" + " a=1; b=1; a; b;" + " b=1; e=1; b; e;" + " e=1; a=1; e; a;"); // If we favor "d" first by declaring "d" earlier, // the coloring partitioning would be: // b = { b, e } // d = { d, a } // c = { c } inFunction("var d,a,b,c,e;" + " a=1; b=1; a; b;" + " b=1; c=1; b; c;" + " c=1; d=1; c; d;" + " d=1; e=1; d; e;" + " e=1; a=1; e; a;", "var d, b,c ;" + " d=1; b=1; d; b;" + " b=1; c=1; b; c;" + " c=1; d=1; c; d;" + " d=1; b=1; d; b;" + " b=1; d=1; b; d;"); } // Sometimes live range can be cross even within a VAR declaration. public void testVarLiveRangeCross() { inFunction("var a={}; var b=a.S(); b", "var a={}; a=a.S(); a"); inFunction("var a={}; var b=a.S(), c=b.SS(); b; c", "var a={}; var b=a.S(), a=b.SS(); b; a"); inFunction("var a={}; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d", "var a={}; var b=a.S(), c=a.SS(), a=a.SSS(); b; c; a"); inFunction("var a={}; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d", "var a={}; var b=a.S(), c=a.SS(), a=a.SSS(); b; c; a"); inFunction("var a={}; d=1; d; var b=a.S(), c=a.SS(), d=a.SSS(); b; c; d"); } public void testBug1445366() { // An assignment might not be complete if the RHS throws an exception. inFunction( " var iframe = getFrame();" + " try {" + " var win = iframe.contentWindow;" + " } catch (e) {" + " } finally {" + " if (win)" + " this.setupWinUtil_();" + " else" + " this.load();" + " }"); // Verify that we can still coalesce it if there are no handlers. inFunction( " var iframe = getFrame();" + " var win = iframe.contentWindow;" + " if (win)" + " this.setupWinUtil_();" + " else" + " this.load();", " var iframe = getFrame();" + " iframe = iframe.contentWindow;" + " if (iframe)" + " this.setupWinUtil_();" + " else" + " this.load();"); } public void testCannotReuseAnyParamsBug() { testSame("function handleKeyboardShortcut(e, key, isModifierPressed) {\n" + " if (!isModifierPressed) {\n" + " return false;\n" + " }\n" + " var command;\n" + " switch (key) {\n" + " case 'b': // Ctrl+B\n" + " command = COMMAND.BOLD;\n" + " break;\n" + " case 'i': // Ctrl+I\n" + " command = COMMAND.ITALIC;\n" + " break;\n" + " case 'u': // Ctrl+U\n" + " command = COMMAND.UNDERLINE;\n" + " break;\n" + " case 's': // Ctrl+S\n" + " return true;\n" + " }\n" + "\n" + " if (command) {\n" + " this.fieldObject.execCommand(command);\n" + " return true;\n" + " }\n" + "\n" + " return false;\n" + "};"); } public void testForInWithAssignment() { inFunction( "var _f = function (commands) {" + " var k, v, ref;" + " for (k in ref = commands) {" + " v = ref[k];" + " alert(k + ':' + v);" + " }" + "}", "var _f = function (commands) {" + " var k,ref;" + " for (k in ref = commands) {" + " commands = ref[k];" + " alert(k + ':' + commands);" + " }" + "}" ); } public void testUsePseduoNames() { usePseudoName = true; inFunction("var x = 0; print(x ); var y = 1; print( y)", "var x_y = 0; print(x_y); x_y = 1; print(x_y)"); inFunction("var x_y = 1; var x = 0; print(x ); var y = 1;" + "print( y); print(x_y);", "var x_y = 1; var x_y$ = 0; print(x_y$); x_y$ = 1;" + "" + "print(x_y$); print(x_y);"); inFunction("var x_y = 1; function f() {" + "var x = 0; print(x ); var y = 1; print( y);" + "print(x_y);}", "var x_y = 1; function f() {" + "var x_y$ = 0; print(x_y$); x_y$ = 1; print(x_y$);" + "print(x_y);}"); inFunction("var x = 0; print(x ); var y = 1; print( y); " + "var closure_var; function bar() { print(closure_var); }", "var x_y = 0; print(x_y); x_y = 1; print(x_y); " + "var closure_var; function bar() { print(closure_var); }"); } public void testMaxVars() { String code = ""; for (int i = 0; i < LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE + 1; i++) { code += String.format("var x%d = 0; print(x%d);", i, i); } inFunction(code); } private void inFunction(String src) { inFunction(src, src); } private void inFunction(String src, String expected) { test("function FUNC(){" + src + "}", "function FUNC(){" + expected + "}"); } private void test(String src) { test(src, src); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CollapsePropertiesTest.java0000644000175000017500000014511512115204405030355 0ustar apoapo/* * Copyright 2006 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.javascript.jscomp.CollapseProperties.UNSAFE_THIS; import com.google.javascript.rhino.Node; /** * Tests for {@link CollapseProperties}. * */ public class CollapsePropertiesTest extends CompilerTestCase { private static String EXTERNS = "var window; function alert(s) {} function parseInt(s) {}" + "/** @constructor */ function String() {}"; private boolean collapsePropertiesOnExternTypes = false; public CollapsePropertiesTest() { super(EXTERNS); } @Override public CompilerPass getProcessor(Compiler compiler) { return new CollapseProperties( compiler, collapsePropertiesOnExternTypes, true); } @Override public void setUp() { enableLineNumberCheck(true); enableNormalize(true); } @Override public int getNumRepetitions() { return 1; } public void testCollapse() { test("var a = {}; a.b = {}; var c = a.b;", "var a$b = {}; var c = a$b"); } public void testMultiLevelCollapse() { test("var a = {}; a.b = {}; a.b.c = {}; var d = a.b.c;", "var a$b$c = {}; var d = a$b$c;"); } public void testDecrement() { test("var a = {}; a.b = 5; a.b--; a.b = 5", "var a$b = 5; a$b--; a$b = 5"); } public void testIncrement() { test("var a = {}; a.b = 5; a.b++; a.b = 5", "var a$b = 5; a$b++; a$b = 5"); } public void testObjLitDeclaration() { test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c", "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c"); } public void testObjLitDeclarationWithGet1() { testSame("var a = {get b(){}};"); } public void testObjLitDeclarationWithGet2() { test("var a = {b: {}, get c(){}}; var d = a.b; var e = a.c", "var a$b = {};var a = {get c(){}};var d = a$b; var e = a.c"); } public void testObjLitDeclarationWithGet3() { test("var a = {b: {get c() { return 3; }}};", "var a$b = {get c() { return 3; }};"); } public void testObjLitDeclarationWithSet1() { testSame("var a = {set b(a){}};"); } public void testObjLitDeclarationWithSet2() { test("var a = {b: {}, set c(a){}}; var d = a.b; var e = a.c", "var a$b = {};var a = {set c(a){}};var d = a$b; var e = a.c"); } public void testObjLitDeclarationWithSet3() { test("var a = {b: {set c(d) {}}};", "var a$b = {set c(d) {}};"); } public void testObjLitDeclarationWithGetAndSet1() { test("var a = {b: {get c() { return 3; },set c(d) {}}};", "var a$b = {get c() { return 3; },set c(d) {}};"); } public void testObjLitDeclarationWithDuplicateKeys() { test("var a = {b: 0, b: 1}; var c = a.b;", "var a$b = 0; var a$b = 1; var c = a$b;", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); } public void testObjLitAssignmentDepth1() { test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c", "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c"); } public void testObjLitAssignmentDepth2() { test("var a = {}; a.b = {c: {}, d: {}}; var e = a.b.c; var f = a.b.d", "var a$b$c = {}; var a$b$d = {}; var e = a$b$c; var f = a$b$d"); } public void testObjLitAssignmentDepth3() { test("var a = {}; a.b = {}; a.b.c = {d: 1, e: 2}; var f = a.b.c.d", "var a$b$c$d = 1; var a$b$c$e = 2; var f = a$b$c$d"); } public void testObjLitAssignmentDepth4() { test("var a = {}; a.b = {}; a.b.c = {}; a.b.c.d = {e: 1, f: 2}; " + "var g = a.b.c.d.e", "var a$b$c$d$e = 1; var a$b$c$d$f = 2; var g = a$b$c$d$e"); } public void testGlobalObjectDeclaredToPreserveItsPreviousValue1() { test("var a = a ? a : {}; a.c = 1;", "var a = a ? a : {}; var a$c = 1;"); } public void testGlobalObjectDeclaredToPreserveItsPreviousValue2() { test("var a = a || {}; a.c = 1;", "var a = a || {}; var a$c = 1;"); } public void testGlobalObjectDeclaredToPreserveItsPreviousValue3() { test("var a = a || {get b() {}}; a.c = 1;", "var a = a || {get b() {}}; var a$c = 1;"); } public void testGlobalObjectNameInBooleanExpressionDepth1_1() { test("var a = {b: 0}; a.c = 1; if (a) x();", "var a$b = 0; var a = {}; var a$c = 1; if (a) x();"); } public void testGlobalObjectNameInBooleanExpressionDepth1_2() { test("var a = {b: 0}; a.c = 1; if (!(a && a.c)) x();", "var a$b = 0; var a = {}; var a$c = 1; if (!(a && a$c)) x();"); } public void testGlobalObjectNameInBooleanExpressionDepth1_3() { test("var a = {b: 0}; a.c = 1; while (a || a.c) x();", "var a$b = 0; var a = {}; var a$c = 1; while (a || a$c) x();"); } public void testGlobalObjectNameInBooleanExpressionDepth1_4() { testSame("var a = {}; a.c = 1; var d = a || {}; a.c;"); } public void testGlobalObjectNameInBooleanExpressionDepth1_5() { testSame("var a = {}; a.c = 1; var d = a.c || a; a.c;"); } public void testGlobalObjectNameInBooleanExpressionDepth1_6() { test("var a = {b: 0}; a.c = 1; var d = !(a.c || a); a.c;", "var a$b = 0; var a = {}; var a$c = 1; var d = !(a$c || a); a$c;"); } public void testGlobalObjectNameInBooleanExpressionDepth2() { test("var a = {b: {}}; a.b.c = 1; if (a.b) x(a.b.c);", "var a$b = {}; var a$b$c = 1; if (a$b) x(a$b$c);"); } public void testGlobalObjectNameInBooleanExpressionDepth3() { // TODO(user): Make CollapseProperties even more aggressive so that // a$b.z gets collapsed. Right now, it doesn't get collapsed because the // expression (a.b && a.b.c) could return a.b. But since it returns a.b iff // a.b *is* safely collapsible, the Boolean logic should be smart enough to // only consider the right side of the && as aliasing. test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + " a.b.z = 1; var d = a.b && a.b.c;", "var a$b = {}; var a$b$c = function(){};" + " a$b.z = 1; var d = a$b && a$b$c;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testGlobalFunctionNameInBooleanExpressionDepth1() { test("function a() {} a.c = 1; if (a) x(a.c);", "function a() {} var a$c = 1; if (a) x(a$c);"); } public void testGlobalFunctionNameInBooleanExpressionDepth2() { test("var a = {b: function(){}}; a.b.c = 1; if (a.b) x(a.b.c);", "var a$b = function(){}; var a$b$c = 1; if (a$b) x(a$b$c);"); } public void testAliasCreatedForObjectDepth1_1() { // An object's properties are not collapsed if the object is referenced // in a such a way that an alias is created for it. testSame("var a = {b: 0}; var c = a; c.b = 1; a.b == c.b;"); } public void testAliasCreatedForObjectDepth1_2() { testSame("var a = {b: 0}; f(a); a.b;"); } public void testAliasCreatedForObjectDepth1_3() { testSame("var a = {b: 0}; new f(a); a.b;"); } public void testAliasCreatedForObjectDepth2_1() { test("var a = {}; a.b = {c: 0}; var d = a.b; a.b.c == d.c;", "var a$b = {c: 0}; var d = a$b; a$b.c == d.c;"); } public void testAliasCreatedForObjectDepth2_2() { test("var a = {}; a.b = {c: 0}; for (var p in a.b) { e(a.b[p]); }", "var a$b = {c: 0}; for (var p in a$b) { e(a$b[p]); }"); } public void testAliasCreatedForEnumDepth1_1() { // An enum's values are always collapsed, even if the enum object is // referenced in a such a way that an alias is created for it. test("/** @enum */ var a = {b: 0}; var c = a; c.b = 1; a.b != c.b;", "var a$b = 0; var a = {b: a$b}; var c = a; c.b = 1; a$b != c.b;"); } public void testAliasCreatedForEnumDepth1_2() { test("/** @enum */ var a = {b: 0}; f(a); a.b;", "var a$b = 0; var a = {b: a$b}; f(a); a$b;"); } public void testAliasCreatedForEnumDepth1_3() { test("/** @enum */ var a = {b: 0}; new f(a); a.b;", "var a$b = 0; var a = {b: a$b}; new f(a); a$b;"); } public void testAliasCreatedForEnumDepth1_4() { test("/** @enum */ var a = {b: 0}; for (var p in a) { f(a[p]); }", "var a$b = 0; var a = {b: a$b}; for (var p in a) { f(a[p]); }"); } public void testAliasCreatedForEnumDepth2_1() { test("var a = {}; /** @enum */ a.b = {c: 0};" + "var d = a.b; d.c = 1; a.b.c != d.c;", "var a$b$c = 0; var a$b = {c: a$b$c};" + "var d = a$b; d.c = 1; a$b$c != d.c;"); } public void testAliasCreatedForEnumDepth2_2() { test("var a = {}; /** @enum */ a.b = {c: 0};" + "for (var p in a.b) { f(a.b[p]); }", "var a$b$c = 0; var a$b = {c: a$b$c};" + "for (var p in a$b) { f(a$b[p]); }"); } public void testAliasCreatedForEnumDepth2_3() { test("var a = {}; var d = a; /** @enum */ a.b = {c: 0};" + "for (var p in a.b) { f(a.b[p]); }", "var a = {}; var d = a; var a$b$c = 0; var a$b = {c: a$b$c};" + "for (var p in a$b) { f(a$b[p]); }", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForEnumOfObjects() { test("var a = {}; " + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c;" + "searchEnum(a.b);", "var a$b$c = {d: 1};var a$b = {c: a$b$c}; a$b$c; " + "searchEnum(a$b)"); } public void testAliasCreatedForEnumOfObjects2() { test("var a = {}; " + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;" + "searchEnum(a.b);", "var a$b$c = {d: 1};var a$b = {c: a$b$c}; a$b$c.d; " + "searchEnum(a$b)"); } public void testAliasCreatedForPropertyOfEnumOfObjects() { test("var a = {}; " + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c;" + "searchEnum(a.b.c);", "var a$b$c = {d: 1}; a$b$c; searchEnum(a$b$c);"); } public void testAliasCreatedForPropertyOfEnumOfObjects2() { test("var a = {}; " + "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;" + "searchEnum(a.b.c);", "var a$b$c = {d: 1}; a$b$c.d; searchEnum(a$b$c);"); } public void testMisusedEnumTag() { testSame("var a = {}; var d = a; a.b = function() {};" + "/** @enum */ a.b.c = 0; a.b.c;"); } public void testMisusedConstructorTag() { testSame("var a = {}; var d = a; a.b = function() {};" + "/** @constructor */ a.b.c = 0; a.b.c;"); } public void testAliasCreatedForFunctionDepth1_1() { testSame("var a = function(){}; a.b = 1; var c = a; c.b = 2; a.b != c.b;"); } public void testAliasCreatedForCtorDepth1_1() { // A constructor's properties *are* collapsed even if the function is // referenced in a such a way that an alias is created for it, // since a function with custom properties is considered a class and its // non-prototype properties are considered static methods and variables. // People don't typically iterate through static members of a class or // refer to them using an alias for the class name. test("/** @constructor */ var a = function(){}; a.b = 1; " + "var c = a; c.b = 2; a.b != c.b;", "var a = function(){}; var a$b = 1; var c = a; c.b = 2; a$b != c.b;"); } public void testAliasCreatedForFunctionDepth1_2() { testSame("var a = function(){}; a.b = 1; f(a); a.b;"); } public void testAliasCreatedForCtorDepth1_2() { test("/** @constructor */ var a = function(){}; a.b = 1; f(a); a.b;", "var a = function(){}; var a$b = 1; f(a); a$b;"); } public void testAliasCreatedForFunctionDepth1_3() { testSame("var a = function(){}; a.b = 1; new f(a); a.b;"); } public void testAliasCreatedForCtorDepth1_3() { test("/** @constructor */ var a = function(){}; a.b = 1; new f(a); a.b;", "var a = function(){}; var a$b = 1; new f(a); a$b;"); } public void testAliasCreatedForFunctionDepth2() { test( "var a = {}; a.b = function() {}; a.b.c = 1; var d = a.b;" + "a.b.c != d.c;", "var a$b = function() {}; a$b.c = 1; var d = a$b;" + "a$b.c != d.c;"); } public void testAliasCreatedForCtorDepth2() { test("var a = {}; /** @constructor */ a.b = function() {}; " + "a.b.c = 1; var d = a.b;" + "a.b.c != d.c;", "var a$b = function() {}; var a$b$c = 1; var d = a$b;" + "a$b$c != d.c;"); } public void testAliasCreatedForClassDepth1_1() { // A class's name is always collapsed, even if one of its prefixes is // referenced in a such a way that an alias is created for it. test("var a = {}; /** @constructor */ a.b = function(){};" + "var c = a; c.b = 0; a.b != c.b;", "var a = {}; var a$b = function(){};" + "var c = a; c.b = 0; a$b != c.b;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassDepth1_2() { test("var a = {}; /** @constructor */ a.b = function(){}; f(a); a.b;", "var a = {}; var a$b = function(){}; f(a); a$b;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassDepth1_3() { test("var a = {}; /** @constructor */ a.b = function(){}; new f(a); a.b;", "var a = {}; var a$b = function(){}; new f(a); a$b;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassDepth2_1() { test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + "var d = a.b; a.b.c != d.c;", "var a$b = {}; var a$b$c = function(){};" + "var d = a$b; a$b$c != d.c;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassDepth2_2() { test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + "f(a.b); a.b.c;", "var a$b = {}; var a$b$c = function(){}; f(a$b); a$b$c;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassDepth2_3() { test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};" + "new f(a.b); a.b.c;", "var a$b = {}; var a$b$c = function(){}; new f(a$b); a$b$c;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasCreatedForClassProperty() { test("var a = {}; /** @constructor */ a.b = function(){};" + "a.b.c = {d: 3}; new f(a.b.c); a.b.c.d;", "var a$b = function(){}; var a$b$c = {d:3}; new f(a$b$c); a$b$c.d;"); } public void testNestedObjLit() { test("var a = {}; a.b = {f: 0, c: {d: 1}}; var e = a.b.c.d", "var a$b$f = 0; var a$b$c$d = 1; var e = a$b$c$d;"); } public void testObjLitDeclarationUsedInSameVarList() { // The collapsed properties must defined in the same place in the var list // where they were originally defined (and not, for example, at the end). test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c;", "var a$b = {}; var a$c = {}; var d = a$b; var e = a$c;"); } public void testPropGetInsideAnObjLit() { test("var x = {}; x.y = 1; var a = {}; a.b = {c: x.y}", "var x$y = 1; var a$b$c = x$y;"); } public void testObjLitWithQuotedKeyThatDoesNotGetRead() { test("var a = {}; a.b = {c: 0, 'd': 1}; var e = a.b.c;", "var a$b$c = 0; var a$b$d = 1; var e = a$b$c;"); } public void testObjLitWithQuotedKeyThatGetsRead() { test("var a = {}; a.b = {c: 0, 'd': 1}; var e = a.b['d'];", "var a$b = {c: 0, 'd': 1}; var e = a$b['d'];"); } public void testFunctionWithQuotedPropertyThatDoesNotGetRead() { test("var a = {}; a.b = function() {}; a.b['d'] = 1;", "var a$b = function() {}; a$b['d'] = 1;"); } public void testFunctionWithQuotedPropertyThatGetsRead() { test("var a = {}; a.b = function() {}; a.b['d'] = 1; f(a.b['d']);", "var a$b = function() {}; a$b['d'] = 1; f(a$b['d']);"); } public void testObjLitAssignedToMultipleNames1() { // An object literal that's assigned to multiple names isn't collapsed. testSame("var a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;"); } public void testObjLitAssignedToMultipleNames2() { testSame("a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;"); } public void testObjLitRedefinedInGlobalScope() { testSame("a = {b: 0}; a = {c: 1}; var d = a.b; var e = a.c;"); } public void testObjLitRedefinedInLocalScope() { test("var a = {}; a.b = {c: 0}; function d() { a.b = {c: 1}; } e(a.b.c);", "var a$b = {c: 0}; function d() { a$b = {c: 1}; } e(a$b.c);"); } public void testObjLitAssignedInTernaryExpression1() { testSame("a = x ? {b: 0} : d; var c = a.b;"); } public void testObjLitAssignedInTernaryExpression2() { testSame("a = x ? {b: 0} : {b: 1}; var c = a.b;"); } public void testGlobalVarSetToObjLitConditionally1() { testSame("var a; if (x) a = {b: 0}; var c = x ? a.b : 0;"); } public void testGlobalVarSetToObjLitConditionally1b() { test("if (x) var a = {b: 0}; var c = x ? a.b : 0;", "if (x) var a$b = 0; var c = x ? a$b : 0;"); } public void testGlobalVarSetToObjLitConditionally2() { test("if (x) var a = {b: 0}; var c = a.b; var d = a.c;", "if (x){ var a$b = 0; var a = {}; }var c = a$b; var d = a.c;"); } public void testGlobalVarSetToObjLitConditionally3() { testSame("var a; if (x) a = {b: 0}; else a = {b: 1}; var c = a.b;"); } public void testObjectPropertySetToObjLitConditionally() { test("var a = {}; if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;", "if (x){ var a$b$c = 0; var a$b = {} } var d = a$b ? a$b$c : 0;"); } public void testFunctionPropertySetToObjLitConditionally() { test("function a() {} if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;", "function a() {} if (x){ var a$b$c = 0; var a$b = {} }" + "var d = a$b ? a$b$c : 0;"); } public void testPrototypePropertySetToAnObjectLiteral() { test("var a = {b: function(){}}; a.b.prototype.c = {d: 0};", "var a$b = function(){}; a$b.prototype.c = {d: 0};"); } public void testObjectPropertyResetInLocalScope() { test("var z = {}; z.a = 0; function f() {z.a = 5; return z.a}", "var z$a = 0; function f() {z$a = 5; return z$a}"); } public void testFunctionPropertyResetInLocalScope() { test("function z() {} z.a = 0; function f() {z.a = 5; return z.a}", "function z() {} var z$a = 0; function f() {z$a = 5; return z$a}"); } public void testNamespaceResetInGlobalScope1() { test("var a = {}; /** @constructor */a.b = function() {}; a = {};", "var a = {}; var a$b = function() {}; a = {};", null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); } public void testNamespaceResetInGlobalScope2() { test("var a = {}; a = {}; /** @constructor */a.b = function() {};", "var a = {}; a = {}; var a$b = function() {};", null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); } public void testNamespaceResetInLocalScope1() { test("var a = {}; /** @constructor */a.b = function() {};" + " function f() { a = {}; }", "var a = {};var a$b = function() {};" + " function f() { a = {}; }", null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); } public void testNamespaceResetInLocalScope2() { test("var a = {}; function f() { a = {}; }" + " /** @constructor */a.b = function() {};", "var a = {}; function f() { a = {}; }" + " var a$b = function() {};", null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); } public void testNamespaceDefinedInLocalScope() { test("var a = {}; (function() { a.b = {}; })();" + " /** @constructor */a.b.c = function() {};", "var a$b; (function() { a$b = {}; })(); var a$b$c = function() {};"); } public void testAddPropertyToObjectInLocalScopeDepth1() { test("var a = {b: 0}; function f() { a.c = 5; return a.c; }", "var a$b = 0; var a$c; function f() { a$c = 5; return a$c; }"); } public void testAddPropertyToObjectInLocalScopeDepth2() { test("var a = {}; a.b = {}; (function() {a.b.c = 0;})(); x = a.b.c;", "var a$b$c; (function() {a$b$c = 0;})(); x = a$b$c;"); } public void testAddPropertyToFunctionInLocalScopeDepth1() { test("function a() {} function f() { a.c = 5; return a.c; }", "function a() {} var a$c; function f() { a$c = 5; return a$c; }"); } public void testAddPropertyToFunctionInLocalScopeDepth2() { test("var a = {}; a.b = function() {}; function f() {a.b.c = 0;}", "var a$b = function() {}; var a$b$c; function f() {a$b$c = 0;}"); } public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth1() { testSame("var a = {}; var c = a; (function() {a.b = 0;})(); a.b;"); } public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth1() { testSame("function a() {} var c = a; (function() {a.b = 0;})(); a.b;"); } public void testAddPropertyToUncollapsibleNamedCtorInLocalScopeDepth1() { testSame( "/** @constructor */ function a() {} var a$b; var c = a; " + "(function() {a$b = 0;})(); a$b;"); } public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth1() { test("/** @constructor */ var a = function() {}; var c = a; " + "(function() {a.b = 0;})(); a.b;", "var a = function() {}; var a$b; " + "var c = a; (function() {a$b = 0;})(); a$b;"); } public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth2() { test("var a = {}; a.b = {}; var d = a.b;" + "(function() {a.b.c = 0;})(); a.b.c;", "var a$b = {}; var d = a$b;" + "(function() {a$b.c = 0;})(); a$b.c;"); } public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth2() { test("var a = {}; a.b = function (){}; var d = a.b;" + "(function() {a.b.c = 0;})(); a.b.c;", "var a$b = function (){}; var d = a$b;" + "(function() {a$b.c = 0;})(); a$b.c;"); } public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth2() { test("var a = {}; /** @constructor */ a.b = function (){}; var d = a.b;" + "(function() {a.b.c = 0;})(); a.b.c;", "var a$b = function (){}; var a$b$c; var d = a$b;" + "(function() {a$b$c = 0;})(); a$b$c;"); } public void testPropertyOfChildFuncOfUncollapsibleObjectDepth1() { testSame("var a = {}; var c = a; a.b = function (){}; a.b.x = 0; a.b.x;"); } public void testPropertyOfChildFuncOfUncollapsibleObjectDepth2() { test("var a = {}; a.b = {}; var c = a.b;" + "a.b.c = function (){}; a.b.c.x = 0; a.b.c.x;", "var a$b = {}; var c = a$b;" + "a$b.c = function (){}; a$b.c.x = 0; a$b.c.x;"); } public void testAddPropertyToChildFuncOfUncollapsibleObjectInLocalScope() { testSame("var a = {}; a.b = function (){}; a.b.x = 0;" + "var c = a; (function() {a.b.y = 1;})(); a.b.x; a.b.y;"); } public void testAddPropertyToChildTypeOfUncollapsibleObjectInLocalScope() { test("var a = {}; /** @constructor */ a.b = function (){}; a.b.x = 0;" + "var c = a; (function() {a.b.y = 1;})(); a.b.x; a.b.y;", "var a = {}; var a$b = function (){}; var a$b$y; var a$b$x = 0;" + "var c = a; (function() {a$b$y = 1;})(); a$b$x; a$b$y;", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAddPropertyToChildOfUncollapsibleFunctionInLocalScope() { testSame( "function a() {} a.b = {x: 0}; var c = a;" + "(function() {a.b.y = 0;})(); a.b.y;"); } public void testAddPropertyToChildOfUncollapsibleCtorInLocalScope() { test("/** @constructor */ var a = function() {}; a.b = {x: 0}; var c = a;" + "(function() {a.b.y = 0;})(); a.b.y;", "var a = function() {}; var a$b$x = 0; var a$b$y; var c = a;" + "(function() {a$b$y = 0;})(); a$b$y;"); } public void testResetObjectPropertyInLocalScope() { test("var a = {b: 0}; a.c = 1; function f() { a.c = 5; }", "var a$b = 0; var a$c = 1; function f() { a$c = 5; }"); } public void testResetFunctionPropertyInLocalScope() { test("function a() {}; a.c = 1; function f() { a.c = 5; }", "function a() {}; var a$c = 1; function f() { a$c = 5; }"); } public void testGlobalNameReferencedInLocalScopeBeforeDefined1() { // Because referencing global names earlier in the source code than they're // defined is such a common practice, we collapse them even though a runtime // exception could result (in the off-chance that the function gets called // before the alias variable is defined). test("var a = {b: 0}; function f() { a.c = 5; } a.c = 1;", "var a$b = 0; function f() { a$c = 5; } var a$c = 1;"); } public void testGlobalNameReferencedInLocalScopeBeforeDefined2() { test("var a = {b: 0}; function f() { return a.c; } a.c = 1;", "var a$b = 0; function f() { return a$c; } var a$c = 1;"); } public void testTwiceDefinedGlobalNameDepth1_1() { testSame("var a = {}; function f() { a.b(); }" + "a = function() {}; a.b = function() {};"); } public void testTwiceDefinedGlobalNameDepth1_2() { testSame("var a = {}; /** @constructor */ a = function() {};" + "a.b = {}; a.b.c = 0; function f() { a.b.d = 1; }"); } public void testTwiceDefinedGlobalNameDepth2() { test("var a = {}; a.b = {}; function f() { a.b.c(); }" + "a.b = function() {}; a.b.c = function() {};", "var a$b = {}; function f() { a$b.c(); }" + "a$b = function() {}; a$b.c = function() {};"); } public void testFunctionCallDepth1() { test("var a = {}; a.b = function(){}; var c = a.b();", "var a$b = function(){}; var c = a$b()"); } public void testFunctionCallDepth2() { test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.c();", "var a$b$c = function(){}; a$b$c();"); } public void testFunctionAlias() { test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.d = a.b.c;", "var a$b$c = function(){}; var a$b$d = a$b$c;"); } public void testCallToRedefinedFunction() { test("var a = {}; a.b = function(){}; a.b = function(){}; a.b();", "var a$b = function(){}; a$b = function(){}; a$b();"); } public void testCollapsePrototypeName() { test("var a = {}; a.b = {}; a.b.c = function(){}; " + "a.b.c.prototype.d = function(){}; (new a.b.c()).d();", "var a$b$c = function(){}; a$b$c.prototype.d = function(){}; " + "new a$b$c().d();"); } public void testReferencedPrototypeProperty() { test("var a = {b: {}}; a.b.c = function(){}; a.b.c.prototype.d = {};" + "e = a.b.c.prototype.d;", "var a$b$c = function(){}; a$b$c.prototype.d = {};" + "e = a$b$c.prototype.d;"); } public void testSetStaticAndPrototypePropertiesOnFunction() { test("var a = {}; a.b = function(){}; a.b.prototype.d = 0; a.b.c = 1;", "var a$b = function(){}; a$b.prototype.d = 0; var a$b$c = 1;"); } public void testReadUndefinedPropertyDepth1() { test("var a = {b: 0}; var c = a.d;", "var a$b = 0; var a = {}; var c = a.d;"); } public void testReadUndefinedPropertyDepth2() { test("var a = {b: {c: 0}}; f(a.b.c); f(a.b.d);", "var a$b$c = 0; var a$b = {}; f(a$b$c); f(a$b.d);"); } public void testCallUndefinedMethodOnObjLitDepth1() { test("var a = {b: 0}; a.c();", "var a$b = 0; var a = {}; a.c();"); } public void testCallUndefinedMethodOnObjLitDepth2() { test("var a = {b: {}}; a.b.c = function() {}; a.b.c(); a.b.d();", "var a$b = {}; var a$b$c = function() {}; a$b$c(); a$b.d();"); } public void testPropertiesOfAnUndefinedVar() { testSame("a.document = d; f(a.document.innerHTML);"); } public void testPropertyOfAnObjectThatIsNeitherFunctionNorObjLit() { testSame("var a = window; a.document = d; f(a.document)"); } public void testStaticFunctionReferencingThis1() { // Note: Google's JavaScript Style Guide says to avoid using the 'this' // keyword in a static function. test("var a = {}; a.b = function() {this.c}; var d = a.b;", "var a$b = function() {this.c}; var d = a$b;", null, UNSAFE_THIS); } public void testStaticFunctionReferencingThis2() { // This gives no warning, because "this" is in a scope whose name is not // getting collapsed. test("var a = {}; " + "a.b = function() { return function(){ return this; }; };", "var a$b = function() { return function(){ return this; }; };"); } public void testStaticFunctionReferencingThis3() { test("var a = {b: function() {this.c}};", "var a$b = function() { this.c };", null, UNSAFE_THIS); } public void testStaticFunctionReferencingThis4() { test("var a = {/** @this {Element} */ b: function() {this.c}};", "var a$b = function() { this.c };"); } public void testPrototypeMethodReferencingThis() { testSame("var A = function(){}; A.prototype = {b: function() {this.c}};"); } public void testConstructorReferencingThis() { test("var a = {}; " + "/** @constructor */ a.b = function() { this.a = 3; };", "var a$b = function() { this.a = 3; };"); } public void testSafeReferenceOfThis() { test("var a = {}; " + "/** @this {Object} */ a.b = function() { this.a = 3; };", "var a$b = function() { this.a = 3; };"); } public void testGlobalFunctionReferenceOfThis() { testSame("var a = function() { this.a = 3; };"); } public void testFunctionGivenTwoNames() { // It's okay to collapse f's properties because g is not added to the // global scope as an alias for f. (Try it in your browser.) test("var f = function g() {}; f.a = 1; h(f.a);", "var f = function g() {}; var f$a = 1; h(f$a);"); } public void testObjLitWithUsedNumericKey() { testSame("a = {40: {}, c: {}}; var d = a[40]; var e = a.c;"); } public void testObjLitWithUnusedNumericKey() { test("var a = {40: {}, c: {}}; var e = a.c;", "var a$1 = {}; var a$c = {}; var e = a$c"); } public void testObjLitWithNonIdentifierKeys() { testSame("a = {' ': 0, ',': 1}; var c = a[' '];"); } public void testChainedAssignments1() { test("var x = {}; x.y = a = 0;", "var x$y = a = 0;"); } public void testChainedAssignments2() { test("var x = {}; x.y = a = b = c();", "var x$y = a = b = c();"); } public void testChainedAssignments3() { test("var x = {y: 1}; a = b = x.y;", "var x$y = 1; a = b = x$y;"); } public void testChainedAssignments4() { test("var x = {}; a = b = x.y;", "var x = {}; a = b = x.y;"); } public void testChainedAssignments5() { test("var x = {}; a = x.y = 0;", "var x$y; a = x$y = 0;"); } public void testChainedAssignments6() { test("var x = {}; a = x.y = b = c();", "var x$y; a = x$y = b = c();"); } public void testChainedAssignments7() { test("var x = {}; a = x.y = {}; /** @constructor */ x.y.z = function() {};", "var x$y; a = x$y = {}; var x$y$z = function() {};", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testChainedVarAssignments1() { test("var x = {y: 1}; var a = x.y = 0;", "var x$y = 1; var a = x$y = 0;"); } public void testChainedVarAssignments2() { test("var x = {y: 1}; var a = x.y = b = 0;", "var x$y = 1; var a = x$y = b = 0;"); } public void testChainedVarAssignments3() { test("var x = {y: {z: 1}}; var b = 0; var a = x.y.z = 1; var c = 2;", "var x$y$z = 1; var b = 0; var a = x$y$z = 1; var c = 2;"); } public void testChainedVarAssignments4() { test("var x = {}; var a = b = x.y = 0;", "var x$y; var a = b = x$y = 0;"); } public void testChainedVarAssignments5() { test("var x = {y: {}}; var a = b = x.y.z = 0;", "var x$y$z; var a = b = x$y$z = 0;"); } public void testPeerAndSubpropertyOfUncollapsibleProperty() { test("var x = {}; var a = x.y = 0; x.w = 1; x.y.z = 2;" + "b = x.w; c = x.y.z;", "var x$y; var a = x$y = 0; var x$w = 1; x$y.z = 2;" + "b = x$w; c = x$y.z;"); } public void testComplexAssignmentAfterInitialAssignment() { test("var d = {}; d.e = {}; d.e.f = 0; a = b = d.e.f = 1;", "var d$e$f = 0; a = b = d$e$f = 1;"); } public void testRenamePrefixOfUncollapsibleProperty() { test("var d = {}; d.e = {}; a = b = d.e.f = 0;", "var d$e$f; a = b = d$e$f = 0;"); } public void testNewOperator() { // Using the new operator on a name doesn't prevent its (static) properties // from getting collapsed. test("var a = {}; a.b = function() {}; a.b.c = 1; var d = new a.b();", "var a$b = function() {}; var a$b$c = 1; var d = new a$b();"); } public void testMethodCall() { test("var a = {}; a.b = function() {}; var d = a.b();", "var a$b = function() {}; var d = a$b();"); } public void testObjLitDefinedInLocalScopeIsLeftAlone() { test("var a = {}; a.b = function() {};" + "a.b.prototype.f_ = function() {" + " var x = { p: '', q: '', r: ''}; var y = x.q;" + "};", "var a$b = function() {};" + "a$b.prototype.f_ = function() {" + " var x = { p: '', q: '', r: ''}; var y = x.q;" + "};"); } public void testPropertiesOnBothSidesOfAssignment() { // This verifies that replacements are done in the right order. Collapsing // the l-value in an assignment affects the parse tree immediately above // the r-value, so we update all rvalues before any lvalues. test("var a = {b: 0}; a.c = a.b;", "var a$b = 0; var a$c = a$b;"); } public void testCallOnUndefinedProperty() { // The "inherits" property is not explicitly defined on a.b anywhere, but // it is accessed as though it certainly exists (it is called), so we infer // that it must be an uncollapsible property that has come into existence // some other way. test("var a = {}; a.b = function(){}; a.b.inherits(x);", "var a$b = function(){}; a$b.inherits(x);"); } public void testGetPropOnUndefinedProperty() { // The "superClass_" property is not explicitly defined on a.b anywhere, // but it is accessed as though it certainly exists (a subproperty of it // is accessed), so we infer that it must be an uncollapsible property that // has come into existence some other way. test("var a = {b: function(){}}; a.b.prototype.c =" + "function() { a.b.superClass_.c.call(this); }", "var a$b = function(){}; a$b.prototype.c =" + "function() { a$b.superClass_.c.call(this); }"); } public void testLocalAlias1() { test("var a = {b: 3}; function f() { var x = a; f(x.b); }", "var a$b = 3; function f() { var x = null; f(a$b); }"); } public void testLocalAlias2() { test("var a = {b: 3, c: 4}; function f() { var x = a; f(x.b); f(x.c);}", "var a$b = 3; var a$c = 4; " + "function f() { var x = null; f(a$b); f(a$c);}"); } public void testLocalAlias3() { test("var a = {b: 3, c: {d: 5}}; " + "function f() { var x = a; f(x.b); f(x.c); f(x.c.d); }", "var a$b = 3; var a$c = {d: 5}; " + "function f() { var x = null; f(a$b); f(a$c); f(a$c.d);}"); } public void testLocalAlias4() { test("var a = {b: 3}; var c = {d: 5}; " + "function f() { var x = a; var y = c; f(x.b); f(y.d); }", "var a$b = 3; var c$d = 5; " + "function f() { var x = null; var y = null; f(a$b); f(c$d);}"); } public void testLocalAlias5() { test("var a = {b: {c: 5}}; " + "function f() { var x = a; var y = x.b; f(a.b.c); f(y.c); }", "var a$b$c = 5; " + "function f() { var x = null; var y = null; f(a$b$c); f(a$b$c);}"); } public void testLocalAlias6() { test("var a = {b: 3}; function f() { var x = a; if (x.b) { f(x.b); } }", "var a$b = 3; function f() { var x = null; if (a$b) { f(a$b); } }"); } public void testLocalAlias7() { test("var a = {b: {c: 5}}; function f() { var x = a.b; f(x.c); }", "var a$b$c = 5; function f() { var x = null; f(a$b$c); }"); } public void testGlobalWriteToAncestor() { testSame("var a = {b: 3}; function f() { var x = a; f(a.b); } a = 5;"); } public void testGlobalWriteToNonAncestor() { test("var a = {b: 3}; function f() { var x = a; f(a.b); } a.b = 5;", "var a$b = 3; function f() { var x = null; f(a$b); } a$b = 5;"); } public void testLocalWriteToAncestor() { testSame("var a = {b: 3}; function f() { a = 5; var x = a; f(a.b); } "); } public void testLocalWriteToNonAncestor() { test("var a = {b: 3}; " + "function f() { a.b = 5; var x = a; f(a.b); }", "var a$b = 3; function f() { a$b = 5; var x = null; f(a$b); } "); } public void testNonWellformedAlias1() { testSame("var a = {b: 3}; function f() { f(x); var x = a; f(x.b); }"); } public void testNonWellformedAlias2() { testSame("var a = {b: 3}; " + "function f() { if (false) { var x = a; f(x.b); } f(x); }"); } public void testLocalAliasOfAncestor() { testSame("var a = {b: {c: 5}}; function g() { f(a); } " + "function f() { var x = a.b; f(x.c); }"); } public void testGlobalAliasOfAncestor() { testSame("var a = {b: {c: 5}}; var y = a; " + "function f() { var x = a.b; f(x.c); }"); } public void testLocalAliasOfOtherName() { testSame("var foo = function() { return {b: 3}; };" + "var a = foo(); a.b = 5; " + "function f() { var x = a.b; f(x); }"); } public void testLocalAliasOfFunction() { test("var a = function() {}; a.b = 5; " + "function f() { var x = a.b; f(x); }", "var a = function() {}; var a$b = 5; " + "function f() { var x = null; f(a$b); }"); } public void testNoInlineGetpropIntoCall() { test("var b = x; function f() { var a = b; a(); }", "var b = x; function f() { var a = null; b(); }"); test("var b = {}; b.c = x; function f() { var a = b.c; a(); }", "var b$c = x; function f() { var a = null; b$c(); }"); } public void testInlineAliasWithModifications() { testSame("var x = 10; function f() { var y = x; x++; alert(y)} "); testSame("var x = 10; function f() { var y = x; x+=1; alert(y)} "); test("var x = {}; x.x = 10; function f() {var y=x.x; x.x++; alert(y)}", "var x$x = 10; function f() {var y=x$x; x$x++; alert(y)}"); test("var x = {}; x.x = 10; function f() {var y=x.x; x.x+=1; alert(y)}", "var x$x = 10; function f() {var y=x$x; x$x+=1; alert(y)}"); } public void testCollapsePropertyOnExternType() { collapsePropertiesOnExternTypes = true; test("String.myFunc = function() {}; String.myFunc();", "var String$myFunc = function() {}; String$myFunc()"); } public void testCollapseForEachWithoutExterns() { collapsePropertiesOnExternTypes = true; test("/** @constructor */function Array(){};\n", "if (!Array.forEach) {\n" + " Array.forEach = function() {};\n" + "}", "if (!Array$forEach) {\n" + " var Array$forEach = function() {};\n" + "}", null, null); } public void testNoCollapseForEachInExterns() { collapsePropertiesOnExternTypes = true; test("/** @constructor */ function Array() {}" + "Array.forEach = function() {}", "if (!Array.forEach) {\n" + " Array.forEach = function() {};\n" + "}", "if (!Array.forEach) {\n" + " Array.forEach = function() {};\n" + "}", null, null); } public void testDoNotCollapsePropertyOnExternType() { collapsePropertiesOnExternTypes = false; test("String.myFunc = function() {}; String.myFunc()", "String.myFunc = function() {}; String.myFunc()"); } public void testBug1704733() { String prelude = "function protect(x) { return x; }" + "function O() {}" + "protect(O).m1 = function() {};" + "protect(O).m2 = function() {};" + "protect(O).m3 = function() {};"; testSame(prelude + "alert(O.m1); alert(O.m2()); alert(!O.m3);"); } public void testBug1956277() { test("var CONST = {}; CONST.URL = 3;", "var CONST$URL = 3;"); } public void testBug1974371() { test( "/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}};" + "for (var key in Foo) {}", "var Foo$A = {c: 2}; var Foo$B = {c: 3};" + "var Foo = {A: Foo$A, B: Foo$B};" + "for (var key in Foo) {}"); } private final String COMMON_ENUM = "/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}};"; public void testEnumOfObjects1() { test( COMMON_ENUM + "for (var key in Foo.A) {}", "var Foo$A = {c: 2}; var Foo$B$c = 3; for (var key in Foo$A) {}"); } public void testEnumOfObjects2() { test( COMMON_ENUM + "foo(Foo.A.c);", "var Foo$A$c = 2; var Foo$B$c = 3; foo(Foo$A$c);"); } public void testEnumOfObjects3() { test( "var x = {c: 2}; var y = {c: 3};" + "/** @enum {Object} */ var Foo = {A: x, B: y};" + "for (var key in Foo) {}", "var x = {c: 2}; var y = {c: 3};" + "var Foo$A = x; var Foo$B = y; var Foo = {A: Foo$A, B: Foo$B};" + "for (var key in Foo) {}"); } public void testEnumOfObjects4() { // Note that this produces bad code, but that's OK, because // checkConsts will yell at you for reassigning an enum value. // (enum values have to be constant). test( COMMON_ENUM + "for (var key in Foo) {} Foo.A = 3; alert(Foo.A);", "var Foo$A = {c: 2}; var Foo$B = {c: 3};" + "var Foo = {A: Foo$A, B: Foo$B};" + "for (var key in Foo) {} Foo$A = 3; alert(Foo$A);"); } public void testObjectOfObjects1() { // Basically the same as testEnumOfObjects4, but without the // constant enum values. testSame( "var Foo = {a: {c: 2}, b: {c: 3}};" + "for (var key in Foo) {} Foo.a = 3; alert(Foo.a);"); } public void testReferenceInAnonymousObject0() { test("var a = {};" + "a.b = function(){};" + "a.b.prototype.c = function(){};" + "var d = a.b.prototype.c;", "var a$b = function(){};" + "a$b.prototype.c = function(){};" + "var d = a$b.prototype.c;"); } public void testReferenceInAnonymousObject1() { test("var a = {};" + "a.b = function(){};" + "var d = a.b.prototype.c;", "var a$b = function(){};" + "var d = a$b.prototype.c;"); } public void testReferenceInAnonymousObject2() { test("var a = {};" + "a.b = function(){};" + "a.b.prototype.c = function(){};" + "var d = {c: a.b.prototype.c};", "var a$b = function(){};" + "a$b.prototype.c = function(){};" + "var d$c = a$b.prototype.c;"); } public void testReferenceInAnonymousObject3() { test("function CreateClass(a$$1) {}" + "var a = {};" + "a.b = function(){};" + "a.b.prototype.c = function(){};" + "a.d = CreateClass({c: a.b.prototype.c});", "function CreateClass(a$$1) {}" + "var a$b = function(){};" + "a$b.prototype.c = function(){};" + "var a$d = CreateClass({c: a$b.prototype.c});"); } public void testReferenceInAnonymousObject4() { test("function CreateClass(a) {}" + "var a = {};" + "a.b = CreateClass({c: function() {}});" + "a.d = CreateClass({c: a.b.c});", "function CreateClass(a$$1) {}" + "var a$b = CreateClass({c: function() {}});" + "var a$d = CreateClass({c: a$b.c});"); } public void testReferenceInAnonymousObject5() { test("function CreateClass(a) {}" + "var a = {};" + "a.b = CreateClass({c: function() {}});" + "a.d = CreateClass({c: a.b.prototype.c});", "function CreateClass(a$$1) {}" + "var a$b = CreateClass({c: function() {}});" + "var a$d = CreateClass({c: a$b.prototype.c});"); } public void testCrashInCommaOperator() { test("var a = {}; a.b = function() {},a.b();", "var a$b; a$b=function() {},a$b();"); } public void testCrashInNestedAssign() { test("var a = {}; if (a.b = function() {}) a.b();", "var a$b; if (a$b=function() {}) { a$b(); }"); } public void testTwinReferenceCancelsChildCollapsing() { test("var a = {}; if (a.b = function() {}) { a.b.c = 3; a.b(a.b.c); }", "var a$b; if (a$b = function() {}) { a$b.c = 3; a$b(a$b.c); }"); } public void testPropWithDollarSign() { test("var a = {$: 3}", "var a$$0 = 3;"); } public void testPropWithDollarSign2() { test("var a = {$: function(){}}", "var a$$0 = function(){};"); } public void testPropWithDollarSign3() { test("var a = {b: {c: 3}, b$c: function(){}}", "var a$b$c = 3; var a$b$0c = function(){};"); } public void testPropWithDollarSign4() { test("var a = {$$: {$$$: 3}};", "var a$$0$0$$0$0$0 = 3;"); } public void testPropWithDollarSign5() { test("var a = {b: {$0c: true}, b$0c: false};", "var a$b$$00c = true; var a$b$00c = false;"); } public void testConstKey() { test("var foo = {A: 3};", "var foo$A = 3;"); } public void testPropertyOnGlobalCtor() { test("/** @constructor */ function Map() {} Map.foo = 3; Map;", "function Map() {} var Map$foo = 3; Map;"); } public void testPropertyOnGlobalInterface() { test("/** @interface */ function Map() {} Map.foo = 3; Map;", "function Map() {} var Map$foo = 3; Map;"); } public void testPropertyOnGlobalFunction() { testSame("function Map() {} Map.foo = 3; Map;"); } public void testIssue389() { test( "function alias() {}" + "var dojo = {};" + "dojo.gfx = {};" + "dojo.declare = function() {};" + "/** @constructor */" + "dojo.gfx.Shape = function() {};" + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + "alias(dojo);", "function alias() {}" + "var dojo = {};" + "dojo.gfx = {};" + "dojo.declare = function() {};" + "/** @constructor */" + "var dojo$gfx$Shape = function() {};" + "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');" + "alias(dojo);", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAliasedTopLevelName() { testSame( "function alias() {}" + "var dojo = {};" + "dojo.gfx = {};" + "dojo.declare = function() {};" + "dojo.gfx.Shape = {SQUARE: 2};" + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + "alias(dojo);" + "alias(dojo$gfx$Shape$SQUARE);"); } public void testAliasedTopLevelEnum() { test( "function alias() {}" + "var dojo = {};" + "dojo.gfx = {};" + "dojo.declare = function() {};" + "/** @enum {number} */" + "dojo.gfx.Shape = {SQUARE: 2};" + "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');" + "alias(dojo);" + "alias(dojo.gfx.Shape.SQUARE);", "function alias() {}" + "var dojo = {};" + "dojo.gfx = {};" + "dojo.declare = function() {};" + "/** @constructor */" + "var dojo$gfx$Shape = {SQUARE: 2};" + "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');" + "alias(dojo);" + "alias(dojo$gfx$Shape.SQUARE);", null, CollapseProperties.UNSAFE_NAMESPACE_WARNING); } public void testAssignFunctionBeforeDefinition() { testSame( "f = function() {};" + "var f = null;"); } public void testObjectLitBeforeDefinition() { testSame( "a = {b: 3};" + "var a = null;" + "this.c = a.b;"); } public void testTypedef1() { test("var foo = {};" + "/** @typedef {number} */ foo.Baz;", "var foo = {}; var foo$Baz;"); } public void testTypedef2() { test("var foo = {};" + "/** @typedef {number} */ foo.Bar.Baz;" + "foo.Bar = function() {};", "var foo$Bar$Baz; var foo$Bar = function(){};"); } public void testDelete1() { testSame( "var foo = {};" + "foo.bar = 3;" + "delete foo.bar;"); } public void testDelete2() { test( "var foo = {};" + "foo.bar = 3;" + "foo.baz = 3;" + "delete foo.bar;", "var foo = {};" + "foo.bar = 3;" + "var foo$baz = 3;" + "delete foo.bar;"); } public void testDelete3() { testSame( "var foo = {bar: 3};" + "delete foo.bar;"); } public void testDelete4() { test( "var foo = {bar: 3, baz: 3};" + "delete foo.bar;", "var foo$baz=3;var foo={bar:3};delete foo.bar"); } public void testDelete5() { test( "var x = {};" + "x.foo = {};" + "x.foo.bar = 3;" + "delete x.foo.bar;", "var x$foo = {};" + "x$foo.bar = 3;" + "delete x$foo.bar;"); } public void testDelete6() { test( "var x = {};" + "x.foo = {};" + "x.foo.bar = 3;" + "x.foo.baz = 3;" + "delete x.foo.bar;", "var x$foo = {};" + "x$foo.bar = 3;" + "var x$foo$baz = 3;" + "delete x$foo.bar;"); } public void testDelete7() { test( "var x = {};" + "x.foo = {bar: 3};" + "delete x.foo.bar;", "var x$foo = {bar: 3};" + "delete x$foo.bar;"); } public void testDelete8() { test( "var x = {};" + "x.foo = {bar: 3, baz: 3};" + "delete x.foo.bar;", "var x$foo$baz = 3; var x$foo = {bar: 3};" + "delete x$foo.bar;"); } public void testDelete9() { testSame( "var x = {};" + "x.foo = {};" + "x.foo.bar = 3;" + "delete x.foo;"); } public void testDelete10() { testSame( "var x = {};" + "x.foo = {bar: 3};" + "delete x.foo;"); } public void testDelete11() { // Constructors are always collapsed. test( "var x = {};" + "x.foo = {};" + "/** @constructor */ x.foo.Bar = function() {};" + "delete x.foo;", "var x = {};" + "x.foo = {};" + "var x$foo$Bar = function() {};" + "delete x.foo;", null, CollapseProperties.NAMESPACE_REDEFINED_WARNING); } public void testPreserveConstructorDoc() { test("var foo = {};" + "/** @constructor */\n" + "foo.bar = function() {}", "var foo$bar = function() {}"); Node root = getLastCompiler().getRoot(); Node fooBarNode = findQualifiedNameNode("foo$bar", root); Node varNode = fooBarNode.getParent(); assertTrue(varNode.isVar()); assertTrue(varNode.getJSDocInfo().isConstructor()); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PureFunctionIdentifierTest.java0000644000175000017500000012610712115204405031162 0ustar apoapo/* * 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.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import static com.google.javascript.jscomp.PureFunctionIdentifier.INVALID_NO_SIDE_EFFECT_ANNOTATION; import com.google.javascript.rhino.Node; import java.util.List; /** * Tests for {@link PureFunctionIdentifier} * */ public class PureFunctionIdentifierTest extends CompilerTestCase { List noSideEffectCalls = Lists.newArrayList(); List localResultCalls = Lists.newArrayList(); boolean regExpHaveSideEffects = true; private static String kExterns = CompilerTypeTestCase.DEFAULT_EXTERNS + "var window; window.setTimeout;" + "/**@nosideeffects*/ function externSENone(){}\n" + "/**@modifies{this}*/ function externSEThis(){}\n" + "/**@constructor\n" + " * @modifies{this}*/\n" + "function externObjSEThis(){}\n" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @modifies{this}\n" + " */\n" + "externObjSEThis.prototype.externObjSEThisMethod = function(s) {};" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @modifies{arguments}\n" + " */\n" + "externObjSEThis.prototype.externObjSEThisMethod2 = function(s) {};" + "/**@nosideeffects*/function Error(){}" + "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.partialFn;" + "externObj.partialSharedFn;" + "var externObj2;" + "externObj2.partialSharedFn = /**@nosideeffects*/function(){};" + "/**@constructor*/function externSefConstructor(){}" + "externSefConstructor.prototype.sefFnOfSefObj = function(){};" + "externSefConstructor.prototype.nsefFnOfSefObj = " + " /**@nosideeffects*/function(){};" + "externSefConstructor.prototype.externShared = function(){};" + "/**@constructor\n@nosideeffects*/function externNsefConstructor(){}" + "externNsefConstructor.prototype.sefFnOfNsefObj = function(){};" + "externNsefConstructor.prototype.nsefFnOfNsefObj = " + " /**@nosideeffects*/function(){};" + "externNsefConstructor.prototype.externShared = " + " /**@nosideeffects*/function(){};" + "/**@constructor\n@nosideeffects*/function externNsefConstructor2(){}" + "externNsefConstructor2.prototype.externShared = " + " /**@nosideeffects*/function(){};" + "externNsefConstructor.prototype.sharedPartialSef;" + "/**@nosideeffects*/externNsefConstructor.prototype.sharedPartialNsef;" + // An externs definition with a stub before. "/**@constructor*/function externObj3(){}" + "externObj3.prototype.propWithStubBefore;" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj3.prototype.propWithStubBefore = function(s) {};" + // useless JsDoc "/**\n" + " * @see {foo}\n" + " */\n" + "externObj3.prototype.propWithStubBeforeWithJSDoc;" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj3.prototype.propWithStubBeforeWithJSDoc = function(s) {};" + // An externs definition with a stub after. "/**@constructor*/function externObj4(){}" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj4.prototype.propWithStubAfter = function(s) {};" + "externObj4.prototype.propWithStubAfter;" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj4.prototype.propWithStubAfterWithJSDoc = function(s) {};" + // useless JsDoc "/**\n" + " * @see {foo}\n" + " */\n" + "externObj4.prototype.propWithStubAfterWithJSDoc;"; public PureFunctionIdentifierTest() { super(kExterns); enableTypeCheck(CheckLevel.ERROR); } @Override protected int getNumRepetitions() { // run pass once. return 1; } @Override protected void tearDown() throws Exception { super.tearDown(); noSideEffectCalls.clear(); localResultCalls.clear(); regExpHaveSideEffects = true; } public void testIssue303() throws Exception { checkMarkedCalls( "/** @constructor */ function F() {" + " var self = this;" + " window.setTimeout(function() {" + " window.location = self.location;" + " }, 0);" + "}" + "F.prototype.setLocation = function(x) {" + " this.location = x;" + "};" + "(new F()).setLocation('http://www.google.com/');", ImmutableList.of()); } public void testIssue303b() throws Exception { checkMarkedCalls( "/** @constructor */ function F() {" + " var self = this;" + " window.setTimeout(function() {" + " window.location = self.location;" + " }, 0);" + "}" + "F.prototype.setLocation = function(x) {" + " this.location = x;" + "};" + "function x() {" + " (new F()).setLocation('http://www.google.com/');" + "} window['x'] = x;", ImmutableList.of()); } public void testAnnotationInExterns_new1() throws Exception { checkMarkedCalls("externSENone()", ImmutableList.of("externSENone")); } public void testAnnotationInExterns_new2() throws Exception { checkMarkedCalls("externSEThis()", ImmutableList.of()); } public void testAnnotationInExterns_new3() throws Exception { checkMarkedCalls("new externObjSEThis()", ImmutableList.of("externObjSEThis")); } public void testAnnotationInExterns_new4() throws Exception { // The entire expression containing "externObjSEThisMethod" is considered // side-effect free in this context. checkMarkedCalls("new externObjSEThis().externObjSEThisMethod('')", ImmutableList.of( "externObjSEThis", "NEW STRING externObjSEThisMethod")); } public void testAnnotationInExterns_new5() throws Exception { checkMarkedCalls( "function f() { new externObjSEThis() };" + "f();", ImmutableList.of("externObjSEThis", "f")); } public void testAnnotationInExterns_new6() throws Exception { // While "externObjSEThisMethod" has modifies "this" // it does not have global side-effects with "this" is // a known local value. // TODO(johnlenz): "f" is side-effect free but we need // to propagate that "externObjSEThisMethod" is modifying // a local object. checkMarkedCalls( "function f() {" + " new externObjSEThis().externObjSEThisMethod('') " + "};" + "f();", ImmutableList.of( "externObjSEThis", "NEW STRING externObjSEThisMethod")); } public void testAnnotationInExterns_new7() throws Exception { // While "externObjSEThisMethod" has modifies "this" // it does not have global side-effects with "this" is // a known local value. checkMarkedCalls( "function f() {" + " var x = new externObjSEThis(); " + " x.externObjSEThisMethod('') " + "};" + "f();", ImmutableList.of("externObjSEThis")); } public void testAnnotationInExterns_new8() throws Exception { // "externObjSEThisMethod" modifies "this", the 'this' // is not a known local value, so it must be assumed it is to // have global side-effects. checkMarkedCalls( "function f(x) {" + " x.externObjSEThisMethod('') " + "};" + "f(new externObjSEThis());", ImmutableList.of("externObjSEThis")); } public void testAnnotationInExterns_new9() throws Exception { // "externObjSEThisMethod" modifies "this", the 'this' // is not a known local value, so it must be assumed it is to // have global side-effects. All possible values of "x" are considered // as no intraprocedural data flow is done. checkMarkedCalls( "function f(x) {" + " x = new externObjSEThis(); " + " x.externObjSEThisMethod('') " + "};" + "f(g);", ImmutableList.of("externObjSEThis")); } public void testAnnotationInExterns_new10() throws Exception { // While "externObjSEThisMethod2" only modifies it arguments // and the arguments are known local values, we don't // yet connect the dots, and "f" is consider to have // global side-effects. checkMarkedCalls( "function f() {" + " new externObjSEThis().externObjSEThisMethod2('') " + "};" + "f();", ImmutableList.of("externObjSEThis")); } public void testAnnotationInExterns1() throws Exception { checkMarkedCalls("externSef1()", ImmutableList.of()); } public void testAnnotationInExterns2() throws Exception { checkMarkedCalls("externSef2()", ImmutableList.of()); } public void testAnnotationInExterns3() throws Exception { checkMarkedCalls("externNsef1()", ImmutableList.of("externNsef1")); } public void testAnnotationInExterns4() throws Exception { checkMarkedCalls("externNsef2()", ImmutableList.of("externNsef2")); } public void testAnnotationInExterns5() throws Exception { checkMarkedCalls("externNsef3()", ImmutableList.of("externNsef3")); } public void testNamespaceAnnotationInExterns1() throws Exception { checkMarkedCalls("externObj.sef1()", ImmutableList.of()); } public void testNamespaceAnnotationInExterns2() throws Exception { checkMarkedCalls("externObj.nsef1()", ImmutableList.of("externObj.nsef1")); } public void testNamespaceAnnotationInExterns3() throws Exception { checkMarkedCalls("externObj.nsef2()", ImmutableList.of("externObj.nsef2")); } public void testNamespaceAnnotationInExterns4() throws Exception { checkMarkedCalls("externObj.partialFn()", ImmutableList.of()); } public void testNamespaceAnnotationInExterns5() throws Exception { // Test that adding a second definition for a partially defined // function doesn't make us think that the function has no side // effects. String templateSrc = "var o = {}; o. = function(){}; o.()"; // Ensure that functions with name != "partialFn" get marked. checkMarkedCalls(templateSrc.replaceAll("", "notPartialFn"), ImmutableList.of("o.notPartialFn")); checkMarkedCalls(templateSrc.replaceAll("", "partialFn"), ImmutableList.of()); } public void testNamespaceAnnotationInExterns6() throws Exception { checkMarkedCalls("externObj.partialSharedFn()", ImmutableList.of()); } public void testConstructorAnnotationInExterns1() throws Exception { checkMarkedCalls("new externSefConstructor()", ImmutableList.of()); } public void testConstructorAnnotationInExterns2() throws Exception { checkMarkedCalls("var a = new externSefConstructor();" + "a.sefFnOfSefObj()", ImmutableList.of()); } public void testConstructorAnnotationInExterns3() throws Exception { checkMarkedCalls("var a = new externSefConstructor();" + "a.nsefFnOfSefObj()", ImmutableList.of("a.nsefFnOfSefObj")); } public void testConstructorAnnotationInExterns4() throws Exception { checkMarkedCalls("var a = new externSefConstructor();" + "a.externShared()", ImmutableList.of()); } public void testConstructorAnnotationInExterns5() throws Exception { checkMarkedCalls("new externNsefConstructor()", ImmutableList.of("externNsefConstructor")); } public void testConstructorAnnotationInExterns6() throws Exception { checkMarkedCalls("var a = new externNsefConstructor();" + "a.sefFnOfNsefObj()", ImmutableList.of("externNsefConstructor")); } public void testConstructorAnnotationInExterns7() throws Exception { checkMarkedCalls("var a = new externNsefConstructor();" + "a.nsefFnOfNsefObj()", ImmutableList.of("externNsefConstructor", "a.nsefFnOfNsefObj")); } public void testConstructorAnnotationInExterns8() throws Exception { checkMarkedCalls("var a = new externNsefConstructor();" + "a.externShared()", ImmutableList.of("externNsefConstructor")); } public void testSharedFunctionName1() throws Exception { checkMarkedCalls("var a; " + "if (true) {" + " a = new externNsefConstructor()" + "} else {" + " a = new externSefConstructor()" + "}" + "a.externShared()", ImmutableList.of("externNsefConstructor")); } public void testSharedFunctionName2() throws Exception { // Implementation for both externNsefConstructor and externNsefConstructor2 // have no side effects. boolean broken = true; if (broken) { checkMarkedCalls("var a; " + "if (true) {" + " a = new externNsefConstructor()" + "} else {" + " a = new externNsefConstructor2()" + "}" + "a.externShared()", ImmutableList.of("externNsefConstructor", "externNsefConstructor2")); } else { checkMarkedCalls("var a; " + "if (true) {" + " a = new externNsefConstructor()" + "} else {" + " a = new externNsefConstructor2()" + "}" + "a.externShared()", ImmutableList.of("externNsefConstructor", "externNsefConstructor2", "a.externShared")); } } public void testAnnotationInExternStubs1() throws Exception { checkMarkedCalls("o.propWithStubBefore('a');", ImmutableList.of("o.propWithStubBefore")); } public void testAnnotationInExternStubs1b() throws Exception { checkMarkedCalls("o.propWithStubBeforeWithJSDoc('a');", ImmutableList.of("o.propWithStubBeforeWithJSDoc")); } public void testAnnotationInExternStubs2() throws Exception { checkMarkedCalls("o.propWithStubAfter('a');", ImmutableList.of("o.propWithStubAfter")); } public void testAnnotationInExternStubs2b() throws Exception { checkMarkedCalls("o.propWithStubAfter('a');", ImmutableList.of("o.propWithStubAfter")); } public void testAnnotationInExternStubs3() throws Exception { checkMarkedCalls("propWithAnnotatedStubAfter('a');", ImmutableList.of()); } public void testAnnotationInExternStubs4() throws Exception { // An externs definition with a stub that differs from the declaration. // Verify our assumption is valid about this. String externs = "/**@constructor*/function externObj5(){}\n" + "externObj5.prototype.propWithAnnotatedStubAfter = function(s) {};\n" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj5.prototype.propWithAnnotatedStubAfter;\n"; List expected = ImmutableList.of(); testSame(externs, "o.prototype.propWithAnnotatedStubAfter", TypeValidator.DUP_VAR_DECLARATION, false); assertEquals(expected, noSideEffectCalls); noSideEffectCalls.clear(); } public void testAnnotationInExternStubs5() throws Exception { // An externs definition with a stub that differs from the declaration. // Verify our assumption is valid about this. String externs = "/**@constructor*/function externObj5(){}\n" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " * @nosideeffects\n" + " */\n" + "externObj5.prototype.propWithAnnotatedStubAfter = function(s) {};\n" + "/**\n" + " * @param {string} s id.\n" + " * @return {string}\n" + " */\n" + "externObj5.prototype.propWithAnnotatedStubAfter;\n"; List expected = ImmutableList.of(); testSame(externs, "o.prototype.propWithAnnotatedStubAfter", TypeValidator.DUP_VAR_DECLARATION, false); assertEquals(expected, noSideEffectCalls); noSideEffectCalls.clear(); } public void testNoSideEffectsSimple() throws Exception { String prefix = "function f(){"; String suffix = "} f()"; List expected = ImmutableList.of("f"); checkMarkedCalls( prefix + "" + suffix, expected); checkMarkedCalls( prefix + "return 1" + suffix, expected); checkMarkedCalls( prefix + "return 1 + 2" + suffix, expected); // local var checkMarkedCalls( prefix + "var a = 1; return a" + suffix, expected); // mutate local var checkMarkedCalls( prefix + "var a = 1; a = 2; return a" + suffix, expected); checkMarkedCalls( prefix + "var a = 1; a = 2; return a + 1" + suffix, expected); // read from obj literal checkMarkedCalls( prefix + "var a = {foo : 1}; return a.foo" + suffix, expected); checkMarkedCalls( prefix + "var a = {foo : 1}; return a.foo + 1" + suffix, expected); // read from extern checkMarkedCalls( prefix + "return externObj" + suffix, expected); checkMarkedCalls( "function g(x) { x.foo = 3; }" /* to suppress missing property */ + prefix + "return externObj.foo" + suffix, expected); } public void testResultLocalitySimple() throws Exception { String prefix = "var g; function f(){"; String suffix = "} f()"; List expected = ImmutableList.of("f"); List notExpected = ImmutableList.of(); // no return checkLocalityOfMarkedCalls( prefix + "" + suffix, expected); // simple return expressions checkLocalityOfMarkedCalls( prefix + "return 1" + suffix, expected); checkLocalityOfMarkedCalls( prefix + "return 1 + 2" + suffix, expected); // global result checkLocalityOfMarkedCalls( prefix + "return g" + suffix, notExpected); // multiple returns checkLocalityOfMarkedCalls( prefix + "return 1; return 2" + suffix, expected); checkLocalityOfMarkedCalls( prefix + "return 1; return g" + suffix, notExpected); // local var, not yet. checkLocalityOfMarkedCalls( prefix + "var a = 1; return a" + suffix, notExpected); // mutate local var, not yet. checkLocalityOfMarkedCalls( prefix + "var a = 1; a = 2; return a" + suffix, notExpected); checkLocalityOfMarkedCalls( prefix + "var a = 1; a = 2; return a + 1" + suffix, expected); // read from obj literal checkLocalityOfMarkedCalls( prefix + "return {foo : 1}.foo" + suffix, notExpected); checkLocalityOfMarkedCalls( prefix + "var a = {foo : 1}; return a.foo" + suffix, notExpected); // read from extern checkLocalityOfMarkedCalls( prefix + "return externObj" + suffix, notExpected); checkLocalityOfMarkedCalls( "function inner(x) { x.foo = 3; }" /* to suppress missing property */ + prefix + "return externObj.foo" + suffix, notExpected); } public void testExternCalls() throws Exception { String prefix = "function f(){"; String suffix = "} f()"; checkMarkedCalls(prefix + "externNsef1()" + suffix, ImmutableList.of("externNsef1", "f")); checkMarkedCalls(prefix + "externObj.nsef1()" + suffix, ImmutableList.of("externObj.nsef1", "f")); checkMarkedCalls(prefix + "externSef1()" + suffix, ImmutableList.of()); checkMarkedCalls(prefix + "externObj.sef1()" + suffix, ImmutableList.of()); } public void testApply() throws Exception { checkMarkedCalls("function f() {return 42}" + "f.apply()", ImmutableList.of("f.apply")); } public void testCall() throws Exception { checkMarkedCalls("function f() {return 42}" + "f.call()", ImmutableList.of("f.call")); } public void testInference1() throws Exception { checkMarkedCalls("function f() {return g()}" + "function g() {return 42}" + "f()", ImmutableList.of("g", "f")); } public void testInference2() throws Exception { checkMarkedCalls("var a = 1;" + "function f() {g()}" + "function g() {a=2}" + "f()", ImmutableList.of()); } public void testInference3() throws Exception { checkMarkedCalls("var f = function() {return g()};" + "var g = function() {return 42};" + "f()", ImmutableList.of("g", "f")); } public void testInference4() throws Exception { checkMarkedCalls("var a = 1;" + "var f = function() {g()};" + "var g = function() {a=2};" + "f()", ImmutableList.of()); } public void testInference5() throws Exception { checkMarkedCalls("var goog = {};" + "goog.f = function() {return goog.g()};" + "goog.g = function() {return 42};" + "goog.f()", ImmutableList.of("goog.g", "goog.f")); } public void testInference6() throws Exception { checkMarkedCalls("var a = 1;" + "var goog = {};" + "goog.f = function() {goog.g()};" + "goog.g = function() {a=2};" + "goog.f()", ImmutableList.of()); } public void testLocalizedSideEffects1() throws Exception { // Returning a function that contains a modification of a local // is not a global side-effect. checkMarkedCalls("function f() {" + " var x = {foo : 0}; return function() {x.foo++};" + "}" + "f()", ImmutableList.of("f")); } public void testLocalizedSideEffects2() throws Exception { // Calling a function that contains a modification of a local // is a global side-effect (the value has escaped). checkMarkedCalls("function f() {" + " var x = {foo : 0}; (function() {x.foo++})();" + "}" + "f()", ImmutableList.of()); } public void testLocalizedSideEffects3() throws Exception { // A local that might be assigned a global value and whose properties // are modified must be considered a global side-effect. checkMarkedCalls("var g = {foo:1}; function f() {var x = g; x.foo++}" + "f()", ImmutableList.of()); } public void testLocalizedSideEffects4() throws Exception { // An array is an local object, assigning a local array is not a global // side-effect. checkMarkedCalls("function f() {var x = []; x[0] = 1;}" + "f()", ImmutableList.of("f")); } public void testLocalizedSideEffects5() throws Exception { // Assigning a local alias of a global is a global // side-effect. checkMarkedCalls("var g = [];function f() {var x = g; x[0] = 1;}" + "f()", ImmutableList.of()); } public void testLocalizedSideEffects6() throws Exception { // Returning a local object that has been modified // is not a global side-effect. checkMarkedCalls("function f() {" + " var x = {}; x.foo = 1; return x;" + "}" + "f()", ImmutableList.of("f")); } public void testLocalizedSideEffects7() throws Exception { // Returning a local object that has been modified // is not a global side-effect. checkMarkedCalls("/** @constructor A */ function A() {};" + "function f() {" + " var a = []; a[1] = 1; return a;" + "}" + "f()", ImmutableList.of("f")); } public void testLocalizedSideEffects8() throws Exception { // Returning a local object that has been modified // is not a global side-effect. // TODO(johnlenz): Not yet. Propagate local object information. checkMarkedCalls("/** @constructor A */ function A() {};" + "function f() {" + " var a = new A; a.foo = 1; return a;" + "}" + "f()", ImmutableList.of("A")); } public void testLocalizedSideEffects9() throws Exception { // Returning a local object that has been modified // is not a global side-effect. // TODO(johnlenz): Not yet. Propagate local object information. checkMarkedCalls("/** @constructor A */ function A() {this.x = 1};" + "function f() {" + " var a = new A; a.foo = 1; return a;" + "}" + "f()", ImmutableList.of("A")); } public void testLocalizedSideEffects10() throws Exception { // Returning a local object that has been modified // is not a global side-effect. checkMarkedCalls("/** @constructor A */ function A() {};" + "A.prototype.g = function() {this.x = 1};" + "function f() {" + " var a = new A; a.g(); return a;" + "}" + "f()", ImmutableList.of("A")); } public void testLocalizedSideEffects11() throws Exception { // Calling a function of a local object that taints this. checkMarkedCalls( "/** @constructor */ function A() {}" + "A.prototype.update = function() { this.x = 1; };" + "/** @constructor */ function B() { " + " this.a_ = new A();" + "}" + "B.prototype.updateA = function() {" + " var b = this.a_;" + " b.update();" + "};" + "var x = new B();" + "x.updateA();", ImmutableList.of("A", "B")); } public void testUnaryOperators1() throws Exception { checkMarkedCalls("function f() {var x = 1; x++}" + "f()", ImmutableList.of("f")); } public void testUnaryOperators2() throws Exception { checkMarkedCalls("var x = 1;" + "function f() {x++}" + "f()", ImmutableList.of()); } public void testUnaryOperators3() throws Exception { checkMarkedCalls("function f() {var x = {foo : 0}; x.foo++}" + "f()", ImmutableList.of("f")); } public void testUnaryOperators4() throws Exception { checkMarkedCalls("var x = {foo : 0};" + "function f() {x.foo++}" + "f()", ImmutableList.of()); } public void testUnaryOperators5() throws Exception { checkMarkedCalls("function f(x) {x.foo++}" + "f({foo : 0})", ImmutableList.of()); } public void testDeleteOperator1() throws Exception { checkMarkedCalls("var x = {};" + "function f() {delete x}" + "f()", ImmutableList.of()); } public void testDeleteOperator2() throws Exception { checkMarkedCalls("function f() {var x = {}; delete x}" + "f()", ImmutableList.of("f")); } public void testOrOperator1() throws Exception { checkMarkedCalls("var f = externNsef1 || externNsef2;\n" + "f()", ImmutableList.of()); } public void testOrOperator2() throws Exception { checkMarkedCalls("var f = function(){} || externNsef2;\n" + "f()", ImmutableList.of()); } public void testOrOperator3() throws Exception { checkMarkedCalls("var f = externNsef2 || function(){};\n" + "f()", ImmutableList.of()); } public void testOrOperators4() throws Exception { checkMarkedCalls("var f = function(){} || function(){};\n" + "f()", ImmutableList.of()); } public void testAndOperator1() throws Exception { checkMarkedCalls("var f = externNsef1 && externNsef2;\n" + "f()", ImmutableList.of()); } public void testAndOperator2() throws Exception { checkMarkedCalls("var f = function(){} && externNsef2;\n" + "f()", ImmutableList.of()); } public void testAndOperator3() throws Exception { checkMarkedCalls("var f = externNsef2 && function(){};\n" + "f()", ImmutableList.of()); } public void testAndOperators4() throws Exception { checkMarkedCalls("var f = function(){} && function(){};\n" + "f()", ImmutableList.of()); } public void testHookOperator1() throws Exception { checkMarkedCalls("var f = true ? externNsef1 : externNsef2;\n" + "f()", ImmutableList.of()); } public void testHookOperator2() throws Exception { checkMarkedCalls("var f = true ? function(){} : externNsef2;\n" + "f()", ImmutableList.of()); } public void testHookOperator3() throws Exception { checkMarkedCalls("var f = true ? externNsef2 : function(){};\n" + "f()", ImmutableList.of()); } public void testHookOperators4() throws Exception { checkMarkedCalls("var f = true ? function(){} : function(){};\n" + "f()", ImmutableList.of()); } public void testThrow1() throws Exception { checkMarkedCalls("function f(){throw Error()};\n" + "f()", ImmutableList.of("Error")); } public void testThrow2() throws Exception { checkMarkedCalls("/**@constructor*/function A(){throw Error()};\n" + "function f(){return new A()}\n" + "f()", ImmutableList.of("Error")); } public void testAssignmentOverride() throws Exception { checkMarkedCalls("/**@constructor*/function A(){}\n" + "A.prototype.foo = function(){};\n" + "var a = new A;\n" + "a.foo();\n", ImmutableList.of("A", "a.foo")); checkMarkedCalls("/**@constructor*/function A(){}\n" + "A.prototype.foo = function(){};\n" + "var x = 1\n" + "function f(){x = 10}\n" + "var a = new A;\n" + "a.foo = f;\n" + "a.foo();\n", ImmutableList.of("A")); } public void testInheritance1() throws Exception { String source = CompilerTypeTestCase.CLOSURE_DEFS + "/**@constructor*/function I(){}\n" + "I.prototype.foo = function(){};\n" + "I.prototype.bar = function(){this.foo()};\n" + "/**@constructor\n@extends {I}*/function A(){};\n" + "goog.inherits(A, I)\n;" + "/** @override */A.prototype.foo = function(){var data=24};\n" + "var i = new I();i.foo();i.bar();\n" + "var a = new A();a.foo();a.bar();"; checkMarkedCalls(source, ImmutableList.of("this.foo", "goog.inherits", "I", "i.foo", "i.bar", "A", "a.foo", "a.bar")); } public void testInheritance2() throws Exception { String source = CompilerTypeTestCase.CLOSURE_DEFS + "/**@constructor*/function I(){}\n" + "I.prototype.foo = function(){};\n" + "I.prototype.bar = function(){this.foo()};\n" + "/**@constructor\n@extends {I}*/function A(){};\n" + "goog.inherits(A, I)\n;" + "/** @override */A.prototype.foo = function(){this.data=24};\n" + "var i = new I();i.foo();i.bar();\n" + "var a = new A();a.foo();a.bar();"; checkMarkedCalls(source, ImmutableList.of("goog.inherits", "I", "A")); } public void testCallBeforeDefinition() throws Exception { checkMarkedCalls("f(); function f(){}", ImmutableList.of("f")); checkMarkedCalls("var a = {}; a.f(); a.f = function (){}", ImmutableList.of("a.f")); } public void testConstructorThatModifiesThis1() throws Exception { String source = "/**@constructor*/function A(){this.foo = 1}\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of("A", "f")); } public void testConstructorThatModifiesThis2() throws Exception { String source = "/**@constructor*/function A(){this.foo()}\n" + "A.prototype.foo = function(){this.data=24};\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of("A", "f")); } public void testConstructorThatModifiesThis3() throws Exception { // test chained String source = "/**@constructor*/function A(){this.foo()}\n" + "A.prototype.foo = function(){this.bar()};\n" + "A.prototype.bar = function(){this.data=24};\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of("A", "f")); } public void testConstructorThatModifiesThis4() throws Exception { // test ".call" notation. String source = "/**@constructor*/function A(){foo.call(this)}\n" + "function foo(){this.data=24};\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of("A", "f")); } public void testConstructorThatModifiesGlobal1() throws Exception { String source = "var b = 0;" + "/**@constructor*/function A(){b=1};\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of()); } public void testConstructorThatModifiesGlobal2() throws Exception { String source = "var b = 0;" + "/**@constructor*/function A(){this.foo()}\n" + "A.prototype.foo = function(){b=1};\n" + "function f() {return new A}" + "f()"; checkMarkedCalls(source, ImmutableList.of()); } public void testCallFunctionThatModifiesThis() throws Exception { String source = "/**@constructor*/function A(){}\n" + "A.prototype.foo = function(){this.data=24};\n" + "function f(){var a = new A; return a}\n" + "function g(){var a = new A; a.foo(); return a}\n" + "f(); g()"; checkMarkedCalls(source, ImmutableList.of("A", "A", "f")); } public void testCallFunctionFOrG() throws Exception { String source = "function f(){}\n" + "function g(){}\n" + "function h(){ (f || g)() }\n" + "h()"; checkMarkedCalls(source, ImmutableList.of("(f || g)", "h")); } public void testCallFunctionFOrGViaHook() throws Exception { String source = "function f(){}\n" + "function g(){}\n" + "function h(){ (false ? f : g)() }\n" + "h()"; checkMarkedCalls(source, ImmutableList.of("(f : g)", "h")); } public void testCallFunctionForGorH() throws Exception { String source = "function f(){}\n" + "function g(){}\n" + "function h(){}\n" + "function i(){ (false ? f : (g || h))() }\n" + "i()"; checkMarkedCalls(source, ImmutableList.of("(f : (g || h))", "i")); } public void testCallFunctionFOrGWithSideEffects() throws Exception { String source = "var x = 0;\n" + "function f(){x = 10}\n" + "function g(){}\n" + "function h(){ (f || g)() }\n" + "function i(){ (g || f)() }\n" + "function j(){ (f || f)() }\n" + "function k(){ (g || g)() }\n" + "h(); i(); j(); k()"; checkMarkedCalls(source, ImmutableList.of("(g || g)", "k")); } public void testCallFunctionFOrGViaHookWithSideEffects() throws Exception { String source = "var x = 0;\n" + "function f(){x = 10}\n" + "function g(){}\n" + "function h(){ (false ? f : g)() }\n" + "function i(){ (false ? g : f)() }\n" + "function j(){ (false ? f : f)() }\n" + "function k(){ (false ? g : g)() }\n" + "h(); i(); j(); k()"; checkMarkedCalls(source, ImmutableList.of("(g : g)", "k")); } public void testCallRegExpWithSideEffects() throws Exception { String source = "var x = 0;\n" + "function k(){(/a/).exec('')}\n" + "k()"; regExpHaveSideEffects = true; checkMarkedCalls(source, ImmutableList.of()); regExpHaveSideEffects = false; checkMarkedCalls(source, ImmutableList.of( "REGEXP STRING exec", "k")); } public void testAnonymousFunction1() throws Exception { String source = "(function (){})();"; checkMarkedCalls(source, ImmutableList.of( "FUNCTION")); } public void testAnonymousFunction2() throws Exception { String source = "(Error || function (){})();"; checkMarkedCalls(source, ImmutableList.of( "(Error || FUNCTION)")); } public void testAnonymousFunction3() throws Exception { String source = "var a = (Error || function (){})();"; checkMarkedCalls(source, ImmutableList.of( "(Error || FUNCTION)")); } // Indirect complex function definitions aren't yet supported. public void testAnonymousFunction4() throws Exception { String source = "var a = (Error || function (){});" + "a();"; // This should be "(Error || FUNCTION)" but isn't. checkMarkedCalls(source, ImmutableList.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 checkMarkedCalls(String source, List expected) { testSame(source); assertEquals(expected, noSideEffectCalls); noSideEffectCalls.clear(); } void checkLocalityOfMarkedCalls(String source, List expected) { testSame(source); assertEquals(expected, localResultCalls); localResultCalls.clear(); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new NoSideEffectCallEnumerator(compiler); } /** * Run PureFunctionIdentifier, then gather a list of calls that are * marked as having no side effects. */ private class NoSideEffectCallEnumerator extends AbstractPostOrderCallback implements CompilerPass { private final Compiler compiler; NoSideEffectCallEnumerator(Compiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { compiler.setHasRegExpGlobalReferences(regExpHaveSideEffects); SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); PureFunctionIdentifier passUnderTest = new PureFunctionIdentifier(compiler, defFinder); passUnderTest.process(externs, root); // Ensure that debug report computation doesn't crash. passUnderTest.getDebugReport(); 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(generateNameString(n.getFirstChild())); } } else if (n.isCall()) { if (!NodeUtil.functionCallHasSideEffects(n)) { noSideEffectCalls.add(generateNameString(n.getFirstChild())); } if (NodeUtil.callHasLocalResult(n)) { localResultCalls.add(generateNameString(n.getFirstChild())); } } } private String generateNameString(Node node) { if (node.isOr()) { return "(" + generateNameString(node.getFirstChild()) + " || " + generateNameString(node.getLastChild()) + ")"; } else if (node.isHook()) { return "(" + generateNameString(node.getFirstChild().getNext()) + " : " + generateNameString(node.getLastChild()) + ")"; } else { String result = node.getQualifiedName(); if (result == null) { if (node.isFunction()) { result = node.toString(false, false, false).trim(); } else { result = node.getFirstChild().toString(false, false, false); result += " " + node.getLastChild().toString(false, false, false); } } return result; } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CrossModuleMethodMotionTest.java0000644000175000017500000004273612115204405031331 0ustar apoapo/* * 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; /** * Tests for {@link CrossModuleMethodMotion}. * * @author nicksantos@google.com (Nick Santos) */ public class CrossModuleMethodMotionTest extends CompilerTestCase { private static final String EXTERNS = "IFoo.prototype.bar; var mExtern; mExtern.bExtern; mExtern['cExtern'];"; private boolean canMoveExterns = false; private final String STUB_DECLARATIONS = CrossModuleMethodMotion.STUB_DECLARATIONS; public CrossModuleMethodMotionTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CrossModuleMethodMotion( compiler, new CrossModuleMethodMotion.IdGenerator(), canMoveExterns); } @Override public void setUp() { super.enableLineNumberCheck(true); canMoveExterns = false; } public void testMovePrototypeMethod1() { testSame(createModuleChain( "function Foo() {}" + "Foo.prototype.bar = function() {};", // Module 2 "(new Foo).bar()")); canMoveExterns = true; test(createModuleChain( "function Foo() {}" + "Foo.prototype.bar = function() {};", // Module 2 "(new Foo).bar()"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.bar = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.bar = JSCompiler_unstubMethod(0, function() {});" + "(new Foo).bar()" }); } public void testMovePrototypeMethod2() { test(createModuleChain( "function Foo() {}" + "Foo.prototype = { method: function() {} };", // Module 2 "(new Foo).method()"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype = { method: JSCompiler_stubMethod(0) };", // Module 2 "Foo.prototype.method = " + " JSCompiler_unstubMethod(0, function() {});" + "(new Foo).method()" }); } public void testMovePrototypeMethod3() { testSame(createModuleChain( "function Foo() {}" + "Foo.prototype = { get method() {} };", // Module 2 "(new Foo).method()")); } public void testMovePrototypeRecursiveMethod() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function() { this.baz(); };", // Module 2 "(new Foo).baz()"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(0, " + " function() { this.baz(); });" + "(new Foo).baz()" }); } public void testCantMovePrototypeProp() { testSame(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = goog.nullFunction;", // Module 2 "(new Foo).baz()")); } public void testMoveMethodsInRightOrder() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function() { return 1; };" + "Foo.prototype.baz = function() { return 2; };", // Module 2 "(new Foo).baz()"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = " + "JSCompiler_unstubMethod(1, function() { return 1; });" + "Foo.prototype.baz = " + "JSCompiler_unstubMethod(0, function() { return 2; });" + "(new Foo).baz()" }); } public void testMoveMethodsInRightOrder2() { JSModule[] m = createModules( "function Foo() {}" + "Foo.prototype.baz = function() { return 1; };" + "function Goo() {}" + "Goo.prototype.baz = function() { return 2; };", // Module 2, depends on 1 "", // Module 3, depends on 2 "(new Foo).baz()", // Module 4, depends on 3 "", // Module 5, depends on 3 "(new Goo).baz()"); m[1].addDependency(m[0]); m[2].addDependency(m[1]); m[3].addDependency(m[2]); m[4].addDependency(m[2]); test(m, new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(1);" + "function Goo() {}" + "Goo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "", // Module 3 "Foo.prototype.baz = " + "JSCompiler_unstubMethod(1, function() { return 1; });" + "Goo.prototype.baz = " + "JSCompiler_unstubMethod(0, function() { return 2; });" + "(new Foo).baz()", // Module 4 "", // Module 5 "(new Goo).baz()" }); } public void testMoveMethodsUsedInTwoModules() { testSame(createModuleStar( "function Foo() {}" + "Foo.prototype.baz = function() {};", // Module 2 "(new Foo).baz()", // Module 3 "(new Foo).baz()")); } public void testMoveMethodsUsedInTwoModules2() { JSModule[] modules = createModules( "function Foo() {}" + "Foo.prototype.baz = function() {};", // Module 2 "", // a blank module in the middle // Module 3 "(new Foo).baz() + 1", // Module 4 "(new Foo).baz() + 2"); modules[1].addDependency(modules[0]); modules[2].addDependency(modules[1]); modules[3].addDependency(modules[1]); test(modules, new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});", // Module 3 "(new Foo).baz() + 1", // Module 4 "(new Foo).baz() + 2" }); } public void testTwoMethods() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function() {};", // Module 2 "Foo.prototype.callBaz = function() { this.baz(); }", // Module 3 "(new Foo).callBaz()"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(1);", // Module 2 "Foo.prototype.callBaz = JSCompiler_stubMethod(0);", // Module 3 "Foo.prototype.baz = JSCompiler_unstubMethod(1, function() {});" + "Foo.prototype.callBaz = " + " JSCompiler_unstubMethod(0, function() { this.baz(); });" + "(new Foo).callBaz()" }); } public void testTwoMethods2() { // if the programmer screws up the module order, we don't try to correct // the mistake. test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function() {};", // Module 2 "(new Foo).callBaz()", // Module 3 "Foo.prototype.callBaz = function() { this.baz(); }"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "(new Foo).callBaz()", // Module 3 "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + "Foo.prototype.callBaz = function() { this.baz(); };" }); } public void testGlobalFunctionsInGraph() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function() {};" + "function x() { return (new Foo).baz(); }", // Module 2 "x();"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);" + "function x() { return (new Foo).baz(); }", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(0, function() {});" + "x();" }); } // Read of closure variable disables method motions. public void testClosureVariableReads1() { testSame(createModuleChain( "function Foo() {}" + "(function() {" + "var x = 'x';" + "Foo.prototype.baz = function() {x};" + "})();", // Module 2 "var y = new Foo(); y.baz();")); } // Read of global variable is fine. public void testClosureVariableReads2() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.b1 = function() {" + " var x = 1;" + " Foo.prototype.b2 = function() {" + " Foo.prototype.b3 = function() {" + " x;" + " }" + " }" + "};", // Module 2 "var y = new Foo(); y.b1();", // Module 3 "y = new Foo(); z.b2();", // Module 4 "y = new Foo(); z.b3();" ), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.b1 = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + " var x = 1;" + " Foo.prototype.b2 = function() {" + " Foo.prototype.b3 = function() {" + " x;" + " }" + " }" + "});" + "var y = new Foo(); y.b1();", // Module 3 "y = new Foo(); z.b2();", // Module 4 "y = new Foo(); z.b3();" }); } public void testClosureVariableReads3() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.b1 = function() {" + " Foo.prototype.b2 = function() {" + " var x = 1;" + " Foo.prototype.b3 = function() {" + " x;" + " }" + " }" + "};", // Module 2 "var y = new Foo(); y.b1();", // Module 3 "y = new Foo(); z.b2();", // Module 4 "y = new Foo(); z.b3();" ), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.b1 = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.b1 = JSCompiler_unstubMethod(0, function() {" + " Foo.prototype.b2 = JSCompiler_stubMethod(1);" + "});" + "var y = new Foo(); y.b1();", // Module 3 "Foo.prototype.b2 = JSCompiler_unstubMethod(1, function() {" + " var x = 1;" + " Foo.prototype.b3 = function() {" + " x;" + " }" + "});" + "y = new Foo(); z.b2();", // Module 4 "y = new Foo(); z.b3();" }); } // Read of global variable is fine. public void testNoClosureVariableReads1() { test(createModuleChain( "function Foo() {}" + "var x = 'x';" + "Foo.prototype.baz = function(){x};", // Module 2 "var y = new Foo(); y.baz();"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "var x = 'x';" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(0, function(){x});" + "var y = new Foo(); y.baz();" }); } // Read of a local is fine. public void testNoClosureVariableReads2() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function(){var x = 1;x};", // Module 2 "var y = new Foo(); y.baz();"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(" + " 0, function(){var x = 1; x});" + "var y = new Foo(); y.baz();" }); } // An anonymous inner function reading a closure variable is fine. public void testInnerFunctionClosureVariableReads() { test(createModuleChain( "function Foo() {}" + "Foo.prototype.baz = function(){var x = 1;" + " return function(){x}};", // Module 2 "var y = new Foo(); y.baz();"), new String[] { STUB_DECLARATIONS + "function Foo() {}" + "Foo.prototype.baz = JSCompiler_stubMethod(0);", // Module 2 "Foo.prototype.baz = JSCompiler_unstubMethod(" + " 0, function(){var x = 1; return function(){x}});" + "var y = new Foo(); y.baz();" }); } public void testIssue600() { testSame( createModuleChain( "var jQuery1 = (function() {\n" + " var jQuery2 = function() {};\n" + " var theLoneliestNumber = 1;\n" + " jQuery2.prototype = {\n" + " size: function() {\n" + " return theLoneliestNumber;\n" + " }\n" + " };\n" + " return jQuery2;\n" + "})();\n", "(function() {" + " var div = jQuery1('div');" + " div.size();" + "})();")); } public void testIssue600b() { testSame( createModuleChain( "var jQuery1 = (function() {\n" + " var jQuery2 = function() {};\n" + " jQuery2.prototype = {\n" + " size: function() {\n" + " return 1;\n" + " }\n" + " };\n" + " return jQuery2;\n" + "})();\n", "(function() {" + " var div = jQuery1('div');" + " div.size();" + "})();")); } public void testIssue600c() { test( createModuleChain( "var jQuery2 = function() {};\n" + "jQuery2.prototype = {\n" + " size: function() {\n" + " return 1;\n" + " }\n" + "};\n", "(function() {" + " var div = jQuery2('div');" + " div.size();" + "})();"), new String[] { STUB_DECLARATIONS + "var jQuery2 = function() {};\n" + "jQuery2.prototype = {\n" + " size: JSCompiler_stubMethod(0)\n" + "};\n", "jQuery2.prototype.size=" + " JSCompiler_unstubMethod(0,function(){return 1});" + "(function() {" + " var div = jQuery2('div');" + " div.size();" + "})();" }); } public void testIssue600d() { test( createModuleChain( "var jQuery2 = function() {};\n" + "(function() {" + " jQuery2.prototype = {\n" + " size: function() {\n" + " return 1;\n" + " }\n" + " };\n" + "})();", "(function() {" + " var div = jQuery2('div');" + " div.size();" + "})();"), new String[] { STUB_DECLARATIONS + "var jQuery2 = function() {};\n" + "(function() {" + " jQuery2.prototype = {\n" + " size: JSCompiler_stubMethod(0)\n" + " };\n" + "})();", "jQuery2.prototype.size=" + " JSCompiler_unstubMethod(0,function(){return 1});" + "(function() {" + " var div = jQuery2('div');" + " div.size();" + "})();" }); } public void testIssue600e() { testSame( createModuleChain( "var jQuery2 = function() {};\n" + "(function() {" + " var theLoneliestNumber = 1;\n" + " jQuery2.prototype = {\n" + " size: function() {\n" + " return theLoneliestNumber;\n" + " }\n" + " };\n" + "})();", "(function() {" + " var div = jQuery2('div');" + " div.size();" + "})();")); } public void testPrototypeOfThisAssign() { testSame( createModuleChain( "/** @constructor */" + "function F() {}" + "this.prototype.foo = function() {};", "(new F()).foo();")); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GenerateExportsTest.java0000644000175000017500000000726612115204405027661 0ustar apoapo/* * 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; /** * Generate exports unit test. * */ public class GenerateExportsTest extends CompilerTestCase { private static final String EXTERNS = "function google_exportSymbol(a, b) {}; " + "goog.exportProperty = function(a, b, c) {}; "; public GenerateExportsTest() { super(EXTERNS); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new GenerateExports(compiler, "google_exportSymbol", "goog.exportProperty"); } @Override protected int getNumRepetitions() { // This pass only runs once. return 1; } @Override public void setUp() throws Exception { super.setUp(); super.enableLineNumberCheck(false); } public void testExportSymbol() { test("/** @export */function foo() {}", "function foo(){}google_exportSymbol(\"foo\",foo)"); } public void testExportSymbolAndProperties() { test("/** @export */function foo() {}" + "/** @export */foo.prototype.bar = function() {}", "function foo(){}" + "google_exportSymbol(\"foo\",foo);" + "foo.prototype.bar=function(){};" + "goog.exportProperty(foo.prototype,\"bar\",foo.prototype.bar)"); } public void testExportSymbolAndConstantProperties() { test("/** @export */function foo() {}" + "/** @export */foo.BAR = 5;", "function foo(){}" + "google_exportSymbol(\"foo\",foo);" + "foo.BAR=5;" + "goog.exportProperty(foo,\"BAR\",foo.BAR)"); } public void testExportVars() { test("/** @export */var FOO = 5", "var FOO=5;" + "google_exportSymbol(\"FOO\",FOO)"); } public void testNoExport() { test("var FOO = 5", "var FOO=5"); } /** * Nested assignments are ambiguous and therefore not supported. * @see FindExportableNodes */ public void testNestedVarAssign() { test("var BAR;\n/** @export */var FOO = BAR = 5", null, FindExportableNodes.NON_GLOBAL_ERROR); } /** * Nested assignments are ambiguous and therefore not supported. * @see FindExportableNodes */ public void testNestedAssign() { test("var BAR;var FOO = {};\n/** @export */FOO.test = BAR = 5", null, FindExportableNodes.NON_GLOBAL_ERROR); } public void testNonGlobalScopeExport() { test("(function() { /** @export */var FOO = 5 })()", null, FindExportableNodes.NON_GLOBAL_ERROR); } public void testExportClass() { test("/** @export */ function G() {} foo();", "function G() {} google_exportSymbol('G', G); foo();"); } public void testExportSubclass() { test("var goog = {}; function F() {}" + "/** @export */ function G() {} goog.inherits(G, F);", "var goog = {}; function F() {}" + "function G() {} goog.inherits(G, F); google_exportSymbol('G', G);"); } public void testExportEnum() { // TODO(johnlenz): Issue 310, should the values also be externed? test("/** @enum {string}\n @export */ var E = {A:1, B:2};", "/** @enum {string}\n @export */ var E = {A:1, B:2};" + "google_exportSymbol('E', E);"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/regtests/0000755000175000017500000000000012115204405024664 5ustar apoapo././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootrootclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/regtests/CompileEachLineOfProgramOutput.javaclosure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/regtests/CompileEachLineOfProgramO0000644000175000017500000000702312115204405031526 0ustar apoapo/* * Copyright 2009 Peter Burns * * 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.regtests; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.logging.Level; import com.google.javascript.jscomp.CompilationLevel; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.WarningLevel; public class CompileEachLineOfProgramOutput { private static final SourceFile extern = SourceFile.fromCode("externs.js", ""); private static final CompilerOptions options = new CompilerOptions(); static { CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( options); WarningLevel.QUIET.setOptionsForWarningLevel(options); Compiler.setLoggingLevel(Level.OFF); } public static void main(String[] args) throws IOException { if (args.length == 0){ usage(); } Runtime r = Runtime.getRuntime(); Process p = null; try { p = r.exec(args); } catch (IOException e) { if (args[0].equals("generatejs")) { // assuming that the command wasn't found System.out.println("generatejs not found, required for generating " + "fuzz test cases"); System.out.println("See: http://github.com/rictic/generatejs"); System.exit(2); } else { throw e; } } BufferedReader br = new BufferedReader( new InputStreamReader(p.getInputStream())); int programsCompiled = 0, compilerErrors = 0; for (String program = br.readLine(); program != null; program = br.readLine()) { try { compile(program, programsCompiled); } catch(Exception e) { System.out.println("Compiler error on program #" + programsCompiled + ":"); System.out.println(program); System.out.println("Details:"); e.printStackTrace(System.out); System.out.println("\n\n\n"); compilerErrors++; } programsCompiled++; } if (compilerErrors == 0){ System.out.println(programsCompiled + " programs compiled without error"); System.exit(0); } else { System.out.println("==========FAILURE==========="); System.out.println(compilerErrors + " programs caused an error within the compiler out of " + programsCompiled + " tested."); System.exit(1); } } public static Result compile(String program, int num) { SourceFile input = SourceFile.fromCode(""+num, program); Compiler compiler = new Compiler(); Result result = compiler.compile(extern, input, options); return result; } private static void usage() { System.out.println( "Usage: pass in a program to execute (with arguments)"); System.out.println( "The program is expected to produce JS programs to stdout, " + "one per line"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckGlobalNamesTest.java0000644000175000017500000002324612115204405027660 0ustar apoapo/* * 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 static com.google.javascript.jscomp.CheckGlobalNames.NAME_DEFINED_LATE_WARNING; import static com.google.javascript.jscomp.CheckGlobalNames.UNDEFINED_NAME_WARNING; import static com.google.javascript.jscomp.CheckGlobalNames.STRICT_MODULE_DEP_QNAME; import com.google.javascript.rhino.Node; /** * Tests for {@code CheckGlobalNames.java}. * * @author nicksantos@google.com (Nick Santos) */ public class CheckGlobalNamesTest extends CompilerTestCase { private boolean injectNamespace = false; public CheckGlobalNamesTest() { super("function alert() {}" + "/** @constructor */ function Object(){}" + "Object.prototype.hasOwnProperty = function() {};" + "/** @constructor */ function Function(){}" + "Function.prototype.call = function() {};"); } @Override protected CompilerPass getProcessor(final Compiler compiler) { final CheckGlobalNames checkGlobalNames = new CheckGlobalNames( compiler, CheckLevel.WARNING); if (injectNamespace) { return new CompilerPass() { @Override public void process(Node externs, Node js) { checkGlobalNames.injectNamespace( new GlobalNamespace(compiler, externs, js)) .process(externs, js); } }; } else { return checkGlobalNames; } } @Override public void setUp() { injectNamespace = false; STRICT_MODULE_DEP_QNAME.level = CheckLevel.WARNING; } private static final String GET_NAMES = "var a = {get d() {return 1}}; a.b = 3; a.c = {get e() {return 5}};"; private static final String SET_NAMES = "var a = {set d(x) {}}; a.b = 3; a.c = {set e(y) {}};"; private static final String NAMES = "var a = {d: 1}; a.b = 3; a.c = {e: 5};"; public void testRefToDefinedProperties1() { testSame(NAMES + "alert(a.b); alert(a.c.e);"); testSame(GET_NAMES + "alert(a.b); alert(a.c.e);"); testSame(SET_NAMES + "alert(a.b); alert(a.c.e);"); } public void testRefToDefinedProperties2() { testSame(NAMES + "a.x={}; alert(a.c);"); testSame(GET_NAMES + "a.x={}; alert(a.c);"); testSame(SET_NAMES + "a.x={}; alert(a.c);"); } public void testRefToDefinedProperties3() { testSame(NAMES + "alert(a.d);"); testSame(GET_NAMES + "alert(a.d);"); testSame(SET_NAMES + "alert(a.d);"); } public void testRefToMethod1() { testSame("function foo() {}; foo.call();"); } public void testRefToMethod2() { testSame("function foo() {}; foo.call.call();"); } public void testCallUndefinedFunctionGivesNoWaring() { // We don't bother checking undeclared variables--there's another // pass that does this already. testSame("foo();"); } public void testRefToPropertyOfAliasedName() { // this is OK, because "a" was aliased testSame(NAMES + "alert(a); alert(a.x);"); } public void testRefToUndefinedProperty1() { testSame(NAMES + "alert(a.x);", UNDEFINED_NAME_WARNING); } public void testRefToUndefinedProperty2() { testSame(NAMES + "a.x();", UNDEFINED_NAME_WARNING); } public void testRefToUndefinedProperty3() { testSame(NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); testSame(GET_NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); testSame(SET_NAMES + "alert(a.c.x);", UNDEFINED_NAME_WARNING); } public void testRefToUndefinedProperty4() { testSame(NAMES + "alert(a.d.x);"); testSame(GET_NAMES + "alert(a.d.x);"); testSame(SET_NAMES + "alert(a.d.x);"); } public void testRefToDescendantOfUndefinedProperty1() { testSame(NAMES + "var c = a.x.b;", UNDEFINED_NAME_WARNING); } public void testRefToDescendantOfUndefinedProperty2() { testSame(NAMES + "a.x.b();", UNDEFINED_NAME_WARNING); } public void testRefToDescendantOfUndefinedProperty3() { testSame(NAMES + "a.x.b = 3;", UNDEFINED_NAME_WARNING); } public void testUndefinedPrototypeMethodRefGivesNoWarning() { testSame("function Foo() {} var a = new Foo(); a.bar();"); } public void testComplexPropAssignGivesNoWarning() { testSame("var a = {}; var b = a.b = 3;"); } public void testTypedefGivesNoWarning() { testSame("var a = {}; /** @typedef {number} */ a.b;"); } public void testRefToDescendantOfUndefinedPropertyGivesCorrectWarning() { testSame("", NAMES + "a.x.b = 3;", UNDEFINED_NAME_WARNING, UNDEFINED_NAME_WARNING.format("a.x")); } public void testNamespaceInjection() { injectNamespace = true; testSame(NAMES + "var c = a.x.b;", UNDEFINED_NAME_WARNING); } public void testSuppressionOfUndefinedNamesWarning() { testSame(new String[] { NAMES + "/** @constructor */ function Foo() { };" + "/** @suppress {undefinedNames} */" + "Foo.prototype.bar = function() {" + " alert(a.x);" + " alert(a.x.b());" + " a.x();" + " var c = a.x.b;" + " var c = a.x.b();" + " a.x.b();" + " a.x.b = 3;" + "};", }); } public void testNoWarningForSimpleVarModuleDep1() { testSame(createModuleChain( NAMES, "var c = a;" )); } public void testNoWarningForSimpleVarModuleDep2() { testSame(createModuleChain( "var c = a;", NAMES )); } public void testNoWarningForGoodModuleDep1() { testSame(createModuleChain( NAMES, "var c = a.b;" )); } public void testBadModuleDep1() { testSame(createModuleChain( "var c = a.b;", NAMES ), STRICT_MODULE_DEP_QNAME); } public void testBadModuleDep2() { testSame(createModuleStar( NAMES, "a.xxx = 3;", "var x = a.xxx;" ), STRICT_MODULE_DEP_QNAME); } public void testSelfModuleDep() { testSame(createModuleChain( NAMES + "var c = a.b;" )); } public void testUndefinedModuleDep1() { testSame(createModuleChain( "var c = a.xxx;", NAMES ), UNDEFINED_NAME_WARNING); } public void testLateDefinedName1() { testSame("x.y = {}; var x = {};", NAME_DEFINED_LATE_WARNING); } public void testLateDefinedName2() { testSame("var x = {}; x.y.z = {}; x.y = {};", NAME_DEFINED_LATE_WARNING); } public void testLateDefinedName3() { testSame("var x = {}; x.y.z = {}; x.y = {z: {}};", NAME_DEFINED_LATE_WARNING); } public void testLateDefinedName4() { testSame("var x = {}; x.y.z.bar = {}; x.y = {z: {}};", NAME_DEFINED_LATE_WARNING); } public void testLateDefinedName5() { testSame("var x = {}; /** @typedef {number} */ x.y.z; x.y = {};", NAME_DEFINED_LATE_WARNING); } public void testLateDefinedName6() { testSame( "var x = {}; x.y.prototype.z = 3;" + "/** @constructor */ x.y = function() {};", NAME_DEFINED_LATE_WARNING); } public void testOkLateDefinedName1() { testSame("function f() { x.y = {}; } var x = {};"); } public void testOkLateDefinedName2() { testSame("var x = {}; function f() { x.y.z = {}; } x.y = {};"); } public void testPathologicalCaseThatsOkAnyway() { testSame( "var x = {};" + "switch (x) { " + " default: x.y.z = {}; " + " case (x.y = {}): break;" + "}", NAME_DEFINED_LATE_WARNING); } public void testOkGlobalDeclExpr() { testSame("var x = {}; /** @type {string} */ x.foo;"); } public void testBadInterfacePropRef() { testSame( "/** @interface */ function F() {}" + "F.bar();", UNDEFINED_NAME_WARNING); } public void testInterfaceFunctionPropRef() { testSame( "/** @interface */ function F() {}" + "F.call(); F.hasOwnProperty('z');"); } public void testObjectPrototypeProperties() { testSame("var x = {}; var y = x.hasOwnProperty('z');"); } public void testCustomObjectPrototypeProperties() { testSame("Object.prototype.seal = function() {};" + "var x = {}; x.seal();"); } public void testFunctionPrototypeProperties() { testSame("var x = {}; var y = x.hasOwnProperty('z');"); } public void testIndirectlyDeclaredProperties() { testSame( "Function.prototype.inherits = function(ctor) {" + " this.superClass_ = ctor;" + "};" + "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {};" + "/** @constructor */ function SubFoo() {}" + "SubFoo.inherits(Foo);" + "SubFoo.superClass_.bar();"); } public void testGoogInheritsAlias() { testSame( "Function.prototype.inherits = function(ctor) {" + " this.superClass_ = ctor;" + "};" + "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {};" + "/** @constructor */ function SubFoo() {}" + "SubFoo.inherits(Foo);" + "SubFoo.superClass_.bar();"); } public void testGoogInheritsAlias2() { testSame( CompilerTypeTestCase.CLOSURE_DEFS + "/** @constructor */ function Foo() {}" + "Foo.prototype.bar = function() {};" + "/** @constructor */ function SubFoo() {}" + "goog.inherits(SubFoo, Foo);" + "SubFoo.superClazz();", UNDEFINED_NAME_WARNING); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/JSModuleTest.java0000644000175000017500000001255312115204405026217 0ustar apoapo/* * 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.ImmutableSet; import junit.framework.TestCase; import java.util.Arrays; import java.util.List; import java.util.ArrayList; /** * Tests for {@link JSModule} * */ public class JSModuleTest extends TestCase { private JSModule mod1; private JSModule mod2; // depends on mod1 private JSModule mod3; // depends on mod1 private JSModule mod4; // depends on mod2, mod3 private JSModule mod5; // depends on mod1 @Override protected void setUp() { List modulesInDepOrder = new ArrayList(); mod1 = new JSModule("mod1"); modulesInDepOrder.add(mod1); mod2 = new JSModule("mod2"); mod2.addDependency(mod1); modulesInDepOrder.add(mod2); mod3 = new JSModule("mod3"); mod3.addDependency(mod1); modulesInDepOrder.add(mod3); mod4 = new JSModule("mod4"); mod4.addDependency(mod2); mod4.addDependency(mod3); modulesInDepOrder.add(mod4); mod5 = new JSModule("mod5"); mod5.addDependency(mod1); modulesInDepOrder.add(mod5); } public void testDependencies() { assertEquals(ImmutableSet.of(), mod1.getAllDependencies()); assertEquals(ImmutableSet.of(mod1), mod2.getAllDependencies()); assertEquals(ImmutableSet.of(mod1), mod3.getAllDependencies()); assertEquals(ImmutableSet.of(mod1, mod2, mod3), mod4.getAllDependencies()); assertEquals(ImmutableSet.of(mod1), mod1.getThisAndAllDependencies()); assertEquals(ImmutableSet.of(mod1, mod2), mod2.getThisAndAllDependencies()); assertEquals(ImmutableSet.of(mod1, mod3), mod3.getThisAndAllDependencies()); assertEquals(ImmutableSet.of(mod1, mod2, mod3, mod4), mod4.getThisAndAllDependencies()); } public void testSortInputs() throws Exception { CompilerInput a = new CompilerInput( SourceFile.fromCode("a.js", "goog.require('b');goog.require('c')")); CompilerInput b = new CompilerInput( SourceFile.fromCode("b.js", "goog.provide('b');goog.require('d')")); CompilerInput c = new CompilerInput( SourceFile.fromCode("c.js", "goog.provide('c');goog.require('d')")); CompilerInput d = new CompilerInput( SourceFile.fromCode("d.js", "goog.provide('d')")); // Independent modules. CompilerInput e = new CompilerInput( SourceFile.fromCode("e.js", "goog.provide('e')")); CompilerInput f = new CompilerInput( SourceFile.fromCode("f.js", "goog.provide('f')")); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(a, b, c, d)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, b, c, a)); assertSortedInputs( ImmutableList.of(d, c, b, a), ImmutableList.of(d, c, b, a)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, a, b, c)); assertSortedInputs( ImmutableList.of(d, b, c, a, e, f), ImmutableList.of(a, b, c, d, e, f)); assertSortedInputs( ImmutableList.of(e, f, d, b, c, a), ImmutableList.of(e, f, a, b, c, d)); assertSortedInputs( ImmutableList.of(e, d, b, c, a, f), ImmutableList.of(a, b, c, e, d, f)); assertSortedInputs( ImmutableList.of(e, f, d, b, c, a), ImmutableList.of(e, a, f, b, c, d)); } private void assertSortedInputs( List expected, List shuffled) throws Exception { JSModule mod = new JSModule("mod"); for (CompilerInput input : shuffled) { input.setModule(null); mod.add(input); } Compiler compiler = new Compiler(System.err); compiler.initCompilerOptionsIfTesting(); mod.sortInputsByDeps(compiler); assertEquals(expected, mod.getInputs()); } public void testSortJsModules() throws Exception { // already in order: assertEquals(ImmutableList.of(mod1, mod2, mod3, mod4), Arrays.asList(JSModule.sortJsModules( ImmutableList.of(mod1, mod2, mod3, mod4)))); assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), Arrays.asList(JSModule.sortJsModules( ImmutableList.of(mod1, mod3, mod2, mod4)))); // one out of order: assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), Arrays.asList(JSModule.sortJsModules( ImmutableList.of(mod4, mod3, mod2, mod1)))); assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), Arrays.asList(JSModule.sortJsModules( ImmutableList.of(mod3, mod1, mod2, mod4)))); // more out of order: assertEquals(ImmutableList.of(mod1, mod3, mod2, mod4), Arrays.asList(JSModule.sortJsModules( ImmutableList.of(mod4, mod3, mod1, mod2)))); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/DisambiguatePropertiesTest.java0000644000175000017500000014406112115204405031210 0ustar apoapo/* * 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.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.testing.BaseJSTypeTestCase; import com.google.javascript.rhino.testing.TestErrorReporter; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * Unit test for the Compiler DisambiguateProperties pass. * */ public class DisambiguatePropertiesTest extends CompilerTestCase { private DisambiguateProperties lastPass; private boolean runTightenTypes; public DisambiguatePropertiesTest() { parseTypeInfo = true; } @Override protected void setUp() throws Exception { super.setUp(); super.enableNormalize(true); super.enableTypeCheck(CheckLevel.WARNING); } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { Map propertiesToErrorFor = Maps.newHashMap(); propertiesToErrorFor.put("foobar", CheckLevel.ERROR); if (runTightenTypes) { TightenTypes tightener = new TightenTypes(compiler); tightener.process(externs, root); lastPass = DisambiguateProperties.forConcreteTypeSystem(compiler, tightener, propertiesToErrorFor); } else { // This must be created after type checking is run as it depends on // any mismatches found during checking. lastPass = DisambiguateProperties.forJSTypeSystem( compiler, propertiesToErrorFor); } lastPass.process(externs, root); } }; } @Override protected int getNumRepetitions() { return 1; } public void testOneType1() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;"; testSets(false, js, js, "{a=[[Foo.prototype]]}"); testSets(true, js, js, "{a=[[Foo.prototype]]}"); } public void testOneType2() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype = {a: 0};\n" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;"; String expected = "{a=[[Foo.prototype]]}"; testSets(false, js, js, expected); testSets(true, js, js, expected); } public void testOneType3() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype = { get a() {return 0}," + " set a(b) {} };\n" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;"; String expected = "{a=[[Foo.prototype]]}"; testSets(false, js, js, expected); testSets(true, js, js, expected); } public void testPrototypeAndInstance() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;"; testSets(false, js, js, "{a=[[Foo.prototype]]}"); testSets(true, js, js, "{a=[[Foo.prototype]]}"); } public void testPrototypeAndInstance2() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "new Foo().a = 0;"; testSets(false, js, js, "{a=[[Foo.prototype]]}"); testSets(true, js, js, "{a=[[Foo.prototype]]}"); } public void testTwoTypes1() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;" + "/** @type Bar */\n" + "var B = new Bar;\n" + "B.a = 0;"; String output = "" + "function Foo(){}" + "Foo.prototype.Foo_prototype$a=0;" + "var F=new Foo;" + "F.Foo_prototype$a=0;" + "function Bar(){}" + "Bar.prototype.Bar_prototype$a=0;" + "var B=new Bar;" + "B.Bar_prototype$a=0"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testTwoTypes2() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype = {a: 0};" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype = {a: 0};" + "/** @type Bar */\n" + "var B = new Bar;\n" + "B.a = 0;"; String output = "" + "function Foo(){}" + "Foo.prototype = {Foo_prototype$a: 0};" + "var F=new Foo;" + "F.Foo_prototype$a=0;" + "function Bar(){}" + "Bar.prototype = {Bar_prototype$a: 0};" + "var B=new Bar;" + "B.Bar_prototype$a=0"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testTwoTypes3() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype = { get a() {return 0}," + " set a(b) {} };\n" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype = { get a() {return 0}," + " set a(b) {} };\n" + "/** @type Bar */\n" + "var B = new Bar;\n" + "B.a = 0;"; String output = "" + "function Foo(){}" + "Foo.prototype = { get Foo_prototype$a() {return 0}," + " set Foo_prototype$a(b) {} };\n" + "var F=new Foo;" + "F.Foo_prototype$a=0;" + "function Bar(){}" + "Bar.prototype = { get Bar_prototype$a() {return 0}," + " set Bar_prototype$a(b) {} };\n" + "var B=new Bar;" + "B.Bar_prototype$a=0"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testTwoFields() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;" + "Foo.prototype.b = 0;" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;" + "F.b = 0;"; String output = "function Foo(){}Foo.prototype.a=0;Foo.prototype.b=0;" + "var F=new Foo;F.a=0;F.b=0"; testSets(false, js, output, "{a=[[Foo.prototype]], b=[[Foo.prototype]]}"); testSets(true, js, output, "{a=[[Foo.prototype]], b=[[Foo.prototype]]}"); } public void testTwoSeparateFieldsTwoTypes() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;" + "Foo.prototype.b = 0;" + "/** @type Foo */\n" + "var F = new Foo;\n" + "F.a = 0;" + "F.b = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;" + "Bar.prototype.b = 0;" + "/** @type Bar */\n" + "var B = new Bar;\n" + "B.a = 0;" + "B.b = 0;"; String output = "" + "function Foo(){}" + "Foo.prototype.Foo_prototype$a=0;" + "Foo.prototype.Foo_prototype$b=0;" + "var F=new Foo;" + "F.Foo_prototype$a=0;" + "F.Foo_prototype$b=0;" + "function Bar(){}" + "Bar.prototype.Bar_prototype$a=0;" + "Bar.prototype.Bar_prototype$b=0;" + "var B=new Bar;" + "B.Bar_prototype$a=0;" + "B.Bar_prototype$b=0"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + " b=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + " b=[[Bar.prototype], [Foo.prototype]]}"); } public void testUnionType() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;" + "/** @type {Bar|Foo} */\n" + "var B = new Bar;\n" + "B.a = 0;\n" + "B = new Foo;\n" + "B.a = 0;\n" + "/** @constructor */ function Baz() {}\n" + "Baz.prototype.a = 0;\n"; testSets(false, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); testSets(true, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); } public void testIgnoreUnknownType() { String js = "" + "/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.blah = 3;\n" + "/** @type {Foo} */\n" + "var F = new Foo;\n" + "F.blah = 0;\n" + "var U = function() { return {} };\n" + "U().blah();"; String expected = "" + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + "var U=function(){return{}};U().blah()"; testSets(false, js, expected, "{}"); testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, expected, "{}"); } public void testIgnoreUnknownType1() { String js = "" + "/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.blah = 3;\n" + "/** @type {Foo} */\n" + "var F = new Foo;\n" + "F.blah = 0;\n" + "/** @return {Object} */\n" + "var U = function() { return {} };\n" + "U().blah();"; String expected = "" + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + "var U=function(){return{}};U().blah()"; testSets(false, js, expected, "{blah=[[Foo.prototype]]}"); testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, expected, "{}"); } public void testIgnoreUnknownType2() { String js = "" + "/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.blah = 3;\n" + "/** @type {Foo} */\n" + "var F = new Foo;\n" + "F.blah = 0;\n" + "/** @constructor */\n" + "function Bar() {}\n" + "Bar.prototype.blah = 3;\n" + "/** @return {Object} */\n" + "var U = function() { return {} };\n" + "U().blah();"; String expected = "" + "function Foo(){}Foo.prototype.blah=3;var F = new Foo;F.blah=0;" + "function Bar(){}Bar.prototype.blah=3;" + "var U=function(){return{}};U().blah()"; testSets(false, js, expected, "{}"); testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, expected, "{}"); } public void testUnionTypeTwoFields() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "Foo.prototype.b = 0;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;\n" + "Bar.prototype.b = 0;\n" + "/** @type {Foo|Bar} */\n" + "var B = new Bar;\n" + "B.a = 0;\n" + "B.b = 0;\n" + "B = new Foo;\n" + "/** @constructor */ function Baz() {}\n" + "Baz.prototype.a = 0;\n" + "Baz.prototype.b = 0;\n"; testSets(false, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]," + " b=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); testSets(true, js, "{a=[[Bar.prototype, Foo.prototype], [Baz.prototype]]," + " b=[[Bar.prototype, Foo.prototype], [Baz.prototype]]}"); } public void testCast() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;" + "/** @type {Foo|Bar} */\n" + "var F = new Foo;\n" + "(/** @type {Bar} */(F)).a = 0;"; String output = "" + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + "var F=new Foo;F.Bar_prototype$a=0;"; String ttOutput = "" + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + "var F=new Foo;F.Unique$1$a=0;"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, ttOutput, "{a=[[Bar.prototype], [Foo.prototype], [Unique$1]]}"); } public void testConstructorFields() { String js = "" + "/** @constructor */\n" + "var Foo = function() { this.a = 0; };\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;" + "new Foo"; String output = "" + "var Foo=function(){this.Foo$a=0};" + "function Bar(){}" + "Bar.prototype.Bar_prototype$a=0;" + "new Foo"; String ttOutput = "" + "var Foo=function(){this.Foo_prototype$a=0};" + "function Bar(){}" + "Bar.prototype.Bar_prototype$a=0;" + "new Foo"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo]]}"); testSets(true, js, ttOutput, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testStaticProperty() { String js = "" + "/** @constructor */ function Foo() {} \n" + "/** @constructor */ function Bar() {}\n" + "Foo.a = 0;" + "Bar.a = 0;"; String output = "" + "function Foo(){}" + "function Bar(){}" + "Foo.function__new_Foo___undefined$a = 0;" + "Bar.function__new_Bar___undefined$a = 0;"; testSets(false, js, output, "{a=[[function (new:Bar): undefined]," + " [function (new:Foo): undefined]]}"); } public void testSupertypeWithSameField() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "/** @constructor\n* @extends Foo */ function Bar() {}\n" + "/** @override */\n" + "Bar.prototype.a = 0;\n" + "/** @type Bar */ var B = new Bar;\n" + "B.a = 0;" + "/** @constructor */ function Baz() {}\n" + "Baz.prototype.a = function(){};\n"; String output = "" + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + "function Bar(){}Bar.prototype.Foo_prototype$a=0;" + "var B = new Bar;B.Foo_prototype$a=0;" + "function Baz(){}Baz.prototype.Baz_prototype$a=function(){};"; String ttOutput = "" + "function Foo(){}Foo.prototype.Foo_prototype$a=0;" + "function Bar(){}Bar.prototype.Bar_prototype$a=0;" + "var B = new Bar;B.Bar_prototype$a=0;" + "function Baz(){}Baz.prototype.Baz_prototype$a=function(){};"; testSets(false, js, output, "{a=[[Baz.prototype], [Foo.prototype]]}"); testSets(true, js, ttOutput, "{a=[[Bar.prototype], [Baz.prototype], [Foo.prototype]]}"); } public void testScopedType() { String js = "" + "var g = {};\n" + "/** @constructor */ g.Foo = function() {}\n" + "g.Foo.prototype.a = 0;" + "/** @constructor */ g.Bar = function() {}\n" + "g.Bar.prototype.a = 0;"; String output = "" + "var g={};" + "g.Foo=function(){};" + "g.Foo.prototype.g_Foo_prototype$a=0;" + "g.Bar=function(){};" + "g.Bar.prototype.g_Bar_prototype$a=0;"; testSets(false, js, output, "{a=[[g.Bar.prototype], [g.Foo.prototype]]}"); testSets(true, js, output, "{a=[[g.Bar.prototype], [g.Foo.prototype]]}"); } public void testUnresolvedType() { // NOTE(nicksantos): This behavior seems very wrong to me. String js = "" + "var g = {};" + "/** @constructor \n @extends {?} */ " + "var Foo = function() {};\n" + "Foo.prototype.a = 0;" + "/** @constructor */ var Bar = function() {};\n" + "Bar.prototype.a = 0;"; String output = "" + "var g={};" + "var Foo=function(){};" + "Foo.prototype.Foo_prototype$a=0;" + "var Bar=function(){};" + "Bar.prototype.Bar_prototype$a=0;"; setExpectParseWarningsThisTest(); testSets(false, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testNamedType() { String js = "" + "var g = {};" + "/** @constructor \n @extends g.Late */ var Foo = function() {}\n" + "Foo.prototype.a = 0;" + "/** @constructor */ var Bar = function() {}\n" + "Bar.prototype.a = 0;" + "/** @constructor */ g.Late = function() {}"; String output = "" + "var g={};" + "var Foo=function(){};" + "Foo.prototype.Foo_prototype$a=0;" + "var Bar=function(){};" + "Bar.prototype.Bar_prototype$a=0;" + "g.Late = function(){}"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testUnknownType() { String js = "" + "/** @constructor */ var Foo = function() {};\n" + "/** @constructor */ var Bar = function() {};\n" + "/** @return {?} */ function fun() {}\n" + "Foo.prototype.a = fun();\n" + "fun().a;\n" + "Bar.prototype.a = 0;"; String ttOutput = "" + "var Foo=function(){};\n" + "var Bar=function(){};\n" + "function fun(){}\n" + "Foo.prototype.Foo_prototype$a=fun();\n" + "fun().Unique$1$a;\n" + "Bar.prototype.Bar_prototype$a=0;"; testSets(false, js, js, "{}"); testSets(true, BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES, js, ttOutput, "{a=[[Bar.prototype], [Foo.prototype], [Unique$1]]}"); } public void testEnum() { String js = "" + "/** @enum {string} */ var En = {\n" + " A: 'first',\n" + " B: 'second'\n" + "};\n" + "var EA = En.A;\n" + "var EB = En.B;\n" + "/** @constructor */ function Foo(){};\n" + "Foo.prototype.A = 0;\n" + "Foo.prototype.B = 0;\n"; String output = "" + "var En={A:'first',B:'second'};" + "var EA=En.A;" + "var EB=En.B;" + "function Foo(){};" + "Foo.prototype.Foo_prototype$A=0;" + "Foo.prototype.Foo_prototype$B=0"; String ttOutput = "" + "var En={A:'first',B:'second'};" + "var EA=En.A;" + "var EB=En.B;" + "function Foo(){};" + "Foo.prototype.Foo_prototype$A=0;" + "Foo.prototype.Foo_prototype$B=0"; testSets(false, js, output, "{A=[[Foo.prototype]], B=[[Foo.prototype]]}"); testSets(true, js, ttOutput, "{A=[[Foo.prototype]], B=[[Foo.prototype]]}"); } public void testEnumOfObjects() { String js = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.format = function() {};" + "/** @enum {!Formatter} */ var Enum = {\n" + " A: new Formatter()\n" + "};\n" + "Enum.A.format();\n"; String output = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.Formatter_prototype$format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.Unrelated_prototype$format = function() {};" + "/** @enum {!Formatter} */ var Enum = {\n" + " A: new Formatter()\n" + "};\n" + "Enum.A.Formatter_prototype$format();\n"; testSets(false, js, output, "{format=[[Formatter.prototype], [Unrelated.prototype]]}"); // TODO(nicksantos): Fix the type tightener to handle this case. // It currently doesn't work, because getSubTypes is broken for enums. } public void testEnumOfObjects2() { String js = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.format = function() {};" + "/** @enum {?Formatter} */ var Enum = {\n" + " A: new Formatter(),\n" + " B: new Formatter()\n" + "};\n" + "function f() {\n" + " var formatter = window.toString() ? Enum.A : Enum.B;\n" + " formatter.format();\n" + "}"; String output = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.format = function() {};" + "/** @enum {?Formatter} */ var Enum = {\n" + " A: new Formatter(),\n" + " B: new Formatter()\n" + "};\n" + "function f() {\n" + " var formatter = window.toString() ? Enum.A : Enum.B;\n" + " formatter.format();\n" + "}"; testSets(false, js, output, "{}"); } public void testEnumOfObjects3() { String js = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.format = function() {};" + "/** @enum {!Formatter} */ var Enum = {\n" + " A: new Formatter(),\n" + " B: new Formatter()\n" + "};\n" + "/** @enum {!Enum} */ var SubEnum = {\n" + " C: Enum.A\n" + "};\n" + "function f() {\n" + " var formatter = SubEnum.C\n" + " formatter.format();\n" + "}"; String output = "" + "/** @constructor */ function Formatter() {}" + "Formatter.prototype.Formatter_prototype$format = function() {};" + "/** @constructor */ function Unrelated() {}" + "Unrelated.prototype.Unrelated_prototype$format = function() {};" + "/** @enum {!Formatter} */ var Enum = {\n" + " A: new Formatter(),\n" + " B: new Formatter()\n" + "};\n" + "/** @enum {!Enum} */ var SubEnum = {\n" + " C: Enum.A\n" + "};\n" + "function f() {\n" + " var formatter = SubEnum.C\n" + " formatter.Formatter_prototype$format();\n" + "}"; testSets(false, js, output, "{format=[[Formatter.prototype], [Unrelated.prototype]]}"); } public void testUntypedExterns() { String externs = BaseJSTypeTestCase.ALL_NATIVE_EXTERN_TYPES + "var window;" + "window.alert = function() {x};"; String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "Foo.prototype.alert = 0;\n" + "Foo.prototype.window = 0;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;\n" + "Bar.prototype.alert = 0;\n" + "Bar.prototype.window = 0;\n" + "window.alert();"; String output = "" + "function Foo(){}" + "Foo.prototype.Foo_prototype$a=0;" + "Foo.prototype.alert=0;" + "Foo.prototype.Foo_prototype$window=0;" + "function Bar(){}" + "Bar.prototype.Bar_prototype$a=0;" + "Bar.prototype.alert=0;" + "Bar.prototype.Bar_prototype$window=0;" + "window.alert();"; testSets(false, externs, js, output, "{a=[[Bar.prototype], [Foo.prototype]]" + ", window=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, externs, js, output, "{a=[[Bar.prototype], [Foo.prototype]]," + " window=[[Bar.prototype], [Foo.prototype]]}"); } public void testUnionTypeInvalidation() { String externs = "" + "/** @constructor */ function Baz() {}" + "Baz.prototype.a"; String js = "" + "/** @constructor */ function Ind() {this.a=0}\n" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;\n" + "/** @type {Foo|Bar} */\n" + "var F = new Foo;\n" + "F.a = 1\n;" + "F = new Bar;\n" + "/** @type {Baz} */\n" + "var Z = new Baz;\n" + "Z.a = 1\n;" + "/** @type {Bar|Baz} */\n" + "var B = new Baz;\n" + "B.a = 1;\n" + "B = new Bar;\n"; // Only the constructor field a of Ind is renamed, as Foo is related to Baz // through Bar in the unions Bar|Baz and Foo|Bar. String output = "" + "function Ind() { this.Ind$a = 0; }" + "function Foo() {}" + "Foo.prototype.a = 0;" + "function Bar() {}" + "Bar.prototype.a = 0;" + "var F = new Foo;" + "F.a = 1;" + "F = new Bar;" + "var Z = new Baz;" + "Z.a = 1;" + "var B = new Baz;" + "B.a = 1;" + "B = new Bar;"; String ttOutput = "" + "function Ind() { this.Unique$1$a = 0; }" + "function Foo() {}" + "Foo.prototype.a = 0;" + "function Bar() {}" + "Bar.prototype.a = 0;" + "var F = new Foo;" + "F.a = 1;" + "F = new Bar;" + "var Z = new Baz;" + "Z.a = 1;" + "var B = new Baz;" + "B.a = 1;" + "B = new Bar;"; testSets(false, externs, js, output, "{a=[[Ind]]}"); testSets(true, externs, js, ttOutput, "{a=[[Unique$1]]}"); } public void testUnionAndExternTypes() { String externs = "" + "/** @constructor */ function Foo() { }" + "Foo.prototype.a = 4;\n"; String js = "" + "/** @constructor */ function Bar() { this.a = 2; }\n" + "/** @constructor */ function Baz() { this.a = 3; }\n" + "/** @constructor */ function Buz() { this.a = 4; }\n" + "/** @constructor */ function T1() { this.a = 3; }\n" + "/** @constructor */ function T2() { this.a = 3; }\n" + "/** @type {Bar|Baz} */ var b;\n" + "/** @type {Baz|Buz} */ var c;\n" + "/** @type {Buz|Foo} */ var d;\n" + "b.a = 5; c.a = 6; d.a = 7;"; String output = "" + "/** @constructor */ function Bar() { this.a = 2; }\n" + "/** @constructor */ function Baz() { this.a = 3; }\n" + "/** @constructor */ function Buz() { this.a = 4; }\n" + "/** @constructor */ function T1() { this.T1$a = 3; }\n" + "/** @constructor */ function T2() { this.T2$a = 3; }\n" + "/** @type {Bar|Baz} */ var b;\n" + "/** @type {Baz|Buz} */ var c;\n" + "/** @type {Buz|Foo} */ var d;\n" + "b.a = 5; c.a = 6; d.a = 7;"; // We are testing the skipping of multiple types caused by unionizing with // extern types. testSets(false, externs, js, output, "{a=[[T1], [T2]]}"); } public void testTypedExterns() { String externs = "" + "/** @constructor */ function Window() {};\n" + "Window.prototype.alert;" + "/** @type {Window} */" + "var window;"; String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.alert = 0;\n" + "window.alert('blarg');"; String output = "" + "function Foo(){}" + "Foo.prototype.Foo_prototype$alert=0;" + "window.alert('blarg');"; testSets(false, externs, js, output, "{alert=[[Foo.prototype]]}"); testSets(true, externs, js, output, "{alert=[[Foo.prototype]]}"); } public void testSubtypesWithSameField() { String js = "" + "/** @constructor */ function Top() {}\n" + "/** @constructor \n@extends Top*/ function Foo() {}\n" + "Foo.prototype.a;\n" + "/** @constructor \n@extends Top*/ function Bar() {}\n" + "Bar.prototype.a;\n" + "/** @param {Top} top */" + "function foo(top) {\n" + " var x = top.a;\n" + "}\n" + "foo(new Foo);\n" + "foo(new Bar);\n"; testSets(false, js, "{}"); testSets(true, js, "{a=[[Bar.prototype, Foo.prototype]]}"); } public void testSupertypeReferenceOfSubtypeProperty() { String externs = "" + "/** @constructor */ function Ext() {}" + "Ext.prototype.a;"; String js = "" + "/** @constructor */ function Foo() {}\n" + "/** @constructor \n@extends Foo*/ function Bar() {}\n" + "Bar.prototype.a;\n" + "/** @param {Foo} foo */" + "function foo(foo) {\n" + " var x = foo.a;\n" + "}\n"; String result = "" + "function Foo() {}\n" + "function Bar() {}\n" + "Bar.prototype.Bar_prototype$a;\n" + "function foo(foo$$1) {\n" + " var x = foo$$1.Bar_prototype$a;\n" + "}\n"; testSets(false, externs, js, result, "{a=[[Bar.prototype]]}"); } public void testObjectLiteralNotRenamed() { String js = "" + "var F = {a:'a', b:'b'};" + "F.a = 'z';"; testSets(false, js, js, "{}"); testSets(true, js, js, "{}"); } public void testObjectLiteralReflected() { String js = "" + "var goog = {};" + "goog.reflect = {};" + "goog.reflect.object = function(x, y) { return y; };" + "/** @constructor */ function F() {}" + "/** @type {number} */ F.prototype.foo = 3;" + "/** @constructor */ function G() {}" + "/** @type {number} */ G.prototype.foo = 3;" + "goog.reflect.object(F, {foo: 5});"; String result = "" + "var goog = {};" + "goog.reflect = {};" + "goog.reflect.object = function(x, y) { return y; };" + "function F() {}" + "F.prototype.F_prototype$foo = 3;" + "function G() {}" + "G.prototype.G_prototype$foo = 3;" + "goog.reflect.object(F, {F_prototype$foo: 5});"; testSets(false, js, result, "{foo=[[F.prototype], [G.prototype]]}"); testSets(true, js, result, "{foo=[[F.prototype], [G.prototype]]}"); } public void testObjectLiteralLends() { String js = "" + "var mixin = function(x) { return x; };" + "/** @constructor */ function F() {}" + "/** @type {number} */ F.prototype.foo = 3;" + "/** @constructor */ function G() {}" + "/** @type {number} */ G.prototype.foo = 3;" + "mixin(/** @lends {F.prototype} */ ({foo: 5}));"; String result = "" + "var mixin = function(x) { return x; };" + "function F() {}" + "F.prototype.F_prototype$foo = 3;" + "function G() {}" + "G.prototype.G_prototype$foo = 3;" + "mixin(/** @lends {F.prototype} */ ({F_prototype$foo: 5}));"; testSets(false, js, result, "{foo=[[F.prototype], [G.prototype]]}"); testSets(true, js, result, "{foo=[[F.prototype], [G.prototype]]}"); } public void testClosureInherits() { String js = "" + "var goog = {};" + "/** @param {Function} childCtor Child class.\n" + " * @param {Function} parentCtor Parent class. */\n" + "goog.inherits = function(childCtor, parentCtor) {\n" + " /** @constructor */\n" + " function tempCtor() {};\n" + " tempCtor.prototype = parentCtor.prototype;\n" + " childCtor.superClass_ = parentCtor.prototype;\n" + " childCtor.prototype = new tempCtor();\n" + " childCtor.prototype.constructor = childCtor;\n" + "};" + "/** @constructor */ function Top() {}\n" + "Top.prototype.f = function() {};" + "/** @constructor \n@extends Top*/ function Foo() {}\n" + "goog.inherits(Foo, Top);\n" + "/** @override */\n" + "Foo.prototype.f = function() {" + " Foo.superClass_.f();" + "};\n" + "/** @constructor \n* @extends Foo */ function Bar() {}\n" + "goog.inherits(Bar, Foo);\n" + "/** @override */\n" + "Bar.prototype.f = function() {" + " Bar.superClass_.f();" + "};\n" + "(new Bar).f();\n"; testSets(false, js, "{f=[[Top.prototype]]}"); testSets(true, js, "{constructor=[[Bar.prototype, Foo.prototype]], " + "f=[[Bar.prototype], [Foo.prototype], [Top.prototype]]}"); } public void testSkipNativeFunctionMethod() { String externs = "" + "/** @constructor \n @param {*} var_args */" + "function Function(var_args) {}" + "Function.prototype.call = function() {};"; String js = "" + "/** @constructor */ function Foo(){};" + "/** @constructor\n @extends Foo */" + "function Bar() { Foo.call(this); };"; // call should not be renamed testSame(externs, js, null); } public void testSkipNativeObjectMethod() { String externs = "" + "/** @constructor \n @param {*} opt_v */ function Object(opt_v) {}" + "Object.prototype.hasOwnProperty;"; String js = "" + "/** @constructor */ function Foo(){};" + "(new Foo).hasOwnProperty('x');"; testSets(false, externs, js, js, "{}"); testSets(true, externs, js, js, "{}"); } public void testExtendNativeType() { String externs = "" + "/** @constructor \n @return {string} */" + "function Date(opt_1, opt_2, opt_3, opt_4, opt_5, opt_6, opt_7) {}" + "/** @override */ Date.prototype.toString = function() {}"; String js = "" + "/** @constructor\n @extends {Date} */ function SuperDate() {};\n" + "(new SuperDate).toString();"; testSets(true, externs, js, js, "{}"); testSets(false, externs, js, js, "{}"); } public void testStringFunction() { // Extern functions are not renamed, but user functions on a native // prototype object are. String externs = "/**@constructor\n@param {*} opt_str \n @return {string}*/" + "function String(opt_str) {};\n" + "/** @override \n @return {string} */\n" + "String.prototype.toString = function() { };\n"; String js = "" + "/** @constructor */ function Foo() {};\n" + "Foo.prototype.foo = function() {};\n" + "String.prototype.foo = function() {};\n" + "var a = 'str'.toString().foo();\n"; String output = "" + "function Foo() {};\n" + "Foo.prototype.Foo_prototype$foo = function() {};\n" + "String.prototype.String_prototype$foo = function() {};\n" + "var a = 'str'.toString().String_prototype$foo();\n"; testSets(false, externs, js, output, "{foo=[[Foo.prototype], [String.prototype]]}"); testSets(true, externs, js, output, "{foo=[[Foo.prototype], [String.prototype]]}"); } public void testUnusedTypeInExterns() { String externs = "" + "/** @constructor */ function Foo() {};\n" + "Foo.prototype.a"; String js = "" + "/** @constructor */ function Bar() {};\n" + "Bar.prototype.a;" + "/** @constructor */ function Baz() {};\n" + "Baz.prototype.a;"; String output = "" + "/** @constructor */ function Bar() {};\n" + "Bar.prototype.Bar_prototype$a;" + "/** @constructor */ function Baz() {};\n" + "Baz.prototype.Baz_prototype$a"; testSets(false, externs, js, output, "{a=[[Bar.prototype], [Baz.prototype]]}"); testSets(true, externs, js, output, "{a=[[Bar.prototype], [Baz.prototype]]}"); } public void testInterface() { String js = "" + "/** @interface */ function I() {};\n" + "I.prototype.a;\n" + "/** @constructor \n @implements I */ function Foo() {};\n" + "Foo.prototype.a;\n" + "/** @type I */\n" + "var F = new Foo;" + "var x = F.a;"; testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); testSets(true, js, "{a=[[Foo.prototype], [I.prototype]]}"); } public void testInterfaceOfSuperclass() { String js = "" + "/** @interface */ function I() {};\n" + "I.prototype.a;\n" + "/** @constructor \n @implements I */ function Foo() {};\n" + "Foo.prototype.a;\n" + "/** @constructor \n @extends Foo */ function Bar() {};\n" + "Bar.prototype.a;\n" + "/** @type Bar */\n" + "var B = new Bar;" + "B.a = 0"; testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); testSets(true, js, "{a=[[Bar.prototype], [Foo.prototype], [I.prototype]]}"); } public void testTwoInterfacesWithSomeInheritance() { String js = "" + "/** @interface */ function I() {};\n" + "I.prototype.a;\n" + "/** @interface */ function I2() {};\n" + "I2.prototype.a;\n" + "/** @constructor \n @implements I */ function Foo() {};\n" + "Foo.prototype.a;\n" + "/** @constructor \n @extends Foo \n @implements I2*/\n" + "function Bar() {};\n" + "Bar.prototype.a;\n" + "/** @type Bar */\n" + "var B = new Bar;" + "B.a = 0"; testSets(false, js, "{a=[[Foo.prototype, I.prototype, I2.prototype]]}"); testSets(true, js, "{a=[[Bar.prototype], [Foo.prototype], " + "[I.prototype], [I2.prototype]]}"); } public void testInvalidatingInterface() { String js = "" + "/** @interface */ function I2() {};\n" + "I2.prototype.a;\n" + "/** @constructor */ function Bar() {}\n" + "/** @type I */\n" + "var i = new Bar;\n" // Make I invalidating + "/** @constructor \n @implements I \n @implements I2 */" + "function Foo() {};\n" + "/** @override */\n" + "Foo.prototype.a = 0;\n" + "(new Foo).a = 0;" + "/** @interface */ function I() {};\n" + "I.prototype.a;\n"; testSets(false, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING); testSets(true, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING); } public void testMultipleInterfaces() { String js = "" + "/** @interface */ function I() {};\n" + "/** @interface */ function I2() {};\n" + "I2.prototype.a;\n" + "/** @constructor \n @implements I \n @implements I2 */" + "function Foo() {};\n" + "/** @override */" + "Foo.prototype.a = 0;\n" + "(new Foo).a = 0"; testSets(false, js, "{a=[[Foo.prototype, I2.prototype]]}"); testSets(true, js, "{a=[[Foo.prototype], [I2.prototype]]}"); } public void testInterfaceWithSupertypeImplementor() { String js = "" + "/** @interface */ function C() {}\n" + "C.prototype.foo = function() {};\n" + "/** @constructor */ function A (){}\n" + "A.prototype.foo = function() {};\n" + "/** @constructor \n @implements {C} \n @extends {A} */\n" + "function B() {}\n" + "/** @type {C} */ var b = new B();\n" + "b.foo();\n"; testSets(false, js, "{foo=[[A.prototype, C.prototype]]}"); testSets(true, js, "{foo=[[A.prototype], [C.prototype]]}"); } public void testSuperInterface() { String js = "" + "/** @interface */ function I() {};\n" + "I.prototype.a;\n" + "/** @interface \n @extends I */ function I2() {};\n" + "/** @constructor \n @implements I2 */" + "function Foo() {};\n" + "/** @override */\n" + "Foo.prototype.a = 0;\n" + "(new Foo).a = 0"; testSets(false, js, "{a=[[Foo.prototype, I.prototype]]}"); testSets(true, js, "{a=[[Foo.prototype], [I.prototype]]}"); } public void testInterfaceUnionWithCtor() { String js = "" + "/** @interface */ function I() {};\n" + "/** @type {!Function} */ I.prototype.addEventListener;\n" + "/** @constructor \n * @implements {I} */ function Impl() {};\n" + "/** @type {!Function} */ Impl.prototype.addEventListener;" + "/** @constructor */ function C() {};\n" + "/** @type {!Function} */ C.prototype.addEventListener;" + "/** @param {C|I} x */" + "function f(x) { x.addEventListener(); };\n" + "f(new C()); f(new Impl());"; testSets(false, js, js, "{addEventListener=[[C.prototype, I.prototype, Impl.prototype]]}"); // In the tightened case, the disambiguator is smart enough to // disambiguate Impl's method from the interface method. String tightenedOutput = "" + "function I() {};\n" + "I.prototype.I_prototype$addEventListener;\n" + "function Impl() {};\n" + "Impl.prototype.C_prototype$addEventListener;" + "function C() {};\n" + "C.prototype.C_prototype$addEventListener;" + "/** @param {C|I} x */" + "function f(x) { x.C_prototype$addEventListener(); };\n" + "f(new C()); f(new Impl());"; testSets(true, js, tightenedOutput, "{addEventListener=[[C.prototype, Impl.prototype], [I.prototype]]}"); } public void testExternInterfaceUnionWithCtor() { String externs = "" + "/** @interface */ function I() {};\n" + "/** @type {!Function} */ I.prototype.addEventListener;\n" + "/** @constructor \n * @implements {I} */ function Impl() {};\n" + "/** @type {!Function} */ Impl.prototype.addEventListener;"; String js = "" + "/** @constructor */ function C() {};\n" + "/** @type {!Function} */ C.prototype.addEventListener;" + "/** @param {C|I} x */" + "function f(x) { x.addEventListener(); };\n" + "f(new C()); f(new Impl());"; testSets(false, externs, js, js, "{}"); testSets(true, externs, js, js, "{}"); } /** * Tests that the type based version skips renaming on types that have a * mismatch, and the type tightened version continues to work as normal. */ public void testMismatchInvalidation() { String js = "" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a = 0;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a = 0;\n" + "/** @type Foo */\n" + "var F = new Bar;\n" + "F.a = 0;"; testSets(false, "", js, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING, "initializing variable\n" + "found : Bar\n" + "required: (Foo|null)"); testSets(true, "", js, js, "{}", TypeValidator.TYPE_MISMATCH_WARNING, "initializing variable\n" + "found : Bar\n" + "required: (Foo|null)"); } public void testBadCast() { String js = "/** @constructor */ function Foo() {};\n" + "Foo.prototype.a = 0;\n" + "/** @constructor */ function Bar() {};\n" + "Bar.prototype.a = 0;\n" + "var a = /** @type {!Foo} */ (new Bar);\n" + "a.a = 4;"; testSets(false, "", js, js, "{}", TypeValidator.INVALID_CAST, "invalid cast - must be a subtype or supertype\n" + "from: Bar\n" + "to : Foo"); } public void testDeterministicNaming() { String js = "/** @constructor */function A() {}\n" + "/** @return {string} */A.prototype.f = function() {return 'a';};\n" + "/** @constructor */function B() {}\n" + "/** @return {string} */B.prototype.f = function() {return 'b';};\n" + "/** @constructor */function C() {}\n" + "/** @return {string} */C.prototype.f = function() {return 'c';};\n" + "/** @type {A|B} */var ab = 1 ? new B : new A;\n" + "/** @type {string} */var n = ab.f();\n"; String output = "function A() {}\n" + "A.prototype.A_prototype$f = function() { return'a'; };\n" + "function B() {}\n" + "B.prototype.A_prototype$f = function() { return'b'; };\n" + "function C() {}\n" + "C.prototype.C_prototype$f = function() { return'c'; };\n" + "var ab = 1 ? new B : new A; var n = ab.A_prototype$f();\n"; for (int i = 0; i < 5; i++) { testSets(false, js, output, "{f=[[A.prototype, B.prototype], [C.prototype]]}"); testSets(true, js, output, "{f=[[A.prototype, B.prototype], [C.prototype]]}"); } } public void testObjectLiteral() { String js = "/** @constructor */ function Foo() {}\n" + "Foo.prototype.a;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.a;\n" + "var F = /** @type {Foo} */({ a: 'a' });\n"; String output = "function Foo() {}\n" + "Foo.prototype.Foo_prototype$a;\n" + "function Bar() {}\n" + "Bar.prototype.Bar_prototype$a;\n" + "var F = { Foo_prototype$a: 'a' };\n"; testSets(false, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); testSets(true, js, output, "{a=[[Bar.prototype], [Foo.prototype]]}"); } public void testCustomInherits() { String js = "Object.prototype.inheritsFrom = function(shuper) {\n" + " /** @constructor */\n" + " function Inheriter() { }\n" + " Inheriter.prototype = shuper.prototype;\n" + " this.prototype = new Inheriter();\n" + " this.superConstructor = shuper;\n" + "};\n" + "function Foo(var1, var2, strength) {\n" + " Foo.superConstructor.call(this, strength);\n" + "}" + "Foo.inheritsFrom(Object);"; String externs = "" + "function Function(var_args) {}" + "/** @return {*} */Function.prototype.call = function(var_args) {};"; testSets(false, externs, js, js, "{}"); } public void testSkipNativeFunctionStaticProperty() { String js = "" + "/** @param {!Function} ctor */\n" + "function addSingletonGetter(ctor) { ctor.a; }\n" + "/** @constructor */ function Foo() {}\n" + "Foo.a = 0;" + "/** @constructor */ function Bar() {}\n" + "Bar.a = 0;"; String output = "" + "function addSingletonGetter(ctor){ctor.a}" + "function Foo(){}" + "Foo.a=0;" + "function Bar(){}" + "Bar.a=0"; testSets(false, js, output, "{}"); } public void testErrorOnProtectedProperty() { test("function addSingletonGetter(foo) { foo.foobar = 'a'; };", null, DisambiguateProperties.Warnings.INVALIDATION); assertTrue(getLastCompiler().getErrors()[0].toString().contains("foobar")); } public void testMismatchForbiddenInvalidation() { test("/** @constructor */ function F() {}" + "/** @type {number} */ F.prototype.foobar = 3;" + "/** @return {number} */ function g() { return new F(); }", null, DisambiguateProperties.Warnings.INVALIDATION); assertTrue(getLastCompiler().getErrors()[0].toString() .contains("Consider fixing errors")); } public void testUnionTypeInvalidationError() { String externs = "" + "/** @constructor */ function Baz() {}" + "Baz.prototype.foobar"; String js = "" + "/** @constructor */ function Ind() {this.foobar=0}\n" + "/** @constructor */ function Foo() {}\n" + "Foo.prototype.foobar = 0;\n" + "/** @constructor */ function Bar() {}\n" + "Bar.prototype.foobar = 0;\n" + "/** @type {Foo|Bar} */\n" + "var F = new Foo;\n" + "F.foobar = 1\n;" + "F = new Bar;\n" + "/** @type {Baz} */\n" + "var Z = new Baz;\n" + "Z.foobar = 1\n;"; test( externs, js, "", DisambiguateProperties.Warnings.INVALIDATION_ON_TYPE, null); assertTrue(getLastCompiler().getErrors()[0].toString() .contains("foobar")); } public void runFindHighestTypeInChain() { // Check that this doesn't go into an infinite loop. DisambiguateProperties.forJSTypeSystem(new Compiler(), Maps.newHashMap()) .getTypeWithProperty("no", new JSTypeRegistry(new TestErrorReporter(null, null)) .getNativeType(JSTypeNative.OBJECT_PROTOTYPE)); } @SuppressWarnings("unchecked") private void testSets(boolean runTightenTypes, String js, String expected, String fieldTypes) { this.runTightenTypes = runTightenTypes; test(js, expected); assertEquals( fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); } @SuppressWarnings("unchecked") private void testSets(boolean runTightenTypes, String externs, String js, String expected, String fieldTypes) { testSets(runTightenTypes, externs, js, expected, fieldTypes, null, null); } @SuppressWarnings("unchecked") private void testSets(boolean runTightenTypes, String externs, String js, String expected, String fieldTypes, DiagnosticType warning, String description) { this.runTightenTypes = runTightenTypes; test(externs, js, expected, null, warning, description); assertEquals( fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); } /** * Compiles the code and checks that the set of types for each field matches * the expected value. * *

The format for the set of types for fields is: * {field=[[Type1, Type2]]} */ private void testSets(boolean runTightenTypes, String js, String fieldTypes) { this.runTightenTypes = runTightenTypes; test(js, null, null, null); assertEquals(fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); } /** * Compiles the code and checks that the set of types for each field matches * the expected value. * *

The format for the set of types for fields is: * {field=[[Type1, Type2]]} */ private void testSets(boolean runTightenTypes, String js, String fieldTypes, DiagnosticType warning) { this.runTightenTypes = runTightenTypes; test(js, null, null, warning); assertEquals(fieldTypes, mapToString(lastPass.getRenamedTypesForTesting())); } /** Sorts the map and converts to a string for comparison purposes. */ private String mapToString(Multimap> map) { TreeMap retMap = Maps.newTreeMap(); for (String key : map.keySet()) { TreeSet treeSet = Sets.newTreeSet(); for (Collection collection : map.get(key)) { Set subSet = Sets.newTreeSet(); for (T type : collection) { subSet.add(type.toString()); } treeSet.add(subSet.toString()); } retMap.put(key, treeSet.toString()); } return retMap.toString(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/GoogleCodingConventionTest.java0000644000175000017500000001227212115204405031136 0ustar apoapo/* * Copyright 2007 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.CodingConvention.SubclassRelationship; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import junit.framework.TestCase; /** * Test class for {@link GoogleCodingConvention}. */ public class GoogleCodingConventionTest extends TestCase { private GoogleCodingConvention conv = new GoogleCodingConvention(); public void testVarAndOptionalParams() { Node args = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "a"), Node.newString(Token.NAME, "b")); Node optArgs = new Node(Token.PARAM_LIST, Node.newString(Token.NAME, "opt_a"), Node.newString(Token.NAME, "opt_b")); assertFalse(conv.isVarArgsParameter(args.getFirstChild())); assertFalse(conv.isVarArgsParameter(args.getLastChild())); assertFalse(conv.isVarArgsParameter(optArgs.getFirstChild())); assertFalse(conv.isVarArgsParameter(optArgs.getLastChild())); assertFalse(conv.isOptionalParameter(args.getFirstChild())); assertFalse(conv.isOptionalParameter(args.getLastChild())); assertTrue(conv.isOptionalParameter(optArgs.getFirstChild())); assertTrue(conv.isOptionalParameter(optArgs.getLastChild())); } public void testInlineName() { assertFalse(conv.isConstant("a")); assertTrue(conv.isConstant("XYZ123_")); assertTrue(conv.isConstant("ABC")); assertFalse(conv.isConstant("ABCdef")); assertFalse(conv.isConstant("aBC")); assertFalse(conv.isConstant("A")); assertFalse(conv.isConstant("_XYZ123")); assertTrue(conv.isConstant("a$b$XYZ123_")); assertTrue(conv.isConstant("a$b$ABC_DEF")); assertTrue(conv.isConstant("a$b$A")); assertFalse(conv.isConstant("a$b$a")); assertFalse(conv.isConstant("a$b$ABCdef")); assertFalse(conv.isConstant("a$b$aBC")); assertFalse(conv.isConstant("a$b$")); assertFalse(conv.isConstant("$")); } public void testExportedName() { assertTrue(conv.isExported("_a")); assertTrue(conv.isExported("_a_")); assertFalse(conv.isExported("a")); assertFalse(conv.isExported("$super", false)); assertTrue(conv.isExported("$super", true)); assertTrue(conv.isExported("$super")); } public void testPrivateName() { assertTrue(conv.isPrivate("a_")); assertFalse(conv.isPrivate("a")); assertFalse(conv.isPrivate("_a_")); } public void testEnumKey() { assertTrue(conv.isValidEnumKey("A")); assertTrue(conv.isValidEnumKey("123")); assertTrue(conv.isValidEnumKey("FOO_BAR")); assertFalse(conv.isValidEnumKey("a")); assertFalse(conv.isValidEnumKey("someKeyInCamelCase")); assertFalse(conv.isValidEnumKey("_FOO_BAR")); } public void testInheritanceDetection1() { assertNotClassDefining("goog.foo(A, B);"); } public void testInheritanceDetection2() { assertDefinesClasses("goog.inherits(A, B);", "A", "B"); } public void testInheritanceDetection3() { assertDefinesClasses("A.inherits(B);", "A", "B"); } public void testInheritanceDetection4() { assertDefinesClasses("goog.inherits(goog.A, goog.B);", "goog.A", "goog.B"); } public void testInheritanceDetection5() { assertDefinesClasses("goog.A.inherits(goog.B);", "goog.A", "goog.B"); } public void testInheritanceDetection6() { assertNotClassDefining("A.inherits(this.B);"); } public void testInheritanceDetection7() { assertNotClassDefining("this.A.inherits(B);"); } public void testInheritanceDetection8() { assertNotClassDefining("goog.inherits(A, B, C);"); } public void testInheritanceDetection9() { assertDefinesClasses("A.mixin(B.prototype);", "A", "B"); } public void testInheritanceDetection10() { assertDefinesClasses("goog.mixin(A.prototype, B.prototype);", "A", "B"); } public void testInheritanceDetectionPostCollapseProperties() { assertDefinesClasses("goog$inherits(A, B);", "A", "B"); assertNotClassDefining("goog$inherits(A);"); } private void assertNotClassDefining(String code) { Node n = parseTestCode(code); assertNull(conv.getClassesDefinedByCall(n.getFirstChild())); } private void assertDefinesClasses(String code, String subclassName, String superclassName) { Node n = parseTestCode(code); SubclassRelationship classes = conv.getClassesDefinedByCall(n.getFirstChild()); assertNotNull(classes); assertEquals(subclassName, classes.subclassName); assertEquals(superclassName, classes.superclassName); } private Node parseTestCode(String code) { Compiler compiler = new Compiler(); return compiler.parseTestCode(code).getFirstChild(); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ScopedAliasesTest.java0000644000175000017500000005306212115204405027254 0ustar apoapo/* * 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.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CompilerOptions.AliasTransformation; import com.google.javascript.jscomp.CompilerOptions.AliasTransformationHandler; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.SourcePosition; import java.util.Collection; import java.util.List; import java.util.Map; /** * Tests for {@link ScopedAliases} * * @author robbyw@google.com (Robby Walker) */ public class ScopedAliasesTest extends CompilerTestCase { private static final String GOOG_SCOPE_START_BLOCK = "goog.scope(function() {"; private static final String GOOG_SCOPE_END_BLOCK = "});"; private static String EXTERNS = "var window;"; AliasTransformationHandler transformationHandler = CompilerOptions.NULL_ALIAS_TRANSFORMATION_HANDLER; public ScopedAliasesTest() { super(EXTERNS); } private void testScoped(String code, String expected) { test(GOOG_SCOPE_START_BLOCK + code + GOOG_SCOPE_END_BLOCK, expected); } private void testScopedNoChanges(String aliases, String code) { testScoped(aliases + code, code); } public void testOneLevel() { testScoped("var g = goog;g.dom.createElement(g.dom.TagName.DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testTwoLevel() { testScoped("var d = goog.dom;d.createElement(d.TagName.DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testTransitive() { testScoped("var d = goog.dom;var DIV = d.TagName.DIV;d.createElement(DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testTransitiveInSameVar() { testScoped("var d = goog.dom, DIV = d.TagName.DIV;d.createElement(DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testMultipleTransitive() { testScoped( "var g=goog;var d=g.dom;var t=d.TagName;var DIV=t.DIV;" + "d.createElement(DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testFourLevel() { testScoped("var DIV = goog.dom.TagName.DIV;goog.dom.createElement(DIV);", "goog.dom.createElement(goog.dom.TagName.DIV);"); } public void testWorksInClosures() { testScoped( "var DIV = goog.dom.TagName.DIV;" + "goog.x = function() {goog.dom.createElement(DIV);};", "goog.x = function() {goog.dom.createElement(goog.dom.TagName.DIV);};"); } public void testOverridden() { // Test that the alias doesn't get unaliased when it's overridden by a // parameter. testScopedNoChanges( "var g = goog;", "goog.x = function(g) {g.z()};"); // Same for a local. testScopedNoChanges( "var g = goog;", "goog.x = function() {var g = {}; g.z()};"); } public void testTwoScopes() { test( "goog.scope(function() {var g = goog;g.method()});" + "goog.scope(function() {g.method();});", "goog.method();g.method();"); } public void testTwoSymbolsInTwoScopes() { test( "var goog = {};" + "goog.scope(function() { var g = goog; g.Foo = function() {}; });" + "goog.scope(function() { " + " var Foo = goog.Foo; goog.bar = function() { return new Foo(); };" + "});", "var goog = {};" + "goog.Foo = function() {};" + "goog.bar = function() { return new goog.Foo(); };"); } public void testAliasOfSymbolInGoogScope() { test( "var goog = {};" + "goog.scope(function() {" + " var g = goog;" + " g.Foo = function() {};" + " var Foo = g.Foo;" + " Foo.prototype.bar = function() {};" + "});", "var goog = {}; goog.Foo = function() {};" + "goog.Foo.prototype.bar = function() {};"); } public void testScopedFunctionReturnThis() { test("goog.scope(function() { " + " var g = goog; g.f = function() { return this; };" + "});", "goog.f = function() { return this; };"); } public void testScopedFunctionAssignsToVar() { test("goog.scope(function() { " + " var g = goog; g.f = function(x) { x = 3; return x; };" + "});", "goog.f = function(x) { x = 3; return x; };"); } public void testScopedFunctionThrows() { test("goog.scope(function() { " + " var g = goog; g.f = function() { throw 'error'; };" + "});", "goog.f = function() { throw 'error'; };"); } public void testPropertiesNotChanged() { testScopedNoChanges("var x = goog.dom;", "y.x();"); } public void testShadowedVar() { test("var Popup = {};" + "var OtherPopup = {};" + "goog.scope(function() {" + " var Popup = OtherPopup;" + " Popup.newMethod = function() { return new Popup(); };" + "});", "var Popup = {};" + "var OtherPopup = {};" + "OtherPopup.newMethod = function() { return new OtherPopup(); };"); } public void testShadowedScopedVar() { test("var goog = {};" + "goog.bar = {};" + "goog.scope(function() {" + " var bar = goog.bar;" + // This is bogus, because when the aliases are expanded, goog will // shadow goog.bar. " bar.newMethod = function(goog) { return goog + bar; };" + "});", "var goog={};" + "goog.bar={};" + "goog.bar.newMethod=function(goog$$1){return goog$$1 + goog.bar}"); } public void testShadowedScopedVarTwoScopes() { test("var goog = {};" + "goog.bar = {};" + "goog.scope(function() {" + " var bar = goog.bar;" + " bar.newMethod = function(goog, a) { return bar + a; };" + "});" + "goog.scope(function() {" + " var bar = goog.bar;" + " bar.newMethod2 = function(goog, b) { return bar + b; };" + "});", "var goog={};" + "goog.bar={};" + "goog.bar.newMethod=function(goog$$1, a){return goog.bar + a};" + "goog.bar.newMethod2=function(goog$$1, b){return goog.bar + b};"); } public void testUsingObjectLiteralToEscapeScoping() { // There are many ways to shoot yourself in the foot with goog.scope // and make the compiler generate bad code. We generally don't care. // // We only try to protect against accidental mis-use, not deliberate // mis-use. test( "var goog = {};" + "goog.bar = {};" + "goog.scope(function() {" + " var bar = goog.bar;" + " var baz = goog.bar.baz;" + " goog.foo = function() {" + " goog.bar = {baz: 3};" + " return baz;" + " };" + "});", "var goog = {};" + "goog.bar = {};" + "goog.foo = function(){" + " goog.bar = {baz:3};" + " return goog.bar.baz;" + "};"); } private void testTypes(String aliases, String code) { testScopedNoChanges(aliases, code); verifyTypes(); } private void verifyTypes() { Compiler lastCompiler = getLastCompiler(); new TypeVerifyingPass(lastCompiler).process(lastCompiler.externsRoot, lastCompiler.jsRoot); } public void testJsDocType() { testTypes( "var x = goog.Timer;", "" + "/** @type {x} */ types.actual;" + "/** @type {goog.Timer} */ types.expected;"); } public void testJsDocParameter() { testTypes( "var x = goog.Timer;", "" + "/** @param {x} a */ types.actual;" + "/** @param {goog.Timer} a */ types.expected;"); } public void testJsDocExtends() { testTypes( "var x = goog.Timer;", "" + "/** @extends {x} */ types.actual;" + "/** @extends {goog.Timer} */ types.expected;"); } public void testJsDocImplements() { testTypes( "var x = goog.Timer;", "" + "/** @implements {x} */ types.actual;" + "/** @implements {goog.Timer} */ types.expected;"); } public void testJsDocEnum() { testTypes( "var x = goog.Timer;", "" + "/** @enum {x} */ types.actual;" + "/** @enum {goog.Timer} */ types.expected;"); } public void testJsDocReturn() { testTypes( "var x = goog.Timer;", "" + "/** @return {x} */ types.actual;" + "/** @return {goog.Timer} */ types.expected;"); } public void testJsDocThis() { testTypes( "var x = goog.Timer;", "" + "/** @this {x} */ types.actual;" + "/** @this {goog.Timer} */ types.expected;"); } public void testJsDocThrows() { testTypes( "var x = goog.Timer;", "" + "/** @throws {x} */ types.actual;" + "/** @throws {goog.Timer} */ types.expected;"); } public void testJsDocSubType() { testTypes( "var x = goog.Timer;", "" + "/** @type {x.Enum} */ types.actual;" + "/** @type {goog.Timer.Enum} */ types.expected;"); } public void testJsDocTypedef() { testTypes( "var x = goog.Timer;", "" + "/** @typedef {x} */ types.actual;" + "/** @typedef {goog.Timer} */ types.expected;"); } public void testArrayJsDoc() { testTypes( "var x = goog.Timer;", "" + "/** @type {Array.} */ types.actual;" + "/** @type {Array.} */ types.expected;"); } public void testObjectJsDoc() { testTypes( "var x = goog.Timer;", "" + "/** @type {{someKey: x}} */ types.actual;" + "/** @type {{someKey: goog.Timer}} */ types.expected;"); testTypes( "var x = goog.Timer;", "" + "/** @type {{x: number}} */ types.actual;" + "/** @type {{x: number}} */ types.expected;"); } public void testUnionJsDoc() { testTypes( "var x = goog.Timer;", "" + "/** @type {x|Object} */ types.actual;" + "/** @type {goog.Timer|Object} */ types.expected;"); } public void testFunctionJsDoc() { testTypes( "var x = goog.Timer;", "" + "/** @type {function(x) : void} */ types.actual;" + "/** @type {function(goog.Timer) : void} */ types.expected;"); testTypes( "var x = goog.Timer;", "" + "/** @type {function() : x} */ types.actual;" + "/** @type {function() : goog.Timer} */ types.expected;"); } public void testForwardJsDoc() { testScoped( "/**\n" + " * @constructor\n" + " */\n" + "foo.Foo = function() {};" + "/** @param {Foo.Bar} x */ foo.Foo.actual = function(x) {3};" + "var Foo = foo.Foo;" + "/** @constructor */ Foo.Bar = function() {};" + "/** @param {foo.Foo.Bar} x */ foo.Foo.expected = function(x) {};", "/**\n" + " * @constructor\n" + " */\n" + "foo.Foo = function() {};" + "/** @param {foo.Foo.Bar} x */ foo.Foo.actual = function(x) {3};" + "/** @constructor */ foo.Foo.Bar = function() {};" + "/** @param {foo.Foo.Bar} x */ foo.Foo.expected = function(x) {};"); verifyTypes(); } public void testTestTypes() { try { testTypes( "var x = goog.Timer;", "" + "/** @type {function() : x} */ types.actual;" + "/** @type {function() : wrong.wrong} */ types.expected;"); fail("Test types should fail here."); } catch (AssertionError e) { } } public void testNullType() { testTypes( "var x = goog.Timer;", "/** @param draggable */ types.actual;" + "/** @param draggable */ types.expected;"); } public void testIssue772() { testTypes( "var b = a.b;" + "var c = b.c;", "/** @param {c.MyType} x */ types.actual;" + "/** @param {a.b.c.MyType} x */ types.expected;"); } // TODO(robbyw): What if it's recursive? var goog = goog.dom; // FAILURE CASES private void testFailure(String code, DiagnosticType expectedError) { test(code, null, expectedError); } private void testScopedFailure(String code, DiagnosticType expectedError) { test("goog.scope(function() {" + code + "});", null, expectedError); } public void testScopedThis() { testScopedFailure("this.y = 10;", ScopedAliases.GOOG_SCOPE_REFERENCES_THIS); testScopedFailure("var x = this;", ScopedAliases.GOOG_SCOPE_REFERENCES_THIS); testScopedFailure("fn(this);", ScopedAliases.GOOG_SCOPE_REFERENCES_THIS); } public void testAliasRedefinition() { testScopedFailure("var x = goog.dom; x = goog.events;", ScopedAliases.GOOG_SCOPE_ALIAS_REDEFINED); } public void testAliasNonRedefinition() { test("var y = {}; goog.scope(function() { goog.dom = y; });", "var y = {}; goog.dom = y;"); } public void testScopedReturn() { testScopedFailure("return;", ScopedAliases.GOOG_SCOPE_USES_RETURN); testScopedFailure("var x = goog.dom; return;", ScopedAliases.GOOG_SCOPE_USES_RETURN); } public void testScopedThrow() { testScopedFailure("throw 'error';", ScopedAliases.GOOG_SCOPE_USES_THROW); } public void testUsedImproperly() { testFailure("var x = goog.scope(function() {});", ScopedAliases.GOOG_SCOPE_USED_IMPROPERLY); } public void testBadParameters() { testFailure("goog.scope()", ScopedAliases.GOOG_SCOPE_HAS_BAD_PARAMETERS); testFailure("goog.scope(10)", ScopedAliases.GOOG_SCOPE_HAS_BAD_PARAMETERS); testFailure("goog.scope(function() {}, 10)", ScopedAliases.GOOG_SCOPE_HAS_BAD_PARAMETERS); testFailure("goog.scope(function z() {})", ScopedAliases.GOOG_SCOPE_HAS_BAD_PARAMETERS); testFailure("goog.scope(function(a, b, c) {})", ScopedAliases.GOOG_SCOPE_HAS_BAD_PARAMETERS); } public void testNonAliasLocal() { testScopedFailure("var x = 10", ScopedAliases.GOOG_SCOPE_NON_ALIAS_LOCAL); testScopedFailure("var x = goog.dom + 10", ScopedAliases.GOOG_SCOPE_NON_ALIAS_LOCAL); testScopedFailure("var x = goog['dom']", ScopedAliases.GOOG_SCOPE_NON_ALIAS_LOCAL); testScopedFailure("var x = goog.dom, y = 10", ScopedAliases.GOOG_SCOPE_NON_ALIAS_LOCAL); testScopedFailure("function f() {}", ScopedAliases.GOOG_SCOPE_NON_ALIAS_LOCAL); } // Alias Recording Tests // TODO(tylerg) : update these to EasyMock style tests once available public void testNoGoogScope() { String fullJsCode = "var g = goog;\n g.dom.createElement(g.dom.TagName.DIV);"; TransformationHandlerSpy spy = new TransformationHandlerSpy(); transformationHandler = spy; test(fullJsCode, fullJsCode); assertTrue(spy.observedPositions.isEmpty()); } public void testRecordOneAlias() { String fullJsCode = GOOG_SCOPE_START_BLOCK + "var g = goog;\n g.dom.createElement(g.dom.TagName.DIV);\n" + GOOG_SCOPE_END_BLOCK; String expectedJsCode = "goog.dom.createElement(goog.dom.TagName.DIV);\n"; TransformationHandlerSpy spy = new TransformationHandlerSpy(); transformationHandler = spy; test(fullJsCode, expectedJsCode); assertTrue(spy.observedPositions.containsKey("testcode")); List> positions = spy.observedPositions.get("testcode"); assertEquals(1, positions.size()); verifyAliasTransformationPosition(1, 0, 2, 1, positions.get(0)); assertEquals(1, spy.constructedAliases.size()); AliasSpy aliasSpy = (AliasSpy) spy.constructedAliases.get(0); assertEquals("goog", aliasSpy.observedDefinitions.get("g")); } public void testRecordMultipleAliases() { String fullJsCode = GOOG_SCOPE_START_BLOCK + "var g = goog;\n var b= g.bar;\n var f = goog.something.foo;" + "g.dom.createElement(g.dom.TagName.DIV);\n b.foo();" + GOOG_SCOPE_END_BLOCK; String expectedJsCode = "goog.dom.createElement(goog.dom.TagName.DIV);\n goog.bar.foo();"; TransformationHandlerSpy spy = new TransformationHandlerSpy(); transformationHandler = spy; test(fullJsCode, expectedJsCode); assertTrue(spy.observedPositions.containsKey("testcode")); List> positions = spy.observedPositions.get("testcode"); assertEquals(1, positions.size()); verifyAliasTransformationPosition(1, 0, 3, 1, positions.get(0)); assertEquals(1, spy.constructedAliases.size()); AliasSpy aliasSpy = (AliasSpy) spy.constructedAliases.get(0); assertEquals("goog", aliasSpy.observedDefinitions.get("g")); assertEquals("g.bar", aliasSpy.observedDefinitions.get("b")); assertEquals("goog.something.foo", aliasSpy.observedDefinitions.get("f")); } public void testRecordAliasFromMultipleGoogScope() { String firstGoogScopeBlock = GOOG_SCOPE_START_BLOCK + "\n var g = goog;\n g.dom.createElement(g.dom.TagName.DIV);\n" + GOOG_SCOPE_END_BLOCK; String fullJsCode = firstGoogScopeBlock + "\n\nvar l = abc.def;\n\n" + GOOG_SCOPE_START_BLOCK + "\n var z = namespace.Zoo;\n z.getAnimals(l);\n" + GOOG_SCOPE_END_BLOCK; String expectedJsCode = "goog.dom.createElement(goog.dom.TagName.DIV);\n" + "\n\nvar l = abc.def;\n\n" + "\n namespace.Zoo.getAnimals(l);\n"; TransformationHandlerSpy spy = new TransformationHandlerSpy(); transformationHandler = spy; test(fullJsCode, expectedJsCode); assertTrue(spy.observedPositions.containsKey("testcode")); List> positions = spy.observedPositions.get("testcode"); assertEquals(2, positions.size()); verifyAliasTransformationPosition(1, 0, 6, 0, positions.get(0)); verifyAliasTransformationPosition(8, 0, 11, 4, positions.get(1)); assertEquals(2, spy.constructedAliases.size()); AliasSpy aliasSpy = (AliasSpy) spy.constructedAliases.get(0); assertEquals("goog", aliasSpy.observedDefinitions.get("g")); aliasSpy = (AliasSpy) spy.constructedAliases.get(1); assertEquals("namespace.Zoo", aliasSpy.observedDefinitions.get("z")); } private void verifyAliasTransformationPosition(int startLine, int startChar, int endLine, int endChar, SourcePosition pos) { assertEquals(startLine, pos.getStartLine()); assertEquals(startChar, pos.getPositionOnStartLine()); assertTrue( "expected endline >= " + endLine + ". Found " + pos.getEndLine(), pos.getEndLine() >= endLine); assertTrue("expected endChar >= " + endChar + ". Found " + pos.getPositionOnEndLine(), pos.getPositionOnEndLine() >= endChar); } @Override protected ScopedAliases getProcessor(Compiler compiler) { return new ScopedAliases(compiler, null, transformationHandler); } private static class TransformationHandlerSpy implements AliasTransformationHandler { private final Map>> observedPositions = Maps.newHashMap(); public final List constructedAliases = Lists.newArrayList(); @Override public AliasTransformation logAliasTransformation( String sourceFile, SourcePosition position) { if(!observedPositions.containsKey(sourceFile)) { observedPositions.put(sourceFile, Lists.> newArrayList()); } observedPositions.get(sourceFile).add(position); AliasTransformation spy = new AliasSpy(); constructedAliases.add(spy); return spy; } } private static class AliasSpy implements AliasTransformation { public final Map observedDefinitions = Maps.newHashMap(); @Override public void addAlias(String alias, String definition) { observedDefinitions.put(alias, definition); } } private static class TypeVerifyingPass implements CompilerPass, NodeTraversal.Callback { private final Compiler compiler; private List actualTypes = null; public TypeVerifyingPass(Compiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo info = n.getJSDocInfo(); if (info != null) { Collection typeNodes = info.getTypeNodes(); if (typeNodes.size() > 0) { if (actualTypes != null) { List expectedTypes = Lists.newArrayList(); for (Node typeNode : info.getTypeNodes()) { expectedTypes.add(typeNode); } assertEquals("Wrong number of JsDoc types", expectedTypes.size(), actualTypes.size()); for (int i = 0; i < expectedTypes.size(); i++) { assertNull( expectedTypes.get(i).checkTreeEquals(actualTypes.get(i))); } } else { actualTypes = Lists.newArrayList(); for (Node typeNode : info.getTypeNodes()) { actualTypes.add(typeNode); } } } } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ConstCheckTest.java0000644000175000017500000000771612115204405026566 0ustar apoapo/* * Copyright 2007 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; /** * Tests {@link ConstCheck}. * */ public class ConstCheckTest extends CompilerTestCase { public ConstCheckTest() { enableNormalize(); } @Override public CompilerPass getProcessor(Compiler compiler) { return new ConstCheck(compiler); } @Override public int getNumRepetitions() { return 1; } public void testConstantDefinition1() { testSame("var XYZ = 1;"); } public void testConstantDefinition2() { testSame("var a$b$XYZ = 1;"); } public void testConstantInitializedInAnonymousNamespace1() { testSame("var XYZ; (function(){ XYZ = 1; })();"); } public void testConstantInitializedInAnonymousNamespace2() { testSame("var a$b$XYZ; (function(){ a$b$XYZ = 1; })();"); } public void testObjectModified() { testSame("var IE = true, XYZ = {a:1,b:1}; if (IE) XYZ['c'] = 1;"); } public void testObjectPropertyInitializedLate() { testSame("var XYZ = {}; for (var i = 0; i < 10; i++) { XYZ[i] = i; }"); } public void testObjectRedefined1() { testError("var XYZ = {}; XYZ = 2;"); } public void testConstantRedefined1() { testError("var XYZ = 1; XYZ = 2;"); } public void testConstantRedefined2() { testError("var a$b$XYZ = 1; a$b$XYZ = 2;"); } public void testConstantRedefinedInLocalScope1() { testError("var XYZ = 1; (function(){ XYZ = 2; })();"); } public void testConstantRedefinedInLocalScope2() { testError("var a$b$XYZ = 1; (function(){ a$b$XYZ = 2; })();"); } public void testConstantRedefinedInLocalScopeOutOfOrder() { testError("function f() { XYZ = 2; } var XYZ = 1;"); } public void testConstantPostIncremented1() { testError("var XYZ = 1; XYZ++;"); } public void testConstantPostIncremented2() { testError("var a$b$XYZ = 1; a$b$XYZ++;"); } public void testConstantPreIncremented1() { testError("var XYZ = 1; XYZ++;"); } public void testConstantPreIncremented2() { testError("var a$b$XYZ = 1; a$b$XYZ++;"); } public void testConstantPostDecremented1() { testError("var XYZ = 1; XYZ--;"); } public void testConstantPostDecremented2() { testError("var a$b$XYZ = 1; a$b$XYZ--;"); } public void testConstantPreDecremented1() { testError("var XYZ = 1; XYZ--;"); } public void testConstantPreDecremented2() { testError("var a$b$XYZ = 1; a$b$XYZ--;"); } public void testAbbreviatedArithmeticAssignment1() { testError("var XYZ = 1; XYZ += 2;"); } public void testAbbreviatedArithmeticAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ %= 2;"); } public void testAbbreviatedBitAssignment1() { testError("var XYZ = 1; XYZ |= 2;"); } public void testAbbreviatedBitAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ &= 2;"); } public void testAbbreviatedShiftAssignment1() { testError("var XYZ = 1; XYZ >>= 2;"); } public void testAbbreviatedShiftAssignment2() { testError("var a$b$XYZ = 1; a$b$XYZ <<= 2;"); } public void testConstAnnotation() { testError("/** @const */ var xyz = 1; xyz = 3;"); } public void testConstSuppression() { testSame("/**\n" + " * @fileoverview\n" + " * @suppress {const}\n" + " */\n" + "/** @const */ var xyz = 1; xyz = 3;"); } private void testError(String js) { test(js, null, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/VariableReferenceCheckTest.java0000644000175000017500000001440112115204405031031 0ustar apoapo/* * 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; /** * Test that warnings are generated in appropriate cases and appropriate * cases only by VariableReferenceCheck * */ public class VariableReferenceCheckTest extends CompilerTestCase { private static final String VARIABLE_RUN = "var a = 1; var b = 2; var c = a + b, d = c;"; private boolean enableAmbiguousFunctionCheck = false; @Override public CompilerOptions getOptions() { CompilerOptions options = super.getOptions(); if (enableAmbiguousFunctionCheck) { options.setWarningLevel( DiagnosticGroups.AMBIGUOUS_FUNCTION_DECL, CheckLevel.WARNING); } return options; } @Override public CompilerPass getProcessor(Compiler compiler) { // Treats bad reads as errors, and reports bad write warnings. return new VariableReferenceCheck(compiler, CheckLevel.WARNING); } @Override public void setUp() throws Exception { super.setUp(); enableAmbiguousFunctionCheck = false; } public void testCorrectCode() { assertNoWarning("function foo(d) { (function() { d.foo(); }); d.bar(); } "); assertNoWarning("function foo() { bar(); } function bar() { foo(); } "); assertNoWarning("function f(d) { d = 3; }"); assertNoWarning(VARIABLE_RUN); assertNoWarning("function f() { " + VARIABLE_RUN + "}"); } public void testCorrectShadowing() { assertNoWarning(VARIABLE_RUN + "function f() { " + VARIABLE_RUN + "}"); } public void testCorrectRedeclare() { assertNoWarning( "function f() { if (1) { var a = 2; } else { var a = 3; } }"); } public void testCorrectRecursion() { assertNoWarning("function f() { var x = function() { x(); }; }"); } public void testCorrectCatch() { assertNoWarning("function f() { try { var x = 2; } catch (x) {} }"); } public void testRedeclare() { // Only test local scope since global scope is covered elsewhere assertRedeclare("function f() { var a = 2; var a = 3; }"); assertRedeclare("function f(a) { var a = 2; }"); } public void testEarlyReference() { assertUndeclared("function f() { a = 2; var a = 3; }"); } public void testCorrectEarlyReference() { assertNoWarning("var goog = goog || {}"); assertNoWarning("function f() { a = 2; } var a = 2;"); } public void testUnreferencedBleedingFunction() { assertNoWarning("var x = function y() {}"); } public void testReferencedBleedingFunction() { assertNoWarning("var x = function y() { return y(); }"); } public void testDoubleDeclaration() { assertRedeclare("function x(y) { if (true) { var y; } }"); } public void testDoubleDeclaration2() { assertRedeclare("function x() { var y; if (true) { var y; } }"); } public void testHoistedFunction1() { enableAmbiguousFunctionCheck = true; assertNoWarning("f(); function f() {}"); } public void testHoistedFunction2() { enableAmbiguousFunctionCheck = true; assertNoWarning("function g() { f(); function f() {} }"); } public void testNonHoistedFunction() { enableAmbiguousFunctionCheck = true; assertUndeclared("if (true) { f(); function f() {} }"); } public void testNonHoistedFunction2() { enableAmbiguousFunctionCheck = true; assertNoWarning("if (false) { function f() {} f(); }"); } public void testNonHoistedFunction3() { enableAmbiguousFunctionCheck = true; assertNoWarning("function g() { if (false) { function f() {} f(); }}"); } public void testNonHoistedFunction4() { enableAmbiguousFunctionCheck = true; assertAmbiguous("if (false) { function f() {} } f();"); } public void testNonHoistedFunction5() { enableAmbiguousFunctionCheck = true; assertAmbiguous("function g() { if (false) { function f() {} } f(); }"); } public void testNonHoistedFunction6() { enableAmbiguousFunctionCheck = true; assertUndeclared("if (false) { f(); function f() {} }"); } public void testNonHoistedFunction7() { enableAmbiguousFunctionCheck = true; assertUndeclared("function g() { if (false) { f(); function f() {} }}"); } public void testNonHoistedRecursiveFunction1() { enableAmbiguousFunctionCheck = true; assertNoWarning("if (false) { function f() { f(); }}"); } public void testNonHoistedRecursiveFunction2() { enableAmbiguousFunctionCheck = true; assertNoWarning("function g() { if (false) { function f() { f(); }}}"); } public void testNonHoistedRecursiveFunction3() { enableAmbiguousFunctionCheck = true; assertNoWarning("function g() { if (false) { function f() { f(); g(); }}}"); } public void testNoWarnInExterns1() { // Verify duplicate suppressions are properly recognized. String externs = "var google;" + "/** @suppress {duplicate} */ var google"; String code = ""; test(externs, code, code, null, null); } public void testNoWarnInExterns2() { // Verify we don't complain about early references in externs String externs = "window;" + "var window;"; String code = ""; test(externs, code, code, null, null); } /** * Expects the JS to generate one bad-read error. */ private void assertRedeclare(String js) { testSame(js, VariableReferenceCheck.REDECLARED_VARIABLE); } /** * Expects the JS to generate one bad-write warning. */ private void assertUndeclared(String js) { testSame(js, VariableReferenceCheck.UNDECLARED_REFERENCE); } /** * Expects the JS to generate one bad-write warning. */ private void assertAmbiguous(String js) { testSame(js, VariableReferenceCheck.AMBIGUOUS_FUNCTION_DECL); } /** * Expects the JS to generate no errors or warnings. */ private void assertNoWarning(String js) { testSame(js); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AliasKeywordsTest.java0000644000175000017500000001444612115204405027321 0ustar apoapo/* * 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; /** * Tests for {@link AliasKeywords}. * */ public class AliasKeywordsTest extends CompilerTestCase { private static final int ENOUGH_TO_ALIAS_LITERAL = AliasKeywords.MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; private static final int TOO_FEW_TO_ALIAS_LITERAL = ENOUGH_TO_ALIAS_LITERAL - 1; private static final int ENOUGH_TO_ALIAS_THROW = AliasKeywords.MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW; private static final int TOO_FEW_TO_ALIAS_THROW = ENOUGH_TO_ALIAS_THROW - 1; @Override public void setUp() { super.enableLineNumberCheck(false); super.enableNormalize(); } @Override public CompilerPass getProcessor(Compiler compiler) { return new AliasKeywords(compiler); } @Override protected int getNumRepetitions() { return 1; } /** * Generate code of the form 'if ();' repeated numReps * times, with prepend prepended. * * For example, generateCode("true", 2, "var a=b;") generates * var a=b;if (true);if (true); */ private static String generateCode( String keyword, int numReps, String prepend) { StringBuilder sb = new StringBuilder(prepend); for (int i = 0; i < numReps; i++) { sb.append("if ("); sb.append(keyword); sb.append(");"); } return sb.toString(); } private static String generateCode(String keyword, int numReps) { return generateCode(keyword, numReps, ""); } private static String generatePreProcessThrowCode(int repititions, String whatToThrow) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < repititions; i++) { sb.append("throw "); sb.append(whatToThrow); sb.append(";"); } return sb.toString(); } private static String generatePostProcessThrowCode( int repetitions, String code, String whatToThrow) { StringBuilder sb = new StringBuilder(); sb.append("function "); sb.append(AliasKeywords.ALIAS_THROW); sb.append("(jscomp_throw_param){throw jscomp_throw_param;}"); sb.append(code); for (int i = 0; i < repetitions; i++) { sb.append(AliasKeywords.ALIAS_THROW); sb.append("("); sb.append(whatToThrow); sb.append(");"); } return sb.toString(); } /** * Don't generate aliases if the keyword is not referenced enough. */ public void testDontAlias() { testSame(generateCode("true", TOO_FEW_TO_ALIAS_LITERAL)); testSame(generateCode("false", TOO_FEW_TO_ALIAS_LITERAL)); testSame(generateCode("null", TOO_FEW_TO_ALIAS_LITERAL)); testSame(generateCode("void 0", TOO_FEW_TO_ALIAS_LITERAL)); testSame(generatePreProcessThrowCode(TOO_FEW_TO_ALIAS_THROW, "1")); // Don't alias void nodes other than "void 0". testSame(generateCode("void 1", ENOUGH_TO_ALIAS_LITERAL)); testSame(generateCode("void x", ENOUGH_TO_ALIAS_LITERAL)); testSame(generateCode("void f()", ENOUGH_TO_ALIAS_LITERAL)); } /** * Generate aliases if the keyword is referenced >= ENOUGH_TO_ALIAS * times. */ public void testAlias() { test(generateCode("true", ENOUGH_TO_ALIAS_LITERAL), generateCode(AliasKeywords.ALIAS_TRUE, ENOUGH_TO_ALIAS_LITERAL, "var JSCompiler_alias_TRUE=true;")); test(generateCode("false", ENOUGH_TO_ALIAS_LITERAL), generateCode(AliasKeywords.ALIAS_FALSE, ENOUGH_TO_ALIAS_LITERAL, "var JSCompiler_alias_FALSE=false;")); test(generateCode("null", ENOUGH_TO_ALIAS_LITERAL), generateCode(AliasKeywords.ALIAS_NULL, ENOUGH_TO_ALIAS_LITERAL, "var JSCompiler_alias_NULL=null;")); test(generateCode("void 0", ENOUGH_TO_ALIAS_LITERAL), generateCode(AliasKeywords.ALIAS_VOID, ENOUGH_TO_ALIAS_LITERAL, "var JSCompiler_alias_VOID=void 0;")); test(generatePreProcessThrowCode(ENOUGH_TO_ALIAS_THROW, "1"), generatePostProcessThrowCode(ENOUGH_TO_ALIAS_THROW, "", "1")); } public void testAliasTrueFalseNull() { StringBuilder actual = new StringBuilder(); actual.append(generateCode("true", ENOUGH_TO_ALIAS_LITERAL)); actual.append(generateCode("false", ENOUGH_TO_ALIAS_LITERAL)); actual.append(generateCode("null", ENOUGH_TO_ALIAS_LITERAL)); actual.append(generateCode("void 0", ENOUGH_TO_ALIAS_LITERAL)); StringBuilder expected = new StringBuilder(); expected.append( "var JSCompiler_alias_VOID=void 0;" + "var JSCompiler_alias_TRUE=true;" + "var JSCompiler_alias_NULL=null;" + "var JSCompiler_alias_FALSE=false;"); expected.append( generateCode(AliasKeywords.ALIAS_TRUE, ENOUGH_TO_ALIAS_LITERAL)); expected.append( generateCode(AliasKeywords.ALIAS_FALSE, ENOUGH_TO_ALIAS_LITERAL)); expected.append( generateCode(AliasKeywords.ALIAS_NULL, ENOUGH_TO_ALIAS_LITERAL)); expected.append( generateCode(AliasKeywords.ALIAS_VOID, ENOUGH_TO_ALIAS_LITERAL)); test(actual.toString(), expected.toString()); } public void testAliasThrowKeywordLiteral() { int repitions = Math.max(ENOUGH_TO_ALIAS_THROW, ENOUGH_TO_ALIAS_LITERAL); String afterCode = generatePostProcessThrowCode( repitions, "var JSCompiler_alias_TRUE=true;", AliasKeywords.ALIAS_TRUE); test(generatePreProcessThrowCode(repitions, "true"), afterCode); } public void testExistingAliasDefinitionFails() { try { testSame("var JSCompiler_alias_TRUE='foo';"); fail(); } catch (RuntimeException expected) { // expected exception assertTrue(-1 != expected.getMessage().indexOf( "Existing alias definition")); } } public void testWithNoInputs() { testSame(new String[] {}); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AstParallelizerTest.java0000644000175000017500000001214712115204405027632 0ustar apoapo/* * 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.rhino.Node; import junit.framework.TestCase; import java.util.List; /** * Unit tests for {@link AstParallelizer}. * */ public class AstParallelizerTest extends TestCase { private static final String HOLDER = AstParallelizer.TEMP_NAME; public void testNoSplit() { splitFunctions("", ""); splitFunctions("var x", "var x"); splitFunctions("var x", "var x"); splitFunctions("x()", "x()"); } public void testSplitNamedFuntion() { splitFunctions("function foo() { foo() } foo()", "function " + HOLDER + "() {} foo()", "function foo() { foo() }"); } public void testSplitNamedFuntionWithArgs() { splitFunctions("function foo(x) { foo(1) } foo(1)", "function " + HOLDER + "() {} foo(1)", "function foo(x) { foo(1) }"); } // TODO(johnlenz): This test is invalid it relies on allowing // nameless function statements, which does not parse. public void disable_testSplitAnonFuntion() { splitFunctions("var foo = function(x) { foo(1) }; foo(1)", "var foo = function " + HOLDER + "() {}; foo(1)", "(function(x) { foo(1) })"); } // TODO(johnlenz): This test is invalid it relies on allowing // nameless function statements, which does not parse. public void disable_testSplitInplaceCall() { splitFunctions("(function() { print('hi') })()", "(function " + HOLDER + "() {})()", "(function() { print('hi') })"); } // TODO(johnlenz): This test is invalid it relies on allowing // nameless function statements, which does not parse. public void disable_testSplitMupltiFuntions() { splitFunctions("var foo = function(x) { foo(1) }; foo();" + "var bar = function(x,y) { bar(1,2) }; bar(1,2)", // Output Root "var foo = function " + HOLDER + "() {}; foo();" + "var bar = function " + HOLDER + "() {}; bar(1,2)", // foo "(function(x) { foo(1) })", // bar "(function(x,y) { bar(1,2) })"); } // TODO(johnlenz): This test is invalid it relies on allowing // nameless function statements, which does not parse. public void disable_testInnerFunctions() { splitFunctions("var foo = function() {var bar = function() {}}", "var foo = function " + HOLDER + "() {}", "function() {var bar = function() {}}"); } public void testSplitFileLevel() { splitFiles(new String[] { "var a", "var b", "var c"}); splitFiles(new String[] { "var a", "var b", "var c", "var d", "function e() {}"}); } /** * Splits at function level with {@link AstParallelizer#split()}, verify the * output matches what is expected and then verify * {@link AstParallelizer#join()} can reverse the whole process. */ private void splitFunctions(String input, String ... output) { Compiler compiler = new Compiler(); Node original = compiler.parseTestCode(input); Node root = original.cloneTree(); AstParallelizer parallelizer = AstParallelizer.createNewFunctionLevelAstParallelizer(root, true); List forest = parallelizer.split(); assertEquals(output.length, forest.size()); int i = 0; for (Node n : forest) { Node tree = compiler.parseTestCode(output[i++]); assertEquals(compiler.toSource(tree), compiler.toSource(n)); } parallelizer.join(); assertTrue(original.isEquivalentTo(root)); } private void splitFiles(String[] input) { Compiler compiler = new Compiler(); List files = Lists.newArrayList(); for (int i = 0; i < input.length; i ++) { files.add(SourceFile.fromCode("file" + i, input[i])); } compiler.init( ImmutableList.of(), files, new CompilerOptions()); compiler.parse(); Node original = compiler.getRoot(); Node root = original.cloneTree(); AstParallelizer parallelizer = AstParallelizer.createNewFileLevelAstParallelizer(root); List forest = parallelizer.split(); assertEquals(input.length, forest.size()); int i = 0; for (Node n : forest) { Node tree = compiler.parseTestCode(input[i++]); assertEquals(compiler.toSource(tree), compiler.toSource(n)); } parallelizer.join(); assertTrue(original.isEquivalentTo(root)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ConvertToDottedPropertiesTest.java0000644000175000017500000000454412115204405031702 0ustar apoapo/* * Copyright 2007 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; /** * Tests for {@link ConvertToDottedProperties}. * */ public class ConvertToDottedPropertiesTest extends CompilerTestCase { @Override public CompilerPass getProcessor(Compiler compiler) { return new ConvertToDottedProperties(compiler); } public void testConvert() { test("a['p']", "a.p"); test("a['_p_']", "a._p_"); test("a['_']", "a._"); test("a['$']", "a.$"); test("a.b.c['p']", "a.b.c.p"); test("a.b['c'].p", "a.b.c.p"); test("a['p']();", "a.p();"); test("a()['p']", "a().p"); // ASCII in Unicode is safe. test("a['\u0041A']", "a.AA"); } public void testDoNotConvert() { testSame("a[0]"); testSame("a['']"); testSame("a[' ']"); testSame("a[',']"); testSame("a[';']"); testSame("a[':']"); testSame("a['.']"); testSame("a['0']"); testSame("a['p ']"); testSame("a['p' + '']"); testSame("a[p]"); testSame("a[P]"); testSame("a[$]"); testSame("a[p()]"); testSame("a['default']"); // Ignorable control characters are ok in Java identifiers, but not in JS. testSame("a['A\u0004']"); // upper case lower half of o from phonetic extensions set. // valid in Safari, not in Firefox, IE. test("a['\u1d17A']", "a['\u1d17A']"); // Latin capital N with tilde - nice if we handled it, but for now let's // only allow simple Latin (aka ASCII) to be converted. test("a['\u00d1StuffAfter']", "a['\u00d1StuffAfter']"); } public void testQuotedProps() { testSame("({'':0})"); testSame("({'1.0':0})"); testSame("({'\u1d17A':0})"); testSame("({'a\u0004b':0})"); } public void test5746867() { testSame("var a = { '$\\\\' : 5 };"); testSame("var a = { 'x\\\\u0041$\\\\' : 5 };"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/SimpleDefinitionFinderTest.java0000644000175000017500000003236312115204405031130 0ustar apoapo/* * 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 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.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.of("DEF STRING_KEY null -> NUMBER")); checkDefinitionsInJs( "({1 : 1}); o[1]", ImmutableSet.of("DEF STRING_KEY null -> NUMBER")); checkDefinitionsInJs( "var a = {b : 1}; a.b", ImmutableSet.of("DEF NAME a -> ", "DEF STRING_KEY null -> NUMBER", "USE NAME a -> []", "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 -> ", "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.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 -> ", "USE NAME a -> []", "DEF NAME f -> FUNCTION")); checkDefinitionsInJs( "var a = 1; function f(a){return a}", ImmutableSet.of("DEF NAME a -> NUMBER", "DEF NAME a -> ", "USE NAME a -> [, 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 -> ", "DEF NAME arg2 -> ", "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 -> ", "DEF NAME a -> NUMBER", "DEF NAME b -> NUMBER", "USE NAME a -> [, 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 -> ", "USE NAME a -> [NUMBER]", "USE NAME b -> [NUMBER]", "USE NAME c -> [ 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 ", "USE NAME a -> [EXTERN ]")); checkDefinitionsInExterns( "var a = {}; a.b", ImmutableSet.of("DEF GETPROP a.b -> EXTERN ", "DEF NAME a -> EXTERN ", "USE NAME a -> [EXTERN ]")); checkDefinitions( "var a = {}", "a.b = 1", ImmutableSet.of("DEF GETPROP a.b -> NUMBER", "DEF NAME a -> EXTERN ", "USE NAME a -> [EXTERN ]")); checkDefinitions( "var a = {}", "a.b", ImmutableSet.of("DEF NAME a -> EXTERN ", "USE NAME a -> [EXTERN ]")); 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 ", "DEF GETPROP goog.HYBRID -> EXTERN ", "DEF NAME goog -> EXTERN ", "DEF STRING_KEY null -> EXTERN NUMBER", "USE GETPROP goog.Enum -> [EXTERN ]", "USE GETPROP goog.Enum.ROADMAP -> [EXTERN NUMBER]", "USE GETPROP goog.HYBRID -> [EXTERN , EXTERN NUMBER]", "USE NAME goog -> [EXTERN ]")); } 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 ", "DEF GETPROP null -> EXTERN ", "DEF NAME goog -> EXTERN ", "USE GETPROP goog.Response -> [EXTERN FUNCTION]", "USE GETPROP goog.Response.prototype.get -> [EXTERN x 2]", "USE NAME goog -> [EXTERN ]")); } void checkDefinitionsInExterns(String externs, Set expected) { checkDefinitions(externs, "", expected); } void checkDefinitionsInJs(String js, Set expected) { checkDefinitions("", js, expected); } void checkDefinitions(String externs, String source, Set 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(""); } found.add(sb.toString()); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Collection 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 defstrs = TreeMultiset.create(); for (Definition def : defs) { String defstr; Node rValue = def.getRValue(); if (rValue != null) { defstr = Token.name(rValue.getType()); } else { defstr = ""; } if (def.isExtern()) { defstr = "EXTERN " + defstr; } defstrs.add(defstr); } sb.append(defstrs.toString()); found.add(sb.toString()); } } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ConcreteTypeTest.java0000644000175000017500000003162412115204405027141 0ustar apoapo/* * 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 static com.google.javascript.jscomp.ConcreteType.ALL; import static com.google.javascript.jscomp.ConcreteType.NONE; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; import com.google.javascript.jscomp.ConcreteType.ConcreteUnionType; import com.google.javascript.jscomp.ConcreteType.Factory; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.testing.AbstractStaticScope; import com.google.javascript.rhino.testing.TestErrorReporter; import junit.framework.TestCase; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; /** * Unit test for the the subclasses of ConcreteType. * */ public class ConcreteTypeTest extends TestCase { private JSTypeRegistry typeRegistry; private JSType unknownType; private Factory factory; @Override public void setUp() { typeRegistry = new JSTypeRegistry(new TestErrorReporter(null, null)); unknownType = typeRegistry.getNativeType(JSTypeNative.UNKNOWN_TYPE); factory = new FakeFactory(); } private void checkEquality(List types) { for (int i = 0; i < types.size(); ++i) { for (int j = 0; j < types.size(); ++j) { if (i == j) { assertEquals(types.get(i), types.get(j)); } else { assertFalse(types.get(i).equals(types.get(j))); } } } } public void testEquals() { ConcreteFunctionType fun1 = createFunction("fun1"); ConcreteFunctionType fun2 = createFunction("fun2"); ConcreteType obj1 = fun1.getInstanceType(); ConcreteType obj2 = fun2.getInstanceType(); ConcreteType union1 = new ConcreteUnionType(fun1, fun2); ConcreteType union2 = new ConcreteUnionType(fun1, obj1); ConcreteType union3 = new ConcreteUnionType(fun1, obj1); checkEquality(Lists.newArrayList(fun1, fun2, obj1, obj2, union1, union2)); assertEquals(union2, union3); } public void testUnionWith() { ConcreteFunctionType fun = createFunction("fun"); ConcreteType obj = fun.getInstanceType(); ConcreteType both = new ConcreteUnionType(fun, obj); assertTrue(fun.isSingleton()); assertTrue(obj.isSingleton()); assertFalse(both.isSingleton()); assertFalse(NONE.isSingleton()); assertFalse(ALL.isSingleton()); checkUnionWith(fun, NONE, fun); checkUnionWith(fun, ALL, ALL); checkUnionWith(fun, obj, both); checkUnionWith(both, NONE, both); checkUnionWith(both, ALL, ALL); } private void checkUnionWith(ConcreteType a, ConcreteType b, ConcreteType c) { assertEquals(a, a.unionWith(a)); assertEquals(b, b.unionWith(b)); assertEquals(c, a.unionWith(b)); assertEquals(c, b.unionWith(a)); } public void testIntersectionWith() { ConcreteFunctionType fun = createFunction("fun"); ConcreteFunctionType fun2 = createFunction("fun2"); ConcreteType obj = fun.getInstanceType(); ConcreteType both = new ConcreteUnionType(fun, obj); assertEquals(NONE, fun.intersectWith(obj)); assertEquals(NONE, obj.intersectWith(fun)); assertEquals(fun, both.intersectWith(fun)); assertEquals(fun, fun.intersectWith(both)); assertEquals(NONE, NONE.intersectWith(both)); assertEquals(NONE, both.intersectWith(NONE)); assertEquals(NONE, fun.intersectWith(NONE)); assertEquals(NONE, NONE.intersectWith(fun)); assertEquals(NONE, both.intersectWith(fun2)); assertEquals(both, ALL.intersectWith(both)); assertEquals(both, both.intersectWith(ALL)); assertEquals(fun, ALL.intersectWith(fun)); assertEquals(fun, fun.intersectWith(ALL)); assertEquals(NONE, ALL.intersectWith(NONE)); assertEquals(NONE, NONE.intersectWith(ALL)); } public void testFunction() { ConcreteFunctionType fun = createFunction("fun", "a", "b"); assertTrue(fun.isFunction()); assertNotNull(fun.getCallSlot()); assertNotNull(fun.getReturnSlot()); assertNotNull(fun.getParameterSlot(0)); assertNotNull(fun.getParameterSlot(1)); assertNull(fun.getParameterSlot(2)); assertTrue(fun.getInstanceType().isInstance()); } public void testInstance() { ConcreteInstanceType obj = createInstance("MyObj", "a", "b"); assertTrue(obj.isInstance()); assertNotNull(obj.getPropertySlot("a")); assertNotNull(obj.getPropertySlot("b")); assertNull(obj.getPropertySlot("c")); // The prototype chain should be: MyObj -> MyObj.prototype -> Object -> // Object.prototype -> null. for (int i = 0; i < 3; ++i) { assertNotNull(obj = obj.getImplicitPrototype()); assertTrue(obj.isInstance()); } assertNull(obj.getImplicitPrototype()); } public void testGetX() { ConcreteFunctionType fun1 = createFunction("fun1"); ConcreteFunctionType fun2 = createFunction("fun2"); ConcreteInstanceType obj1 = fun1.getInstanceType(); ConcreteInstanceType obj2 = fun2.getInstanceType(); ConcreteType union1 = fun1.unionWith(obj1); ConcreteType union2 = union1.unionWith(fun2).unionWith(obj2); assertEqualSets(Lists.newArrayList(), NONE.getFunctions()); assertEqualSets(Lists.newArrayList(), NONE.getInstances()); assertEqualSets(Lists.newArrayList(fun1), fun1.getFunctions()); assertEqualSets(Lists.newArrayList(), fun1.getInstances()); assertEqualSets(Lists.newArrayList(), obj1.getFunctions()); assertEqualSets(Lists.newArrayList(obj1), obj1.getInstances()); assertEqualSets(Lists.newArrayList(fun1), union1.getFunctions()); assertEqualSets(Lists.newArrayList(obj1), union1.getInstances()); assertEqualSets(Lists.newArrayList(fun1, fun2), union2.getFunctions()); assertEqualSets(Lists.newArrayList(obj1, obj2), union2.getInstances()); } /** Checks that the two collections are equal as sets. */ private void assertEqualSets(Collection first, Collection second) { assertEquals(Sets.newHashSet(first), Sets.newHashSet(second)); } /** Creates a fake function with the given description. */ private ConcreteFunctionType createFunction( String name, String... paramNames) { Node args = new Node(Token.PARAM_LIST); for (int i = 0; i < paramNames.length; ++i) { args.addChildToBack(Node.newString(Token.NAME, paramNames[i])); } Node decl = new Node(Token.FUNCTION, Node.newString(Token.NAME, name), args, new Node(Token.BLOCK)); JSType[] paramTypes = new JSType[paramNames.length]; Arrays.fill(paramTypes, unknownType); decl.setJSType(typeRegistry.createConstructorType( name, decl, args, unknownType, null)); return new ConcreteFunctionType(factory, decl, null); } /** Creates a fake instance with the given description. */ private ConcreteInstanceType createInstance( String name, String... propNames) { ObjectType objType = typeRegistry.createObjectType(name, null, typeRegistry.createObjectType(name + ".prototype", null, null)); for (int i = 0; i < propNames.length; ++i) { objType.defineDeclaredProperty(propNames[i], unknownType, null); } return new ConcreteInstanceType(factory, objType); } private class FakeFactory implements Factory { private final Map functionByDeclaration = Maps.newHashMap(); private final Map functionByJSType = Maps.newHashMap(); private final Map instanceByJSType = Maps.newHashMap(); private final JSTypeRegistry registry = new JSTypeRegistry( new TestErrorReporter(null, null)); @Override public JSTypeRegistry getTypeRegistry() { return registry; } /** {@inheritDoc} */ @Override public ConcreteFunctionType createConcreteFunction( Node decl, StaticScope parent) { ConcreteFunctionType funcType = functionByDeclaration.get(decl); if (funcType == null) { functionByDeclaration.put(decl, funcType = new ConcreteFunctionType(this, decl, parent)); if (decl.getJSType() != null) { functionByJSType.put((FunctionType) decl.getJSType(), funcType); } } return funcType; } /** {@inheritDoc} */ @Override public ConcreteInstanceType createConcreteInstance( ObjectType instanceType) { ConcreteInstanceType instType = instanceByJSType.get(instanceType); if (instType == null) { instanceByJSType.put(instanceType, instType = new ConcreteInstanceType(this, instanceType)); } return instType; } /** {@inheritDoc} */ @Override public ConcreteFunctionType getConcreteFunction(FunctionType functionType) { return functionByJSType.get(functionType); } /** {@inheritDoc} */ @Override public ConcreteInstanceType getConcreteInstance(ObjectType instanceType) { return instanceByJSType.get(instanceType); } /** {@inheritDoc} */ @Override public StaticScope createFunctionScope( Node decl, StaticScope parent) { FakeScope scope = new FakeScope((FakeScope) parent); scope.addSlot(ConcreteFunctionType.CALL_SLOT_NAME); scope.addSlot(ConcreteFunctionType.THIS_SLOT_NAME); scope.addSlot(ConcreteFunctionType.RETURN_SLOT_NAME); for (Node n = decl.getFirstChild().getNext().getFirstChild(); n != null; n = n.getNext()) { scope.addSlot(n.getString()); } return scope; } /** {@inheritDoc} */ @Override public StaticScope createInstanceScope( ObjectType instanceType) { FakeScope parentScope = null; if (instanceType.getImplicitPrototype() != null) { ConcreteInstanceType prototype = createConcreteInstance(instanceType.getImplicitPrototype()); parentScope = (FakeScope) prototype.getScope(); } FakeScope scope = new FakeScope(parentScope); for (String propName : instanceType.getOwnPropertyNames()) { scope.addSlot(propName); } return scope; } } // TODO(user): move to a common place if it can be used elsewhere private class FakeScope extends AbstractStaticScope { private final FakeScope parent; private final Map slots = Maps.newHashMap(); FakeScope(FakeScope parent) { this.parent = parent; } /** {@inheritDoc} */ @Override public StaticScope getParentScope() { return parent; } /** {@inheritDoc} */ @Override public StaticSlot getOwnSlot(String name) { return slots.get(name); } /** {@inheritDoc} */ @Override public StaticSlot getSlot(String name) { if (slots.containsKey(name)) { return slots.get(name); } else if (parent != null) { return parent.getSlot(name); } else { return null; } } /** {@inheritDoc} */ @Override public ConcreteType getTypeOfThis() { return ConcreteType.ALL; } void addSlot(String name) { slots.put(name, new FakeSlot(name)); } } // TODO(user): move to a common place if it can be used elsewhere private class FakeSlot implements StaticSlot { private final String name; FakeSlot(String name) { this.name = name; } @Override public String getName() { return name; } @Override public ConcreteType getType() { return ConcreteType.ALL; } @Override public boolean isTypeInferred() { return true; } @Override public StaticReference getDeclaration() { return null; } @Override public JSDocInfo getJSDocInfo() { return null; } } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeFoldWithTypesTest.java0000644000175000017500000000500212115204405030753 0ustar apoapo/* * 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; /** * Tests for {@link ExternExportsPass}. * * @author dcc@google.com (Devin Coughlin) */ public class PeepholeFoldWithTypesTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(Compiler compiler) { return new PeepholeOptimizationsPass(compiler, new PeepholeFoldWithTypes()); } @Override public void setUp() { enableTypeCheck(CheckLevel.WARNING); } public void testFoldTypeofObject() { test("var x = {};typeof x", "var x = {};\"object\""); test("var x = [];typeof x", "var x = [];\"object\""); // typeof null is "object" in JavaScript test("var x = null;typeof x", "var x = null;\"object\""); } public void testFoldTypeofString() { test("var x = \"foo\";typeof x", "var x = \"foo\";\"string\""); test("var x = new String(\"foo\");typeof x", "var x = new String(\"foo\");\"object\""); } public void testFoldTypeofNumber() { test("var x = 10;typeof x", "var x = 10;\"number\""); test("var x = new Number(6);typeof x", "var x = new Number(6);\"object\""); } public void testFoldTypeofBoolean() { test("var x = false;typeof x", "var x = false;\"boolean\""); test("var x = new Boolean(true);typeof x", "var x = new Boolean(true);\"object\""); } public void testFoldTypeofUndefined() { test("var x = undefined;typeof x", "var x = undefined;\"undefined\""); } public void testDontFoldTypeofUnionTypes() { // For now we don't do anything with union types testSame("var x = (unknown ? {} : null);typeof x"); } public void testDontFoldTypeofSideEffects() { // Shouldn't fold if argument to typeof has side effects testSame("var x = 6 ;typeof (x++)"); } public void testDontFoldTypeofWithTypeCheckDisabled() { disableTypeCheck(); testSame("var x = {};typeof x"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExportTestFunctionsTest.java0000644000175000017500000001217112115204405030543 0ustar apoapo/* * 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; /** * Tests for ExportTestFunctions. * */ public class ExportTestFunctionsTest extends CompilerTestCase { private static final String EXTERNS = "function google_exportSymbol(a, b) {}; " + "function google_exportProperty(a, b, c) {};"; private static final String TEST_FUNCTIONS_WITH_NAMES = "function Foo(arg) {}; " + "function setUp(arg3) {}; " + "function tearDown(arg, arg2) {}; " + "function testBar(arg) {}"; public ExportTestFunctionsTest() { super(EXTERNS); } @Override public void setUp() { super.enableLineNumberCheck(false); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ExportTestFunctions(compiler, "google_exportSymbol", "google_exportProperty"); } @Override protected int getNumRepetitions() { // This pass only runs once. return 1; } public void testFunctionsAreExported() { test(TEST_FUNCTIONS_WITH_NAMES, "function Foo(arg){}; " + "function setUp(arg3){} google_exportSymbol(\"setUp\",setUp);; " + "function tearDown(arg,arg2) {} " + "google_exportSymbol(\"tearDown\",tearDown);; " + "function testBar(arg){} google_exportSymbol(\"testBar\",testBar)" ); } // Helper functions public void testBasicTestFunctionsAreExported() { test("function Foo() {function testA() {}}", "function Foo() {function testA(){}}"); test("function setUp() {}", "function setUp(){} google_exportSymbol('setUp',setUp)"); test("function setUpPage() {}", "function setUpPage(){} google_exportSymbol('setUpPage',setUpPage)"); test("function tearDown() {}", "function tearDown(){} google_exportSymbol('tearDown',tearDown)"); test("function tearDownPage() {}", "function tearDownPage(){} google_exportSymbol('tearDownPage'," + "tearDownPage)"); test("function testBar() { function testB() {}}", "function testBar(){function testB(){}}" + "google_exportSymbol('testBar',testBar)"); testSame("var testCase = {}; testCase.setUpPage = function() {}"); } /** * Make sure this works for global functions declared as function expressions: *

   * var testFunctionName = function() {
   *   // Implementation
   * };
   * 
* This format should be supported in addition to function statements. */ public void testFunctionExpressionsAreExported() { test("var Foo = function() {var testA = function() {}}", "var Foo = function() {var testA = function() {}}"); test("var setUp = function() {}", "var setUp = function() {}; " + "google_exportSymbol('setUp',setUp)"); test("var setUpPage = function() {}", "var setUpPage = function() {}; " + "google_exportSymbol('setUpPage',setUpPage)"); test("var tearDown = function() {}", "var tearDown = function() {}; " + "google_exportSymbol('tearDown',tearDown)"); test("var tearDownPage = function() {}", "var tearDownPage = function() {}; " + "google_exportSymbol('tearDownPage', tearDownPage)"); test("var testBar = function() { var testB = function() {}}", "var testBar = function(){ var testB = function() {}}; " + "google_exportSymbol('testBar',testBar)"); } public void testFunctionAssignmentsAreExported() { test("Foo = {}; Foo.prototype.bar = function() {};", "Foo = {}; Foo.prototype.bar = function() {};"); test("Foo = {}; Foo.prototype.setUpPage = function() {};", "Foo = {}; Foo.prototype.setUpPage = function() {};" + "google_exportProperty(Foo.prototype, 'setUpPage', " + "Foo.prototype.setUpPage);"); test("Foo = {}; Foo.prototype.testBar = function() {};", "Foo = {}; Foo.prototype.testBar = function() {};" + "google_exportProperty(Foo.prototype, 'testBar', " + "Foo.prototype.testBar);"); test("Foo = {}; Foo.prototype.testBar = function() " + "{ var testBaz = function() {}};", "Foo = {}; Foo.prototype.testBar = function() " + "{ var testBaz = function() {}};" + "google_exportProperty(Foo.prototype, 'testBar', " + "Foo.prototype.testBar);"); test("Foo = {}; Foo.baz.prototype.testBar = function() " + "{ var testBaz = function() {}};", "Foo = {}; Foo.baz.prototype.testBar = function() " + "{ var testBaz = function() {}};" + "google_exportProperty(Foo.baz.prototype, 'testBar', " + "Foo.baz.prototype.testBar);"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AmbiguatePropertiesTest.java0000644000175000017500000004441212115204405030507 0ustar apoapo/* * 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.Maps; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.Node; import java.util.Map; /** * Unit test for AmbiguateProperties Compiler pass. * */ public class AmbiguatePropertiesTest extends CompilerTestCase { private AmbiguateProperties lastPass; private static final String EXTERNS = "Function.prototype.call=function(){};" + "Function.prototype.inherits=function(){};" + "prop.toString;" + "var google = { gears: { factory: {}, workerPool: {} } };"; public AmbiguatePropertiesTest() { super(EXTERNS); enableNormalize(); enableTypeCheck(CheckLevel.WARNING); enableClosurePass(); } @Override public CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { lastPass = new AmbiguateProperties(compiler, new char[]{'$'}); lastPass.process(externs, root); } }; } @Override protected int getNumRepetitions() { return 1; } @Override protected CompilerOptions getOptions() { // no missing properties check CompilerOptions options = new CompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5); return options; } public void testOneVar1() { test("/** @constructor */ var Foo = function(){};Foo.prototype.b = 0;", "var Foo = function(){};Foo.prototype.a = 0;"); } public void testOneVar2() { testSame("/** @constructor */ var Foo = function(){};" + "Foo.prototype = {b: 0};"); } public void testOneVar3() { testSame("/** @constructor */ var Foo = function(){};" + "Foo.prototype = {get b() {return 0}};"); } public void testOneVar4() { testSame("/** @constructor */ var Foo = function(){};" + "Foo.prototype = {set b(a) {}};"); } public void testTwoVar1() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.z=0;\n" + "Foo.prototype.z=0;\n" + "Foo.prototype.x=0;"; String output = "" + "var Foo = function(){};\n" + "Foo.prototype.a=0;\n" + "Foo.prototype.a=0;\n" + "Foo.prototype.b=0;"; test(js, output); } public void testTwoVar2() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype={z:0, z:1, x:0};\n"; // TODO(johnlenz): It would be nice to handle this type of declaration. testSame(js); } public void testTwoIndependentVar() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.b = 0;\n" + "/** @constructor */ var Bar = function(){};\n" + "Bar.prototype.c = 0;"; String output = "" + "var Foo = function(){};" + "Foo.prototype.a=0;" + "var Bar = function(){};" + "Bar.prototype.a=0;"; test(js, output); } public void testTwoTypesTwoVar() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.r = 0;\n" + "Foo.prototype.g = 0;\n" + "/** @constructor */ var Bar = function(){};\n" + "Bar.prototype.c = 0;" + "Bar.prototype.r = 0;"; String output = "" + "var Foo = function(){};" + "Foo.prototype.a=0;" + "Foo.prototype.b=0;" + "var Bar = function(){};" + "Bar.prototype.b=0;" + "Bar.prototype.a=0;"; test(js, output); } public void testUnion() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "/** @constructor */ var Bar = function(){};\n" + "Foo.prototype.foodoo=0;\n" + "Bar.prototype.bardoo=0;\n" + "/** @type {Foo|Bar} */\n" + "var U;\n" + "U.joint;" + "U.joint"; String output = "" + "var Foo = function(){};\n" + "var Bar = function(){};\n" + "Foo.prototype.b=0;\n" + "Bar.prototype.b=0;\n" + "var U;\n" + "U.a;" + "U.a"; test(js, output); } public void testUnions() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "/** @constructor */ var Bar = function(){};\n" + "/** @constructor */ var Baz = function(){};\n" + "/** @constructor */ var Bat = function(){};\n" + "Foo.prototype.lone1=0;\n" + "Bar.prototype.lone2=0;\n" + "Baz.prototype.lone3=0;\n" + "Bat.prototype.lone4=0;\n" + "/** @type {Foo|Bar} */\n" + "var U1;\n" + "U1.j1;" + "U1.j2;" + "/** @type {Baz|Bar} */\n" + "var U2;\n" + "U2.j3;" + "U2.j4;" + "/** @type {Baz|Bat} */\n" + "var U3;" + "U3.j5;" + "U3.j6"; String output = "" + "var Foo = function(){};\n" + "var Bar = function(){};\n" + "var Baz = function(){};\n" + "var Bat = function(){};\n" + "Foo.prototype.c=0;\n" + "Bar.prototype.e=0;\n" + "Baz.prototype.e=0;\n" + "Bat.prototype.c=0;\n" + "var U1;\n" + "U1.a;" + "U1.b;" + "var U2;\n" + "U2.c;" + "U2.d;" + "var U3;" + "U3.a;" + "U3.b"; test(js, output); } public void testExtends() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.x=0;\n" + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + "goog.inherits(Bar, Foo);\n" + "Bar.prototype.y=0;\n" + "Bar.prototype.z=0;\n" + "/** @constructor */ var Baz = function(){};\n" + "Baz.prototype.l=0;\n" + "Baz.prototype.m=0;\n" + "Baz.prototype.n=0;\n" + "(new Baz).m\n"; String output = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.a=0;\n" + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + "goog.inherits(Bar, Foo);\n" + "Bar.prototype.b=0;\n" + "Bar.prototype.c=0;\n" + "/** @constructor */ var Baz = function(){};\n" + "Baz.prototype.b=0;\n" + "Baz.prototype.a=0;\n" + "Baz.prototype.c=0;\n" + "(new Baz).a\n"; test(js, output); } public void testLotsOfVars() { StringBuilder js = new StringBuilder(); StringBuilder output = new StringBuilder(); js.append("/** @constructor */ var Foo = function(){};\n"); js.append("/** @constructor */ var Bar = function(){};\n"); output.append(js.toString()); int vars = 10; for (int i = 0; i < vars; i++) { js.append("Foo.prototype.var" + i + " = 0;"); js.append("Bar.prototype.var" + (i + 10000) + " = 0;"); output.append("Foo.prototype." + (char) ('a' + i) + "=0;"); output.append("Bar.prototype." + (char) ('a' + i) + "=0;"); } test(js.toString(), output.toString()); } public void testLotsOfClasses() { StringBuilder b = new StringBuilder(); int classes = 10; for (int i = 0; i < classes; i++) { String c = "Foo" + i; b.append("/** @constructor */ var " + c + " = function(){};\n"); b.append(c + ".prototype.varness" + i + " = 0;"); } String js = b.toString(); test(js, js.replaceAll("varness\\d+", "a")); } public void testFunctionType() { String js = "" + "/** @constructor */ function Foo(){};\n" + "/** @return {Bar} */\n" + "Foo.prototype.fun = function() { return new Bar(); };\n" + "/** @constructor */ function Bar(){};\n" + "Bar.prototype.bazz;\n" + "(new Foo).fun().bazz();"; String output = "" + "function Foo(){};\n" + "Foo.prototype.a = function() { return new Bar(); };\n" + "function Bar(){};\n" + "Bar.prototype.a;\n" + "(new Foo).a().a();"; test(js, output); } public void testPrototypePropertiesAsObjLitKeys1() { test("/** @constructor */ function Bar() {};" + "Bar.prototype = {2: function(){}, getA: function(){}};", "/** @constructor */ function Bar() {};" + "Bar.prototype = {2: function(){}, a: function(){}};"); } public void testPrototypePropertiesAsObjLitKeys2() { testSame("/** @constructor */ function Bar() {};" + "Bar.prototype = {2: function(){}, 'getA': function(){}};"); } public void testQuotedPrototypeProperty() { testSame("/** @constructor */ function Bar() {};" + "Bar.prototype['getA'] = function(){};" + "var bar = new Bar();" + "bar['getA']();"); } public void testOverlappingOriginalAndGeneratedNames() { test("/** @constructor */ function Bar(){};" + "Bar.prototype.b = function(){};" + "Bar.prototype.a = function(){};" + "var bar = new Bar();" + "bar.b();", "function Bar(){};" + "Bar.prototype.a = function(){};" + "Bar.prototype.b = function(){};" + "var bar = new Bar();" + "bar.a();"); } public void testPropertyAddedToObject() { testSame("var foo = {}; foo.prop = '';"); } public void testPropertyAddedToFunction() { test("var foo = function(){}; foo.prop = '';", "var foo = function(){}; foo.a = '';"); } public void testPropertyOfObjectOfUnknownType() { testSame("var foo = x(); foo.prop = '';"); } public void testPropertyOnParamOfUnknownType() { testSame("/** @constructor */ function Foo(){};\n" + "Foo.prototype.prop = 0;" + "function go(aFoo){\n" + " aFoo.prop = 1;" + "}"); } public void testSetPropertyOfGlobalThis() { testSame("this.prop = 'bar'"); } public void testReadPropertyOfGlobalThis() { testSame("f(this.prop);"); } public void testSetQuotedPropertyOfThis() { testSame("this['prop'] = 'bar';"); } public void testExternedPropertyName() { test("/** @constructor */ var Bar = function(){};" + "/** @override */ Bar.prototype.toString = function(){};" + "Bar.prototype.func = function(){};" + "var bar = new Bar();" + "bar.toString();", "var Bar = function(){};" + "Bar.prototype.toString = function(){};" + "Bar.prototype.a = function(){};" + "var bar = new Bar();" + "bar.toString();"); } public void testExternedPropertyNameDefinedByObjectLiteral() { testSame("/**@constructor*/function Bar(){};Bar.prototype.factory"); } public void testStaticAndInstanceMethodWithSameName() { test("/** @constructor */function Bar(){}; Bar.getA = function(){}; " + "Bar.prototype.getA = function(){}; Bar.getA();" + "var bar = new Bar(); bar.getA();", "function Bar(){}; Bar.a = function(){};" + "Bar.prototype.a = function(){}; Bar.a();" + "var bar = new Bar(); bar.a();"); } public void testStaticAndInstanceProperties() { test("/** @constructor */function Bar(){};" + "Bar.getA = function(){}; " + "Bar.prototype.getB = function(){};", "function Bar(){}; Bar.a = function(){};" + "Bar.prototype.a = function(){};"); } public void testStaticAndSubInstanceProperties() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.x=0;\n" + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + "goog.inherits(Bar, Foo);\n" + "Bar.y=0;\n" + "Bar.prototype.z=0;\n"; String output = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.a=0;\n" + "/** @constructor \n @extends Foo */ var Bar = function(){};\n" + "goog.inherits(Bar, Foo);\n" + "Bar.a=0;\n" + "Bar.prototype.a=0;\n"; test(js, output); } public void testStaticWithFunctions() { String js = "" + "/** @constructor */ var Foo = function() {};\n" + "Foo.x = 0;" + "/** @param {!Function} x */ function f(x) { x.y = 1 }" + "f(Foo)"; String output = "" + "/** @constructor */ var Foo = function() {};\n" + "Foo.a = 0;" + "/** @param {!Function} x */ function f(x) { x.y = 1 }" + "f(Foo)"; test(js, output); js = "" + "/** @constructor */ var Foo = function() {};\n" + "Foo.x = 0;" + "/** @param {!Function} x */ function f(x) { x.y = 1; x.x = 2;}" + "f(Foo)"; test(js, js); js = "" + "/** @constructor */ var Foo = function() {};\n" + "Foo.x = 0;" + "/** @constructor */ var Bar = function() {};\n" + "Bar.y = 0;"; output = "" + "/** @constructor */ var Foo = function() {};\n" + "Foo.a = 0;" + "/** @constructor */ var Bar = function() {};\n" + "Bar.a = 0;"; test(js, output); } public void testTypeMismatch() { testSame(EXTERNS, "/** @constructor */var Foo = function(){};\n" + "/** @constructor */var Bar = function(){};\n" + "Foo.prototype.b = 0;\n" + "/** @type {Foo} */\n" + "var F = new Bar();", TypeValidator.TYPE_MISMATCH_WARNING, "initializing variable\n" + "found : Bar\n" + "required: (Foo|null)"); } public void testRenamingMap() { String js = "" + "/** @constructor */ var Foo = function(){};\n" + "Foo.prototype.z=0;\n" + "Foo.prototype.z=0;\n" + "Foo.prototype.x=0;\n" + "Foo.prototype.y=0;"; String output = "" + "var Foo = function(){};\n" + "Foo.prototype.a=0;\n" + "Foo.prototype.a=0;\n" + "Foo.prototype.b=0;\n" + "Foo.prototype.c=0;"; test(js, output); Map answerMap = Maps.newHashMap(); answerMap.put("x", "b"); answerMap.put("y", "c"); answerMap.put("z", "a"); assertEquals(answerMap, lastPass.getRenamingMap()); } public void testInline() { String js = "" + "/** @interface */ function Foo(){}\n" + "Foo.prototype.x = function(){};\n" + "/**\n" + " * @constructor\n" + " * @implements {Foo}\n" + " */\n" + "function Bar(){}\n" + "/** @inheritDoc */\n" + "Bar.prototype.x = function() { return this.y; };\n" + "Bar.prototype.z = function() {};\n" // Simulates inline getters. + "/** @type {Foo} */ (new Bar).y;"; String output = "" + "function Foo(){}\n" + "Foo.prototype.a = function(){};\n" + "function Bar(){}\n" + "Bar.prototype.a = function() { return this.b; };\n" + "Bar.prototype.c = function() {};\n" // Simulates inline getters. + "(new Bar).b;"; test(js, output); } public void testImplementsAndExtends() { String js = "" + "/** @interface */ function Foo() {}\n" + "/**\n" + " * @constructor\n" + " */\n" + "function Bar(){}\n" + "Bar.prototype.y = function() { return 3; };\n" + "/**\n" + " * @constructor\n" + " * @extends {Bar}\n" + " * @implements {Foo}\n" + " */\n" + "function SubBar(){ }\n" + "/** @param {Foo} x */ function f(x) { x.z = 3; }\n" + "/** @param {SubBar} x */ function g(x) { x.z = 3; }"; String output = "" + "function Foo(){}\n" + "function Bar(){}\n" + "Bar.prototype.b = function() { return 3; };\n" + "function SubBar(){}\n" + "function f(x) { x.a = 3; }\n" + "function g(x) { x.a = 3; }"; test(js, output); } public void testImplementsAndExtends2() { String js = "" + "/** @interface */ function A() {}\n" + "/**\n" + " * @constructor\n" + " */\n" + "function C1(){}\n" + "/**\n" + " * @constructor\n" + " * @extends {C1}\n" + " * @implements {A}\n" + " */\n" + "function C2(){}\n" + "/** @param {C1} x */ function f(x) { x.y = 3; }\n" + "/** @param {A} x */ function g(x) { x.z = 3; }\n"; String output = "" + "function A(){}\n" + "function C1(){}\n" + "function C2(){}\n" + "function f(x) { x.a = 3; }\n" + "function g(x) { x.b = 3; }\n"; test(js, output); } public void testExtendsInterface() { String js = "" + "/** @interface */ function A() {}\n" + "/** @interface \n @extends {A} */ function B() {}\n" + "/** @param {A} x */ function f(x) { x.y = 3; }\n" + "/** @param {B} x */ function g(x) { x.z = 3; }\n"; String output = "" + "function A(){}\n" + "function B(){}\n" + "function f(x) { x.a = 3; }\n" + "function g(x) { x.b = 3; }\n"; test(js, output); } public void testFunctionSubType() { String js = "" + "Function.prototype.a = 1;\n" + "function f() {}\n" + "f.y = 2;\n"; String output = "" + "Function.prototype.a = 1;\n" + "function f() {}\n" + "f.b = 2;\n"; test(js, output); } public void testFunctionSubType2() { String js = "" + "Function.prototype.a = 1;\n" + "/** @constructor */ function F() {}\n" + "F.y = 2;\n"; String output = "" + "Function.prototype.a = 1;\n" + "function F() {}\n" + "F.b = 2;\n"; test(js, output); } public void testPredeclaredType() { String js = "goog.addDependency('zzz.js', ['goog.Foo'], []);" + "/** @constructor */ " + "function A() {" + " this.x = 3;" + "}" + "/** @param {goog.Foo} x */" + "function f(x) { x.y = 4; }"; String result = "0;" + "/** @constructor */ " + "function A() {" + " this.a = 3;" + "}" + "/** @param {goog.Foo} x */" + "function f(x) { x.y = 4; }"; test(js, result); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CreateSyntheticBlocksTest.java0000644000175000017500000000733312115204405030771 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Tests for {@link CreateSyntheticBlocks} * */ public class CreateSyntheticBlocksTest extends CompilerTestCase { private static final String START_MARKER = "startMarker"; private static final String END_MARKER = "endMarker"; public CreateSyntheticBlocksTest() { // Can't use compare as a tree because of the added synthetic blocks. super("", false); } @Override public void setUp() { super.enableLineNumberCheck(false); } @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node js) { new CreateSyntheticBlocks(compiler, START_MARKER, END_MARKER).process( externs, js); NodeTraversal.traverse(compiler, js, new MinimizeExitPoints(compiler)); new PeepholeOptimizationsPass(compiler, new PeepholeRemoveDeadCode(), new PeepholeSubstituteAlternateSyntax(true), new PeepholeFoldConstants(true)) .process(externs, js); new MinimizeExitPoints(compiler).process(externs, js); new Denormalize(compiler).process(externs, js); } }; } @Override protected int getNumRepetitions() { return 1; } // TODO(johnlenz): Add tests to the IntegrationTest. public void testFold1() { test("function f() { if (x) return; y(); }", "function f(){x||y()}"); } public void testFoldWithMarkers1() { testSame("function f(){startMarker();if(x)return;endMarker();y()}"); } public void testFoldWithMarkers1a() { testSame("function f(){startMarker();if(x)return;endMarker()}"); } public void testFold2() { test("function f() { if (x) return; y(); if (a) return; b(); }", "function f(){if(!x){y();a||b()}}"); } public void testFoldWithMarkers2() { testSame("function f(){startMarker(\"FOO\");startMarker(\"BAR\");" + "if(x)return;endMarker(\"BAR\");y();if(a)return;" + "endMarker(\"FOO\");b()}"); } public void testUnmatchedStartMarker() { testSame("startMarker()", CreateSyntheticBlocks.UNMATCHED_START_MARKER); } public void testUnmatchedEndMarker1() { testSame("endMarker()", CreateSyntheticBlocks.UNMATCHED_END_MARKER); } public void testUnmatchedEndMarker2() { test("if(y){startMarker();x()}endMarker()", "if(y){startMarker();x()}endMarker()", null, CreateSyntheticBlocks.UNMATCHED_END_MARKER); } public void testInvalid1() { test("startMarker() && true", "startMarker()", null, CreateSyntheticBlocks.INVALID_MARKER_USAGE); } public void testInvalid2() { test("false && endMarker()", "", null, CreateSyntheticBlocks.INVALID_MARKER_USAGE); } public void testDenormalize() { testSame("startMarker();for(;;);endMarker()"); } public void testNonMarkingUse() { testSame("function foo(endMarker){}"); testSame("function foo(){startMarker:foo()}"); } public void testContainingBlockPreservation() { testSame("if(y){startMarker();x();endMarker()}"); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ExpandJqueryAliasesTest.java0000644000175000017500000001556012115204405030457 0ustar apoapo/* * Copyright 2011 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; /** * Tests for {@link ExpandJqueryAliases} */ public class ExpandJqueryAliasesTest extends CompilerTestCase { private JqueryCodingConvention conv = new JqueryCodingConvention(); final DiagnosticType NAME_ERROR = ExpandJqueryAliases.JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR; final DiagnosticType INVALID_LIT_ERROR = ExpandJqueryAliases.JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR; final DiagnosticType USELESS_EACH_ERROR = ExpandJqueryAliases.JQUERY_USELESS_EACH_EXPANSION; public ExpandJqueryAliasesTest() {} @Override protected CompilerPass getProcessor(Compiler compiler) { compiler.options.setCodingConvention(conv); return new ExpandJqueryAliases(compiler); } public void testJqueryFnAliasExpansion() { String setupCode = "var jQuery={};jQuery.fn=jQuery.prototype;"; testSame(setupCode); test(setupCode + "jQuery.fn.foo='bar';", setupCode + "jQuery.prototype.foo='bar';"); test(setupCode + "jQuerySub.fn.foo='bar';", setupCode + "jQuerySub.prototype.foo='bar';"); } public void testJqueryExtendExpansion() { String setupCode = "var jQuery={},obj2={};"; // test for extend call that should not be expanded - no arguments testSame(setupCode + "jQuery.extend()"); // test for extend call that should not be expanded - empty argument // this statement has no effect in actual code testSame(setupCode + "jQuery.extend({})"); // test single argument call - should assign to the jQuery namespace test(setupCode + "jQuery.extend({a:'test'})", setupCode + "{jQuery.a = 'test';}"); // test expansion when extending the jQuery prototype test(setupCode + "jQuery.fn=jQuery.prototype;" + "jQuery.fn.extend({a:'test', b:'test2'});", setupCode + "jQuery.fn=jQuery.prototype;" + "{jQuery.prototype.a = 'test'; jQuery.prototype.b = 'test2';}"); // Expand the extension of obj2 test(setupCode + "jQuery.extend(obj2, {a:'test', b:'test2'});", setupCode + "{obj2=obj2||{}; obj2.a='test'; obj2.b='test2';}"); // Expand the jQuery namespace - 2 argument call // Must ensure that the first argument is defined test(setupCode + "jQuery.extend(jQuery,{a:'test', b:'test2'});", setupCode + "{jQuery = jQuery || {}; jQuery.a = 'test';" + "jQuery.b = 'test2';}"); // Test extend call where first argument includes a method call testSame(setupCode+"obj2.meth=function() { return { a:{} }; };" + "jQuery.extend(obj2.meth().a, {a: 'test'});"); } public void testJqueryExpandedEachExpansion() { String setupCode = "var jQuery={};" + "jQuery.expandedEach=function(vals, callback){};"; testSame(setupCode); // Test expansion with object literal test(setupCode + "jQuery.expandedEach({'a': 1, 'b': 2, 'c': 8}," + "function(key, val) { var a = key; jQuery[key] = val; });", setupCode + "(function(){ var a = 'a'; jQuery.a = 1 })();" + "(function(){ var a = 'b'; jQuery.b = 2 })();" + "(function(){ var a = 'c'; jQuery.c = 8 })();"); // Test expansion with array literal // For array literals, the key parameter will be the element index number // and the value parameter will be the string literal. In this case, the // string literal value should become a property name. test(setupCode + "jQuery.expandedEach(['a', 'b', 'c']," + "function(key, val){ jQuery[val] = key; });", setupCode + "(function(){ jQuery.a = 0; })();" + "(function(){ jQuery.b = 1; })();" + "(function(){ jQuery.c = 2 })();"); // Test expansion with object literal using 'this' keyword test(setupCode + "jQuery.expandedEach({'a': 1, 'b': 2, 'c': 8}," + "function(key, val) { var a = key; jQuery[key] = this; });", setupCode + "(function(){ var a = 'a'; jQuery.a = 1 })();" + "(function(){ var a = 'b'; jQuery.b = 2 })();" + "(function(){ var a = 'c'; jQuery.c = 8 })();"); // Test expansion with array literal using 'this' keyword test(setupCode + "jQuery.expandedEach(['a', 'b', 'c']," + "function(key, val){ jQuery[this] = key; });", setupCode + "(function(){ jQuery.a = 0; })();" + "(function(){ jQuery.b = 1; })();" + "(function(){ jQuery.c = 2 })();"); // test nested function using argument name to shadow callback name test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + "jQuery[val] = key; (function(key) { jQuery[key] = 1;})('test'); })", setupCode + "(function(){ jQuery.a = 0;" + "(function(key){ jQuery[key] = 1})('test') })()"); // test nested function using var name to shadow callback name test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + "jQuery[val] = key; (function(key) { var val = 2;" + "jQuery[key] = val;})('test');})", setupCode + "(function(){" + "jQuery.a=0;" + "(function(key){var val = 2; jQuery[key] = val;})('test')})()"); // test nested function using function name to shadow callback name test(setupCode + "jQuery.expandedEach(['a'], function(key,val) {" + "jQuery[val] = key; (function(key1) {" + "function key() {}; key();" + "})('test');})", setupCode + "(function(){" + "jQuery.a=0;(function(key1) {" + "function key() {}; key(); })('test')})()"); // test using return val test(setupCode + "alert(jQuery.expandedEach(['a']," + "function(key,val) { jQuery[val] = key;})[0])", setupCode + "alert((function(){" + "(function(){ jQuery.a = 0;})(); return ['a']})()[0]);"); // Loop object is a variable. Test that warning is raised. testSame(setupCode + "var a = ['a'];" + "jQuery.expandedEach(a, function(key,val){ jQuery[key]=val; })", INVALID_LIT_ERROR); // Invalid property name. Test that warning is raised. testSame(setupCode + "var obj2={};" + "jQuery.expandedEach(['foo','bar'], function(i, name) {" + "obj2[ '[object ' + name + ']' ] = 'a';});", NAME_ERROR, true); // Useless expansion (key not used). Test that warning is raised. testSame(setupCode + "var obj2={};" + "jQuery.expandedEach(['foo','bar'], function(i, name) {" + "obj2[i] = 1;});", USELESS_EACH_ERROR, false); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckSideEffectsTest.java0000644000175000017500000001052612115204405027655 0ustar apoapo/* * Copyright 2006 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.CheckLevel; public class CheckSideEffectsTest extends CompilerTestCase { public CheckSideEffectsTest() { this.parseTypeInfo = true; allowExternsChanges(true); } @Override protected int getNumRepetitions() { return 1; } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CheckSideEffects(compiler, CheckLevel.WARNING, true); } @Override public void test(String js, String expected, DiagnosticType warning) { test(js, expected, null, warning); } public void test(String js, DiagnosticType warning) { test(js, js, null, warning); } final DiagnosticType e = CheckSideEffects.USELESS_CODE_ERROR; final DiagnosticType ok = null; // no warning public void testUselessCode() { test("function f(x) { if(x) return; }", ok); test("function f(x) { if(x); }", "function f(x) { if(x); }", e); test("if(x) x = y;", ok); test("if(x) x == bar();", "if(x) JSCOMPILER_PRESERVE(x == bar());", e); test("x = 3;", ok); test("x == 3;", "JSCOMPILER_PRESERVE(x == 3);", e); test("var x = 'test'", ok); test("var x = 'test'\n'str'", "var x = 'test'\nJSCOMPILER_PRESERVE('str')", e); test("", ok); test("foo();;;;bar();;;;", ok); test("var a, b; a = 5, b = 6", ok); test("var a, b; a = 5, b == 6", "var a, b; a = 5, JSCOMPILER_PRESERVE(b == 6)", e); test("var a, b; a = (5, 6)", "var a, b; a = (JSCOMPILER_PRESERVE(5), 6)", e); test("var a, b; a = (bar(), 6, 7)", "var a, b; a = (bar(), JSCOMPILER_PRESERVE(6), 7)", e); test("var a, b; a = (bar(), bar(), 7, 8)", "var a, b; a = (bar(), bar(), JSCOMPILER_PRESERVE(7), 8)", e); test("var a, b; a = (b = 7, 6)", ok); test("function x(){}\nfunction f(a, b){}\nf(1,(x(), 2));", ok); test("function x(){}\nfunction f(a, b){}\nf(1,(2, 3));", "function x(){}\nfunction f(a, b){}\n" + "f(1,(JSCOMPILER_PRESERVE(2), 3));", e); } public void testUselessCodeInFor() { test("for(var x = 0; x < 100; x++) { foo(x) }", ok); test("for(; true; ) { bar() }", ok); test("for(foo(); true; foo()) { bar() }", ok); test("for(void 0; true; foo()) { bar() }", "for(JSCOMPILER_PRESERVE(void 0); true; foo()) { bar() }", e); test("for(foo(); true; void 0) { bar() }", "for(foo(); true; JSCOMPILER_PRESERVE(void 0)) { bar() }", e); test("for(foo(); true; (1, bar())) { bar() }", "for(foo(); true; (JSCOMPILER_PRESERVE(1), bar())) { bar() }", e); test("for(foo in bar) { foo() }", ok); test("for (i = 0; el = el.previousSibling; i++) {}", ok); test("for (i = 0; el = el.previousSibling; i++);", ok); } public void testTypeAnnotations() { test("x;", "JSCOMPILER_PRESERVE(x);", e); test("a.b.c.d;", "JSCOMPILER_PRESERVE(a.b.c.d);", e); test("/** @type Number */ a.b.c.d;", ok); test("if (true) { /** @type Number */ a.b.c.d; }", ok); test("function A() { this.foo; }", "function A() { JSCOMPILER_PRESERVE(this.foo); }", e); test("function A() { /** @type Number */ this.foo; }", ok); } public void testJSDocComments() { test("function A() { /** This is a JsDoc comment */ this.foo; }", ok); test("function A() { /* This is a normal comment */ this.foo; }", "function A() { " + " /* This is a normal comment */ JSCOMPILER_PRESERVE(this.foo); }", e); } public void testIssue80() { test("(0, eval)('alert');", ok); test("(0, foo)('alert');", "(JSCOMPILER_PRESERVE(0), foo)('alert');", e); } public void testIsue504() { test("void f();", "JSCOMPILER_PRESERVE(void f());", null, e, "Suspicious code. The result of the 'void' operator is not being used."); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/ProcessCommonJSModulesTest.java0000644000175000017500000001414712115204405031113 0ustar apoapo/* * Copyright 2011 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 java.util.List; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; /** * Unit tests for {@link ProcessCommonJSModules} */ public class ProcessCommonJSModulesTest extends CompilerTestCase { public ProcessCommonJSModulesTest() { } @Override protected CompilerPass getProcessor(Compiler compiler) { return new ProcessCommonJSModules(compiler, "foo/bar/", false); } @Override protected int getNumRepetitions() { return 1; } public void testWithoutExports() { setFilename("test"); test( "var name = require('name');" + "name()", "goog.provide('module$test');" + "var module$test = {};" + "goog.require('module$name');" + "var name$$module$test = module$name;" + "name$$module$test();"); setFilename("test/sub"); test( "var name = require('mod/name');" + "(function() { name(); })();", "goog.provide('module$test$sub');" + "var module$test$sub = {};" + "goog.require('module$mod$name');" + "var name$$module$test$sub = module$mod$name;" + "(function() { name$$module$test$sub(); })();"); } public void testExports() { setFilename("test"); test( "var name = require('name');" + "exports.foo = 1;", "goog.provide('module$test');" + "var module$test = {};" + "goog.require('module$name');" + "var name$$module$test = module$name;" + "module$test.foo = 1;"); test( "var name = require('name');" + "module.exports = function() {};", "goog.provide('module$test');" + "var module$test = {};" + "goog.require('module$name');" + "var name$$module$test = module$name;" + "module$test.module$exports = function() {};" + "if(module$test.module$exports)" + "module$test=module$test.module$exports"); } public void testVarRenaming() { setFilename("test"); test( "var a = 1, b = 2;" + "(function() { var a; b = 4})()", "goog.provide('module$test');" + "var module$test = {};" + "var a$$module$test = 1, b$$module$test = 2;" + "(function() { var a; b$$module$test = 4})();"); } public void testDash() { setFilename("test-test"); test( "var name = require('name'); exports.foo = 1;", "goog.provide('module$test_test');" + "var module$test_test = {};" + "goog.require('module$name');" + "var name$$module$test_test = module$name;" + "module$test_test.foo = 1;"); } public void testModuleName() { assertEquals("module$foo$baz", ProcessCommonJSModules.toModuleName("./baz.js", "foo/bar.js")); assertEquals("module$foo$baz_bar", ProcessCommonJSModules.toModuleName("./baz-bar.js", "foo/bar.js")); assertEquals("module$baz", ProcessCommonJSModules.toModuleName("../baz.js", "foo/bar.js")); assertEquals("module$baz", ProcessCommonJSModules.toModuleName("../../baz.js", "foo/bar/abc.js")); assertEquals("module$baz", ProcessCommonJSModules.toModuleName( "../../../baz.js", "foo/bar/abc/xyz.js")); setFilename("foo/bar"); test( "var name = require('name');", "goog.provide('module$foo$bar'); var module$foo$bar = {};" + "goog.require('module$name');" + "var name$$module$foo$bar = module$name;"); test( "var name = require('./name');", "goog.provide('module$foo$bar');" + "var module$foo$bar = {};" + "goog.require('module$foo$name');" + "var name$$module$foo$bar = module$foo$name;"); } public void testGuessModuleName() { ProcessCommonJSModules pass = new ProcessCommonJSModules(null, "foo"); assertEquals("module$baz", pass.guessCJSModuleName("foo/baz.js")); assertEquals("module$baz", pass.guessCJSModuleName("foo\\baz.js")); assertEquals("module$bar$baz", pass.guessCJSModuleName("foo\\bar\\baz.js")); } public void testSortInputs() throws Exception { SourceFile a = SourceFile.fromCode("a.js", "require('b');require('c')"); SourceFile b = SourceFile.fromCode("b.js", "require('d')"); SourceFile c = SourceFile.fromCode("c.js", "require('d')"); SourceFile d = SourceFile.fromCode("d.js", "1;"); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(a, b, c, d)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, b, c, a)); assertSortedInputs( ImmutableList.of(d, c, b, a), ImmutableList.of(d, c, b, a)); assertSortedInputs( ImmutableList.of(d, b, c, a), ImmutableList.of(d, a, b, c)); } private void assertSortedInputs( List expected, List shuffled) throws Exception { Compiler compiler = new Compiler(System.err); compiler.initCompilerOptionsIfTesting(); compiler.getOptions().setProcessCommonJSModules(true); compiler.getOptions().dependencyOptions.setEntryPoints( Lists.newArrayList(ProcessCommonJSModules.toModuleName("a"))); compiler.compile(Lists.newArrayList(SourceFile.fromCode("externs.js", "")), shuffled, compiler.getOptions()); List result = Lists.newArrayList(); for (JSModule m : compiler.getModuleGraph().getAllModules()) { for (CompilerInput i : m.getInputs()) { result.add(i.getSourceFile()); } } assertEquals(expected, result); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/RenamePrototypesTest.java0000644000175000017500000002236412115204405030056 0ustar apoapo/* * Copyright 2005 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; public class RenamePrototypesTest extends CompilerTestCase { private static final String EXTERNS = "var js_version;js_version.toString;"; private VariableMap prevUsedRenameMap; private RenamePrototypes renamePrototypes; public RenamePrototypesTest() { super(EXTERNS); enableNormalize(); } @Override public CompilerPass getProcessor(Compiler compiler) { return renamePrototypes = new RenamePrototypes(compiler, true, null, prevUsedRenameMap); } @Override protected void tearDown() throws Exception { super.tearDown(); prevUsedRenameMap = null; } @Override protected int getNumRepetitions() { // The RenamePrototypes pass should only be run once over a parse tree. return 1; } public void testRenamePrototypes1() { test("Bar.prototype={'getFoo':function(){},2:function(){}}", "Bar.prototype={'a':function(){},2:function(){}}"); } public void testRenamePrototypes2() { // Simple test("Bar.prototype.getFoo=function(){};Bar.getFoo(b);" + "Bar.prototype.getBaz=function(){}", "Bar.prototype.a=function(){};Bar.a(b);" + "Bar.prototype.b=function(){}"); test("Bar.prototype['getFoo']=function(){};Bar.getFoo(b);" + "Bar.prototype['getBaz']=function(){}", "Bar.prototype['a']=function(){};Bar.a(b);" + "Bar.prototype['b']=function(){}"); test("Bar.prototype={'getFoo':function(){},2:function(){}}", "Bar.prototype={'a':function(){},2:function(){}}"); test("Bar.prototype={'getFoo':function(){}," + "'getBar':function(){}};b.getFoo()", "Bar.prototype={'a':function(){}," + "'b':function(){}};b.a()"); test("Bar.prototype={'B':function(){}," + "'getBar':function(){}};b.getBar()", "Bar.prototype={'b':function(){}," + "'a':function(){}};b.a()"); // overlap test("Bar.prototype={'a':function(){}," + "'b':function(){}};b.b()", "Bar.prototype={'b':function(){}," + "'a':function(){}};b.a()"); // don't rename anything with a leading underscore test("Bar.prototype={'_getFoo':function(){}," + "'getBar':function(){}};b._getFoo()", "Bar.prototype={'_getFoo':function(){}," + "'a':function(){}};b._getFoo()"); // Externed methods test("Bar.prototype={'toString':function(){}," + "'getBar':function(){}};b.toString()", "Bar.prototype={'toString':function(){}," + "'a':function(){}};b.toString()"); // don't rename a method to an existing (unrenamed) property test("Bar.prototype.foo=function(){}" + ";bar.foo();bar.a", "Bar.prototype.b=function(){}" + ";bar.b();bar.a"); } public void testRenamePrototypesWithGetOrSet() { // Simple // TODO(johnlenz): Enable these for after Rhino support is added. // test("Bar.prototype={get 'getFoo'(){}}", // "Bar.prototype={get a(){}}"); // test("Bar.prototype={get 2(){}}", // "Bar.prototype={get 2(){}}"); test("Bar.prototype={get getFoo(){}}", "Bar.prototype={get a(){}}"); test("Bar.prototype={get getFoo(){}}; a.getFoo;", "Bar.prototype={get a(){}}; a.a;"); // TODO(johnlenz): Enable these for after Rhino support is added. // test("Bar.prototype={set 'getFoo'(x){}}", // "Bar.prototype={set a(x){}}"); // test("Bar.prototype={set 2(x){}}", // "Bar.prototype={set 2(x){}}"); test("Bar.prototype={set getFoo(x){}}", "Bar.prototype={set a(x){}}"); test("Bar.prototype={set getFoo(x){}}; a.getFoo;", "Bar.prototype={set a(x){}}; a.a;"); // overlap test("Bar.prototype={get a(){}," + "get b(){}};b.b()", "Bar.prototype={get b(){}," + "get a(){}};b.a()"); } /** * Test renaming private properties (end with underscores) and test to make * sure we don't rename other properties. */ public void testRenameProperties() { test("var foo; foo.prop_='bar'", "var foo;foo.a='bar'"); test("this.prop_='bar'", "this.a='bar'"); test("this.prop='bar'", "this.prop='bar'"); test("this['prop_']='bar'", "this['a']='bar'"); test("this['prop']='bar'", "this['prop']='bar'"); test("var foo={prop1_: 'bar',prop2_: 'baz'};", "var foo={a:'bar',b:'baz'}"); } /** * Tests a potential tricky interaction between prototype renaming and * property renaming. */ public void testBoth() { test("Bar.prototype.getFoo_=function(){};Bar.getFoo_(b);" + "Bar.prototype.getBaz_=function(){}", "Bar.prototype.a=function(){};Bar.a(b);" + "Bar.prototype.b=function(){}"); } public void testPropertyNameThatIsBothObjLitKeyAndPrototypeProperty() { // This test protects against regression of a bug where non-private object // literal keys were getting renamed if they clashed with custom prototype // methods. Now we don't simply don't rename in this situation, since // references like z.myprop are ambiguous. test("x.prototype.myprop=function(){};y={myprop:0};z.myprop", "x.prototype.myprop=function(){};y={myprop:0};z.myprop"); // This test shows that a property can be renamed if both the prototype // property renaming policy and the objlit key renaming policy agree that // it can be renamed. test("x.prototype.myprop_=function(){};y={myprop_:0};z.myprop_", "x.prototype.a=function(){};y={a:0};z.a"); } public void testModule() { JSModule[] modules = createModules( "function Bar(){} var foo; Bar.prototype.getFoo_=function(x){};" + "foo.getFoo_(foo);foo.doo_=foo;foo.bloo_=foo;", "function Far(){} var too; Far.prototype.getGoo_=function(x){};" + "too.getGoo_(too);too.troo_=too;too.bloo_=too;"); test(modules, new String[] { "function Bar(){}var foo; Bar.prototype.a=function(x){};" + "foo.a(foo);foo.d=foo;foo.c=foo;", "function Far(){}var too; Far.prototype.b=function(x){};" + "too.b(too);too.e=too;too.c=too;" }); } public void testStableSimple1() { testStable( "Bar.prototype.getFoo=function(){};Bar.getFoo(b);" + "Bar.prototype.getBaz=function(){}", "Bar.prototype.a=function(){};Bar.a(b);" + "Bar.prototype.b=function(){}", "Bar.prototype.getBar=function(){};Bar.getBar(b);" + "Bar.prototype.getFoo=function(){};Bar.getFoo(b);" + "Bar.prototype.getBaz=function(){}", "Bar.prototype.c=function(){};Bar.c(b);" + "Bar.prototype.a=function(){};Bar.a(b);" + "Bar.prototype.b=function(){}"); } public void testStableSimple2() { testStable( "Bar.prototype['getFoo']=function(){};Bar.getFoo(b);" + "Bar.prototype['getBaz']=function(){}", "Bar.prototype['a']=function(){};Bar.a(b);" + "Bar.prototype['b']=function(){}", "Bar.prototype['getFoo']=function(){};Bar.getFoo(b);" + "Bar.prototype['getBar']=function(){};" + "Bar.prototype['getBaz']=function(){}", "Bar.prototype['a']=function(){};Bar.a(b);" + "Bar.prototype['c']=function(){};" + "Bar.prototype['b']=function(){}"); } public void testStableSimple3() { testStable( "Bar.prototype={'getFoo':function(){}," + "'getBar':function(){}};b.getFoo()", "Bar.prototype={'a':function(){}, 'b':function(){}};b.a()", "Bar.prototype={'getFoo':function(){}," + "'getBaz':function(){},'getBar':function(){}};b.getFoo()", "Bar.prototype={'a':function(){}, " + "'c':function(){}, 'b':function(){}};b.a()"); } public void testStableOverlap() { testStable( "Bar.prototype={'a':function(){},'b':function(){}};b.b()", "Bar.prototype={'b':function(){},'a':function(){}};b.a()", "Bar.prototype={'a':function(){},'b':function(){}};b.b()", "Bar.prototype={'b':function(){},'a':function(){}};b.a()"); } public void testStableTrickyExternedMethods() { test("Bar.prototype={'toString':function(){}," + "'getBar':function(){}};b.toString()", "Bar.prototype={'toString':function(){}," + "'a':function(){}};b.toString()"); prevUsedRenameMap = renamePrototypes.getPropertyMap(); String externs = EXTERNS + "prop.a;"; test(externs, "Bar.prototype={'toString':function(){}," + "'getBar':function(){}};b.toString()", "Bar.prototype={'toString':function(){}," + "'b':function(){}};b.toString()", null, null); } public void testStable(String input1, String expected1, String input2, String expected2) { test(input1, expected1); prevUsedRenameMap = renamePrototypes.getPropertyMap(); test(input2, expected2); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/AstValidatorTest.java0000644000175000017500000000737612115204405027141 0ustar apoapo/* * Copyright 2011 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.AstValidator.ViolationHandler; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.SimpleSourceFile; /** * @author johnlenz@google.com (John Lenz) */ public class AstValidatorTest extends CompilerTestCase { private boolean lastCheckWasValid = true; @Override protected CompilerPass getProcessor(Compiler compiler) { return createValidator(); } private AstValidator createValidator() { lastCheckWasValid = true; return new AstValidator(new ViolationHandler() { @Override public void handleViolation(String message, Node n) { lastCheckWasValid = false; } }); } @Override protected int getNumRepetitions() { return 1; } @Override protected void setUp() throws Exception { super.enableAstValidation(false); super.disableNormalize(); super.enableLineNumberCheck(false); super.setUp(); } public void testForIn() { valid("for(var a in b);"); valid("for(var a = 1 in b);"); valid("for(a in b);"); valid("for(a in []);"); valid("for(a in {});"); } public void testDebugger() { valid("debugger;"); } public void testValidScript() { Node n = new Node(Token.SCRIPT); expectInvalid(n, Check.SCRIPT); n.setInputId(new InputId("something_input")); n.setStaticSourceFile(new SimpleSourceFile("something", false)); expectValid(n, Check.SCRIPT); expectInvalid(n, Check.STATEMENT); expectInvalid(n, Check.EXPRESSION); } public void testValidStatement1() { Node n = new Node(Token.RETURN); expectInvalid(n, Check.EXPRESSION); expectValid(n, Check.STATEMENT); expectInvalid(n, Check.SCRIPT); } public void testValidExpression1() { Node n = new Node(Token.ARRAYLIT, new Node(Token.EMPTY)); expectValid(n, Check.EXPRESSION); expectInvalid(n, Check.STATEMENT); expectInvalid(n, Check.SCRIPT); } public void testValidExpression2() { Node n = new Node(Token.NOT, new Node(Token.TRUE)); expectValid(n, Check.EXPRESSION); expectInvalid(n, Check.STATEMENT); expectInvalid(n, Check.SCRIPT); } public void testInvalidEmptyStatement() { Node n = new Node(Token.EMPTY, new Node(Token.TRUE)); expectInvalid(n, Check.STATEMENT); n.detachChildren(); expectValid(n, Check.STATEMENT); } private void valid(String code) { testSame(code); assertTrue(lastCheckWasValid); } private enum Check { SCRIPT, STATEMENT, EXPRESSION } private boolean doCheck(Node n, Check level) { AstValidator validator = createValidator(); switch (level) { case SCRIPT: validator.validateScript(n); break; case STATEMENT: validator.validateStatement(n); break; case EXPRESSION: validator.validateExpression(n); break; } return lastCheckWasValid; } private void expectInvalid(Node n, Check level) { assertFalse(doCheck(n, level)); } private void expectValid(Node n, Check level) { assertTrue(doCheck(n, level)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CommandLineRunnerTest.java0000644000175000017500000012656512115204405030126 0ustar apoapo/* * 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.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.AbstractCommandLineRunner.FlagUsageException; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.rhino.Node; import junit.framework.TestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.List; import java.util.Map; /** * Tests for {@link CommandLineRunner}. * * @author nicksantos@google.com (Nick Santos) */ public class CommandLineRunnerTest extends TestCase { private Compiler lastCompiler = null; private CommandLineRunner lastCommandLineRunner = null; private List exitCodes = null; private ByteArrayOutputStream outReader = null; private ByteArrayOutputStream errReader = null; private Map filenames; // If set, this will be appended to the end of the args list. // For testing args parsing. private String lastArg = null; // If set to true, uses comparison by string instead of by AST. private boolean useStringComparison = false; private ModulePattern useModules = ModulePattern.NONE; private enum ModulePattern { NONE, CHAIN, STAR } private List args = Lists.newArrayList(); /** Externs for the test */ private final List DEFAULT_EXTERNS = ImmutableList.of( SourceFile.fromCode("externs", "var arguments;" + "/**\n" + " * @constructor\n" + " * @param {...*} var_args\n" + " * @nosideeffects\n" + " * @throws {Error}\n" + " */\n" + "function Function(var_args) {}\n" + "/**\n" + " * @param {...*} var_args\n" + " * @return {*}\n" + " */\n" + "Function.prototype.call = function(var_args) {};" + "/**\n" + " * @constructor\n" + " * @param {...*} var_args\n" + " * @return {!Array}\n" + " */\n" + "function Array(var_args) {}" + "/**\n" + " * @param {*=} opt_begin\n" + " * @param {*=} opt_end\n" + " * @return {!Array}\n" + " * @this {Object}\n" + " */\n" + "Array.prototype.slice = function(opt_begin, opt_end) {};" + "/** @constructor */ function Window() {}\n" + "/** @type {string} */ Window.prototype.name;\n" + "/** @type {Window} */ var window;" + "/** @constructor */ function Element() {}" + "Element.prototype.offsetWidth;" + "/** @nosideeffects */ function noSideEffects() {}\n" + "/** @param {...*} x */ function alert(x) {}\n") ); private List externs; @Override public void setUp() throws Exception { super.setUp(); externs = DEFAULT_EXTERNS; filenames = Maps.newHashMap(); lastCompiler = null; lastArg = null; outReader = new ByteArrayOutputStream(); errReader = new ByteArrayOutputStream(); useStringComparison = false; useModules = ModulePattern.NONE; args.clear(); exitCodes = Lists.newArrayList(); } @Override public void tearDown() throws Exception { super.tearDown(); } public void testUnknownAnnotation() { args.add("--warning_level=VERBOSE"); test("/** @unknownTag */ function f() {}", RhinoErrorReporter.BAD_JSDOC_ANNOTATION); args.add("--extra_annotation_name=unknownTag"); testSame("/** @unknownTag */ function f() {}"); } public void testWarningGuardOrdering1() { args.add("--jscomp_error=globalThis"); args.add("--jscomp_off=globalThis"); testSame("function f() { this.a = 3; }"); } public void testWarningGuardOrdering2() { args.add("--jscomp_off=globalThis"); args.add("--jscomp_error=globalThis"); test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testWarningGuardOrdering3() { args.add("--jscomp_warning=globalThis"); args.add("--jscomp_off=globalThis"); testSame("function f() { this.a = 3; }"); } public void testWarningGuardOrdering4() { args.add("--jscomp_off=globalThis"); args.add("--jscomp_warning=globalThis"); test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testSimpleModeLeavesUnusedParams() { args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); testSame("window.f = function(a) {};"); } public void testAdvancedModeRemovesUnusedParams() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test("window.f = function(a) {};", "window.a = function() {};"); } public void testCheckGlobalThisOffByDefault() { testSame("function f() { this.a = 3; }"); } public void testCheckGlobalThisOnWithAdvancedMode() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testCheckGlobalThisOnWithErrorFlag() { args.add("--jscomp_error=globalThis"); test("function f() { this.a = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testCheckGlobalThisOff() { args.add("--warning_level=VERBOSE"); args.add("--jscomp_off=globalThis"); testSame("function f() { this.a = 3; }"); } public void testTypeCheckingOffByDefault() { test("function f(x) { return x; } f();", "function f(a) { return a; } f();"); } public void testReflectedMethods() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test( "/** @constructor */" + "function Foo() {}" + "Foo.prototype.handle = function(x, y) { alert(y); };" + "var x = goog.reflect.object(Foo, {handle: 1});" + "for (var i in x) { x[i].call(x); }" + "window['Foo'] = Foo;", "function a() {}" + "a.prototype.a = function(e, d) { alert(d); };" + "var b = goog.c.b(a, {a: 1}),c;" + "for (c in b) { b[c].call(b); }" + "window.Foo = a;"); } public void testInlineVariables() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test( "/** @constructor */ function F() { this.a = 0; }" + "F.prototype.inc = function() { this.a++; return 10; };" + "F.prototype.bar = function() { " + " var c = 3; var val = inc(); this.a += val + c;" + "};" + "window['f'] = new F();" + "window['f']['bar'] = window['f'].bar;", "function a(){ this.a = 0; }" + "a.prototype.b = function(){ var b=inc(); this.a += b + 3; };" + "window.f = new a;" + "window.f.bar = window.f.b"); } public void testTypedAdvanced() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); args.add("--use_types_for_optimization"); test( "/** @constructor */\n" + "function Foo() {}\n" + "Foo.prototype.handle1 = function(x, y) { alert(y); };\n" + "/** @constructor */\n" + "function Bar() {}\n" + "Bar.prototype.handle1 = function(x, y) {};\n" + "new Foo().handle1(1, 2);\n" + "new Bar().handle1(1, 2);\n", "alert(2)"); } public void testTypeCheckingOnWithVerbose() { args.add("--warning_level=VERBOSE"); test("function f(x) { return x; } f();", TypeCheck.WRONG_ARGUMENT_COUNT); } public void testTypeParsingOffByDefault() { testSame("/** @return {number */ function f(a) { return a; }"); } public void testTypeParsingOnWithVerbose() { args.add("--warning_level=VERBOSE"); test("/** @return {number */ function f(a) { return a; }", RhinoErrorReporter.TYPE_PARSE_ERROR); test("/** @return {n} */ function f(a) { return a; }", RhinoErrorReporter.TYPE_PARSE_ERROR); } public void testTypeCheckOverride1() { args.add("--warning_level=VERBOSE"); args.add("--jscomp_off=checkTypes"); testSame("var x = x || {}; x.f = function() {}; x.f(3);"); } public void testTypeCheckOverride2() { args.add("--warning_level=DEFAULT"); testSame("var x = x || {}; x.f = function() {}; x.f(3);"); args.add("--jscomp_warning=checkTypes"); test("var x = x || {}; x.f = function() {}; x.f(3);", TypeCheck.WRONG_ARGUMENT_COUNT); } public void testCheckSymbolsOffForDefault() { args.add("--warning_level=DEFAULT"); test("x = 3; var y; var y;", "x=3; var y;"); } public void testCheckSymbolsOnForVerbose() { args.add("--warning_level=VERBOSE"); test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR); test("var y; var y;", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); } public void testCheckSymbolsOverrideForVerbose() { args.add("--warning_level=VERBOSE"); args.add("--jscomp_off=undefinedVars"); testSame("x = 3;"); } public void testCheckSymbolsOverrideForQuiet() { args.add("--warning_level=QUIET"); args.add("--jscomp_error=undefinedVars"); test("x = 3;", VarCheck.UNDEFINED_VAR_ERROR); } public void testCheckUndefinedProperties1() { args.add("--warning_level=VERBOSE"); args.add("--jscomp_error=missingProperties"); test("var x = {}; var y = x.bar;", TypeCheck.INEXISTENT_PROPERTY); } public void testCheckUndefinedProperties2() { args.add("--warning_level=VERBOSE"); args.add("--jscomp_off=missingProperties"); test("var x = {}; var y = x.bar;", CheckGlobalNames.UNDEFINED_NAME_WARNING); } public void testCheckUndefinedProperties3() { args.add("--warning_level=VERBOSE"); test("function f() {var x = {}; var y = x.bar;}", TypeCheck.INEXISTENT_PROPERTY); } public void testDuplicateParams() { test("function f(a, a) {}", RhinoErrorReporter.DUPLICATE_PARAM); assertTrue(lastCompiler.hasHaltingErrors()); } public void testDefineFlag() { args.add("--define=FOO"); args.add("--define=\"BAR=5\""); args.add("--D"); args.add("CCC"); args.add("-D"); args.add("DDD"); test("/** @define {boolean} */ var FOO = false;" + "/** @define {number} */ var BAR = 3;" + "/** @define {boolean} */ var CCC = false;" + "/** @define {boolean} */ var DDD = false;", "var FOO = !0, BAR = 5, CCC = !0, DDD = !0;"); } public void testDefineFlag2() { args.add("--define=FOO='x\"'"); test("/** @define {string} */ var FOO = \"a\";", "var FOO = \"x\\\"\";"); } public void testDefineFlag3() { args.add("--define=FOO=\"x'\""); test("/** @define {string} */ var FOO = \"a\";", "var FOO = \"x'\";"); } public void testScriptStrictModeNoWarning() { test("'use strict';", ""); test("'no use strict';", CheckSideEffects.USELESS_CODE_ERROR); } public void testFunctionStrictModeNoWarning() { test("function f() {'use strict';}", "function f() {}"); test("function f() {'no use strict';}", CheckSideEffects.USELESS_CODE_ERROR); } public void testQuietMode() { args.add("--warning_level=DEFAULT"); test("/** @const \n * @const */ var x;", RhinoErrorReporter.PARSE_ERROR); args.add("--warning_level=QUIET"); testSame("/** @const \n * @const */ var x;"); } public void testProcessClosurePrimitives() { test("var goog = {}; goog.provide('goog.dom');", "var goog = {dom:{}};"); args.add("--process_closure_primitives=false"); testSame("var goog = {}; goog.provide('goog.dom');"); } public void testGetMsgWiring() throws Exception { test("var goog = {}; goog.getMsg = function(x) { return x; };" + "/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');", "var goog={getMsg:function(a){return a}}, " + "MSG_FOO=goog.getMsg('foo');"); args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test("var goog = {}; goog.getMsg = function(x) { return x; };" + "/** @desc A real foo. */ var MSG_FOO = goog.getMsg('foo');" + "window['foo'] = MSG_FOO;", "window.foo = 'foo';"); } public void testCssNameWiring() throws Exception { test("var goog = {}; goog.getCssName = function() {};" + "goog.setCssNameMapping = function() {};" + "goog.setCssNameMapping({'goog': 'a', 'button': 'b'});" + "var a = goog.getCssName('goog-button');" + "var b = goog.getCssName('css-button');" + "var c = goog.getCssName('goog-menu');" + "var d = goog.getCssName('css-menu');", "var goog = { getCssName: function() {}," + " setCssNameMapping: function() {} }," + " a = 'a-b'," + " b = 'css-b'," + " c = 'a-menu'," + " d = 'css-menu';"); } ////////////////////////////////////////////////////////////////////////////// // Integration tests public void testIssue70a() { test("function foo({}) {}", RhinoErrorReporter.PARSE_ERROR); } public void testIssue70b() { test("function foo([]) {}", RhinoErrorReporter.PARSE_ERROR); } public void testIssue81() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); useStringComparison = true; test("eval('1'); var x = eval; x('2');", "eval(\"1\");(0,eval)(\"2\");"); } public void testIssue115() { args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); args.add("--jscomp_off=es5Strict"); args.add("--warning_level=VERBOSE"); test("function f() { " + " var arguments = Array.prototype.slice.call(arguments, 0);" + " return arguments[0]; " + "}", "function f() { " + " arguments = Array.prototype.slice.call(arguments, 0);" + " return arguments[0]; " + "}"); } public void testIssue297() { args.add("--compilation_level=SIMPLE_OPTIMIZATIONS"); test("function f(p) {" + " var x;" + " return ((x=p.id) && (x=parseInt(x.substr(1))) && x>0);" + "}", "function f(b) {" + " var a;" + " return ((a=b.id) && (a=parseInt(a.substr(1))) && 0 node1 [weight=1];\n" + " node1 -> RETURN [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> RETURN [label=\"SYN_BLOCK\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + " node0 -> node1 [label=\"UNCOND\", " + "fontcolor=\"red\", weight=0.01, color=\"red\"];\n" + "}\n\n", new String(outReader.toByteArray())); } public void testSyntheticExterns() { externs = ImmutableList.of( SourceFile.fromCode("externs", "myVar.property;")); test("var theirVar = {}; var myVar = {}; var yourVar = {};", VarCheck.UNDEFINED_EXTERN_VAR_ERROR); args.add("--jscomp_off=externsValidation"); args.add("--warning_level=VERBOSE"); test("var theirVar = {}; var myVar = {}; var yourVar = {};", "var theirVar={},myVar={},yourVar={};"); args.add("--jscomp_off=externsValidation"); args.add("--warning_level=VERBOSE"); test("var theirVar = {}; var myVar = {}; var myVar = {};", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); } public void testGoogAssertStripping() { args.add("--compilation_level=ADVANCED_OPTIMIZATIONS"); test("goog.asserts.assert(false)", ""); args.add("--debug"); test("goog.asserts.assert(false)", "goog.$asserts$.$assert$(!1)"); } public void testMissingReturnCheckOnWithVerbose() { args.add("--warning_level=VERBOSE"); test("/** @return {number} */ function f() {f()} f();", CheckMissingReturn.MISSING_RETURN_STATEMENT); } public void testGenerateExports() { args.add("--generate_exports=true"); test("/** @export */ foo.prototype.x = function() {};", "foo.prototype.x=function(){};"+ "goog.exportSymbol(\"foo.prototype.x\",foo.prototype.x);"); } public void testDepreciationWithVerbose() { args.add("--warning_level=VERBOSE"); test("/** @deprecated */ function f() {}; f()", CheckAccessControls.DEPRECATED_NAME); } public void testTwoParseErrors() { // If parse errors are reported in different files, make // sure all of them are reported. Compiler compiler = compile(new String[] { "var a b;", "var b c;" }); assertEquals(2, compiler.getErrors().length); } public void testES3ByDefault() { test("var x = f.function", RhinoErrorReporter.PARSE_ERROR); } public void testES5ChecksByDefault() { testSame("var x = 3; delete x;"); } public void testES5ChecksInVerbose() { args.add("--warning_level=VERBOSE"); test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE); } public void testES5() { args.add("--language_in=ECMASCRIPT5"); test("var x = f.function", "var x = f.function"); test("var let", "var let"); } public void testES5Strict() { args.add("--language_in=ECMASCRIPT5_STRICT"); test("var x = f.function", "'use strict';var x = f.function"); test("var let", RhinoErrorReporter.PARSE_ERROR); test("function f(x) { delete x; }", StrictModeCheck.DELETE_VARIABLE); } public void testES5StrictUseStrict() { args.add("--language_in=ECMASCRIPT5_STRICT"); Compiler compiler = compile(new String[] {"var x = f.function"}); String outputSource = compiler.toSource(); assertEquals("'use strict'", outputSource.substring(0, 12)); } public void testES5StrictUseStrictMultipleInputs() { args.add("--language_in=ECMASCRIPT5_STRICT"); Compiler compiler = compile(new String[] {"var x = f.function", "var y = f.function", "var z = f.function"}); String outputSource = compiler.toSource(); assertEquals("'use strict'", outputSource.substring(0, 12)); assertEquals(outputSource.substring(13).indexOf("'use strict'"), -1); } public void testWithKeywordDefault() { test("var x = {}; with (x) {}", ControlStructureCheck.USE_OF_WITH); } public void testWithKeywordWithEs5ChecksOff() { args.add("--jscomp_off=es5Strict"); testSame("var x = {}; with (x) {}"); } public void testNoSrCFilesWithManifest() throws IOException { args.add("--use_only_custom_externs=true"); args.add("--output_manifest=test.MF"); CommandLineRunner runner = createCommandLineRunner(new String[0]); String expectedMessage = ""; try { runner.doRun(); } catch (FlagUsageException e) { expectedMessage = e.getMessage(); } assertEquals(expectedMessage, "Bad --js flag. " + "Manifest files cannot be generated when the input is from stdin."); } public void testTransformAMD() { args.add("--transform_amd_modules"); test("define({test: 1})", "exports = {test: 1}"); } public void testProcessCJS() { useStringComparison = true; args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); setFilename(0, "foo/bar.js"); String expected = "var module$foo$bar={test:1};"; test("exports.test = 1", expected); assertEquals(expected + "\n", outReader.toString()); } public void testProcessCJSWithModuleOutput() { useStringComparison = true; args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); args.add("--module=auto"); setFilename(0, "foo/bar.js"); test("exports.test = 1", "var module$foo$bar={test:1};"); // With modules=auto no direct output is created. assertEquals("", outReader.toString()); } public void testFormattingSingleQuote() { testSame("var x = '';"); assertEquals("var x=\"\";", lastCompiler.toSource()); args.add("--formatting=SINGLE_QUOTES"); testSame("var x = '';"); assertEquals("var x='';", lastCompiler.toSource()); } public void testTransformAMDAndProcessCJS() { useStringComparison = true; args.add("--transform_amd_modules"); args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); setFilename(0, "foo/bar.js"); test("define({foo: 1})", "var module$foo$bar={},module$foo$bar={foo:1};"); } public void testModuleJSON() { useStringComparison = true; args.add("--transform_amd_modules"); args.add("--process_common_js_modules"); args.add("--common_js_entry_module=foo/bar"); args.add("--output_module_dependencies=test.json"); setFilename(0, "foo/bar.js"); test("define({foo: 1})", "var module$foo$bar={},module$foo$bar={foo:1};"); } public void testOutputSameAsInput() { args.add("--js_output_file=" + getFilename(0)); test("", AbstractCommandLineRunner.OUTPUT_SAME_AS_INPUT_ERROR); } /* Helper functions */ private void testSame(String original) { testSame(new String[] { original }); } private void testSame(String[] original) { test(original, original); } private void test(String original, String compiled) { test(new String[] { original }, new String[] { compiled }); } /** * Asserts that when compiling with the given compiler options, * {@code original} is transformed into {@code compiled}. */ private void test(String[] original, String[] compiled) { test(original, compiled, null); } /** * Asserts that when compiling with the given compiler options, * {@code original} is transformed into {@code compiled}. * If {@code warning} is non-null, we will also check if the given * warning type was emitted. */ private void test(String[] original, String[] compiled, DiagnosticType warning) { Compiler compiler = compile(original); if (warning == null) { assertEquals("Expected no warnings or errors\n" + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), 0, compiler.getErrors().length + compiler.getWarnings().length); } else { assertEquals(1, compiler.getWarnings().length); assertEquals(warning, compiler.getWarnings()[0].getType()); } Node root = compiler.getRoot().getLastChild(); if (useStringComparison) { assertEquals(Joiner.on("").join(compiled), compiler.toSource()); } else { Node expectedRoot = parse(compiled); String explanation = expectedRoot.checkTreeEquals(root); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(root) + "\n" + explanation, explanation); } } /** * Asserts that when compiling, there is an error or warning. */ private void test(String original, DiagnosticType warning) { test(new String[] { original }, warning); } private void test(String original, String expected, DiagnosticType warning) { test(new String[] { original }, new String[] { expected }, warning); } /** * Asserts that when compiling, there is an error or warning. */ private void test(String[] original, DiagnosticType warning) { Compiler compiler = compile(original); assertEquals("Expected exactly one warning or error " + "Errors: \n" + Joiner.on("\n").join(compiler.getErrors()) + "Warnings: \n" + Joiner.on("\n").join(compiler.getWarnings()), 1, compiler.getErrors().length + compiler.getWarnings().length); assertTrue(exitCodes.size() > 0); int lastExitCode = exitCodes.get(exitCodes.size() - 1); if (compiler.getErrors().length > 0) { assertEquals(1, compiler.getErrors().length); assertEquals(warning, compiler.getErrors()[0].getType()); assertEquals(1, lastExitCode); } else { assertEquals(1, compiler.getWarnings().length); assertEquals(warning, compiler.getWarnings()[0].getType()); assertEquals(0, lastExitCode); } } private CommandLineRunner createCommandLineRunner(String[] original) { for (int i = 0; i < original.length; i++) { args.add("--js"); args.add("/path/to/input" + i + ".js"); if (useModules == ModulePattern.CHAIN) { args.add("--module"); args.add("m" + i + ":1" + (i > 0 ? (":m" + (i - 1)) : "")); } else if (useModules == ModulePattern.STAR) { args.add("--module"); args.add("m" + i + ":1" + (i > 0 ? ":m0" : "")); } } if (lastArg != null) { args.add(lastArg); } String[] argStrings = args.toArray(new String[] {}); return new CommandLineRunner( argStrings, new PrintStream(outReader), new PrintStream(errReader)); } private Compiler compile(String[] original) { CommandLineRunner runner = createCommandLineRunner(original); assertTrue(new String(errReader.toByteArray()), runner.shouldRunCompiler()); Supplier> inputsSupplier = null; Supplier> modulesSupplier = null; if (useModules == ModulePattern.NONE) { List inputs = Lists.newArrayList(); for (int i = 0; i < original.length; i++) { inputs.add(SourceFile.fromCode(getFilename(i), original[i])); } inputsSupplier = Suppliers.ofInstance(inputs); } else if (useModules == ModulePattern.STAR) { modulesSupplier = Suppliers.>ofInstance( Lists.newArrayList( CompilerTestCase.createModuleStar(original))); } else if (useModules == ModulePattern.CHAIN) { modulesSupplier = Suppliers.>ofInstance( Lists.newArrayList( CompilerTestCase.createModuleChain(original))); } else { throw new IllegalArgumentException("Unknown module type: " + useModules); } runner.enableTestMode( Suppliers.>ofInstance(externs), inputsSupplier, modulesSupplier, new Function() { @Override public Boolean apply(Integer code) { return exitCodes.add(code); } }); runner.run(); lastCompiler = runner.getCompiler(); lastCommandLineRunner = runner; return lastCompiler; } private Node parse(String[] original) { String[] argStrings = args.toArray(new String[] {}); CommandLineRunner runner = new CommandLineRunner(argStrings); Compiler compiler = runner.createCompiler(); List inputs = Lists.newArrayList(); for (int i = 0; i < original.length; i++) { inputs.add(SourceFile.fromCode(getFilename(i), original[i])); } CompilerOptions options = new CompilerOptions(); // ECMASCRIPT5 is the most forgiving. options.setLanguageIn(LanguageMode.ECMASCRIPT5); compiler.init(externs, inputs, options); Node all = compiler.parseInputs(); Preconditions.checkState(compiler.getErrorCount() == 0); Preconditions.checkNotNull(all); Node n = all.getLastChild(); return n; } private void setFilename(int i, String filename) { this.filenames.put(i, filename); } private String getFilename(int i) { if (filenames.isEmpty()) { return "input" + i; } return filenames.get(i); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/CheckMissingGetCssNameTest.java0000644000175000017500000000447712115204405031024 0ustar apoapo/* * 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; /** * @author mkretzschmar@google.com (Martin Kretzschmar) */ public class CheckMissingGetCssNameTest extends CompilerTestCase { @Override protected CompilerPass getProcessor(final Compiler compiler) { return new CombinedCompilerPass( compiler, new CheckMissingGetCssName(compiler, CheckLevel.ERROR, "goog-[a-z-]*")); } public void testMissingGetCssName() { testMissing("var s = 'goog-inline-block'"); testMissing("var s = 'CSS_FOO goog-menu'"); testMissing("alert('goog-inline-block ' + goog.getClassName('CSS_FOO'))"); testMissing("html = '
Hello
'"); } public void testRecognizeGetCssName() { testNotMissing("var s = goog.getCssName('goog-inline-block')"); } public void testIgnoreGetUniqueIdArguments() { testNotMissing("var s = goog.events.getUniqueId('goog-some-event')"); testNotMissing("var s = joe.random.getUniqueId('joe-is-a-goob')"); } public void testIgnoreAssignmentsToIdConstant() { testNotMissing("SOME_ID = 'goog-some-id'"); testNotMissing("SOME_PRIVATE_ID_ = 'goog-some-id'"); testNotMissing("var SOME_ID_ = 'goog-some-id'"); } public void testNotMissingGetCssName() { testNotMissing("s = 'not-a-css-name'"); testNotMissing("s = 'notagoog-css-name'"); } public void testDontCrashIfTheresNoQualifiedName() { testMissing("things[2].DONT_CARE_ABOUT_THIS_KIND_OF_ID = " + "'goog-inline-block'"); testMissing("objects[3].doSomething('goog-inline-block')"); } private void testMissing(String js) { test(js, js, CheckMissingGetCssName.MISSING_GETCSSNAME); } private void testNotMissing(String js) { test(js, js); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/IntegrationTest.java0000644000175000017500000023736612115204405027033 0ustar apoapo/* * 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.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.CompilerOptions.TracerMode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.List; import java.util.regex.Pattern; /** * Tests for {@link PassFactory}. * * @author nicksantos@google.com (Nick Santos) */ public class IntegrationTest extends IntegrationTestCase { private static final String CLOSURE_BOILERPLATE = "/** @define {boolean} */ var COMPILED = false; var goog = {};" + "goog.exportSymbol = function() {};"; private static final String CLOSURE_COMPILED = "var COMPILED = true; var goog$exportSymbol = function() {};"; public void testConstructorCycle() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; test(options, "/** @return {function()} */ var AsyncTestCase = function() {};\n" + "/**\n" + " * @constructor\n" + " */ Foo = /** @type {function(new:Foo)} */ (AyncTestCase());", RhinoErrorReporter.PARSE_ERROR); } public void testBug1949424() { CompilerOptions options = createCompilerOptions(); options.collapseProperties = true; options.closurePass = true; test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO'); FOO.bar = 3;", CLOSURE_COMPILED + "var FOO$bar = 3;"); } public void testBug1949424_v2() { CompilerOptions options = createCompilerOptions(); options.collapseProperties = true; options.closurePass = true; test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO.BAR'); FOO.BAR = 3;", CLOSURE_COMPILED + "var FOO$BAR = 3;"); } public void testBug1956277() { CompilerOptions options = createCompilerOptions(); options.collapseProperties = true; options.inlineVariables = true; test(options, "var CONST = {}; CONST.bar = null;" + "function f(url) { CONST.bar = url; }", "var CONST$bar = null; function f(url) { CONST$bar = url; }"); } public void testBug1962380() { CompilerOptions options = createCompilerOptions(); options.collapseProperties = true; options.inlineVariables = true; options.generateExports = true; test(options, CLOSURE_BOILERPLATE + "/** @export */ goog.CONSTANT = 1;" + "var x = goog.CONSTANT;", "(function() {})('goog.CONSTANT', 1);" + "var x = 1;"); } public void testBug2410122() { CompilerOptions options = createCompilerOptions(); options.generateExports = true; options.closurePass = true; test(options, "var goog = {};" + "function F() {}" + "/** @export */ function G() { goog.base(this); } " + "goog.inherits(G, F);", "var goog = {};" + "function F() {}" + "function G() { F.call(this); } " + "goog.inherits(G, F); goog.exportSymbol('G', G);"); } public void testIssue90() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; options.inlineVariables = true; options.removeDeadCode = true; test(options, "var x; x && alert(1);", ""); } public void testClosurePassOff() { CompilerOptions options = createCompilerOptions(); options.closurePass = false; testSame( options, "var goog = {}; goog.require = function(x) {}; goog.require('foo');"); testSame( options, "var goog = {}; goog.getCssName = function(x) {};" + "goog.getCssName('foo');"); } public void testClosurePassOn() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; test( options, "var goog = {}; goog.require = function(x) {}; goog.require('foo');", ProcessClosurePrimitives.MISSING_PROVIDE_ERROR); test( options, "/** @define {boolean} */ var COMPILED = false;" + "var goog = {}; goog.getCssName = function(x) {};" + "goog.getCssName('foo');", "var COMPILED = true;" + "var goog = {}; goog.getCssName = function(x) {};" + "'foo';"); } public void testCssNameCheck() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.checkMissingGetCssNameLevel = CheckLevel.ERROR; options.checkMissingGetCssNameBlacklist = "foo"; test(options, "var x = 'foo';", CheckMissingGetCssName.MISSING_GETCSSNAME); } public void testBug2592659() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.checkTypes = true; options.checkMissingGetCssNameLevel = CheckLevel.WARNING; options.checkMissingGetCssNameBlacklist = "foo"; test(options, "var goog = {};\n" + "/**\n" + " * @param {string} className\n" + " * @param {string=} opt_modifier\n" + " * @return {string}\n" + "*/\n" + "goog.getCssName = function(className, opt_modifier) {}\n" + "var x = goog.getCssName(123, 'a');", TypeValidator.TYPE_MISMATCH_WARNING); } public void testTypedefBeforeOwner1() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; test(options, "goog.provide('foo.Bar.Type');\n" + "goog.provide('foo.Bar');\n" + "/** @typedef {number} */ foo.Bar.Type;\n" + "foo.Bar = function() {};", "var foo = {}; foo.Bar.Type; foo.Bar = function() {};"); } public void testTypedefBeforeOwner2() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.collapseProperties = true; test(options, "goog.provide('foo.Bar.Type');\n" + "goog.provide('foo.Bar');\n" + "/** @typedef {number} */ foo.Bar.Type;\n" + "foo.Bar = function() {};", "var foo$Bar$Type; var foo$Bar = function() {};"); } public void testExportedNames() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.variableRenaming = VariableRenamingPolicy.ALL; test(options, "/** @define {boolean} */ var COMPILED = false;" + "var goog = {}; goog.exportSymbol('b', goog);", "var a = true; var c = {}; c.exportSymbol('b', c);"); test(options, "/** @define {boolean} */ var COMPILED = false;" + "var goog = {}; goog.exportSymbol('a', goog);", "var b = true; var c = {}; c.exportSymbol('a', c);"); } public void testCheckGlobalThisOn() { CompilerOptions options = createCompilerOptions(); options.checkSuspiciousCode = true; options.checkGlobalThisLevel = CheckLevel.ERROR; test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testSusiciousCodeOff() { CompilerOptions options = createCompilerOptions(); options.checkSuspiciousCode = false; options.checkGlobalThisLevel = CheckLevel.ERROR; test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS); } public void testCheckGlobalThisOff() { CompilerOptions options = createCompilerOptions(); options.checkSuspiciousCode = true; options.checkGlobalThisLevel = CheckLevel.OFF; testSame(options, "function f() { this.y = 3; }"); } public void testCheckRequiresAndCheckProvidesOff() { testSame(createCompilerOptions(), new String[] { "/** @constructor */ function Foo() {}", "new Foo();" }); } public void testCheckRequiresOn() { CompilerOptions options = createCompilerOptions(); options.checkRequires = CheckLevel.ERROR; test(options, new String[] { "/** @constructor */ function Foo() {}", "new Foo();" }, CheckRequiresForConstructors.MISSING_REQUIRE_WARNING); } public void testCheckProvidesOn() { CompilerOptions options = createCompilerOptions(); options.checkProvides = CheckLevel.ERROR; test(options, new String[] { "/** @constructor */ function Foo() {}", "new Foo();" }, CheckProvides.MISSING_PROVIDE_WARNING); } public void testGenerateExportsOff() { testSame(createCompilerOptions(), "/** @export */ function f() {}"); } public void testGenerateExportsOn() { CompilerOptions options = createCompilerOptions(); options.generateExports = true; test(options, "/** @export */ function f() {}", "/** @export */ function f() {} goog.exportSymbol('f', f);"); } public void testAngularPassOff() { testSame(createCompilerOptions(), "/** @ngInject */ function f() {} " + "/** @ngInject */ function g(a){} " + "/** @ngInject */ var b = function f(a) {} "); } public void testAngularPassOn() { CompilerOptions options = createCompilerOptions(); options.angularPass = true; test(options, "/** @ngInject */ function f() {} " + "/** @ngInject */ function g(a){} " + "/** @ngInject */ var b = function f(a, b, c) {} ", "function f() {} " + "function g(a) {} g['$inject']=['a'];" + "var b = function f(a, b, c) {}; b['$inject']=['a', 'b', 'c']"); } public void testExportTestFunctionsOff() { testSame(createCompilerOptions(), "function testFoo() {}"); } public void testExportTestFunctionsOn() { CompilerOptions options = createCompilerOptions(); options.exportTestFunctions = true; test(options, "function testFoo() {}", "/** @export */ function testFoo() {}" + "goog.exportSymbol('testFoo', testFoo);"); } public void testExpose() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); test(options, "var x = {eeny: 1, /** @expose */ meeny: 2};" + "/** @constructor */ var Foo = function() {};" + "/** @expose */ Foo.prototype.miny = 3;" + "Foo.prototype.moe = 4;" + "/** @expose */ Foo.prototype.tiger;" + "function moe(a, b) { return a.meeny + b.miny + a.tiger; }" + "window['x'] = x;" + "window['Foo'] = Foo;" + "window['moe'] = moe;", "function a(){}" + "a.prototype.miny=3;" + "window.x={a:1,meeny:2};" + "window.Foo=a;" + "window.moe=function(b,c){" + " return b.meeny+c.miny+b.tiger" + "}"); } public void testCheckSymbolsOff() { CompilerOptions options = createCompilerOptions(); testSame(options, "x = 3;"); } public void testCheckSymbolsOn() { CompilerOptions options = createCompilerOptions(); options.checkSymbols = true; test(options, "x = 3;", VarCheck.UNDEFINED_VAR_ERROR); } public void testCheckReferencesOff() { CompilerOptions options = createCompilerOptions(); testSame(options, "x = 3; var x = 5;"); } public void testCheckReferencesOn() { CompilerOptions options = createCompilerOptions(); options.aggressiveVarCheck = CheckLevel.ERROR; test(options, "x = 3; var x = 5;", VariableReferenceCheck.UNDECLARED_REFERENCE); } public void testInferTypes() { CompilerOptions options = createCompilerOptions(); options.inferTypes = true; options.checkTypes = false; options.closurePass = true; test(options, CLOSURE_BOILERPLATE + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", TypeCheck.ENUM_NOT_CONSTANT); assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); // This does not generate a warning. test(options, "/** @type {number} */ var n = window.name;", "var n = window.name;"); assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0); } public void testTypeCheckAndInference() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; test(options, "/** @type {number} */ var n = window.name;", TypeValidator.TYPE_MISMATCH_WARNING); assertTrue(lastCompiler.getErrorManager().getTypedPercent() > 0); } public void testTypeNameParser() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; test(options, "/** @type {n} */ var n = window.name;", RhinoErrorReporter.TYPE_PARSE_ERROR); } // This tests that the TypedScopeCreator is memoized so that it only creates a // Scope object once for each scope. If, when type inference requests a scope, // it creates a new one, then multiple JSType objects end up getting created // for the same local type, and ambiguate will rename the last statement to // o.a(o.a, o.a), which is bad. public void testMemoizedTypedScopeCreator() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; options.ambiguateProperties = true; options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; test(options, "function someTest() {\n" + " /** @constructor */\n" + " function Foo() { this.instProp = 3; }\n" + " Foo.prototype.protoProp = function(a, b) {};\n" + " /** @constructor\n @extends Foo */\n" + " function Bar() {}\n" + " goog.inherits(Bar, Foo);\n" + " var o = new Bar();\n" + " o.protoProp(o.protoProp, o.instProp);\n" + "}", "function someTest() {\n" + " function Foo() { this.b = 3; }\n" + " function Bar() {}\n" + " Foo.prototype.a = function(a, b) {};\n" + " goog.c(Bar, Foo);\n" + " var o = new Bar();\n" + " o.a(o.a, o.b);\n" + "}"); } public void testCheckTypes() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; test(options, "var x = x || {}; x.f = function() {}; x.f(3);", TypeCheck.WRONG_ARGUMENT_COUNT); } public void testReplaceCssNames() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.gatherCssNames = true; test(options, "/** @define {boolean} */\n" + "var COMPILED = false;\n" + "goog.setCssNameMapping({'foo':'bar'});\n" + "function getCss() {\n" + " return goog.getCssName('foo');\n" + "}", "var COMPILED = true;\n" + "function getCss() {\n" + " return \"bar\";" + "}"); assertEquals( ImmutableMap.of("foo", new Integer(1)), lastCompiler.getPassConfig().getIntermediateState().cssNames); } public void testRemoveClosureAsserts() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; testSame(options, "var goog = {};" + "goog.asserts.assert(goog);"); options.removeClosureAsserts = true; test(options, "var goog = {};" + "goog.asserts.assert(goog);", "var goog = {};"); } public void testDeprecation() { String code = "/** @deprecated */ function f() { } function g() { f(); }"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.setWarningLevel(DiagnosticGroups.DEPRECATED, CheckLevel.ERROR); testSame(options, code); options.checkTypes = true; test(options, code, CheckAccessControls.DEPRECATED_NAME); } public void testVisibility() { String[] code = { "/** @private */ function f() { }", "function g() { f(); }" }; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.setWarningLevel(DiagnosticGroups.VISIBILITY, CheckLevel.ERROR); testSame(options, code); options.checkTypes = true; test(options, code, CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS); } public void testUnreachableCode() { String code = "function f() { return \n 3; }"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.checkUnreachableCode = CheckLevel.ERROR; test(options, code, CheckUnreachableCode.UNREACHABLE_CODE); } public void testMissingReturn() { String code = "/** @return {number} */ function f() { if (f) { return 3; } }"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.checkMissingReturn = CheckLevel.ERROR; testSame(options, code); options.checkTypes = true; test(options, code, CheckMissingReturn.MISSING_RETURN_STATEMENT); } public void testIdGenerators() { String code = "function f() {} f('id');"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.idGenerators = Sets.newHashSet("f"); test(options, code, "function f() {} 'a';"); } public void testOptimizeArgumentsArray() { String code = "function f() { return arguments[0]; }"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.optimizeArgumentsArray = true; String argName = "JSCompiler_OptimizeArgumentsArray_p0"; test(options, code, "function f(" + argName + ") { return " + argName + "; }"); } public void testOptimizeParameters() { String code = "function f(a) { return a; } f(true);"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.optimizeParameters = true; test(options, code, "function f() { var a = true; return a;} f();"); } public void testOptimizeReturns() { String code = "function f(a) { return a; } f(true);"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.optimizeReturns = true; test(options, code, "function f(a) {return;} f(true);"); } public void testRemoveAbstractMethods() { String code = CLOSURE_BOILERPLATE + "var x = {}; x.foo = goog.abstractMethod; x.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.closurePass = true; options.collapseProperties = true; test(options, code, CLOSURE_COMPILED + " var x$bar = 3;"); } public void testCollapseProperties1() { String code = "var x = {}; x.FOO = 5; x.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.collapseProperties = true; test(options, code, "var x$FOO = 5; var x$bar = 3;"); } public void testCollapseProperties2() { String code = "var x = {}; x.FOO = 5; x.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.collapseProperties = true; options.collapseObjectLiterals = true; test(options, code, "var x$FOO = 5; var x$bar = 3;"); } public void testCollapseObjectLiteral1() { // Verify collapseObjectLiterals does nothing in global scope String code = "var x = {}; x.FOO = 5; x.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.collapseObjectLiterals = true; testSame(options, code); } public void testCollapseObjectLiteral2() { String code = "function f() {var x = {}; x.FOO = 5; x.bar = 3;}"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.collapseObjectLiterals = true; test(options, code, "function f(){" + "var JSCompiler_object_inline_FOO_0;" + "var JSCompiler_object_inline_bar_1;" + "JSCompiler_object_inline_FOO_0=5;" + "JSCompiler_object_inline_bar_1=3}"); } public void testTightenTypesWithoutTypeCheck() { CompilerOptions options = createCompilerOptions(); options.tightenTypes = true; test(options, "", DefaultPassConfig.TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); } public void testDisambiguateProperties() { String code = "/** @constructor */ function Foo(){} Foo.prototype.bar = 3;" + "/** @constructor */ function Baz(){} Baz.prototype.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.disambiguateProperties = true; options.checkTypes = true; test(options, code, "function Foo(){} Foo.prototype.Foo_prototype$bar = 3;" + "function Baz(){} Baz.prototype.Baz_prototype$bar = 3;"); } public void testMarkPureCalls() { String testCode = "function foo() {} foo();"; CompilerOptions options = createCompilerOptions(); options.removeDeadCode = true; testSame(options, testCode); options.computeFunctionSideEffects = true; test(options, testCode, "function foo() {}"); } public void testMarkNoSideEffects() { String testCode = "noSideEffects();"; CompilerOptions options = createCompilerOptions(); options.removeDeadCode = true; testSame(options, testCode); options.markNoSideEffectCalls = true; test(options, testCode, ""); } public void testChainedCalls() { CompilerOptions options = createCompilerOptions(); options.chainCalls = true; test( options, "/** @constructor */ function Foo() {} " + "Foo.prototype.bar = function() { return this; }; " + "var f = new Foo();" + "f.bar(); " + "f.bar(); ", "function Foo() {} " + "Foo.prototype.bar = function() { return this; }; " + "var f = new Foo();" + "f.bar().bar();"); } public void testExtraAnnotationNames() { CompilerOptions options = createCompilerOptions(); options.setExtraAnnotationNames(Sets.newHashSet("TagA", "TagB")); test( options, "/** @TagA */ var f = new Foo(); /** @TagB */ f.bar();", "var f = new Foo(); f.bar();"); } public void testDevirtualizePrototypeMethods() { CompilerOptions options = createCompilerOptions(); options.devirtualizePrototypeMethods = true; test( options, "/** @constructor */ var Foo = function() {}; " + "Foo.prototype.bar = function() {};" + "(new Foo()).bar();", "var Foo = function() {};" + "var JSCompiler_StaticMethods_bar = " + " function(JSCompiler_StaticMethods_bar$self) {};" + "JSCompiler_StaticMethods_bar(new Foo());"); } public void testCheckConsts() { CompilerOptions options = createCompilerOptions(); options.inlineConstantVars = true; test(options, "var FOO = true; FOO = false", ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } public void testAllChecksOn() { CompilerOptions options = createCompilerOptions(); options.checkSuspiciousCode = true; options.checkControlStructures = true; options.checkRequires = CheckLevel.ERROR; options.checkProvides = CheckLevel.ERROR; options.generateExports = true; options.exportTestFunctions = true; options.closurePass = true; options.checkMissingGetCssNameLevel = CheckLevel.ERROR; options.checkMissingGetCssNameBlacklist = "goog"; options.syntheticBlockStartMarker = "synStart"; options.syntheticBlockEndMarker = "synEnd"; options.checkSymbols = true; options.aggressiveVarCheck = CheckLevel.ERROR; options.processObjectPropertyString = true; options.collapseProperties = true; test(options, CLOSURE_BOILERPLATE, CLOSURE_COMPILED); } public void testTypeCheckingWithSyntheticBlocks() { CompilerOptions options = createCompilerOptions(); options.syntheticBlockStartMarker = "synStart"; options.syntheticBlockEndMarker = "synEnd"; options.checkTypes = true; // We used to have a bug where the CFG drew an // edge straight from synStart to f(progress). // If that happens, then progress will get type {number|undefined}. testSame( options, "/** @param {number} x */ function f(x) {}" + "function g() {" + " synStart('foo');" + " var progress = 1;" + " f(progress);" + " synEnd('foo');" + "}"); } public void testCompilerDoesNotBlowUpIfUndefinedSymbols() { CompilerOptions options = createCompilerOptions(); options.checkSymbols = true; // Disable the undefined variable check. options.setWarningLevel( DiagnosticGroup.forType(VarCheck.UNDEFINED_VAR_ERROR), CheckLevel.OFF); // The compiler used to throw an IllegalStateException on this. testSame(options, "var x = {foo: y};"); } // Make sure that if we change variables which are constant to have // $$constant appended to their names, we remove that tag before // we finish. public void testConstantTagsMustAlwaysBeRemoved() { CompilerOptions options = createCompilerOptions(); options.variableRenaming = VariableRenamingPolicy.LOCAL; String originalText = "var G_GEO_UNKNOWN_ADDRESS=1;\n" + "function foo() {" + " var localVar = 2;\n" + " if (G_GEO_UNKNOWN_ADDRESS == localVar) {\n" + " alert(\"A\"); }}"; String expectedText = "var G_GEO_UNKNOWN_ADDRESS=1;" + "function foo(){var a=2;if(G_GEO_UNKNOWN_ADDRESS==a){alert(\"A\")}}"; test(options, originalText, expectedText); } public void testClosurePassPreservesJsDoc() { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; options.closurePass = true; test(options, CLOSURE_BOILERPLATE + "goog.provide('Foo'); /** @constructor */ Foo = function() {};" + "var x = new Foo();", "var COMPILED=true;var goog={};goog.exportSymbol=function(){};" + "var Foo=function(){};var x=new Foo"); test(options, CLOSURE_BOILERPLATE + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};", TypeCheck.ENUM_NOT_CONSTANT); } public void testProvidedNamespaceIsConst() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.inlineConstantVars = true; options.collapseProperties = true; test(options, "var goog = {}; goog.provide('foo'); " + "function f() { foo = {};}", "var foo = {}; function f() { foo = {}; }", ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } public void testProvidedNamespaceIsConst2() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.inlineConstantVars = true; options.collapseProperties = true; test(options, "var goog = {}; goog.provide('foo.bar'); " + "function f() { foo.bar = {};}", "var foo$bar = {};" + "function f() { foo$bar = {}; }", ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } public void testProvidedNamespaceIsConst3() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.inlineConstantVars = true; options.collapseProperties = true; test(options, "var goog = {}; " + "goog.provide('foo.bar'); goog.provide('foo.bar.baz'); " + "/** @constructor */ foo.bar = function() {};" + "/** @constructor */ foo.bar.baz = function() {};", "var foo$bar = function(){};" + "var foo$bar$baz = function(){};"); } public void testProvidedNamespaceIsConst4() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.inlineConstantVars = true; options.collapseProperties = true; test(options, "var goog = {}; goog.provide('foo.Bar'); " + "var foo = {}; foo.Bar = {};", "var foo = {}; foo = {}; foo.Bar = {};"); } public void testProvidedNamespaceIsConst5() { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.inlineConstantVars = true; options.collapseProperties = true; test(options, "var goog = {}; goog.provide('foo.Bar'); " + "foo = {}; foo.Bar = {};", "var foo = {}; foo = {}; foo.Bar = {};"); } public void testProcessDefinesAlwaysOn() { test(createCompilerOptions(), "/** @define {boolean} */ var HI = true; HI = false;", "var HI = false;false;"); } public void testProcessDefinesAdditionalReplacements() { CompilerOptions options = createCompilerOptions(); options.setDefineToBooleanLiteral("HI", false); test(options, "/** @define {boolean} */ var HI = true;", "var HI = false;"); } public void testReplaceMessages() { CompilerOptions options = createCompilerOptions(); String prefix = "var goog = {}; goog.getMsg = function() {};"; testSame(options, prefix + "var MSG_HI = goog.getMsg('hi');"); options.messageBundle = new EmptyMessageBundle(); test(options, prefix + "/** @desc xyz */ var MSG_HI = goog.getMsg('hi');", prefix + "var MSG_HI = 'hi';"); } public void testCheckGlobalNames() { CompilerOptions options = createCompilerOptions(); options.checkGlobalNamesLevel = CheckLevel.ERROR; test(options, "var x = {}; var y = x.z;", CheckGlobalNames.UNDEFINED_NAME_WARNING); } public void testInlineGetters() { CompilerOptions options = createCompilerOptions(); String code = "function Foo() {} Foo.prototype.bar = function() { return 3; };" + "var x = new Foo(); x.bar();"; testSame(options, code); options.inlineGetters = true; test(options, code, "function Foo() {} Foo.prototype.bar = function() { return 3 };" + "var x = new Foo(); 3;"); } public void testInlineGettersWithAmbiguate() { CompilerOptions options = createCompilerOptions(); String code = "/** @constructor */" + "function Foo() {}" + "/** @type {number} */ Foo.prototype.field;" + "Foo.prototype.getField = function() { return this.field; };" + "/** @constructor */" + "function Bar() {}" + "/** @type {string} */ Bar.prototype.field;" + "Bar.prototype.getField = function() { return this.field; };" + "new Foo().getField();" + "new Bar().getField();"; testSame(options, code); options.inlineGetters = true; test(options, code, "function Foo() {}" + "Foo.prototype.field;" + "Foo.prototype.getField = function() { return this.field; };" + "function Bar() {}" + "Bar.prototype.field;" + "Bar.prototype.getField = function() { return this.field; };" + "new Foo().field;" + "new Bar().field;"); options.checkTypes = true; options.ambiguateProperties = true; // Propagating the wrong type information may cause ambiguate properties // to generate bad code. testSame(options, code); } public void testInlineVariables() { CompilerOptions options = createCompilerOptions(); String code = "function foo() {} var x = 3; foo(x);"; testSame(options, code); options.inlineVariables = true; test(options, code, "(function foo() {})(3);"); options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; test(options, code, DefaultPassConfig.CANNOT_USE_PROTOTYPE_AND_VAR); } public void testInlineConstants() { CompilerOptions options = createCompilerOptions(); String code = "function foo() {} var x = 3; foo(x); var YYY = 4; foo(YYY);"; testSame(options, code); options.inlineConstantVars = true; test(options, code, "function foo() {} var x = 3; foo(x); foo(4);"); } public void testMinimizeExits() { CompilerOptions options = createCompilerOptions(); String code = "function f() {" + " if (window.foo) return; window.h(); " + "}"; testSame(options, code); options.foldConstants = true; test( options, code, "function f() {" + " window.foo || window.h(); " + "}"); } public void testFoldConstants() { CompilerOptions options = createCompilerOptions(); String code = "if (true) { window.foo(); }"; testSame(options, code); options.foldConstants = true; test(options, code, "window.foo();"); } public void testRemoveUnreachableCode() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return; f(); }"; testSame(options, code); options.removeDeadCode = true; test(options, code, "function f() {}"); } public void testRemoveUnusedPrototypeProperties1() { CompilerOptions options = createCompilerOptions(); String code = "function Foo() {} " + "Foo.prototype.bar = function() { return new Foo(); };"; testSame(options, code); options.removeUnusedPrototypeProperties = true; test(options, code, "function Foo() {}"); } public void testRemoveUnusedPrototypeProperties2() { CompilerOptions options = createCompilerOptions(); String code = "function Foo() {} " + "Foo.prototype.bar = function() { return new Foo(); };" + "function f(x) { x.bar(); }"; testSame(options, code); options.removeUnusedPrototypeProperties = true; testSame(options, code); options.removeUnusedVars = true; test(options, code, ""); } public void testSmartNamePass() { CompilerOptions options = createCompilerOptions(); String code = "function Foo() { this.bar(); } " + "Foo.prototype.bar = function() { return Foo(); };"; testSame(options, code); options.smartNameRemoval = true; test(options, code, ""); } public void testDeadAssignmentsElimination() { CompilerOptions options = createCompilerOptions(); String code = "function f() { var x = 3; 4; x = 5; return x; } f(); "; testSame(options, code); options.deadAssignmentElimination = true; testSame(options, code); options.removeUnusedVars = true; test(options, code, "function f() { var x = 3; 4; x = 5; return x; } f();"); } public void testInlineFunctions() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return 3; } f(); "; testSame(options, code); options.inlineFunctions = true; test(options, code, "3;"); } public void testRemoveUnusedVars1() { CompilerOptions options = createCompilerOptions(); String code = "function f(x) {} f();"; testSame(options, code); options.removeUnusedVars = true; test(options, code, "function f() {} f();"); } public void testRemoveUnusedVars2() { CompilerOptions options = createCompilerOptions(); String code = "(function f(x) {})();var g = function() {}; g();"; testSame(options, code); options.removeUnusedVars = true; test(options, code, "(function() {})();var g = function() {}; g();"); options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; test(options, code, "(function f() {})();var g = function $g$() {}; g();"); } public void testCrossModuleCodeMotion() { CompilerOptions options = createCompilerOptions(); String[] code = new String[] { "var x = 1;", "x;", }; testSame(options, code); options.crossModuleCodeMotion = true; test(options, code, new String[] { "", "var x = 1; x;", }); } public void testCrossModuleMethodMotion() { CompilerOptions options = createCompilerOptions(); String[] code = new String[] { "var Foo = function() {}; Foo.prototype.bar = function() {};" + "var x = new Foo();", "x.bar();", }; testSame(options, code); options.crossModuleMethodMotion = true; test(options, code, new String[] { CrossModuleMethodMotion.STUB_DECLARATIONS + "var Foo = function() {};" + "Foo.prototype.bar=JSCompiler_stubMethod(0); var x=new Foo;", "Foo.prototype.bar=JSCompiler_unstubMethod(0,function(){}); x.bar()", }); } public void testFlowSensitiveInlineVariables1() { CompilerOptions options = createCompilerOptions(); String code = "function f() { var x = 3; x = 5; return x; }"; testSame(options, code); options.flowSensitiveInlineVariables = true; test(options, code, "function f() { var x = 3; return 5; }"); String unusedVar = "function f() { var x; x = 5; return x; } f()"; test(options, unusedVar, "function f() { var x; return 5; } f()"); options.removeUnusedVars = true; test(options, unusedVar, "function f() { return 5; } f()"); } public void testFlowSensitiveInlineVariables2() { CompilerOptions options = createCompilerOptions(); CompilationLevel.SIMPLE_OPTIMIZATIONS .setOptionsForCompilationLevel(options); test(options, "function f () {\n" + " var ab = 0;\n" + " ab += '-';\n" + " alert(ab);\n" + "}", "function f () {\n" + " alert('0-');\n" + "}"); } public void testCollapseAnonymousFunctions() { CompilerOptions options = createCompilerOptions(); String code = "var f = function() {};"; testSame(options, code); options.collapseAnonymousFunctions = true; test(options, code, "function f() {}"); } public void testMoveFunctionDeclarations() { CompilerOptions options = createCompilerOptions(); String code = "var x = f(); function f() { return 3; }"; testSame(options, code); options.moveFunctionDeclarations = true; test(options, code, "function f() { return 3; } var x = f();"); } public void testNameAnonymousFunctions() { CompilerOptions options = createCompilerOptions(); String code = "var f = function() {};"; testSame(options, code); options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; test(options, code, "var f = function $() {}"); assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; test(options, code, "var f = function $f$() {}"); assertNull(lastCompiler.getResult().namedAnonFunctionMap); } public void testNameAnonymousFunctionsWithVarRemoval() { CompilerOptions options = createCompilerOptions(); options.setRemoveUnusedVariables(CompilerOptions.Reach.LOCAL_ONLY); options.setInlineVariables(true); String code = "var f = function longName() {}; var g = function() {};" + "function longerName() {} var i = longerName;"; test(options, code, "var f = function() {}; var g = function() {}; " + "var i = function() {};"); options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED; test(options, code, "var f = function longName() {}; var g = function $() {};" + "var i = function longerName(){};"); assertNotNull(lastCompiler.getResult().namedAnonFunctionMap); options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; test(options, code, "var f = function longName() {}; var g = function $g$() {};" + "var i = function longerName(){};"); assertNull(lastCompiler.getResult().namedAnonFunctionMap); } public void testExtractPrototypeMemberDeclarations() { CompilerOptions options = createCompilerOptions(); String code = "var f = function() {};"; String expected = "var a; var b = function() {}; a = b.prototype;"; for (int i = 0; i < 10; i++) { code += "f.prototype.a = " + i + ";"; expected += "a.a = " + i + ";"; } testSame(options, code); options.extractPrototypeMemberDeclarations = true; options.variableRenaming = VariableRenamingPolicy.ALL; test(options, code, expected); options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; options.variableRenaming = VariableRenamingPolicy.OFF; testSame(options, code); } public void testDevirtualizationAndExtractPrototypeMemberDeclarations() { CompilerOptions options = createCompilerOptions(); options.devirtualizePrototypeMethods = true; options.collapseAnonymousFunctions = true; options.extractPrototypeMemberDeclarations = true; options.variableRenaming = VariableRenamingPolicy.ALL; String code = "var f = function() {};"; String expected = "var a; function b() {} a = b.prototype;"; for (int i = 0; i < 10; i++) { code += "f.prototype.argz = function() {arguments};"; code += "f.prototype.devir" + i + " = function() {};"; char letter = (char) ('d' + i); // skip i,j,o (reserved) if (letter >= 'i') letter++; if (letter >= 'j') letter++; if (letter >= 'o') letter++; expected += "a.argz = function() {arguments};"; expected += "function " + letter + "(c){}"; } code += "var F = new f(); F.argz();"; expected += "var q = new b(); q.argz();"; for (int i = 0; i < 10; i++) { code += "F.devir" + i + "();"; char letter = (char) ('d' + i); // skip i,j,o (reserved) if (letter >= 'i') letter++; if (letter >= 'j') letter++; if (letter >= 'o') letter++; expected += letter + "(q);"; } test(options, code, expected); } public void testCoalesceVariableNames() { CompilerOptions options = createCompilerOptions(); String code = "function f() {var x = 3; var y = x; var z = y; return z;}"; testSame(options, code); options.coalesceVariableNames = true; test(options, code, "function f() {var x = 3; x = x; x = x; return x;}"); } public void testPropertyRenaming() { CompilerOptions options = createCompilerOptions(); options.propertyAffinity = true; String code = "function f() { return this.foo + this['bar'] + this.Baz; }" + "f.prototype.bar = 3; f.prototype.Baz = 3;"; String heuristic = "function f() { return this.foo + this['bar'] + this.a; }" + "f.prototype.bar = 3; f.prototype.a = 3;"; String aggHeuristic = "function f() { return this.foo + this['b'] + this.a; } " + "f.prototype.b = 3; f.prototype.a = 3;"; String all = "function f() { return this.b + this['bar'] + this.a; }" + "f.prototype.c = 3; f.prototype.a = 3;"; testSame(options, code); options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC; test(options, code, heuristic); options.propertyRenaming = PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; test(options, code, aggHeuristic); options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; test(options, code, all); } public void testConvertToDottedProperties() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return this['bar']; } f.prototype.bar = 3;"; String expected = "function f() { return this.bar; } f.prototype.a = 3;"; testSame(options, code); options.convertToDottedProperties = true; options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED; test(options, code, expected); } public void testRewriteFunctionExpressions() { CompilerOptions options = createCompilerOptions(); String code = "var a = function() {};"; String expected = "function JSCompiler_emptyFn(){return function(){}} " + "var a = JSCompiler_emptyFn();"; for (int i = 0; i < 10; i++) { code += "a = function() {};"; expected += "a = JSCompiler_emptyFn();"; } testSame(options, code); options.rewriteFunctionExpressions = true; test(options, code, expected); } public void testAliasAllStrings() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return 'a'; }"; String expected = "var $$S_a = 'a'; function f() { return $$S_a; }"; testSame(options, code); options.aliasAllStrings = true; test(options, code, expected); } public void testAliasExterns() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return window + window + window + window; }"; String expected = "var GLOBAL_window = window;" + "function f() { return GLOBAL_window + GLOBAL_window + " + " GLOBAL_window + GLOBAL_window; }"; testSame(options, code); options.aliasExternals = true; test(options, code, expected); } public void testAliasKeywords() { CompilerOptions options = createCompilerOptions(); String code = "function f() { return true + true + true + true + true + true; }"; String expected = "var JSCompiler_alias_TRUE = true;" + "function f() { return JSCompiler_alias_TRUE + " + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + " JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " + " JSCompiler_alias_TRUE; }"; testSame(options, code); options.aliasKeywords = true; test(options, code, expected); } public void testRenameVars1() { CompilerOptions options = createCompilerOptions(); String code = "var abc = 3; function f() { var xyz = 5; return abc + xyz; }"; String local = "var abc = 3; function f() { var a = 5; return abc + a; }"; String all = "var a = 3; function c() { var b = 5; return a + b; }"; testSame(options, code); options.variableRenaming = VariableRenamingPolicy.LOCAL; test(options, code, local); options.variableRenaming = VariableRenamingPolicy.ALL; test(options, code, all); options.reserveRawExports = true; } public void testRenameVars2() { CompilerOptions options = createCompilerOptions(); options.variableRenaming = VariableRenamingPolicy.ALL; String code = "var abc = 3; function f() { window['a'] = 5; }"; String noexport = "var a = 3; function b() { window['a'] = 5; }"; String export = "var b = 3; function c() { window['a'] = 5; }"; options.reserveRawExports = false; test(options, code, noexport); options.reserveRawExports = true; test(options, code, export); } public void testShadowVaribles() { CompilerOptions options = createCompilerOptions(); options.variableRenaming = VariableRenamingPolicy.LOCAL; options.shadowVariables = true; String code = "var f = function(x) { return function(y) {}}"; String expected = "var f = function(a) { return function(a) {}}"; test(options, code, expected); } public void testRenameLabels() { CompilerOptions options = createCompilerOptions(); String code = "longLabel: for(;true;) { break longLabel; }"; String expected = "a: for(;true;) { break a; }"; testSame(options, code); options.labelRenaming = true; test(options, code, expected); } public void testBadBreakStatementInIdeMode() { // Ensure that type-checking doesn't crash, even if the CFG is malformed. // This can happen in IDE mode. CompilerOptions options = createCompilerOptions(); options.ideMode = true; options.checkTypes = true; test(options, "function f() { try { } catch(e) { break; } }", RhinoErrorReporter.PARSE_ERROR); } public void testIssue63SourceMap() { CompilerOptions options = createCompilerOptions(); String code = "var a;"; options.skipAllPasses = true; options.sourceMapOutputPath = "./src.map"; Compiler compiler = compile(options, code); compiler.toSource(); } public void testRegExp1() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "/(a)/.test(\"a\");"; testSame(options, code); options.computeFunctionSideEffects = true; String expected = ""; test(options, code, expected); } public void testRegExp2() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "/(a)/.test(\"a\");var a = RegExp.$1"; testSame(options, code); options.computeFunctionSideEffects = true; test(options, code, CheckRegExp.REGEXP_REFERENCE); options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF); testSame(options, code); } public void testFoldLocals1() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; // An external object, whose constructor has no side-effects, // and whose method "go" only modifies the object. String code = "new Widget().go();"; testSame(options, code); options.computeFunctionSideEffects = true; test(options, code, ""); } public void testFoldLocals2() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; options.checkTypes = true; // An external function that returns a local object that the // method "go" that only modifies the object. String code = "widgetToken().go();"; testSame(options, code); options.computeFunctionSideEffects = true; test(options, code, "widgetToken()"); } public void testFoldLocals3() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; // A function "f" who returns a known local object, and a method that // modifies only modifies that. String definition = "function f(){return new Widget()}"; String call = "f().go();"; String code = definition + call; testSame(options, code); options.computeFunctionSideEffects = true; // BROKEN //test(options, code, definition); testSame(options, code); } public void testFoldLocals4() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "/** @constructor */\n" + "function InternalWidget(){this.x = 1;}" + "InternalWidget.prototype.internalGo = function (){this.x = 2};" + "new InternalWidget().internalGo();"; testSame(options, code); options.computeFunctionSideEffects = true; String optimized = "" + "function InternalWidget(){this.x = 1;}" + "InternalWidget.prototype.internalGo = function (){this.x = 2};"; test(options, code, optimized); } public void testFoldLocals5() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "" + "function fn(){var a={};a.x={};return a}" + "fn().x.y = 1;"; // "fn" returns a unescaped local object, we should be able to fold it, // but we don't currently. String result = "" + "function fn(){var a={x:{}};return a}" + "fn().x.y = 1;"; test(options, code, result); options.computeFunctionSideEffects = true; test(options, code, result); } public void testFoldLocals6() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "" + "function fn(){return {}}" + "fn().x.y = 1;"; testSame(options, code); options.computeFunctionSideEffects = true; testSame(options, code); } public void testFoldLocals7() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; String code = "" + "function InternalWidget(){return [];}" + "Array.prototype.internalGo = function (){this.x = 2};" + "InternalWidget().internalGo();"; testSame(options, code); options.computeFunctionSideEffects = true; String optimized = "" + "function InternalWidget(){return [];}" + "Array.prototype.internalGo = function (){this.x = 2};"; test(options, code, optimized); } public void testVarDeclarationsIntoFor() { CompilerOptions options = createCompilerOptions(); options.collapseVariableDeclarations = false; String code = "var a = 1; for (var b = 2; ;) {}"; testSame(options, code); options.collapseVariableDeclarations = true; test(options, code, "for (var a = 1, b = 2; ;) {}"); } public void testExploitAssigns() { CompilerOptions options = createCompilerOptions(); options.collapseVariableDeclarations = false; String code = "a = 1; b = a; c = b"; testSame(options, code); options.collapseVariableDeclarations = true; test(options, code, "c=b=a=1"); } public void testRecoverOnBadExterns() throws Exception { // This test is for a bug in a very narrow set of circumstances: // 1) externs validation has to be off. // 2) aliasExternals has to be on. // 3) The user has to reference a "normal" variable in externs. // This case is handled at checking time by injecting a // synthetic extern variable, and adding a "@suppress {duplicate}" to // the normal code at compile time. But optimizations may remove that // annotation, so we need to make sure that the variable declarations // are de-duped before that happens. CompilerOptions options = createCompilerOptions(); options.aliasExternals = true; externs = ImmutableList.of( SourceFile.fromCode("externs", "extern.foo")); test(options, "var extern; " + "function f() { return extern + extern + extern + extern; }", "var extern; " + "function f() { return extern + extern + extern + extern; }", VarCheck.UNDEFINED_EXTERN_VAR_ERROR); } public void testDuplicateVariablesInExterns() { CompilerOptions options = createCompilerOptions(); options.checkSymbols = true; externs = ImmutableList.of( SourceFile.fromCode("externs", "var externs = {}; /** @suppress {duplicate} */ var externs = {};")); testSame(options, ""); } public void testLanguageMode() { CompilerOptions options = createCompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT3); String code = "var a = {get f(){}}"; Compiler compiler = compile(options, code); checkUnexpectedErrorsOrWarnings(compiler, 1); assertEquals( "JSC_PARSE_ERROR. Parse error. " + "getters are not supported in older versions of JS. " + "If you are targeting newer versions of JS, " + "set the appropriate language_in option. " + "at i0 line 1 : 0", compiler.getErrors()[0].toString()); options.setLanguageIn(LanguageMode.ECMASCRIPT5); testSame(options, code); options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); testSame(options, code); } public void testLanguageMode2() { CompilerOptions options = createCompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT3); options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); String code = "var a = 2; delete a;"; testSame(options, code); options.setLanguageIn(LanguageMode.ECMASCRIPT5); testSame(options, code); options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); test(options, code, code, StrictModeCheck.DELETE_VARIABLE); } public void testIssue598() { CompilerOptions options = createCompilerOptions(); options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT); WarningLevel.VERBOSE.setOptionsForWarningLevel(options); options.setLanguageIn(LanguageMode.ECMASCRIPT5); String code = "'use strict';\n" + "function App() {}\n" + "App.prototype = {\n" + " get appData() { return this.appData_; },\n" + " set appData(data) { this.appData_ = data; }\n" + "};"; testSame(options, code); } public void testIssue701() { // Check ASCII art in license comments. String ascii = "/**\n" + " * @preserve\n" + " This\n" + " is\n" + " ASCII ART\n" + "*/"; String result = "/*\n\n" + " This\n" + " is\n" + " ASCII ART\n" + "*/\n"; testSame(createCompilerOptions(), ascii); assertEquals(result, lastCompiler.toSource()); } public void testIssue724() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); String code = "isFunction = function(functionToCheck) {" + " var getType = {};" + " return functionToCheck && " + " getType.toString.apply(functionToCheck) === " + " '[object Function]';" + "};"; String result = "isFunction=function(a){var b={};" + "return a&&\"[object Function]\"===b.b.a(a)}"; test(options, code, result); } public void testIssue730() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); String code = "/** @constructor */function A() {this.foo = 0; Object.seal(this);}\n" + "/** @constructor */function B() {this.a = new A();}\n" + "B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" + "new B().dostuff();\n"; test(options, code, "function a(){this.b=0;Object.seal(this)}" + "(new function(){this.a=new a}).a.b++;" + "alert(\"hi\")"); options.removeUnusedClassProperties = true; // This is still a problem when removeUnusedClassProperties are enabled. test(options, code, "function a(){Object.seal(this)}" + "(new function(){this.a=new a}).a.b++;" + "alert(\"hi\")"); } public void testCoaleseVariables() { CompilerOptions options = createCompilerOptions(); options.foldConstants = false; options.coalesceVariableNames = true; String code = "function f(a) {" + " if (a) {" + " return a;" + " } else {" + " var b = a;" + " return b;" + " }" + " return a;" + "}"; String expected = "function f(a) {" + " if (a) {" + " return a;" + " } else {" + " a = a;" + " return a;" + " }" + " return a;" + "}"; test(options, code, expected); options.foldConstants = true; options.coalesceVariableNames = false; code = "function f(a) {" + " if (a) {" + " return a;" + " } else {" + " var b = a;" + " return b;" + " }" + " return a;" + "}"; expected = "function f(a) {" + " if (!a) {" + " var b = a;" + " return b;" + " }" + " return a;" + "}"; test(options, code, expected); options.foldConstants = true; options.coalesceVariableNames = true; expected = "function f(a) {" + " return a;" + "}"; test(options, code, expected); } public void testLateStatementFusion() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; test(options, "while(a){a();if(b){b();b()}}", "for(;a;)a(),b&&(b(),b())"); } public void testLateConstantReordering() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; test(options, "if (x < 1 || x > 1 || 1 < x || 1 > x) { alert(x) }", " (1 > x || 1 < x || 1 < x || 1 > x) && alert(x) "); } public void testsyntheticBlockOnDeadAssignments() { CompilerOptions options = createCompilerOptions(); options.deadAssignmentElimination = true; options.removeUnusedVars = true; options.syntheticBlockStartMarker = "START"; options.syntheticBlockEndMarker = "END"; test(options, "var x; x = 1; START(); x = 1;END();x()", "var x; x = 1;{START();{x = 1}END()}x()"); } public void testBug4152835() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; options.syntheticBlockStartMarker = "START"; options.syntheticBlockEndMarker = "END"; test(options, "START();END()", "{START();{}END()}"); } public void testBug5786871() { CompilerOptions options = createCompilerOptions(); options.ideMode = true; test(options, "function () {}", RhinoErrorReporter.PARSE_ERROR); } public void testIssue378() { CompilerOptions options = createCompilerOptions(); options.inlineVariables = true; options.flowSensitiveInlineVariables = true; testSame(options, "function f(c) {var f = c; arguments[0] = this;" + " f.apply(this, arguments); return this;}"); } public void testIssue550() { CompilerOptions options = createCompilerOptions(); CompilationLevel.SIMPLE_OPTIMIZATIONS .setOptionsForCompilationLevel(options); options.foldConstants = true; options.inlineVariables = true; options.flowSensitiveInlineVariables = true; test(options, "function f(h) {\n" + " var a = h;\n" + " a = a + 'x';\n" + " a = a + 'y';\n" + " return a;\n" + "}", // This should eventually get inlined completely. "function f(a) { a += 'x'; return a += 'y'; }"); } public void testIssue284() { CompilerOptions options = createCompilerOptions(); options.smartNameRemoval = true; test(options, "var goog = {};" + "goog.inherits = function(x, y) {};" + "var ns = {};" + "/** @constructor */" + "ns.PageSelectionModel = function() {};" + "/** @constructor */" + "ns.PageSelectionModel.FooEvent = function() {};" + "/** @constructor */" + "ns.PageSelectionModel.SelectEvent = function() {};" + "goog.inherits(ns.PageSelectionModel.ChangeEvent," + " ns.PageSelectionModel.FooEvent);", ""); } public void testIssue772() throws Exception { CompilerOptions options = createCompilerOptions(); options.closurePass = true; options.checkTypes = true; test( options, "/** @const */ var a = {};" + "/** @const */ a.b = {};" + "/** @const */ a.b.c = {};" + "goog.scope(function() {" + " var b = a.b;" + " var c = b.c;" + " /** @typedef {string} */" + " c.MyType;" + " /** @param {c.MyType} x The variable. */" + " c.myFunc = function(x) {};" + "});", "/** @const */ var a = {};" + "/** @const */ a.b = {};" + "/** @const */ a.b.c = {};" + "a.b.c.MyType;" + "a.b.c.myFunc = function(x) {};"); } public void testCodingConvention() { Compiler compiler = new Compiler(); compiler.initOptions(new CompilerOptions()); assertEquals( compiler.getCodingConvention().getClass().toString(), ClosureCodingConvention.class.toString()); } public void testJQueryStringSplitLoops() { CompilerOptions options = createCompilerOptions(); options.foldConstants = true; test(options, "var x=['1','2','3','4','5','6','7']", "var x='1234567'.split('')"); options = createCompilerOptions(); options.foldConstants = true; options.computeFunctionSideEffects = false; options.removeUnusedVars = true; // If we do splits too early, it would add a side-effect to x. test(options, "var x=['1','2','3','4','5','6','7']", ""); } public void testAlwaysRunSafetyCheck() { CompilerOptions options = createCompilerOptions(); options.checkSymbols = false; options.customPasses = ArrayListMultimap.create(); options.customPasses.put( CustomPassExecutionTime.BEFORE_OPTIMIZATIONS, new CompilerPass() { @Override public void process(Node externs, Node root) { Node var = root.getLastChild().getFirstChild(); assertEquals(Token.VAR, var.getType()); var.detachFromParent(); } }); try { test(options, "var x = 3; function f() { return x + z; }", "function f() { return x + z; }"); fail("Expected run-time exception"); } catch (RuntimeException e) { assertTrue(e.getMessage().indexOf("Unexpected variable x") != -1); } } public void testSuppressEs5StrictWarning() { CompilerOptions options = createCompilerOptions(); options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING); test(options, "/** @suppress{es5Strict} */\n" + "function f() { var arguments; }", "function f() {}"); } public void testCheckProvidesWarning() { CompilerOptions options = createCompilerOptions(); options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); options.setCheckProvides(CheckLevel.WARNING); test(options, "/** @constructor */\n" + "function f() { var arguments; }", DiagnosticType.warning("JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')")); } public void testSuppressCheckProvidesWarning() { CompilerOptions options = createCompilerOptions(); options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING); options.setCheckProvides(CheckLevel.WARNING); testSame(options, "/** @constructor\n" + " * @suppress{checkProvides} */\n" + "function f() {}"); } public void testSuppressCastWarning() { CompilerOptions options = createCompilerOptions(); options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.WARNING); normalizeResults = true; test(options, "function f() { var xyz = /** @type {string} */ (0); }", DiagnosticType.warning( "JSC_INVALID_CAST", "invalid cast")); testSame(options, "/** @suppress {invalidCasts} */\n" + "function f() { var xyz = /** @type {string} */ (0); }"); testSame(options, "/** @const */ var g = {};" + "/** @suppress {invalidCasts} */" + "g.a = g.b = function() { var xyz = /** @type {string} */ (0); }"); } public void testLhsCast() { CompilerOptions options = createCompilerOptions(); test( options, "/** @const */ var g = {};" + "/** @type {number} */ (g.foo) = 3;", "/** @const */ var g = {};" + "g.foo = 3;"); } public void testRenamePrefix() { String code = "var x = {}; function f(y) {}"; CompilerOptions options = createCompilerOptions(); options.renamePrefix = "G_"; options.variableRenaming = VariableRenamingPolicy.ALL; test(options, code, "var G_={}; function G_a(a) {}"); } public void testRenamePrefixNamespace() { String code = "var x = {}; x.FOO = 5; x.bar = 3;"; CompilerOptions options = createCompilerOptions(); testSame(options, code); options.collapseProperties = true; options.renamePrefixNamespace = "_"; test(options, code, "_.x$FOO = 5; _.x$bar = 3;"); } public void testRenamePrefixNamespaceProtectSideEffects() { String code = "var x = null; try { +x.FOO; } catch (e) {}"; CompilerOptions options = createCompilerOptions(); testSame(options, code); CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel( options); options.renamePrefixNamespace = "_"; test(options, code, "_.x = null; try { +_.x.FOO; } catch (e) {}"); } public void testRenamePrefixNamespaceActivatesMoveFunctionDeclarations() { CompilerOptions options = createCompilerOptions(); String code = "var x = f; function f() { return 3; }"; testSame(options, code); assertFalse(options.moveFunctionDeclarations); options.renamePrefixNamespace = "_"; test(options, code, "_.f = function() { return 3; }; _.x = _.f;"); } public void testBrokenNameSpace() { CompilerOptions options = createCompilerOptions(); String code = "var goog; goog.provide('i.am.on.a.Horse');" + "i.am.on.a.Horse = function() {};" + "i.am.on.a.Horse.prototype.x = function() {};" + "i.am.on.a.Boat.prototype.y = function() {}"; options.closurePass = true; options.collapseProperties = true; options.smartNameRemoval = true; test(options, code, ""); } public void testNamelessParameter() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); String code = "var impl_0;" + "$load($init());" + "function $load(){" + " window['f'] = impl_0;" + "}" + "function $init() {" + " impl_0 = {};" + "}"; String result = "window.f = {};"; test(options, code, result); } public void testHiddenSideEffect() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); options.setAliasExternals(true); String code = "window.offsetWidth;"; String result = "window.offsetWidth;"; test(options, code, result); } public void testNegativeZero() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); test(options, "function bar(x) { return x; }\n" + "function foo(x) { print(x / bar(0));\n" + " print(x / bar(-0)); }\n" + "foo(3);", "print(3/0);print(3/-0);"); } public void testSingletonGetter1() { CompilerOptions options = createCompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS .setOptionsForCompilationLevel(options); options.setCodingConvention(new ClosureCodingConvention()); test(options, "/** @const */\n" + "var goog = goog || {};\n" + "goog.addSingletonGetter = function(ctor) {\n" + " ctor.getInstance = function() {\n" + " return ctor.instance_ || (ctor.instance_ = new ctor());\n" + " };\n" + "};" + "function Foo() {}\n" + "goog.addSingletonGetter(Foo);" + "Foo.prototype.bar = 1;" + "function Bar() {}\n" + "goog.addSingletonGetter(Bar);" + "Bar.prototype.bar = 1;", ""); } public void testIncompleteFunction1() { CompilerOptions options = createCompilerOptions(); options.ideMode = true; DiagnosticType[] warnings = new DiagnosticType[]{ RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR}; test(options, new String[] { "var foo = {bar: function(e) }" }, new String[] { "var foo = {bar: function(e){}};" }, warnings ); } public void testIncompleteFunction2() { CompilerOptions options = createCompilerOptions(); options.ideMode = true; DiagnosticType[] warnings = new DiagnosticType[]{ RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR, RhinoErrorReporter.PARSE_ERROR}; test(options, new String[] { "function hi" }, new String[] { "function hi() {}" }, warnings ); } public void testSortingOff() { CompilerOptions options = new CompilerOptions(); options.closurePass = true; options.setCodingConvention(new ClosureCodingConvention()); test(options, new String[] { "goog.require('goog.beer');", "goog.provide('goog.beer');" }, ProcessClosurePrimitives.LATE_PROVIDE_ERROR); } public void testUnboundedArrayLiteralInfiniteLoop() { CompilerOptions options = createCompilerOptions(); options.ideMode = true; test(options, "var x = [1, 2", "var x = [1, 2]", RhinoErrorReporter.PARSE_ERROR); } public void testProvideRequireSameFile() throws Exception { CompilerOptions options = createCompilerOptions(); options.setDependencyOptions( new DependencyOptions() .setDependencySorting(true)); options.closurePass = true; test( options, "goog.provide('x');\ngoog.require('x');", "var x = {};"); } public void testDependencySorting() throws Exception { CompilerOptions options = createCompilerOptions(); options.setDependencyOptions( new DependencyOptions() .setDependencySorting(true)); test( options, new String[] { "goog.require('x');", "goog.provide('x');", }, new String[] { "goog.provide('x');", "goog.require('x');", // For complicated reasons involving modules, // the compiler creates a synthetic source file. "", }); } public void testStrictWarningsGuard() throws Exception { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; options.addWarningsGuard(new StrictWarningsGuard()); Compiler compiler = compile(options, "/** @return {number} */ function f() { return true; }"); assertEquals(1, compiler.getErrors().length); assertEquals(0, compiler.getWarnings().length); } public void testStrictWarningsGuardEmergencyMode() throws Exception { CompilerOptions options = createCompilerOptions(); options.checkTypes = true; options.addWarningsGuard(new StrictWarningsGuard()); options.useEmergencyFailSafe(); Compiler compiler = compile(options, "/** @return {number} */ function f() { return true; }"); assertEquals(0, compiler.getErrors().length); assertEquals(1, compiler.getWarnings().length); } public void testInlineProperties() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); level.setTypeBasedOptimizationOptions(options); String code = "" + "var ns = {};\n" + "/** @constructor */\n" + "ns.C = function () {this.someProperty = 1}\n" + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; assertTrue(options.inlineProperties); assertTrue(options.collapseProperties); // CollapseProperties used to prevent inlining this property. test(options, code, "alert(2);"); } public void testGoogDefineClass1() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); level.setTypeBasedOptimizationOptions(options); String code = "" + "var ns = {};\n" + "ns.C = goog.defineClass(null, {\n" + " /** @constructor */\n" + " constructor: function () {this.someProperty = 1}\n" + "});\n" + "alert(new ns.C().someProperty + new ns.C().someProperty);\n"; assertTrue(options.inlineProperties); assertTrue(options.collapseProperties); // CollapseProperties used to prevent inlining this property. test(options, code, "alert(2);"); } public void testGoogDefineClass2() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); level.setTypeBasedOptimizationOptions(options); String code = "" + "var C = goog.defineClass(null, {\n" + " /** @constructor */\n" + " constructor: function () {this.someProperty = 1}\n" + "});\n" + "alert(new C().someProperty + new C().someProperty);\n"; assertTrue(options.inlineProperties); assertTrue(options.collapseProperties); // CollapseProperties used to prevent inlining this property. test(options, code, "alert(2);"); } public void testGoogDefineClass3() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); level.setTypeBasedOptimizationOptions(options); WarningLevel warnings = WarningLevel.VERBOSE; warnings.setOptionsForWarningLevel(options); String code = "" + "var C = goog.defineClass(null, {\n" + " /** @constructor */\n" + " constructor: function () {\n" + " /** @type {number} */\n" + " this.someProperty = 1},\n" + " /** @param {string} a */\n" + " someMethod: function (a) {}\n" + "});" + "var x = new C();\n" + "x.someMethod(x.someProperty);\n"; assertTrue(options.inlineProperties); assertTrue(options.collapseProperties); // CollapseProperties used to prevent inlining this property. test(options, code, TypeValidator.TYPE_MISMATCH_WARNING); } public void testCheckConstants1() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); WarningLevel warnings = WarningLevel.QUIET; warnings.setOptionsForWarningLevel(options); String code = "" + "var foo; foo();\n" + "/** @const */\n" + "var x = 1; foo(); x = 2;\n"; test(options, code, code); } public void testCheckConstants2() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); WarningLevel warnings = WarningLevel.DEFAULT; warnings.setOptionsForWarningLevel(options); String code = "" + "var foo;\n" + "/** @const */\n" + "var x = 1; foo(); x = 2;\n"; test(options, code, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); } public void testIssue787() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); WarningLevel warnings = WarningLevel.DEFAULT; warnings.setOptionsForWarningLevel(options); String code = "" + "function some_function() {\n" + " var fn1;\n" + " var fn2;\n" + "\n" + " if (any_expression) {\n" + " fn2 = external_ref;\n" + " fn1 = function (content) {\n" + " return fn2();\n" + " }\n" + " }\n" + "\n" + " return {\n" + " method1: function () {\n" + " if (fn1) fn1();\n" + " return true;\n" + " },\n" + " method2: function () {\n" + " return false;\n" + " }\n" + " }\n" + "}"; String result = "" + "function some_function() {\n" + " var a, b;\n" + " any_expression && (b = external_ref, a = function(a) {\n" + " return b()\n" + " });\n" + " return{method1:function() {\n" + " a && a();\n" + " return !0\n" + " }, method2:function() {\n" + " return !1\n" + " }}\n" + "}\n" + ""; test(options, code, result); } public void testManyAdds() { CompilerOptions options = createCompilerOptions(); CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS; level.setOptionsForCompilationLevel(options); WarningLevel warnings = WarningLevel.VERBOSE; warnings.setOptionsForWarningLevel(options); int numAdds = 4750; StringBuilder original = new StringBuilder("var x = 0"); for (int i = 0; i < numAdds; i++) { original.append(" + 1"); } original.append(";"); test(options, original.toString(), "var x = " + numAdds + ";"); } // Checks that the summary and the log in the output of PerformanceTracker // have the expected number of columns public void testPerfTracker() { ByteArrayOutputStream output = new ByteArrayOutputStream(); PrintStream outstream = new PrintStream(output); Compiler compiler = new Compiler(outstream); CompilerOptions options = new CompilerOptions(); List inputs = Lists.newArrayList(); List externs = Lists.newArrayList(); options.setTracerMode(TracerMode.ALL); inputs.add(SourceFile.fromCode("foo", "function fun(){}")); compiler.compile(externs, inputs, options); outstream.flush(); outstream.close(); Pattern p = Pattern.compile( ".*Summary:\npass,runtime,runs,changingRuns,reduction,gzReduction" + ".*TOTAL:" + "\nRuntime\\(ms\\): [0-9]+" + "\n#Runs: [0-9]+" + "\n#Changing runs: [0-9]+" + "\n#Loopable runs: [0-9]+" + "\n#Changing loopable runs: [0-9]+" + "\nReduction\\(bytes\\): [0-9]+" + "\nGzReduction\\(bytes\\): [0-9]+" + "\nSize\\(bytes\\): [0-9]+" + "\nGzSize\\(bytes\\): [0-9]+" + "\n\nLog:\n" + "pass,runtime,runs,changingRuns,reduction,gzReduction,size,gzSize.*", Pattern.DOTALL); assertTrue(p.matcher(output.toString()).matches()); } /** Creates a CompilerOptions object with google coding conventions. */ @Override protected CompilerOptions createCompilerOptions() { CompilerOptions options = new CompilerOptions(); options.setCodingConvention(new GoogleCodingConvention()); return options; } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/LinkedFlowScopeTest.java0000644000175000017500000002545612115204405027573 0ustar apoapo/* * 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)); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/TypeInferenceTest.java0000644000175000017500000010430012115204405027265 0ustar apoapo/* * 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 static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_INSTANCE_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_RESOLVED_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.DataFlowAnalysis.BranchedFlowState; import com.google.javascript.jscomp.type.FlowScope; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.testing.Asserts; import junit.framework.TestCase; import java.util.Map; /** * Tests {@link TypeInference}. * */ public class TypeInferenceTest extends TestCase { private Compiler compiler; private JSTypeRegistry registry; private Map assumptions; private JSType assumedThisType; private FlowScope returnScope; private static final Map ASSERTION_FUNCTION_MAP = Maps.newHashMap(); static { for (AssertionFunctionSpec func : new ClosureCodingConvention().getAssertionFunctions()) { ASSERTION_FUNCTION_MAP.put(func.getFunctionName(), func); } } @Override public void setUp() { compiler = new Compiler(); CompilerOptions options = new CompilerOptions(); options.setClosurePass(true); options.setLanguageIn(LanguageMode.ECMASCRIPT5); compiler.initOptions(options); registry = compiler.getTypeRegistry(); assumptions = Maps.newHashMap(); returnScope = null; } private void assumingThisType(JSType type) { assumedThisType = type; } private void assuming(String name, JSType type) { assumptions.put(name, type); } private void assuming(String name, JSTypeNative type) { assuming(name, registry.getNativeType(type)); } private void inFunction(String js) { // Parse the body of the function. String thisBlock = assumedThisType == null ? "" : "/** @this {" + assumedThisType + "} */"; Node root = compiler.parseTestCode( "(" + thisBlock + " function() {" + js + "});"); assertEquals("parsing error: " + Joiner.on(", ").join(compiler.getErrors()), 0, compiler.getErrorCount()); Node n = root.getFirstChild().getFirstChild(); // Create the scope with the assumptions. TypedScopeCreator scopeCreator = new TypedScopeCreator(compiler); Scope assumedScope = scopeCreator.createScope( n, scopeCreator.createScope(root, null)); for (Map.Entry entry : assumptions.entrySet()) { assumedScope.declare(entry.getKey(), null, entry.getValue(), null, false); } // Create the control graph. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, n); ControlFlowGraph cfg = cfa.getCfg(); // Create a simple reverse abstract interpreter. ReverseAbstractInterpreter rai = compiler.getReverseAbstractInterpreter(); // Do the type inference by data-flow analysis. TypeInference dfa = new TypeInference(compiler, cfg, rai, assumedScope, ASSERTION_FUNCTION_MAP); dfa.analyze(); // Get the scope of the implicit return. BranchedFlowState rtnState = cfg.getImplicitReturn().getAnnotation(); returnScope = rtnState.getIn(); } private JSType getType(String name) { assertTrue("The return scope should not be null.", returnScope != null); StaticSlot var = returnScope.getSlot(name); assertTrue("The variable " + name + " is missing from the scope.", var != null); return var.getType(); } private void verify(String name, JSType type) { Asserts.assertTypeEquals("Mismatch for " + name, type, getType(name)); } private void verify(String name, JSTypeNative type) { verify(name, registry.getNativeType(type)); } private void verifySubtypeOf(String name, JSType type) { JSType varType = getType(name); assertTrue("The variable " + name + " is missing a type.", varType != null); assertTrue("The type " + varType + " of variable " + name + " is not a subtype of " + type +".", varType.isSubtype(type)); } private void verifySubtypeOf(String name, JSTypeNative type) { verifySubtypeOf(name, registry.getNativeType(type)); } private EnumType createEnumType(String name, JSTypeNative elemType) { return createEnumType(name, registry.getNativeType(elemType)); } private EnumType createEnumType(String name, JSType elemType) { return registry.createEnumType(name, null, elemType); } private JSType createUndefinableType(JSTypeNative type) { return registry.createUnionType( registry.getNativeType(type), registry.getNativeType(VOID_TYPE)); } private JSType createNullableType(JSTypeNative type) { return createNullableType(registry.getNativeType(type)); } private JSType createNullableType(JSType type) { return registry.createNullableType(type); } private JSType createUnionType(JSTypeNative type1, JSTypeNative type2) { return registry.createUnionType( registry.getNativeType(type1), registry.getNativeType(type2)); } public void testAssumption() { assuming("x", NUMBER_TYPE); inFunction(""); verify("x", NUMBER_TYPE); } public void testVar() { inFunction("var x = 1;"); verify("x", NUMBER_TYPE); } public void testEmptyVar() { inFunction("var x;"); verify("x", VOID_TYPE); } public void testAssignment() { assuming("x", OBJECT_TYPE); inFunction("x = 1;"); verify("x", NUMBER_TYPE); } public void testExprWithinCast() { assuming("x", OBJECT_TYPE); inFunction("/** @type {string} */ (x = 1);"); verify("x", NUMBER_TYPE); } public void testGetProp() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("x.y();"); verify("x", OBJECT_TYPE); } public void testGetElemDereference() { assuming("x", createUndefinableType(OBJECT_TYPE)); inFunction("x['z'] = 3;"); verify("x", OBJECT_TYPE); } public void testIf1() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = {}; if (x) { y = x; }"); verifySubtypeOf("y", OBJECT_TYPE); } public void testIf1a() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = {}; if (x != null) { y = x; }"); verifySubtypeOf("y", OBJECT_TYPE); } public void testIf2() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = x; if (x) { y = x; } else { y = {}; }"); verifySubtypeOf("y", OBJECT_TYPE); } public void testIf3() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = 1; if (x) { y = x; }"); verify("y", createUnionType(OBJECT_TYPE, NUMBER_TYPE)); } public void testPropertyInference1() { ObjectType thisType = registry.createAnonymousObjectType(null); thisType.defineDeclaredProperty("foo", createUndefinableType(STRING_TYPE), null); assumingThisType(thisType); inFunction("var y = 1; if (this.foo) { y = this.foo; }"); verify("y", createUnionType(NUMBER_TYPE, STRING_TYPE)); } public void testPropertyInference2() { ObjectType thisType = registry.createAnonymousObjectType(null); thisType.defineDeclaredProperty("foo", createUndefinableType(STRING_TYPE), null); assumingThisType(thisType); inFunction("var y = 1; this.foo = 'x'; y = this.foo;"); verify("y", STRING_TYPE); } public void testPropertyInference3() { ObjectType thisType = registry.createAnonymousObjectType(null); thisType.defineDeclaredProperty("foo", createUndefinableType(STRING_TYPE), null); assumingThisType(thisType); inFunction("var y = 1; this.foo = x; y = this.foo;"); verify("y", CHECKED_UNKNOWN_TYPE); } public void testAssert1() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assert(x); out2 = x;"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssert1a() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assert(x !== null); out2 = x;"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssert2() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); inFunction("goog.asserts.assert(1, x); out1 = x;"); verify("out1", startType); } public void testAssert3() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); assuming("y", startType); inFunction("out1 = x; goog.asserts.assert(x && y); out2 = x; out3 = y;"); verify("out1", startType); verify("out2", OBJECT_TYPE); verify("out3", OBJECT_TYPE); } public void testAssert4() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); assuming("y", startType); inFunction("out1 = x; goog.asserts.assert(x && !y); out2 = x; out3 = y;"); verify("out1", startType); verify("out2", OBJECT_TYPE); verify("out3", NULL_TYPE); } public void testAssert5() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); assuming("y", startType); inFunction("goog.asserts.assert(x || y); out1 = x; out2 = y;"); verify("out1", startType); verify("out2", startType); } public void testAssert6() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x.y", startType); inFunction("out1 = x.y; goog.asserts.assert(x.y); out2 = x.y;"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssert7() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); inFunction("out1 = x; out2 = goog.asserts.assert(x);"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssert8() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); inFunction("out1 = x; out2 = goog.asserts.assert(x != null);"); verify("out1", startType); verify("out2", BOOLEAN_TYPE); } public void testAssert9() { JSType startType = createNullableType(NUMBER_TYPE); assuming("x", startType); inFunction("out1 = x; out2 = goog.asserts.assert(y = x);"); verify("out1", startType); verify("out2", NUMBER_TYPE); } public void testAssert10() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x", startType); assuming("y", startType); inFunction("out1 = x; out2 = goog.asserts.assert(x && y); out3 = x;"); verify("out1", startType); verify("out2", OBJECT_TYPE); verify("out3", OBJECT_TYPE); } public void testAssertNumber() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertNumber(x); out2 = x;"); verify("out1", startType); verify("out2", NUMBER_TYPE); } public void testAssertNumber2() { // Make sure it ignores expressions. JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("goog.asserts.assertNumber(x + x); out1 = x;"); verify("out1", startType); } public void testAssertNumber3() { // Make sure it ignores expressions. JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; out2 = goog.asserts.assertNumber(x + x);"); verify("out1", startType); verify("out2", NUMBER_TYPE); } public void testAssertString() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertString(x); out2 = x;"); verify("out1", startType); verify("out2", STRING_TYPE); } public void testAssertFunction() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertFunction(x); out2 = x;"); verify("out1", startType); verifySubtypeOf("out2", FUNCTION_INSTANCE_TYPE); } public void testAssertObject() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertObject(x); out2 = x;"); verify("out1", startType); verifySubtypeOf("out2", OBJECT_TYPE); } public void testAssertObject2() { JSType startType = createNullableType(ARRAY_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertObject(x); out2 = x;"); verify("out1", startType); verify("out2", ARRAY_TYPE); } public void testAssertObject3() { JSType startType = createNullableType(OBJECT_TYPE); assuming("x.y", startType); inFunction("out1 = x.y; goog.asserts.assertObject(x.y); out2 = x.y;"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssertObject4() { JSType startType = createNullableType(ARRAY_TYPE); assuming("x", startType); inFunction("out1 = x; out2 = goog.asserts.assertObject(x);"); verify("out1", startType); verify("out2", ARRAY_TYPE); } public void testAssertObject5() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction( "out1 = x;" + "out2 = /** @type {!Array} */ (goog.asserts.assertObject(x));"); verify("out1", startType); verify("out2", ARRAY_TYPE); } public void testAssertArray() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertArray(x); out2 = x;"); verify("out1", startType); verifySubtypeOf("out2", ARRAY_TYPE); } public void testAssertInstanceof1() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertInstanceof(x); out2 = x;"); verify("out1", startType); verify("out2", OBJECT_TYPE); } public void testAssertInstanceof2() { JSType startType = createNullableType(ALL_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertInstanceof(x, String); out2 = x;"); verify("out1", startType); verify("out2", STRING_OBJECT_TYPE); } public void testAssertInstanceof3() { JSType startType = registry.getNativeType(UNKNOWN_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertInstanceof(x, String); out2 = x;"); verify("out1", startType); verify("out2", UNKNOWN_TYPE); } public void testAssertInstanceof4() { JSType startType = registry.getNativeType(STRING_OBJECT_TYPE); assuming("x", startType); inFunction("out1 = x; goog.asserts.assertInstanceof(x, Object); out2 = x;"); verify("out1", startType); verify("out2", STRING_OBJECT_TYPE); } public void testAssertInstanceof5() { JSType startType = registry.getNativeType(ALL_TYPE); assuming("x", startType); inFunction( "out1 = x; goog.asserts.assertInstanceof(x, String); var r = x;"); verify("out1", startType); verify("x", STRING_OBJECT_TYPE); } public void testAssertWithIsDefAndNotNull() { JSType startType = createNullableType(NUMBER_TYPE); assuming("x", startType); inFunction( "out1 = x;" + "goog.asserts.assert(goog.isDefAndNotNull(x));" + "out2 = x;"); verify("out1", startType); verify("out2", NUMBER_TYPE); } public void testIsDefAndNoResolvedType() { JSType startType = createUndefinableType(NO_RESOLVED_TYPE); assuming("x", startType); inFunction( "out1 = x;" + "if (goog.isDef(x)) { out2a = x; out2b = x.length; out2c = x; }" + "out3 = x;" + "if (goog.isDef(x)) { out4 = x; }"); verify("out1", startType); verify("out2a", NO_RESOLVED_TYPE); verify("out2b", CHECKED_UNKNOWN_TYPE); verify("out2c", NO_RESOLVED_TYPE); verify("out3", startType); verify("out4", NO_RESOLVED_TYPE); } public void testAssertWithNotIsNull() { JSType startType = createNullableType(NUMBER_TYPE); assuming("x", startType); inFunction( "out1 = x;" + "goog.asserts.assert(!goog.isNull(x));" + "out2 = x;"); verify("out1", startType); verify("out2", NUMBER_TYPE); } public void testReturn1() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("if (x) { return x; }\nx = {};\nreturn x;"); verify("x", OBJECT_TYPE); } public void testReturn2() { assuming("x", createNullableType(NUMBER_TYPE)); inFunction("if (!x) { x = 0; }\nreturn x;"); verify("x", NUMBER_TYPE); } public void testWhile1() { assuming("x", createNullableType(NUMBER_TYPE)); inFunction("while (!x) { if (x == null) { x = 0; } else { x = 1; } }"); verify("x", NUMBER_TYPE); } public void testWhile2() { assuming("x", createNullableType(NUMBER_TYPE)); inFunction("while (!x) { x = {}; }"); verifySubtypeOf("x", createUnionType(OBJECT_TYPE, NUMBER_TYPE)); } public void testDo() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("do { x = 1; } while (!x);"); verify("x", NUMBER_TYPE); } public void testFor1() { assuming("y", NUMBER_TYPE); inFunction("var x = null; var i = null; for (i=y; !i; i=1) { x = 1; }"); verify("x", createNullableType(NUMBER_TYPE)); verify("i", NUMBER_TYPE); } public void testFor2() { assuming("y", OBJECT_TYPE); inFunction("var x = null; var i = null; for (i in y) { x = 1; }"); verify("x", createNullableType(NUMBER_TYPE)); verify("i", createNullableType(STRING_TYPE)); } public void testFor3() { assuming("y", OBJECT_TYPE); inFunction("var x = null; var i = null; for (var i in y) { x = 1; }"); verify("x", createNullableType(NUMBER_TYPE)); verify("i", createNullableType(STRING_TYPE)); } public void testFor4() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = {};\n" + "if (x) { for (var i = 0; i < 10; i++) { break; } y = x; }"); verifySubtypeOf("y", OBJECT_TYPE); } public void testFor5() { assuming("y", templatize( getNativeObjectType(ARRAY_TYPE), ImmutableList.of(getNativeType(NUMBER_TYPE)))); inFunction( "var x = null; for (var i = 0; i < y.length; i++) { x = y[i]; }"); verify("x", createNullableType(NUMBER_TYPE)); verify("i", NUMBER_TYPE); } public void testFor6() { assuming("y", getNativeObjectType(ARRAY_TYPE)); inFunction( "var x = null;" + "for (var i = 0; i < y.length; i++) { " + " if (y[i] == 'z') { x = y[i]; } " + "}"); verify("x", getNativeType(UNKNOWN_TYPE)); verify("i", NUMBER_TYPE); } public void testSwitch1() { assuming("x", NUMBER_TYPE); inFunction("var y = null; switch(x) {\n" + "case 1: y = 1; break;\n" + "case 2: y = {};\n" + "case 3: y = {};\n" + "default: y = 0;}"); verify("y", NUMBER_TYPE); } public void testSwitch2() { assuming("x", ALL_TYPE); inFunction("var y = null; switch (typeof x) {\n" + "case 'string':\n" + " y = x;\n" + " return;" + "default:\n" + " y = 'a';\n" + "}"); verify("y", STRING_TYPE); } public void testSwitch3() { assuming("x", createNullableType(createUnionType(NUMBER_TYPE, STRING_TYPE))); inFunction("var y; var z; switch (typeof x) {\n" + "case 'string':\n" + " y = 1; z = null;\n" + " return;\n" + "case 'number':\n" + " y = x; z = null;\n" + " return;" + "default:\n" + " y = 1; z = x;\n" + "}"); verify("y", NUMBER_TYPE); verify("z", NULL_TYPE); } public void testSwitch4() { assuming("x", ALL_TYPE); inFunction("var y = null; switch (typeof x) {\n" + "case 'string':\n" + "case 'number':\n" + " y = x;\n" + " return;\n" + "default:\n" + " y = 1;\n" + "}\n"); verify("y", createUnionType(NUMBER_TYPE, STRING_TYPE)); } public void testCall1() { assuming("x", createNullableType( registry.createFunctionType(registry.getNativeType(NUMBER_TYPE)))); inFunction("var y = x();"); verify("y", NUMBER_TYPE); } public void testNew1() { assuming("x", createNullableType( registry.getNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE))); inFunction("var y = new x();"); verify("y", UNKNOWN_TYPE); } public void testInnerFunction1() { inFunction("var x = 1; function f() { x = null; };"); verify("x", NUMBER_TYPE); } public void testInnerFunction2() { inFunction("var x = 1; var f = function() { x = null; };"); verify("x", NUMBER_TYPE); } public void testHook() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = x ? x : {};"); verifySubtypeOf("y", OBJECT_TYPE); } public void testThrow() { assuming("x", createNullableType(NUMBER_TYPE)); inFunction("var y = 1;\n" + "if (x == null) { throw new Error('x is null') }\n" + "y = x;"); verify("y", NUMBER_TYPE); } public void testTry1() { assuming("x", NUMBER_TYPE); inFunction("var y = null; try { y = null; } finally { y = x; }"); verify("y", NUMBER_TYPE); } public void testTry2() { assuming("x", NUMBER_TYPE); inFunction("var y = null;\n" + "try { } catch (e) { y = null; } finally { y = x; }"); verify("y", NUMBER_TYPE); } public void testTry3() { assuming("x", NUMBER_TYPE); inFunction("var y = null; try { y = x; } catch (e) { }"); verify("y", NUMBER_TYPE); } public void testCatch1() { inFunction("var y = null; try { foo(); } catch (e) { y = e; }"); verify("y", UNKNOWN_TYPE); } public void testCatch2() { inFunction("var y = null; var e = 3; try { foo(); } catch (e) { y = e; }"); verify("y", UNKNOWN_TYPE); } public void testUnknownType1() { inFunction("var y = 3; y = x;"); verify("y", UNKNOWN_TYPE); } public void testUnknownType2() { assuming("x", ARRAY_TYPE); inFunction("var y = 5; y = x[0];"); verify("y", UNKNOWN_TYPE); } public void testInfiniteLoop1() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("x = {}; while(x != null) { x = {}; }"); } public void testInfiniteLoop2() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("x = {}; do { x = null; } while (x == null);"); } public void testJoin1() { JSType unknownOrNull = createUnionType(NULL_TYPE, UNKNOWN_TYPE); assuming("x", BOOLEAN_TYPE); assuming("unknownOrNull", unknownOrNull); inFunction("var y; if (x) y = unknownOrNull; else y = null;"); verify("y", unknownOrNull); } public void testJoin2() { JSType unknownOrNull = createUnionType(NULL_TYPE, UNKNOWN_TYPE); assuming("x", BOOLEAN_TYPE); assuming("unknownOrNull", unknownOrNull); inFunction("var y; if (x) y = null; else y = unknownOrNull;"); verify("y", unknownOrNull); } public void testArrayLit() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = 3; if (x) { x = [y = x]; }"); verify("x", createUnionType(NULL_TYPE, ARRAY_TYPE)); verify("y", createUnionType(NUMBER_TYPE, OBJECT_TYPE)); } public void testGetElem() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = 3; if (x) { x = x[y = x]; }"); verify("x", UNKNOWN_TYPE); verify("y", createUnionType(NUMBER_TYPE, OBJECT_TYPE)); } public void testEnumRAI1() { JSType enumType = createEnumType("MyEnum", ARRAY_TYPE).getElementsType(); assuming("x", enumType); inFunction("var y = null; if (x) y = x;"); verify("y", createNullableType(enumType)); } public void testEnumRAI2() { JSType enumType = createEnumType("MyEnum", NUMBER_TYPE).getElementsType(); assuming("x", enumType); inFunction("var y = null; if (typeof x == 'number') y = x;"); verify("y", createNullableType(enumType)); } public void testEnumRAI3() { JSType enumType = createEnumType("MyEnum", NUMBER_TYPE).getElementsType(); assuming("x", enumType); inFunction("var y = null; if (x && typeof x == 'number') y = x;"); verify("y", createNullableType(enumType)); } public void testEnumRAI4() { JSType enumType = createEnumType("MyEnum", createUnionType(STRING_TYPE, NUMBER_TYPE)).getElementsType(); assuming("x", enumType); inFunction("var y = null; if (typeof x == 'number') y = x;"); verify("y", createNullableType(NUMBER_TYPE)); } public void testShortCircuitingAnd() { assuming("x", NUMBER_TYPE); inFunction("var y = null; if (x && (y = 3)) { }"); verify("y", createNullableType(NUMBER_TYPE)); } public void testShortCircuitingAnd2() { assuming("x", NUMBER_TYPE); inFunction("var y = null; var z = 4; if (x && (y = 3)) { z = y; }"); verify("z", NUMBER_TYPE); } public void testShortCircuitingOr() { assuming("x", NUMBER_TYPE); inFunction("var y = null; if (x || (y = 3)) { }"); verify("y", createNullableType(NUMBER_TYPE)); } public void testShortCircuitingOr2() { assuming("x", NUMBER_TYPE); inFunction("var y = null; var z = 4; if (x || (y = 3)) { z = y; }"); verify("z", createNullableType(NUMBER_TYPE)); } public void testAssignInCondition() { assuming("x", createNullableType(NUMBER_TYPE)); inFunction("var y; if (!(y = x)) { y = 3; }"); verify("y", NUMBER_TYPE); } public void testInstanceOf1() { assuming("x", OBJECT_TYPE); inFunction("var y = null; if (x instanceof String) y = x;"); verify("y", createNullableType(STRING_OBJECT_TYPE)); } public void testInstanceOf2() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction("var y = 1; if (x instanceof String) y = x;"); verify("y", createUnionType(STRING_OBJECT_TYPE, NUMBER_TYPE)); } public void testInstanceOf3() { assuming("x", createUnionType(STRING_OBJECT_TYPE, NUMBER_OBJECT_TYPE)); inFunction("var y = null; if (x instanceof String) y = x;"); verify("y", createNullableType(STRING_OBJECT_TYPE)); } public void testInstanceOf4() { assuming("x", createUnionType(STRING_OBJECT_TYPE, NUMBER_OBJECT_TYPE)); inFunction("var y = null; if (x instanceof String); else y = x;"); verify("y", createNullableType(NUMBER_OBJECT_TYPE)); } public void testInstanceOf5() { assuming("x", OBJECT_TYPE); inFunction("var y = null; if (x instanceof String); else y = x;"); verify("y", createNullableType(OBJECT_TYPE)); } public void testInstanceOf6() { // Here we are using "instanceof" to restrict the unknown type to // the type of the instance. This has the following problems: // 1) The type may actually be any sub-type // 2) The type may implement any interface // After the instanceof we will require casts for methods that require // sub-type or unrelated interfaces which would not have been required // before. JSType startType = registry.getNativeType(UNKNOWN_TYPE); assuming("x", startType); inFunction("out1 = x; if (x instanceof String) out2 = x;"); verify("out1", startType); verify("out2", STRING_OBJECT_TYPE); } public void testFlattening() { for (int i = 0; i < LinkedFlowScope.MAX_DEPTH + 1; i++) { assuming("s" + i, ALL_TYPE); } assuming("b", JSTypeNative.BOOLEAN_TYPE); StringBuilder body = new StringBuilder(); body.append("if (b) {"); for (int i = 0; i < LinkedFlowScope.MAX_DEPTH + 1; i++) { body.append("s"); body.append(i); body.append(" = 1;\n"); } body.append(" } else { "); for (int i = 0; i < LinkedFlowScope.MAX_DEPTH + 1; i++) { body.append("s"); body.append(i); body.append(" = 'ONE';\n"); } body.append("}"); JSType numberORString = createUnionType(NUMBER_TYPE, STRING_TYPE); inFunction(body.toString()); for (int i = 0; i < LinkedFlowScope.MAX_DEPTH + 1; i++) { verify("s" + i, numberORString); } } public void testUnary() { assuming("x", NUMBER_TYPE); inFunction("var y = +x;"); verify("y", NUMBER_TYPE); inFunction("var z = -x;"); verify("z", NUMBER_TYPE); } public void testAdd1() { assuming("x", NUMBER_TYPE); inFunction("var y = x + 5;"); verify("y", NUMBER_TYPE); } public void testAdd2() { assuming("x", NUMBER_TYPE); inFunction("var y = x + '5';"); verify("y", STRING_TYPE); } public void testAdd3() { assuming("x", NUMBER_TYPE); inFunction("var y = '5' + x;"); verify("y", STRING_TYPE); } public void testAssignAdd() { assuming("x", NUMBER_TYPE); inFunction("x += '5';"); verify("x", STRING_TYPE); } public void testComparison() { inFunction("var x = 'foo'; var y = (x = 3) < 4;"); verify("x", NUMBER_TYPE); inFunction("var x = 'foo'; var y = (x = 3) > 4;"); verify("x", NUMBER_TYPE); inFunction("var x = 'foo'; var y = (x = 3) <= 4;"); verify("x", NUMBER_TYPE); inFunction("var x = 'foo'; var y = (x = 3) >= 4;"); verify("x", NUMBER_TYPE); } public void testThrownExpression() { inFunction("var x = 'foo'; " + "try { throw new Error(x = 3); } catch (ex) {}"); verify("x", NUMBER_TYPE); } public void testObjectLit() { inFunction("var x = {}; var out = x.a;"); verify("out", UNKNOWN_TYPE); // Shouldn't this be 'undefined'? inFunction("var x = {a:1}; var out = x.a;"); verify("out", NUMBER_TYPE); inFunction("var x = {a:1}; var out = x.a; x.a = 'string'; var out2 = x.a;"); verify("out", NUMBER_TYPE); verify("out2", STRING_TYPE); inFunction("var x = { get a() {return 1} }; var out = x.a;"); verify("out", UNKNOWN_TYPE); inFunction( "var x = {" + " /** @return {number} */ get a() {return 1}" + "};" + "var out = x.a;"); verify("out", NUMBER_TYPE); inFunction("var x = { set a(b) {} }; var out = x.a;"); verify("out", UNKNOWN_TYPE); inFunction("var x = { " + "/** @param {number} b */ set a(b) {} };" + "var out = x.a;"); verify("out", NUMBER_TYPE); } public void testCast1() { inFunction("var x = /** @type {Object} */ (this);"); verify("x", createNullableType(OBJECT_TYPE)); } public void testCast2() { inFunction( "/** @return {boolean} */" + "Object.prototype.method = function() { return true; };" + "var x = /** @type {Object} */ (this).method;"); verify( "x", registry.createFunctionType( registry.getNativeObjectType(OBJECT_TYPE), registry.getNativeType(BOOLEAN_TYPE), ImmutableList.of() /* params */)); } public void testBackwardsInferenceCall() { inFunction( "/** @param {{foo: (number|undefined)}} x */" + "function f(x) {}" + "var y = {};" + "f(y);"); assertEquals("{foo: (number|undefined)}", getType("y").toString()); } public void testBackwardsInferenceNew() { inFunction( "/**\n" + " * @constructor\n" + " * @param {{foo: (number|undefined)}} x\n" + " */" + "function F(x) {}" + "var y = {};" + "new F(y);"); assertEquals("{foo: (number|undefined)}", getType("y").toString()); } public void testNoThisInference() { JSType thisType = createNullableType(OBJECT_TYPE); assumingThisType(thisType); inFunction("var out = 3; if (goog.isNull(this)) out = this;"); verify("out", createUnionType(OBJECT_TYPE, NUMBER_TYPE)); } public void testRecordInference() { inFunction( "/** @param {{a: (boolean|undefined)}|{b: (string|undefined)}} x */" + "function f(x) {}" + "var out = {};" + "f(out);"); assertEquals("{a: (boolean|undefined), b: (string|undefined)}", getType("out").toString()); } public void testIssue785() { inFunction("/** @param {string|{prop: (string|undefined)}} x */" + "function f(x) {}" + "var out = {};" + "f(out);"); assertEquals("{prop: (string|undefined)}", getType("out").toString()); } public void testAssertTypeofProp() { assuming("x", createNullableType(OBJECT_TYPE)); inFunction( "goog.asserts.assert(typeof x.prop != 'undefined');" + "out = x.prop;"); verify("out", CHECKED_UNKNOWN_TYPE); } private ObjectType getNativeObjectType(JSTypeNative t) { return registry.getNativeObjectType(t); } private JSType getNativeType(JSTypeNative t) { return registry.getNativeType(t); } private JSType templatize(ObjectType objType, ImmutableList t) { return registry.createTemplatizedType(objType, t); } } closure-compiler-20130227+dfsg1/test/com/google/javascript/jscomp/PeepholeSimplifyRegExpTest.java0000644000175000017500000003220012115204405031115 0ustar apoapo/* * Copyright 2011 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.rhino.Node; public class PeepholeSimplifyRegExpTest extends CompilerTestCase { public final void testWaysOfMatchingEmptyString() { testSame("/(?:)/"); test("/(?:)/i", "/(?:)/"); // We can get rid of i flag when no letters. test("/.{0}/i", "/(?:)/"); test("/[^\\0-\\uffff]{0}/", "/(?:)/"); // Cannot get rid of capturing groups. testSame("/(){0}/"); } public final void testCharsetOptimizations() { testSame("/./"); test("/[\\0-\\uffff]/", "/[\\S\\s]/"); test("/[^\\0-\\uffff]/", "/(?!)/"); test("/[^\\0-\\x40\\x42-\\uffff]/", "/A/"); test("/[0-9a-fA-F]/i", "/[\\da-f]/i"); test("/[0-9a-zA-Z_$]/i", "/[\\w$]/"); test("/[()*+\\-,]/g", "/[(--]/g"); test("/[()*+\\-,z]/g", "/[(--z]/g"); test("/[\\-\\.\\/0]/g", "/[--0]/g"); test("/[\\-\\.\\/0\\n]/g", "/[\\n\\--0]/g"); test("/[\\[\\\\\\]]/g", "/[[-\\]]/g"); test("/[\\[\\\\\\]\\^]/g", "/[[-^]/g"); test("/[\\^`_]/g", "/[\\^-`]/g"); test("/[^\\^`_]/g", "/[^^-`]/g"); test("/^(?=[^a-z])/i", "/^(?=[\\W\\d_])/"); test("/^[^a-z0-9]/i", "/^[\\W_]/"); test("/[0-FA-Z]/", "/[0-Z]/"); test("/[0-9]/", "/\\d/"); test("/[^0-9]/", "/\\D/"); testSame("/\\D/"); test("/[_a-z0-9]/i", "/\\w/"); test("/[0-9_a-z]/i", "/\\w/"); test("/[_a-z0-9]/", "/[\\d_a-z]/"); test("/[_E-Za-f0-9]/i", "/\\w/"); test("/[E-Za-f]/i", "/[a-z]/i"); test("/[_E-Za-f0-9]/", "/[\\dE-Z_a-f]/"); // Test case normalization. // U+00CA and U+00EA are E and e with ^ above test("/[\\u00ca\\u00ea]/", "/[\\xca\\xea]/"); test("/[\\u00ca\\u00ea]/i", "/\\xca/i"); // IE (at least 6, 7, and 8) do not include \xA0 in \s so when an author // explicitly includes it make sure it appears in the output. testSame("/^[\\s\\xa0]*$/"); test("/^(?:\\s|\\xA0)*$/", "/^[\\s\\xa0]*$/"); } public final void testCharsetFixup() { testSame("/[a-z]/i"); // This is the case. The below produces no output in squarefree. // (function () { // // Runs to just before the letter 'a' and starts right after 'z'. // var re = /[^\0-`{-\uffff]/i // for (var i = 0; i < 0x10000; ++i) { // var s = String.fromCharCode(i); // if (re.test(s)) { print(s + ' : ' + s.charCodeAt(0).toString(16)); } // } // })() test("/[^\\0-`{-\\uffff]/i", "/(?!)/"); // This looks a bit odd, but // /[^a-z]/i is the same as all non-word characters, all digits, and _ and // /[\W\d_]/ is the same length. test("/[^a-z]/i", "/[\\W\\d_]/"); } public final void testGroups() { testSame("/foo(bar)baz/"); } public final void testBackReferences() { testSame("/foo(bar)baz(?:\\1|\\x01)boo/"); // But when there is no group to refer to, then the back-reference *is* // the same as an octal escape. test("/foo(?:bar)baz(?:\\1|\\x01)boo/", "/foobarbaz\\x01boo/"); // \\8 is never an octal escape. If there is no 8th group, then it // is the literal character '8' test("/foo(?:bar)baz(?:\\8|8)boo/", "/foobarbaz8boo/"); // \10 can be a capturing group. test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)" + "\\12\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/", "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)" // \\12 does not match any group, so is treated as group 1 followed // by literal 2. + "\\1(?:2)\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/"); // But \1 should not be emitted followed by a digit un-parenthesized. test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(?:\\1)0/", "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)\\1(?:0)/"); // \012 is never treated as a group even when there are 12 groups. test("/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(C?)" + "\\012\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/", "/(1?)(2?)(3?)(4?)(5?)(6?)(7?)(8?)(9?)(A?)(B?)(C?)" + "\\n\\11\\10\\9\\8\\7\\6\\5\\4\\3\\2\\1\\0/"); } public final void testSingleCharAlterations() { test("/a|B|c|D/i", "/[a-d]/i"); test("/a|B|c|D/", "/[BDac]/"); test("/a|[Bc]|D/", "/[BDac]/"); test("/[aB]|[cD]/", "/[BDac]/"); test("/a|B|c|D|a|B/i", "/[a-d]/i"); // Duplicates. test("/a|A|/i", "/a?/i"); } public final void testAlterations() { testSame("/foo|bar/"); test("/Foo|BAR/i", "/foo|bar/i"); test("/Foo||BAR/", "/Foo||BAR/"); test("/Foo|BAR|/", "/Foo|BAR|/"); } public final void testNestedAlterations() { test("/foo|bar|(?:baz|boo)|far/", "/foo|bar|baz|boo|far/"); } public final void testEscapeSequencesAndNonLatinChars() { test("/\u1234/i", "/\\u1234/"); testSame("/\\u1234/"); test("/\u00A0/", "/\\xa0/"); test("/\\u00A0/", "/\\xa0/"); test("/\\u00a0/", "/\\xa0/"); } public final void testAnchors() { // m changes the meaning of anchors which is useless if there are none. testSame("/foo(?!$)/gm"); test("/./m", "/./"); test("/\\^/m", "/\\^/"); test("/[\\^]/m", "/\\^/"); testSame("/(^|foo)bar/"); testSame("/^.|.$/gm"); test("/foo(?=)$/m", "/foo$/m"); // We can get rid of the g when there are no capturing groups and the // pattern is fully anchored. test("/^foo$/g", "/^foo$/"); } public final void testRepetitions() { testSame("/a*/"); testSame("/a+/"); testSame("/a+?/"); testSame("/a?/"); testSame("/a{6}/"); testSame("/a{4,}/"); test("/a{3,}/", "/aaa+/"); testSame("/a{4,6}/"); testSame("/a{4,6}?/"); test("/(?:a?)?/", "/a?/"); test("/(?:a?)*/", "/a*/"); test("/(?:a*)?/", "/a*/"); test("/a(?:a*)?/", "/a+/"); test("/(?:a{2,3}){3,4}/", "/a{6,12}/"); test("/a{2,3}a{3,4}/", "/a{5,7}/"); testSame("/a{5,7}b{5,6}/"); test("/a{2,3}b{3,4}/", "/aaa?bbbb?/"); test("/a{3}b{3,4}/", "/aaabbbb?/"); testSame("/[a-z]{1,2}/"); test("/\\d{1,2}/", "/\\d\\d?/"); test("/a*a*/", "/a*/"); test("/a+a+/", "/aa+/"); test("/a+a*/", "/a+/"); // We don't conflate literal curly brackets with repetitions. testSame("/a\\{3,1}/"); test("/a(?:{3,1})/", "/a\\{3,1}/"); test("/a{3\\,1}/", "/a\\{3,1}/"); testSame("/a\\{3}/"); testSame("/a\\{3,}/"); testSame("/a\\{1,3}/"); // We don't over-escape curly brackets. testSame("/a{/"); testSame("/a{}/"); testSame("/a{x}/"); testSame("/a{-1}/"); testSame("/a{,3}/"); testSame("/{{[a-z]+}}/"); testSame("/{\\{0}}/"); testSame("/{\\{0?}}/"); } public final void testMoreCharsets() { test("var a = /[\\x00\\x22\\x26\\x27\\x3c\\x3e]/g", "var a = /[\\0\"&'<>]/g"); test("var b = /[\\x00\\x22\\x27\\x3c\\x3e]/g", "var b = /[\\0\"'<>]/g"); test("var c = /[\\x00\\x09-\\x0d \\x22\\x26\\x27\\x2d\\/\\x3c-\\x3e`" + "\\x85\\xa0\\u2028\\u2029]/g", "var c = /[\\0\\t-\\r \"&'/<->`\\x85\\xa0\\u2028\\u2029-]/g"); test("var d = /[\\x00\\x09-\\x0d \\x22\\x27\\x2d\\/\\x3c-\\x3e`" + "\\x85\\xa0\\u2028\\u2029]/g", "var d = /[\\0\\t-\\r \"'/<->`\\x85\\xa0\\u2028\\u2029-]/g"); test("var e = /[\\x00\\x08-\\x0d\\x22\\x26\\x27\\/\\x3c-\\x3e\\\\" + "\\x85\\u2028\\u2029]/g", "var e = /[\\0\\b-\\r\"&'/<->\\\\\\x85\\u2028\\u2029]/g"); test("var f = /[\\x00\\x08-\\x0d\\x22\\x24\\x26-\\/\\x3a\\x3c-\\x3f" + "\\x5b-\\x5e\\x7b-\\x7d\\x85\\u2028\\u2029]/g", "var f = /[\\0\\b-\\r\"$&-/:<-?[-^{-}\\x85\\u2028\\u2029]/g"); test("var g = /[\\x00\\x08-\\x0d\\x22\\x26-\\x2a\\/\\x3a-\\x3e@\\\\" + "\\x7b\\x7d\\x85\\xa0\\u2028\\u2029]/g", "var g = /[\\0\\b-\\r\"&-*/:->@\\\\{}\\x85\\xa0\\u2028\\u2029]/g"); test("var h = /^(?!-*(?:expression|(?:moz-)?binding))(?:[.#]?-?" + "(?:[_a-z0-9][_a-z0-9-]*)(?:-[_a-z][_a-z0-9-]*)*-?|-?" + "(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9])(?:[a-z]{1,2}|%)?|!important|)$/i", "var h = /^(?!-*(?:expression|(?:moz-)?binding))(?:[#.]?-?" + "\\w[\\w-]*(?:-[_a-z][\\w-]*)*-?|-?" + "(?:\\d+(?:\\.\\d*)?|\\.\\d)(?:[a-z]{1,2}|%)?|!important|)$/i"); test("var i = /^(?:(?:https?|mailto):|[^&:\\/?#]*(?:[\\/?#]|$))/i", "var i = /^(?:(?:https?|mailto):|[^#&/:?]*(?:[#/?]|$))/i"); test("var j = /^(?!style|on|action|archive|background|cite|classid" + "|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*" + "|dir=(?:ltr|rtl))$/i", "var j = /^(?!style|on|action|archive|background|cite|classid" + "|codebase|data|dsync|href|longdesc|src|usemap)(?:[\\w$:-]*" + "|dir=(?:ltr|rtl))$/i"); test("var k = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i", "var k = /^(?!script|style|title|textarea|xmp|no)[\\w$:-]*$/i"); test("var l = /<(?:!|\\/?[a-z])(?:[^>'\"]|\"[^\"]*\"|'[^']*')*>/gi", "var l = /<(?:!|\\/?[a-z])(?:[^\"'>]|\"[^\"]*\"|'[^']*')*>/gi"); } public final void testMoreRegularExpression() { testSame("/\"/"); testSame("/'/"); test("/(?:[^<\\/\"'\\s\\\\]|<(?!\\/script))+/i", "/(?:[^\\s\"'/<\\\\]|<(?!\\/script))+/i"); testSame("/-->/"); testSame("/ closure-compiler-20130227+dfsg1/build.xml0000644000175000017500000004446712115204405016212 0ustar apoapo closure-compiler-20130227+dfsg1/README0000644000175000017500000001546512115204405015245 0ustar apoapo/* * 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. */ // // Contents // The Closure Compiler performs checking, instrumentation, and optimizations on JavaScript code. The purpose of this README is to explain how to build and run the Closure Compiler. The Closure Compiler requires Java 6 or higher. http://www.java.com/ // // Building The Closure Compiler // There are three ways to get a Closure Compiler executable. 1) Use one we built for you. Pre-built Closure binaries can be found at http://code.google.com/p/closure-compiler/downloads/list 2) Check out the source and build it with Apache Ant. First, check out the full source tree of the Closure Compiler. There are instructions on how to do this at the project site. http://code.google.com/p/closure-compiler/source/checkout Apache Ant is a cross-platform build tool. http://ant.apache.org/ At the root of the source tree, there is an Ant file named build.xml. To use it, navigate to the same directory and type the command ant jar This will produce a jar file called "build/compiler.jar". 3) Check out the source and build it with Eclipse. Eclipse is a cross-platform IDE. http://www.eclipse.org/ Under Eclipse's File menu, click "New > Project ..." and create a "Java Project." You will see an options screen. Give the project a name, select "Create project from existing source," and choose the root of the checked-out source tree as the existing directory. Verify that you are using JRE version 6 or higher. Eclipse can use the build.xml file to discover rules. When you navigate to the build.xml file, you will see all the build rules in the "Outline" pane. Run the "jar" rule to build the compiler in build/compiler.jar. // // Running The Closure Compiler // Once you have the jar binary, running the Closure Compiler is straightforward. On the command line, type java -jar compiler.jar This starts the compiler in interactive mode. Type var x = 17 + 25; then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) and "Enter" again. The Compiler will respond: var x=42; The Closure Compiler has many options for reading input from a file, writing output to a file, checking your code, and running optimizations. To learn more, type java -jar compiler.jar --help You can read more detailed documentation about the many flags at http://code.google.com/closure/compiler/docs/gettingstarted_app.html // // Compiling Multiple Scripts // If you have multiple scripts, you should compile them all together with one compile command. java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js The Closure Compiler will concatenate the files in the order they're passed at the command line. If you need to compile many, many scripts together, you may start to run into problems with managing dependencies between scripts. You should check out the Closure Library. It contains functions for enforcing dependencies between scripts, and a tool called calcdeps.py that knows how to give scripts to the Closure Compiler in the right order. http://code.google.com/p/closure-library/ // // Licensing // Unless otherwise stated, all source files are licensed under the Apache License, Version 2.0. ----- Code under: src/com/google/javascript/rhino test/com/google/javascript/rhino URL: http://www.mozilla.org/rhino Version: 1.5R3, with heavy modifications License: Netscape Public License and MPL / GPL dual license Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an implementation of JavaScript for the JVM. The JavaScript parser and the parse tree data structures were extracted and modified significantly for use by Google's JavaScript compiler. Local Modifications: The packages have been renamespaced. All code not relevant to parsing has been removed. A JsDoc parser and static typing system have been added. ----- Code in: lib/rhino Rhino URL: http://www.mozilla.org/rhino Version: Trunk License: Netscape Public License and MPL / GPL dual license Description: Mozilla Rhino is an implementation of JavaScript for the JVM. Local Modifications: Minor changes to parsing JSDoc that usually get pushed up-stream to Rhino trunk. ----- Code in: lib/args4j.jar Args4j URL: https://args4j.dev.java.net/ Version: 2.0.16 License: MIT Description: args4j is a small Java class library that makes it easy to parse command line options/arguments in your CUI application. Local Modifications: None. ----- Code in: lib/guava.jar Guava Libraries URL: http://code.google.com/p/guava-libraries/ Version: 14.0 License: Apache License 2.0 Description: Google's core Java libraries. Local Modifications: None. ----- Code in: lib/jsr305.jar Annotations for software defect detection URL: http://code.google.com/p/jsr-305/ Version: svn revision 47 License: BSD License Description: Annotations for software defect detection. Local Modifications: None. ----- Code in: lib/jarjar.jar Jar Jar Links URL: http://jarjar.googlecode.com/ Version: 1.1 License: Apache License 2.0 Description: A utility for repackaging Java libraries. Local Modifications: None. ---- Code in: lib/junit.jar JUnit URL: http://sourceforge.net/projects/junit/ Version: 4.10 License: Common Public License 1.0 Description: A framework for writing and running automated tests in Java. Local Modifications: None. --- Code in: lib/protobuf-java.jar Protocol Buffers URL: http://code.google.com/p/protobuf/ Version: 2.4.1 License: New BSD License Description: Supporting libraries for protocol buffers, an encoding of structured data. Local Modifications: None --- Code in: lib/ant.jar lib/ant-launcher.jar URL: http://ant.apache.org/bindownload.cgi Version: 1.8.1 License: Apache License 2.0 Description: Ant is a Java based build tool. In theory it is kind of like "make" without make's wrinkles and with the full portability of pure java code. Local Modifications: None --- Code in: lib/json.jar URL: http://json.org/java/index.html Version: JSON version 20090211 License: MIT license Description: JSON is a set of java files for use in transmitting data in JSON format. Local Modifications: None --- Code in: tools/maven-ant-tasks-2.1.3.jar URL: http://maven.apache.org Version 2.1.3 License: Apache License 2.0 Description: Maven Ant tasks are used to manage dependencies and to install/deploy to maven repositories. Local Modifications: None closure-compiler-20130227+dfsg1/src/0000755000175000017500000000000012115204405015141 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/0000755000175000017500000000000012115204405015717 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/0000755000175000017500000000000012115204405017173 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/debugging/0000755000175000017500000000000012115204405021126 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/0000755000175000017500000000000012115204405023124 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapConsumerFactory.java0000644000175000017500000000532112115204405030732 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import org.json.JSONException; import org.json.JSONObject; /** * Detect and parse the provided source map. * @author johnlenz@google.com (John Lenz) */ public class SourceMapConsumerFactory { /** not constructible */ private SourceMapConsumerFactory() {} /** * @param contents The string representing the source map file contents. * @return The parsed source map. * @throws SourceMapParseException */ public static SourceMapping parse(String contents) throws SourceMapParseException { return parse(contents, null); } /** * @param contents The string representing the source map file contents. * @param supplier A supplier for any referenced maps. * @return The parsed source map. * @throws SourceMapParseException */ public static SourceMapping parse(String contents, SourceMapSupplier supplier) throws SourceMapParseException { // Version 1, starts with a magic string if (contents.startsWith("/** Begin line maps. **/")) { SourceMapConsumerV1 consumer = new SourceMapConsumerV1(); consumer.parse(contents); return consumer; } else if (contents.startsWith("{")){ try { // Revision 2 and 3, are JSON Objects JSONObject sourceMapRoot = new JSONObject(contents); // Check basic assertions about the format. int version = sourceMapRoot.getInt("version"); switch (version) { case 2: { SourceMapConsumerV2 consumer = new SourceMapConsumerV2(); consumer.parse(sourceMapRoot); return consumer; } case 3: { SourceMapConsumerV3 consumer = new SourceMapConsumerV3(); consumer.parse(sourceMapRoot, supplier); return consumer; } default: throw new SourceMapParseException( "Unknown source map version:" + version); } } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } throw new SourceMapParseException("unable to detect source map format"); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/FilePosition.java0000644000175000017500000000254112115204405026375 0ustar apoapo/* * 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.debugging.sourcemap; /** * Represents a position in a source file. * */ public class FilePosition { private final int line; private final int column; public FilePosition(int line, int column) { this.line = line; this.column = column; } /** * Returns the line number of this position. * Note: The v1 and v2 source maps use a line number with the first line * being 1, whereas the v3 source map corrects this and uses a first line * number of 0 to be consistent with the column representation. */ public int getLine() { return line; } /** * @return the character index on the line * of this position, with the first column being 0. */ public int getColumn() { return column; } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapGeneratorV3.java0000644000175000017500000005625012115204405027755 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.SourceMapConsumerV3.EntryVisitor; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes. * * @author johnlenz@google.com (John Lenz) */ public class SourceMapGeneratorV3 implements SourceMapGenerator { private static final int UNMAPPED = -1; /** * A pre-order traversal ordered list of mappings stored in this map. */ private List mappings = Lists.newArrayList(); /** * A map of source names to source name index */ private LinkedHashMap sourceFileMap = Maps.newLinkedHashMap(); /** * A map of source names to source name index */ private LinkedHashMap originalNameMap = Maps.newLinkedHashMap(); /** * Cache of the last mappings source name. */ private String lastSourceFile = null; /** * Cache of the last mappings source name index. */ private int lastSourceFileIndex = -1; /** * For validation store the last mapping added. */ private Mapping lastMapping; /** * The position that the current source map is offset in the * buffer being used to generated the compiled source file. */ private FilePosition offsetPosition = new FilePosition(0, 0); /** * The position that the current source map is offset in the * generated the compiled source file by the addition of a * an output wrapper prefix. */ private FilePosition prefixPosition = new FilePosition(0, 0); /** * {@inheritDoc} */ @Override public void reset() { mappings.clear(); lastMapping = null; sourceFileMap.clear(); originalNameMap.clear(); lastSourceFile = null; lastSourceFileIndex = -1; offsetPosition = new FilePosition(0, 0); prefixPosition = new FilePosition(0, 0); } /** * @param validate Whether to perform (potentially costly) validation on the * generated source map. */ @Override public void validate(boolean validate) { // Nothing currently. } /** * Sets the prefix used for wrapping the generated source file before * it is written. This ensures that the source map is adjusted for the * change in character offsets. * * @param prefix The prefix that is added before the generated source code. */ @Override public void setWrapperPrefix(String prefix) { // Determine the current line and character position. int prefixLine = 0; int prefixIndex = 0; for (int i = 0; i < prefix.length(); ++i) { if (prefix.charAt(i) == '\n') { prefixLine++; prefixIndex = 0; } else { prefixIndex++; } } prefixPosition = new FilePosition(prefixLine, prefixIndex); } /** * Sets the source code that exists in the buffer for which the * generated code is being generated. This ensures that the source map * accurately reflects the fact that the source is being appended to * an existing buffer and as such, does not start at line 0, position 0 * but rather some other line and position. * * @param offsetLine The index of the current line being printed. * @param offsetIndex The column index of the current character being printed. */ @Override public void setStartingPosition(int offsetLine, int offsetIndex) { Preconditions.checkState(offsetLine >= 0); Preconditions.checkState(offsetIndex >= 0); offsetPosition = new FilePosition(offsetLine, offsetIndex); } /** * Adds a mapping for the given node. Mappings must be added in order. * @param startPosition The position on the starting line * @param endPosition The position on the ending line. */ @Override public void addMapping( String sourceName, @Nullable String symbolName, FilePosition sourceStartPosition, FilePosition startPosition, FilePosition endPosition) { // Don't bother if there is not sufficient information to be useful. if (sourceName == null || sourceStartPosition.getLine() < 0) { return; } FilePosition adjustedStart = startPosition; FilePosition adjustedEnd = endPosition; if (offsetPosition.getLine() != 0 || offsetPosition.getColumn() != 0) { // If the mapping is found on the first line, we need to offset // its character position by the number of characters found on // the *last* line of the source file to which the code is // being generated. int offsetLine = offsetPosition.getLine(); int startOffsetPosition = offsetPosition.getColumn(); int endOffsetPosition = offsetPosition.getColumn(); if (startPosition.getLine() > 0) { startOffsetPosition = 0; } if (endPosition.getLine() > 0) { endOffsetPosition = 0; } adjustedStart = new FilePosition( startPosition.getLine() + offsetLine, startPosition.getColumn() + startOffsetPosition); adjustedEnd = new FilePosition( endPosition.getLine() + offsetLine, endPosition.getColumn() + endOffsetPosition); } // Create the new mapping. Mapping mapping = new Mapping(); mapping.sourceFile = sourceName; mapping.originalPosition = sourceStartPosition; mapping.originalName = symbolName; mapping.startPosition = adjustedStart; mapping.endPosition = adjustedEnd; // Validate the mappings are in a proper order. if (lastMapping != null) { int lastLine = lastMapping.startPosition.getLine(); int lastColumn = lastMapping.startPosition.getColumn(); int nextLine = mapping.startPosition.getLine(); int nextColumn = mapping.startPosition.getColumn(); Preconditions.checkState(nextLine > lastLine || (nextLine == lastLine && nextColumn >= lastColumn), "Incorrect source mappings order, previous : (%s,%s)\n" + "new : (%s,%s)\nnode : %s", lastLine, lastColumn, nextLine, nextColumn); } lastMapping = mapping; mappings.add(mapping); } class ConsumerEntryVisitor implements EntryVisitor { @Override public void visit( String sourceName, String symbolName, FilePosition sourceStartPosition, FilePosition startPosition, FilePosition endPosition) { addMapping(sourceName, symbolName, sourceStartPosition, startPosition, endPosition); } } public void mergeMapSection(int line, int column, String mapSectionContents) throws SourceMapParseException { setStartingPosition(line, column); SourceMapConsumerV3 section = new SourceMapConsumerV3(); section.parse(mapSectionContents); section.visitMappings(new ConsumerEntryVisitor()); } /** * Writes out the source map in the following format (line numbers are for * reference only and are not part of the format): * * 1. { * 2. version: 3, * 3. file: "out.js", * 4. lineCount: 2, * 5. sourceRoot: "", * 6. sources: ["foo.js", "bar.js"], * 7. names: ["src", "maps", "are", "fun"], * 8. mappings: "a;;abcde,abcd,a;" * 9. } * * Line 1: The entire file is a single JSON object * Line 2: File revision (always the first entry in the object) * Line 3: The name of the file that this source map is associated with. * Line 4: The number of lines represented in the source map. * Line 5: An optional source root, useful for relocating source files on a * server or removing repeated prefix values in the "sources" entry. * Line 6: A list of sources used by the "mappings" entry relative to the * sourceRoot. * Line 7: A list of symbol names used by the "mapping" entry. This list * may be incomplete. * Line 8: The mappings field. */ @Override public void appendTo(Appendable out, String name) throws IOException { int maxLine = prepMappings(); // Add the header fields. out.append("{\n"); appendFirstField(out, "version", "3"); appendField(out, "file", escapeString(name)); appendField(out, "lineCount", String.valueOf(maxLine + 1)); // Add the mappings themselves. appendFieldStart(out, "mappings"); // out.append("["); (new LineMapper(out)).appendLineMappings(); // out.append("]"); appendFieldEnd(out); // Files names appendFieldStart(out, "sources"); out.append("["); addSourceNameMap(out); out.append("]"); appendFieldEnd(out); // Files names appendFieldStart(out, "names"); out.append("["); addSymbolNameMap(out); out.append("]"); appendFieldEnd(out); out.append("\n}\n"); } /** * Writes the source name map to 'out'. */ private void addSourceNameMap(Appendable out) throws IOException { addNameMap(out, sourceFileMap); } /** * Writes the source name map to 'out'. */ private void addSymbolNameMap(Appendable out) throws IOException { addNameMap(out, originalNameMap); } private void addNameMap(Appendable out, Map map) throws IOException { int i = 0; for (Entry entry : map.entrySet()) { String key = entry.getKey(); if (i != 0) { out.append(","); } out.append(escapeString(key)); i++; } } /** * Escapes the given string for JSON. */ private static String escapeString(String value) { return Util.escapeString(value); } // Source map field helpers. private static void appendFirstField( Appendable out, String name, CharSequence value) throws IOException { out.append("\""); out.append(name); out.append("\""); out.append(":"); out.append(value); } private static void appendField( Appendable out, String name, CharSequence value) throws IOException { out.append(",\n"); out.append("\""); out.append(name); out.append("\""); out.append(":"); out.append(value); } private static void appendFieldStart(Appendable out, String name) throws IOException { appendField(out, name, ""); } @SuppressWarnings("unused") private static void appendFieldEnd(Appendable out) throws IOException { } /** * Assigns sequential ids to used mappings, and returns the last line mapped. */ private int prepMappings() throws IOException { // Mark any unused mappings. (new MappingTraversal()).traverse(new UsedMappingCheck()); // Renumber used mappings and keep track of the last line. int id = 0; int maxLine = 0; for (Mapping m : mappings) { if (m.used) { m.id = id++; int endPositionLine = m.endPosition.getLine(); maxLine = Math.max(maxLine, endPositionLine); } } // Adjust for the prefix. return maxLine + prefixPosition.getLine(); } /** * A mapping from a given position in an input source file to a given position * in the generated code. */ static class Mapping { /** * A unique ID for this mapping for record keeping purposes. */ int id = UNMAPPED; /** * The source file index. */ String sourceFile; /** * The position of the code in the input source file. Both * the line number and the character index are indexed by * 1 for legacy reasons via the Rhino Node class. */ FilePosition originalPosition; /** * The starting position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition startPosition; /** * The ending position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition endPosition; /** * The original name of the token found at the position * represented by this mapping (if any). */ String originalName; /** * Whether the mapping is actually used by the source map. */ boolean used = false; } /** * Mark any visited mapping as "used". */ private class UsedMappingCheck implements MappingVisitor { /** * @throws IOException */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { if (m != null) { m.used = true; } } } private interface MappingVisitor { /** * @param m The mapping for the current code segment. null if the segment * is unmapped. * @param line The starting line for this code segment. * @param col The starting column for this code segment. * @param endLine The ending line * @param endCol The ending column * @throws IOException */ void visit(Mapping m, int line, int col, int endLine, int endCol) throws IOException; } /** * Walk the mappings and visit each segment of the mappings, unmapped * segments are visited with a null mapping, unused mapping are not visited. */ private class MappingTraversal { // The last line and column written private int line; private int col; MappingTraversal() { } // Append the line mapping entries. void traverse(MappingVisitor v) throws IOException { // The mapping list is ordered as a pre-order traversal. The mapping // positions give us enough information to rebuild the stack and this // allows the building of the source map in O(n) time. Deque stack = new ArrayDeque(); for (Mapping m : mappings) { // Find the closest ancestor of the current mapping: // An overlapping mapping is an ancestor of the current mapping, any // non-overlapping mappings are siblings (or cousins) and must be // closed in the reverse order of when they encountered. while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { Mapping previous = stack.pop(); maybeVisit(v, previous); } // Any gaps between the current line position and the start of the // current mapping belong to the parent. Mapping parent = stack.peek(); maybeVisitParent(v, parent, m); stack.push(m); } // There are no more children to be had, simply close the remaining // mappings in the reverse order of when they encountered. while (!stack.isEmpty()) { Mapping m = stack.pop(); maybeVisit(v, m); } } /** * @return The line adjusted for the prefix position. */ private int getAdjustedLine(FilePosition p) { return p.getLine() + prefixPosition.getLine(); } /** * @return The column adjusted for the prefix position. */ private int getAdjustedCol(FilePosition p) { int rawLine = p.getLine(); int rawCol = p.getColumn(); // Only the first line needs the character position adjusted. return (rawLine != 0) ? rawCol : rawCol + prefixPosition.getColumn(); } /** * @return Whether m1 ends before m2 starts. */ private boolean isOverlapped(Mapping m1, Mapping m2) { // No need to use adjusted values here, relative positions are sufficient. int l1 = m1.endPosition.getLine(); int l2 = m2.startPosition.getLine(); int c1 = m1.endPosition.getColumn(); int c2 = m2.startPosition.getColumn(); return (l1 == l2 && c1 >= c2) || l1 > l2; } /** * Write any needed entries from the current position to the end of the * provided mapping. */ private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.endPosition); int nextCol = getAdjustedCol(m.endPosition); // If this anything remaining in this mapping beyond the // current line and column position, write it out now. if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, m, nextLine, nextCol); } } /** * Write any needed entries to complete the provided mapping. */ private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.startPosition); int nextCol = getAdjustedCol(m.startPosition); // If the previous value is null, no mapping exists. Preconditions.checkState(line < nextLine || col <= nextCol); if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, parent, nextLine, nextCol); } } /** * Write any entries needed between the current position the next position * and update the current position. */ private void visit(MappingVisitor v, Mapping m, int nextLine, int nextCol) throws IOException { Preconditions.checkState(line <= nextLine); Preconditions.checkState(line < nextLine || col < nextCol); if (line == nextLine && col == nextCol) { // Nothing to do. Preconditions.checkState(false); return; } v.visit(m, line, col, nextLine, nextCol); line = nextLine; col = nextCol; } } /** * Appends the index source map to the given buffer. * * @param out The stream to which the map will be appended. * @param name The name of the generated source file that this source map * represents. * @param sections An ordered list of map sections to include in the index. * @throws IOException */ @Override public void appendIndexMapTo( Appendable out, String name, List sections) throws IOException { // Add the header fields. out.append("{\n"); appendFirstField(out, "version", "3"); appendField(out, "file", escapeString(name)); // Add the line character maps. appendFieldStart(out, "sections"); out.append("[\n"); boolean first = true; for (SourceMapSection section : sections) { if (first) { first = false; } else { out.append(",\n"); } out.append("{\n"); appendFirstField(out, "offset", offsetValue(section.getLine(), section.getColumn())); if (section.getSectionType() == SourceMapSection.SectionType.URL) { appendField(out, "url", escapeString(section.getSectionValue())); } else if (section.getSectionType() == SourceMapSection.SectionType.MAP) { appendField(out, "map", section.getSectionValue()); } else { throw new IOException("Unexpected section type"); } out.append("\n}"); } out.append("\n]"); appendFieldEnd(out); out.append("\n}\n"); } private CharSequence offsetValue(int line, int column) throws IOException { StringBuilder out = new StringBuilder(); out.append("{\n"); appendFirstField(out, "line", String.valueOf(line)); appendField(out, "column", String.valueOf(column)); out.append("\n}"); return out; } private int getSourceId(String sourceName) { if (sourceName != lastSourceFile) { lastSourceFile = sourceName; Integer index = sourceFileMap.get(sourceName); if (index != null) { lastSourceFileIndex = index; } else { lastSourceFileIndex = sourceFileMap.size(); sourceFileMap.put(sourceName, lastSourceFileIndex); } } return lastSourceFileIndex; } private int getNameId(String symbolName) { int originalNameIndex; Integer index = originalNameMap.get(symbolName); if (index != null) { originalNameIndex = index; } else { originalNameIndex = originalNameMap.size(); originalNameMap.put(symbolName, originalNameIndex); } return originalNameIndex; } private class LineMapper implements MappingVisitor { // The destination. private final Appendable out; private int previousLine = -1; private int previousColumn = 0; // Previous values used for storing relative ids. private int previousSourceFileId; private int previousSourceLine; private int previousSourceColumn; private int previousNameId; LineMapper(Appendable out) { this.out = out; } /** * As each segment is visited write out the appropriate line mapping. */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { if (previousLine != line) { previousColumn = 0; } if (line != nextLine || col != nextCol) { if (previousLine == line) { // not the first entry for the line out.append(','); } writeEntry(m, col); previousLine = line; previousColumn = col; } for (int i = line; i <= nextLine; i++) { if (i == nextLine) { break; } closeLine(false); openLine(false); } } /** * Writes an entry for the given column (of the generated text) and * associated mapping. * The values are stored as relative to the last seen values for each * field and encoded as Base64VLQs. */ void writeEntry(Mapping m, int column) throws IOException { // The relative generated column number Base64VLQ.encode(out, column - previousColumn); previousColumn = column; if (m != null) { // The relative source file id int sourceId = getSourceId(m.sourceFile); Base64VLQ.encode(out, sourceId - previousSourceFileId); previousSourceFileId = sourceId; // The relative source file line and column int srcline = m.originalPosition.getLine(); int srcColumn = m.originalPosition.getColumn(); Base64VLQ.encode(out, srcline - previousSourceLine); previousSourceLine = srcline; Base64VLQ.encode(out, srcColumn - previousSourceColumn); previousSourceColumn = srcColumn; if (m.originalName != null) { // The relative id for the associated symbol name int nameId = getNameId(m.originalName); Base64VLQ.encode(out, (nameId - previousNameId)); previousNameId = nameId; } } } // Append the line mapping entries. void appendLineMappings() throws IOException { // Start the first line. openLine(true); (new MappingTraversal()).traverse(this); // And close the final line. closeLine(true); } /** * Begin the entry for a new line. */ private void openLine(boolean firstEntry) throws IOException { if (firstEntry) { out.append('\"'); } } /** * End the entry for a line. */ private void closeLine(boolean finalEntry) throws IOException { out.append(';'); if (finalEntry) { out.append('\"'); } } } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapConsumerV3.java0000644000175000017500000005264112115204405027622 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.debugging.sourcemap.Base64VLQ.CharIterator; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping.Builder; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Class for parsing version 3 of the SourceMap format, as produced by the * Closure Compiler, etc. * http://code.google.com/p/closure-compiler/wiki/SourceMaps * @author johnlenz@google.com (John Lenz) */ public class SourceMapConsumerV3 implements SourceMapConsumer, SourceMappingReversable { static final int UNMAPPED = -1; private String[] sources; private String[] names; private int lineCount; // Slots in the lines list will be null if the line does not have any entries. private ArrayList> lines = null; /** originalFile path ==> original line ==> target mappings */ private Map>> reverseSourceMapping; public SourceMapConsumerV3() { } static class DefaultSourceMapSupplier implements SourceMapSupplier { @Override public String getSourceMap(String url) { return null; } } /** * Parses the given contents containing a source map. */ @Override public void parse(String contents) throws SourceMapParseException { parse(contents, null); } /** * Parses the given contents containing a source map. */ public void parse(String contents, SourceMapSupplier sectionSupplier) throws SourceMapParseException { try { JSONObject sourceMapRoot = new JSONObject(contents); parse(sourceMapRoot, sectionSupplier); } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } /** * Parses the given contents containing a source map. */ public void parse(JSONObject sourceMapRoot) throws SourceMapParseException { parse(sourceMapRoot, null); } /** * Parses the given contents containing a source map. */ public void parse(JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) throws SourceMapParseException { try { // Check basic assertions about the format. int version = sourceMapRoot.getInt("version"); if (version != 3) { throw new SourceMapParseException("Unknown version: " + version); } String file = sourceMapRoot.getString("file"); if (file.isEmpty()) { throw new SourceMapParseException("File entry is missing or empty"); } if (sourceMapRoot.has("sections")) { // Looks like a index map, try to parse it that way. parseMetaMap(sourceMapRoot, sectionSupplier); return; } lineCount = sourceMapRoot.getInt("lineCount"); String lineMap = sourceMapRoot.getString("mappings"); sources = getJavaStringArray(sourceMapRoot.getJSONArray("sources")); names = getJavaStringArray(sourceMapRoot.getJSONArray("names")); lines = Lists.newArrayListWithCapacity(lineCount); new MappingBuilder(lineMap).build(); } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } /** * @param sourceMapRoot * @throws SourceMapParseException */ private void parseMetaMap( JSONObject sourceMapRoot, SourceMapSupplier sectionSupplier) throws SourceMapParseException { if (sectionSupplier == null) { sectionSupplier = new DefaultSourceMapSupplier(); } try { // Check basic assertions about the format. int version = sourceMapRoot.getInt("version"); if (version != 3) { throw new SourceMapParseException("Unknown version: " + version); } String file = sourceMapRoot.getString("file"); if (file.isEmpty()) { throw new SourceMapParseException("File entry is missing or empty"); } if (sourceMapRoot.has("lineCount") || sourceMapRoot.has("mappings") || sourceMapRoot.has("sources") || sourceMapRoot.has("names")) { throw new SourceMapParseException("Invalid map format"); } SourceMapGeneratorV3 generator = new SourceMapGeneratorV3(); JSONArray sections = sourceMapRoot.getJSONArray("sections"); for (int i = 0, count = sections.length(); i < count; i++) { JSONObject section = sections.getJSONObject(i); if (section.has("map") && section.has("url")) { throw new SourceMapParseException( "Invalid map format: section may not have both 'map' and 'url'"); } JSONObject offset = section.getJSONObject("offset"); int line = offset.getInt("line"); int column = offset.getInt("column"); String mapSectionContents; if (section.has("url")) { String url = section.getString("url"); mapSectionContents = sectionSupplier.getSourceMap(url); if (mapSectionContents == null) { throw new SourceMapParseException("Unable to retrieve: " + url); } } else if (section.has("map")) { mapSectionContents = section.getString("map"); } else { throw new SourceMapParseException( "Invalid map format: section must have either 'map' or 'url'"); } generator.mergeMapSection(line, column, mapSectionContents); } StringBuilder sb = new StringBuilder(); try { generator.appendTo(sb, file); } catch (IOException e) { // Can't happen. throw new RuntimeException(e); } parse(sb.toString()); } catch (IOException ex) { throw new SourceMapParseException("IO exception: " + ex); } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } @Override public OriginalMapping getMappingForLine(int lineNumber, int column) { // Normalize the line and column numbers to 0. lineNumber--; column--; if (lineNumber < 0 || lineNumber >= lines.size()) { return null; } Preconditions.checkState(lineNumber >= 0); Preconditions.checkState(column >= 0); // If the line is empty return the previous mapping. if (lines.get(lineNumber) == null) { return getPreviousMapping(lineNumber); } ArrayList entries = lines.get(lineNumber); // No empty lists. Preconditions.checkState(entries.size() > 0); if (entries.get(0).getGeneratedColumn() > column) { return getPreviousMapping(lineNumber); } int index = search(entries, column, 0, entries.size() - 1); Preconditions.checkState(index >= 0, "unexpected:%s", index); return getOriginalMappingForEntry(entries.get(index)); } @Override public Collection getOriginalSources() { return Arrays.asList(sources); } @Override public Collection getReverseMapping(String originalFile, int line, int column) { // TODO(user): This implementation currently does not make use of the column // parameter. // Synchronization needs to be handled by callers. if (reverseSourceMapping == null) { createReverseMapping(); } Map> sourceLineToCollectionMap = reverseSourceMapping.get(originalFile); if (sourceLineToCollectionMap == null) { return Collections.emptyList(); } else { Collection mappings = sourceLineToCollectionMap.get(line); if (mappings == null) { return Collections.emptyList(); } else { return mappings; } } } private String[] getJavaStringArray(JSONArray array) throws JSONException { int len = array.length(); String[] result = new String[len]; for(int i = 0; i < len; i++) { result[i] = array.getString(i); } return result; } private class MappingBuilder { private static final int MAX_ENTRY_VALUES = 5; private final StringCharIterator content; private int line = 0; private int previousCol = 0; private int previousSrcId = 0; private int previousSrcLine = 0; private int previousSrcColumn = 0; private int previousNameId = 0; MappingBuilder(String lineMap) { this.content = new StringCharIterator(lineMap); } void build() { int [] temp = new int[MAX_ENTRY_VALUES]; ArrayList entries = new ArrayList(); while (content.hasNext()) { // ';' denotes a new line. if (tryConsumeToken(';')) { // The line is complete, store the result for the line, // null if the line is empty. ArrayList result; if (entries.size() > 0) { result = entries; // A new array list for the next line. entries = new ArrayList(); } else { result = null; } lines.add(result); entries.clear(); line++; previousCol = 0; } else { // grab the next entry for the current line. int entryValues = 0; while (!entryComplete()) { temp[entryValues] = nextValue(); entryValues++; } Entry entry = decodeEntry(temp, entryValues); validateEntry(entry); entries.add(entry); // Consume the separating token, if there is one. tryConsumeToken(','); } } } /** * Sanity check the entry. */ private void validateEntry(Entry entry) { Preconditions.checkState(line < lineCount); Preconditions.checkState(entry.getSourceFileId() == UNMAPPED || entry.getSourceFileId() < sources.length); Preconditions.checkState(entry.getNameId() == UNMAPPED || entry.getNameId() < names.length); } /** * Decodes the next entry, using the previous encountered values to * decode the relative values. * * @param vals An array of integers that represent values in the entry. * @param entryValues The number of entries in the array. * @return The entry object. */ private Entry decodeEntry(int[] vals, int entryValues) { Entry entry; switch (entryValues) { // The first values, if present are in the following order: // 0: the starting column in the current line of the generated file // 1: the id of the original source file // 2: the starting line in the original source // 3: the starting column in the original source // 4: the id of the original symbol name // The values are relative to the last encountered value for that field. // Note: the previously column value for the generated file is reset // to '0' when a new line is encountered. This is done in the 'build' // method. case 1: // An unmapped section of the generated file. entry = new UnmappedEntry( vals[0] + previousCol); // Set the values see for the next entry. previousCol = entry.getGeneratedColumn(); return entry; case 4: // A mapped section of the generated file. entry = new UnnamedEntry( vals[0] + previousCol, vals[1] + previousSrcId, vals[2] + previousSrcLine, vals[3] + previousSrcColumn); // Set the values see for the next entry. previousCol = entry.getGeneratedColumn(); previousSrcId = entry.getSourceFileId(); previousSrcLine = entry.getSourceLine(); previousSrcColumn = entry.getSourceColumn(); return entry; case 5: // A mapped section of the generated file, that has an associated // name. entry = new NamedEntry( vals[0] + previousCol, vals[1] + previousSrcId, vals[2] + previousSrcLine, vals[3] + previousSrcColumn, vals[4] + previousNameId); // Set the values see for the next entry. previousCol = entry.getGeneratedColumn(); previousSrcId = entry.getSourceFileId(); previousSrcLine = entry.getSourceLine(); previousSrcColumn = entry.getSourceColumn(); previousNameId = entry.getNameId(); return entry; default: throw new IllegalStateException( "Unexpected number of values for entry:" + entryValues); } } private boolean tryConsumeToken(char token) { if (content.hasNext() && content.peek() == token) { // consume the comma content.next(); return true; } return false; } private boolean entryComplete() { if (!content.hasNext()) { return true; } char c = content.peek(); return (c == ';' || c == ','); } private int nextValue() { return Base64VLQ.decode(content); } } /** * Perform a binary search on the array to find a section that covers * the target column. */ private int search(ArrayList entries, int target, int start, int end) { while (true) { int mid = ((end - start) / 2) + start; int compare = compareEntry(entries, mid, target); if (compare == 0) { return mid; } else if (compare < 0) { // it is in the upper half start = mid + 1; if (start > end) { return end; } } else { // it is in the lower half end = mid - 1; if (end < start) { return end; } } } } /** * Compare an array entry's column value to the target column value. */ private int compareEntry(ArrayList entries, int entry, int target) { return entries.get(entry).getGeneratedColumn() - target; } /** * Returns the mapping entry that proceeds the supplied line or null if no * such entry exists. */ private OriginalMapping getPreviousMapping(int lineNumber) { do { if (lineNumber == 0) { return null; } lineNumber--; } while (lines.get(lineNumber) == null); ArrayList entries = lines.get(lineNumber); return getOriginalMappingForEntry(entries.get(entries.size() - 1)); } /** * Creates an "OriginalMapping" object for the given entry object. */ private OriginalMapping getOriginalMappingForEntry(Entry entry) { if (entry.getSourceFileId() == UNMAPPED) { return null; } else { // Adjust the line/column here to be start at 1. Builder x = OriginalMapping.newBuilder() .setOriginalFile(sources[entry.getSourceFileId()]) .setLineNumber(entry.getSourceLine() + 1) .setColumnPosition(entry.getSourceColumn() + 1); if (entry.getNameId() != UNMAPPED) { x.setIdentifier(names[entry.getNameId()]); } return x.build(); } } /** * Reverse the source map; the created mapping will allow us to quickly go * from a source file and line number to a collection of target * OriginalMappings. */ private void createReverseMapping() { reverseSourceMapping = new HashMap>>(); for (int targetLine = 0; targetLine < lines.size(); targetLine++) { ArrayList entries = lines.get(targetLine); if (entries != null) { for (Entry entry : entries) { if (entry.getSourceFileId() != UNMAPPED && entry.getSourceLine() != UNMAPPED) { String originalFile = sources[entry.getSourceFileId()]; if (!reverseSourceMapping.containsKey(originalFile)) { reverseSourceMapping.put(originalFile, new HashMap>()); } Map> lineToCollectionMap = reverseSourceMapping.get(originalFile); int sourceLine = entry.getSourceLine(); if (!lineToCollectionMap.containsKey(sourceLine)) { lineToCollectionMap.put(sourceLine, new ArrayList(1)); } Collection mappings = lineToCollectionMap.get(sourceLine); Builder builder = OriginalMapping.newBuilder().setLineNumber( targetLine).setColumnPosition(entry.getGeneratedColumn()); mappings.add(builder.build()); } } } } } /** * A implementation of the Base64VLQ CharIterator used for decoding the * mappings encoded in the JSON string. */ private static class StringCharIterator implements CharIterator { final String content; final int length; int current = 0; StringCharIterator(String content) { this.content = content; this.length = content.length(); } @Override public char next() { return content.charAt(current++); } char peek() { return content.charAt(current); } @Override public boolean hasNext() { return current < length; } } /** * Represents a mapping entry in the source map. */ private interface Entry { int getGeneratedColumn(); int getSourceFileId(); int getSourceLine(); int getSourceColumn(); int getNameId(); } /** * This class represents a portion of the generated file, that is not mapped * to a section in the original source. */ private static class UnmappedEntry implements Entry { private final int column; UnmappedEntry(int column) { this.column = column; } @Override public int getGeneratedColumn() { return column; } @Override public int getSourceFileId() { return UNMAPPED; } @Override public int getSourceLine() { return UNMAPPED; } @Override public int getSourceColumn() { return UNMAPPED; } @Override public int getNameId() { return UNMAPPED; } } /** * This class represents a portion of the generated file, that is mapped * to a section in the original source. */ private static class UnnamedEntry extends UnmappedEntry { private final int srcFile; private final int srcLine; private final int srcColumn; UnnamedEntry(int column, int srcFile, int srcLine, int srcColumn) { super(column); this.srcFile = srcFile; this.srcLine = srcLine; this.srcColumn = srcColumn; } @Override public int getSourceFileId() { return srcFile; } @Override public int getSourceLine() { return srcLine; } @Override public int getSourceColumn() { return srcColumn; } @Override public int getNameId() { return UNMAPPED; } } /** * This class represents a portion of the generated file, that is mapped * to a section in the original source, and is associated with a name. */ private static class NamedEntry extends UnnamedEntry { private final int name; NamedEntry(int column, int srcFile, int srcLine, int srcColumn, int name) { super(column, srcFile, srcLine, srcColumn); this.name = name; } @Override public int getNameId() { return name; } } public static interface EntryVisitor { void visit(String sourceName, String symbolName, FilePosition sourceStartPosition, FilePosition startPosition, FilePosition endPosition); } public void visitMappings(EntryVisitor visitor) { boolean pending = false; String sourceName = null; String symbolName = null; FilePosition sourceStartPosition = null; FilePosition startPosition = null; final int lineCount = lines.size(); for (int i = 0; i < lineCount; i++) { ArrayList line = lines.get(i); if (line != null) { final int entryCount = line.size(); for (int j = 0; j < entryCount; j++) { Entry entry = line.get(j); if (pending) { FilePosition endPosition = new FilePosition( i, entry.getGeneratedColumn()); visitor.visit( sourceName, symbolName, sourceStartPosition, startPosition, endPosition); pending = false; } if (entry.getSourceFileId() != UNMAPPED) { pending = true; sourceName = sources[entry.getSourceFileId()]; symbolName = (entry.getNameId() != UNMAPPED) ? names[entry.getNameId()] : null; sourceStartPosition = new FilePosition( entry.getSourceLine(), entry.getSourceColumn()); startPosition = new FilePosition( i, entry.getGeneratedColumn()); } } } } } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMappingReversable.java0000644000175000017500000000316712115204405030565 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import java.util.Collection; /** * A SourceMappingReversable is a SourceMapping that can provide the reverse * (source --> target) source mapping. */ public interface SourceMappingReversable extends SourceMapping { /** * @return the collection of original sources in this source mapping */ public Collection getOriginalSources(); /** * Given a source file, line, and column, return the reverse mapping (source --> target). * A collection is returned as in some cases (like a function being inlined), one source line * may map to more then one target location. An empty collection is returned if there were * no matches. * @param originalFile the source file * @param line the source line * @param column the source column * @return the reverse mapping (source --> target) */ public Collection getReverseMapping(String originalFile, int line, int column); } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/Base64.java0000644000175000017500000000440012115204405025011 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import java.util.Arrays; /** * A utility class for working with Base64 values. * @author johnlenz@google.com (John Lenz) */ public final class Base64 { // This is a utility class private Base64() {} /** * A map used to convert integer values in the range 0-63 to their base64 * values. */ private static final String BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; /** * A map used to convert base64 character into integer values. */ private static final int[] BASE64_DECODE_MAP = new int[256]; static { Arrays.fill(BASE64_DECODE_MAP, -1); for (int i = 0; i < BASE64_MAP.length(); i++) { BASE64_DECODE_MAP[BASE64_MAP.charAt(i)] = i; } } /** * @param value A value in the range of 0-63. * @return a base64 digit. */ public static char toBase64(int value) { assert (value <= 63 && value >= 0) : "value out of range:" + value; return BASE64_MAP.charAt(value); } /** * @param c A base64 digit. * @return A value in the range of 0-63. */ public static int fromBase64(char c) { int result = BASE64_DECODE_MAP[c]; assert (result != -1) : "invalid char"; return BASE64_DECODE_MAP[c]; } /** * @param value an integer to base64 encode. * @return the six digit long base64 encoded value of the integer. */ public static String base64EncodeInt(int value) { char[] c = new char[6]; for (int i = 0; i < 5; i++) { c[i] = Base64.toBase64((value >> (26 - i * 6)) & 0x3f); } c[5] = Base64.toBase64((value << 4) & 0x3f); return new String(c); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapConsumerV1.java0000644000175000017500000004470112115204405027616 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Interner; import com.google.common.collect.Interners; import com.google.common.collect.Lists; import com.google.common.primitives.Bytes; import com.google.common.primitives.Shorts; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; /** * Class for parsing and representing a SourceMap, as produced by the * Closure Compiler, Caja-Compiler, etc. */ public class SourceMapConsumerV1 implements SourceMapConsumer { private static final String LINEMAP_HEADER = "/** Begin line maps. **/"; private static final String FILEINFO_HEADER = "/** Begin file information. **/"; private static final String DEFINITION_HEADER = "/** Begin mapping definitions. **/"; /** * Internal class for parsing the SourceMap. Used to maintain parser * state in an easy to use instance. */ private static class ParseState { final String contents; int currentPosition = 0; ParseState(String contents) { this.contents = contents; } /** Reads a line, returning null at EOF. */ String readLineOrNull() { if (currentPosition >= contents.length()) { return null; } int index = contents.indexOf('\n', currentPosition); if (index < 0) { index = contents.length(); } String line = contents.substring(currentPosition, index); currentPosition = index + 1; return line; } /** Reads a line, throwing a parse exception at EOF. */ String readLine() throws SourceMapParseException { String line = readLineOrNull(); if (line == null) { fail("EOF"); } return line; } /** * Reads a line and throws an parse exception if the line does not * equal the argument. */ void expectLine(String expect) throws SourceMapParseException { String line = readLine(); if (!expect.equals(line)) { fail("Expected " + expect + " got " + line); } } /** * Indicates that parsing has failed by throwing a parse exception. */ void fail(String message) throws SourceMapParseException { throw new SourceMapParseException(message); } } /** * Mapping from a line number (0-indexed), to a list of mapping IDs, one for * each character on the line. For example, if the array for line 2 is * {@code [4,,,,5,6,,,7]}, then there will be the entry: * *
   * 1 => {4, 4, 4, 4, 5, 6, 6, 6, 7}
   * 
*/ private ImmutableList> characterMap; /** * Map of Mapping IDs to the actual mapping object. */ private ImmutableList mappings; /** * Parses the given contents containing a source map. */ @Override public void parse(String contents) throws SourceMapParseException { ParseState parser = new ParseState(contents); try { parseInternal(parser); } catch (JSONException ex) { parser.fail("JSON parse exception: " + ex); } } /** * Parses the first section of the source map file that has character * mappings. * @param parser The parser to use * @param lineCount The number of lines in the generated JS * @return The max id found in the file */ private int parseCharacterMap( ParseState parser, int lineCount, ImmutableList.Builder> characterMapBuilder) throws SourceMapParseException, JSONException { int maxID = -1; // [0,,,,,,1,2] for (int i = 0; i < lineCount; ++i) { String currentLine = parser.readLine(); // Blank lines are allowed in the spec to indicate no mapping // information for the line. if (currentLine.isEmpty()) { continue; } ImmutableList.Builder fragmentList = ImmutableList.builder(); // We need the start index to initialize this, needs to be done in the // loop. LineFragment myLineFragment = null; JSONArray charArray = new JSONArray(currentLine); int lastID = -1; int startID = Integer.MIN_VALUE; List currentOffsets = Lists.newArrayList(); for (int j = 0; j < charArray.length(); ++j) { // Keep track of the current mappingID, if the next element in the // array is empty we reuse the existing mappingID for the column. int mappingID = lastID; if (!charArray.isNull(j)) { mappingID = charArray.optInt(j); if (mappingID > maxID) { maxID = mappingID; } } if (startID == Integer.MIN_VALUE) { startID = mappingID; } else { // If the difference is bigger than a byte we need to keep track of // a new line fragment with a new start value. if (mappingID - lastID > Byte.MAX_VALUE || mappingID - lastID < Byte.MIN_VALUE) { myLineFragment = new LineFragment( startID, Bytes.toArray(currentOffsets)); currentOffsets.clear(); // Start a new section. fragmentList.add(myLineFragment); startID = mappingID; } else { currentOffsets.add((byte) (mappingID - lastID)); } } lastID = mappingID; } if (startID != Integer.MIN_VALUE) { myLineFragment = new LineFragment( startID, Bytes.toArray(currentOffsets)); fragmentList.add(myLineFragment); } characterMapBuilder.add(fragmentList.build()); } return maxID; } private class FileName { private final String dir; private final String name; FileName(String directory, String name) { this.dir = directory; this.name = name; } } /** * Split the file into a filename/directory pair. * * @param interner The interner to use for interning the strings. * @param input The input to split. * @return The pair of directory, filename. */ private FileName splitFileName( Interner interner, String input) { int hashIndex = input.lastIndexOf('/'); String dir = interner.intern(input.substring(0, hashIndex + 1)); String fileName = interner.intern(input.substring(hashIndex + 1)); return new FileName(dir, fileName); } /** * Parse the file mappings section of the source map file. This maps the * ids to the filename, line number and column number in the original * files. * @param parser The parser to get the data from. * @param maxID The maximum id found in the character mapping section. */ private void parseFileMappings(ParseState parser, int maxID) throws SourceMapParseException, JSONException { // ['d.js', 3, 78, 'foo'] // Intern the strings to save memory. Interner interner = Interners.newStrongInterner(); ImmutableList.Builder mappingsBuilder = ImmutableList.builder(); // Setup all the arrays to keep track of the various details about the // source file. ArrayList lineOffsets = Lists.newArrayList(); ArrayList columns = Lists.newArrayList(); ArrayList identifiers = Lists.newArrayList(); // The indexes and details about the current position in the file to do // diffs against. String currentFile = null; int lastLine = -1; int startLine = -1; int startMapId = -1; for (int mappingId = 0; mappingId <= maxID; ++mappingId) { String currentLine = parser.readLine(); JSONArray mapArray = new JSONArray(currentLine); if (mapArray.length() < 3) { parser.fail("Invalid mapping array"); } // Split up the file and directory names to reduce memory usage. String myFile = mapArray.getString(0); int line = mapArray.getInt(1); if (!myFile.equals(currentFile) || (line - lastLine) > Byte.MAX_VALUE || (line - lastLine) < Byte.MIN_VALUE) { if (currentFile != null) { FileName dirFile = splitFileName(interner, currentFile); SourceFile.Builder builder = SourceFile.newBuilder() .setDir(dirFile.dir) .setFileName(dirFile.name) .setStartLine(startLine) .setStartMapId(startMapId) .setLineOffsets(lineOffsets) .setColumns(columns) .setIdentifiers(identifiers); mappingsBuilder.add(builder.build()); } // Reset all the positions back to the start and clear out the arrays // to start afresh. currentFile = myFile; startLine = line; lastLine = line; startMapId = mappingId; columns.clear(); lineOffsets.clear(); identifiers.clear(); } // We need to add on the columns and identifiers for all the lines, even // for the first line. lineOffsets.add((byte) (line - lastLine)); columns.add((short) mapArray.getInt(2)); identifiers.add(interner.intern(mapArray.optString(3, ""))); lastLine = line; } if (currentFile != null) { FileName dirFile = splitFileName(interner, currentFile); SourceFile.Builder builder = SourceFile.newBuilder() .setDir(dirFile.dir) .setFileName(dirFile.name) .setStartLine(startLine) .setStartMapId(startMapId) .setLineOffsets(lineOffsets) .setColumns(columns) .setIdentifiers(identifiers); mappingsBuilder.add(builder.build()); } mappings = mappingsBuilder.build(); } private void parseInternal(ParseState parser) throws SourceMapParseException, JSONException { // /** Begin line maps. **/{ count: 2 } String headerCount = parser.readLine(); Preconditions.checkArgument(headerCount.startsWith(LINEMAP_HEADER), "Expected %s", LINEMAP_HEADER); JSONObject countObject = new JSONObject( headerCount.substring(LINEMAP_HEADER.length())); if (!countObject.has("count")) { parser.fail("Missing 'count'"); } int lineCount = countObject.getInt("count"); if (lineCount <= 0) { parser.fail("Count must be >= 1"); } ImmutableList.Builder> characterMapBuilder = ImmutableList.builder(); int maxId = parseCharacterMap(parser, lineCount, characterMapBuilder); characterMap = characterMapBuilder.build(); // /** Begin file information. **/ parser.expectLine(FILEINFO_HEADER); // File information. Not used, so we just consume it. for (int i = 0; i < lineCount; i++) { parser.readLine(); } // /** Begin mapping definitions. **/ parser.expectLine(DEFINITION_HEADER); parseFileMappings(parser, maxId); } @Override public OriginalMapping getMappingForLine(int lineNumber, int columnIndex) { Preconditions.checkNotNull(characterMap, "parse() must be called first"); if (lineNumber < 1 || lineNumber > characterMap.size() || columnIndex < 1) { return null; } List lineFragments = characterMap.get(lineNumber - 1); if (lineFragments == null || lineFragments.isEmpty()) { return null; } int columnOffset = 0; // The code assumes everything past the end is the same as the last item // so we default to the last item in the line. LineFragment lastFragment = lineFragments.get(lineFragments.size() - 1); int mapId = lastFragment.valueAtColumn(lastFragment.length()); for (LineFragment lineFragment : lineFragments) { int columnPosition = columnIndex - columnOffset; if (columnPosition <= lineFragment.length()) { mapId = lineFragment.valueAtColumn(columnPosition); break; } columnOffset += lineFragment.length(); } if (mapId < 0) { return null; } return getMappingFromId(mapId); } /** * Do a binary search for the correct mapping array to use. * * @param mapId The mapping array to find * @return The source file mapping to use. */ private SourceFile binarySearch(int mapId) { int lower = 0; int upper = mappings.size() - 1; while (lower <= upper) { int middle = lower + (upper - lower) / 2; SourceFile middleCompare = mappings.get(middle); if (mapId < middleCompare.getStartMapId()) { upper = middle - 1; } else if (mapId < (middleCompare.getStartMapId() + middleCompare.getLength())) { return middleCompare; } else { lower = middle + 1; } } return null; } /** * Find the original mapping for the specified mapping id. * * @param mapID The mapID to lookup. * @return The originalMapping protocol buffer for the id. */ private OriginalMapping getMappingFromId(int mapID) { SourceFile match = binarySearch(mapID); if (match == null) { return null; } int pos = mapID - match.getStartMapId(); return match.getOriginalMapping(pos); } /** * Keeps track of the information about the line in a more compact way. It * represents a fragment of the line starting at a specific index and then * looks at offsets from that index stored as a byte, this dramatically * reduces the memory usage of this array. */ private static final class LineFragment { private final int startIndex; private final byte[] offsets; /** * Create a new line fragment to store information about. * * @param startIndex The start index for this line. * @param offsets The byte array of offsets to store. */ LineFragment(int startIndex, byte[] offsets) { this.startIndex = startIndex; this.offsets = offsets; } /** * The length of columns stored in the line. One is added because we * store the start index outside of the offsets array. */ int length() { return offsets.length + 1; } /** * Find the mapping id at the specified column. * * @param column The column to lookup * @return the value at that point in the column */ int valueAtColumn(int column) { Preconditions.checkArgument(column > 0); int pos = startIndex; for (int i = 0; i < column - 1; i++) { pos += offsets[i]; } return pos; } } /** * Keeps track of data about the source file itself. This is contains a list * of line offsets and columns to track down where exactly a line falls into * the data. */ private static final class SourceFile { final String dir; final String fileName; final int startMapId; final int startLine; final byte[] lineOffsets; final short[] columns; final String[] identifiers; private SourceFile( String dir, String fileName, int startLine, int startMapId, byte[] lineOffsets, short[] columns, String[] identifiers) { this.fileName = Preconditions.checkNotNull(fileName); this.dir = Preconditions.checkNotNull(dir); this.startLine = startLine; this.startMapId = startMapId; this.lineOffsets = Preconditions.checkNotNull(lineOffsets); this.columns = Preconditions.checkNotNull(columns); this.identifiers = Preconditions.checkNotNull(identifiers); Preconditions.checkArgument(lineOffsets.length == columns.length && columns.length == identifiers.length); } private SourceFile(int startMapId) { // Only used for binary searches. this.startMapId = startMapId; this.fileName = null; this.dir = null; this.startLine = 0; this.lineOffsets = null; this.columns = null; this.identifiers = null; } /** * Returns the number of elements in this source file. */ int getLength() { return lineOffsets.length; } /** * Returns the number of elements in this source file. */ int getStartMapId() { return startMapId; } /** * Creates an original mapping from the data. * * @param offset The offset into the array to find the mapping for. * @return A new original mapping object. */ OriginalMapping getOriginalMapping(int offset) { int lineNumber = this.startLine; // Offset is an index into this array and we need to include it. for (int i = 0; i <= offset; i++) { lineNumber += lineOffsets[i]; } OriginalMapping.Builder builder = OriginalMapping.newBuilder() .setOriginalFile(dir + fileName) .setLineNumber(lineNumber) .setColumnPosition(columns[offset]) .setIdentifier(identifiers[offset]); return builder.build(); } /** * Builder to make a new SourceFile object. */ static final class Builder { String dir; String fileName; int startMapId; int startLine; byte[] lineOffsets; short[] columns; String[] identifiers; Builder setDir(String dir) { this.dir = dir; return this; } Builder setFileName(String fileName) { this.fileName = fileName; return this; } Builder setStartMapId(int startMapId) { this.startMapId = startMapId; return this; } Builder setStartLine(int startLine) { this.startLine = startLine; return this; } Builder setLineOffsets(List lineOffsets) { this.lineOffsets = Bytes.toArray(lineOffsets); return this; } Builder setColumns(List columns) { this.columns = Shorts.toArray(columns); return this; } Builder setIdentifiers(List identifiers) { this.identifiers = identifiers.toArray(new String[0]); return this; } /** * Creates a new SourceFile from the parameters. */ SourceFile build() { return new SourceFile(dir, fileName, startLine, startMapId, lineOffsets, columns, identifiers); } } static Builder newBuilder() { return new Builder(); } } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapping.java0000644000175000017500000000235112115204405026544 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; /** * Interface for provide a way of mapping (line, column) positions back to * positions in the original (uncompiled) source code. * */ public interface SourceMapping { /** * Returns the original mapping for the line number and column position found * in the source map. Returns null if none is found. * * @param lineNumber The line number, with the first being '1'. * @param columnIndex The column index, with the first being '1'. */ OriginalMapping getMappingForLine(int lineNumber, int columnIndex); } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapGeneratorV2.java0000644000175000017500000006466512115204405027765 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes. * */ public class SourceMapGeneratorV2 implements SourceMapGenerator { private boolean validate = false; private static final int UNMAPPED = -1; /** * A pre-order traversal ordered list of mappings stored in this map. */ private List mappings = Lists.newArrayList(); /** * A map of source names to source name index */ private LinkedHashMap sourceFileMap = Maps.newLinkedHashMap(); /** * A map of symbol names to symbol name index */ private LinkedHashMap originalNameMap = Maps.newLinkedHashMap(); /** * Cache of the last mappings source name. */ private String lastSourceFile = null; /** * Cache of the last mappings source name index. */ private int lastSourceFileIndex = -1; /** * For validation store the last mapping added. */ private Mapping lastMapping; /** * The position that the current source map is offset in the * buffer being used to generated the compiled source file. */ private FilePosition offsetPosition = new FilePosition(0, 0); /** * The position that the current source map is offset in the * generated the compiled source file by the addition of a * an output wrapper prefix. */ private FilePosition prefixPosition = new FilePosition(0, 0); /** * {@inheritDoc} */ @Override public void reset() { mappings.clear(); lastMapping = null; sourceFileMap.clear(); originalNameMap.clear(); lastSourceFile = null; lastSourceFileIndex = -1; offsetPosition = new FilePosition(0, 0); prefixPosition = new FilePosition(0, 0); } /** * @param validate Whether to perform (potentially costly) validation on the * generated source map. */ @Override @VisibleForTesting public void validate(boolean validate) { this.validate = validate; } /** * Sets the prefix used for wrapping the generated source file before * it is written. This ensures that the source map is adjusted for the * change in character offsets. * * @param prefix The prefix that is added before the generated source code. */ @Override public void setWrapperPrefix(String prefix) { // Determine the current line and character position. int prefixLine = 0; int prefixIndex = 0; for (int i = 0; i < prefix.length(); ++i) { if (prefix.charAt(i) == '\n') { prefixLine++; prefixIndex = 0; } else { prefixIndex++; } } prefixPosition = new FilePosition(prefixLine, prefixIndex); } /** * Sets the source code that exists in the buffer for which the * generated code is being generated. This ensures that the source map * accurately reflects the fact that the source is being appended to * an existing buffer and as such, does not start at line 0, position 0 * but rather some other line and position. * * @param offsetLine The index of the current line being printed. * @param offsetIndex The column index of the current character being printed. */ @Override public void setStartingPosition(int offsetLine, int offsetIndex) { Preconditions.checkState(offsetLine >= 0); Preconditions.checkState(offsetIndex >= 0); offsetPosition = new FilePosition(offsetLine, offsetIndex); } /** * Adds a mapping for the given node. Mappings must be added in order. * @param startPosition The position on the starting line * @param endPosition The position on the ending line. */ @Override public void addMapping( String sourceName, @Nullable String symbolName, FilePosition sourceStartPosition, FilePosition startPosition, FilePosition endPosition) { // Don't bother if there is not sufficient information to be useful. if (sourceName == null || sourceStartPosition.getLine() < 0) { return; } FilePosition adjustedStart = startPosition; FilePosition adjustedEnd = endPosition; if (offsetPosition.getLine() != 0 || offsetPosition.getColumn() != 0) { // If the mapping is found on the first line, we need to offset // its character position by the number of characters found on // the *last* line of the source file to which the code is // being generated. int offsetLine = offsetPosition.getLine(); int startOffsetPosition = offsetPosition.getColumn(); int endOffsetPosition = offsetPosition.getColumn(); if (startPosition.getLine() > 0) { startOffsetPosition = 0; } if (endPosition.getLine() > 0) { endOffsetPosition = 0; } adjustedStart = new FilePosition( startPosition.getLine() + offsetLine, startPosition.getColumn() + startOffsetPosition); adjustedEnd = new FilePosition( endPosition.getLine() + offsetLine, endPosition.getColumn() + endOffsetPosition); } // Create the new mapping. Mapping mapping = new Mapping(); mapping.sourceFile = getSourceId(sourceName); mapping.originalPosition = sourceStartPosition; mapping.originalName = symbolName; mapping.startPosition = adjustedStart; mapping.endPosition = adjustedEnd; // Validate the mappings are in a proper order. if (lastMapping != null) { int lastLine = lastMapping.startPosition.getLine(); int lastColumn = lastMapping.startPosition.getColumn(); int nextLine = mapping.startPosition.getLine(); int nextColumn = mapping.startPosition.getColumn(); Preconditions.checkState(nextLine > lastLine || (nextLine == lastLine && nextColumn >= lastColumn), "Incorrect source mappings order, previous : (%s,%s)\n" + "new : (%s,%s)\nnode : %s", lastLine, lastColumn, nextLine, nextColumn); } lastMapping = mapping; mappings.add(mapping); } /** * Writes out the source map in the following format (line numbers are for * reference only and are not part of the format): * * 1. { * 2. version: 2, * 3. file: "out.js" * 4. lineCount: 2 * 5. lineMaps: [ * 6. "ABAAA", * 7. "ABAA" * 8. ], * 9. sourceRoot: "", * 10. sources: ["foo.js", "bar.js"], * 11. names: ["src", "maps", "are", "fun"], * 12. mappings: [ * 13. [1, 1, 2, 4], * 14. [2, 1, 2, "yack"], * 15. ], * 16. } * * Line 1: The entire file is a single JSON object * Line 2: File revision (always the first entry in the object) * Line 3: The name of the file that this source map is associated with. * Line 4: The number of lines represented in the source map. * Line 5: "lineMaps" field is a JSON array, where each entry represents a * line in the generated text. * Line 6: A line entry, representing a series of line segments, where each * segment encodes an mappings-id and repetition count. * Line 9: An optional source root, useful for relocating source files on a * server or removing repeated prefix values in the "sources" entry. * Line 10: A list of sources used by the "mappings" entry relative to the * sourceRoot. * Line 11: A list of symbol names used by the "mapping" entry. This list * may be incomplete. * Line 12: The mappings field. * Line 13: Each entry represent a block of text in the original source, and * consists four fields: * The source file name * The line in the source file the text begins * The column in the line that the text begins * An optional name (from the original source) that this entry represents. * This can either be an string or index into the "names" field. */ @Override public void appendTo(Appendable out, String name) throws IOException { int maxLine = prepMappings(); // Add the header fields. out.append("{\n"); appendFirstField(out, "version", "2"); appendField(out, "file", escapeString(name)); appendField(out, "lineCount", String.valueOf(maxLine + 1)); // Add the line character maps. appendFieldStart(out, "lineMaps"); out.append("["); (new LineMapper(out)).appendLineMappings(); out.append("]"); appendFieldEnd(out); // Add the mappings themselves. appendFieldStart(out, "mappings"); out.append("["); (new MappingWriter()).appendMappings(out); out.append("]"); appendFieldEnd(out); // Files names appendFieldStart(out, "sources"); out.append("["); addSourceNameMap(out); out.append("]"); appendFieldEnd(out); // Files names appendFieldStart(out, "names"); out.append("["); addOriginalNameMap(out); out.append("]"); appendFieldEnd(out); out.append("\n}\n"); } /** * Writes the source name map to 'out'. */ private void addSourceNameMap(Appendable out) throws IOException { addMap(out, sourceFileMap); } /** * Writes the original name map to 'out'. */ private void addOriginalNameMap(Appendable out) throws IOException { addMap(out, originalNameMap); } /** * Writes the source name map to 'out'. */ private void addMap(Appendable out, Map map) throws IOException { int i = 0; for (Entry entry : map.entrySet()) { String key = entry.getKey(); if (i != 0) { out.append(","); } out.append(escapeString(key)); i++; } } /** * Escapes the given string for JSON. */ private static String escapeString(String value) { return Util.escapeString(value); } // Source map field helpers. private static void appendFirstField( Appendable out, String name, CharSequence value) throws IOException { out.append("\""); out.append(name); out.append("\""); out.append(":"); out.append(value); } private static void appendField( Appendable out, String name, CharSequence value) throws IOException { out.append(",\n"); out.append("\""); out.append(name); out.append("\""); out.append(":"); out.append(value); } private static void appendFieldStart(Appendable out, String name) throws IOException { appendField(out, name, ""); } @SuppressWarnings("unused") private static void appendFieldEnd(Appendable out) throws IOException { } /** * Assigns sequential ids to used mappings, and returns the last line mapped. */ private int prepMappings() throws IOException { // Mark any unused mappings. (new MappingTraversal()).traverse(new UsedMappingCheck()); // Renumber used mappings and keep track of the last line. int id = 0; int maxLine = 0; for (Mapping m : mappings) { if (m.used) { m.id = id++; int endPositionLine = m.endPosition.getLine(); maxLine = Math.max(maxLine, endPositionLine); } } // Adjust for the prefix. return maxLine + prefixPosition.getLine(); } /** * Pools source names. * @param sourceName The source location to index. * @return The id to represent the source name in the output. */ private int getSourceId(String sourceName) { if (sourceName != lastSourceFile) { lastSourceFile = sourceName; Integer index = sourceFileMap.get(sourceName); if (index != null) { lastSourceFileIndex = index; } else { lastSourceFileIndex = sourceFileMap.size(); sourceFileMap.put(sourceName, lastSourceFileIndex); } } return lastSourceFileIndex; } /** * Pools symbol names * @param symbolName The symbol name to index. * @return The id to represent the symbol name in the output. */ private int getNameId(String symbolName) { int originalNameIndex; Integer index = originalNameMap.get(symbolName); if (index != null) { originalNameIndex = index; } else { originalNameIndex = originalNameMap.size(); originalNameMap.put(symbolName, originalNameIndex); } return originalNameIndex; } /** * A mapping from a given position in an input source file to a given position * in the generated code. */ static class Mapping { /** * A unique ID for this mapping for record keeping purposes. */ int id = UNMAPPED; /** * The source file index. */ int sourceFile; /** * The position of the code in the input source file. Both * the line number and the character index are indexed by * 1 for legacy reasons via the Rhino Node class. */ FilePosition originalPosition; /** * The starting position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition startPosition; /** * The ending position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition endPosition; /** * The original name of the token found at the position * represented by this mapping (if any). */ String originalName; /** * Whether the mapping is actually used by the source map. */ boolean used = false; } private class MappingWriter { /** * Cache of escaped source file name. */ private int lastLine = 0; private String lastLineString = String.valueOf(0); /** * Appends the mapping to the given buffer. */ private void appendMappingTo( Mapping m, Appendable out) throws IOException { out.append("["); out.append(String.valueOf(m.sourceFile)); out.append(","); int line = m.originalPosition.getLine(); if (line != lastLine) { lastLineString = String.valueOf(line); } String lineValue = lastLineString; out.append(lineValue); out.append(","); out.append(String.valueOf(m.originalPosition.getColumn())); if (m.originalName != null) { out.append(","); out.append(String.valueOf(getNameId(m.originalName))); } out.append("],\n"); } /** * Add used mappings to the supplied Appendable. */ void appendMappings(Appendable out) throws IOException { for (Mapping m : mappings) { if (m.used) { appendMappingTo(m, out); } } } } private class LineMapper implements MappingVisitor { // The destination. private final Appendable out; // Whether the current line has had a value written yet. private int lastId = UNMAPPED; LineMapper(Appendable out) { this.out = out; } /** * As each segment is visited write out the appropriate line mapping. */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { int id = (m != null) ? m.id : UNMAPPED; for (int i = line; i <= nextLine; i++) { if (i == nextLine) { closeEntry(id, nextCol - col); break; } closeLine(false); openLine(); // Set the starting location for the next line. col = 0; } } // Append the line mapping entries. void appendLineMappings() throws IOException { // Start the first line. openLine(); (new MappingTraversal()).traverse(this); // And close the final line. closeLine(true); } /** * Begin the entry for a new line. */ private void openLine() throws IOException { out.append("\""); // The first id of the line is not relative. this.lastId = 0; } /** * End the entry for a line. */ private void closeLine(boolean finalEntry) throws IOException { if (finalEntry) { out.append("\""); } else { out.append("\",\n"); } } private void closeEntry(int id, int reps) throws IOException { if (reps == 0) { return; } StringBuilder sb = new StringBuilder(); LineMapEncoder.encodeEntry(sb, id, lastId, reps); if (validate) { SourceMapLineDecoder.LineEntry entry = SourceMapLineDecoder.decodeLineEntry(sb.toString(), lastId); Preconditions.checkState(entry.id == id && entry.reps == reps, "expected (%s,%s) but got (%s,%s)", id, reps, entry.id, entry.reps); } out.append(sb); lastId = id; } } @VisibleForTesting public static class LineMapEncoder { /** * The source map line map is consists of a series of entries each * representing a map entry and a repetition count of that entry. * * @param out The entry destination. * @param id The id for the entry. * @param lastId The previous id written, used to generate a relative * map id. * @param reps The number of times the id is repeated in the map. * @throws IOException */ public static void encodeEntry(Appendable out, int id, int lastId, int reps) throws IOException { Preconditions.checkState(reps > 0); int relativeIdLength = getRelativeMappingIdLength(id, lastId); int relativeId = getRelativeMappingId(id, relativeIdLength, lastId); String relativeIdString = valueToBase64(relativeId, relativeIdLength); // If we can, we use a single base64 digit to encode both the id length // and the repetition count. The current best division of the base64 // digit (which has 6 bits) is 2 bits for the id length (1-4 digits) and // 4 bit for the repetition count (1-16 repetitions). If either of these // two values are exceeded a "!" is written (a non-base64 character) to // signal the a full base64 character is used for repetition count and // the mapping id length. As the repetition count can exceed 64, we // allow the "escape" ("!") to be repeated to signal additional // repetition count length characters. It is extremely unlikely that // mapping id length will exceed 64 base64 characters in length so // additional "!" don't signal additional id length characters. if (reps > 16 || relativeIdLength > 4) { String repsString = valueToBase64(reps - 1, 1); for (int i = 0; i < repsString.length(); i++) { // TODO(johnlenz): update this to whatever is agreed to. out.append('!'); } String sizeId = valueToBase64(relativeIdString.length() - 1, 1); out.append(sizeId); out.append(repsString); } else { int prefix = ((reps - 1) << 2) + (relativeIdString.length() - 1); Preconditions.checkState(prefix < 64 && prefix >= 0, "prefix (%s) reps(%s) map id size(%s)", prefix, reps, relativeIdString.length()); out.append(valueToBase64(prefix, 1)); } out.append(relativeIdString); } /** * @param idLength the length relative id, when encoded in as a base64 * value. @see #getRelativeMappingIdLength * @return A value relative to the the lastId. Negative value are * represented as a two-complement value. */ public static int getRelativeMappingId(int id, int idLength, int lastId) { int base = 1 << (idLength * 6); int relativeId = id - lastId; return (relativeId < 0) ? relativeId + base : relativeId; } /** * @return The length of the base64 number needed to include the id. */ public static int getRelativeMappingIdLength(int rawId, int lastId) { Preconditions.checkState(rawId >= 0 || rawId == UNMAPPED); int relativeId = rawId - lastId; int id = (relativeId < 0 ? Math.abs(relativeId) - 1 : relativeId) << 1; int digits = 1; int base = 64; while (id >= base) { digits++; base *= 64; } return digits; } /** * @return return the base64 number encoding the provided value, * padded if necessary to create a number with the given minimum length. */ static String valueToBase64(int value, int minimumSize) { int size = 0; char chars[] = new char[4]; do { int charValue = value & 63; // base64 chars value = value >>> 6; // get the next value; chars[size++] = Base64.toBase64(charValue); } while (value > 0); StringBuilder sb = new StringBuilder(size); while (minimumSize > size) { sb.append(Base64.toBase64(0)); minimumSize--; } while (size > 0) { sb.append(chars[--size]); } return sb.toString(); } } /** * Mark any visited mapping as "used". */ private class UsedMappingCheck implements MappingVisitor { /** * @throws IOException */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { if (m != null) { m.used = true; } } } private interface MappingVisitor { /** * @param m The mapping for the current code segment. null if the segment * is unmapped. * @param line The starting line for this code segment. * @param col The starting column for this code segment. * @param endLine The ending line * @param endCol The ending column * @throws IOException */ void visit(Mapping m, int line, int col, int endLine, int endCol) throws IOException; } /** * Walk the mappings and visit each segment of the mappings, unmapped * segments are visited with a null mapping, unused mapping are not visited. */ private class MappingTraversal { // The last line and column written private int line; private int col; MappingTraversal() { } // Append the line mapping entries. void traverse(MappingVisitor v) throws IOException { // The mapping list is ordered as a pre-order traversal. The mapping // positions give us enough information to rebuild the stack and this // allows the building of the source map in O(n) time. Deque stack = new ArrayDeque(); for (Mapping m : mappings) { // Find the closest ancestor of the current mapping: // An overlapping mapping is an ancestor of the current mapping, any // non-overlapping mappings are siblings (or cousins) and must be // closed in the reverse order of when they encountered. while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { Mapping previous = stack.pop(); maybeVisit(v, previous); } // Any gaps between the current line position and the start of the // current mapping belong to the parent. Mapping parent = stack.peek(); maybeVisitParent(v, parent, m); stack.push(m); } // There are no more children to be had, simply close the remaining // mappings in the reverse order of when they encountered. while (!stack.isEmpty()) { Mapping m = stack.pop(); maybeVisit(v, m); } } /** * @return The line adjusted for the prefix position. */ private int getAdjustedLine(FilePosition p) { return p.getLine() + prefixPosition.getLine(); } /** * @return The column adjusted for the prefix position. */ private int getAdjustedCol(FilePosition p) { int rawLine = p.getLine(); int rawCol = p.getColumn(); // Only the first line needs the character position adjusted. return (rawLine != 0) ? rawCol : rawCol + prefixPosition.getColumn(); } /** * @return Whether m1 ends before m2 starts. */ private boolean isOverlapped(Mapping m1, Mapping m2) { // No need to use adjusted values here, relative positions are sufficient. int l1 = m1.endPosition.getLine(); int l2 = m2.startPosition.getLine(); int c1 = m1.endPosition.getColumn(); int c2 = m2.startPosition.getColumn(); return (l1 == l2 && c1 >= c2) || l1 > l2; } /** * Write any needed entries from the current position to the end of the * provided mapping. */ private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.endPosition); int nextCol = getAdjustedCol(m.endPosition); // If this anything remaining in this mapping beyond the // current line and column position, write it out now. if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, m, nextLine, nextCol); } } /** * Write any needed entries to complete the provided mapping. */ private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.startPosition); int nextCol = getAdjustedCol(m.startPosition); // If the previous value is null, no mapping exists. Preconditions.checkState(line < nextLine || col <= nextCol); if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, parent, nextLine, nextCol); } } /** * Write any entries needed between the current position the next position * and update the current position. */ private void visit(MappingVisitor v, Mapping m, int nextLine, int nextCol) throws IOException { Preconditions.checkState(line <= nextLine); Preconditions.checkState(line < nextLine || col < nextCol); if (line == nextLine && col == nextCol) { // Nothing to do. Preconditions.checkState(false); return; } v.visit(m, line, col, nextLine, nextCol); line = nextLine; col = nextCol; } } @Override public void appendIndexMapTo( Appendable out, String name, List appSections) { throw new UnsupportedOperationException(); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/Util.java0000644000175000017500000001240012115204405024701 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import java.io.IOException; import java.nio.charset.CharsetEncoder; /** * @author johnlenz@google.com (John Lenz) */ class Util { private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Escapes the given string to a double quoted (") JavaScript/JSON string */ static String escapeString(String s) { return escapeString(s, '"', "\\\"", "\'", "\\\\", null); } /** Helper to escape JavaScript string as well as regular expression */ static String escapeString(String s, char quote, String doublequoteEscape, String singlequoteEscape, String backslashEscape, CharsetEncoder outputCharsetEncoder) { StringBuilder sb = new StringBuilder(s.length() + 2); sb.append(quote); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '\\': sb.append(backslashEscape); break; case '\"': sb.append(doublequoteEscape); break; case '\'': sb.append(singlequoteEscape); break; case '>': // Break --> into --\> or ]]> into ]]\> if (i >= 2 && ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || (s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']'))) { sb.append("\\>"); } else { sb.append(c); } break; case '<': // Break 0x1f && c <= 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some JS parsers, // or perhaps mangled by proxies along the way, // so we play it safe and Unicode escape them. appendCharAsHex(sb, c); } } } } sb.append(quote); return sb.toString(); } /** * @see #appendHexJavaScriptRepresentation(Appendable, int) */ @SuppressWarnings("cast") private static void appendCharAsHex( StringBuilder sb, char c) { try { appendHexJavaScriptRepresentation(sb, (int)c); } catch (IOException ex) { // StringBuilder does not throw IOException. throw new RuntimeException(ex); } } /** * Returns a JavaScript representation of the character in a hex escaped * format. * @param out The buffer to which the hex representation should be appended. * @param codePoint The code point to append. */ private static void appendHexJavaScriptRepresentation( Appendable out, int codePoint) throws IOException { if (Character.isSupplementaryCodePoint(codePoint)) { // Handle supplementary Unicode values which are not representable in // JavaScript. We deal with these by escaping them as two 4B sequences // so that they will round-trip properly when sent from Java to JavaScript // and back. char[] surrogates = Character.toChars(codePoint); appendHexJavaScriptRepresentation(out, surrogates[0]); appendHexJavaScriptRepresentation(out, surrogates[1]); return; } out.append("\\u") .append(HEX_CHARS[(codePoint >>> 12) & 0xf]) .append(HEX_CHARS[(codePoint >>> 8) & 0xf]) .append(HEX_CHARS[(codePoint >>> 4) & 0xf]) .append(HEX_CHARS[codePoint & 0xf]); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/proto/0000755000175000017500000000000012115204405024267 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/proto/mapping.proto0000644000175000017500000000173412115204405027014 0ustar apoapo// Copyright 2009 Google Inc. All rights reserved. // // Protocol Buffer definitions of the various source map structures. // // Author: jschorr@google.com (Joseph Schorr) syntax = "proto2"; package sourcemap; option java_package = "com.google.debugging.sourcemap.proto"; //option java_api_version = 2; // Maps a position on a given line to the mapping describing // the original code. message LineMapping { // The line number of the generated code. optional int32 line_number = 1; // The column position on the line. optional int32 column_position = 2; // The original mapping for this line mapping. optional OriginalMapping original_mapping = 3; } message OriginalMapping { // The original source file. optional string original_file = 1; // The line in the original file. optional int32 line_number = 2; // The column number on the line. optional int32 column_position = 3; // The original name of the identifier. optional string identifier = 4; } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapConsumer.java0000644000175000017500000000203612115204405027402 0ustar apoapo/* * 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.debugging.sourcemap; /** * A SourceMapConsumer is a SourceMapping provide that can parse from a raw * string. * * @author johnlenz@google.com (John Lenz) */ public interface SourceMapConsumer extends SourceMapping { /** * Parses the given contents containing a source map to provide initialize * a class providing SourceMapping. */ public void parse(String contents) throws SourceMapParseException; } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/Base64VLQ.java0000644000175000017500000001056112115204405025401 0ustar apoapo/* * Copyright 2011 The Closure Compiler Authors. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.google.debugging.sourcemap; import java.io.IOException; /** * We encode our variable length numbers as base64 encoded strings with * the least significant digit coming first. Each base64 digit encodes * a 5-bit value (0-31) and a continuation bit. Signed values can be * represented by using the least significant bit of the value as the * sign bit. * * @author johnlenz@google.com (John Lenz) */ final class Base64VLQ { // Utility class. private Base64VLQ() {} // A Base64 VLQ digit can represent 5 bits, so it is base-32. private static final int VLQ_BASE_SHIFT = 5; private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT; // A mask of bits for a VLQ digit (11111), 31 decimal. private static final int VLQ_BASE_MASK = VLQ_BASE-1; // The continuation bit is the 6th bit. private static final int VLQ_CONTINUATION_BIT = VLQ_BASE; /** * Converts from a two-complement value to a value where the sign bit is * is placed in the least significant bit. For example, as decimals: * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) */ private static int toVLQSigned(int value) { if (value < 0) { return ((-value) << 1) + 1; } else { return (value << 1) + 0; } } /** * Converts to a two-complement value from a value where the sign bit is * is placed in the least significant bit. For example, as decimals: * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 */ private static int fromVLQSigned(int value) { boolean negate = (value & 1) == 1; value = value >> 1; return negate ? -value : value; } /** * Writes a VLQ encoded value to the provide appendable. * @throws IOException */ public static void encode(Appendable out, int value) throws IOException { value = toVLQSigned(value); do { int digit = value & VLQ_BASE_MASK; value >>>= VLQ_BASE_SHIFT; if (value > 0) { digit |= VLQ_CONTINUATION_BIT; } out.append(Base64.toBase64(digit)); } while (value > 0); } /** * A simple interface for advancing through a sequence of characters, that * communicates that advance back to the source. */ interface CharIterator { boolean hasNext(); char next(); } /** * Decodes the next VLQValue from the provided CharIterator. */ public static int decode(CharIterator in) { int result = 0; boolean continuation; int shift = 0; do { char c = in.next(); int digit = Base64.fromBase64(c); continuation = (digit & VLQ_CONTINUATION_BIT) != 0; digit &= VLQ_BASE_MASK; result = result + (digit << shift); shift = shift + VLQ_BASE_SHIFT; } while (continuation); return fromVLQSigned(result); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapFormat.java0000644000175000017500000000174712115204405027047 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; /** * A list of currently support SourceMap format revisions. * @author johnlenz@google.com (John Lenz) */ public enum SourceMapFormat { /** The latest "stable" format */ DEFAULT, /** V1: The original Closure Inspector format */ V1, /** V2: A more compact format */ V2, /** V3: An even more compact format */ V3; } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapLineDecoder.java0000644000175000017500000001012212115204405027757 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.common.collect.Lists; import java.util.List; /** * Class for parsing the line maps in SourceMap v2. * * @author johnlenz@google.com (John Lenz) * @author jschorr@google.com (Joseph Schorr) */ class SourceMapLineDecoder { /** * Decodes a line in a character map into a list of mapping IDs. */ static List decodeLine(String lineSource) { return decodeLine(new StringParser(lineSource)); } private SourceMapLineDecoder() {} static LineEntry decodeLineEntry(String in, int lastId) { return decodeLineEntry(new StringParser(in), lastId); } private static LineEntry decodeLineEntry(StringParser reader, int lastId) { int repDigits = 0; // Determine the number of digits used for the repetition count. // Each "!" indicates another base64 digit. for (char peek = reader.peek(); peek == '!'; peek = reader.peek()) { repDigits++; reader.next(); // consume the "!" } int idDigits = 0; int reps = 0; if (repDigits == 0) { // No repetition digit escapes, so the next character represents the // number of digits in the id (bottom 2 bits) and the number of // repetitions (top 4 digits). char digit = reader.next(); int value = addBase64Digit(digit, 0); reps = (value >> 2); idDigits = (value & 3); } else { char digit = reader.next(); idDigits = addBase64Digit(digit, 0); int value = 0; for (int i = 0; i < repDigits; i++) { digit = reader.next(); value = addBase64Digit(digit, value); } reps = value; } // Adjust for 1 offset encoding. reps += 1; idDigits += 1; // Decode the id token. int value = 0; for (int i = 0; i < idDigits; i++) { char digit = reader.next(); value = addBase64Digit(digit, value); } int mappingId = getIdFromRelativeId(value, idDigits, lastId); return new LineEntry(mappingId, reps); } private static List decodeLine(StringParser reader) { List result = Lists.newArrayListWithCapacity(512); int lastId = 0; while (reader.hasNext()) { LineEntry entry = decodeLineEntry(reader, lastId); lastId = entry.id; for (int i=0; i < entry.reps; i++) { result.add(entry.id); } } return result; } /** * Build base64 number a digit at a time, most significant digit first. */ private static int addBase64Digit(char digit, int previousValue) { return (previousValue * 64) + Base64.fromBase64(digit); } /** * @return the id from the relative id. */ static int getIdFromRelativeId(int rawId, int digits, int lastId) { // The value range depends on the number of digits int base = 1 << (digits * 6); return ((rawId >= base/2) ? rawId - base : rawId) + lastId; } /** * Simple class for tracking a single entry in a line map. */ static class LineEntry { final int id; final int reps; public LineEntry(int id, int reps) { this.id = id; this.reps = reps; } } /** * A simple class for maintaining the current location * in the input. */ static class StringParser { final String content; int current = 0; StringParser(String content) { this.content = content; } char next() { return content.charAt(current++); } char peek() { return content.charAt(current); } boolean hasNext() { return current < content.length() -1; } } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapParseException.java0000644000175000017500000000153212115204405030540 0ustar apoapo/* * 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.debugging.sourcemap; /** * Throw if an invalid or unknown source map is encountered. */ public class SourceMapParseException extends Exception { public SourceMapParseException(String message) { super(message); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapGeneratorV1.java0000644000175000017500000004616512115204405027757 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import javax.annotation.Nullable; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes. * */ public class SourceMapGeneratorV1 implements SourceMapGenerator { private final static int UNMAPPED = -1; /** * A mapping from a given position in an input source file to a given position * in the generated code. */ static class Mapping { /** * A unique ID for this mapping for record keeping purposes. */ int id = UNMAPPED; /** * The input source file. */ String sourceFile; /** * The position of the code in the input source file. Both * the line number and the character index are indexed by * 1 for legacy reasons via the Rhino Node class. */ FilePosition originalPosition; /** * The starting position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition startPosition; /** * The ending position of the code in the generated source * file which this mapping represents. Indexed by 0. */ FilePosition endPosition; /** * The original name of the token found at the position * represented by this mapping (if any). */ String originalName; /** * Whether the mapping is actually used by the source map. */ boolean used = false; } private class MappingWriter { /** * Cache of escaped source file name. */ private String lastSourceFile = null; private String lastSourceFileEscaped = null; private int lastLine = 0; private String lastLineString = String.valueOf(0); /** * Appends the mapping to the given buffer. */ private void appendMappingTo( Mapping m, Appendable out) throws IOException { out.append("["); String sourceFile = m.sourceFile; // The source file rarely changes, so cache the escaped string. String escapedSourceFile; if (lastSourceFile != sourceFile) { // yes, s1 != s2, not !s1.equals(s2) lastSourceFile = sourceFile; lastSourceFileEscaped = escapeString(sourceFile); } escapedSourceFile = lastSourceFileEscaped; out.append(escapedSourceFile); out.append(","); int line = m.originalPosition.getLine(); if (line != lastLine) { lastLineString = String.valueOf(line); } String lineValue = lastLineString; out.append(lineValue); out.append(","); out.append(String.valueOf( m.originalPosition.getColumn())); if (m.originalName != null) { out.append(","); out.append(escapeString(m.originalName)); } out.append("]\n"); } /** * Add used mappings to the supplied Appendable. */ void appendMappings(Appendable out) throws IOException { for (Mapping m : mappings) { if (m.used) { appendMappingTo(m, out); } } } } /** * A pre-order traversal ordered list of mappings stored in this map. */ private List mappings = Lists.newArrayList(); /** * For validation store the start of the last mapping added. */ private Mapping lastMapping; /** * The position that the current source map is offset in the * buffer being used to generated the compiled source file. */ private FilePosition offsetPosition = new FilePosition(0, 0); /** * The position that the current source map is offset in the * generated the compiled source file by the addition of a * an output wrapper prefix. */ private FilePosition prefixPosition = new FilePosition(0, 0); /** * Escapes the given string for JSON. */ private static String escapeString(String value) { return Util.escapeString(value); } /** * Adds a mapping for the given node. Mappings must be added in order. * @param startPosition The position on the starting line * @param endPosition The position on the ending line. */ @Override public void addMapping( String sourceName, @Nullable String symbolName, FilePosition sourceStartPosition, FilePosition startPosition, FilePosition endPosition) { // Don't bother if there is not sufficient information to be useful. if (sourceName == null || sourceStartPosition.getLine() < 0) { return; } // Create the new mapping. Mapping mapping = new Mapping(); mapping.sourceFile = sourceName; mapping.originalPosition = sourceStartPosition; mapping.originalName = symbolName; // may be null // NOTE: When multiple outputs are concatenated together, the positions in // the mapping are relative to offsetPosition. if (offsetPosition.getLine() == 0 && offsetPosition.getColumn() == 0) { mapping.startPosition = startPosition; mapping.endPosition = endPosition; } else { // If the mapping is found on the first line, we need to offset // its character position by the number of characters found on // the *last* line of the source file to which the code is // being generated. int offsetLine = offsetPosition.getLine(); int startOffsetPosition = offsetPosition.getColumn(); int endOffsetPosition = offsetPosition.getColumn(); if (startPosition.getLine() > 0) { startOffsetPosition = 0; } if (endPosition.getLine() > 0) { endOffsetPosition = 0; } mapping.startPosition = new FilePosition(startPosition.getLine() + offsetLine, startPosition.getColumn() + startOffsetPosition); mapping.endPosition = new FilePosition(endPosition.getLine() + offsetLine, endPosition.getColumn() + endOffsetPosition); } // Validate the mappings are in a proper order. if (lastMapping != null) { int lastLine = lastMapping.startPosition.getLine(); int lastColumn = lastMapping.startPosition.getColumn(); int nextLine = mapping.startPosition.getLine(); int nextColumn = mapping.startPosition.getColumn(); Preconditions.checkState(nextLine > lastLine || (nextLine == lastLine && nextColumn >= lastColumn), "Incorrect source mappings order, previous : (%s,%s)\n" + "new : (%s,%s)\nnode : %s", lastLine, lastColumn, nextLine, nextColumn); } lastMapping = mapping; mappings.add(mapping); } /** * Sets the prefix used for wrapping the generated source file before * it is output. This ensures that the source map is adjusted as * needed. * * @param prefix The prefix that is added before the generated source code. */ @Override public void setWrapperPrefix(String prefix) { // Determine the current line and character position. int prefixLine = 0; int prefixIndex = 0; for (int i = 0; i < prefix.length(); ++i) { if (prefix.charAt(i) == '\n') { prefixLine++; prefixIndex = 0; } else { prefixIndex++; } } prefixPosition = new FilePosition(prefixLine, prefixIndex); } /** * Sets the source code that exists in the buffer to which the * generated code is being generated. This ensures that the source map * accurately reflects the fact that the source is being appended to * an existing buffer and as such, does not start at line 0, position 0 * but rather some other line and position. * * @param offsetLine The index of the current line being printed. * @param offsetIndex The column index of the current character being printed. */ @Override public void setStartingPosition(int offsetLine, int offsetIndex) { Preconditions.checkState(offsetLine >= 0); Preconditions.checkState(offsetIndex >= 0); offsetPosition = new FilePosition(offsetLine, offsetIndex); } /** * Resets the source map for reuse for the generation of a new source file. */ @Override public void reset() { mappings = Lists.newArrayList(); lastMapping = null; offsetPosition = new FilePosition(0, 0); prefixPosition = new FilePosition(0, 0); } /** * Appends the source map in LavaBug format to the given buffer. * * @param out The stream to which the map will be appended. * @param name The name of the generated source file that this source map * represents. */ @Override public void appendTo(Appendable out, String name) throws IOException { // Write the mappings out to the file. The format of the generated // source map is three sections, each delimited by a magic comment. // // The first section contains an array for each line of the generated // code, where each element in the array is the ID of the mapping which // best represents the index-th character found on that line of the // generated source code. // // The second section contains an array per generated line. Unused. // // The third and final section contains an array per line, each of which // represents a mapping with a unique ID. The mappings are added in order. // The array itself contains a tuple representing // ['source file', line, col (, 'original name')] // // Example for 2 lines of generated code (with line numbers added for // readability): // // 1) /** Begin line maps. **/{ "count": 2 } // 2) [0,0,0,0,0,0,1,1,1,1,2] // 3) [2,2,2,2,2,2,3,4,4,4,4,4] // 4) /** Begin file information. **/ // 5) [] // 6) [] // 7) /** Begin mapping definitions. **/ // 8) ["a.js", 1, 34] // 9) ["a.js", 5, 2] // 10) ["b.js", 1, 3, "event"] // 11) ["c.js", 1, 4] // 12) ["d.js", 3, 78, "foo"] int maxLine = prepMappings(); // Add the line character maps. out.append("/** Begin line maps. **/{ \"file\" : "); out.append(escapeString(name)); out.append(", \"count\": "); out.append(String.valueOf(maxLine + 1)); out.append(" }\n"); (new LineMapper(out)).appendLineMappings(); // Add the source file maps. out.append("/** Begin file information. **/\n"); // This section is unused but we need one entry per line to // prevent changing the format. for (int i = 0; i <= maxLine; ++i) { out.append("[]\n"); } // Add the mappings themselves. out.append("/** Begin mapping definitions. **/\n"); (new MappingWriter()).appendMappings(out); } /** * Assigns sequential ids to used mappings, and returns the last line mapped. */ private int prepMappings() throws IOException { // Mark any unused mappings. (new MappingTraversal()).traverse(new UsedMappingCheck()); // Renumber used mappings and keep track of the last line. int id = 0; int maxLine = 0; for (Mapping m : mappings) { if (m.used) { m.id = id++; int endPositionLine = m.endPosition.getLine(); maxLine = Math.max(maxLine, endPositionLine); } } // Adjust for the prefix. return maxLine + prefixPosition.getLine(); } private class LineMapper implements MappingVisitor { // The destination. private final Appendable out; // Whether the current line has had a value written yet. private boolean firstChar = true; private final static String UNMAPPED_STRING = "-1"; private int lastId = UNMAPPED; private String lastIdString = UNMAPPED_STRING; LineMapper(Appendable out) { this.out = out; } /** * As each segment is visited write out the appropriate line mapping. */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { int id = (m != null) ? m.id : UNMAPPED; if (lastId != id) { // Prevent the creation of unnecessary temporary stings for often // repeated values. lastIdString = (id == UNMAPPED) ? UNMAPPED_STRING : String.valueOf(id); lastId = id; } String idString = lastIdString; for (int i = line; i <= nextLine; i++) { if (i == nextLine) { for (int j = col; j < nextCol; j++) { addCharEntry(idString); } break; } closeLine(); openLine(); // Set the starting location for the next line. col = 0; } } // Append the line mapping entries. void appendLineMappings() throws IOException { // Start the first line. openLine(); (new MappingTraversal()).traverse(this); // And close the final line. closeLine(); } /** * Begin the entry for a new line. */ private void openLine() throws IOException { if (out != null) { out.append("["); this.firstChar = true; } } /** * End the entry for a line. */ private void closeLine() throws IOException { if (out != null) { out.append("]\n"); } } /** * Add a new char position entry. * @param id The mapping id to record. */ private void addCharEntry(String id) throws IOException { if (out != null) { if (firstChar) { firstChar = false; } else { out.append(","); } out.append(id); } } } /** * Mark any visited mapping as "used". */ private class UsedMappingCheck implements MappingVisitor { /** * @throws IOException */ @Override public void visit(Mapping m, int line, int col, int nextLine, int nextCol) throws IOException { if (m != null) { m.used = true; } } } private interface MappingVisitor { /** * @param m The mapping for the current code segment. null if the segment * is unmapped. * @param line The starting line for this code segment. * @param col The starting column for this code segment. * @param endLine The ending line * @param endCol The ending column * @throws IOException */ void visit(Mapping m, int line, int col, int endLine, int endCol) throws IOException; } /** * Walk the mappings and visit each segment of the mappings, unmapped * segments are visited with a null mapping, unused mapping are not visited. */ private class MappingTraversal { // The last line and column written private int line; private int col; MappingTraversal() { } // Append the line mapping entries. void traverse(MappingVisitor v) throws IOException { // The mapping list is ordered as a pre-order traversal. The mapping // positions give us enough information to rebuild the stack and this // allows the building of the source map in O(n) time. Deque stack = new ArrayDeque(); for (Mapping m : mappings) { // Find the closest ancestor of the current mapping: // An overlapping mapping is an ancestor of the current mapping, any // non-overlapping mappings are siblings (or cousins) and must be // closed in the reverse order of when they encountered. while (!stack.isEmpty() && !isOverlapped(stack.peek(), m)) { Mapping previous = stack.pop(); maybeVisit(v, previous); } // Any gaps between the current line position and the start of the // current mapping belong to the parent. Mapping parent = stack.peek(); maybeVisitParent(v, parent, m); stack.push(m); } // There are no more children to be had, simply close the remaining // mappings in the reverse order of when they encountered. while (!stack.isEmpty()) { Mapping m = stack.pop(); maybeVisit(v, m); } } /** * @return The line adjusted for the prefix position. */ private int getAdjustedLine(FilePosition p) { return p.getLine() + prefixPosition.getLine(); } /** * @return The column adjusted for the prefix position. */ private int getAdjustedCol(FilePosition p) { int rawLine = p.getLine(); int rawCol = p.getColumn(); // Only the first line needs the character position adjusted. return (rawLine != 0) ? rawCol : rawCol + prefixPosition.getColumn(); } /** * @return Whether m1 ends before m2 starts. */ private boolean isOverlapped(Mapping m1, Mapping m2) { // No need to use adjusted values here, relative positions are sufficient. int l1 = m1.endPosition.getLine(); int l2 = m2.startPosition.getLine(); int c1 = m1.endPosition.getColumn(); int c2 = m2.startPosition.getColumn(); return (l1 == l2 && c1 >= c2) || l1 > l2; } /** * Write any needed entries from the current position to the end of the * provided mapping. */ private void maybeVisit(MappingVisitor v, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.endPosition); int nextCol = getAdjustedCol(m.endPosition); // If this anything remaining in this mapping beyond the // current line and column position, write it out now. if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, m, nextLine, nextCol); } } /** * Write any needed entries to complete the provided mapping. */ private void maybeVisitParent(MappingVisitor v, Mapping parent, Mapping m) throws IOException { int nextLine = getAdjustedLine(m.startPosition); int nextCol = getAdjustedCol(m.startPosition); // If the previous value is null, no mapping exists. Preconditions.checkState(line < nextLine || col <= nextCol); if (line < nextLine || (line == nextLine && col < nextCol)) { visit(v, parent, nextLine, nextCol); } } /** * Write any entries needed between the current position the next position * and update the current position. */ private void visit(MappingVisitor v, Mapping m, int nextLine, int nextCol) throws IOException { Preconditions.checkState(line <= nextLine); Preconditions.checkState(line < nextLine || col < nextCol); if (line == nextLine && col == nextCol) { // Nothing to do. Preconditions.checkState(false); return; } v.visit(m, line, col, nextLine, nextCol); line = nextLine; col = nextCol; } } @Override public void validate(boolean validate) { // No additional validation to do. } @Override public void appendIndexMapTo( Appendable out, String name, List appSections) { throw new UnsupportedOperationException(); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapSection.java0000644000175000017500000000521112115204405027211 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; /** * A class representing a partial source map. * @author johnlenz@google.com (John Lenz) */ public class SourceMapSection { /** * A URL for a valid source map file that represents a section of a generate * source file such as when multiple files are concatenated together. */ private final String value; private final int line; private final int column; private final SectionType type; public static enum SectionType { URL, MAP } /** * @param sectionUrl The URL for the partial source map * @param line The number of lines into the file where the represented section * starts. * @param column The number of characters into the line where the represented * section starts. * @deprecated */ @Deprecated public SourceMapSection(String sectionUrl, int line, int column) { this.type = SectionType.URL; this.value = sectionUrl; this.line = line; this.column = column; } private SourceMapSection( SectionType type, String value, int line, int column) { this.type = type; this.value = value; this.line = line; this.column = column; } public static SourceMapSection forMap(String value, int line, int column) { return new SourceMapSection(SectionType.MAP, value, line, column); } public static SourceMapSection forURL(String value, int line, int column) { return new SourceMapSection(SectionType.URL, value, line, column); } public SectionType getSectionType() { return this.type; } /** * @return the name of the map * @deprecated */ @Deprecated public String getSectionUrl() { assert(type.equals(SectionType.URL)); return value; } /** * @return the value that represents the map for this section. */ public String getSectionValue() { return value; } /** * @return the starting line for this section */ public int getLine() { return line; } /** * @return the column for this section */ public int getColumn() { return column; } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/package.html0000644000175000017500000000026312115204405025406 0ustar apoapo Provides utilities to the creation and use of source maps. closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapGeneratorFactory.java0000644000175000017500000000262212115204405031066 0ustar apoapo/* * 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.debugging.sourcemap; /** * @author johnlenz@google.com (John Lenz) */ public class SourceMapGeneratorFactory { /** * @return The appropriate source map object for the given source map format. */ public static SourceMapGenerator getInstance() { return getInstance(SourceMapFormat.DEFAULT); } /** * @return The appropriate source map object for the given source map format. */ public static SourceMapGenerator getInstance(SourceMapFormat format) { switch (format) { case V1: return new SourceMapGeneratorV1(); case V2: return new SourceMapGeneratorV2(); case DEFAULT: case V3: return new SourceMapGeneratorV3(); default: throw new IllegalStateException("unsupported source map format"); } } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapSupplier.java0000644000175000017500000000205612115204405027414 0ustar apoapo/* * Copyright 2011 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.debugging.sourcemap; import java.io.IOException; /** * A class for mapping source map names to the actual contents. Used * when parsing index maps. * * @author johnlenz@google.com (John Lenz) */ public interface SourceMapSupplier { /** * @param url The URL of the source map. * @return The contents of the map associated with the URL * @throws IOException */ String getSourceMap(String url) throws IOException; }closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapConsumerV2.java0000644000175000017500000001311612115204405027613 0ustar apoapo/* * 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.debugging.sourcemap; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.proto.Mapping.OriginalMapping; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.List; import java.util.Map; /** * Class for parsing version 2 of the SourceMap format, as produced by the * Closure Compiler, etc. * @author johnlenz@google.com (John Lenz) * @author jschorr@google.com (Joseph Schorr) */ public class SourceMapConsumerV2 implements SourceMapConsumer { /** * The character map for each line. If a line does not have an entry, * then it has not yet been decoded. */ private Map> characterMap = null; /** * The undecoded line maps. Will be accessed to decode lines as needed. */ private JSONArray lineMaps = null; /** * Map of Mapping IDs to the actual mapping object. */ private List mappings; public SourceMapConsumerV2() {} /** * Parses the given contents containing a source map. */ @Override public void parse(String contents) throws SourceMapParseException { try { JSONObject sourceMapRoot = new JSONObject(contents); parse(sourceMapRoot); } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } /** * Parses the given contents containing a source map. */ public void parse(JSONObject sourceMapRoot) throws SourceMapParseException { try { parseInternal(sourceMapRoot); } catch (JSONException ex) { throw new SourceMapParseException("JSON parse exception: " + ex); } } /** * Parses the given contents as version 2 of a SourceMap. */ private void parseInternal(JSONObject sourceMapRoot) throws JSONException, SourceMapParseException { // Check basic assertions about the format. int version = sourceMapRoot.getInt("version"); if (version != 2) { throw new SourceMapParseException("Unknown version: " + version); } String file = sourceMapRoot.getString("file"); if (file.isEmpty()) { throw new SourceMapParseException("File entry is missing or empty"); } int lineCount = sourceMapRoot.getInt("lineCount"); lineMaps = sourceMapRoot.getJSONArray("lineMaps"); if (lineCount != lineMaps.length()) { throw new SourceMapParseException( "lineMaps length does not match lineCount"); } // Build an empty character map. The character map will be filled in as // lines are requested. characterMap = Maps.newHashMap(); JSONArray sources = sourceMapRoot.getJSONArray("sources"); JSONArray names = sourceMapRoot.has("names") ? sourceMapRoot.getJSONArray("names") : null; // Create each of the OriginalMappings. JSONArray jsonMappings = sourceMapRoot.getJSONArray("mappings"); mappings = Lists.newArrayListWithCapacity(lineCount); for (int i = 0; i < jsonMappings.length(); i++) { JSONArray entry = jsonMappings.getJSONArray(i); // The name can be accessed in two ways: Directly (i.e. a string) or // indirectly (i.e. an index into the name map). String name = entry.optString(3, ""); if (names != null) { try { int nameIndex = entry.getInt(3); name = names.getString(nameIndex); } catch (JSONException e) { } } // Build the new OriginalMapping entry. String sourceFile = sources.getString(entry.getInt(0)); int lineNumber = entry.getInt(1); int column = entry.getInt(2); OriginalMapping.Builder builder = OriginalMapping.newBuilder() .setOriginalFile(sourceFile) .setLineNumber(lineNumber) .setColumnPosition(column) .setIdentifier(name); mappings.add(builder.build()); } } @Override public OriginalMapping getMappingForLine(int lineNumber, int columnIndex) { // Normalize the line and column numbers to 0. lineNumber--; columnIndex--; if (lineNumber >= lineMaps.length()) { return null; } Preconditions.checkState(lineNumber >= 0, "Line number must be >= 0"); Preconditions.checkState(columnIndex >= 0, "Column index must be >= 0"); if (!characterMap.containsKey(lineNumber)) { // Parse the line map entry and place it into the character map. try { characterMap.put(lineNumber, SourceMapLineDecoder.decodeLine(lineMaps.getString(lineNumber))); } catch (JSONException jse) { throw new IllegalStateException( "JSON exception when retrieving line map", jse); } } List map = characterMap.get(lineNumber); if (map == null || map.size() <= columnIndex) { return null; } int index = map.get(columnIndex); if (index == -1) { return null; } Preconditions.checkState(index < mappings.size(), "Invalid mapping reference"); return mappings.get(index); } } closure-compiler-20130227+dfsg1/src/com/google/debugging/sourcemap/SourceMapGenerator.java0000644000175000017500000000705312115204405027541 0ustar apoapo/* * 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.debugging.sourcemap; import java.io.IOException; import java.util.List; import javax.annotation.Nullable; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes * * @author johnlenz@google.com (John Lenz) */ public interface SourceMapGenerator { /** * Appends the source map to the given buffer. * * @param out The stream to which the map will be appended. * @param name The name of the generated source file that this source map * represents. */ void appendTo(Appendable out, String name) throws IOException; /** * Appends the index source map to the given buffer. * * @param out The stream to which the map will be appended. * @param name The name of the generated source file that this source map * represents. * @param sections An ordered list of map sections to include in the index. * @throws IOException */ void appendIndexMapTo( Appendable out, String name, List sections) throws IOException; /** * Resets the source map for reuse. A reset needs to be called between * each generated output file. */ void reset(); /** * Adds a mapping for the given node. Mappings must be added in order. * @param sourceName The file name to use in the generate source map * to represent this source. * @param symbolName The symbol name associated with this position in the * source map. * @param sourceStartPosition The starting position in the original source for * represented range outputStartPosition to outputEndPosition in the * generated file. * @param outputStartPosition The position on the starting line * @param outputEndPosition The position on the ending line. */ void addMapping(String sourceName, @Nullable String symbolName, FilePosition sourceStartPosition, FilePosition outputStartPosition, FilePosition outputEndPosition); /** * Sets the prefix used for wrapping the generated source file before * it is written. This ensures that the source map is adjusted for the * change in character offsets. * * @param prefix The prefix that is added before the generated source code. */ void setWrapperPrefix(String prefix); /** * Sets the source code that exists in the buffer for which the * generated code is being generated. This ensures that the source map * accurately reflects the fact that the source is being appended to * an existing buffer and as such, does not start at line 0, position 0 * but rather some other line and position. * * @param offsetLine The index of the current line being printed. * @param offsetIndex The column index of the current character being printed. */ void setStartingPosition(int offsetLine, int offsetIndex); /** * Whether to perform additional validation on the source map. * @param validate */ void validate(boolean validate); } closure-compiler-20130227+dfsg1/src/com/google/javascript/0000755000175000017500000000000012115204405021341 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/0000755000175000017500000000000012115204405022634 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JsAst.java0000644000175000017500000000613312115204405024526 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.parsing.ParserRunner; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.logging.Logger; /** * Generates an AST for a JavaScript source file. * */ public class JsAst implements SourceAst { private static final Logger logger_ = Logger.getLogger(JsAst.class.getName()); private static final long serialVersionUID = 1L; private transient InputId inputId; private transient SourceFile sourceFile; private String fileName; private Node root; public JsAst(SourceFile sourceFile) { this.inputId = new InputId(sourceFile.getName()); this.sourceFile = sourceFile; this.fileName = sourceFile.getName(); } @Override public Node getAstRoot(AbstractCompiler compiler) { if (root == null) { parse(compiler); root.setInputId(inputId); } return root; } @Override public void clearAst() { root = null; // While we're at it, clear out any saved text in the source file on // the assumption that if we're dumping the parse tree, then we probably // assume regenerating everything else is a smart idea also. sourceFile.clearCachedSource(); } @Override public InputId getInputId() { return inputId; } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { Preconditions.checkState(fileName.equals(file.getName())); sourceFile = file; } private void parse(AbstractCompiler compiler) { try { logger_.fine("Parsing: " + sourceFile.getName()); ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(), compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); root = result.ast; compiler.setOldParseTree(sourceFile.getName(), result.oldAst); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } if (root == null || compiler.hasHaltingErrors()) { // There was a parse error or IOException, so use a dummy block. root = IR.script(); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.setStaticSourceFile(sourceFile); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ProcessTweaks.java0000644000175000017500000004631212115204405026302 0ustar apoapo/* * Copyright 2011 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.base.CharMatcher; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.SortedMap; /** * Process goog.tweak primitives. Checks that: *
    *
  • parameters to goog.tweak.register* are literals of the correct type. *
  • the parameter to goog.tweak.get* is a string literal. *
  • parameters to goog.tweak.overrideDefaultValue are literals of the correct * type. *
  • tweak IDs passed to goog.tweak.get* and goog.tweak.overrideDefaultValue * correspond to registered tweaks. *
  • all calls to goog.tweak.register* and goog.tweak.overrideDefaultValue are * within the top-level context. *
  • each tweak is registered only once. *
  • calls to goog.tweak.overrideDefaultValue occur before the call to the * corresponding goog.tweak.register* function. *
* @author agrieve@google.com (Andrew Grieve) */ class ProcessTweaks implements CompilerPass { private final AbstractCompiler compiler; private final boolean stripTweaks; private final SortedMap compilerDefaultValueOverrides; private static final CharMatcher ID_MATCHER = CharMatcher.inRange('a', 'z'). or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.anyOf("0123456789_.")); // Warnings and Errors. static final DiagnosticType UNKNOWN_TWEAK_WARNING = DiagnosticType.warning( "JSC_UNKNOWN_TWEAK_WARNING", "no tweak registered with ID {0}"); static final DiagnosticType TWEAK_MULTIPLY_REGISTERED_ERROR = DiagnosticType.error( "JSC_TWEAK_MULTIPLY_REGISTERED_ERROR", "Tweak {0} has already been registered."); static final DiagnosticType NON_LITERAL_TWEAK_ID_ERROR = DiagnosticType.error( "JSC_NON_LITERAL_TWEAK_ID_ERROR", "tweak ID must be a string literal"); static final DiagnosticType INVALID_TWEAK_DEFAULT_VALUE_WARNING = DiagnosticType.warning( "JSC_INVALID_TWEAK_DEFAULT_VALUE_WARNING", "tweak {0} registered with {1} must have a default value that is a " + "literal of type {2}"); static final DiagnosticType NON_GLOBAL_TWEAK_INIT_ERROR = DiagnosticType.error( "JSC_NON_GLOBAL_TWEAK_INIT_ERROR", "tweak declaration {0} must occur in the global scope"); static final DiagnosticType TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR = DiagnosticType.error( "JSC_TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR", "Cannot override the default value of tweak {0} after it has been " + "registered"); static final DiagnosticType TWEAK_WRONG_GETTER_TYPE_WARNING = DiagnosticType.warning( "JSC_TWEAK_WRONG_GETTER_TYPE_WARNING", "tweak getter function {0} used for tweak registered using {1}"); static final DiagnosticType INVALID_TWEAK_ID_ERROR = DiagnosticType.error( "JSC_INVALID_TWEAK_ID_ERROR", "tweak ID contains illegal characters. Only letters, numbers, _ " + "and . are allowed"); /** * An enum of goog.tweak functions. */ private static enum TweakFunction { REGISTER_BOOLEAN("goog.tweak.registerBoolean", "boolean", Token.TRUE, Token.FALSE), REGISTER_NUMBER("goog.tweak.registerNumber", "number", Token.NUMBER), REGISTER_STRING("goog.tweak.registerString", "string", Token.STRING), OVERRIDE_DEFAULT_VALUE("goog.tweak.overrideDefaultValue"), GET_COMPILER_OVERRIDES("goog.tweak.getCompilerOverrides_"), GET_BOOLEAN("goog.tweak.getBoolean", REGISTER_BOOLEAN), GET_NUMBER("goog.tweak.getNumber", REGISTER_NUMBER), GET_STRING("goog.tweak.getString", REGISTER_STRING); final String name; final String expectedTypeName; final int validNodeTypeA; final int validNodeTypeB; final TweakFunction registerFunction; TweakFunction(String name) { this(name, null, Token.ERROR, Token.ERROR, null); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA) { this(name, expectedTypeName, validNodeTypeA, Token.ERROR, null); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA, int validNodeTypeB) { this(name, expectedTypeName, validNodeTypeA, validNodeTypeB, null); } TweakFunction(String name, TweakFunction registerFunction) { this(name, null, Token.ERROR, Token.ERROR, registerFunction); } TweakFunction(String name, String expectedTypeName, int validNodeTypeA, int validNodeTypeB, TweakFunction registerFunction) { this.name = name; this.expectedTypeName = expectedTypeName; this.validNodeTypeA = validNodeTypeA; this.validNodeTypeB = validNodeTypeB; this.registerFunction = registerFunction; } boolean isValidNodeType(int type) { return type == validNodeTypeA || type == validNodeTypeB; } boolean isCorrectRegisterFunction(TweakFunction registerFunction) { Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); default: throw new IllegalStateException(); } } } // A map of function name -> TweakFunction. private static final Map TWEAK_FUNCTIONS_MAP; static { TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); for (TweakFunction func : TweakFunction.values()) { TWEAK_FUNCTIONS_MAP.put(func.getName(), func); } } ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, Map compilerDefaultValueOverrides) { this.compiler = compiler; this.stripTweaks = stripTweaks; // Having the map sorted is required for the unit tests to be deterministic. this.compilerDefaultValueOverrides = Maps.newTreeMap(); this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); } @Override public void process(Node externs, Node root) { CollectTweaksResult result = collectTweaks(root); applyCompilerDefaultValueOverrides(result.tweakInfos); boolean changed = false; if (stripTweaks) { changed = stripAllCalls(result.tweakInfos); } else if (!compilerDefaultValueOverrides.isEmpty()) { changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); } if (changed) { compiler.reportCodeChange(); } } /** * Passes the compiler default value overrides to the JS by replacing calls * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; */ private boolean replaceGetCompilerOverridesCalls( List calls) { for (TweakFunctionCall call : calls) { Node callNode = call.callNode; Node objNode = createCompilerDefaultValueOverridesVarNode(callNode); callNode.getParent().replaceChild(callNode, objNode); } return !calls.isEmpty(); } /** * Removes all CALL nodes in the given TweakInfos, replacing calls to getter * functions with the tweak's default value. */ private boolean stripAllCalls(Map tweakInfos) { for (TweakInfo tweakInfo : tweakInfos.values()) { boolean isRegistered = tweakInfo.isRegistered(); for (TweakFunctionCall functionCall : tweakInfo.functionCalls) { Node callNode = functionCall.callNode; Node parent = callNode.getParent(); if (functionCall.tweakFunc.isGetterFunction()) { Node newValue; if (isRegistered) { newValue = tweakInfo.getDefaultValueNode().cloneNode(); } else { // When we find a getter of an unregistered tweak, there has // already been a warning about it, so now just use a default // value when stripping. TweakFunction registerFunction = functionCall.tweakFunc.registerFunction; newValue = registerFunction.createDefaultValueNode(); } parent.replaceChild(callNode, newValue); } else { Node voidZeroNode = IR.voidNode(IR.number(0).srcref(callNode)) .srcref(callNode); parent.replaceChild(callNode, voidZeroNode); } } } return !tweakInfos.isEmpty(); } /** * Creates a JS object that holds a map of tweakId -> default value override. */ private Node createCompilerDefaultValueOverridesVarNode( Node sourceInformationNode) { Node objNode = IR.objectlit().srcref(sourceInformationNode); for (Entry entry : compilerDefaultValueOverrides.entrySet()) { Node objKeyNode = IR.stringKey(entry.getKey()) .copyInformationFrom(sourceInformationNode); Node objValueNode = entry.getValue().cloneNode() .copyInformationFrom(sourceInformationNode); objKeyNode.addChildToBack(objValueNode); objNode.addChildToBack(objKeyNode); } return objNode; } /** Sets the default values of tweaks based on compiler options. */ private void applyCompilerDefaultValueOverrides( Map tweakInfos) { for (Entry entry : compilerDefaultValueOverrides.entrySet()) { String tweakId = entry.getKey(); TweakInfo tweakInfo = tweakInfos.get(tweakId); if (tweakInfo == null) { compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); } else { TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; Node value = entry.getValue(); if (!registerFunc.isValidNodeType(value.getType())) { compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } else { tweakInfo.defaultValueNode = value; } } } } /** * Finds all calls to goog.tweak functions and emits warnings/errors if any * of the calls have issues. * @return A map of {@link TweakInfo} structures, keyed by tweak ID. */ private CollectTweaksResult collectTweaks(Node root) { CollectTweaks pass = new CollectTweaks(); NodeTraversal.traverse(compiler, root, pass); Map tweakInfos = pass.allTweaks; for (TweakInfo tweakInfo: tweakInfos.values()) { tweakInfo.emitAllWarnings(); } return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls); } private final static class CollectTweaksResult { final Map tweakInfos; final List getOverridesCalls; CollectTweaksResult(Map tweakInfos, List getOverridesCalls) { this.tweakInfos = tweakInfos; this.getOverridesCalls = getOverridesCalls; } } /** * Processes all calls to goog.tweak functions. */ private final class CollectTweaks extends AbstractPostOrderCallback { final Map allTweaks = Maps.newHashMap(); final List getOverridesCalls = Lists.newArrayList(); @SuppressWarnings("incomplete-switch") @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } String callName = n.getFirstChild().getQualifiedName(); TweakFunction tweakFunc = TWEAK_FUNCTIONS_MAP.get(callName); if (tweakFunc == null) { return; } if (tweakFunc == TweakFunction.GET_COMPILER_OVERRIDES) { getOverridesCalls.add( new TweakFunctionCall(t.getSourceName(), tweakFunc, n)); return; } // Ensure the first parameter (the tweak ID) is a string literal. Node tweakIdNode = n.getFirstChild().getNext(); if (!tweakIdNode.isString()) { compiler.report(t.makeError(tweakIdNode, NON_LITERAL_TWEAK_ID_ERROR)); return; } String tweakId = tweakIdNode.getString(); // Make sure there is a TweakInfo structure for it. TweakInfo tweakInfo = allTweaks.get(tweakId); if (tweakInfo == null) { tweakInfo = new TweakInfo(tweakId); allTweaks.put(tweakId, tweakInfo); } switch (tweakFunc) { case REGISTER_BOOLEAN: case REGISTER_NUMBER: case REGISTER_STRING: // Ensure the ID contains only valid characters. if (!ID_MATCHER.matchesAllOf(tweakId)) { compiler.report(t.makeError(tweakIdNode, INVALID_TWEAK_ID_ERROR)); } // Ensure tweaks are registered in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweaks are registered only once. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_MULTIPLY_REGISTERED_ERROR, tweakId)); break; } Node tweakDefaultValueNode = tweakIdNode.getNext().getNext(); tweakInfo.addRegisterCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case OVERRIDE_DEFAULT_VALUE: // Ensure tweaks overrides occur in the global scope. if (!t.inGlobalScope()) { compiler.report( t.makeError(n, NON_GLOBAL_TWEAK_INIT_ERROR, tweakId)); break; } // Ensure tweak overrides occur before the tweak is registered. if (tweakInfo.isRegistered()) { compiler.report( t.makeError(n, TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR, tweakId)); break; } tweakDefaultValueNode = tweakIdNode.getNext(); tweakInfo.addOverrideDefaultValueCall(t.getSourceName(), tweakFunc, n, tweakDefaultValueNode); break; case GET_BOOLEAN: case GET_NUMBER: case GET_STRING: tweakInfo.addGetterCall(t.getSourceName(), tweakFunc, n); } } } /** * Holds information about a call to a goog.tweak function. */ private static final class TweakFunctionCall { final String sourceName; final TweakFunction tweakFunc; final Node callNode; final Node valueNode; TweakFunctionCall(String sourceName, TweakFunction tweakFunc, Node callNode) { this(sourceName, tweakFunc, callNode, null); } TweakFunctionCall(String sourceName, TweakFunction tweakFunc, Node callNode, Node valueNode) { this.sourceName = sourceName; this.callNode = callNode; this.tweakFunc = tweakFunc; this.valueNode = valueNode; } Node getIdNode() { return callNode.getFirstChild().getNext(); } } /** * Stores information about a single tweak. */ private final class TweakInfo { final String tweakId; final List functionCalls; TweakFunctionCall registerCall; Node defaultValueNode; TweakInfo(String tweakId) { this.tweakId = tweakId; functionCalls = Lists.newArrayList(); } /** * If this tweak is registered, then looks for type warnings in default * value parameters and getter functions. If it is not registered, emits an * error for each function call. */ void emitAllWarnings() { if (isRegistered()) { emitAllTypeWarnings(); } else { emitUnknownTweakErrors(); } } /** * Emits a warning for each default value parameter that has the wrong type * and for each getter function that was used for the wrong type of tweak. */ void emitAllTypeWarnings() { for (TweakFunctionCall call : functionCalls) { Node valueNode = call.valueNode; TweakFunction tweakFunc = call.tweakFunc; TweakFunction registerFunc = registerCall.tweakFunc; if (valueNode != null) { // For register* and overrideDefaultValue calls, ensure the default // value is a literal of the correct type. if (!registerFunc.isValidNodeType(valueNode.getType())) { compiler.report(JSError.make(call.sourceName, valueNode, INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } } else if (tweakFunc.isGetterFunction()) { // For getter calls, ensure the correct getter was used. if (!tweakFunc.isCorrectRegisterFunction(registerFunc)) { compiler.report(JSError.make(call.sourceName, call.callNode, TWEAK_WRONG_GETTER_TYPE_WARNING, tweakFunc.getName(), registerFunc.getName())); } } } } /** * Emits an error for each function call that was found. */ void emitUnknownTweakErrors() { for (TweakFunctionCall call : functionCalls) { compiler.report(JSError.make(call.sourceName, call.getIdNode(), UNKNOWN_TWEAK_WARNING, tweakId)); } } void addRegisterCall(String sourceName, TweakFunction tweakFunc, Node callNode, Node defaultValueNode) { registerCall = new TweakFunctionCall(sourceName, tweakFunc, callNode, defaultValueNode); functionCalls.add(registerCall); } void addOverrideDefaultValueCall(String sourceName, TweakFunction tweakFunc, Node callNode, Node defaultValueNode) { functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode, defaultValueNode)); this.defaultValueNode = defaultValueNode; } void addGetterCall(String sourceName, TweakFunction tweakFunc, Node callNode) { functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode)); } boolean isRegistered() { return registerCall != null; } Node getDefaultValueNode() { Preconditions.checkState(isRegistered()); // Use calls to goog.tweak.overrideDefaultValue() first. if (defaultValueNode != null) { return defaultValueNode; } // Use the value passed to the register function next. if (registerCall.valueNode != null) { return registerCall.valueNode; } // Otherwise, use the default value for the tweak's type. return registerCall.tweakFunc.createDefaultValueNode(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PhaseOptimizer.java0000644000175000017500000002734312115204405026453 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Logger; /** * Optimizes the order of compiler passes. * @author nicksantos@google.com (Nick Santos) */ class PhaseOptimizer implements CompilerPass { // This ordering is computed offline by running with compute_phase_ordering. @VisibleForTesting static final List OPTIMAL_ORDER = ImmutableList.of( "deadAssignmentsElimination", "inlineFunctions", "removeUnusedPrototypeProperties", "removeUnreachableCode", "removeUnusedVars", "minimizeExitPoints", "inlineVariables", "collapseObjectLiterals", "peepholeOptimizations"); static final int MAX_LOOPS = 100; static final String OPTIMIZE_LOOP_ERROR = "Fixed point loop exceeded the maximum number of iterations."; // Only used by Loop/process, but enum types can't be local enum State { RUN_PASSES_NOT_RUN_IN_PREV_ITER, RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER } private static final Logger logger = Logger.getLogger(PhaseOptimizer.class.getName()); private final List passes = Lists.newArrayList(); private final AbstractCompiler compiler; private final PerformanceTracker tracker; private boolean loopMutex = false; private PassFactory sanityCheck = null; private boolean printAstHashcodes = false; private double progress = 0.0; private double progressStep = 0.0; // The following static properties are only used for computing optimal // phase orderings. They should not be touched by normal compiler runs. private static boolean randomizeLoops = false; private static List> loopsRun = Lists.newArrayList(); private final ProgressRange progressRange; /** * @param compiler the compiler that owns/creates this. * @param tracker an optional performance tracker * @param progressRange the progress range for the process function or null * if progress should not be reported. */ PhaseOptimizer(AbstractCompiler compiler, PerformanceTracker tracker, ProgressRange progressRange) { this.compiler = compiler; this.tracker = tracker; this.progressRange = progressRange; } /** * Randomizes loops. This should only be used when computing optimal phase * orderings. */ static void randomizeLoops() { randomizeLoops = true; } /** * Get the phase ordering of loops during this run. * Returns an empty list when the loops are not randomized. */ static List> getLoopsRun() { return loopsRun; } /** * Clears the phase ordering of loops during this run. */ static void clearLoopsRun() { loopsRun.clear(); } /** * Add the passes generated by the given factories to the compile sequence. * * Automatically pulls multi-run passes into fixed point loops. If there * are 1 or more multi-run passes in a row, they will run together in * the same fixed point loop. The passes will run until they are finished * making changes. * * The PhaseOptimizer is free to tweak the order and frequency of multi-run * passes in a fixed-point loop. */ void consume(List factories) { Loop currentLoop = new Loop(); boolean isCurrentLoopPopulated = false; for (PassFactory factory : factories) { if (factory.isOneTimePass()) { if (isCurrentLoopPopulated) { passes.add(currentLoop); currentLoop = new Loop(); isCurrentLoopPopulated = false; } addOneTimePass(factory); } else { currentLoop.addLoopedPass(factory); isCurrentLoopPopulated = true; } } if (isCurrentLoopPopulated) { passes.add(currentLoop); } } /** * Add the pass generated by the given factory to the compile sequence. * This pass will be run once. */ void addOneTimePass(PassFactory factory) { passes.add(new NamedPass(factory)); } /** * Add a loop to the compile sequence. This loop will continue running * until the AST stops changing. * @return The loop structure. Pass suppliers should be added to the loop. */ Loop addFixedPointLoop() { Loop loop = new Loop(); passes.add(loop); return loop; } /** * Adds a sanity checker to be run after every pass. Intended for development. */ void setSanityCheck(PassFactory sanityCheck) { this.sanityCheck = sanityCheck; } /** * Sets the hashcode of the AST to be logged every pass. Intended for development. */ void setPrintAstHashcodes(boolean printAstHashcodes) { this.printAstHashcodes = printAstHashcodes; } /** * Run all the passes in the optimizer. */ @Override public void process(Node externs, Node root) { progress = 0.0; progressStep = 0.0; if (progressRange != null) { progressStep = (progressRange.maxValue - progressRange.initialValue) / passes.size(); progress = progressRange.initialValue; } for (CompilerPass pass : passes) { pass.process(externs, root); if (hasHaltingErrors()) { return; } } } private void maybePrintAstHashcodes(String passName, Node root) { if (printAstHashcodes) { String hashCodeMsg = "AST hashCode after " + passName + ": " + compiler.toSource(root).hashCode(); System.err.println(hashCodeMsg); compiler.addToDebugLog(hashCodeMsg); } } /** * Runs the sanity check if it is available. */ void maybeSanityCheck(Node externs, Node root) { if (sanityCheck != null) { sanityCheck.create(compiler).process(externs, root); } } private boolean hasHaltingErrors() { return compiler.hasHaltingErrors(); } /** * A single compiler pass. */ class NamedPass implements CompilerPass { final String name; private final PassFactory factory; private Tracer tracer; NamedPass(PassFactory factory) { this.name = factory.getName(); this.factory = factory; } @Override public void process(Node externs, Node root) { logger.fine(name); if (tracker != null) { tracker.recordPassStart(name, factory.isOneTimePass()); } tracer = new Tracer("JSCompiler"); // Delay the creation of the actual pass until *after* all previous passes // have been processed. // Some precondition checks rely on this, eg, in CoalesceVariableNames. factory.create(compiler).process(externs, root); try { if (progressRange == null) { compiler.setProgress(-1, name); } else { progress += progressStep; compiler.setProgress(progress, name); } if (tracker != null) { tracker.recordPassStop(name, tracer.stop()); } maybePrintAstHashcodes(name, root); maybeSanityCheck(externs, root); } catch (Exception e) { // TODO(johnlenz): Remove this once the normalization checks report // errors instead of exceptions. throw new RuntimeException("Sanity check failed for " + name, e); } } } /** * Runs a set of compiler passes until they reach a fixed point. * * Notice that this is a non-static class, because it includes the closure * of PhaseOptimizer. */ class Loop implements CompilerPass { private final List myPasses = Lists.newArrayList(); private final Set myNames = Sets.newHashSet(); private CodeChangeHandler recentChange = new CodeChangeHandler(); void addLoopedPass(PassFactory factory) { String name = factory.getName(); Preconditions.checkArgument(!myNames.contains(name), "Already a pass with name '%s' in this loop", name); myNames.add(name); myPasses.add(new NamedPass(factory)); } @Override public void process(Node externs, Node root) { Preconditions.checkState(!loopMutex, "Nested loops are forbidden"); loopMutex = true; if (randomizeLoops) { randomizePasses(); } else { optimizePasses(); } compiler.addChangeHandler(recentChange); // Contains a pass iff it made changes the last time it was run. Set madeChanges = new HashSet(); // Contains a pass iff it was run during the last inner loop. Set runInPrevIter = new HashSet(); State s = State.RUN_PASSES_NOT_RUN_IN_PREV_ITER; boolean lastIterMadeChanges; int count = 0; try { while (true) { if (count++ > MAX_LOOPS) { compiler.throwInternalError(OPTIMIZE_LOOP_ERROR, null); } lastIterMadeChanges = false; for (NamedPass pass : myPasses) { recentChange.reset(); if ((s == State.RUN_PASSES_NOT_RUN_IN_PREV_ITER && !runInPrevIter.contains(pass)) || (s == State.RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER && madeChanges.contains(pass))) { pass.process(externs, root); runInPrevIter.add(pass); if (hasHaltingErrors()) { return; } else if (recentChange.hasCodeChanged()) { madeChanges.add(pass); lastIterMadeChanges = true; } else { madeChanges.remove(pass); } } else { runInPrevIter.remove(pass); } } if (s == State.RUN_PASSES_NOT_RUN_IN_PREV_ITER) { if (lastIterMadeChanges) { s = State.RUN_PASSES_THAT_CHANGED_STH_IN_PREV_ITER; } else { return; } } else if (!lastIterMadeChanges) { s = State.RUN_PASSES_NOT_RUN_IN_PREV_ITER; } } } finally { loopMutex = false; compiler.removeChangeHandler(recentChange); } } /** Re-arrange the passes in a random order. */ private void randomizePasses() { Collections.shuffle(myPasses); } /** Re-arrange the passes in an optimal order. */ private void optimizePasses() { // It's important that this ordering is deterministic, so that // multiple compiles with the same input produce exactly the same // results. // // To do this, grab any passes we recognize, and move them to the end // in an "optimal" order. List optimalPasses = Lists.newArrayList(); for (String passName : OPTIMAL_ORDER) { for (NamedPass pass : myPasses) { if (pass.name.equals(passName)) { optimalPasses.add(pass); break; } } } myPasses.removeAll(optimalPasses); myPasses.addAll(optimalPasses); } } static class ProgressRange { public final double initialValue; public final double maxValue; public ProgressRange(double initialValue, double maxValue) { this.initialValue = initialValue; this.maxValue = maxValue; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckAccessControls.java0000644000175000017500000006000212115204405027360 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSourceFile; /** * A compiler pass that checks that the programmer has obeyed all the access * control restrictions indicated by JSDoc annotations, like * {@code @private} and {@code @deprecated}. * * Because access control restrictions are attached to type information, * it's important that TypedScopeCreator, TypeInference, and InferJSDocInfo * all run before this pass. TypedScopeCreator creates and resolves types, * TypeInference propagates those types across the AST, and InferJSDocInfo * propagates JSDoc across the types. * * @author nicksantos@google.com (Nick Santos) */ class CheckAccessControls implements ScopedCallback, HotSwapCompilerPass { static final DiagnosticType DEPRECATED_NAME = DiagnosticType.disabled( "JSC_DEPRECATED_VAR", "Variable {0} has been deprecated."); static final DiagnosticType DEPRECATED_NAME_REASON = DiagnosticType.disabled( "JSC_DEPRECATED_VAR_REASON", "Variable {0} has been deprecated: {1}"); static final DiagnosticType DEPRECATED_PROP = DiagnosticType.disabled( "JSC_DEPRECATED_PROP", "Property {0} of type {1} has been deprecated."); static final DiagnosticType DEPRECATED_PROP_REASON = DiagnosticType.disabled( "JSC_DEPRECATED_PROP_REASON", "Property {0} of type {1} has been deprecated: {2}"); static final DiagnosticType DEPRECATED_CLASS = DiagnosticType.disabled( "JSC_DEPRECATED_CLASS", "Class {0} has been deprecated."); static final DiagnosticType DEPRECATED_CLASS_REASON = DiagnosticType.disabled( "JSC_DEPRECATED_CLASS_REASON", "Class {0} has been deprecated: {1}"); static final DiagnosticType BAD_PRIVATE_GLOBAL_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_GLOBAL_ACCESS", "Access to private variable {0} not allowed outside file {1}."); static final DiagnosticType BAD_PRIVATE_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PRIVATE_PROPERTY_ACCESS", "Access to private property {0} of {1} not allowed here."); static final DiagnosticType BAD_PROTECTED_PROPERTY_ACCESS = DiagnosticType.disabled( "JSC_BAD_PROTECTED_PROPERTY_ACCESS", "Access to protected property {0} of {1} not allowed here."); static final DiagnosticType PRIVATE_OVERRIDE = DiagnosticType.disabled( "JSC_PRIVATE_OVERRIDE", "Overriding private property of {0}."); static final DiagnosticType EXTEND_FINAL_CLASS = DiagnosticType.error( "JSC_EXTEND_FINAL_CLASS", "{0} is not allowed to extend final class {1}."); static final DiagnosticType VISIBILITY_MISMATCH = DiagnosticType.disabled( "JSC_VISIBILITY_MISMATCH", "Overriding {0} property of {1} with {2} property."); static final DiagnosticType CONST_PROPERTY_REASSIGNED_VALUE = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_REASSIGNED_VALUE", "constant property {0} assigned a value more than once"); static final DiagnosticType CONST_PROPERTY_DELETED = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_DELETED", "constant property {0} cannot be deleted"); private final AbstractCompiler compiler; private final TypeValidator validator; // State about the current traversal. private int deprecatedDepth = 0; private int methodDepth = 0; private JSType currentClass = null; private final Multimap initializedConstantProperties; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.initializedConstantProperties = HashMultimap.create(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); if (isDeprecatedFunction(n)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.isAssign()) { Node lValue = parent.getFirstChild(); if (NodeUtil.isGet(lValue)) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; case Token.FUNCTION: checkFinalClassOverrides(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.isFunction() || parent.isVar() || parent.isNew()) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.isNew()) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE) { StaticSourceFile varSrc = var.getSourceFile(); StaticSourceFile refSrc = name.getStaticSourceFile(); if (varSrc != null && refSrc != null && !varSrc.getName().equals(refSrc.getName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), varSrc.getName())); } } } } } /** * Checks if a constructor is trying to override a final class. */ private void checkFinalClassOverrides(NodeTraversal t, Node fn, Node parent) { JSType type = fn.getJSType().toMaybeFunctionType(); if (type != null && type.isConstructor()) { JSType finalParentClass = getFinalParentClass(getClassOfMethod(fn, parent)); if (finalParentClass != null) { compiler.report( t.makeError(fn, EXTEND_FINAL_CLASS, type.getDisplayName(), finalParentClass.getDisplayName())); } } } /** * Determines whether the given property with @const tag got reassigned * @param t The current traversal. * @param getprop The getprop node. */ private void checkConstantProperty(NodeTraversal t, Node getprop) { // Check whether the property is modified Node parent = getprop.getParent(); boolean isDelete = parent.isDelProp(); if (!(NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == getprop) && !parent.isInc() && !parent.isDec() && !isDelete) { return; } ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); boolean isConstant = isPropertyDeclaredConstant(objectType, propertyName); // Check whether constant properties are reassigned if (isConstant) { if (isDelete) { compiler.report( t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName)); return; } ObjectType oType = objectType; while (oType != null) { if (oType.hasReferenceName()) { if (initializedConstantProperties.containsEntry( oType.getReferenceName(), propertyName)) { compiler.report( t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE, propertyName)); break; } } oType = oType.getImplicitPrototype(); } Preconditions.checkState(objectType.hasReferenceName()); initializedConstantProperties.put(objectType.getReferenceName(), propertyName); // Add the prototype when we're looking at an instance object if (objectType.isInstanceType()) { ObjectType prototype = objectType.getImplicitPrototype(); if (prototype != null) { if (prototype.hasProperty(propertyName) && prototype.hasReferenceName()) { initializedConstantProperties.put(prototype.getReferenceName(), propertyName); } } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = parent.getJSDocInfo() != null && parent.isAssign() && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } String referenceSource = getprop.getSourceFileName(); String definingSource = docInfo.getSourceName(); boolean sameInput = referenceSource != null && referenceSource.equals(definingSource); Visibility visibility = docInfo.getVisibility(); JSType ownerType = normalizeClassType(objectType); if (isOverride) { // Check an ASSIGN statement that's trying to override a property // on a superclass. JSDocInfo overridingInfo = parent.getJSDocInfo(); Visibility overridingVisibility = overridingInfo == null ? Visibility.INHERITED : overridingInfo.getVisibility(); // Check that (a) the property *can* be overridden, and // (b) that the visibility of the override is the same as the // visibility of the original property. if (visibility == Visibility.PRIVATE && !sameInput) { compiler.report( t.makeError(getprop, PRIVATE_OVERRIDE, objectType.toString())); } else if (overridingVisibility != Visibility.INHERITED && overridingVisibility != visibility) { compiler.report( t.makeError(getprop, VISIBILITY_MISMATCH, visibility.name(), objectType.toString(), overridingVisibility.name())); } } else { if (sameInput) { // private access is always allowed in the same file. return; } else if (visibility == Visibility.PRIVATE && (currentClass == null || !ownerType.isEquivalentTo(currentClass))) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } // private access is not allowed outside the file from a different // enclosing class. compiler.report( t.makeError(getprop, BAD_PRIVATE_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } else if (visibility == Visibility.PROTECTED) { // There are 3 types of legal accesses of a protected property: // 1) Accesses in the same file // 2) Overriding the property in a subclass // 3) Accessing the property from inside a subclass // The first two have already been checked for. if (currentClass == null || !currentClass.isSubtype(ownerType)) { compiler.report( t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } } } } } /** * Whether the given access of a private constructor is legal. * * For example, * new PrivateCtor_(); // not legal * PrivateCtor_.newInstance(); // legal * x instanceof PrivateCtor_ // legal * * This is a weird special case, because our visibility system is inherited * from Java, and JavaScript has no distinction between classes and * constructors like Java does. * * We may want to revisit this if we decide to make the restrictions tighter. */ private static boolean isValidPrivateConstructorAccess(Node parent) { return !parent.isNew(); } /** * Determines whether a deprecation warning should be emitted. * @param t The current traversal. * @param n The node which we are checking. * @param parent The parent of the node which we are checking. */ private boolean shouldEmitDeprecationWarning( NodeTraversal t, Node n, Node parent) { // In the global scope, there are only two kinds of accesses that should // be flagged for warnings: // 1) Calls of deprecated functions and methods. // 2) Instantiations of deprecated classes. // For now, we just let everything else by. if (t.inGlobalScope()) { if (!((parent.isCall() && parent.getFirstChild() == n) || n.isNew())) { return false; } } // We can always assign to a deprecated property, to keep it up to date. if (n.isGetProp() && n == parent.getFirstChild() && NodeUtil.isAssignmentOp(parent)) { return false; } return !canAccessDeprecatedTypes(t); } /** * Returns whether it's currently OK to access deprecated names and * properties. * * There are 3 exceptions when we're allowed to use a deprecated * type or property: * 1) When we're in a deprecated function. * 2) When we're in a deprecated class. * 3) When we're in a static method of a deprecated class. */ private boolean canAccessDeprecatedTypes(NodeTraversal t) { Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #2 (getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) || // Case #3 (scopeRootParent != null && scopeRootParent.isAssign() && getTypeDeprecationInfo( getClassOfMethod(scopeRoot, scopeRootParent)) != null); } /** * Returns whether this is a function node annotated as deprecated. */ private static boolean isDeprecatedFunction(Node n) { if (n.isFunction()) { JSType type = n.getJSType(); if (type != null) { return getTypeDeprecationInfo(type) != null; } } return false; } /** * Returns the deprecation reason for the type if it is marked * as being deprecated. Returns empty string if the type is deprecated * but no reason was given. Returns null if the type is not deprecated. */ private static String getTypeDeprecationInfo(JSType type) { if (type == null) { return null; } JSDocInfo info = type.getJSDocInfo(); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType objType = ObjectType.cast(type); if (objType != null) { ObjectType implicitProto = objType.getImplicitPrototype(); if (implicitProto != null) { return getTypeDeprecationInfo(implicitProto); } } return null; } /** * Returns if a property is declared constant. */ private static boolean isPropertyDeclaredConstant( ObjectType objectType, String prop) { for (; // Only objects with reference names can have constant properties. objectType != null && objectType.hasReferenceName(); objectType = objectType.getImplicitPrototype()) { JSDocInfo docInfo = objectType.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isConstant()) { return true; } } return false; } /** * Returns the deprecation reason for the property if it is marked * as being deprecated. Returns empty string if the property is deprecated * but no reason was given. Returns null if the property is not deprecated. */ private static String getPropertyDeprecationInfo(ObjectType type, String prop) { JSDocInfo info = type.getOwnPropertyJSDocInfo(prop); if (info != null && info.isDeprecated()) { if (info.getDeprecationReason() != null) { return info.getDeprecationReason(); } return ""; } ObjectType implicitProto = type.getImplicitPrototype(); if (implicitProto != null) { return getPropertyDeprecationInfo(implicitProto, prop); } return null; } /** * Dereference a type, autoboxing it and filtering out null. */ private static JSType dereference(JSType type) { return type == null ? null : type.dereference(); } /** * Returns the super class of the given type that has a constructor. */ private JSType getFinalParentClass(JSType type) { if (type != null) { ObjectType iproto = ObjectType.cast(type).getImplicitPrototype(); while (iproto != null && iproto.getConstructor() == null) { iproto = iproto.getImplicitPrototype(); } if (iproto != null) { Node source = iproto.getConstructor().getSource(); JSDocInfo jsDoc = source != null ? NodeUtil.getBestJSDocInfo(source) : null; if (jsDoc != null && jsDoc.isConstant()) { return iproto; } } } return null; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveUnusedVars.java0000644000175000017500000010366312115204405026765 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Garbage collection for variable and function definitions. Basically performs * a mark-and-sweep type algorithm over the JavaScript parse tree. * * For each scope: * (1) Scan the variable/function declarations at that scope. * (2) Traverse the scope for references, marking all referenced variables. * Unlike other compiler passes, this is a pre-order traversal, not a * post-order traversal. * (3) If the traversal encounters an assign without other side-effects, * create a continuation. Continue the continuation iff the assigned * variable is referenced. * (4) When the traversal completes, remove all unreferenced variables. * * If it makes it easier, you can think of the continuations of the traversal * as a reference graph. Each continuation represents a set of edges, where the * source node is a known variable, and the destination nodes are lazily * evaluated when the continuation is executed. * * This algorithm is similar to the algorithm used by {@code SmartNameRemoval}. * {@code SmartNameRemoval} maintains an explicit graph of dependencies * between global symbols. However, {@code SmartNameRemoval} cannot handle * non-trivial edges in the reference graph ("A is referenced iff both B and C * are referenced"), or local variables. {@code SmartNameRemoval} is also * substantially more complicated because it tries to handle namespaces * (which is largely unnecessary in the presence of {@code CollapseProperties}. * * This pass also uses a more complex analysis of assignments, where * an assignment to a variable or a property of that variable does not * necessarily count as a reference to that variable, unless we can prove * that it modifies external state. This is similar to * {@code FlowSensitiveInlineVariables}, except that it works for variables * used across scopes. * */ class RemoveUnusedVars implements CompilerPass, OptimizeCalls.CallGraphCompilerPass { private final AbstractCompiler compiler; private final CodingConvention codingConvention; private final boolean removeGlobals; private boolean preserveFunctionExpressionNames; /** * Keep track of variables that we've referenced. */ private final Set referenced = Sets.newHashSet(); /** * Keep track of variables that might be unreferenced. */ private final List maybeUnreferenced = Lists.newArrayList(); /** * Keep track of scopes that we've traversed. */ private final List allFunctionScopes = Lists.newArrayList(); /** * Keep track of assigns to variables that we haven't referenced. */ private final Multimap assignsByVar = ArrayListMultimap.create(); /** * The assigns, indexed by the NAME node that they assign to. */ private final Map assignsByNode = Maps.newHashMap(); /** * Subclass name -> class-defining call EXPR node. (like inherits) */ private final Multimap classDefiningCalls = ArrayListMultimap.create(); /** * Keep track of continuations that are finished iff the variable they're * indexed by is referenced. */ private final Multimap continuations = ArrayListMultimap.create(); private boolean modifyCallSites; private CallSiteOptimizer callSiteOptimizer; RemoveUnusedVars( AbstractCompiler compiler, boolean removeGlobals, boolean preserveFunctionExpressionNames, boolean modifyCallSites) { this.compiler = compiler; this.codingConvention = compiler.getCodingConvention(); this.removeGlobals = removeGlobals; this.preserveFunctionExpressionNames = preserveFunctionExpressionNames; this.modifyCallSites = modifyCallSites; } /** * Traverses the root, removing all unused variables. Multiple traversals * may occur to ensure all unused variables are removed. */ @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); SimpleDefinitionFinder defFinder = null; if (modifyCallSites) { // For testing, allow the SimpleDefinitionFinder to be build now. defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); } process(externs, root, defFinder); } @Override public void process( Node externs, Node root, SimpleDefinitionFinder defFinder) { if (modifyCallSites) { Preconditions.checkNotNull(defFinder); callSiteOptimizer = new CallSiteOptimizer(compiler, defFinder); } traverseAndRemoveUnusedReferences(root); if (callSiteOptimizer != null) { callSiteOptimizer.applyChanges(); } } /** * Traverses a node recursively. Call this once per pass. */ private void traverseAndRemoveUnusedReferences(Node root) { Scope scope = new SyntacticScopeCreator(compiler).createScope(root, null); traverseNode(root, null, scope); if (removeGlobals) { collectMaybeUnreferencedVars(scope); } interpretAssigns(); removeUnreferencedVars(); for (Scope fnScope : allFunctionScopes) { removeUnreferencedFunctionArgs(fnScope); } } /** * Traverses everything in the current scope and marks variables that * are referenced. * * During traversal, we identify subtrees that will only be * referenced if their enclosing variables are referenced. Instead of * traversing those subtrees, we create a continuation for them, * and traverse them lazily. */ private void traverseNode(Node n, Node parent, Scope scope) { int type = n.getType(); Var var = null; switch (type) { case Token.FUNCTION: // If this function is a removable var, then create a continuation // for it instead of traversing immediately. if (NodeUtil.isFunctionDeclaration(n)) { var = scope.getVar(n.getFirstChild().getString()); } if (var != null && isRemovableVar(var)) { continuations.put(var, new Continuation(n, scope)); } else { traverseFunction(n, scope); } return; case Token.ASSIGN: Assign maybeAssign = Assign.maybeCreateAssign(n); if (maybeAssign != null) { // Put this in the assign map. It might count as a reference, // but we won't know that until we have an index of all assigns. var = scope.getVar(maybeAssign.nameNode.getString()); if (var != null) { assignsByVar.put(var, maybeAssign); assignsByNode.put(maybeAssign.nameNode, maybeAssign); if (isRemovableVar(var) && !maybeAssign.mayHaveSecondarySideEffects) { // If the var is unreferenced and performing this assign has // no secondary side effects, then we can create a continuation // for it instead of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } } break; case Token.CALL: Var modifiedVar = null; // Look for calls to inheritance-defining calls (such as goog.inherits). SubclassRelationship subclassRelationship = codingConvention.getClassesDefinedByCall(n); if (subclassRelationship != null) { modifiedVar = scope.getVar(subclassRelationship.subclassName); } else { // Look for calls to addSingletonGetter calls. String className = codingConvention.getSingletonGetterClassName(n); if (className != null) { modifiedVar = scope.getVar(className); } } // Don't try to track the inheritance calls for non-globals. It would // be more correct to only not track when the subclass does not // reference a constructor, but checking that it is a global is // easier and mostly the same. if (modifiedVar != null && modifiedVar.isGlobal() && !referenced.contains(modifiedVar)) { // Save a reference to the EXPR node. classDefiningCalls.put(modifiedVar, parent); continuations.put(modifiedVar, new Continuation(n, scope)); return; } break; case Token.NAME: var = scope.getVar(n.getString()); if (parent.isVar()) { Node value = n.getFirstChild(); if (value != null && var != null && isRemovableVar(var) && !NodeUtil.mayHaveSideEffects(value, compiler)) { // If the var is unreferenced and creating its value has no side // effects, then we can create a continuation for it instead // of traversing immediately. continuations.put(var, new Continuation(n, scope)); return; } } else { // If arguments is escaped, we just assume the worst and continue // on all the parameters. if ("arguments".equals(n.getString()) && scope.isLocal()) { Node lp = scope.getRootNode().getFirstChild().getNext(); for (Node a = lp.getFirstChild(); a != null; a = a.getNext()) { markReferencedVar(scope.getVar(a.getString())); } } // All name references that aren't declarations or assigns // are references to other vars. if (var != null) { // If that var hasn't already been marked referenced, then // start tracking it. If this is an assign, do nothing // for now. if (isRemovableVar(var)) { if (!assignsByNode.containsKey(n)) { markReferencedVar(var); } } else { markReferencedVar(var); } } } break; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { traverseNode(c, n, scope); } } private boolean isRemovableVar(Var var) { // Global variables are off-limits if the user might be using them. if (!removeGlobals && var.isGlobal()) { return false; } // Referenced variables are off-limits. if (referenced.contains(var)) { return false; } // Exported variables are off-limits. if (codingConvention.isExported(var.getName())) { return false; } return true; } /** * Traverses a function, which creates a new scope in JavaScript. * * Note that CATCH blocks also create a new scope, but only for the * catch variable. Declarations within the block actually belong to the * enclosing scope. Because we don't remove catch variables, there's * no need to treat CATCH blocks differently like we do functions. */ private void traverseFunction(Node n, Scope parentScope) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node body = n.getLastChild(); Preconditions.checkState(body.getNext() == null && body.isBlock()); Scope fnScope = new SyntacticScopeCreator(compiler).createScope(n, parentScope); traverseNode(body, n, fnScope); collectMaybeUnreferencedVars(fnScope); allFunctionScopes.add(fnScope); } /** * For each variable in this scope that we haven't found a reference * for yet, add it to the list of variables to check later. */ private void collectMaybeUnreferencedVars(Scope scope) { for (Iterator it = scope.getVars(); it.hasNext(); ) { Var var = it.next(); if (isRemovableVar(var)) { maybeUnreferenced.add(var); } } } /** * Removes unreferenced arguments from a function declaration and when * possible the function's callSites. * * @param fnScope The scope inside the function */ private void removeUnreferencedFunctionArgs(Scope fnScope) { // Notice that removing unreferenced function args breaks // Function.prototype.length. In advanced mode, we don't really care // about this: we consider "length" the equivalent of reflecting on // the function's lexical source. // // Rather than create a new option for this, we assume that if the user // is removing globals, then it's OK to remove unused function args. // // See http://code.google.com/p/closure-compiler/issues/detail?id=253 if (!removeGlobals) { return; } Node function = fnScope.getRootNode(); Preconditions.checkState(function.isFunction()); if (NodeUtil.isGetOrSetKey(function.getParent())) { // The parameters object literal setters can not be removed. return; } Node argList = getFunctionArgList(function); boolean modifyCallers = modifyCallSites && callSiteOptimizer.canModifyCallers(function); if (!modifyCallers) { // Strip unreferenced args off the end of the function declaration. Node lastArg; while ((lastArg = argList.getLastChild()) != null) { Var var = fnScope.getVar(lastArg.getString()); if (!referenced.contains(var)) { argList.removeChild(lastArg); compiler.reportCodeChange(); } else { break; } } } else { callSiteOptimizer.optimize(fnScope, referenced); } } /** * @return the LP node containing the function parameters. */ private static Node getFunctionArgList(Node function) { return function.getFirstChild().getNext(); } private static class CallSiteOptimizer { private final AbstractCompiler compiler; private final SimpleDefinitionFinder defFinder; private final List toRemove = Lists.newArrayList(); private final List toReplaceWithZero = Lists.newArrayList(); CallSiteOptimizer( AbstractCompiler compiler, SimpleDefinitionFinder defFinder) { this.compiler = compiler; this.defFinder = defFinder; } public void optimize(Scope fnScope, Set referenced) { Node function = fnScope.getRootNode(); Preconditions.checkState(function.isFunction()); Node argList = getFunctionArgList(function); // In this path we try to modify all the call sites to remove unused // function parameters. boolean changeCallSignature = canChangeSignature(function); markUnreferencedFunctionArgs( fnScope, function, referenced, argList.getFirstChild(), 0, changeCallSignature); } /** * Applies optimizations to all previously marked nodes. */ public void applyChanges() { for (Node n : toRemove) { n.getParent().removeChild(n); compiler.reportCodeChange(); } for (Node n : toReplaceWithZero) { n.getParent().replaceChild(n, IR.number(0).srcref(n)); compiler.reportCodeChange(); } } /** * For each unused function parameter, determine if it can be removed * from all the call sites, if so, remove it from the function signature * and the call sites otherwise replace the unused value where possible * with a constant (0). * * @param scope The function scope * @param function The function * @param param The current parameter node in the parameter list. * @param paramIndex The index of the current parameter * @param canChangeSignature Whether function signature can be change. * @return Whether there is a following function parameter. */ private boolean markUnreferencedFunctionArgs( Scope scope, Node function, Set referenced, Node param, int paramIndex, boolean canChangeSignature) { if (param != null) { // Take care of the following siblings first. boolean hasFollowing = markUnreferencedFunctionArgs( scope, function, referenced, param.getNext(), paramIndex+1, canChangeSignature); Var var = scope.getVar(param.getString()); if (!referenced.contains(var)) { Preconditions.checkNotNull(var); // Remove call parameter if we can generally change the signature // or if it is the last parameter in the parameter list. boolean modifyAllCallSites = canChangeSignature || !hasFollowing; if (modifyAllCallSites) { modifyAllCallSites = canRemoveArgFromCallSites( function, paramIndex); } tryRemoveArgFromCallSites(function, paramIndex, modifyAllCallSites); // Remove an unused function parameter if all the call sites can // be modified to remove it, or if it is the last parameter. if (modifyAllCallSites || !hasFollowing) { toRemove.add(param); return hasFollowing; } } return true; } else { // Anything past the last formal parameter can be removed from the call // sites. tryRemoveAllFollowingArgs(function, paramIndex-1); return false; } } /** * Remove all references to a parameter, otherwise simplify the known * references. * @return Whether all the references were removed. */ private boolean canRemoveArgFromCallSites(Node function, int argIndex) { Definition definition = getFunctionDefinition(function); // Check all the call sites. for (UseSite site : defFinder.getUseSites(definition)) { if (isModifiableCallSite(site)) { Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); // TODO(johnlenz): try to remove parameters with side-effects by // decomposing the call expression. if (arg != null && NodeUtil.mayHaveSideEffects(arg, compiler)) { return false; } } else { return false; } } return true; } /** * Remove all references to a parameter if possible otherwise simplify the * side-effect free parameters. */ private void tryRemoveArgFromCallSites( Node function, int argIndex, boolean canModifyAllSites) { Definition definition = getFunctionDefinition(function); for (UseSite site : defFinder.getUseSites(definition)) { if (isModifiableCallSite(site)) { Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex); if (arg != null) { // Even if we can't change the signature in general we can always // remove an unused value off the end of the parameter list. if (canModifyAllSites || (arg.getNext() == null && !NodeUtil.mayHaveSideEffects(arg, compiler))) { toRemove.add(arg); } else { // replace the node in the arg with 0 if (!NodeUtil.mayHaveSideEffects(arg, compiler) && (!arg.isNumber() || arg.getDouble() != 0)) { toReplaceWithZero.add(arg); } } } } } } /** * Remove all the following parameters without side-effects */ private void tryRemoveAllFollowingArgs(Node function, final int argIndex) { Definition definition = getFunctionDefinition(function); for (UseSite site : defFinder.getUseSites(definition)) { if (!isModifiableCallSite(site)) { continue; } Node arg = getArgumentForCallOrNewOrDotCall(site, argIndex + 1); while (arg != null) { if (!NodeUtil.mayHaveSideEffects(arg)) { toRemove.add(arg); } arg = arg.getNext(); } } } /** * Returns the nth argument node given a usage site for a direct function * call or for a func.call() node. */ private static Node getArgumentForCallOrNewOrDotCall(UseSite site, final int argIndex) { int adjustedArgIndex = argIndex; Node parent = site.node.getParent(); if (NodeUtil.isFunctionObjectCall(parent)) { adjustedArgIndex++; } return NodeUtil.getArgumentForCallOrNew(parent, adjustedArgIndex); } /** * @param function * @return Whether the callers to this function can be modified in any way. */ boolean canModifyCallers(Node function) { if (NodeUtil.isVarArgsFunction(function)) { return false; } DefinitionSite defSite = defFinder.getDefinitionForFunction(function); if (defSite == null) { return false; } Definition definition = defSite.definition; // Be conservative, don't try to optimize any declaration that isn't as // simple function declaration or assignment. if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(function)) { return false; } return defFinder.canModifyDefinition(definition); } /** * @param site The site to inspect * @return Whether the call site is suitable for modification */ private static boolean isModifiableCallSite(UseSite site) { return SimpleDefinitionFinder.isCallOrNewSite(site) && !NodeUtil.isFunctionObjectApply(site.node.getParent()); } /** * @return Whether the definitionSite represents a function whose call * signature can be modified. */ private boolean canChangeSignature(Node function) { Definition definition = getFunctionDefinition(function); CodingConvention convention = compiler.getCodingConvention(); Preconditions.checkState(!definition.isExtern()); Collection useSites = defFinder.getUseSites(definition); for (UseSite site : useSites) { Node parent = site.node.getParent(); // This was a use site removed by something else before we run. // 1. By another pass before us which means the definition graph is // no updated properly. // 2. By the continuations algorithm above. if (parent == null) { continue; // Ignore it. } // Ignore references within goog.inherits calls. if (parent.isCall() && convention.getClassesDefinedByCall(parent) != null) { continue; } // Accessing the property directly prevents rewrite. if (!SimpleDefinitionFinder.isCallOrNewSite(site)) { if (!(parent.isGetProp() && NodeUtil.isFunctionObjectCall(parent.getParent()))) { return false; } } if (NodeUtil.isFunctionObjectApply(parent)) { return false; } // TODO(johnlenz): support specialization // Multiple definitions prevent rewrite. // Attempt to validate the state of the simple definition finder. Node nameNode = site.node; Collection singleSiteDefinitions = defFinder.getDefinitionsReferencedAt(nameNode); Preconditions.checkState(singleSiteDefinitions.size() == 1); Preconditions.checkState(singleSiteDefinitions.contains(definition)); } return true; } /** * @param function * @return the Definition object for the function. */ private Definition getFunctionDefinition(Node function) { DefinitionSite definitionSite = defFinder.getDefinitionForFunction( function); Preconditions.checkNotNull(definitionSite); Definition definition = definitionSite.definition; Preconditions.checkState(!definitionSite.inExterns); Preconditions.checkState(definition.getRValue() == function); return definition; } } /** * Look at all the property assigns to all variables. * These may or may not count as references. For example, * * * var x = {}; * x.foo = 3; // not a reference. * var y = foo(); * y.foo = 3; // is a reference. * * * Interpreting assignments could mark a variable as referenced that * wasn't referenced before, in order to keep it alive. Because we find * references by lazily traversing subtrees, marking a variable as * referenced could trigger new traversals of new subtrees, which could * find new references. * * Therefore, this interpretation needs to be run to a fixed point. */ private void interpretAssigns() { boolean changes = false; do { changes = false; // We can't use traditional iterators and iterables for this list, // because our lazily-evaluated continuations will modify it while // we traverse it. for (int current = 0; current < maybeUnreferenced.size(); current++) { Var var = maybeUnreferenced.get(current); if (referenced.contains(var)) { maybeUnreferenced.remove(current); current--; } else { boolean assignedToUnknownValue = false; boolean hasPropertyAssign = false; if (var.getParentNode().isVar() && !NodeUtil.isForIn(var.getParentNode().getParent())) { Node value = var.getInitialValue(); assignedToUnknownValue = value != null && !NodeUtil.isLiteralValue(value, true); } else { // This was initialized to a function arg or a catch param // or a for...in variable. assignedToUnknownValue = true; } boolean maybeEscaped = false; for (Assign assign : assignsByVar.get(var)) { if (assign.isPropertyAssign) { hasPropertyAssign = true; } else if (!NodeUtil.isLiteralValue( assign.assignNode.getLastChild(), true)) { assignedToUnknownValue = true; } if (assign.maybeAliased) { maybeEscaped = true; } } if ((assignedToUnknownValue || maybeEscaped) && hasPropertyAssign) { changes = markReferencedVar(var) || changes; maybeUnreferenced.remove(current); current--; } } } } while (changes); } /** * Remove all assigns to a var. */ private void removeAllAssigns(Var var) { for (Assign assign : assignsByVar.get(var)) { assign.remove(); compiler.reportCodeChange(); } } /** * Marks a var as referenced, recursing into any values of this var * that we skipped. * @return True if this variable had not been referenced before. */ private boolean markReferencedVar(Var var) { if (referenced.add(var)) { for (Continuation c : continuations.get(var)) { c.apply(); } return true; } return false; } /** * Removes any vars in the scope that were not referenced. Removes any * assignments to those variables as well. */ private void removeUnreferencedVars() { for (Iterator it = maybeUnreferenced.iterator(); it.hasNext(); ) { Var var = it.next(); // Remove calls to inheritance-defining functions where the unreferenced // class is the subclass. for (Node exprCallNode : classDefiningCalls.get(var)) { NodeUtil.removeChild(exprCallNode.getParent(), exprCallNode); compiler.reportCodeChange(); } // Regardless of what happens to the original declaration, // we need to remove all assigns, because they may contain references // to other unreferenced variables. removeAllAssigns(var); compiler.addToDebugLog("Unreferenced var: " + var.name); Node nameNode = var.nameNode; Node toRemove = nameNode.getParent(); Node parent = toRemove.getParent(); Preconditions.checkState( toRemove.isVar() || toRemove.isFunction() || toRemove.isParamList() && parent.isFunction(), "We should only declare vars and functions and function args"); if (toRemove.isParamList() && parent.isFunction()) { // Don't remove function arguments here. That's a special case // that's taken care of in removeUnreferencedFunctionArgs. } else if (NodeUtil.isFunctionExpression(toRemove)) { if (!preserveFunctionExpressionNames) { toRemove.getFirstChild().setString(""); compiler.reportCodeChange(); } // Don't remove bleeding functions. } else if (parent != null && parent.isFor() && parent.getChildCount() < 4) { // foreach iterations have 3 children. Leave them alone. } else if (toRemove.isVar() && nameNode.hasChildren() && NodeUtil.mayHaveSideEffects(nameNode.getFirstChild(), compiler)) { // If this is a single var declaration, we can at least remove the // declaration itself and just leave the value, e.g., // var a = foo(); => foo(); if (toRemove.getChildCount() == 1) { parent.replaceChild(toRemove, IR.exprResult(nameNode.removeFirstChild())); compiler.reportCodeChange(); } } else if (toRemove.isVar() && toRemove.getChildCount() > 1) { // For var declarations with multiple names (i.e. var a, b, c), // only remove the unreferenced name toRemove.removeChild(nameNode); compiler.reportCodeChange(); } else if (parent != null) { NodeUtil.removeChild(parent, toRemove); compiler.reportCodeChange(); } } } /** * Our progress in a traversal can be expressed completely as the * current node and scope. The continuation lets us save that * information so that we can continue the traversal later. */ private class Continuation { private final Node node; private final Scope scope; Continuation(Node node, Scope scope) { this.node = node; this.scope = scope; } void apply() { if (NodeUtil.isFunctionDeclaration(node)) { traverseFunction(node, scope); } else { for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { traverseNode(child, node, scope); } } } } private static class Assign { final Node assignNode; final Node nameNode; // If false, then this is an assign to the normal variable. Otherwise, // this is an assign to a property of that variable. final boolean isPropertyAssign; // Secondary side effects are any side effects in this assign statement // that aren't caused by the assignment operation itself. For example, // a().b = 3; // a = b(); // var foo = (a = b); // In the first two cases, the sides of the assignment have side-effects. // In the last one, the result of the assignment is used, so we // are conservative and assume that it may be used in a side-effecting // way. final boolean mayHaveSecondarySideEffects; // If true, the value may have escaped and any modification is a use. final boolean maybeAliased; Assign(Node assignNode, Node nameNode, boolean isPropertyAssign) { Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); this.assignNode = assignNode; this.nameNode = nameNode; this.isPropertyAssign = isPropertyAssign; this.maybeAliased = NodeUtil.isExpressionResultUsed(assignNode); this.mayHaveSecondarySideEffects = maybeAliased || NodeUtil.mayHaveSideEffects(assignNode.getFirstChild()) || NodeUtil.mayHaveSideEffects(assignNode.getLastChild()); } /** * If this is an assign to a variable or its property, return it. * Otherwise, return null. */ static Assign maybeCreateAssign(Node assignNode) { Preconditions.checkState(NodeUtil.isAssignmentOp(assignNode)); // Skip one level of GETPROPs or GETELEMs. // // Don't skip more than one level, because then we get into // situations where assigns to properties of properties will always // trigger side-effects, and the variable they're on cannot be removed. boolean isPropAssign = false; Node current = assignNode.getFirstChild(); if (NodeUtil.isGet(current)) { current = current.getFirstChild(); isPropAssign = true; if (current.isGetProp() && current.getLastChild().getString().equals("prototype")) { // Prototype properties sets should be considered like normal // property sets. current = current.getFirstChild(); } } if (current.isName()) { return new Assign(assignNode, current, isPropAssign); } return null; } /** * Replace the current assign with its right hand side. */ void remove() { Node parent = assignNode.getParent(); if (mayHaveSecondarySideEffects) { Node replacement = assignNode.getLastChild().detachFromParent(); // Aggregate any expressions in GETELEMs. for (Node current = assignNode.getFirstChild(); !current.isName(); current = current.getFirstChild()) { if (current.isGetElem()) { replacement = IR.comma( current.getLastChild().detachFromParent(), replacement); replacement.copyInformationFrom(current); } } parent.replaceChild(assignNode, replacement); } else { Node gramps = parent.getParent(); if (parent.isExprResult()) { gramps.removeChild(parent); } else { parent.replaceChild(assignNode, assignNode.getLastChild().detachFromParent()); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GlobalNamespace.java0000644000175000017500000011713412115204405026523 0ustar apoapo/* * Copyright 2006 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.javascript.rhino.jstype.JSTypeNative.GLOBAL_THIS; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Builds a global namespace of all the objects and their properties in * the global scope. Also builds an index of all the references to those names. * */ class GlobalNamespace implements StaticScope, StaticSymbolTable { private AbstractCompiler compiler; private final Node root; private final Node externsRoot; private boolean inExterns; private Scope externsScope; private boolean generated = false; /** * Each reference has an index in post-order. * Notice that some nodes are represented by 2 Ref objects, so * this index is not necessarily unique. */ private int currentPreOrderIndex = 0; /** Global namespace tree */ private List globalNames = new ArrayList(); /** Maps names (e.g. "a.b.c") to nodes in the global namespace tree */ private Map nameMap = new HashMap(); /** * Creates an instance that may emit warnings when building the namespace. * * @param compiler The AbstractCompiler, for reporting code changes * @param root The root of the rest of the code to build a namespace for. */ GlobalNamespace(AbstractCompiler compiler, Node root) { this(compiler, null, root); } /** * Creates an instance that may emit warnings when building the namespace. * * @param compiler The AbstractCompiler, for reporting code changes * @param externsRoot The root of the externs to build a namespace for. If * this is null, externs and properties defined on extern types will not * be included in the global namespace. If non-null, it allows * user-defined function on extern types to be included in the global * namespace. E.g. String.foo. * @param root The root of the rest of the code to build a namespace for. */ GlobalNamespace(AbstractCompiler compiler, Node externsRoot, Node root) { this.compiler = compiler; this.externsRoot = externsRoot; this.root = root; } boolean hasExternsRoot() { return externsRoot != null; } @Override public Node getRootNode() { return root.getParent(); } @Override public StaticScope getParentScope() { return null; } @Override public Name getSlot(String name) { return getOwnSlot(name); } @Override public Name getOwnSlot(String name) { ensureGenerated(); return nameMap.get(name); } @Override public JSType getTypeOfThis() { return compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS); } @Override public Iterable getReferences(Name slot) { ensureGenerated(); return Collections.unmodifiableList(slot.getRefs()); } @Override public StaticScope getScope(Name slot) { return this; } @Override public Iterable getAllSymbols() { ensureGenerated(); return Collections.unmodifiableCollection(getNameIndex().values()); } private void ensureGenerated() { if (!generated) { process(); } } /** * Gets a list of the roots of the forest of the global names, where the * roots are the top-level names. */ List getNameForest() { ensureGenerated(); return globalNames; } /** * Gets an index of all the global names, indexed by full qualified name * (as in "a", "a.b.c", etc.). */ Map getNameIndex() { ensureGenerated(); return nameMap; } /** * If the client adds new nodes to the AST, scan these new nodes * to see if they've added any references to the global namespace. * @param scope The scope to scan. * @param newNodes New nodes to check. */ void scanNewNodes(Scope scope, Set newNodes) { NodeTraversal t = new NodeTraversal(compiler, new BuildGlobalNamespace(new NodeFilter(newNodes))); t.traverseAtScope(scope); } /** * A filter that looks for qualified names that contain one of the nodes * in the given set. */ private static class NodeFilter implements Predicate { private final Set newNodes; NodeFilter(Set newNodes) { this.newNodes = newNodes; } @Override public boolean apply(Node n) { if (!n.isQualifiedName()) { return false; } Node current; for (current = n; current.isGetProp(); current = current.getFirstChild()) { if (newNodes.contains(current)) { return true; } } return current.isName() && newNodes.contains(current); } } /** * Builds the namespace lazily. */ private void process() { if (externsRoot != null) { inExterns = true; NodeTraversal.traverse(compiler, externsRoot, new BuildGlobalNamespace()); } inExterns = false; NodeTraversal.traverse(compiler, root, new BuildGlobalNamespace()); generated = true; } /** * Determines whether a name reference in a particular scope is a global name * reference. * * @param name A variable or property name (e.g. "a" or "a.b.c.d") * @param s The scope in which the name is referenced * @return Whether the name reference is a global name reference */ private boolean isGlobalNameReference(String name, Scope s) { String topVarName = getTopVarName(name); return isGlobalVarReference(topVarName, s); } /** * Gets the top variable name from a possibly namespaced name. * * @param name A variable or qualified property name (e.g. "a" or "a.b.c.d") * @return The top variable name (e.g. "a") */ private String getTopVarName(String name) { int firstDotIndex = name.indexOf('.'); return firstDotIndex == -1 ? name : name.substring(0, firstDotIndex); } /** * Determines whether a variable name reference in a particular scope is a * global variable reference. * * @param name A variable name (e.g. "a") * @param s The scope in which the name is referenced * @return Whether the name reference is a global variable reference */ private boolean isGlobalVarReference(String name, Scope s) { Scope.Var v = s.getVar(name); if (v == null && externsScope != null) { v = externsScope.getVar(name); } return v != null && !v.isLocal(); } /** * Gets whether a scope is the global scope. * * @param s A scope * @return Whether the scope is the global scope */ private boolean isGlobalScope(Scope s) { return s.getParent() == null; } // ------------------------------------------------------------------------- /** * Builds a tree representation of the global namespace. Omits prototypes. */ private class BuildGlobalNamespace implements NodeTraversal.Callback { private final Predicate nodeFilter; BuildGlobalNamespace() { this(null); } /** * Builds a global namespace, but only visits nodes that match the * given filter. */ BuildGlobalNamespace(Predicate nodeFilter) { this.nodeFilter = nodeFilter; } @Override public void visit(NodeTraversal t, Node n, Node parent) {} /** Collect the references in pre-order. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { collect(t, n, parent); return true; } public void collect(NodeTraversal t, Node n, Node parent) { if (nodeFilter != null && !nodeFilter.apply(n)) { return; } // If we are traversing the externs, then we save a pointer to the scope // generated by them, so that we can do lookups in it later. if (externsRoot != null && n == externsRoot) { externsScope = t.getScope(); } String name; boolean isSet = false; Name.Type type = Name.Type.OTHER; boolean isPropAssign = false; switch (n.getType()) { case Token.GETTER_DEF: case Token.SETTER_DEF: case Token.STRING_KEY: // This may be a key in an object literal declaration. name = null; if (parent != null && parent.isObjectLit()) { name = getNameForObjLitKey(n); } if (name == null) return; isSet = true; switch (n.getType()) { case Token.STRING_KEY: type = getValueType(n.getFirstChild()); break; case Token.GETTER_DEF: type = Name.Type.GET; break; case Token.SETTER_DEF: type = Name.Type.SET; break; default: throw new IllegalStateException("unexpected:" + n); } break; case Token.NAME: // This may be a variable get or set. if (parent != null) { switch (parent.getType()) { case Token.VAR: isSet = true; Node rvalue = n.getFirstChild(); type = rvalue == null ? Name.Type.OTHER : getValueType(rvalue); break; case Token.ASSIGN: if (parent.getFirstChild() == n) { isSet = true; type = getValueType(n.getNext()); } break; case Token.GETPROP: return; case Token.FUNCTION: Node gramps = parent.getParent(); if (gramps == null || NodeUtil.isFunctionExpression(parent)) return; isSet = true; type = Name.Type.FUNCTION; break; case Token.INC: case Token.DEC: isSet = true; type = Name.Type.OTHER; break; default: if (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) { isSet = true; type = Name.Type.OTHER; } } } name = n.getString(); break; case Token.GETPROP: // This may be a namespaced name get or set. if (parent != null) { switch (parent.getType()) { case Token.ASSIGN: if (parent.getFirstChild() == n) { isSet = true; type = getValueType(n.getNext()); isPropAssign = true; } break; case Token.INC: case Token.DEC: isSet = true; type = Name.Type.OTHER; break; case Token.GETPROP: return; default: if (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) { isSet = true; type = Name.Type.OTHER; } } } name = n.getQualifiedName(); if (name == null) return; break; default: return; } // We are only interested in global names. Scope scope = t.getScope(); if (!isGlobalNameReference(name, scope)) { return; } if (isSet) { if (isGlobalScope(scope)) { handleSetFromGlobal(t, n, parent, name, isPropAssign, type); } else { handleSetFromLocal(t, n, parent, name); } } else { handleGet(t, n, parent, name); } } /** * Gets the fully qualified name corresponding to an object literal key, * as long as it and its prefix property names are valid JavaScript * identifiers. The object literal may be nested inside of other object * literals. * * For example, if called with node {@code n} representing "z" in any of * the following expressions, the result would be "w.x.y.z": * var w = {x: {y: {z: 0}}}; * w.x = {y: {z: 0}}; * w.x.y = {'a': 0, 'z': 0}; * * @param n A child of an OBJLIT node * @return The global name, or null if {@code n} doesn't correspond to the * key of an object literal that can be named */ String getNameForObjLitKey(Node n) { Node parent = n.getParent(); Preconditions.checkState(parent.isObjectLit()); Node gramps = parent.getParent(); if (gramps == null) { return null; } Node greatGramps = gramps.getParent(); String name; switch (gramps.getType()) { case Token.NAME: // VAR // NAME (gramps) // OBJLIT (parent) // STRING (n) if (greatGramps == null || !greatGramps.isVar()) { return null; } name = gramps.getString(); break; case Token.ASSIGN: // ASSIGN (gramps) // NAME|GETPROP // OBJLIT (parent) // STRING (n) Node lvalue = gramps.getFirstChild(); name = lvalue.getQualifiedName(); break; case Token.STRING_KEY: // OBJLIT // STRING (gramps) // OBJLIT (parent) // STRING (n) if (greatGramps != null && greatGramps.isObjectLit()) { name = getNameForObjLitKey(gramps); } else { return null; } break; default: return null; } if (name != null) { String key = n.getString(); if (TokenStream.isJSIdentifier(key)) { return name + '.' + key; } } return null; } /** * Gets the type of a value or simple expression. * * @param n An r-value in an assignment or variable declaration (not null) * @return A {@link Name.Type} */ Name.Type getValueType(Node n) { switch (n.getType()) { case Token.OBJECTLIT: return Name.Type.OBJECTLIT; case Token.FUNCTION: return Name.Type.FUNCTION; case Token.OR: // Recurse on the second value. If the first value were an object // literal or function, then the OR would be meaningless and the // second value would be dead code. Assume that if the second value // is an object literal or function, then the first value will also // evaluate to one when it doesn't evaluate to false. return getValueType(n.getLastChild()); case Token.HOOK: // The same line of reasoning used for the OR case applies here. Node second = n.getFirstChild().getNext(); Name.Type t = getValueType(second); if (t != Name.Type.OTHER) return t; Node third = second.getNext(); return getValueType(third); } return Name.Type.OTHER; } /** * Updates our representation of the global namespace to reflect an * assignment to a global name in global scope. * * @param t The traversal * @param n The node currently being visited * @param parent {@code n}'s parent * @param name The global name (e.g. "a" or "a.b.c.d") * @param isPropAssign Whether this set corresponds to a property * assignment of the form a.b.c = ...; * @param type The type of the value that the name is being assigned */ void handleSetFromGlobal(NodeTraversal t, Node n, Node parent, String name, boolean isPropAssign, Name.Type type) { if (maybeHandlePrototypePrefix(t, n, parent, name)) return; Name nameObj = getOrCreateName(name); nameObj.type = type; Ref set = new Ref(t, n, nameObj, Ref.Type.SET_FROM_GLOBAL, currentPreOrderIndex++); nameObj.addRef(set); if (isNestedAssign(parent)) { // This assignment is both a set and a get that creates an alias. Ref get = new Ref(t, n, nameObj, Ref.Type.ALIASING_GET, currentPreOrderIndex++); nameObj.addRef(get); Ref.markTwins(set, get); } else if (isTypeDeclaration(n, parent)) { // Names with a @constructor or @enum annotation are always collapsed nameObj.setDeclaredType(); } } /** * Determines whether a set operation is a constructor or enumeration * or interface declaration. The set operation may either be an assignment * to a name, a variable declaration, or an object literal key mapping. * * @param n The node that represents the name being set * @param parent Parent node of {@code n} (an ASSIGN, VAR, or OBJLIT node) * @return Whether the set operation is either a constructor or enum * declaration */ private boolean isTypeDeclaration(Node n, Node parent) { Node valueNode = NodeUtil.getRValueOfLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); // Heed the annotations only if they're sensibly used. return info != null && valueNode != null && (info.isConstructor() && valueNode.isFunction() || info.isInterface() && valueNode.isFunction() || info.hasEnumParameterType() && valueNode.isObjectLit()); } /** * Updates our representation of the global namespace to reflect an * assignment to a global name in a local scope. * * @param t The traversal * @param n The node currently being visited * @param parent {@code n}'s parent * @param name The global name (e.g. "a" or "a.b.c.d") */ void handleSetFromLocal(NodeTraversal t, Node n, Node parent, String name) { if (maybeHandlePrototypePrefix(t, n, parent, name)) return; Name nameObj = getOrCreateName(name); Ref set = new Ref(t, n, nameObj, Ref.Type.SET_FROM_LOCAL, currentPreOrderIndex++); nameObj.addRef(set); if (isNestedAssign(parent)) { // This assignment is both a set and a get that creates an alias. Ref get = new Ref(t, n, nameObj, Ref.Type.ALIASING_GET, currentPreOrderIndex++); nameObj.addRef(get); Ref.markTwins(set, get); } } /** * Updates our representation of the global namespace to reflect a read * of a global name. * * @param t The traversal * @param n The node currently being visited * @param parent {@code n}'s parent * @param name The global name (e.g. "a" or "a.b.c.d") */ void handleGet(NodeTraversal t, Node n, Node parent, String name) { if (maybeHandlePrototypePrefix(t, n, parent, name)) return; Ref.Type type = Ref.Type.DIRECT_GET; if (parent != null) { switch (parent.getType()) { case Token.IF: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: break; case Token.CALL: type = n == parent.getFirstChild() ? Ref.Type.CALL_GET : Ref.Type.ALIASING_GET; break; case Token.NEW: type = n == parent.getFirstChild() ? Ref.Type.DIRECT_GET : Ref.Type.ALIASING_GET; break; case Token.OR: case Token.AND: // This node is x or y in (x||y) or (x&&y). We only know that an // alias is not getting created for this name if the result is used // in a boolean context or assigned to the same name // (e.g. var a = a || {}). type = determineGetTypeForHookOrBooleanExpr(t, parent, name); break; case Token.HOOK: if (n != parent.getFirstChild()) { // This node is y or z in (x?y:z). We only know that an alias is // not getting created for this name if the result is assigned to // the same name (e.g. var a = a ? a : {}). type = determineGetTypeForHookOrBooleanExpr(t, parent, name); } break; case Token.DELPROP: type = Ref.Type.DELETE_PROP; break; default: type = Ref.Type.ALIASING_GET; break; } } handleGet(t, n, parent, name, type); } /** * Determines whether the result of a hook (x?y:z) or boolean expression * (x||y) or (x&&y) is assigned to a specific global name. * * @param t The traversal * @param parent The parent of the current node in the traversal. This node * should already be known to be a HOOK, AND, or OR node. * @param name A name that is already known to be global in the current * scope (e.g. "a" or "a.b.c.d") * @return The expression's get type, either {@link Ref.Type#DIRECT_GET} or * {@link Ref.Type#ALIASING_GET} */ Ref.Type determineGetTypeForHookOrBooleanExpr( NodeTraversal t, Node parent, String name) { Node prev = parent; for (Node anc : parent.getAncestors()) { switch (anc.getType()) { case Token.EXPR_RESULT: case Token.VAR: case Token.IF: case Token.WHILE: case Token.FOR: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return Ref.Type.DIRECT_GET; case Token.HOOK: if (anc.getFirstChild() == prev) { return Ref.Type.DIRECT_GET; } break; case Token.ASSIGN: if (!name.equals(anc.getFirstChild().getQualifiedName())) { return Ref.Type.ALIASING_GET; } break; case Token.NAME: // a variable declaration if (!name.equals(anc.getString())) { return Ref.Type.ALIASING_GET; } break; case Token.CALL: if (anc.getFirstChild() != prev) { return Ref.Type.ALIASING_GET; } break; case Token.DELPROP: return Ref.Type.DELETE_PROP; } prev = anc; } return Ref.Type.ALIASING_GET; } /** * Updates our representation of the global namespace to reflect a read * of a global name. * * @param t The current node traversal * @param n The node currently being visited * @param parent {@code n}'s parent * @param name The global name (e.g. "a" or "a.b.c.d") * @param type The reference type */ void handleGet(NodeTraversal t, Node n, Node parent, String name, Ref.Type type) { Name nameObj = getOrCreateName(name); // No need to look up additional ancestors, since they won't be used. nameObj.addRef(new Ref(t, n, nameObj, type, currentPreOrderIndex++)); } /** * Updates our representation of the global namespace to reflect a read * of a global name's longest prefix before the "prototype" property if the * name includes the "prototype" property. Does nothing otherwise. * * @param t The current node traversal * @param n The node currently being visited * @param parent {@code n}'s parent * @param name The global name (e.g. "a" or "a.b.c.d") * @return Whether the name was handled */ boolean maybeHandlePrototypePrefix(NodeTraversal t, Node n, Node parent, String name) { // We use a string-based approach instead of inspecting the parse tree // to avoid complexities with object literals, possibly nested, beneath // assignments. int numLevelsToRemove; String prefix; if (name.endsWith(".prototype")) { numLevelsToRemove = 1; prefix = name.substring(0, name.length() - 10); } else { int i = name.indexOf(".prototype."); if (i == -1) { return false; } prefix = name.substring(0, i); numLevelsToRemove = 2; i = name.indexOf('.', i + 11); while (i >= 0) { numLevelsToRemove++; i = name.indexOf('.', i + 1); } } if (parent != null && NodeUtil.isObjectLitKey(n)) { // Object literal keys have no prefix that's referenced directly per // key, so we're done. return true; } for (int i = 0; i < numLevelsToRemove; i++) { parent = n; n = n.getFirstChild(); } handleGet(t, n, parent, prefix, Ref.Type.PROTOTYPE_GET); return true; } /** * Determines whether an assignment is nested (i.e. whether its return * value is used). * * @param parent The parent of the current traversal node (not null) * @return Whether it appears that the return value of the assignment is * used */ boolean isNestedAssign(Node parent) { return parent.isAssign() && !parent.getParent().isExprResult(); } /** * Gets a {@link Name} instance for a global name. Creates it if necessary, * as well as instances for any of its prefixes that are not yet defined. * * @param name A global name (e.g. "a", "a.b.c.d") * @return The {@link Name} instance for {@code name} */ Name getOrCreateName(String name) { Name node = nameMap.get(name); if (node == null) { int i = name.lastIndexOf('.'); if (i >= 0) { String parentName = name.substring(0, i); Name parent = getOrCreateName(parentName); node = parent.addProperty(name.substring(i + 1), inExterns); } else { node = new Name(name, null, inExterns); globalNames.add(node); } nameMap.put(name, node); } return node; } } // ------------------------------------------------------------------------- /** * A name defined in global scope (e.g. "a" or "a.b.c.d"). These form a tree. * As the parse tree traversal proceeds, we'll discover that some names * correspond to JavaScript objects whose properties we should consider * collapsing. */ static class Name implements StaticSlot { enum Type { OBJECTLIT, FUNCTION, GET, SET, OTHER, } private final String baseName; final Name parent; List props; /** The first global assignment to a name. */ private Ref declaration; /** All references to a name. This must contain {@code declaration}. */ private List refs; Type type; private boolean declaredType = false; private boolean hasDeclaredTypeDescendant = false; int globalSets = 0; int localSets = 0; int aliasingGets = 0; int totalGets = 0; int callGets = 0; int deleteProps = 0; final boolean inExterns; JSDocInfo docInfo = null; Name(String name, Name parent, boolean inExterns) { this.baseName = name; this.parent = parent; this.type = Type.OTHER; this.inExterns = inExterns; } Name addProperty(String name, boolean inExterns) { if (props == null) { props = new ArrayList(); } Name node = new Name(name, this, inExterns); props.add(node); return node; } String getBaseName() { return baseName; } @Override public String getName() { return getFullName(); } String getFullName() { return parent == null ? baseName : parent.getFullName() + '.' + baseName; } @Override public Ref getDeclaration() { return declaration; } @Override public boolean isTypeInferred() { return false; } @Override public JSType getType() { return null; } void addRef(Ref ref) { addRefInternal(ref); switch (ref.type) { case SET_FROM_GLOBAL: if (declaration == null) { declaration = ref; docInfo = getDocInfoForDeclaration(ref); } globalSets++; break; case SET_FROM_LOCAL: localSets++; break; case PROTOTYPE_GET: case DIRECT_GET: totalGets++; break; case ALIASING_GET: aliasingGets++; totalGets++; break; case CALL_GET: callGets++; totalGets++; break; case DELETE_PROP: deleteProps++; break; default: throw new IllegalStateException(); } } void removeRef(Ref ref) { if (refs != null && refs.remove(ref)) { if (ref == declaration) { declaration = null; if (refs != null) { for (Ref maybeNewDecl : refs) { if (maybeNewDecl.type == Ref.Type.SET_FROM_GLOBAL) { declaration = maybeNewDecl; break; } } } } switch (ref.type) { case SET_FROM_GLOBAL: globalSets--; break; case SET_FROM_LOCAL: localSets--; break; case PROTOTYPE_GET: case DIRECT_GET: totalGets--; break; case ALIASING_GET: aliasingGets--; totalGets--; break; case CALL_GET: callGets--; totalGets--; break; case DELETE_PROP: deleteProps--; break; default: throw new IllegalStateException(); } } } List getRefs() { return refs == null ? ImmutableList.of() : refs; } void addRefInternal(Ref ref) { if (refs == null) { refs = Lists.newArrayList(); } refs.add(ref); } boolean canEliminate() { if (!canCollapseUnannotatedChildNames() || totalGets > 0) { return false; } if (props != null) { for (Name n : props) { if (!n.canCollapse()) { return false; } } } return true; } boolean isSimpleStubDeclaration() { if (getRefs().size() == 1) { Ref ref = refs.get(0); if (ref.node.getParent() != null && ref.node.getParent().isExprResult()) { return true; } } return false; } boolean canCollapse() { return !inExterns && !isGetOrSetDefinition() && (declaredType || (parent == null || parent.canCollapseUnannotatedChildNames()) && (globalSets > 0 || localSets > 0) && deleteProps == 0); } boolean isGetOrSetDefinition() { return this.type == Type.GET || this.type == Type.SET; } boolean canCollapseUnannotatedChildNames() { if (type == Type.OTHER || isGetOrSetDefinition() || globalSets != 1 || localSets != 0 || deleteProps != 0) { return false; } // Don't try to collapse if the one global set is a twin reference. // We could theoretically handle this case in CollapseProperties, but // it's probably not worth the effort. Preconditions.checkNotNull(declaration); if (declaration.getTwin() != null) { return false; } if (declaredType) { return true; } // If this is a key of an aliased object literal, then it will be aliased // later. So we won't be able to collapse its properties. if (parent != null && parent.shouldKeepKeys()) { return false; } // If this is aliased, then its properties can't be collapsed either. if (aliasingGets > 0) { return false; } return (parent == null || parent.canCollapseUnannotatedChildNames()); } /** Whether this is an object literal that needs to keep its keys. */ boolean shouldKeepKeys() { return type == Type.OBJECTLIT && aliasingGets > 0; } boolean needsToBeStubbed() { return globalSets == 0 && localSets > 0; } void setDeclaredType() { declaredType = true; for (Name ancestor = parent; ancestor != null; ancestor = ancestor.parent) { ancestor.hasDeclaredTypeDescendant = true; } } boolean isDeclaredType() { return declaredType; } /** * Determines whether this name is a prefix of at least one class or enum * name. Because classes and enums are always collapsed, the namespace will * have different properties in compiled code than in uncompiled code. * * For example, if foo.bar.DomHelper is a class, then foo and foo.bar are * considered namespaces. */ boolean isNamespace() { return hasDeclaredTypeDescendant && type == Type.OBJECTLIT; } /** * Determines whether this is a simple name (as opposed to a qualified * name). */ boolean isSimpleName() { return parent == null; } @Override public String toString() { return getFullName() + " (" + type + "): globalSets=" + globalSets + ", localSets=" + localSets + ", totalGets=" + totalGets + ", aliasingGets=" + aliasingGets + ", callGets=" + callGets; } @Override public JSDocInfo getJSDocInfo() { return docInfo; } /** * Tries to get the doc info for a given declaration ref. */ private static JSDocInfo getDocInfoForDeclaration(Ref ref) { if (ref.node != null) { Node refParent = ref.node.getParent(); switch (refParent.getType()) { case Token.FUNCTION: case Token.ASSIGN: return refParent.getJSDocInfo(); case Token.VAR: return ref.node == refParent.getFirstChild() ? refParent.getJSDocInfo() : ref.node.getJSDocInfo(); } } return null; } } // ------------------------------------------------------------------------- /** * A global name reference. Contains references to the relevant parse tree * node and its ancestors that may be affected. */ static class Ref implements StaticReference { enum Type { SET_FROM_GLOBAL, SET_FROM_LOCAL, PROTOTYPE_GET, ALIASING_GET, // Prevents a name's properties from being collapsed DIRECT_GET, // Prevents a name from being completely eliminated CALL_GET, // Prevents a name from being collapsed if never set DELETE_PROP, // Prevents a name from being collapsed at all. } Node node; final JSModule module; final StaticSourceFile source; final Name name; final Type type; final Scope scope; final int preOrderIndex; /** * Certain types of references are actually double-refs. For example, * var a = b = 0; * counts as both a "set" of b and an "alias" of b. * * We create two Refs for this node, and mark them as twins of each other. */ private Ref twin = null; /** * Creates a reference at the current node. */ Ref(NodeTraversal t, Node node, Name name, Type type, int index) { this.node = node; this.name = name; this.module = t.getInput() == null ? null : t.getInput().getModule(); this.source = node.getStaticSourceFile(); this.type = type; this.scope = t.getScope(); this.preOrderIndex = index; } private Ref(Ref original, Type type, int index) { this.node = original.node; this.name = original.name; this.module = original.module; this.source = original.source; this.type = type; this.scope = original.scope; this.preOrderIndex = index; } private Ref(Type type, int index) { this.type = type; this.module = null; this.source = null; this.scope = null; this.name = null; this.preOrderIndex = index; } @Override public Node getNode() { return node; } @Override public StaticSourceFile getSourceFile() { return source; } @Override public StaticSlot getSymbol() { return name; } JSModule getModule() { return module; } String getSourceName() { return source == null ? "" : source.getName(); } Ref getTwin() { return twin; } boolean isSet() { return type == Type.SET_FROM_GLOBAL || type == Type.SET_FROM_LOCAL; } static void markTwins(Ref a, Ref b) { Preconditions.checkArgument( (a.type == Type.ALIASING_GET || b.type == Type.ALIASING_GET) && (a.type == Type.SET_FROM_GLOBAL || a.type == Type.SET_FROM_LOCAL || b.type == Type.SET_FROM_GLOBAL || b.type == Type.SET_FROM_LOCAL)); a.twin = b; b.twin = a; } /** * Create a new ref that is the same as this one, but of * a different class. */ Ref cloneAndReclassify(Type type) { return new Ref(this, type, this.preOrderIndex); } static Ref createRefForTesting(Type type) { return new Ref(type, -1); } } /** * An experimental compiler pass for tracking what symbols were added/removed * at each stage of compilation. * * When "global namespace tracker" mode is on, we rebuild the global namespace * after each pass, and diff it against the last namespace built. */ static class Tracker implements CompilerPass { private final AbstractCompiler compiler; private final PrintStream stream; private final Predicate isInterestingSymbol; private Set previousSymbolsInTree = ImmutableSet.of(); /** @param stream The stream to print logs to. * @param isInterestingSymbol A predicate to determine which symbols * we care about. */ Tracker(AbstractCompiler compiler, PrintStream stream, Predicate isInterestingSymbol) { this.compiler = compiler; this.stream = stream; this.isInterestingSymbol = isInterestingSymbol; } @Override public void process(Node externs, Node root) { GlobalNamespace namespace = new GlobalNamespace(compiler, externs, root); Set currentSymbols = Sets.newTreeSet(); for (String name : namespace.getNameIndex().keySet()) { if (isInterestingSymbol.apply(name)) { currentSymbols.add(name); } } String passName = compiler.getLastPassName(); if (passName == null) { passName = "[Unknown pass]"; } for (String sym : currentSymbols) { if (!previousSymbolsInTree.contains(sym)) { stream.println(String.format("%s: Added by %s", sym, passName)); } } for (String sym : previousSymbolsInTree) { if (!currentSymbols.contains(sym)) { stream.println(String.format("%s: Removed by %s", sym, passName)); } } previousSymbolsInTree = currentSymbols; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionTypeBuilder.java0000644000175000017500000007574712115204405027461 0ustar apoapo/* * 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 static com.google.javascript.jscomp.TypeCheck.BAD_IMPLEMENTED_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionBuilder; import com.google.javascript.rhino.jstype.FunctionParamBuilder; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.TemplateType; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.annotation.Nullable; /** * A builder for FunctionTypes, because FunctionTypes are so * ridiculously complex. All methods return {@code this} for ease of use. * * Right now, this mostly uses JSDocInfo to infer type information about * functions. In the long term, developers should extend it to use other * signals by overloading the various "inferXXX" methods. For example, we * might want to use {@code goog.inherits} calls as a signal for inheritance, or * {@code return} statements as a signal for return type. * * NOTE(nicksantos): Organizationally, this feels like it should be in Rhino. * But it depends on some coding convention stuff that's really part * of JSCompiler. * * @author nicksantos@google.com (Nick Santos) * @author pascallouis@google.com (Pascal-Louis Perez) */ final class FunctionTypeBuilder { private final String fnName; private final AbstractCompiler compiler; private final CodingConvention codingConvention; private final JSTypeRegistry typeRegistry; private final Node errorRoot; private final String sourceName; private final Scope scope; private FunctionContents contents = UnknownFunctionContents.get(); private JSType returnType = null; private boolean returnTypeInferred = false; private List implementedInterfaces = null; private List extendedInterfaces = null; private ObjectType baseType = null; private JSType thisType = null; private boolean isConstructor = false; private boolean makesStructs = false; private boolean makesDicts = false; private boolean isInterface = false; private Node parametersNode = null; private ImmutableList templateTypeNames = ImmutableList.of(); private ImmutableList classTypeParameterNames = ImmutableList.of();; static final DiagnosticType EXTENDS_WITHOUT_TYPEDEF = DiagnosticType.warning( "JSC_EXTENDS_WITHOUT_TYPEDEF", "@extends used without @constructor or @interface for {0}"); static final DiagnosticType EXTENDS_NON_OBJECT = DiagnosticType.warning( "JSC_EXTENDS_NON_OBJECT", "{0} @extends non-object type {1}"); static final DiagnosticType RESOLVED_TAG_EMPTY = DiagnosticType.warning( "JSC_RESOLVED_TAG_EMPTY", "Could not resolve type in {0} tag of {1}"); static final DiagnosticType IMPLEMENTS_WITHOUT_CONSTRUCTOR = DiagnosticType.warning( "JSC_IMPLEMENTS_WITHOUT_CONSTRUCTOR", "@implements used without @constructor or @interface for {0}"); static final DiagnosticType CONSTRUCTOR_REQUIRED = DiagnosticType.warning("JSC_CONSTRUCTOR_REQUIRED", "{0} used without @constructor for {1}"); static final DiagnosticType CLASS_TEMPLATE_WITHOUT_CONSTRUCTOR = DiagnosticType.warning( "JSC_CLASS_TEMPLATE_WITHOUT_CONSTRUCTOR", "@classTemplate used without @constructor or @interface for {0}"); static final DiagnosticType VAR_ARGS_MUST_BE_LAST = DiagnosticType.warning( "JSC_VAR_ARGS_MUST_BE_LAST", "variable length argument must be last"); static final DiagnosticType OPTIONAL_ARG_AT_END = DiagnosticType.warning( "JSC_OPTIONAL_ARG_AT_END", "optional arguments must be at the end"); static final DiagnosticType INEXISTANT_PARAM = DiagnosticType.warning( "JSC_INEXISTANT_PARAM", "parameter {0} does not appear in {1}''s parameter list"); static final DiagnosticType TYPE_REDEFINITION = DiagnosticType.warning( "JSC_TYPE_REDEFINITION", "attempted re-definition of type {0}\n" + "found : {1}\n" + "expected: {2}"); static final DiagnosticType TEMPLATE_TYPE_DUPLICATED = DiagnosticType.warning( "JSC_TEMPLATE_TYPE_DUPLICATED", "Only one parameter type must be the template type"); static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.warning( "JSC_TEMPLATE_TYPE_EXPECTED", "The template type must be a parameter type"); static final DiagnosticType THIS_TYPE_NON_OBJECT = DiagnosticType.warning( "JSC_THIS_TYPE_NON_OBJECT", "@this type of a function must be an object\n" + "Actual type: {0}"); private class ExtendedTypeValidator implements Predicate { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportWarning(EXTENDS_NON_OBJECT, formatFnName(), type.toString()); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@extends", formatFnName()); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName); return false; } } else { return true; } } } private class ImplementedTypeValidator implements Predicate { @Override public boolean apply(JSType type) { ObjectType objectType = ObjectType.cast(type); if (objectType == null) { reportError(BAD_IMPLEMENTED_TYPE, fnName); return false; } else if (objectType.isEmptyType()) { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } else if (objectType.isUnknownType()) { if (hasMoreTagsToResolve(objectType)) { return true; } else { reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName); return false; } } else { return true; } } } /** * @param fnName The function name. * @param compiler The compiler. * @param errorRoot The node to associate with any warning generated by * this builder. * @param sourceName A source name for associating any warnings that * we have to emit. * @param scope The syntactic scope. */ FunctionTypeBuilder(String fnName, AbstractCompiler compiler, Node errorRoot, String sourceName, Scope scope) { Preconditions.checkNotNull(errorRoot); this.fnName = fnName == null ? "" : fnName; this.codingConvention = compiler.getCodingConvention(); this.typeRegistry = compiler.getTypeRegistry(); this.errorRoot = errorRoot; this.sourceName = sourceName; this.compiler = compiler; this.scope = scope; } /** Format the function name for use in warnings. */ String formatFnName() { return fnName.isEmpty() ? "" : fnName; } /** * Sets the contents of this function. */ FunctionTypeBuilder setContents(@Nullable FunctionContents contents) { if (contents != null) { this.contents = contents; } return this; } /** * Infer the parameter and return types of a function from * the parameter and return types of the function it is overriding. * * @param oldType The function being overridden. Does nothing if this is null. * @param paramsParent The LP node of the function that we're assigning to. * If null, that just means we're not initializing this to a function * literal. */ FunctionTypeBuilder inferFromOverriddenFunction( @Nullable FunctionType oldType, @Nullable Node paramsParent) { if (oldType == null) { return this; } returnType = oldType.getReturnType(); returnTypeInferred = oldType.isReturnTypeInferred(); if (paramsParent == null) { // Not a function literal. parametersNode = oldType.getParametersNode(); if (parametersNode == null) { parametersNode = new FunctionParamBuilder(typeRegistry).build(); } } else { // We're overriding with a function literal. Apply type information // to each parameter of the literal. FunctionParamBuilder paramBuilder = new FunctionParamBuilder(typeRegistry); Iterator oldParams = oldType.getParameters().iterator(); boolean warnedAboutArgList = false; boolean oldParamsListHitOptArgs = false; for (Node currentParam = paramsParent.getFirstChild(); currentParam != null; currentParam = currentParam.getNext()) { if (oldParams.hasNext()) { Node oldParam = oldParams.next(); Node newParam = paramBuilder.newParameterFromNode(oldParam); oldParamsListHitOptArgs = oldParamsListHitOptArgs || oldParam.isVarArgs() || oldParam.isOptionalArg(); // The subclass method might write its var_args as individual // arguments. if (currentParam.getNext() != null && newParam.isVarArgs()) { newParam.setVarArgs(false); newParam.setOptionalArg(true); } } else { warnedAboutArgList |= addParameter( paramBuilder, typeRegistry.getNativeType(UNKNOWN_TYPE), warnedAboutArgList, codingConvention.isOptionalParameter(currentParam) || oldParamsListHitOptArgs, codingConvention.isVarArgsParameter(currentParam)); } } // Clone any remaining params that aren't in the function literal, // but make them optional. while (oldParams.hasNext()) { paramBuilder.newOptionalParameterFromNode(oldParams.next()); } parametersNode = paramBuilder.build(); } return this; } /** * Infer the return type from JSDocInfo. */ FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { if (info != null && info.hasReturnType()) { returnType = info.getReturnType().evaluate(scope, typeRegistry); returnTypeInferred = false; } return this; } /** * Infer the role of the function (whether it's a constructor or interface) * and what it inherits from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); makesStructs = info.makesStructs(); makesDicts = info.makesDicts(); isInterface = info.isInterface(); if (makesStructs && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@struct", formatFnName()); } else if (makesDicts && !isConstructor) { reportWarning(CONSTRUCTOR_REQUIRED, "@dict", formatFnName()); } // base type if (info.hasBaseType()) { if (isConstructor) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, formatFnName()); } } // Implemented interfaces (for constructors only). if (info.getImplementedInterfaceCount() > 0) { if (isConstructor) { implementedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType != null && maybeInterType.setValidator(new ImplementedTypeValidator())) { implementedInterfaces.add((ObjectType) maybeInterType); } } } else if (isInterface) { reportWarning( TypeCheck.CONFLICTING_IMPLEMENTED_TYPE, formatFnName()); } else { reportWarning(CONSTRUCTOR_REQUIRED, "@implements", formatFnName()); } } // extended interfaces (for interfaces only) // We've already emitted a warning if this is not an interface. if (isInterface) { extendedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getExtendedInterfaces()) { JSType maybeInterfaceType = t.evaluate(scope, typeRegistry); if (maybeInterfaceType != null && maybeInterfaceType.setValidator(new ExtendedTypeValidator())) { extendedInterfaces.add((ObjectType) maybeInterfaceType); } } } ImmutableList typeParameters = info.getClassTemplateTypeNames(); if (!typeParameters.isEmpty()) { if (isConstructor || isInterface) { this.classTypeParameterNames = typeParameters; } else { reportWarning(CLASS_TEMPLATE_WITHOUT_CONSTRUCTOR, formatFnName()); } } } return this; } /** * Infers the type of {@code this}. * @param type The type of this if the info is missing. */ FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) { // Look at the @this annotation first. inferThisType(info); if (thisType == null) { ObjectType objType = ObjectType.cast(type); if (objType != null && (info == null || !info.hasType())) { thisType = objType; } } return this; } /** * Infers the type of {@code this}. * @param info The JSDocInfo for this function. */ FunctionTypeBuilder inferThisType(JSDocInfo info) { JSType maybeThisType = null; if (info != null && info.hasThisType()) { // TODO(johnlenz): In ES5 strict mode a function can have a null or // undefined "this" value, but all the existing "@this" annotations // don't declare restricted types. maybeThisType = info.getThisType().evaluate(scope, typeRegistry) .restrictByNotNullOrUndefined(); } if (maybeThisType != null) { thisType = maybeThisType; } return this; } /** * Infer the parameter types from the doc info alone. */ FunctionTypeBuilder inferParameterTypes(JSDocInfo info) { // Create a fake args parent. Node lp = IR.paramList(); for (String name : info.getParameterNames()) { lp.addChildToBack(IR.name(name)); } return inferParameterTypes(lp, info); } /** * Infer the parameter types from the list of argument names and * the doc info. */ FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, @Nullable JSDocInfo info) { if (argsParent == null) { if (info == null) { return this; } else { return inferParameterTypes(info); } } // arguments Node oldParameterType = null; if (parametersNode != null) { oldParameterType = parametersNode.getFirstChild(); } FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); boolean warnedAboutArgList = false; Set allJsDocParams = (info == null) ? Sets.newHashSet() : Sets.newHashSet(info.getParameterNames()); boolean isVarArgs = false; for (Node arg : argsParent.children()) { String argumentName = arg.getString(); allJsDocParams.remove(argumentName); // type from JSDocInfo JSType parameterType = null; boolean isOptionalParam = isOptionalParameter(arg, info); isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (arg.getJSDocInfo() != null && arg.getJSDocInfo().hasType()) { parameterType = arg.getJSDocInfo().getType().evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } warnedAboutArgList |= addParameter( builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); if (oldParameterType != null) { oldParameterType = oldParameterType.getNext(); } } // Copy over any old parameters that aren't in the param list. if (!isVarArgs) { while (oldParameterType != null && !isVarArgs) { builder.newParameterFromNode(oldParameterType); oldParameterType = oldParameterType.getNext(); } } for (String inexistentName : allJsDocParams) { reportWarning(INEXISTANT_PARAM, inexistentName, formatFnName()); } parametersNode = builder.build(); return this; } /** * @return Whether the given param is an optional param. */ private boolean isOptionalParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isOptionalParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isOptionalArg(); } /** * Determine whether this is a var args parameter. * @return Whether the given param is a var args param. */ private boolean isVarArgsParameter( Node param, @Nullable JSDocInfo info) { if (codingConvention.isVarArgsParameter(param)) { return true; } String paramName = param.getString(); return info != null && info.hasParameterType(paramName) && info.getParameterType(paramName).isVarArgs(); } /** * Infer the template type from the doc info. */ FunctionTypeBuilder inferTemplateTypeName( @Nullable JSDocInfo info) { if (info != null && !info.getTemplateTypeNames().isEmpty()) { ImmutableList.Builder builder = ImmutableList.builder(); for (String key : info.getTemplateTypeNames()) { builder.add(typeRegistry.createTemplateType(key)); } templateTypeNames = builder.build(); } else { templateTypeNames = ImmutableList.of(); } typeRegistry.setTemplateTypeNames(templateTypeNames); return this; } /** * Add a parameter to the param list. * @param builder A builder. * @param paramType The parameter type. * @param warnedAboutArgList Whether we've already warned about arg ordering * issues (like if optional args appeared before required ones). * @param isOptional Is this an optional parameter? * @param isVarArgs Is this a var args parameter? * @return Whether a warning was emitted. */ private boolean addParameter(FunctionParamBuilder builder, JSType paramType, boolean warnedAboutArgList, boolean isOptional, boolean isVarArgs) { boolean emittedWarning = false; if (isOptional) { // Remembering that an optional parameter has been encountered // so that if a non optional param is encountered later, an // error can be reported. if (!builder.addOptionalParams(paramType) && !warnedAboutArgList) { reportWarning(VAR_ARGS_MUST_BE_LAST); emittedWarning = true; } } else if (isVarArgs) { if (!builder.addVarArgs(paramType) && !warnedAboutArgList) { reportWarning(VAR_ARGS_MUST_BE_LAST); emittedWarning = true; } } else { if (!builder.addRequiredParams(paramType) && !warnedAboutArgList) { // An optional parameter was seen and this argument is not an optional // or var arg so it is an error. if (builder.hasVarArgs()) { reportWarning(VAR_ARGS_MUST_BE_LAST); } else { reportWarning(OPTIONAL_ARG_AT_END); } emittedWarning = true; } } return emittedWarning; } /** * Builds the function type, and puts it in the registry. */ FunctionType buildAndRegister() { if (returnType == null) { // Infer return types. // We need to be extremely conservative about this, because of two // competing needs. // 1) If we infer the return type of f too widely, then we won't be able // to assign f to other functions. // 2) If we infer the return type of f too narrowly, then we won't be // able to override f in subclasses. // So we only infer in cases where the user doesn't expect to write // @return annotations--when it's very obvious that the function returns // nothing. if (!contents.mayHaveNonEmptyReturns() && !contents.mayHaveSingleThrow() && !contents.mayBeFromExterns()) { returnType = typeRegistry.getNativeType(VOID_TYPE); returnTypeInferred = true; } } if (returnType == null) { returnType = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (parametersNode == null) { throw new IllegalStateException( "All Function types must have params and a return type"); } FunctionType fnType; if (isConstructor) { fnType = getOrCreateConstructor(); } else if (isInterface) { fnType = typeRegistry.createInterfaceType( fnName, contents.getSourceNode()); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } maybeSetBaseType(fnType); } else { fnType = new FunctionBuilder(typeRegistry) .withName(fnName) .withSourceNode(contents.getSourceNode()) .withParamsNode(parametersNode) .withReturnType(returnType, returnTypeInferred) .withTypeOfThis(thisType) .withTemplateKeys(templateTypeNames) .build(); maybeSetBaseType(fnType); } if (implementedInterfaces != null) { fnType.setImplementedInterfaces(implementedInterfaces); } if (extendedInterfaces != null) { fnType.setExtendedInterfaces(extendedInterfaces); } typeRegistry.clearTemplateTypeNames(); return fnType; } private void maybeSetBaseType(FunctionType fnType) { if (!fnType.isInterface() && baseType != null) { fnType.setPrototypeBasedOn(baseType); } } /** * Returns a constructor function either by returning it from the * registry if it exists or creating and registering a new type. If * there is already a type, then warn if the existing type is * different than the one we are creating, though still return the * existing function if possible. The primary purpose of this is * that registering a constructor will fail for all built-in types * that are initialized in {@link JSTypeRegistry}. We a) want to * make sure that the type information specified in the externs file * matches what is in the registry and b) annotate the externs with * the {@link JSType} from the registry so that there are not two * separate JSType objects for one type. */ private FunctionType getOrCreateConstructor() { FunctionType fnType = typeRegistry.createConstructorType( fnName, contents.getSourceNode(), parametersNode, returnType, classTypeParameterNames); JSType existingType = typeRegistry.getType(fnName); if (makesStructs) { fnType.setStruct(); } else if (makesDicts) { fnType.setDict(); } if (existingType != null) { boolean isInstanceObject = existingType.isInstanceType(); if (isInstanceObject || fnName.equals("Function")) { FunctionType existingFn = isInstanceObject ? existingType.toObjectType().getConstructor() : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); if (existingFn.getSource() == null) { existingFn.setSource(contents.getSourceNode()); } if (!existingFn.hasEqualCallType(fnType)) { reportWarning(TYPE_REDEFINITION, formatFnName(), fnType.toString(), existingFn.toString()); } return existingFn; } else { // We fall through and return the created type, even though it will fail // to register. We have no choice as we have to return a function. We // issue an error elsewhere though, so the user should fix it. } } maybeSetBaseType(fnType); if (getScopeDeclaredIn().isGlobal() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } return fnType; } private void reportWarning(DiagnosticType warning, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, warning, args)); } private void reportError(DiagnosticType error, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, error, args)); } /** * Determines whether the given JsDoc info declares a function type. */ static boolean isFunctionTypeDeclaration(JSDocInfo info) { return info.getParameterCount() > 0 || info.hasReturnType() || info.hasThisType() || info.isConstructor() || info.isInterface(); } /** * The scope that we should declare this function in, if it needs * to be declared in a scope. Notice that TypedScopeCreator takes * care of most scope-declaring. */ private Scope getScopeDeclaredIn() { int dotIndex = fnName.indexOf("."); if (dotIndex != -1) { String rootVarName = fnName.substring(0, dotIndex); Var rootVar = scope.getVar(rootVarName); if (rootVar != null) { return rootVar.getScope(); } } return scope; } /** * Check whether a type is resolvable in the future * If this has a supertype that hasn't been resolved yet, then we can assume * this type will be OK once the super type resolves. * @param objectType * @return true if objectType is resolvable in the future */ private static boolean hasMoreTagsToResolve(ObjectType objectType) { Preconditions.checkArgument(objectType.isUnknownType()); if (objectType.getImplicitPrototype() != null) { // constructor extends class if (objectType.getImplicitPrototype().isResolved()) { return false; } else { return true; } } else { // interface extends interfaces FunctionType ctor = objectType.getConstructor(); if (ctor != null) { for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { if (!interfaceType.isResolved()) { return true; } } } return false; } } /** Holds data dynamically inferred about functions. */ static interface FunctionContents { /** Returns the source node of this function. May be null. */ Node getSourceNode(); /** Returns if the function may be in externs. */ boolean mayBeFromExterns(); /** Returns if a return of a real value (not undefined) appears. */ boolean mayHaveNonEmptyReturns(); /** Returns if this consists of a single throw. */ boolean mayHaveSingleThrow(); /** Gets a list of variables in this scope that are escaped. */ Iterable getEscapedVarNames(); /** Gets a list of variables whose properties are escaped. */ Set getEscapedQualifiedNames(); /** Gets the number of times each variable has been assigned. */ Multiset getAssignedNameCounts(); } static class UnknownFunctionContents implements FunctionContents { private static UnknownFunctionContents singleton = new UnknownFunctionContents(); static FunctionContents get() { return singleton; } @Override public Node getSourceNode() { return null; } @Override public boolean mayBeFromExterns() { return true; } @Override public boolean mayHaveNonEmptyReturns() { return true; } @Override public boolean mayHaveSingleThrow() { return true; } @Override public Iterable getEscapedVarNames() { return ImmutableList.of(); } @Override public Set getEscapedQualifiedNames() { return ImmutableSet.of(); } @Override public Multiset getAssignedNameCounts() { return ImmutableMultiset.of(); } } static class AstFunctionContents implements FunctionContents { private final Node n; private boolean hasNonEmptyReturns = false; private Set escapedVarNames; private Set escapedQualifiedNames; private final Multiset assignedVarNames = HashMultiset.create(); AstFunctionContents(Node n) { this.n = n; } @Override public Node getSourceNode() { return n; } @Override public boolean mayBeFromExterns() { return n.isFromExterns(); } @Override public boolean mayHaveNonEmptyReturns() { return hasNonEmptyReturns; } void recordNonEmptyReturn() { hasNonEmptyReturns = true; } @Override public boolean mayHaveSingleThrow() { Node block = n.getLastChild(); return block.hasOneChild() && block.getFirstChild().isThrow(); } @Override public Iterable getEscapedVarNames() { return escapedVarNames == null ? ImmutableList.of() : escapedVarNames; } void recordEscapedVarName(String name) { if (escapedVarNames == null) { escapedVarNames = Sets.newHashSet(); } escapedVarNames.add(name); } @Override public Set getEscapedQualifiedNames() { return escapedQualifiedNames == null ? ImmutableSet.of() : escapedQualifiedNames; } void recordEscapedQualifiedName(String name) { if (escapedQualifiedNames == null) { escapedQualifiedNames = Sets.newHashSet(); } escapedQualifiedNames.add(name); } @Override public Multiset getAssignedNameCounts() { return assignedVarNames; } void recordAssignedName(String name) { assignedVarNames.add(name); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckLevelLegacy.java0000644000175000017500000000171412115204405026634 0ustar apoapo/* * 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; /** * Enum used in flags to control the behavior of JS compiler checks. These * must be converted to CheckLevel enums before being used to control * options. Only use this for legacy flags. For new flags, simply use * {@link CheckLevel}. */ public enum CheckLevelLegacy { LEGACY, OFF, WARNING, ERROR, } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckUnreachableCode.java0000644000175000017500000000753612115204405027454 0ustar apoapo/* * 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.base.Predicate; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.GraphReachability; import com.google.javascript.jscomp.graph.GraphReachability.EdgeTuple; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.TernaryValue; /** * Use {@link ControlFlowGraph} and {@link GraphReachability} to inform user * about unreachable code. * */ class CheckUnreachableCode implements ScopedCallback { static final DiagnosticType UNREACHABLE_CODE = DiagnosticType.error( "JSC_UNREACHABLE_CODE", "unreachable code"); private final AbstractCompiler compiler; private final CheckLevel level; CheckUnreachableCode(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; } @Override public void enterScope(NodeTraversal t) { initScope(t.getControlFlowGraph()); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { GraphNode gNode = t.getControlFlowGraph().getNode(n); if (gNode != null && gNode.getAnnotation() != GraphReachability.REACHABLE) { // Only report error when there are some line number informations. // There are synthetic nodes with no line number informations, nodes // introduce by other passes (although not likely since this pass should // be executed early) or some rhino bug. if (n.getLineno() != -1 && // Allow spurious semi-colons and spurious breaks. !n.isEmpty() && !n.isBreak()) { compiler.report(t.makeError(n, level, UNREACHABLE_CODE)); // From now on, we are going to assume the user fixed the error and not // give more warning related to code section reachable from this node. new GraphReachability( t.getControlFlowGraph()).recompute(n); // Saves time by not traversing children. return false; } } return true; } private void initScope(ControlFlowGraph controlFlowGraph) { new GraphReachability( controlFlowGraph, new ReachablePredicate()).compute( controlFlowGraph.getEntry().getValue()); } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { } private final class ReachablePredicate implements Predicate> { @Override public boolean apply(EdgeTuple input) { Branch branch = input.edge; if (!branch.isConditional()) { return true; } Node predecessor = input.sourceNode; Node condition = NodeUtil.getConditionExpression(predecessor); // TODO(user): Handle more complicated expression like true == true, // etc.... if (condition != null) { TernaryValue val = NodeUtil.getImpureBooleanValue(condition); if (val != TernaryValue.UNKNOWN) { return val.toBoolean(true) == (branch == Branch.ON_TRUE); } } return true; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PerformanceTracker.java0000644000175000017500000002375212115204405027265 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CompilerOptions.TracerMode; import com.google.javascript.rhino.Node; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.zip.GZIPOutputStream; /** */ public class PerformanceTracker { private final Node jsRoot; private final boolean trackSize; private final boolean trackGzSize; // Keeps track of AST changes and computes code size estimation // if there is any. private final CodeChangeHandler codeChange = new CodeChangeHandler(); private int initCodeSize = 0; private int initGzCodeSize = 0; private int runtime = 0; private int runs = 0; private int changes = 0; private int loopRuns = 0; private int loopChanges = 0; private int codeSize = 0; private int gzCodeSize = 0; private int diff = 0; private int gzDiff = 0; private Deque currentPass = new ArrayDeque(); /** Summary stats by pass name. */ private final Map summary = Maps.newHashMap(); // To share with the rest of the program private ImmutableMap summaryCopy = null; /** Stats for each run of a compiler pass. */ private final List log = Lists.newArrayList(); /** For each pass, keep track of the runtime, the size changes, etc */ public static class Stats { Stats(String pass, boolean iot) { this.pass = pass; this.isOneTime = iot; } public final String pass; public final boolean isOneTime; public long runtime = 0; public int runs = 0; public int changes = 0; public int diff = 0; public int gzDiff = 0; public int size = 0; public int gzSize = 0; } PerformanceTracker(Node jsRoot, TracerMode mode) { this.jsRoot = jsRoot; switch (mode) { case TIMING_ONLY: this.trackSize = false; this.trackGzSize = false; break; case RAW_SIZE: this.trackSize = true; this.trackGzSize = false; break; case ALL: this.trackSize = true; this.trackGzSize = true; break; case OFF: default: throw new UnsupportedOperationException(); } } CodeChangeHandler getCodeChangeHandler() { return codeChange; } void recordPassStart(String passName, boolean isOneTime) { currentPass.push(new Stats(passName, isOneTime)); codeChange.reset(); } /** * Record that a pass has stopped. * * @param passName Short name of the pass. * @param result Execution time. */ void recordPassStop(String passName, long result) { Stats logStats = currentPass.pop(); if (!passName.equals(logStats.pass)) { throw new RuntimeException(passName + " is not running."); } // After parsing, initialize codeSize and gzCodeSize if (passName.equals(Compiler.PARSING_PASS_NAME) && trackSize) { CodeSizeEstimatePrinter printer = new CodeSizeEstimatePrinter(); CodeGenerator.forCostEstimation(printer).add(jsRoot); initCodeSize = codeSize = printer.calcSize(); if (this.trackGzSize) { initGzCodeSize = gzCodeSize = printer.calcZippedSize(); } } // Populate log and summary log.add(logStats); Stats summaryStats = summary.get(passName); if (summaryStats == null) { summaryStats = new Stats(passName, logStats.isOneTime); summary.put(passName, summaryStats); } // Update fields that aren't related to code size logStats.runtime = result; logStats.runs = 1; summaryStats.runtime += result; summaryStats.runs += 1; if (codeChange.hasCodeChanged()) { logStats.changes = 1; summaryStats.changes += 1; } // Update fields related to code size if (codeChange.hasCodeChanged() && trackSize) { int newSize = 0; CodeSizeEstimatePrinter printer = new CodeSizeEstimatePrinter(); CodeGenerator.forCostEstimation(printer).add(jsRoot); if (trackSize) { newSize = printer.calcSize(); logStats.diff = codeSize - newSize; summaryStats.diff += logStats.diff; codeSize = summaryStats.size = logStats.size = newSize; } if (trackGzSize) { newSize = printer.calcZippedSize(); logStats.gzDiff = gzCodeSize - newSize; summaryStats.gzDiff += logStats.gzDiff; gzCodeSize = summaryStats.gzSize = logStats.gzSize = newSize; } } } public int getRuntime() { calcTotalStats(); return runtime; } public int getSize() { calcTotalStats(); return codeSize; } public int getGzSize() { calcTotalStats(); return gzCodeSize; } public ImmutableMap getStats() { calcTotalStats(); return summaryCopy; } class CmpEntries implements Comparator> { @Override public int compare(Entry e1, Entry e2) { return (int) (e1.getValue().runtime - e2.getValue().runtime); } } private void calcTotalStats() { // This method only does work the first time it's called if (summaryCopy != null) { return; } summaryCopy = ImmutableMap.copyOf(summary); for (Entry entry : summary.entrySet()) { Stats stats = entry.getValue(); runtime += stats.runtime; runs += stats.runs; changes += stats.changes; if (!stats.isOneTime) { loopRuns += stats.runs; loopChanges += stats.changes; } diff += stats.diff; gzDiff += stats.gzDiff; } Preconditions.checkState(!trackSize || initCodeSize == diff + codeSize); Preconditions.checkState(!trackGzSize || initGzCodeSize == gzDiff + gzCodeSize); } public void outputTracerReport(PrintStream pstr) { JvmMetrics.maybeWriteJvmMetrics(pstr, "verbose:pretty:all"); OutputStreamWriter output = new OutputStreamWriter(pstr); try { calcTotalStats(); ArrayList> a = new ArrayList>(); for (Entry entry : summary.entrySet()) { a.add(entry); } Collections.sort(a, new CmpEntries()); output.write("Summary:\n" + "pass,runtime,runs,changingRuns,reduction,gzReduction\n"); for (Entry entry : a) { String key = entry.getKey(); Stats stats = entry.getValue(); output.write(key + "," + String.valueOf(stats.runtime) + "," + String.valueOf(stats.runs) + "," + String.valueOf(stats.changes) + "," + String.valueOf(stats.diff) + "," + String.valueOf(stats.gzDiff) + "\n"); } output.write("\nTOTAL:" + "\nRuntime(ms): " + String.valueOf(runtime) + "\n#Runs: " + String.valueOf(runs) + "\n#Changing runs: " + String.valueOf(changes) + "\n#Loopable runs: " + String.valueOf(loopRuns) + "\n#Changing loopable runs: " + String.valueOf(loopChanges) + "\nReduction(bytes): " + String.valueOf(diff) + "\nGzReduction(bytes): " + String.valueOf(gzDiff) + "\nSize(bytes): " + String.valueOf(codeSize) + "\nGzSize(bytes): " + String.valueOf(gzCodeSize) + "\n\n"); output.write("Log:\n" + "pass,runtime,runs,changingRuns,reduction,gzReduction,size,gzSize\n"); for (Stats stats : log) { output.write(stats.pass + "," + String.valueOf(stats.runtime) + "," + String.valueOf(stats.runs) + "," + String.valueOf(stats.changes) + "," + String.valueOf(stats.diff) + "," + String.valueOf(stats.gzDiff) + "," + String.valueOf(stats.size) + "," + String.valueOf(stats.gzSize) + "\n"); } output.write("\n"); output.close(); } catch (IOException e) { e.printStackTrace(); } } /** * Purely use to get a code size estimate and not generate any code at all. */ private final class CodeSizeEstimatePrinter extends CodeConsumer { private int size = 0; private char lastChar = '\0'; private final ByteArrayOutputStream output = new ByteArrayOutputStream(); private final GZIPOutputStream stream; private CodeSizeEstimatePrinter() { try { stream = new GZIPOutputStream(output); } catch (IOException e) { throw new RuntimeException(e); } } @Override void append(String str) { int len = str.length(); if (len > 0) { size += len; lastChar = str.charAt(len - 1); if (trackGzSize) { try { stream.write(str.getBytes()); } catch (IOException e) { throw new RuntimeException(e); } } } } @Override char getLastChar() { return lastChar; } private int calcSize() { return size; } // Called iff trackGzSize is true private int calcZippedSize() { try { stream.finish(); stream.flush(); stream.close(); return output.size(); } catch (IOException e) { throw new RuntimeException(e); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExportTestFunctions.java0000644000175000017500000001401512115204405027512 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.regex.Pattern; /** * Generates goog.exportSymbol for test functions, so they can be recognized * by the test runner, even if the code is compiled. * */ class ExportTestFunctions implements CompilerPass { private static final Pattern TEST_FUNCTIONS_NAME_PATTERN = Pattern.compile("^(?:((\\w+\\.)+prototype\\.)*" + "(setUpPage|setUp|tearDown|tearDownPage|test\\w+))$"); private AbstractCompiler compiler; private final String exportSymbolFunction; private final String exportPropertyFunction; /** * Creates a new export test functions compiler pass. * @param compiler * @param exportSymbolFunction The function name used to export symbols in JS. * @param exportPropertyFunction The function name used to export properties * in JS. */ ExportTestFunctions(AbstractCompiler compiler, String exportSymbolFunction, String exportPropertyFunction) { Preconditions.checkNotNull(compiler); this.compiler = compiler; this.exportSymbolFunction = exportSymbolFunction; this.exportPropertyFunction = exportPropertyFunction; } private class ExportTestFunctionsNodes extends NodeTraversal.AbstractShallowCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (parent == null) { return; } if (parent.isScript()) { if (NodeUtil.isFunctionDeclaration(n)) { // Check for a test function statement. String functionName = NodeUtil.getFunctionName(n); if (isTestFunction(n, functionName)) { exportTestFunctionAsSymbol(functionName, n, parent); } } else if (isVarDeclaredFunction(n)) { // Check for a test function expression. Node functionNode = n.getFirstChild().getFirstChild(); String functionName = NodeUtil.getFunctionName(functionNode); if (isTestFunction(functionNode, functionName)) { exportTestFunctionAsSymbol(functionName, n, parent); } } } else if (NodeUtil.isExprAssign(parent) && !n.getLastChild().isAssign()) { // Check for a test method assignment. Node grandparent = parent.getParent(); if (grandparent != null && grandparent.isScript()) { String functionName = n.getFirstChild().getQualifiedName(); if (isTestFunction(n, functionName)) { exportTestFunctionAsProperty(functionName, parent, n, grandparent); } } } } /** * Whether node corresponds to a function expression declared with var, * which is of the form: *
     * var functionName = function() {
     *   // Implementation
     * };
     * 
* This has the AST structure VAR -> NAME -> FUNCTION * @param node */ private boolean isVarDeclaredFunction(Node node) { if (!node.isVar()) { return false; } Node grandchild = node.getFirstChild().getFirstChild(); return grandchild != null && grandchild.isFunction(); } } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new ExportTestFunctionsNodes()); } // Adds exportSymbol(testFunctionName, testFunction); private void exportTestFunctionAsSymbol(String testFunctionName, Node node, Node scriptNode) { Node exportCallTarget = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), exportSymbolFunction, node, testFunctionName); Node call = IR.call( exportCallTarget); if (exportCallTarget.isName()) { call.putBooleanProp(Node.FREE_CALL, true); } call.addChildToBack(IR.string(testFunctionName)); call.addChildToBack(NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), testFunctionName, node, testFunctionName)); Node expression = IR.exprResult(call); scriptNode.addChildAfter(expression, node); compiler.reportCodeChange(); } // Adds exportProperty() of the test function name on the prototype object private void exportTestFunctionAsProperty(String fullyQualifiedFunctionName, Node parent, Node node, Node scriptNode) { String testFunctionName = NodeUtil.getPrototypePropertyName(node.getFirstChild()); String objectName = fullyQualifiedFunctionName.substring(0, fullyQualifiedFunctionName.lastIndexOf('.')); String exportCallStr = String.format("%s(%s, '%s', %s);", exportPropertyFunction, objectName, testFunctionName, fullyQualifiedFunctionName); Node exportCall = this.compiler.parseSyntheticCode(exportCallStr) .removeChildren(); exportCall.useSourceInfoFromForTree(scriptNode); scriptNode.addChildAfter(exportCall, parent); compiler.reportCodeChange(); } /** * Whether a function is recognized as a test function. We follow the JsUnit * convention for naming (functions should start with "test"), and we also * check if it has no parameters declared. * * @param n The function node * @param functionName The name of the function * @return {@code true} if the function is recognized as a test function. */ private boolean isTestFunction(Node n, String functionName) { return !(functionName == null || !TEST_FUNCTIONS_NAME_PATTERN.matcher(functionName).matches()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/parsing/0000755000175000017500000000000012115204405024277 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/parsing/JsDocTokenStream.java0000644000175000017500000002635312115204405030332 0ustar apoapo/* * 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.parsing; import com.google.common.base.Preconditions; import com.google.javascript.rhino.head.ScriptRuntime; /** * This class implements the scanner for JsDoc strings. * * It is heavily based on Rhino's TokenStream. * */ class JsDocTokenStream { /* * For chars - because we need something out-of-range * to check. (And checking EOF by exception is annoying.) * Note distinction from EOF token type! */ private final static int EOF_CHAR = -1; JsDocTokenStream(String sourceString) { this(sourceString, 0); } JsDocTokenStream(String sourceString, int lineno) { this(sourceString, lineno, 0); } JsDocTokenStream(String sourceString, int lineno, int initCharno) { Preconditions.checkNotNull(sourceString); this.lineno = lineno; this.sourceString = sourceString; this.sourceEnd = sourceString.length(); this.sourceCursor = this.cursor = 0; this.initLineno = lineno; this.initCharno = initCharno; } /** * Tokenizes JSDoc comments. */ @SuppressWarnings("fallthrough") final JsDocToken getJsDocToken() { int c; stringBufferTop = 0; for (;;) { // eat white spaces for (;;) { charno = -1; c = getChar(); if (c == EOF_CHAR) { return JsDocToken.EOF; } else if (c == '\n') { return JsDocToken.EOL; } else if (!isJSSpace(c)) { break; } } switch (c) { // annotation, e.g. @type or @constructor case '@': do { c = getChar(); if (isAlpha(c)) { addToString(c); } else { ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.ANNOTATION; } } while (true); case '*': if (matchChar('/')) { return JsDocToken.EOC; } else { return JsDocToken.STAR; } case ',': return JsDocToken.COMMA; case '>': return JsDocToken.GT; case '(': return JsDocToken.LP; case ')': return JsDocToken.RP; case '{': return JsDocToken.LC; case '}': return JsDocToken.RC; case '[': return JsDocToken.LB; case ']': return JsDocToken.RB; case '?': return JsDocToken.QMARK; case '!': return JsDocToken.BANG; case ':': return JsDocToken.COLON; case '=': return JsDocToken.EQUALS; case '|': matchChar('|'); return JsDocToken.PIPE; case '.': c = getChar(); if (c == '<') { return JsDocToken.LT; } else { if (c == '.') { c = getChar(); if (c == '.') { return JsDocToken.ELLIPSIS; } else { addToString('.'); } } // we may backtrack across line boundary ungetBuffer[ungetCursor++] = c; c = '.'; } // fall through default: { // recognize a JsDoc string but discard last . if it is followed by // a non-JsDoc comment char, e.g. Array.< int c1 = c; addToString(c); int c2 = getChar(); if (!isJSDocString(c2)) { ungetChar(c2); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { do { c1 = c2; c2 = getChar(); if (c1 == '.' && c2 == '<') { ungetChar(c2); ungetChar(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { if (isJSDocString(c2)) { addToString(c1); } else { ungetChar(c2); addToString(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } } } while (true); } } } } } /** * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. */ @SuppressWarnings("fallthrough") String getRemainingJSDocLine() { int c; for (;;) { c = getChar(); switch (c) { case '*': if (peekChar() != '/') { addToString(c); break; } // fall through case EOF_CHAR: case '\n': ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return this.string; default: addToString(c); break; } } } final int getLineno() { return lineno; } final int getCharno() { return lineno == initLineno? initCharno + charno : charno; } final String getString() { return string; } final boolean eof() { return hitEOF; } private String getStringFromBuffer() { tokenEnd = cursor; return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy(stringBuffer, 0, tmp, 0, N); stringBuffer = tmp; } stringBuffer[N] = (char)c; stringBufferTop = N + 1; } void ungetChar(int c) { // can not unread past across line boundary assert(!(ungetCursor != 0 && ungetBuffer[ungetCursor - 1] == '\n')); ungetBuffer[ungetCursor++] = c; cursor--; } private boolean matchChar(int test) { int c = getCharIgnoreLineEnd(); if (c == test) { tokenEnd = cursor; return true; } else { ungetCharIgnoreLineEnd(c); return false; } } private static boolean isAlpha(int c) { // Use 'Z' < 'a' if (c <= 'Z') { return 'A' <= c; } else { return 'a' <= c && c <= 'z'; } } private boolean isJSDocString(int c) { switch (c) { case '@': case '*': case ',': case '>': case ':': case '(': case ')': case '{': case '}': case '[': case ']': case '?': case '!': case '|': case '=': case EOF_CHAR: case '\n': return false; default: return !isJSSpace(c); } } /* As defined in ECMA. jsscan.c uses C isspace() (which allows * \v, I think.) note that code in getChar() implicitly accepts * '\r' == \u000D as well. */ static boolean isJSSpace(int c) { if (c <= 127) { return c == 0x20 || c == 0x9 || c == 0xC || c == 0xB; } else { return c == 0xA0 || Character.getType((char)c) == Character.SPACE_SEPARATOR; } } private static boolean isJSFormatChar(int c) { return c > 127 && Character.getType((char)c) == Character.FORMAT; } /** * Allows the JSDocParser to update the character offset * so that getCharno() returns a valid character position. */ void update() { charno = getOffset(); } private int peekChar() { int c = getChar(); ungetChar(c); return c; } protected int getChar() { if (ungetCursor != 0) { cursor++; --ungetCursor; if (charno == -1) { charno = getOffset(); } return ungetBuffer[ungetCursor]; } for(;;) { int c; if (sourceCursor == sourceEnd) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } cursor++; c = sourceString.charAt(sourceCursor++); if (lineEndChar >= 0) { if (lineEndChar == '\r' && c == '\n') { lineEndChar = '\n'; continue; } lineEndChar = -1; lineStart = sourceCursor - 1; lineno++; } if (c <= 127) { if (c == '\n' || c == '\r') { lineEndChar = c; c = '\n'; } } else { if (isJSFormatChar(c)) { continue; } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; } } if (charno == -1) { charno = getOffset(); } return c; } } private int getCharIgnoreLineEnd() { if (ungetCursor != 0) { cursor++; --ungetCursor; if (charno == -1) { charno = getOffset(); } return ungetBuffer[ungetCursor]; } for(;;) { int c; if (sourceCursor == sourceEnd) { hitEOF = true; if (charno == -1) { charno = getOffset(); } return EOF_CHAR; } cursor++; c = sourceString.charAt(sourceCursor++); if (c <= 127) { if (c == '\n' || c == '\r') { lineEndChar = c; c = '\n'; } } else { if (isJSFormatChar(c)) { continue; } if (ScriptRuntime.isJSLineTerminator(c)) { lineEndChar = c; c = '\n'; } } if (charno == -1) { charno = getOffset(); } return c; } } private void ungetCharIgnoreLineEnd(int c) { ungetBuffer[ungetCursor++] = c; cursor--; } /** * Returns the offset into the current line. */ final int getOffset() { return sourceCursor - lineStart - ungetCursor - 1; } // Set this to an initial non-null value so that the Parser has // something to retrieve even if an error has occurred and no // string is found. Fosters one class of error, but saves lots of // code. private String string = ""; private char[] stringBuffer = new char[128]; private int stringBufferTop; // Room to backtrace from to < on failed match of the last - in Provides utilities to help with parsing JSDoc annotations and performing AST transformations. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/parsing/JsDocInfoParser.java0000644000175000017500000022003412115204405030136 0ustar apoapo/* * Copyright 2007 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.parsing; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.parsing.Config.LanguageMode; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.JSDocInfoBuilder; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.ScriptRuntime; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.head.ErrorReporter; import com.google.javascript.rhino.head.ast.Comment; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A parser for JSDoc comments. * */ // TODO(nicksantos): Unify all the JSDocInfo stuff into one package, instead of // spreading it across multiple packages. public final class JsDocInfoParser { private final JsDocTokenStream stream; private final JSDocInfoBuilder jsdocBuilder; private final StaticSourceFile sourceFile; private final Node associatedNode; private final ErrorReporter errorReporter; private final ErrorReporterParser parser = new ErrorReporterParser(); // Use a template node for properties set on all nodes to minimize the // memory footprint associated with these (similar to IRFactory). private final Node templateNode; private class ErrorReporterParser { void addParserWarning(String messageId, String messageArg, int lineno, int charno) { errorReporter.warning(ScriptRuntime.getMessage1(messageId, messageArg), getSourceName(), lineno, null, charno); } void addParserWarning(String messageId, int lineno, int charno) { errorReporter.warning(ScriptRuntime.getMessage0(messageId), getSourceName(), lineno, null, charno); } void addTypeWarning(String messageId, String messageArg, int lineno, int charno) { errorReporter.warning( "Bad type annotation. " + ScriptRuntime.getMessage1(messageId, messageArg), getSourceName(), lineno, null, charno); } void addTypeWarning(String messageId, int lineno, int charno) { errorReporter.warning( "Bad type annotation. " + ScriptRuntime.getMessage0(messageId), getSourceName(), lineno, null, charno); } } // The DocInfo with the fileoverview tag for the whole file. private JSDocInfo fileOverviewJSDocInfo = null; private State state; private final Map annotationNames; private final Set suppressionNames; static private final Set modifiesAnnotationKeywords = ImmutableSet.of("this", "arguments"); private Node.FileLevelJsDocBuilder fileLevelJsDocBuilder; /** * Sets the JsDocBuilder for the file-level (root) node of this parse. The * parser uses the builder to append any preserve annotations it encounters * in JsDoc comments. * * @param fileLevelJsDocBuilder */ void setFileLevelJsDocBuilder( Node.FileLevelJsDocBuilder fileLevelJsDocBuilder) { this.fileLevelJsDocBuilder = fileLevelJsDocBuilder; } /** * Sets the file overview JSDocInfo, in order to warn about multiple uses of * the @fileoverview tag in a file. */ void setFileOverviewJSDocInfo(JSDocInfo fileOverviewJSDocInfo) { this.fileOverviewJSDocInfo = fileOverviewJSDocInfo; } private enum State { SEARCHING_ANNOTATION, SEARCHING_NEWLINE, NEXT_IS_ANNOTATION } JsDocInfoParser(JsDocTokenStream stream, Comment commentNode, Node associatedNode, Config config, ErrorReporter errorReporter) { this.stream = stream; this.associatedNode = associatedNode; // Sometimes this will be null in tests. this.sourceFile = associatedNode == null ? null : associatedNode.getStaticSourceFile(); this.jsdocBuilder = new JSDocInfoBuilder(config.parseJsDocDocumentation); if (commentNode != null) { this.jsdocBuilder.recordOriginalCommentString(commentNode.getValue()); } this.annotationNames = config.annotationNames; this.suppressionNames = config.suppressionNames; this.errorReporter = errorReporter; this.templateNode = this.createTemplateNode(); } private String getSourceName() { return sourceFile == null ? null : sourceFile.getName(); } /** * Parse a description as a {@code @type}. */ public JSDocInfo parseInlineTypeDoc() { skipEOLs(); Node typeAst = parseAndRecordTypeNode(next()); JSTypeExpression expr = createJSTypeExpression(typeAst); if (expr != null) { jsdocBuilder.recordType(expr); return retrieveAndResetParsedJSDocInfo(); } return null; } /** * Parses a string containing a JsDoc type declaration, returning the * type if the parsing succeeded or {@code null} if it failed. */ public static Node parseTypeString(String typeString) { Config config = new Config( Sets.newHashSet(), Sets.newHashSet(), false, LanguageMode.ECMASCRIPT3, false); JsDocInfoParser parser = new JsDocInfoParser( new JsDocTokenStream(typeString), null, null, config, NullErrorReporter.forNewRhino()); return parser.parseTopLevelTypeExpression(parser.next()); } /** * Parses a {@link JSDocInfo} object. This parsing method reads all tokens * returned by the {@link JsDocTokenStream#getJsDocToken()} method until the * {@link JsDocToken#EOC} is returned. * * @return {@code true} if JSDoc information was correctly parsed, * {@code false} otherwise */ boolean parse() { state = State.SEARCHING_ANNOTATION; skipEOLs(); JsDocToken token = next(); // Always record that we have a comment. if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo blockInfo = extractBlockComment(token); token = blockInfo.token; if (!blockInfo.string.isEmpty()) { jsdocBuilder.recordBlockDescription(blockInfo.string); } } else { if (token != JsDocToken.ANNOTATION && token != JsDocToken.EOC) { // Mark that there was a description, but don't bother marking // what it was. jsdocBuilder.recordBlockDescription(""); } } return parseHelperLoop(token, Lists.newArrayList()); } private boolean parseHelperLoop(JsDocToken token, List extendedTypes) { while (true) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; token = parseAnnotation(token, extendedTypes); } else { token = next(); } break; case EOC: if (hasParsedFileOverviewDocInfo()) { fileOverviewJSDocInfo = retrieveAndResetParsedJSDocInfo(); } checkExtendedTypes(extendedTypes); return true; case EOF: // discard any accumulated information jsdocBuilder.build(null); parser.addParserWarning("msg.unexpected.eof", stream.getLineno(), stream.getCharno()); checkExtendedTypes(extendedTypes); return false; case EOL: if (state == State.SEARCHING_NEWLINE) { state = State.SEARCHING_ANNOTATION; } token = next(); break; default: if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { token = next(); } else { state = State.SEARCHING_NEWLINE; token = eatTokensUntilEOL(); } break; } } } private JsDocToken parseAnnotation(JsDocToken token, List extendedTypes) { // JSTypes are represented as Rhino AST nodes, and then resolved later. JSTypeExpression type; int lineno = stream.getLineno(); int charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addParserWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case NG_INJECT: if (jsdocBuilder.isNgInjectRecorded()) { parser.addParserWarning("msg.jsdoc.nginject.extra", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.recordNgInject(true); } return eatTokensUntilEOL(); case AUTHOR: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo authorInfo = extractSingleLineBlock(); String author = authorInfo.string; if (author.length() == 0) { parser.addParserWarning("msg.jsdoc.authormissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addAuthor(author); } token = authorInfo.token; } else { token = eatTokensUntilEOL(token); } return token; case CONSISTENTIDGENERATOR: if (!jsdocBuilder.recordConsistentIdGenerator()) { parser.addParserWarning("msg.jsdoc.consistidgen", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case STRUCT: if (!jsdocBuilder.recordStruct()) { parser.addTypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case DICT: if (!jsdocBuilder.recordDict()) { parser.addTypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case CONSTRUCTOR: if (!jsdocBuilder.recordConstructor()) { if (jsdocBuilder.isInterfaceRecorded()) { parser.addTypeWarning("msg.jsdoc.interface.constructor", stream.getLineno(), stream.getCharno()); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } } return eatTokensUntilEOL(); case DEPRECATED: if (!jsdocBuilder.recordDeprecated()) { parser.addParserWarning("msg.jsdoc.deprecated", stream.getLineno(), stream.getCharno()); } // Find the reason/description, if any. ExtractionInfo reasonInfo = extractMultilineTextualBlock(token); String reason = reasonInfo.string; if (reason.length() > 0) { jsdocBuilder.recordDeprecationReason(reason); } token = reasonInfo.token; return token; case INTERFACE: if (!jsdocBuilder.recordInterface()) { if (jsdocBuilder.isConstructorRecorded()) { parser.addTypeWarning("msg.jsdoc.interface.constructor", stream.getLineno(), stream.getCharno()); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", stream.getLineno(), stream.getCharno()); } } return eatTokensUntilEOL(); case DESC: if (jsdocBuilder.isDescriptionRecorded()) { parser.addParserWarning("msg.jsdoc.desc.extra", stream.getLineno(), stream.getCharno()); return eatTokensUntilEOL(); } else { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; jsdocBuilder.recordDescription(description); token = descriptionInfo.token; return token; } case FILE_OVERVIEW: String fileOverview = ""; if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo fileOverviewInfo = extractMultilineTextualBlock(token, WhitespaceOption.TRIM); fileOverview = fileOverviewInfo.string; token = fileOverviewInfo.token; } else { token = eatTokensUntilEOL(token); } if (!jsdocBuilder.recordFileOverview(fileOverview)) { parser.addParserWarning("msg.jsdoc.fileoverview.extra", stream.getLineno(), stream.getCharno()); } return token; case LICENSE: case PRESERVE: ExtractionInfo preserveInfo = extractMultilineTextualBlock(token, WhitespaceOption.PRESERVE); String preserve = preserveInfo.string; if (preserve.length() > 0) { if (fileLevelJsDocBuilder != null) { fileLevelJsDocBuilder.append(preserve); } } token = preserveInfo.token; return token; case ENUM: token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token != JsDocToken.EOL && token != JsDocToken.EOC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); } if (type == null) { type = createJSTypeExpression(newStringNode("number")); } if (!jsdocBuilder.recordEnumParameterType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } token = eatTokensUntilEOL(token); return token; case EXPORT: if (!jsdocBuilder.recordExport()) { parser.addParserWarning("msg.jsdoc.export", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case EXPOSE: if (!jsdocBuilder.recordExpose()) { parser.addParserWarning("msg.jsdoc.expose", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case EXTERNS: if (!jsdocBuilder.recordExterns()) { parser.addParserWarning("msg.jsdoc.externs", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case JAVA_DISPATCH: if (!jsdocBuilder.recordJavaDispatch()) { parser.addParserWarning("msg.jsdoc.javadispatch", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case EXTENDS: case IMPLEMENTS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); boolean matchingRc = false; if (token == JsDocToken.LC) { token = next(); matchingRc = true; } if (token == JsDocToken.STRING) { Node typeNode = parseAndRecordTypeNameNode( token, lineno, charno, matchingRc); lineno = stream.getLineno(); charno = stream.getCharno(); typeNode = wrapNode(Token.BANG, typeNode); type = createJSTypeExpression(typeNode); if (annotation == Annotation.EXTENDS) { // record the extended type, check later extendedTypes.add(new ExtendedTypeInfo( type, stream.getLineno(), stream.getCharno())); } else { Preconditions.checkState( annotation == Annotation.IMPLEMENTS); if (!jsdocBuilder.recordImplementedInterface(type)) { parser.addTypeWarning("msg.jsdoc.implements.duplicate", lineno, charno); } } token = next(); if (matchingRc) { if (token != JsDocToken.RC) { parser.addTypeWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } } else if (token != JsDocToken.EOL && token != JsDocToken.EOF && token != JsDocToken.EOC) { parser.addTypeWarning("msg.end.annotation.expected", stream.getLineno(), stream.getCharno()); } } else { parser.addTypeWarning("msg.no.type.name", lineno, charno); } token = eatTokensUntilEOL(token); return token; case HIDDEN: if (!jsdocBuilder.recordHiddenness()) { parser.addParserWarning("msg.jsdoc.hidden", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case LENDS: skipEOLs(); matchingRc = false; if (match(JsDocToken.LC)) { token = next(); matchingRc = true; } if (match(JsDocToken.STRING)) { token = next(); if (!jsdocBuilder.recordLends(stream.getString())) { parser.addTypeWarning("msg.jsdoc.lends.incompatible", stream.getLineno(), stream.getCharno()); } } else { parser.addTypeWarning("msg.jsdoc.lends.missing", stream.getLineno(), stream.getCharno()); } if (matchingRc && !match(JsDocToken.RC)) { parser.addTypeWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case MEANING: ExtractionInfo meaningInfo = extractMultilineTextualBlock(token); String meaning = meaningInfo.string; token = meaningInfo.token; if (!jsdocBuilder.recordMeaning(meaning)) { parser.addParserWarning("msg.jsdoc.meaning.extra", stream.getLineno(), stream.getCharno()); } return token; case NO_ALIAS: if (!jsdocBuilder.recordNoAlias()) { parser.addParserWarning("msg.jsdoc.noalias", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case NO_COMPILE: if (!jsdocBuilder.recordNoCompile()) { parser.addParserWarning("msg.jsdoc.nocompile", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case NO_TYPE_CHECK: if (!jsdocBuilder.recordNoTypeCheck()) { parser.addParserWarning("msg.jsdoc.nocheck", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case NOT_IMPLEMENTED: return eatTokensUntilEOL(); case INHERIT_DOC: case OVERRIDE: if (!jsdocBuilder.recordOverride()) { parser.addTypeWarning("msg.jsdoc.override", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case THROWS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing return eatTokensUntilEOL(); } } // *Update* the token to that after the type annotation. token = current(); // Save the throw type. jsdocBuilder.recordThrowType(type); // Find the throw's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; if (description.length() > 0) { jsdocBuilder.recordThrowDescription(type, description); } token = descriptionInfo.token; } else { token = eatTokensUntilEOL(token); } return token; case PARAM: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing return eatTokensUntilEOL(); } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addTypeWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter // annotation. It makes no sense under our type // system. if (JsDocToken.EQUALS == token) { token = next(); if (JsDocToken.STRING == token) { token = next(); } } if (JsDocToken.RB != token) { reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } else if (type != null) { // Make the type expression optional, if it isn't // already. type = JSTypeExpression.makeOptionalArg(type); } } // If the param name has a DOT in it, just throw it out // quietly. We do not handle the JsDocToolkit method // for handling properties of params. if (name.indexOf('.') > -1) { name = null; } else if (!jsdocBuilder.recordParameter(name, type)) { if (jsdocBuilder.hasParameter(name)) { parser.addTypeWarning("msg.dup.variable.name", name, lineno, charno); } else { parser.addTypeWarning("msg.jsdoc.incompat.type", name, lineno, charno); } } } if (name == null) { token = eatTokensUntilEOL(token); return token; } jsdocBuilder.markName(name, sourceFile, lineno, charno); // Find the parameter's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo paramDescriptionInfo = extractMultilineTextualBlock(token); String paramDescription = paramDescriptionInfo.string; if (paramDescription.length() > 0) { jsdocBuilder.recordParameterDescription(name, paramDescription); } token = paramDescriptionInfo.token; } else { token = eatTokensUntilEOL(token); } return token; case PRESERVE_TRY: if (!jsdocBuilder.recordPreserveTry()) { parser.addParserWarning("msg.jsdoc.preservertry", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case NO_SHADOW: if (!jsdocBuilder.recordNoShadow()) { parser.addParserWarning("msg.jsdoc.noshadow", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case NO_SIDE_EFFECTS: if (!jsdocBuilder.recordNoSideEffects()) { parser.addParserWarning("msg.jsdoc.nosideeffects", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case MODIFIES: token = parseModifiesTag(next()); return token; case IMPLICIT_CAST: if (!jsdocBuilder.recordImplicitCast()) { parser.addTypeWarning("msg.jsdoc.implicitcast", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case SEE: if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo referenceInfo = extractSingleLineBlock(); String reference = referenceInfo.string; if (reference.length() == 0) { parser.addParserWarning("msg.jsdoc.seemissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addReference(reference); } token = referenceInfo.token; } else { token = eatTokensUntilEOL(token); } return token; case STABLEIDGENERATOR: if (!jsdocBuilder.recordStableIdGenerator()) { parser.addParserWarning("msg.jsdoc.stableidgen", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case SUPPRESS: token = parseSuppressTag(next()); return token; case TEMPLATE: { ExtractionInfo templateInfo = extractSingleLineBlock(); List names = Lists.newArrayList( Splitter.on(',') .trimResults() .split(templateInfo.string)); if (names.size() == 0 || names.get(0).length() == 0) { parser.addTypeWarning("msg.jsdoc.templatemissing", stream.getLineno(), stream.getCharno()); } else if (!jsdocBuilder.recordTemplateTypeNames(names)) { parser.addTypeWarning("msg.jsdoc.template.at.most.once", stream.getLineno(), stream.getCharno()); } token = templateInfo.token; return token; } case CLASS_TEMPLATE: { ExtractionInfo classTemplateInfo = extractSingleLineBlock(); List names = Lists.newArrayList( Splitter.on(',') .trimResults() .split(classTemplateInfo.string)); if (names.size() == 0 || names.get(0).length() == 0) { parser.addTypeWarning( "msg.jsdoc.classtemplate.missing.type.name", stream.getLineno(), stream.getCharno()); } else if (!jsdocBuilder.recordClassTemplateTypeNames(names)) { parser.addTypeWarning( "msg.jsdoc.classtemplate.at.most.once", stream.getLineno(), stream.getCharno()); } token = classTemplateInfo.token; return token; } case IDGENERATOR: if (!jsdocBuilder.recordIdGenerator()) { parser.addParserWarning("msg.jsdoc.idgen", stream.getLineno(), stream.getCharno()); } return eatTokensUntilEOL(); case VERSION: ExtractionInfo versionInfo = extractSingleLineBlock(); String version = versionInfo.string; if (version.length() == 0) { parser.addParserWarning("msg.jsdoc.versionmissing", stream.getLineno(), stream.getCharno()); } else { if (!jsdocBuilder.recordVersion(version)) { parser.addParserWarning("msg.jsdoc.extraversion", stream.getLineno(), stream.getCharno()); } } token = versionInfo.token; return token; case CONSTANT: case DEFINE: case RETURN: case PRIVATE: case PROTECTED: case PUBLIC: case THIS: case TYPE: case TYPEDEF: lineno = stream.getLineno(); charno = stream.getCharno(); Node typeNode = null; boolean hasType = lookAheadForTypeAnnotation(); boolean isAlternateTypeAnnotation = (annotation == Annotation.PRIVATE || annotation == Annotation.PROTECTED || annotation == Annotation.PUBLIC || annotation == Annotation.CONSTANT); boolean canSkipTypeAnnotation = (isAlternateTypeAnnotation || annotation == Annotation.RETURN); type = null; if (hasType || !canSkipTypeAnnotation) { skipEOLs(); token = next(); typeNode = parseAndRecordTypeNode(token); if (annotation == Annotation.THIS) { typeNode = wrapNode(Token.BANG, typeNode); } type = createJSTypeExpression(typeNode); } // The error was reported during recursive descent // recovering parsing boolean hasError = type == null && !canSkipTypeAnnotation; if (!hasError) { // Record types for @type. // If the @private, @protected, or @public annotations // have a type attached, pretend that they actually wrote: // @type {type}\n@private // This will have some weird behavior in some cases // (for example, @private can now be used as a type-cast), // but should be mostly OK. if ((type != null && isAlternateTypeAnnotation) || annotation == Annotation.TYPE) { if (!jsdocBuilder.recordType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } } switch (annotation) { case CONSTANT: if (!jsdocBuilder.recordConstancy()) { parser.addParserWarning("msg.jsdoc.const", stream.getLineno(), stream.getCharno()); } break; case DEFINE: if (!jsdocBuilder.recordDefineType(type)) { parser.addParserWarning("msg.jsdoc.define", lineno, charno); } break; case PRIVATE: if (!jsdocBuilder.recordVisibility(Visibility.PRIVATE)) { parser.addParserWarning( "msg.jsdoc.visibility.private", lineno, charno); } break; case PROTECTED: if (!jsdocBuilder.recordVisibility(Visibility.PROTECTED)) { parser.addParserWarning( "msg.jsdoc.visibility.protected", lineno, charno); } break; case PUBLIC: if (!jsdocBuilder.recordVisibility(Visibility.PUBLIC)) { parser.addParserWarning( "msg.jsdoc.visibility.public", lineno, charno); } break; case RETURN: if (type == null) { type = createJSTypeExpression(newNode(Token.QMARK)); } if (!jsdocBuilder.recordReturnType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); break; } // Find the return's description (if applicable). if (jsdocBuilder.shouldParseDocumentation()) { ExtractionInfo returnDescriptionInfo = extractMultilineTextualBlock(token); String returnDescription = returnDescriptionInfo.string; if (returnDescription.length() > 0) { jsdocBuilder.recordReturnDescription( returnDescription); } token = returnDescriptionInfo.token; } else { token = eatTokensUntilEOL(token); } return token; case THIS: if (!jsdocBuilder.recordThisType(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } break; case TYPEDEF: if (!jsdocBuilder.recordTypedef(type)) { parser.addTypeWarning( "msg.jsdoc.incompat.type", lineno, charno); } break; } } return eatTokensUntilEOL(); } } return next(); } private void checkExtendedTypes(List extendedTypes) { for (ExtendedTypeInfo typeInfo : extendedTypes) { // If interface, record the multiple extended interfaces if (jsdocBuilder.isInterfaceRecorded()) { if (!jsdocBuilder.recordExtendedInterface(typeInfo.type)) { parser.addParserWarning("msg.jsdoc.extends.duplicate", typeInfo.lineno, typeInfo.charno); } } else { if (!jsdocBuilder.recordBaseType(typeInfo.type)) { parser.addTypeWarning("msg.jsdoc.incompat.type", typeInfo.lineno, typeInfo.charno); } } } } /** * Parse a {@code @suppress} tag of the form * {@code @suppress{warning1|warning2}}. * * @param token The current token. */ private JsDocToken parseSuppressTag(JsDocToken token) { if (token == JsDocToken.LC) { Set suppressions = new HashSet(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!suppressionNames.contains(name)) { parser.addParserWarning("msg.jsdoc.suppress.unknown", name, stream.getLineno(), stream.getCharno()); } suppressions.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE, JsDocToken.COMMA)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addParserWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Parse a {@code @modifies} tag of the form * {@code @modifies{this|arguments|param}}. * * @param token The current token. */ private JsDocToken parseModifiesTag(JsDocToken token) { if (token == JsDocToken.LC) { Set modifies = new HashSet(); while (true) { if (match(JsDocToken.STRING)) { String name = stream.getString(); if (!modifiesAnnotationKeywords.contains(name) && !jsdocBuilder.hasParameter(name)) { parser.addParserWarning("msg.jsdoc.modifies.unknown", name, stream.getLineno(), stream.getCharno()); } modifies.add(stream.getString()); token = next(); } else { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addParserWarning("msg.jsdoc.modifies", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordModifies(modifies)) { parser.addParserWarning("msg.jsdoc.modifies.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @return The type expression found or null if none. */ Node parseAndRecordTypeNode(JsDocToken token) { return parseAndRecordTypeNode(token, token == JsDocToken.LC); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param matchingLC Whether the type expression starts with a "{". * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, boolean matchingLC) { return parseAndRecordTypeNode(token, stream.getLineno(), stream.getCharno(), matchingLC, false); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @return The type expression found or null if none. */ private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC) { return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * Parameter type expressions are special for two reasons: *
    *
  1. They must begin with '{', to distinguish type names from param names. *
  2. They may end in '=', to denote optionality. *
* * @param token The current token. * @return The type expression found or null if none. */ private Node parseAndRecordParamTypeNode(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); int lineno = stream.getLineno(); int startCharno = stream.getCharno(); Node typeNode = parseParamTypeExpressionAnnotation(token); if (typeNode != null) { int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endLineno, endCharno, true); } return typeNode; } /** * Looks for a parameter type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null) { int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode( typeNode, lineno, startCharno, endLineno, endCharno, matchingLC); } return typeNode; } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, getSourceName()); } /** * Tuple for returning both the string extracted and the * new token following a call to any of the extract*Block * methods. */ private static class ExtractionInfo { private final String string; private final JsDocToken token; public ExtractionInfo(String string, JsDocToken token) { this.string = string; this.token = token; } } /** * Tuple for recording extended types */ private static class ExtendedTypeInfo { final JSTypeExpression type; final int lineno; final int charno; public ExtendedTypeInfo(JSTypeExpression type, int lineno, int charno) { this.type = type; this.lineno = lineno; this.charno = charno; } } /** * Extracts the text found on the current line starting at token. Note that * token = token.info; should be called after this method is used to update * the token properly in the parser. * * @return The extraction information. */ private ExtractionInfo extractSingleLineBlock() { // Get the current starting point. stream.update(); int lineno = stream.getLineno(); int charno = stream.getCharno() + 1; String line = getRemainingJSDocLine().trim(); // Record the textual description. if (line.length() > 0) { jsdocBuilder.markText(line, lineno, charno, lineno, charno + line.length()); } return new ExtractionInfo(line, next()); } private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE); } private enum WhitespaceOption { /** * Preserves all whitespace and formatting. Needed for licenses and * purposely formatted text. */ PRESERVE, /** Preserves newlines but trims the output. */ TRIM, /** Removes newlines and turns the output into a single line string. */ SINGLE_LINE } /** * Extracts the text found on the current line and all subsequent * until either an annotation, end of comment or end of file is reached. * Note that if this method detects an end of line as the first token, it * will quit immediately (indicating that there is no text where it was * expected). Note that token = info.token; should be called after this * method is used to update the token properly in the parser. * * @param token The start token. * @param option How to handle whitespace. * * @return The extraction information. */ @SuppressWarnings("fallthrough") private ExtractionInfo extractMultilineTextualBlock(JsDocToken token, WhitespaceOption option) { if (token == JsDocToken.EOC || token == JsDocToken.EOL || token == JsDocToken.EOF) { return new ExtractionInfo("", token); } stream.update(); int startLineno = stream.getLineno(); int startCharno = stream.getCharno() + 1; // Read the content from the first line. String line = getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = line.trim(); } StringBuilder builder = new StringBuilder(); builder.append(line); state = State.SEARCHING_ANNOTATION; token = next(); boolean ignoreStar = false; // Track the start of the line to count whitespace that // the tokenizer skipped. Because this case is rare, it's easier // to do this here than in the tokenizer. int lineStartChar = -1; do { switch (token) { case STAR: if (ignoreStar) { // Mark the position after the star as the new start of the line. lineStartChar = stream.getCharno() + 1; } else { // The star is part of the comment. if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; lineStartChar = 0; token = next(); continue; default: ignoreStar = false; state = State.SEARCHING_ANNOTATION; boolean isEOC = token == JsDocToken.EOC; if (!isEOC) { if (lineStartChar != -1 && option == WhitespaceOption.PRESERVE) { int numSpaces = stream.getCharno() - lineStartChar; for (int i = 0; i < numSpaces; i++) { builder.append(' '); } lineStartChar = -1; } else if (builder.length() > 0) { // All tokens must be separated by a space. builder.append(' '); } } if (token == JsDocToken.EOC || token == JsDocToken.EOF || // When we're capturing a license block, annotations // in the block are OK. (token == JsDocToken.ANNOTATION && option != WhitespaceOption.PRESERVE)) { String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(multilineText, startLineno, startCharno, endLineno, endCharno); } return new ExtractionInfo(multilineText, token); } builder.append(toString(token)); line = getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = trimEnd(line); } builder.append(line); token = next(); } } while (true); } /** * Extracts the top-level block comment from the JsDoc comment, if any. * This method differs from the extractMultilineTextualBlock in that it * terminates under different conditions (it doesn't have the same * prechecks), it does not first read in the remaining of the current * line and its conditions for ignoring the "*" (STAR) are different. * * @param token The starting token. * * @return The extraction information. */ private ExtractionInfo extractBlockComment(JsDocToken token) { StringBuilder builder = new StringBuilder(); boolean ignoreStar = true; do { switch (token) { case ANNOTATION: case EOC: case EOF: return new ExtractionInfo(builder.toString().trim(), token); case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: ignoreStar = true; builder.append('\n'); token = next(); continue; default: if (!ignoreStar && builder.length() > 0) { builder.append(' '); } ignoreStar = false; builder.append(toString(token)); String line = getRemainingJSDocLine(); line = trimEnd(line); builder.append(line); token = next(); } } while (true); } /** * Trim characters from only the end of a string. * This method will remove all whitespace characters * (defined by Character.isWhitespace(char), in addition to the characters * provided, from the end of the provided string. * * @param s String to be trimmed * @return String with whitespace and characters in extraChars removed * from the end. */ private static String trimEnd(String s) { int trimCount = 0; while (trimCount < s.length()) { char ch = s.charAt(s.length() - trimCount - 1); if (Character.isWhitespace(ch)) { trimCount++; } else { break; } } if (trimCount == 0) { return s; } return s.substring(0, s.length() - trimCount); } // Based on ES4 grammar proposed on July 10, 2008. // http://wiki.ecmascript.org/doku.php?id=spec:spec // Deliberately written to line up with the actual grammar rules, // for maximum flexibility. // TODO(nicksantos): The current implementation tries to maintain backwards // compatibility with previous versions of the spec whenever we can. // We should try to gradually withdraw support for these. /** * TypeExpressionAnnotation := TypeExpression | * '{' TopLevelTypeExpression '}' */ private Node parseTypeExpressionAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTopLevelTypeExpression(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, IR.empty()); } restArg = true; } Node typeNode = parseTopLevelTypeExpression(token); if (typeNode != null) { skipEOLs(); if (restArg) { typeNode = wrapNode(Token.ELLIPSIS, typeNode); } else if (match(JsDocToken.EQUALS)) { next(); skipEOLs(); typeNode = wrapNode(Token.EQUALS, typeNode); } if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } /** * TypeNameAnnotation := TypeName | '{' TypeName '}' */ private Node parseTypeNameAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTypeName(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeName(token); } } /** * TopLevelTypeExpression := TypeExpression * | TypeUnionList * * We made this rule up, for the sake of backwards compatibility. */ private Node parseTopLevelTypeExpression(JsDocToken token) { Node typeExpr = parseTypeExpression(token); if (typeExpr != null) { // top-level unions are allowed if (match(JsDocToken.PIPE)) { next(); if (match(JsDocToken.PIPE)) { // We support double pipes for backwards-compatibility. next(); } skipEOLs(); token = next(); return parseUnionTypeWithAlternate(token, typeExpr); } } return typeExpr; } /** * TypeExpressionList := TopLevelTypeExpression * | TopLevelTypeExpression ',' TypeExpressionList */ private Node parseTypeExpressionList(JsDocToken token) { Node typeExpr = parseTopLevelTypeExpression(token); if (typeExpr == null) { return null; } Node typeList = IR.block(); typeList.addChildToBack(typeExpr); while (match(JsDocToken.COMMA)) { next(); skipEOLs(); typeExpr = parseTopLevelTypeExpression(next()); if (typeExpr == null) { return null; } typeList.addChildToBack(typeExpr); } return typeList; } /** * TypeExpression := BasicTypeExpression * | '?' BasicTypeExpression * | '!' BasicTypeExpression * | BasicTypeExpression '?' * | BasicTypeExpression '!' * | '?' */ private Node parseTypeExpression(JsDocToken token) { if (token == JsDocToken.QMARK) { // A QMARK could mean that a type is nullable, or that it's unknown. // We use look-ahead 1 to determine whether it's unknown. Otherwise, // we assume it means nullable. There are 5 cases: // {?} - right curly // {?=} - equals // {function(?, number)} - comma // {function(number, ?)} - right paren // {function(number, ...[?])} - right bracket // {function(): ?|number} - pipe // {Array.} - greater than // I'm not a big fan of using look-ahead for this, but it makes // the type language a lot nicer. token = next(); if (token == JsDocToken.COMMA || token == JsDocToken.EQUALS || token == JsDocToken.RB || token == JsDocToken.RC || token == JsDocToken.RP || token == JsDocToken.PIPE || token == JsDocToken.GT) { restoreLookAhead(token); return newNode(Token.QMARK); } return wrapNode(Token.QMARK, parseBasicTypeExpression(token)); } else if (token == JsDocToken.BANG) { return wrapNode(Token.BANG, parseBasicTypeExpression(next())); } else { Node basicTypeExpr = parseBasicTypeExpression(token); if (basicTypeExpr != null) { if (match(JsDocToken.QMARK)) { next(); return wrapNode(Token.QMARK, basicTypeExpr); } else if (match(JsDocToken.BANG)) { next(); return wrapNode(Token.BANG, basicTypeExpr); } } return basicTypeExpr; } } /** * BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName * | FunctionType | UnionType | RecordType | ArrayType */ private Node parseBasicTypeExpression(JsDocToken token) { if (token == JsDocToken.STAR) { return newNode(Token.STAR); } else if (token == JsDocToken.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else { return parseTypeName(token); } } restoreLookAhead(token); return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' */ private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } String typeName = stream.getString(); int lineno = stream.getLineno(); int charno = stream.getCharno(); while (match(JsDocToken.EOL) && typeName.charAt(typeName.length() - 1) == '.') { skipEOLs(); if (match(JsDocToken.STRING)) { next(); typeName += stream.getString(); } } Node typeNameNode = newStringNode(typeName, lineno, charno); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeNameNode.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeNameNode; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token) { // NOTE(nicksantos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { restoreLookAhead(token); return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING) { String tokenStr = stream.getString(); boolean isThis = "this".equals(tokenStr); boolean isNew = "new".equals(tokenStr); if (isThis || isNew) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node contextType = wrapNode( isThis ? Token.THIS : Token.NEW, parseTypeName(next())); if (contextType == null) { return null; } functionType.addChildToFront(contextType); } else { return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); } if (match(JsDocToken.COMMA)) { next(); skipEOLs(); token = next(); } else { hasParams = false; } } } if (hasParams) { parameters = parseParametersType(token); if (parameters == null) { return null; } } } if (parameters != null) { functionType.addChildToBack(parameters); } skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } skipEOLs(); Node resultType = parseResultType(next()); if (resultType == null) { return null; } else { functionType.addChildToBack(resultType); } return functionType; } /** * ParametersType := RestParameterType | NonRestParametersType * | NonRestParametersType ',' RestParameterType * RestParameterType := '...' Identifier * NonRestParametersType := ParameterType ',' NonRestParametersType * | ParameterType * | OptionalParametersType * OptionalParametersType := OptionalParameterType * | OptionalParameterType, OptionalParametersType * OptionalParameterType := ParameterType= * ParameterType := TypeExpression | Identifier ':' TypeExpression */ // NOTE(nicksantos): The official ES4 grammar forces optional and rest // arguments to come after the required arguments. Our parser does not // enforce this. Instead we allow them anywhere in the function at parse-time, // and then warn about them during type resolution. // // In theory, it might be mathematically nicer to do the order-checking here. // But in practice, the order-checking for structural functions is exactly // the same as the order-checking for @param annotations. And the latter // has to happen during type resolution. Rather than duplicate the // order-checking in two places, we just do all of it in type resolution. private Node parseParametersType(JsDocToken token) { Node paramsType = newNode(Token.PARAM_LIST); boolean isVarArgs = false; Node paramType = null; if (token != JsDocToken.RP) { do { if (paramType != null) { // skip past the comma next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { // In the latest ES4 proposal, there are no type constraints allowed // on variable arguments. We support the old syntax for backwards // compatibility, but we should gradually tear it out. skipEOLs(); if (match(JsDocToken.RP)) { paramType = newNode(Token.ELLIPSIS); } else { skipEOLs(); if (!match(JsDocToken.LB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lb"); } next(); skipEOLs(); paramType = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); skipEOLs(); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } skipEOLs(); next(); } isVarArgs = true; } else { paramType = parseTypeExpression(token); if (match(JsDocToken.EQUALS)) { skipEOLs(); next(); paramType = wrapNode(Token.EQUALS, paramType); } } if (paramType == null) { return null; } paramsType.addChildToBack(paramType); if (isVarArgs) { break; } } while (match(JsDocToken.COMMA)); } if (isVarArgs && match(JsDocToken.COMMA)) { return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); } // The right paren will be checked by parseFunctionType return paramsType; } /** * ResultType := | ':' void | ':' TypeExpression */ private Node parseResultType(JsDocToken token) { skipEOLs(); if (!match(JsDocToken.COLON)) { return newNode(Token.EMPTY); } token = next(); skipEOLs(); if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { next(); return newNode(Token.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatibility. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) { return null; } union.addChildToBack(expr); // We support commas for backwards compatibility. } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); if (alternate == null) { skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } next(); } return union; } /** * ArrayType := '[' ElementTypeList ']' * ElementTypeList := | TypeExpression | '...' TypeExpression * | TypeExpression ',' ElementTypeList */ private Node parseArrayType(JsDocToken token) { Node array = newNode(Token.LB); Node arg = null; boolean hasVarArgs = false; do { if (arg != null) { next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { arg = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); hasVarArgs = true; } else { arg = parseTypeExpression(token); } if (arg == null) { return null; } array.addChildToBack(arg); if (hasVarArgs) { break; } skipEOLs(); } while (match(JsDocToken.COMMA)); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } next(); return array; } /** * RecordType := '{' FieldTypeList '}' */ private Node parseRecordType(JsDocToken token) { Node recordType = newNode(Token.LC); Node fieldTypeList = parseFieldTypeList(token); if (fieldTypeList == null) { return reportGenericTypeSyntaxWarning(); } skipEOLs(); if (!match(JsDocToken.RC)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } next(); recordType.addChildToBack(fieldTypeList); return recordType; } /** * FieldTypeList := FieldType | FieldType ',' FieldTypeList */ private Node parseFieldTypeList(JsDocToken token) { Node fieldTypeList = newNode(Token.LB); do { Node fieldType = parseFieldType(token); if (fieldType == null) { return null; } fieldTypeList.addChildToBack(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newStringNode(String s) { return newStringNode(s, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s, int lineno, int charno) { Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode); n.setLength(s.length()); return n; } // This is similar to IRFactory.createTemplateNode to share common props // e.g., source-name, between all nodes. private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = IR.script(); templateNode.setStaticSourceFile( this.associatedNode != null ? this.associatedNode.getStaticSourceFile() : null); return templateNode; } private Node reportTypeSyntaxWarning(String warning) { parser.addTypeWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL() { return eatTokensUntilEOL(next()); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL(JsDocToken token) { do { if (token == JsDocToken.EOL || token == JsDocToken.EOC || token == JsDocToken.EOF) { state = State.SEARCHING_ANNOTATION; return token; } token = next(); } while (true); } /** * Specific value indicating that the {@link #unreadToken} contains no token. */ private static final JsDocToken NO_UNREAD_TOKEN = null; /** * One token buffer. */ private JsDocToken unreadToken = NO_UNREAD_TOKEN; /** Restores the lookahead token to the token stream */ private void restoreLookAhead(JsDocToken token) { unreadToken = token; } /** * Tests whether the next symbol of the token stream matches the specific * token. */ private boolean match(JsDocToken token) { unreadToken = next(); return unreadToken == token; } /** * Tests that the next symbol of the token stream matches one of the specified * tokens. */ private boolean match(JsDocToken token1, JsDocToken token2) { unreadToken = next(); return unreadToken == token1 || unreadToken == token2; } /** * Gets the next token of the token stream or the buffered token if a matching * was previously made. */ private JsDocToken next() { if (unreadToken == NO_UNREAD_TOKEN) { return stream.getJsDocToken(); } else { return current(); } } /** * Gets the current token, invalidating it in the process. */ private JsDocToken current() { JsDocToken t = unreadToken; unreadToken = NO_UNREAD_TOKEN; return t; } /** * Skips all EOLs and all empty lines in the JSDoc. Call this method if you * want the JSDoc entry to span multiple lines. */ private void skipEOLs() { while (match(JsDocToken.EOL)) { next(); if (match(JsDocToken.STAR)) { next(); } } } /** * Returns the remainder of the line. */ private String getRemainingJSDocLine() { String result = stream.getRemainingJSDocLine(); unreadToken = NO_UNREAD_TOKEN; return result; } /** * Determines whether the parser has been populated with docinfo with a * fileoverview tag. */ private boolean hasParsedFileOverviewDocInfo() { return jsdocBuilder.isPopulatedWithFileOverview(); } boolean hasParsedJSDocInfo() { return jsdocBuilder.isPopulated(); } JSDocInfo retrieveAndResetParsedJSDocInfo() { return jsdocBuilder.build(associatedNode); } /** * Gets the fileoverview JSDocInfo, if any. */ JSDocInfo getFileOverviewJSDocInfo() { return fileOverviewJSDocInfo; } /** * Look ahead for a type annotation by advancing the character stream. * Does not modify the token stream. * This is kind of a hack, and is only necessary because we use the token * stream to parse types, but need the underlying character stream to get * JsDoc descriptions. * @return Whether we found a type annotation. */ private boolean lookAheadForTypeAnnotation() { boolean matchedLc = false; int c; while (true) { c = stream.getChar(); if (c == ' ') { continue; } else if (c == '{') { matchedLc = true; break; } else { break; } } stream.ungetChar(c); return matchedLc; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/parsing/TypeSafeDispatcher.java0000644000175000017500000003031212115204405030670 0ustar apoapo/* * 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.parsing; import com.google.javascript.rhino.head.Token; import com.google.javascript.rhino.head.ast.ArrayLiteral; import com.google.javascript.rhino.head.ast.Assignment; import com.google.javascript.rhino.head.ast.AstNode; import com.google.javascript.rhino.head.ast.AstRoot; import com.google.javascript.rhino.head.ast.Block; import com.google.javascript.rhino.head.ast.BreakStatement; import com.google.javascript.rhino.head.ast.CatchClause; import com.google.javascript.rhino.head.ast.ConditionalExpression; import com.google.javascript.rhino.head.ast.ContinueStatement; import com.google.javascript.rhino.head.ast.DoLoop; import com.google.javascript.rhino.head.ast.ElementGet; import com.google.javascript.rhino.head.ast.EmptyExpression; import com.google.javascript.rhino.head.ast.EmptyStatement; import com.google.javascript.rhino.head.ast.ExpressionStatement; import com.google.javascript.rhino.head.ast.ForInLoop; import com.google.javascript.rhino.head.ast.ForLoop; import com.google.javascript.rhino.head.ast.FunctionCall; import com.google.javascript.rhino.head.ast.FunctionNode; import com.google.javascript.rhino.head.ast.IfStatement; import com.google.javascript.rhino.head.ast.InfixExpression; import com.google.javascript.rhino.head.ast.KeywordLiteral; import com.google.javascript.rhino.head.ast.Label; import com.google.javascript.rhino.head.ast.LabeledStatement; import com.google.javascript.rhino.head.ast.Name; import com.google.javascript.rhino.head.ast.NewExpression; import com.google.javascript.rhino.head.ast.NumberLiteral; import com.google.javascript.rhino.head.ast.ObjectLiteral; import com.google.javascript.rhino.head.ast.ObjectProperty; import com.google.javascript.rhino.head.ast.ParenthesizedExpression; import com.google.javascript.rhino.head.ast.PropertyGet; import com.google.javascript.rhino.head.ast.RegExpLiteral; import com.google.javascript.rhino.head.ast.ReturnStatement; import com.google.javascript.rhino.head.ast.Scope; import com.google.javascript.rhino.head.ast.StringLiteral; import com.google.javascript.rhino.head.ast.SwitchCase; import com.google.javascript.rhino.head.ast.SwitchStatement; import com.google.javascript.rhino.head.ast.ThrowStatement; import com.google.javascript.rhino.head.ast.TryStatement; import com.google.javascript.rhino.head.ast.UnaryExpression; import com.google.javascript.rhino.head.ast.VariableDeclaration; import com.google.javascript.rhino.head.ast.VariableInitializer; import com.google.javascript.rhino.head.ast.WhileLoop; import com.google.javascript.rhino.head.ast.WithStatement; /** * Type safe dispatcher interface for use with new-style Rhino ASTs. * * The contents of this file was generated using a script; it is * likely that the implementation below really belongs in a virtual * typeSafeProcess(TypeSafeDispatcher) method implemented by all AST * classes - which would make switching based on types and casting * unnecessary. * */ abstract class TypeSafeDispatcher { abstract T processArrayLiteral(ArrayLiteral literalNode); abstract T processAssignment(Assignment assignmentNode); abstract T processAstRoot(AstRoot rootNode); abstract T processBlock(Block blockNode); abstract T processBreakStatement(BreakStatement statementNode); abstract T processCatchClause(CatchClause clauseNode); abstract T processConditionalExpression(ConditionalExpression exprNode); abstract T processContinueStatement(ContinueStatement statementNode); abstract T processDoLoop(DoLoop loopNode); abstract T processElementGet(ElementGet getNode); abstract T processEmptyExpression(EmptyExpression exprNode); abstract T processEmptyStatement(EmptyStatement exprNode); abstract T processExpressionStatement(ExpressionStatement statementNode); abstract T processForInLoop(ForInLoop loopNode); abstract T processForLoop(ForLoop loopNode); abstract T processFunctionCall(FunctionCall callNode); abstract T processFunctionNode(FunctionNode functionNode); abstract T processIfStatement(IfStatement statementNode); abstract T processInfixExpression(InfixExpression exprNode); abstract T processKeywordLiteral(KeywordLiteral literalNode); abstract T processLabel(Label labelNode); abstract T processLabeledStatement(LabeledStatement statementNode); abstract T processName(Name nameNode); abstract T processNewExpression(NewExpression exprNode); abstract T processNumberLiteral(NumberLiteral literalNode); abstract T processObjectLiteral(ObjectLiteral literalNode); abstract T processObjectProperty(ObjectProperty propertyNode); abstract T processParenthesizedExpression(ParenthesizedExpression exprNode); abstract T processPropertyGet(PropertyGet getNode); abstract T processRegExpLiteral(RegExpLiteral literalNode); abstract T processReturnStatement(ReturnStatement statementNode); abstract T processScope(Scope scopeNode); abstract T processStringLiteral(StringLiteral literalNode); abstract T processSwitchCase(SwitchCase caseNode); abstract T processSwitchStatement(SwitchStatement statementNode); abstract T processThrowStatement(ThrowStatement statementNode); abstract T processTryStatement(TryStatement statementNode); abstract T processUnaryExpression(UnaryExpression exprNode); abstract T processVariableDeclaration(VariableDeclaration declarationNode); abstract T processVariableInitializer(VariableInitializer initializerNode); abstract T processWhileLoop(WhileLoop loopNode); abstract T processWithStatement(WithStatement statementNode); abstract T processIllegalToken(AstNode node); public T process(AstNode node) { switch (node.getType()) { case Token.ADD: case Token.AND: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.OR: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return processInfixExpression((InfixExpression) node); case Token.ARRAYLIT: return processArrayLiteral((ArrayLiteral) node); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_DIV: case Token.ASSIGN_LSH: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_RSH: case Token.ASSIGN_SUB: case Token.ASSIGN_URSH: return processAssignment((Assignment) node); case Token.BITNOT: case Token.DEC: case Token.DELPROP: case Token.INC: case Token.NEG: case Token.NOT: case Token.POS: case Token.TYPEOF: case Token.VOID: return processUnaryExpression((UnaryExpression) node); case Token.BLOCK: if (node instanceof Block) { return processBlock((Block) node); } else if (node instanceof Scope) { return processScope((Scope) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.BREAK: return processBreakStatement((BreakStatement) node); case Token.CALL: return processFunctionCall((FunctionCall) node); case Token.CASE: case Token.DEFAULT: return processSwitchCase((SwitchCase) node); case Token.CATCH: return processCatchClause((CatchClause) node); case Token.COLON: return processObjectProperty((ObjectProperty) node); case Token.CONTINUE: return processContinueStatement((ContinueStatement) node); case Token.DO: return processDoLoop((DoLoop) node); case Token.EMPTY: return (node instanceof EmptyExpression) ? processEmptyExpression((EmptyExpression) node) : processEmptyStatement((EmptyStatement) node); case Token.EXPR_RESULT: case Token.EXPR_VOID: if (node instanceof ExpressionStatement) { return processExpressionStatement((ExpressionStatement) node); } else if (node instanceof LabeledStatement) { return processLabeledStatement((LabeledStatement) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.DEBUGGER: case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: return processKeywordLiteral((KeywordLiteral) node); case Token.FOR: if (node instanceof ForInLoop) { return processForInLoop((ForInLoop) node); } else if (node instanceof ForLoop) { return processForLoop((ForLoop) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.FUNCTION: return processFunctionNode((FunctionNode) node); case Token.GETELEM: return processElementGet((ElementGet) node); case Token.GETPROP: return processPropertyGet((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.CONST: case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariableInitializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/parsing/JsDocToken.java0000644000175000017500000000220012115204405027137 0ustar apoapo/* * 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.parsing; /** * JSDoc-specific tokens. * * This class is based on Rhino's Token. * */ enum JsDocToken { // Tokens recycled from Rhino EOF, // end of file token - (not EOF_CHAR) EOL, // end of line LT, GT, STRING, LB, // left and right brackets RB, LC, // left and right curlies (braces) RC, LP, // left and right parentheses RP, COMMA, // comma operator COLON, // JsDoc-only tokens ANNOTATION, PIPE, STAR, EOC, QMARK, ELLIPSIS, BANG, EQUALS; } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CssRenamingMap.java0000644000175000017500000000243112115204405026346 0ustar apoapo/* * 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; /** * Interface used by {@link ReplaceCssNames} to substitute CSS class names. */ public interface CssRenamingMap { public static enum Style { BY_WHOLE, BY_PART, } String get(String value); Style getStyle(); public static abstract class ByPart implements CssRenamingMap { @Override abstract public String get(String value); @Override public Style getStyle() { return Style.BY_PART; } } public static abstract class ByWhole implements CssRenamingMap { @Override abstract public String get(String value); @Override public Style getStyle() { return Style.BY_WHOLE; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VariableMap.java0000644000175000017500000001361212115204405025665 0ustar apoapo/* * Copyright 2005 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.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import com.google.common.io.Files; import java.io.*; import java.text.*; import java.util.*; /** * Stores the mapping from original variable name to new variable names. * @see RenameVars */ public class VariableMap { /** Maps original source name to new name */ private final ImmutableMap map; /** Maps new name to source name, lazily initialized */ private ImmutableMap reverseMap = null; private static final char SEPARATOR = ':'; VariableMap(Map map) { this.map = ImmutableMap.copyOf(map); } /** * Given an original variable name, look up new name, may return null * if it's not found. */ public String lookupNewName(String sourceName) { return map.get(sourceName); } /** * Given a new variable name, lookup the source name, may return null * if it's not found. */ public String lookupSourceName(String newName) { initReverseMap(); return reverseMap.get(newName); } /** * Initializes the reverse map. */ private synchronized void initReverseMap() { if (reverseMap == null) { ImmutableMap.Builder rm = ImmutableMap.builder(); for (Map.Entry entry : map.entrySet()) { rm.put(entry.getValue(), entry.getKey()); } reverseMap = rm.build(); } } /** * Returns an unmodifiable mapping from original names to new names. */ public Map getOriginalNameToNewNameMap() { return map; } /** * Returns an unmodifiable mapping from new names to original names. */ public Map getNewNameToOriginalNameMap() { initReverseMap(); return reverseMap; } /** * Saves the variable map to a file. */ public void save(String filename) throws IOException { Files.write(toBytes(), new File(filename)); } /** * Reads the variable map from a file written via {@link #save(String)}. */ public static VariableMap load(String filename) throws IOException { try { return fromBytes(Files.toByteArray(new File(filename))); } catch (ParseException e) { // Wrap parse exception for backwards compatibility. throw new IOException(e); } } /** * Serializes the variable map to a byte array. */ public byte[] toBytes() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(baos, Charsets.UTF_8); try { for (Map.Entry entry : map.entrySet()) { writer.write(escape(entry.getKey())); writer.write(SEPARATOR); writer.write(escape(entry.getValue())); writer.write('\n'); } writer.close(); } catch (IOException e) { // Note: A ByteArrayOutputStream never throws IOException. This try/catch // is just here to appease the Java compiler. throw new RuntimeException(e); } return baos.toByteArray(); } /** * Deserializes the variable map from a byte array returned by * {@link #toBytes()}. */ public static VariableMap fromBytes(byte[] bytes) throws ParseException { Iterable lines; try { lines = CharStreams.readLines(CharStreams.newReaderSupplier( ByteStreams.newInputStreamSupplier(bytes), Charsets.UTF_8)); } catch (IOException e) { // Note: An IOException is never thrown while reading from a byte array. // This try/catch is just here to appease the Java compiler. throw new RuntimeException(e); } ImmutableMap.Builder map = ImmutableMap.builder(); for (String line : lines) { int pos = findIndexOfChar(line, SEPARATOR); if (pos <= 0 || pos == line.length() - 1) { throw new ParseException("Bad line: " + line, 0); } map.put( unescape(line.substring(0, pos)), unescape(line.substring(pos + 1))); } return new VariableMap(map.build()); } private static String escape(String value) { return value.replace("\\", "\\\\") .replace(":", "\\:") .replace("\n", "\\n"); } private static int findIndexOfChar(String value, char stopChar) { int len = value.length(); for (int i=0; i map) { return new VariableMap(map); } @VisibleForTesting Map toMap() { return map; } }closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Compiler.java0000644000175000017500000022723212115204405025261 0ustar apoapo/* * Copyright 2004 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.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.io.CharStreams; import com.google.javascript.jscomp.CompilerOptions.DevMode; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.deps.SortedDependencies; import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException; import com.google.javascript.jscomp.parsing.Config; import com.google.javascript.jscomp.parsing.ParserRunner; import com.google.javascript.jscomp.type.ChainableReverseAbstractInterpreter; import com.google.javascript.jscomp.type.ClosureReverseAbstractInterpreter; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.jscomp.type.SemanticReverseAbstractInterpreter; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.head.ErrorReporter; import com.google.javascript.rhino.head.ast.AstRoot; import com.google.javascript.rhino.jstype.JSTypeRegistry; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; /** * Compiler (and the other classes in this package) does the following: *
    *
  • parses JS code *
  • checks for undefined variables *
  • performs optimizations such as constant folding and constants inlining *
  • renames variables (to short names) *
  • outputs compact JavaScript code *
* * External variables are declared in 'externs' files. For instance, the file * may include definitions for global javascript/browser objects such as * window, document. * */ public class Compiler extends AbstractCompiler { static final String SINGLETON_MODULE_NAME = "[singleton]"; static final DiagnosticType MODULE_DEPENDENCY_ERROR = DiagnosticType.error("JSC_MODULE_DEPENDENCY_ERROR", "Bad dependency: {0} -> {1}. " + "Modules must be listed in dependency order."); static final DiagnosticType MISSING_ENTRY_ERROR = DiagnosticType.error( "JSC_MISSING_ENTRY_ERROR", "required entry point \"{0}\" never provided"); static final DiagnosticType MISSING_MODULE_ERROR = DiagnosticType.error( "JSC_MISSING_ENTRY_ERROR", "unknown module \"{0}\" specified in entry point spec"); // Used in PerformanceTracker static final String PARSING_PASS_NAME = "parseInputs"; private static final String CONFIG_RESOURCE = "com.google.javascript.jscomp.parsing.ParserConfig"; CompilerOptions options = null; private PassConfig passes = null; // The externs inputs private List externs; // The JS source modules private List modules; // The graph of the JS source modules. Must be null if there are less than // 2 modules, because we use this as a signal for which passes to run. private JSModuleGraph moduleGraph; // The JS source inputs private List inputs; // error manager to which error management is delegated private ErrorManager errorManager; // Warnings guard for filtering warnings. private WarningsGuard warningsGuard; // Compile-time injected libraries. The node points to the last node of // the library, so code can be inserted after. private final Map injectedLibraries = Maps.newLinkedHashMap(); // Parse tree root nodes Node externsRoot; Node jsRoot; Node externAndJsRoot; private Map inputsById; /** The source code map */ private SourceMap sourceMap; /** The externs created from the exports. */ private String externExports = null; /** * Ids for function inlining so that each declared name remains * unique. */ private int uniqueNameId = 0; /** * Whether to assume there are references to the RegExp Global object * properties. */ private boolean hasRegExpGlobalReferences = true; /** The function information map */ private FunctionInformationMap functionInformationMap; /** Debugging information */ private final StringBuilder debugLog = new StringBuilder(); /** Detects Google-specific coding conventions. */ CodingConvention defaultCodingConvention = new ClosureCodingConvention(); private JSTypeRegistry typeRegistry; private Config parserConfig = null; private ReverseAbstractInterpreter abstractInterpreter; private TypeValidator typeValidator; public PerformanceTracker tracker; // The oldErrorReporter exists so we can get errors from the JSTypeRegistry. private final com.google.javascript.rhino.ErrorReporter oldErrorReporter = RhinoErrorReporter.forOldRhino(this); // This error reporter gets the messages from the current Rhino parser. private final ErrorReporter defaultErrorReporter = RhinoErrorReporter.forNewRhino(this); /** Error strings used for reporting JSErrors */ public static final DiagnosticType OPTIMIZE_LOOP_ERROR = DiagnosticType.error( "JSC_OPTIMIZE_LOOP_ERROR", "Exceeded max number of optimization iterations: {0}"); public static final DiagnosticType MOTION_ITERATIONS_ERROR = DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR", "Exceeded max number of code motion iterations: {0}"); // We use many recursive algorithms that use O(d) memory in the depth // of the tree. private static final long COMPILER_STACK_SIZE = (1 << 21); // About 2MB /** * Under JRE 1.6, the JS Compiler overflows the stack when running on some * large or complex JS code. When threads are available, we run all compile * jobs on a separate thread with a larger stack. * * That way, we don't have to increase the stack size for *every* thread * (which is what -Xss does). */ private static final ExecutorService compilerExecutor = Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(null, r, "jscompiler", COMPILER_STACK_SIZE); } }); /** * Use a dedicated compiler thread per Compiler instance. */ private Thread compilerThread = null; /** Whether to use threads. */ private boolean useThreads = true; /** * Logger for the whole com.google.javascript.jscomp domain - * setting configuration for this logger affects all loggers * in other classes within the compiler. */ private static final Logger logger = Logger.getLogger("com.google.javascript.jscomp"); private final PrintStream outStream; private GlobalVarReferenceMap globalRefMap = null; private volatile double progress = 0.0; private String lastPassName; /** * Creates a Compiler that reports errors and warnings to its logger. */ public Compiler() { this((PrintStream) null); } /** * Creates a Compiler that reports errors and warnings to an output stream. */ public Compiler(PrintStream stream) { addChangeHandler(recentChange); outStream = stream; } /** * Creates a Compiler that uses a custom error manager. */ public Compiler(ErrorManager errorManager) { this(); setErrorManager(errorManager); } /** * Sets the error manager. * * @param errorManager the error manager, it cannot be {@code null} */ public void setErrorManager(ErrorManager errorManager) { Preconditions.checkNotNull( errorManager, "the error manager cannot be null"); this.errorManager = errorManager; } /** * Creates a message formatter instance corresponding to the value of * {@link CompilerOptions}. */ private MessageFormatter createMessageFormatter() { boolean colorize = options.shouldColorizeErrorOutput(); return options.errorFormat.toFormatter(this, colorize); } /** * Initialize the compiler options. Only necessary if you're not doing * a normal compile() job. */ public void initOptions(CompilerOptions options) { this.options = options; if (errorManager == null) { if (outStream == null) { setErrorManager( new LoggerErrorManager(createMessageFormatter(), logger)); } else { PrintStreamErrorManager printer = new PrintStreamErrorManager(createMessageFormatter(), outStream); printer.setSummaryDetailLevel(options.summaryDetailLevel); setErrorManager(printer); } } reconcileOptionsWithGuards(); // Initialize the warnings guard. List guards = Lists.newArrayList(); guards.add( new SuppressDocWarningsGuard( getDiagnosticGroups().getRegisteredGroups())); guards.add(options.getWarningsGuard()); this.warningsGuard = new ComposeWarningsGuard(guards); } /** * When the CompilerOptions and its WarningsGuard overlap, reconcile * any discrepencies. */ protected void reconcileOptionsWithGuards() { // DiagnosticGroups override the plain checkTypes option. if (options.enables(DiagnosticGroups.CHECK_TYPES)) { options.checkTypes = true; } else if (options.disables(DiagnosticGroups.CHECK_TYPES)) { options.checkTypes = false; } else if (!options.checkTypes) { // If DiagnosticGroups did not override the plain checkTypes // option, and checkTypes is enabled, then turn off the // parser type warnings. options.setWarningLevel( DiagnosticGroup.forType( RhinoErrorReporter.TYPE_PARSE_ERROR), CheckLevel.OFF); } if (options.checkGlobalThisLevel.isOn() && !options.disables(DiagnosticGroups.GLOBAL_THIS)) { options.setWarningLevel( DiagnosticGroups.GLOBAL_THIS, options.checkGlobalThisLevel); } if (options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT) { options.setWarningLevel( DiagnosticGroups.ES5_STRICT, CheckLevel.ERROR); } // All passes must run the variable check. This synthesizes // variables later so that the compiler doesn't crash. It also // checks the externs file for validity. If you don't want to warn // about missing variable declarations, we shut that specific // error off. if (!options.checkSymbols && !options.enables(DiagnosticGroups.CHECK_VARIABLES)) { options.setWarningLevel( DiagnosticGroups.CHECK_VARIABLES, CheckLevel.OFF); } } /** * Initializes the instance state needed for a compile job. * @deprecated Convert your arrays to lists and use the list-based API. */ @Deprecated public void init(JSSourceFile[] externs, JSSourceFile[] inputs, CompilerOptions options) { init(Lists.newArrayList(externs), Lists.newArrayList(inputs), options); } /** * Initializes the instance state needed for a compile job. */ public void init( List externs, List inputs, CompilerOptions options) { JSModule module = new JSModule(SINGLETON_MODULE_NAME); for (SourceFile input : inputs) { module.add(input); } initModules(externs, Lists.newArrayList(module), options); } /** * Initializes the instance state needed for a compile job if the sources * are in modules. * @deprecated Convert your arrays to lists and use the list-based API. */ @Deprecated public void init(JSSourceFile[] externs, JSModule[] modules, CompilerOptions options) { initModules(Lists.newArrayList(externs), Lists.newArrayList(modules), options); } /** * Initializes the instance state needed for a compile job if the sources * are in modules. */ public void initModules( List externs, List modules, CompilerOptions options) { initOptions(options); checkFirstModule(modules); fillEmptyModules(modules); this.externs = makeCompilerInput(externs, true); // Generate the module graph, and report any errors in the module // specification as errors. this.modules = modules; if (modules.size() > 1) { try { this.moduleGraph = new JSModuleGraph(modules); } catch (JSModuleGraph.ModuleDependenceException e) { // problems with the module format. Report as an error. The // message gives all details. report(JSError.make(MODULE_DEPENDENCY_ERROR, e.getModule().getName(), e.getDependentModule().getName())); return; } } else { this.moduleGraph = null; } this.inputs = getAllInputsFromModules(modules); initBasedOnOptions(); initInputsByIdMap(); } /** * Do any initialization that is dependent on the compiler options. */ private void initBasedOnOptions() { // Create the source map if necessary. if (options.sourceMapOutputPath != null) { sourceMap = options.sourceMapFormat.getInstance(); sourceMap.setPrefixMappings(options.sourceMapLocationMappings); } } private List makeCompilerInput( List files, boolean isExtern) { List inputs = Lists.newArrayList(); for (T file : files) { inputs.add(new CompilerInput(file, isExtern)); } return inputs; } private static final DiagnosticType EMPTY_MODULE_LIST_ERROR = DiagnosticType.error("JSC_EMPTY_MODULE_LIST_ERROR", "At least one module must be provided"); private static final DiagnosticType EMPTY_ROOT_MODULE_ERROR = DiagnosticType.error("JSC_EMPTY_ROOT_MODULE_ERROR", "Root module '{0}' must contain at least one source code input"); /** * Verifies that at least one module has been provided and that the first one * has at least one source code input. */ private void checkFirstModule(List modules) { if (modules.isEmpty()) { report(JSError.make(EMPTY_MODULE_LIST_ERROR)); } else if (modules.get(0).getInputs().isEmpty() && modules.size() > 1) { // The root module may only be empty if there is exactly 1 module. report(JSError.make(EMPTY_ROOT_MODULE_ERROR, modules.get(0).getName())); } } /** * Empty modules get an empty "fill" file, so that we can move code into * an empty module. */ static String createFillFileName(String moduleName) { return "[" + moduleName + "]"; } /** * Fill any empty modules with a place holder file. It makes any cross module * motion easier. */ private static void fillEmptyModules(List modules) { for (JSModule module : modules) { if (module.getInputs().isEmpty()) { module.add(SourceFile.fromCode( createFillFileName(module.getName()), "")); } } } /** * Rebuilds the internal list of inputs by iterating over all modules. * This is necessary if inputs have been added to or removed from a module * after the {@link #init(List, List, CompilerOptions)} call. */ public void rebuildInputsFromModules() { inputs = getAllInputsFromModules(modules); initInputsByIdMap(); } /** * Builds a single list of all module inputs. Verifies that it contains no * duplicates. */ private static List getAllInputsFromModules( List modules) { List inputs = Lists.newArrayList(); Map inputMap = Maps.newHashMap(); for (JSModule module : modules) { for (CompilerInput input : module.getInputs()) { String inputName = input.getName(); // NOTE(nicksantos): If an input is in more than one module, // it will show up twice in the inputs list, and then we // will get an error down the line. inputs.add(input); inputMap.put(inputName, module); } } return inputs; } static final DiagnosticType DUPLICATE_INPUT = DiagnosticType.error("JSC_DUPLICATE_INPUT", "Duplicate input: {0}"); static final DiagnosticType DUPLICATE_EXTERN_INPUT = DiagnosticType.error("JSC_DUPLICATE_EXTERN_INPUT", "Duplicate extern input: {0}"); /** * Creates a map to make looking up an input by name fast. Also checks for * duplicate inputs. */ void initInputsByIdMap() { inputsById = new HashMap(); for (CompilerInput input : externs) { InputId id = input.getInputId(); CompilerInput previous = putCompilerInput(id, input); if (previous != null) { report(JSError.make(DUPLICATE_EXTERN_INPUT, input.getName())); } } for (CompilerInput input : inputs) { InputId id = input.getInputId(); CompilerInput previous = putCompilerInput(id, input); if (previous != null) { report(JSError.make(DUPLICATE_INPUT, input.getName())); } } } public Result compile( SourceFile extern, SourceFile input, CompilerOptions options) { return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); } /** * @deprecated Convert your arrays to lists and use the list-based API. */ @Deprecated public Result compile( SourceFile extern, JSSourceFile[] input, CompilerOptions options) { return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options); } /** * @deprecated Convert your arrays to lists and use the list-based * compileModules method. */ @Deprecated public Result compile( JSSourceFile extern, JSModule[] modules, CompilerOptions options) { return compileModules( Lists.newArrayList(extern), Lists.newArrayList(modules), options); } /** * Compiles a list of inputs. * @deprecated Convert your arrays to lists and use the list-based compile * method. */ @Deprecated public Result compile(JSSourceFile[] externs, JSSourceFile[] inputs, CompilerOptions options) { return compile(Lists.newArrayList(externs), Lists.newArrayList(inputs), options); } /** * Compiles a list of inputs. */ public Result compile( List externs, List inputs, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { init(externs, inputs, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } /** * Compiles a list of modules. * @deprecated Convert your arrays to lists and use the list-based * compileModules method. */ @Deprecated public Result compile(JSSourceFile[] externs, JSModule[] modules, CompilerOptions options) { return compileModules(Lists.newArrayList(externs), Lists.newArrayList(modules), options); } /** * Compiles a list of modules. */ public Result compileModules(List externs, List modules, CompilerOptions options) { // The compile method should only be called once. Preconditions.checkState(jsRoot == null); try { initModules(externs, modules, options); if (hasErrors()) { return getResult(); } return compile(); } finally { Tracer t = newTracer("generateReport"); errorManager.generateReport(); stopTracer(t, "generateReport"); } } private Result compile() { return runInCompilerThread(new Callable() { @Override public Result call() throws Exception { compileInternal(); return getResult(); } }); } /** * Disable threads. This is for clients that run on AppEngine and * don't have threads. */ public void disableThreads() { useThreads = false; } @SuppressWarnings("unchecked") T runInCompilerThread(final Callable callable) { final boolean dumpTraceReport = options != null && options.tracer.isOn(); T result = null; final Throwable[] exception = new Throwable[1]; Callable bootCompilerThread = new Callable() { @Override public T call() { try { compilerThread = Thread.currentThread(); if (dumpTraceReport) { Tracer.initCurrentThreadTrace(); } return callable.call(); } catch (Throwable e) { exception[0] = e; } finally { compilerThread = null; if (dumpTraceReport) { Tracer.logAndClearCurrentThreadTrace(); tracker.outputTracerReport(outStream == null ? System.out : outStream); } } return null; } }; Preconditions.checkState( compilerThread == null || compilerThread == Thread.currentThread(), "Please do not share the Compiler across threads"); // If the compiler thread is available, use it. if (useThreads && compilerThread == null) { try { result = compilerExecutor.submit(bootCompilerThread).get(); } catch (InterruptedException e) { throw Throwables.propagate(e); } catch (ExecutionException e) { throw Throwables.propagate(e); } } else { try { result = callable.call(); } catch (Exception e) { exception[0] = e; } } // Pass on any exception caught by the runnable object. if (exception[0] != null) { throw new RuntimeException(exception[0]); } return result; } private void compileInternal() { setProgress(0.0, null); parse(); // 15 percent of the work is assumed to be for parsing (based on some // minimal analysis on big JS projects, of course this depends on options) setProgress(0.15, "parse"); if (hasErrors()) { return; } if (!precheck()) { return; } if (options.nameAnonymousFunctionsOnly) { // TODO(nicksantos): Move this into an instrument() phase maybe? check(); return; } if (!options.skipAllPasses) { check(); if (hasErrors()) { return; } if (options.isExternExportsEnabled() || options.externExportsPath != null) { externExports(); } // IDE-mode is defined to stop here, before the heavy rewriting begins. if (!options.ideMode) { optimize(); } } if (options.recordFunctionInformation) { recordFunctionInformation(); } if (options.devMode == DevMode.START_AND_END) { runSanityCheck(); } setProgress(1.0, "recordFunctionInformation"); } public void parse() { parseInputs(); } PassConfig getPassConfig() { if (passes == null) { passes = createPassConfigInternal(); } return passes; } /** * Create the passes object. Clients should use setPassConfig instead of * overriding this. */ PassConfig createPassConfigInternal() { return new DefaultPassConfig(options); } /** * @param passes The PassConfig to use with this Compiler. * @throws NullPointerException if passes is null * @throws IllegalStateException if this.passes has already been assigned */ public void setPassConfig(PassConfig passes) { // Important to check for null because if setPassConfig(null) is // called before this.passes is set, getPassConfig() will create a // new PassConfig object and use that, which is probably not what // the client wanted since he or she probably meant to use their // own PassConfig object. Preconditions.checkNotNull(passes); if (this.passes != null) { throw new IllegalStateException("this.passes has already been assigned"); } this.passes = passes; } /** * Carry out any special checks or procedures that need to be done before * proceeding with rest of the compilation process. * * @return true, to continue with compilation */ boolean precheck() { return true; } public void check() { runCustomPasses(CustomPassExecutionTime.BEFORE_CHECKS); // We are currently only interested in check-passes for progress reporting // as it is used for IDEs, that's why the maximum progress is set to 1.0. PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, new PhaseOptimizer.ProgressRange(getProgress(), 1.0)); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } if (options.getCheckDeterminism()) { phaseOptimizer.setPrintAstHashcodes(true); } phaseOptimizer.consume(getPassConfig().getChecks()); phaseOptimizer.process(externsRoot, jsRoot); if (hasErrors()) { return; } // TODO(nicksantos): clean this up. The flow here is too hard to follow. if (options.nameAnonymousFunctionsOnly) { return; } if (options.removeTryCatchFinally) { removeTryCatchFinally(); } if (options.getTweakProcessing().shouldStrip() || !options.stripTypes.isEmpty() || !options.stripNameSuffixes.isEmpty() || !options.stripTypePrefixes.isEmpty() || !options.stripNamePrefixes.isEmpty()) { stripCode(options.stripTypes, options.stripNameSuffixes, options.stripTypePrefixes, options.stripNamePrefixes); } runCustomPasses(CustomPassExecutionTime.BEFORE_OPTIMIZATIONS); } private void externExports() { logger.fine("Creating extern file for exports"); startPass("externExports"); ExternExportsPass pass = new ExternExportsPass(this); process(pass); externExports = pass.getGeneratedExterns(); endPass(); } @Override void process(CompilerPass p) { p.process(externsRoot, jsRoot); } private final PassFactory sanityCheck = new PassFactory("sanityCheck", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new SanityCheck(compiler); } }; private void maybeSanityCheck() { if (options.devMode == DevMode.EVERY_PASS) { runSanityCheck(); } } private void runSanityCheck() { sanityCheck.create(this).process(externsRoot, jsRoot); } /** * Removes try/catch/finally statements for easier debugging. */ void removeTryCatchFinally() { logger.fine("Remove try/catch/finally"); startPass("removeTryCatchFinally"); RemoveTryCatch r = new RemoveTryCatch(this); process(r); endPass(); } /** * Strips code for smaller compiled code. This is useful for removing debug * statements to prevent leaking them publicly. */ void stripCode(Set stripTypes, Set stripNameSuffixes, Set stripTypePrefixes, Set stripNamePrefixes) { logger.fine("Strip code"); startPass("stripCode"); StripCode r = new StripCode(this, stripTypes, stripNameSuffixes, stripTypePrefixes, stripNamePrefixes); if (options.getTweakProcessing().shouldStrip()) { r.enableTweakStripping(); } process(r); endPass(); } /** * Runs custom passes that are designated to run at a particular time. */ private void runCustomPasses(CustomPassExecutionTime executionTime) { if (options.customPasses != null) { Tracer t = newTracer("runCustomPasses"); try { for (CompilerPass p : options.customPasses.get(executionTime)) { process(p); } } finally { stopTracer(t, "runCustomPasses"); } } } private Tracer currentTracer = null; private String currentPassName = null; /** * Marks the beginning of a pass. */ void startPass(String passName) { Preconditions.checkState(currentTracer == null); currentPassName = passName; currentTracer = newTracer(passName); } /** * Marks the end of a pass. */ void endPass() { Preconditions.checkState(currentTracer != null, "Tracer should not be null at the end of a pass."); stopTracer(currentTracer, currentPassName); currentPassName = null; currentTracer = null; maybeSanityCheck(); } /** * Returns a new tracer for the given pass name. */ Tracer newTracer(String passName) { String comment = passName + (recentChange.hasCodeChanged() ? " on recently changed AST" : ""); if (options.tracer.isOn()) { tracker.recordPassStart(passName, true); } return new Tracer("Compiler", comment); } void stopTracer(Tracer t, String passName) { long result = t.stop(); if (options.tracer.isOn()) { tracker.recordPassStop(passName, result); } } /** * Returns the result of the compilation. */ public Result getResult() { PassConfig.State state = getPassConfig().getIntermediateState(); return new Result(getErrors(), getWarnings(), debugLog.toString(), state.variableMap, state.propertyMap, state.anonymousFunctionNameMap, state.stringMap, functionInformationMap, sourceMap, externExports, state.cssNames, state.idGeneratorMap); } /** * Returns an array constructed from errors + temporary warnings. */ public JSError[] getMessages() { return getErrors(); } /** * Returns the array of errors (never null). */ public JSError[] getErrors() { return errorManager.getErrors(); } /** * Returns the array of warnings (never null). */ public JSError[] getWarnings() { return errorManager.getWarnings(); } @Override public Node getRoot() { return externAndJsRoot; } /** * Creates a new id for making unique names. */ private int nextUniqueNameId() { return uniqueNameId++; } /** * Resets the unique name id counter */ @VisibleForTesting void resetUniqueNameId() { uniqueNameId = 0; } @Override Supplier getUniqueNameIdSupplier() { final Compiler self = this; return new Supplier() { @Override public String get() { return String.valueOf(self.nextUniqueNameId()); } }; } @Override boolean areNodesEqualForInlining(Node n1, Node n2) { if (options.ambiguateProperties || options.disambiguateProperties) { // The type based optimizations require that type information is preserved // during other optimizations. return n1.isEquivalentToTyped(n2); } else { return n1.isEquivalentTo(n2); } } //------------------------------------------------------------------------ // Inputs //------------------------------------------------------------------------ // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler // interface, and which ones should always be injected. @Override public CompilerInput getInput(InputId id) { return inputsById.get(id); } /** * Removes an input file from AST. * @param id The id of the input to be removed. */ protected void removeExternInput(InputId id) { CompilerInput input = getInput(id); if (input == null) { return; } Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName()); inputsById.remove(id); externs.remove(input); Node root = input.getAstRoot(this); if (root != null) { root.detachFromParent(); } } @Override public CompilerInput newExternInput(String name) { SourceAst ast = new SyntheticAst(name); if (inputsById.containsKey(ast.getInputId())) { throw new IllegalArgumentException("Conflicting externs name: " + name); } CompilerInput input = new CompilerInput(ast, true); putCompilerInput(input.getInputId(), input); externsRoot.addChildToFront(ast.getAstRoot(this)); externs.add(0, input); return input; } private CompilerInput putCompilerInput(InputId id, CompilerInput input) { input.setCompiler(this); return inputsById.put(id, input); } /** Add a source input dynamically. Intended for incremental compilation. */ void addIncrementalSourceAst(JsAst ast) { InputId id = ast.getInputId(); Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName()); putCompilerInput(id, new CompilerInput(ast)); } /** * Replace a source input dynamically. Intended for incremental * re-compilation. * * If the new source input doesn't parse, then keep the old input * in the AST and return false. * * @return Whether the new AST was attached successfully. */ boolean replaceIncrementalSourceAst(JsAst ast) { CompilerInput oldInput = getInput(ast.getInputId()); Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName()); Node newRoot = ast.getAstRoot(this); if (newRoot == null) { return false; } Node oldRoot = oldInput.getAstRoot(this); if (oldRoot != null) { oldRoot.getParent().replaceChild(oldRoot, newRoot); } else { getRoot().getLastChild().addChildToBack(newRoot); } CompilerInput newInput = new CompilerInput(ast); putCompilerInput(ast.getInputId(), newInput); JSModule module = oldInput.getModule(); if (module != null) { module.addAfter(newInput, oldInput); module.remove(oldInput); } // Verify the input id is set properly. Preconditions.checkState( newInput.getInputId().equals(oldInput.getInputId())); InputId inputIdOnAst = newInput.getAstRoot(this).getInputId(); Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst)); inputs.remove(oldInput); return true; } /** * Add a new source input dynamically. Intended for incremental compilation. *

* If the new source input doesn't parse, it will not be added, and a false * will be returned. * * @param ast the JS Source to add. * @return true if the source was added successfully, false otherwise. * @throws IllegalStateException if an input for this ast already exists. */ boolean addNewSourceAst(JsAst ast) { CompilerInput oldInput = getInput(ast.getInputId()); if (oldInput != null) { throw new IllegalStateException( "Input already exists: " + ast.getInputId().getIdName()); } Node newRoot = ast.getAstRoot(this); if (newRoot == null) { return false; } getRoot().getLastChild().addChildToBack(newRoot); CompilerInput newInput = new CompilerInput(ast); // TODO(tylerg): handle this for multiple modules at some point. if (moduleGraph == null && !modules.isEmpty()) { // singleton module modules.get(0).add(newInput); } putCompilerInput(ast.getInputId(), newInput); return true; } @Override JSModuleGraph getModuleGraph() { return moduleGraph; } /** * Gets a module graph. This will always return a module graph, even * in the degenerate case when there's only one module. */ JSModuleGraph getDegenerateModuleGraph() { return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph; } @Override public JSTypeRegistry getTypeRegistry() { if (typeRegistry == null) { typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes); } return typeRegistry; } @Override public MemoizedScopeCreator getTypedScopeCreator() { return getPassConfig().getTypedScopeCreator(); } @SuppressWarnings("unchecked") DefaultPassConfig ensureDefaultPassConfig() { PassConfig passes = getPassConfig().getBasePassConfig(); Preconditions.checkState(passes instanceof DefaultPassConfig, "PassConfigs must eventually delegate to the DefaultPassConfig"); return (DefaultPassConfig) passes; } public SymbolTable buildKnownSymbolTable() { SymbolTable symbolTable = new SymbolTable(getTypeRegistry()); MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator(); if (typedScopeCreator != null) { symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes()); symbolTable.addSymbolsFrom(typedScopeCreator); } else { symbolTable.findScopes(this, externsRoot, jsRoot); } GlobalNamespace globalNamespace = ensureDefaultPassConfig().getGlobalNamespace(); if (globalNamespace != null) { symbolTable.addSymbolsFrom(globalNamespace); } ReferenceCollectingCallback refCollector = new ReferenceCollectingCallback( this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); NodeTraversal.traverse(this, getRoot(), refCollector); symbolTable.addSymbolsFrom(refCollector); PreprocessorSymbolTable preprocessorSymbolTable = ensureDefaultPassConfig().getPreprocessorSymbolTable(); if (preprocessorSymbolTable != null) { symbolTable.addSymbolsFrom(preprocessorSymbolTable); } symbolTable.fillNamespaceReferences(); symbolTable.fillPropertyScopes(); symbolTable.fillThisReferences(this, externsRoot, jsRoot); symbolTable.fillPropertySymbols(this, externsRoot, jsRoot); symbolTable.fillJSDocInfo(this, externsRoot, jsRoot); return symbolTable; } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main JS sources. jsRoot = IR.block(); jsRoot.setIsSyntheticBlock(true); externsRoot = IR.block(); externsRoot.setIsSyntheticBlock(true); externAndJsRoot = IR.block(externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer(PARSING_PASS_NAME); try { // Parse externs sources. for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Modules inferred in ProcessCommonJS pass. if (options.transformAMDToCJSModules || options.processCommonJSModules) { processAMDAndCommonJSModules(); } hoistExterns(externsRoot); // Check if the sources need to be re-ordered. boolean staleInputs = false; if (options.dependencyOptions.needsManagement()) { for (CompilerInput input : inputs) { // Forward-declare all the provided types, so that they // are not flagged even if they are dropped from the process. for (String provide : input.getProvides()) { getTypeRegistry().forwardDeclareType(provide); } } try { inputs = (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph) .manageDependencies(options.dependencyOptions, inputs); staleInputs = true; } catch (CircularDependencyException e) { report(JSError.make( JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } catch (MissingProvideException e) { report(JSError.make( MISSING_ENTRY_ERROR, e.getMessage())); } catch (JSModuleGraph.MissingModuleException e) { report(JSError.make( MISSING_MODULE_ERROR, e.getMessage())); } // If in IDE mode, we ignore the error and keep going. if (hasErrors()) { return null; } } hoistNoCompileFiles(); if (staleInputs) { repartitionInputs(); } // Build the AST. for (CompilerInput input : inputs) { Node n = input.getAstRoot(this); if (n == null) { continue; } if (devMode) { runSanityCheck(); if (hasErrors()) { return null; } } if (options.sourceMapOutputPath != null || options.nameReferenceReportPath != null) { // Annotate the nodes in the tree with information from the // input file. This information is used to construct the SourceMap. SourceInformationAnnotator sia = new SourceInformationAnnotator( input.getName(), options.devMode != DevMode.OFF); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } if (hasErrors()) { return null; } return externAndJsRoot; } finally { stopTracer(tracer, PARSING_PASS_NAME); } } /** * Hoists inputs with the @externs annotation into the externs list. */ private void hoistExterns(Node externsRoot) { boolean staleInputs = false; for (CompilerInput input : inputs) { if (options.dependencyOptions.needsManagement()) { // If we're doing scanning dependency info anyway, use that // information to skip sources that obviously aren't externs. if (!input.getProvides().isEmpty() || !input.getRequires().isEmpty()) { continue; } } Node n = input.getAstRoot(this); // Inputs can have a null AST on a parse error. if (n == null) { continue; } JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isExterns()) { // If the input file is explicitly marked as an externs file, then // assume the programmer made a mistake and throw it into // the externs pile anyways. externsRoot.addChildToBack(n); input.setIsExtern(true); input.getModule().remove(input); externs.add(input); staleInputs = true; } } if (staleInputs) { repartitionInputs(); } } /** * Hoists inputs with the @nocompiler annotation out of the inputs. */ private void hoistNoCompileFiles() { boolean staleInputs = false; for (CompilerInput input : inputs) { Node n = input.getAstRoot(this); // Inputs can have a null AST on a parse error. if (n == null) { continue; } JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoCompile()) { input.getModule().remove(input); staleInputs = true; } } if (staleInputs) { repartitionInputs(); } } private void repartitionInputs() { fillEmptyModules(modules); rebuildInputsFromModules(); } /** * Transforms AMD and CJS modules to something closure compiler can * process and creates JSModules and the corresponding dependency tree * on the way. */ void processAMDAndCommonJSModules() { Map modulesByName = Maps.newLinkedHashMap(); Map modulesByInput = Maps.newLinkedHashMap(); // TODO(nicksantos): Refactor module dependency resolution to work nicely // with multiple ways to express dependencies. Directly support JSModules // that are equivalent to a signal file and which express their deps // directly in the source. for (CompilerInput input : inputs) { input.setCompiler(this); Node root = input.getAstRoot(this); if (root == null) { continue; } if (options.transformAMDToCJSModules) { new TransformAMDToCJSModule(this).process(null, root); } if (options.processCommonJSModules) { ProcessCommonJSModules cjs = new ProcessCommonJSModules(this, options.commonJSModulePathPrefix); cjs.process(null, root); JSModule m = cjs.getModule(); if (m != null) { modulesByName.put(m.getName(), m); modulesByInput.put(input, m); } } } if (options.processCommonJSModules) { List modules = Lists.newArrayList(modulesByName.values()); if (!modules.isEmpty()) { this.modules = modules; this.moduleGraph = new JSModuleGraph(this.modules); } for (JSModule module : modules) { for (CompilerInput input : module.getInputs()) { for (String require : input.getRequires()) { JSModule dependency = modulesByName.get(require); if (dependency == null) { report(JSError.make(MISSING_ENTRY_ERROR, require)); } else { module.addDependency(dependency); } } } } try { modules = Lists.newArrayList(); for (CompilerInput input : this.moduleGraph.manageDependencies( options.dependencyOptions, inputs)) { modules.add(modulesByInput.get(input)); } JSModule root = new JSModule("root"); for (JSModule m : modules) { m.addDependency(root); } modules.add(0, root); SortedDependencies sorter = new SortedDependencies(modules); modules = sorter.getDependenciesOf(modules, true); this.modules = modules; this.moduleGraph = new JSModuleGraph(modules); } catch (Exception e) { Throwables.propagate(e); } } } public Node parse(SourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } private int syntheticCodeId = 0; @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( SourceFile.fromCode(" [synthetic:" + (++syntheticCodeId) + "] ", js)); putCompilerInput(input.getInputId(), input); return input.getAstRoot(this); } /** * Allow subclasses to override the default CompileOptions object. */ protected CompilerOptions newCompilerOptions() { return new CompilerOptions(); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(newCompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(SourceFile.fromCode(fileName, js)); } @Override Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( SourceFile.fromCode("[testcode]", js)); if (inputsById == null) { inputsById = Maps.newHashMap(); } putCompilerInput(input.getInputId(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to JS code. */ public String toSource() { return runInCompilerThread(new Callable() { @Override public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode = jsRoot.getFirstChild(); scriptNode != null; scriptNode = scriptNode.getNext()) { toSource(cb, i++, scriptNode); } } return cb.toString(); } finally { stopTracer(tracer, "toSource"); } } }); } /** * Converts the parse tree for each input back to JS code. */ public String[] toSourceArray() { return runInCompilerThread(new Callable() { @Override public String[] call() throws Exception { Tracer tracer = newTracer("toSourceArray"); try { int numInputs = inputs.size(); String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } finally { stopTracer(tracer, "toSourceArray"); } } }); } /** * Converts the parse tree for a module back to JS code. */ public String toSource(final JSModule module) { return runInCompilerThread(new Callable() { @Override public String call() throws Exception { List inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return ""; } CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module: " + module.getName()); } toSource(cb, i, scriptNode); } return cb.toString(); } }); } /** * Converts the parse tree for each input in a module back to JS code. */ public String[] toSourceArray(final JSModule module) { return runInCompilerThread(new Callable() { @Override public String[] call() throws Exception { List inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out JS code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable() { @Override public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.isScript()); String delimiter = options.inputDelimiter; String inputName = root.getInputId().getIdName(); String sourceName = root.getSourceFileName(); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter .replaceAll("%name%", Matcher.quoteReplacement(inputName)) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to it that the current // root node's mappings are offset by the given string builder buffer. if (options.sourceMapOutputPath != null) { sourceMap.setStartingPosition( cb.getLineIndex(), cb.getColumnIndex()); } // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict' // for the first input file String code = toSource(root, sourceMap, inputSeqNum == 0); if (!code.isEmpty()) { cb.append(code); // In order to avoid parse ambiguity when files are concatenated // together, all files should end in a semi-colon. Do a quick // heuristic check if there's an obvious semi-colon already there. int length = code.length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';'); if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null, true); } /** * Generates JavaScript source code for an AST. */ private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) { CodePrinter.Builder builder = new CodePrinter.Builder(n); builder.setCompilerOptions(options); builder.setSourceMap(sourceMap); builder.setTagAsStrict(firstOutput && options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); return builder.build(); } /** * Stores a buffer of text to which more can be appended. This is just like a * StringBuilder except that we also track the number of lines. */ public static class CodeBuilder { private final StringBuilder sb = new StringBuilder(); private int lineCount = 0; private int colCount = 0; /** Removes all text, but leaves the line count unchanged. */ void reset() { sb.setLength(0); } /** Appends the given string to the text buffer. */ CodeBuilder append(String str) { sb.append(str); // Adjust the line and column information for the new text. int index = -1; int lastIndex = index; while ((index = str.indexOf('\n', index + 1)) >= 0) { ++lineCount; lastIndex = index; } if (lastIndex == -1) { // No new lines, append the new characters added. colCount += str.length(); } else { colCount = str.length() - (lastIndex + 1); } return this; } /** Returns all text in the text buffer. */ @Override public String toString() { return sb.toString(); } /** Returns the length of the text buffer. */ public int getLength() { return sb.length(); } /** Returns the (zero-based) index of the last line in the text buffer. */ int getLineIndex() { return lineCount; } /** Returns the (zero-based) index of the last column in the text buffer. */ int getColumnIndex() { return colCount; } /** Determines whether the text ends with the given suffix. */ boolean endsWith(String suffix) { return (sb.length() > suffix.length()) && suffix.equals(sb.substring(sb.length() - suffix.length())); } } //------------------------------------------------------------------------ // Optimizations //------------------------------------------------------------------------ public void optimize() { // Ideally, this pass should be the first pass run, however: // 1) VariableReferenceCheck reports unexpected warnings if Normalize // is done first. // 2) ReplaceMessages, stripCode, and potentially custom passes rely on // unmodified local names. normalize(); PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker, null); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } if (options.getCheckDeterminism()) { phaseOptimizer.setPrintAstHashcodes(true); } phaseOptimizer.consume(getPassConfig().getOptimizations()); phaseOptimizer.process(externsRoot, jsRoot); } @Override void setCssRenamingMap(CssRenamingMap map) { options.cssRenamingMap = map; } @Override CssRenamingMap getCssRenamingMap() { return options.cssRenamingMap; } /** * Reprocesses the current defines over the AST. This is used by GwtCompiler * to generate N outputs for different targets from the same (checked) AST. * For each target, we apply the target-specific defines by calling * {@code processDefines} and then {@code optimize} to optimize the AST * specifically for that target. */ public void processDefines() { (new DefaultPassConfig(options)).processDefines.create(this) .process(externsRoot, jsRoot); } boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Control Flow Analysis. */ ControlFlowGraph computeCFG() { logger.fine("Computing Control Flow Graph"); Tracer tracer = newTracer("computeCFG"); ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); process(cfa); stopTracer(tracer, "computeCFG"); return cfa.getCfg(); } public void normalize() { logger.fine("Normalizing"); startPass("normalize"); process(new Normalize(this, false)); endPass(); } @Override void prepareAst(Node root) { CompilerPass pass = new PrepareAst(this); pass.process(null, root); } void recordFunctionInformation() { logger.fine("Recording function information"); startPass("recordFunctionInformation"); RecordFunctionInformation recordFunctionInfoPass = new RecordFunctionInformation( this, getPassConfig().getIntermediateState().functionNames); process(recordFunctionInfoPass); functionInformationMap = recordFunctionInfoPass.getMap(); endPass(); } protected final CodeChangeHandler recentChange = new CodeChangeHandler(); private final List codeChangeHandlers = Lists.newArrayList(); /** Name of the synthetic input that holds synthesized externs. */ static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}"; private CompilerInput synthesizedExternsInput = null; @Override void addChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.add(handler); } @Override void removeChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.remove(handler); } /** * All passes should call reportCodeChange() when they alter * the JS tree structure. This is verified by CompilerTestCase. * This allows us to optimize to a fixed point. */ @Override public void reportCodeChange() { for (CodeChangeHandler handler : codeChangeHandlers) { handler.reportChange(); } } @Override public CodingConvention getCodingConvention() { CodingConvention convention = options.getCodingConvention(); convention = convention != null ? convention : defaultCodingConvention; return convention; } @Override public boolean isIdeMode() { return options.ideMode; } @Override public boolean acceptEcmaScript5() { switch (options.getLanguageIn()) { case ECMASCRIPT5: case ECMASCRIPT5_STRICT: return true; case ECMASCRIPT3: return false; } throw new IllegalStateException("unexpected language mode"); } public LanguageMode languageMode() { return options.getLanguageIn(); } @Override public boolean acceptConstKeyword() { return options.acceptConstKeyword; } @Override Config getParserConfig() { if (parserConfig == null) { Config.LanguageMode mode; switch (options.getLanguageIn()) { case ECMASCRIPT3: mode = Config.LanguageMode.ECMASCRIPT3; break; case ECMASCRIPT5: mode = Config.LanguageMode.ECMASCRIPT5; break; case ECMASCRIPT5_STRICT: mode = Config.LanguageMode.ECMASCRIPT5_STRICT; break; default: throw new IllegalStateException("unexpected language mode"); } parserConfig = ParserRunner.createConfig( isIdeMode(), mode, acceptConstKeyword(), options.extraAnnotationNames); } return parserConfig; } @Override public boolean isTypeCheckingEnabled() { return options.checkTypes; } //------------------------------------------------------------------------ // Error reporting //------------------------------------------------------------------------ /** * The warning classes that are available from the command-line, and * are suppressible by the {@code @suppress} annotation. */ protected DiagnosticGroups getDiagnosticGroups() { return new DiagnosticGroups(); } @Override public void report(JSError error) { CheckLevel level = error.getDefaultLevel(); if (warningsGuard != null) { CheckLevel newLevel = warningsGuard.level(error); if (newLevel != null) { level = newLevel; } } if (level.isOn()) { if (getOptions().errorHandler != null) { getOptions().errorHandler.report(level, error); } errorManager.report(level, error); } } @Override public CheckLevel getErrorLevel(JSError error) { Preconditions.checkNotNull(options); return warningsGuard.level(error); } /** * Report an internal error. */ @Override void throwInternalError(String message, Exception cause) { String finalMessage = "INTERNAL COMPILER ERROR.\n" + "Please report this problem.\n" + message; RuntimeException e = new RuntimeException(finalMessage, cause); if (cause != null) { e.setStackTrace(cause.getStackTrace()); } throw e; } /** * Gets the number of errors. */ public int getErrorCount() { return errorManager.getErrorCount(); } /** * Gets the number of warnings. */ public int getWarningCount() { return errorManager.getWarningCount(); } @Override boolean hasHaltingErrors() { return !isIdeMode() && getErrorCount() > 0; } /** * Consults the {@link ErrorManager} to see if we've encountered errors * that should halt compilation.

* * If {@link CompilerOptions#ideMode} is {@code true}, this function * always returns {@code false} without consulting the error manager. The * error manager will continue to be told about new errors and warnings, but * the compiler will complete compilation of all inputs.

*/ public boolean hasErrors() { return hasHaltingErrors(); } /** Called from the compiler passes, adds debug info */ @Override void addToDebugLog(String str) { debugLog.append(str); debugLog.append('\n'); logger.fine(str); } @Override SourceFile getSourceFileByName(String sourceName) { // Here we assume that the source name is the input name, this // is try of JavaScript parsed from source. if (sourceName != null) { CompilerInput input = inputsById.get(new InputId(sourceName)); if (input != null) { return input.getSourceFile(); } } return null; } @Override public String getSourceLine(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getLine(lineNumber); } return null; } @Override public Region getSourceRegion(String sourceName, int lineNumber) { if (lineNumber < 1) { return null; } SourceFile input = getSourceFileByName(sourceName); if (input != null) { return input.getRegion(lineNumber); } return null; } //------------------------------------------------------------------------ // Package-private helpers //------------------------------------------------------------------------ @Override Node getNodeForCodeInsertion(JSModule module) { if (module == null) { if (inputs.isEmpty()) { throw new IllegalStateException("No inputs"); } return inputs.get(0).getAstRoot(this); } List moduleInputs = module.getInputs(); if (moduleInputs.size() > 0) { return moduleInputs.get(0).getAstRoot(this); } throw new IllegalStateException("Root module has no inputs"); } public SourceMap getSourceMap() { return sourceMap; } VariableMap getVariableMap() { return getPassConfig().getIntermediateState().variableMap; } VariableMap getPropertyMap() { return getPassConfig().getIntermediateState().propertyMap; } CompilerOptions getOptions() { return options; } FunctionInformationMap getFunctionalInformationMap() { return functionInformationMap; } /** * Sets the logging level for the com.google.javascript.jscomp package. */ public static void setLoggingLevel(Level level) { logger.setLevel(level); } /** Gets the DOT graph of the AST generated at the end of compilation. */ public String getAstDotGraph() throws IOException { if (jsRoot != null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); cfa.process(null, jsRoot); return DotFormatter.toDot(jsRoot, cfa.getCfg()); } else { return ""; } } @Override public ErrorManager getErrorManager() { if (options == null) { initOptions(newCompilerOptions()); } return errorManager; } @Override List getInputsInOrder() { return Collections.unmodifiableList(inputs); } /** * Returns an unmodifiable view of the compiler inputs indexed by id. */ public Map getInputsById() { return Collections.unmodifiableMap(inputsById); } /** * Gets the externs in the order in which they are being processed. */ List getExternsInOrder() { return Collections.unmodifiableList(externs); } /** * Stores the internal compiler state just before optimization is performed. * This can be saved and restored in order to efficiently optimize multiple * different output targets without having to perform checking multiple times. * * NOTE: This does not include all parts of the compiler's internal state. In * particular, SourceFiles and CompilerOptions are not recorded. In * order to recreate a Compiler instance from scratch, you would need to * call {@code init} with the same arguments as in the initial creation before * restoring intermediate state. */ public static class IntermediateState implements Serializable { private static final long serialVersionUID = 1L; Node externsRoot; private Node jsRoot; private List externs; private List inputs; private List modules; private PassConfig.State passConfigState; private JSTypeRegistry typeRegistry; private AbstractCompiler.LifeCycleStage lifeCycleStage; private Map injectedLibraries; private IntermediateState() {} } /** * Returns the current internal state, excluding the input files and modules. */ public IntermediateState getState() { IntermediateState state = new IntermediateState(); state.externsRoot = externsRoot; state.jsRoot = jsRoot; state.externs = externs; state.inputs = inputs; state.modules = modules; state.passConfigState = getPassConfig().getIntermediateState(); state.typeRegistry = typeRegistry; state.lifeCycleStage = getLifeCycleStage(); state.injectedLibraries = Maps.newLinkedHashMap(injectedLibraries); return state; } /** * Sets the internal state to the capture given. Note that this assumes that * the input files are already set up. */ public void setState(IntermediateState state) { externsRoot = state.externsRoot; jsRoot = state.jsRoot; externs = state.externs; inputs = state.inputs; modules = state.modules; passes = createPassConfigInternal(); getPassConfig().setIntermediateState(state.passConfigState); typeRegistry = state.typeRegistry; setLifeCycleStage(state.lifeCycleStage); injectedLibraries.clear(); injectedLibraries.putAll(state.injectedLibraries); } @VisibleForTesting List getInputsForTesting() { return inputs; } @VisibleForTesting List getExternsForTesting() { return externs; } @Override boolean hasRegExpGlobalReferences() { return hasRegExpGlobalReferences; } @Override void setHasRegExpGlobalReferences(boolean references) { hasRegExpGlobalReferences = references; } @Override void updateGlobalVarReferences(Map refMapPatch, Node collectionRoot) { Preconditions.checkState(collectionRoot.isScript() || collectionRoot.isBlock()); if (globalRefMap == null) { globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(), getExternsInOrder()); } globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot); } @Override GlobalVarReferenceMap getGlobalVarReferences() { return globalRefMap; } @Override CompilerInput getSynthesizedExternsInput() { if (synthesizedExternsInput == null) { synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS); } return synthesizedExternsInput; } @Override public double getProgress() { return progress; } @Override String getLastPassName() { return lastPassName; } @Override void setProgress(double newProgress, String passName) { this.lastPassName = passName; if (newProgress > 1.0) { progress = 1.0; } else { progress = newProgress; } } /** * Replaces one file in a hot-swap mode. The given JsAst should be made * from a new version of a file that already was present in the last compile * call. If the file is new, this will silently ignored. * * @param ast the ast of the file that is being replaced */ public void replaceScript(JsAst ast) { CompilerInput input = this.getInput(ast.getInputId()); if (!replaceIncrementalSourceAst(ast)) { return; } Node originalRoot = input.getAstRoot(this); processNewScript(ast, originalRoot); } /** * Adds a new Script AST to the compile state. If a script for the same file * already exists the script will not be added, instead a call to * #replaceScript should be used. * * @param ast the ast of the new file */ public void addNewScript(JsAst ast) { if (!addNewSourceAst(ast)) { return; } Node emptyScript = new Node(Token.SCRIPT); InputId inputId = ast.getInputId(); emptyScript.setInputId(inputId); emptyScript.setStaticSourceFile( SourceFile.fromCode(inputId.getIdName(), "")); processNewScript(ast, emptyScript); } private void processNewScript(JsAst ast, Node originalRoot) { Node js = ast.getAstRoot(this); Preconditions.checkNotNull(js); runHotSwap(originalRoot, js, this.getCleanupPassConfig()); // NOTE: If hot swap passes that use GlobalNamespace are added, we will need // to revisit this approach to clearing GlobalNamespaces runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks); this.getTypeRegistry().clearNamedTypes(); this.removeSyntheticVarsInput(); runHotSwap(originalRoot, js, this.ensureDefaultPassConfig()); } /** * Execute the passes from a PassConfig instance over a single replaced file. */ private void runHotSwap( Node originalRoot, Node js, PassConfig passConfig) { for (PassFactory passFactory : passConfig.getChecks()) { runHotSwapPass(originalRoot, js, passFactory); } } private void runHotSwapPass( Node originalRoot, Node js, PassFactory passFactory) { HotSwapCompilerPass pass = passFactory.getHotSwapPass(this); if (pass != null) { logger.info("Performing HotSwap for pass " + passFactory.getName()); pass.hotSwapScript(js, originalRoot); } } private PassConfig getCleanupPassConfig() { return new CleanupPasses(getOptions()); } private void removeSyntheticVarsInput() { String sourceName = Compiler.SYNTHETIC_EXTERNS; removeExternInput(new InputId(sourceName)); } @Override Node ensureLibraryInjected(String resourceName) { if (injectedLibraries.containsKey(resourceName)) { return null; } // All libraries depend on js/base.js boolean isBase = "base".equals(resourceName); if (!isBase) { ensureLibraryInjected("base"); } Node firstChild = loadLibraryCode(resourceName).removeChildren(); Node lastChild = firstChild.getLastSibling(); Node parent = getNodeForCodeInsertion(null); if (isBase) { parent.addChildrenToFront(firstChild); } else { parent.addChildrenAfter( firstChild, injectedLibraries.get("base")); } reportCodeChange(); injectedLibraries.put(resourceName, lastChild); return lastChild; } /** Load a library as a resource */ @VisibleForTesting Node loadLibraryCode(String resourceName) { String originalCode; try { originalCode = CharStreams.toString(new InputStreamReader( Compiler.class.getResourceAsStream( String.format("js/%s.js", resourceName)), Charsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } return Normalize.parseAndNormalizeSyntheticCode( this, originalCode, String.format("jscomp_%s_", resourceName)); } /** Returns the compiler version baked into the jar. */ public static String getReleaseVersion() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.version"); } /** Returns the compiler date baked into the jar. */ public static String getReleaseDate() { ResourceBundle config = ResourceBundle.getBundle(CONFIG_RESOURCE); return config.getString("compiler.date"); } /** * {@inheritDoc} */ @Override public void setOldParseTree(String sourceName, AstRoot oldAst) { } /** * {@inheritDoc} */ @Override public AstRoot getOldParseTreeByName(String sourceName) { return null; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MessageFormatter.java0000644000175000017500000000174512115204405026756 0ustar apoapo/* * Copyright 2007 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; /** * Format warnings and errors. This interface may be used by implementations of * {@link ErrorManager} to request message formatting capabilities. * */ public interface MessageFormatter { /** * Format an error. */ String formatError(JSError error); /** * Format a warning. */ String formatWarning(JSError warning); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DotFormatter.java0000644000175000017500000002174612115204405026123 0ustar apoapo/* * Copyright 2007 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.ControlFlowGraph.Branch; import com.google.javascript.jscomp.graph.GraphvizGraph; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge; import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; /** *

DotFormatter prints out a dot file of the Abstract Syntax Tree. * For a detailed description of the dot format and visualization tool refer * to Graphviz.

*

Typical usage of this class

* System.out.println(new DotFormatter().toDot(node)); *

This class is not thread safe and should not be used without proper * external synchronization.

* */ public class DotFormatter { private static final String INDENT = " "; private static final String ARROW = " -> "; private static final String LINE = " -- "; // stores the current assignment of node to keys private HashMap assignments = new HashMap(); // key count in order to assign a unique key to each node private int keyCount = 0; // the builder used to generate the dot diagram private Appendable builder; private final ControlFlowGraph cfg; private final boolean printAnnotations; /** For Testing Only */ private DotFormatter() { this.builder = new StringBuilder(); this.cfg = null; this.printAnnotations = false; } private DotFormatter(Node n, ControlFlowGraph cfg, Appendable builder, boolean printAnnotations) throws IOException { this.cfg = cfg; this.builder = builder; this.printAnnotations = printAnnotations; formatPreamble(); traverseNodes(n); formatConclusion(); } /** * Converts an AST to dot representation. * @param n the root of the AST described in the dot formatted string * @return the dot representation of the AST */ public static String toDot(Node n) throws IOException { return toDot(n, null); } /** * Converts an AST to dot representation. * @param n the root of the AST described in the dot formatted string * @param inCFG Control Flow Graph. * @param printAnnotations print annotations. * @return the dot representation of the AST */ static String toDot( Node n, ControlFlowGraph inCFG, boolean printAnnotations) throws IOException { StringBuilder builder = new StringBuilder(); new DotFormatter(n, inCFG, builder, printAnnotations); return builder.toString(); } /** * Converts an AST to dot representation. * @param n the root of the AST described in the dot formatted string * @param inCFG Control Flow Graph. * @return the dot representation of the AST */ static String toDot(Node n, ControlFlowGraph inCFG) throws IOException { StringBuilder builder = new StringBuilder(); new DotFormatter(n, inCFG, builder, false); return builder.toString(); } /** * Converts an AST to dot representation and appends it to the given buffer. * @param n the root of the AST described in the dot formatted string * @param inCFG Control Flow Graph. * @param builder A place to dump the graph. */ static void appendDot(Node n, ControlFlowGraph inCFG, Appendable builder) throws IOException { new DotFormatter(n, inCFG, builder, false); } /** * Creates a DotFormatter purely for testing DotFormatter's internal methods. */ static DotFormatter newInstanceForTesting() { return new DotFormatter(); } private void traverseNodes(Node parent) throws IOException { // key int keyParent = key(parent); // edges for (Node child = parent.getFirstChild(); child != null; child = child.getNext()) { int keyChild = key(child); builder.append(INDENT); builder.append(formatNodeName(keyParent)); builder.append(ARROW); builder.append(formatNodeName(keyChild)); builder.append(" [weight=1];\n"); traverseNodes(child); } // Flow Edges if (cfg != null && cfg.hasNode(parent)) { List> outEdges = cfg.getOutEdges(parent); String[] edgeList = new String[outEdges.size()]; for (int i = 0; i < edgeList.length; i++) { DiGraphEdge edge = outEdges.get(i); DiGraphNode succ = edge.getDestination(); String toNode = null; if (succ == cfg.getImplicitReturn()) { toNode = "RETURN"; } else { int keySucc = key(succ.getValue()); toNode = formatNodeName(keySucc); } edgeList[i] = formatNodeName(keyParent) + ARROW + toNode + " [label=\"" + edge.getValue().toString() + "\", " + "fontcolor=\"red\", " + "weight=0.01, color=\"red\"];\n"; } Arrays.sort(edgeList); for (int i = 0; i < edgeList.length; i++) { builder.append(INDENT); builder.append(edgeList[i]); } } } int key(Node n) throws IOException { Integer key = assignments.get(n); if (key == null) { key = keyCount++; assignments.put(n, key); builder.append(INDENT); builder.append(formatNodeName(key)); builder.append(" [label=\""); builder.append(name(n)); JSType type = n.getJSType(); if (type != null) { builder.append(" : "); builder.append(type.toString()); } if (printAnnotations && cfg != null && cfg.hasNode(n)) { Object annotation = cfg.getNode(n).getAnnotation(); if (annotation != null) { builder.append("\\n"); builder.append(annotation.toString()); } } builder.append("\""); if (n.getJSDocInfo() != null) { builder.append(" color=\"green\""); } builder.append("];\n"); } return key; } private String name(Node n) { int type = n.getType(); switch (type) { case Token.VOID: return "VOID"; default: return Token.name(type); } } private String formatNodeName(Integer key) { return "node" + key; } private void formatPreamble() throws IOException { builder.append("digraph AST {\n"); builder.append(INDENT); builder.append("node [color=lightblue2, style=filled];\n"); } private void formatConclusion() throws IOException { builder.append("}\n"); } /** * Outputs a string in DOT format that presents the graph. * * @param graph Input graph. * @return A string in Dot format that presents the graph. */ public static String toDot(GraphvizGraph graph) { StringBuilder builder = new StringBuilder (); builder.append(graph.isDirected() ? "digraph" : "graph"); builder.append(INDENT); builder.append(graph.getName()); builder.append(" {\n"); builder.append(INDENT); builder.append("node [color=lightblue2, style=filled];\n"); final String edgeSymbol = graph.isDirected() ? ARROW : LINE; List nodes = graph.getGraphvizNodes(); String[] nodeNames = new String[nodes.size()]; for (int i = 0; i < nodeNames.length; i++) { GraphvizNode gNode = nodes.get(i); nodeNames[i] = gNode.getId() + " [label=\"" + gNode.getLabel() + "\" color=\"" + gNode.getColor() + "\"]"; } // We sort the nodes so we get a deterministic output every time regardless // of the implementation of the graph data structure. Arrays.sort(nodeNames); for (String nodeName : nodeNames) { builder.append(INDENT); builder.append(nodeName); builder.append(";\n"); } List edges = graph.getGraphvizEdges(); String[] edgeNames = new String[edges.size()]; for (int i = 0; i < edgeNames.length; i++) { GraphvizEdge edge = edges.get(i); edgeNames[i] = edge.getNode1Id() + edgeSymbol + edge.getNode2Id(); } // Again, we sort the edges as well. Arrays.sort(edgeNames); for (String edgeName : edgeNames) { builder.append(INDENT); builder.append(edgeName); builder.append(";\n"); } builder.append("}\n"); return builder.toString(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Scope.java0000644000175000017500000003736112115204405024562 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * Scope contains information about a variable scope in JavaScript. * Scopes can be nested, a scope points back to its parent scope. * A Scope contains information about variables defined in that scope. *

* A Scope is also used as a lattice element for flow-sensitive type inference. * As a lattice element, a Scope is viewed as a map from names to types. A name * not in the map is considered to have the bottom type. The join of two maps m1 * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} * to meet the m1 type and m2 type. * * @see NodeTraversal * @see DataFlowAnalysis * */ public class Scope implements StaticScope, StaticSymbolTable { private final Map vars = new LinkedHashMap(); private final Scope parent; private final int depth; private final Node rootNode; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; private Var arguments; private static final Predicate DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = new Predicate() { @Override public boolean apply(Var var) { return var.getParentNode() != null && var.getType() == null && // no declared type var.getParentNode().isVar() && !var.isExtern(); } }; /** Stores info about a variable */ public static class Var implements StaticSlot, StaticReference { /** name */ final String name; /** Var node */ final Node nameNode; /** * The variable's type. */ private JSType type; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ final CompilerInput input; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ final int index; /** The enclosing scope */ final Scope scope; /** @see #isMarkedEscaped */ private boolean markedEscaped = false; /** @see #isMarkedAssignedExactlyOnce */ private boolean markedAssignedExactlyOnce = false; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred, String name, Node nameNode, JSType type, Scope scope, int index, CompilerInput input) { this.name = name; this.nameNode = nameNode; this.type = type; this.scope = scope; this.index = index; this.input = input; this.typeInferred = inferred; } /** * Gets the name of the variable. */ @Override public String getName() { return name; } /** * Gets the node for the name of the variable. */ @Override public Node getNode() { return nameNode; } CompilerInput getInput() { return input; } @Override public StaticSourceFile getSourceFile() { return nameNode.getStaticSourceFile(); } @Override public Var getSymbol() { return this; } @Override public Var getDeclaration() { return nameNode == null ? null : this; } /** * Gets the parent of the name node. */ public Node getParentNode() { return nameNode == null ? null : nameNode.getParent(); } /** * Whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope). */ public boolean isBleedingFunction() { return NodeUtil.isFunctionExpression(getParentNode()); } /** * Gets the scope where this variable is declared. */ Scope getScope() { return scope; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return nameNode != null && NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotated by {@code @define}. */ public boolean isDefine() { JSDocInfo info = getJSDocInfo(); return info != null && info.isDefine(); } public Node getInitialValue() { return NodeUtil.getRValueOfLValue(nameNode); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isTypeInferred()}. */ @Override public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ @Override public JSDocInfo getJSDocInfo() { return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode); } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Resolve this variable's type. */ void resolveType(ErrorReporter errorReporter) { if (type != null) { type = type.resolve(errorReporter, scope); } } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ @Override public boolean isTypeInferred() { return typeInferred; } public String getInputName() { if (input == null) return ""; else return input.getName(); } public boolean isNoShadow() { JSDocInfo info = getJSDocInfo(); return info != null && info.isNoShadow(); } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** * Record that this is escaped by an inner scope. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarkedEscaped() { return markedEscaped; } /** * Record that this is assigned exactly once.. * * In other words, it's assigned in an inner scope so that it's much harder * to make assertions about its value at a given point. */ void markAssignedExactlyOnce() { markedAssignedExactlyOnce = true; } /** * Whether this is assigned exactly once. * Notice that not all scope creators record this information. */ boolean isMarkedAssignedExactlyOnce() { return markedAssignedExactlyOnce; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null // input ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() { return System.identityHashCode(this); } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; this.isBottom = false; this.depth = parent.depth + 1; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param isBottom Whether this is the bottom of a lattice. Otherwise, * it must be a global scope. */ private Scope(Node rootNode, boolean isBottom) { this.parent = null; this.rootNode = rootNode; this.isBottom = isBottom; this.depth = 0; } static Scope createGlobalScope(Node rootNode) { return new Scope(rootNode, false); } static Scope createLatticeBottom(Node rootNode) { return new Scope(rootNode, true); } /** The depth of the scope. The global scope has depth 0. */ int getDepth() { return depth; } /** Whether this is the bottom of the lattice. */ boolean isBottom() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ @Override public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ @Override public JSType getTypeOfThis() { if (isGlobal()) { return ObjectType.cast(rootNode.getJSType()); } Preconditions.checkState(rootNode.isFunction()); JSType nodeType = rootNode.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { return nodeType.toMaybeFunctionType().getTypeOfThis(); } else { return parent.getTypeOfThis(); } } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } @Override public Var getSlot(String name) { return getVar(name); } @Override public Var getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); } else { return null; } } /** * Get a unique VAR object to represents "arguments" within this scope */ public Var getArgumentsVar() { if (arguments == null) { arguments = new Arguments(this); } return arguments; } /** * Returns true if a variable is declared. */ public boolean isDeclared(String name, boolean recurse) { Scope scope = this; if (scope.vars.containsKey(name)) return true; if (scope.parent != null && recurse) { return scope.parent.isDeclared(name, recurse); } return false; } /** * Return an iterator over all of the variables declared in this scope. */ public Iterator getVars() { return vars.values().iterator(); } /** * Return an iterable over all of the variables declared in this scope. */ Iterable getVarIterable() { return vars.values(); } @Override public Iterable getReferences(Var var) { return ImmutableList.of(var); } @Override public StaticScope getScope(Var var) { return var.scope; } @Override public Iterable getAllSymbols() { return Collections.unmodifiableCollection(vars.values()); } /** * Returns number of variables in this scope */ public int getVarCount() { return vars.size(); } /** * Returns whether this is the global scope. */ public boolean isGlobal() { return parent == null; } /** * Returns whether this is a local scope (i.e. not the global scope). */ public boolean isLocal() { return !isGlobal(); } /** * Gets all variables declared with "var" but without declared types attached. */ public Iterator getDeclarativelyUnboundVarsWithoutTypes() { return Iterators.filter( getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodeChangeHandler.java0000644000175000017500000000267012115204405026762 0ustar apoapo/* * 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.javascript.rhino.Node; /** * A simple listener for code change events. * Records whether the code has changed since the last reset. * @author nicksantos@google.com (Nick Santos) * @author dimvar@google.com (Dimitris Vardoulakis) */ class CodeChangeHandler { private boolean hasChanged = false; /** Report a change to some unspecified node of the AST. */ public void reportChange() { hasChanged = true; } /** Report a change to a specific function in the AST. */ public void reportChangedFun(Node n) { hasChanged = true; } /** Report that a function node has been removed from the AST */ public void reportDeletedFun(Node n) { hasChanged = true; } void reset() { hasChanged = false; } boolean hasCodeChanged() { return hasChanged; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MustBeReachingVariableDef.java0000644000175000017500000003326112115204405030431 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; /** * Computes reaching definition for all use of each variables. * * A definition of {@code A} in {@code A = foo()} is a reaching definition of * the use of {@code A} in {@code alert(A)} if all paths from entry node must * reaches that definition and it is the last definition before the use. * */ final class MustBeReachingVariableDef extends DataFlowAnalysis { // The scope of the function that we are analyzing. private final Scope jsScope; private final AbstractCompiler compiler; private final Set escaped; MustBeReachingVariableDef( ControlFlowGraph cfg, Scope jsScope, AbstractCompiler compiler) { super(cfg, new MustDefJoin()); this.jsScope = jsScope; this.compiler = compiler; this.escaped = Sets.newHashSet(); computeEscaped(jsScope, escaped, compiler); } /** * Abstraction of a local variable definition. It represents the node which * a local variable is defined as well as a set of other local variables that * this definition reads from. For example N: a = b + foo.bar(c). The * definition node will be N, the depending set would be {b,c}. */ static class Definition { final Node node; final Set depends = Sets.newHashSet(); private boolean unknownDependencies = false; Definition(Node node) { this.node = node; } @Override public boolean equals(Object other) { if (!(other instanceof Definition)) { return false; } Definition otherDef = (Definition) other; // If the var has the same definition node we can assume they have the // same depends set. return otherDef.node == node; } } /** * Must reaching definition lattice representation. It captures a product * lattice for each local (non-escaped) variable. The sub-lattice is * a n + 2 element lattice with all the {@link Definition} in the program, * TOP and BOTTOM. * *

Since this is a Must-Define analysis, BOTTOM represents the case where * there might be more than one reaching definition for the variable. * * * (TOP) * / | | \ * N1 N2 N3 ....Nn * \ | | / * (BOTTOM) * */ static final class MustDef implements LatticeElement { // TODO(user): Use bit vector instead of maps might get better // performance. Change it after this is tested to be fully functional. // When a Var "A" = "TOP", "A" does not exist in reachingDef's keySet. // When a Var "A" = Node N, "A" maps to that node. // When a Var "A" = "BOTTOM", "A" maps to null. final Map reachingDef; public MustDef() { reachingDef = Maps.newHashMap(); } public MustDef(Iterator vars) { this(); while(vars.hasNext()) { Var var = vars.next(); // Every variable in the scope is defined once in the beginning of the // function: all the declared variables are undefined, all functions // have been assigned and all arguments has its value from the caller. reachingDef.put(var, new Definition(var.scope.getRootNode())); } } /** * Copy constructor. * * @param other The constructed object is a replicated copy of this element. */ public MustDef(MustDef other) { reachingDef = Maps.newHashMap(other.reachingDef); } @Override public boolean equals(Object other) { return (other instanceof MustDef) && ((MustDef) other).reachingDef.equals(this.reachingDef); } } private static class MustDefJoin extends JoinOp.BinaryJoinOp { @Override public MustDef apply(MustDef a, MustDef b) { MustDef result = new MustDef(); Map resultMap = result.reachingDef; // Take the join of all variables that are not TOP in this. for (Map.Entry varEntry : a.reachingDef.entrySet()) { Var var = varEntry.getKey(); Definition aDef = varEntry.getValue(); if (aDef == null) { // "a" is BOTTOM implies that the variable has more than one possible // definition. We set the join of this to be BOTTOM regardless of what // "b" might be. resultMap.put(var, null); continue; } if (b.reachingDef.containsKey(var)) { Definition bDef = b.reachingDef.get(var); if (aDef.equals(bDef)) { resultMap.put(var, aDef); } else { resultMap.put(var, null); } } else { resultMap.put(var, aDef); } } // Take the join of all variables that are not TOP in other but it is TOP // in this. for (Map.Entry entry : b.reachingDef.entrySet()) { Var var = entry.getKey(); if (!a.reachingDef.containsKey(var)) { resultMap.put(var, entry.getValue()); } } return result; } } @Override boolean isForward() { return true; } @Override MustDef createEntryLattice() { return new MustDef(jsScope.getVars()); } @Override MustDef createInitialEstimateLattice() { return new MustDef(); } @Override MustDef flowThrough(Node n, MustDef input) { // TODO(user): We are doing a straight copy from input to output. There // might be some opportunities to cut down overhead. MustDef output = new MustDef(input); // TODO(user): This must know about ON_EX edges but it should handle // it better than what we did in liveness. Because we are in a forward mode, // we can used the branched forward analysis. computeMustDef(n, n, output, false); return output; } /** * @param n The node in question. * @param cfgNode The node to add * @param conditional true if the definition is not always executed. */ private void computeMustDef( Node n, Node cfgNode, MustDef output, boolean conditional) { switch (n.getType()) { case Token.BLOCK: case Token.FUNCTION: return; case Token.WHILE: case Token.DO: case Token.IF: computeMustDef( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeMustDef( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (lhs.isVar()) { lhs = lhs.getLastChild(); // for(var x in y) {...} } if (lhs.isName()) { addToDefIfLocal(lhs.getString(), cfgNode, rhs, output); } } return; case Token.AND: case Token.OR: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.HOOK: computeMustDef(n.getFirstChild(), cfgNode, output, conditional); computeMustDef(n.getFirstChild().getNext(), cfgNode, output, true); computeMustDef(n.getLastChild(), cfgNode, output, true); return; case Token.VAR: for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.hasChildren()) { computeMustDef(c.getFirstChild(), cfgNode, output, conditional); addToDefIfLocal(c.getString(), conditional ? null : cfgNode, c.getFirstChild(), output); } } return; default: if (NodeUtil.isAssignmentOp(n)) { if (n.getFirstChild().isName()) { Node name = n.getFirstChild(); computeMustDef(name.getNext(), cfgNode, output, conditional); addToDefIfLocal(name.getString(), conditional ? null : cfgNode, n.getLastChild(), output); return; } else if (NodeUtil.isGet(n.getFirstChild())) { // Treat all assignments to arguments as redefining the // parameters itself. Node obj = n.getFirstChild().getFirstChild(); if (obj.isName() && "arguments".equals(obj.getString())) { // TODO(user): More accuracy can be introduced // i.e. We know exactly what arguments[x] is if x is a constant // number. escapeParameters(output); } } } if (n.isName() && "arguments".equals(n.getString())) { escapeParameters(output); } // DEC and INC actually defines the variable. if (n.isDec() || n.isInc()) { Node target = n.getFirstChild(); if (target.isName()) { addToDefIfLocal(target.getString(), conditional ? null : cfgNode, null, output); return; } } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { computeMustDef(c, cfgNode, output, conditional); } } } /** * Set the variable lattice for the given name to the node value in the def * lattice. Do nothing if the variable name is one of the escaped variable. * * @param node The CFG node where the definition should be record to. * {@code null} if this is a conditional define. */ private void addToDefIfLocal( String name, @Nullable Node node, @Nullable Node rValue, MustDef def) { Var var = jsScope.getVar(name); // var might be null because the variable might be defined in the extern // that we might not traverse. if (var == null || var.scope != jsScope) { return; } for (Var other : def.reachingDef.keySet()) { Definition otherDef = def.reachingDef.get(other); if (otherDef == null) { continue; } if (otherDef.depends.contains(var)) { def.reachingDef.put(other, null); } } if (!escaped.contains(var)) { if (node == null) { def.reachingDef.put(var, null); } else { Definition definition = new Definition(node); if (rValue != null) { computeDependence(definition, rValue); } def.reachingDef.put(var, definition); } } } private void escapeParameters(MustDef output) { for (Iterator i = jsScope.getVars(); i.hasNext();) { Var v = i.next(); if (isParameter(v)) { // Assume we no longer know where the parameter comes from // anymore. output.reachingDef.put(v, null); } } // Also, assume we no longer know anything that depends on a parameter. for (Entry pair: output.reachingDef.entrySet()) { Definition value = pair.getValue(); if (value == null) { continue; } for (Var dep : value.depends) { if (isParameter(dep)) { output.reachingDef.put(pair.getKey(), null); } } } } private boolean isParameter(Var v) { return v.getParentNode().isParamList(); } /** * Computes all the local variables that rValue reads from and store that * in the def's depends set. */ private void computeDependence(final Definition def, Node rValue) { NodeTraversal.traverse(compiler, rValue, new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var dep = jsScope.getVar(n.getString()); if (dep == null) { def.unknownDependencies = true; } else { def.depends.add(dep); } } } }); } /** * Gets the must reaching definition of a given node. * * @param name name of the variable. It can only be names of local variable * that are not function parameters, escaped variables or variables * declared in catch. * @param useNode the location of the use where the definition reaches. */ Definition getDef(String name, Node useNode) { Preconditions.checkArgument(getCfg().hasNode(useNode)); GraphNode n = getCfg().getNode(useNode); FlowState state = n.getAnnotation(); return state.getIn().reachingDef.get(jsScope.getVar(name)); } Node getDefNode(String name, Node useNode) { Definition def = getDef(name, useNode); return def == null ? null : def.node; } boolean dependsOnOuterScopeVars(Definition def) { if (def.unknownDependencies) { return true; } for (Var s : def.depends) { if (s.scope != jsScope) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionArgumentInjector.java0000644000175000017500000004107512115204405030474 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeUtil.Visitor; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * A nifty set of functions to deal with the issues of replacing function * parameters with a set of call argument expressions. * * @author johnlenz@google.com (John Lenz) */ class FunctionArgumentInjector { // A string to use to represent "this". Anything that is not a valid // identifier can be used, so we use "this". static final String THIS_MARKER = "this"; private FunctionArgumentInjector() { // A private constructor to prevent instantiation. } /** * With the map provided, replace the names with expression trees. * @param node The root of the node tree within which to perform the * substitutions. * @param parent The parent root node. * @param replacements The map of names to template node trees with which * to replace the name Nodes. * @returns The root node or its replacement. */ static Node inject(AbstractCompiler compiler, Node node, Node parent, Map replacements) { return inject(compiler, node, parent, replacements, true); } static Node inject(AbstractCompiler compiler, Node node, Node parent, Map replacements, boolean replaceThis) { if (node.isName()) { Node replacementTemplate = replacements.get(node.getString()); if (replacementTemplate != null) { // This should not be replacing declared names. Preconditions.checkState(!parent.isFunction() || !parent.isVar() || !parent.isCatch()); // The name may need to be replaced more than once, // so we need to clone the node. Node replacement = replacementTemplate.cloneTree(); parent.replaceChild(node, replacement); return replacement; } } else if (replaceThis && node.isThis()) { Node replacementTemplate = replacements.get(THIS_MARKER); Preconditions.checkNotNull(replacementTemplate); if (!replacementTemplate.isThis()) { // The name may need to be replaced more than once, // so we need to clone the node. Node replacement = replacementTemplate.cloneTree(); parent.replaceChild(node, replacement); // Remove the value. This isn't required but it ensures that we won't // inject side-effects multiple times as it will trigger the null // check above if we do. if (NodeUtil.mayHaveSideEffects(replacementTemplate, compiler)) { replacements.remove(THIS_MARKER); } return replacement; } } else if (node.isFunction()) { // Once we enter another scope the "this" value changes, don't try // to replace it within an inner scope. replaceThis = false; } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { // We have to reassign c in case it was replaced, because the removed c's // getNext() would no longer be correct. c = inject(compiler, c, node, replacements, replaceThis); } return node; } /** * Get a mapping for function parameter names to call arguments. */ static LinkedHashMap getFunctionCallParameterMap( Node fnNode, Node callNode, Supplier safeNameIdSupplier) { // Create an argName -> expression map // NOTE: A linked map is created here to provide ordering. LinkedHashMap argMap = Maps.newLinkedHashMap(); // CALL NODE: [ NAME, ARG1, ARG2, ... ] Node cArg = callNode.getFirstChild().getNext(); if (cArg != null && NodeUtil.isFunctionObjectCall(callNode)) { argMap.put(THIS_MARKER, cArg); cArg = cArg.getNext(); } else { // 'apply' isn't supported yet. Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); argMap.put(THIS_MARKER, NodeUtil.newUndefinedNode(callNode)); } for (Node fnArg : NodeUtil.getFunctionParameters(fnNode).children()) { if (cArg != null) { argMap.put(fnArg.getString(), cArg); cArg = cArg.getNext(); } else { Node srcLocation = callNode; argMap.put(fnArg.getString(), NodeUtil.newUndefinedNode(srcLocation)); } } // Add temp names for arguments that don't have named parameters in the // called function. while (cArg != null) { String uniquePlaceholder = getUniqueAnonymousParameterName(safeNameIdSupplier); argMap.put(uniquePlaceholder, cArg); cArg = cArg.getNext(); } return argMap; } /** * Parameter names will be name unique when at a later time. */ private static String getUniqueAnonymousParameterName( Supplier safeNameIdSupplier) { return "JSCompiler_inline_anon_param_" + safeNameIdSupplier.get(); } /** * Retrieve a set of names that can not be safely substituted in place. * Example: * function(a) { * a = 0; * } * Inlining this without taking precautions would cause the call site value * to be modified (bad). */ static Set findModifiedParameters(Node fnNode) { Set names = getFunctionParameterSet(fnNode); Set unsafeNames = Sets.newHashSet(); return findModifiedParameters( fnNode.getLastChild(), null, names, unsafeNames, false); } /** * Check for uses of the named value that imply a pass-by-value * parameter is expected. This is used to prevent cases like: * * function (x) { * x=2; * return x; * } * * We don't want "undefined" to be substituted for "x", and get * undefined=2 * * @param n The node in question. * @param parent The parent of the node. * @param names The set of names to check. * @param unsafe The set of names that require aliases. * @param inInnerFunction Whether the inspection is occurring on a inner * function. */ private static Set findModifiedParameters( Node n, Node parent, Set names, Set unsafe, boolean inInnerFunction) { Preconditions.checkArgument(unsafe != null); if (n.isName()) { if (names.contains(n.getString())) { if (inInnerFunction || canNameValueChange(n, parent)) { unsafe.add(n.getString()); } } } else if (n.isFunction()) { // A function parameter can not be replaced with a direct inlined value // if it is referred to by an inner function. The inner function // can out live the call we are replacing, so inner function must // capture a unique name. This approach does not work within loop // bodies so those are forbidden elsewhere. inInnerFunction = true; } for (Node c : n.children()) { findModifiedParameters(c, n, names, unsafe, inInnerFunction); } return unsafe; } /** * This is similar to NodeUtil.isLValue except that object properties and * array member modification aren't important ("o" in "o.a = 2" is still "o" * after assignment, where in as "o = x", "o" is now "x"). * * This also looks for the redefinition of a name. * function (x){var x;} * * @param n The NAME node in question. * @param parent The parent of the node. */ private static boolean canNameValueChange(Node n, Node parent) { int type = parent.getType(); return (type == Token.VAR || type == Token.INC || type == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n)); } /** * Updates the set of parameter names in set unsafe to include any * arguments from the call site that require aliases. * @param fnNode The FUNCTION node to be inlined. * @param argMap The argument list for the call to fnNode. * @param namesNeedingTemps The set of names to update. */ static void maybeAddTempsForCallArguments( Node fnNode, Map argMap, Set namesNeedingTemps, CodingConvention convention) { if (argMap.isEmpty()) { // No arguments to check, we are done. return; } Preconditions.checkArgument(fnNode.isFunction()); Node block = fnNode.getLastChild(); Set parameters = argMap.keySet(); // Get the list of parameters that may need temporaries due to // side-effects. Set namesAfterSideEffects = findParametersReferencedAfterSideEffect( parameters, block); // Check for arguments that are evaluated more than once. for (Map.Entry entry : argMap.entrySet()) { String argName = entry.getKey(); if (namesNeedingTemps.contains(argName)) { continue; } Node cArg = entry.getValue(); boolean safe = true; int references = NodeUtil.getNameReferenceCount(block, argName); if (NodeUtil.mayEffectMutableState(cArg) && references > 0) { // Note: Mutable arguments should be assigned to temps, as the // may be within in a loop: // function x(a) { // for(var i=0; i<0; i++) { // foo(a); // } // x( [] ); // // The parameter in the call to foo should not become "[]". safe = false; } else if (NodeUtil.mayHaveSideEffects(cArg)) { // Even if there are no references, we still need to evaluate the // expression if it has side-effects. safe = false; } else if (NodeUtil.canBeSideEffected(cArg) && namesAfterSideEffects.contains(argName)) { safe = false; } else if (references > 1) { // Safe is a misnomer, this is a check for "large". switch (cArg.getType()) { case Token.NAME: String name = cArg.getString(); safe = !(convention.isExported(name)); break; case Token.THIS: safe = true; break; case Token.STRING: safe = (cArg.getString().length() < 2); break; default: safe = NodeUtil.isImmutableValue(cArg); break; } } if (!safe) { namesNeedingTemps.add(argName); } } } /** * Boot strap a traversal to look for parameters referenced * after a non-local side-effect. * NOTE: This assumes no-inner functions. * @param parameters The set of parameter names. * @param root The function code block. * @return The subset of parameters referenced after the first * seen non-local side-effect. */ private static Set findParametersReferencedAfterSideEffect( Set parameters, Node root) { // TODO(johnlenz): Consider using scope for this. Set locals = Sets.newHashSet(parameters); gatherLocalNames(root, locals); ReferencedAfterSideEffect collector = new ReferencedAfterSideEffect( parameters, locals); NodeUtil.visitPostOrder( root, collector, collector); return collector.getResults(); } /** * Collect parameter names referenced after a non-local side-effect. * * Assumptions: * - We assume parameters are not modified in the function body * (that is checked separately). * - There are no inner functions (also checked separately). * * As we are trying to replace parameters with there passed in values * we are interested in anything that may affect those value. So, ignoring * changes to local variables, we look for things that may affect anything * outside the local-state. Once such a side-effect is seen any following * reference to the function parameters are collected. These will need * to be assigned to temporaries to prevent changes to their value as would * have happened during the function call. * * To properly handle loop structures all references to the function * parameters are recorded and the decision to keep or throw away those * references is deferred until exiting the loop structure. */ private static class ReferencedAfterSideEffect implements Visitor, Predicate { private final Set parameters; private final Set locals; private boolean sideEffectSeen = false; private Set parametersReferenced = Sets.newHashSet(); private int loopsEntered = 0; ReferencedAfterSideEffect(Set parameters, Set locals) { this.parameters = parameters; this.locals = locals; } Set getResults() { return parametersReferenced; } @Override public boolean apply(Node node) { // Keep track of any loop structures entered. if (NodeUtil.isLoopStructure(node)) { loopsEntered++; } // If we have found all the parameters, don't bother looking // at the children. return !(sideEffectSeen && parameters.size() == parametersReferenced.size()); } boolean inLoop() { return loopsEntered != 0; } @Override public void visit(Node n) { // If we are exiting a loop. if (NodeUtil.isLoopStructure(n)) { loopsEntered--; if (!inLoop() && !sideEffectSeen) { // Now that the loops has been fully traversed and // no side-effects have been seen, throw away // the references seen in them. parametersReferenced.clear(); } } if (!sideEffectSeen) { // Look for side-effects. if (hasNonLocalSideEffect(n)) { sideEffectSeen = true; } } // If traversing the nodes of a loop save any references // that are seen. if (inLoop() || sideEffectSeen) { // Record references to parameters. if (n.isName()) { String name = n.getString(); if (parameters.contains(name)) { parametersReferenced.add(name); } } else if (n.isThis()) { parametersReferenced.add(THIS_MARKER); } } } /** * @return Whether the node may have non-local side-effects. */ private boolean hasNonLocalSideEffect(Node n) { boolean sideEffect = false; int type = n.getType(); // Note: Only care about changes to non-local names, specifically // ignore VAR declaration assignments. if (NodeUtil.isAssignmentOp(n) || type == Token.INC || type == Token.DEC) { Node lhs = n.getFirstChild(); // Ignore changes to local names. if (!isLocalName(lhs)) { sideEffect = true; } } else if (type == Token.CALL) { sideEffect = NodeUtil.functionCallHasSideEffects(n); } else if (type == Token.NEW) { sideEffect = NodeUtil.constructorCallHasSideEffects(n); } else if (type == Token.DELPROP) { sideEffect = true; } return sideEffect; } /** * @return Whether node is a reference to locally declared name. */ private boolean isLocalName(Node node) { if (node.isName()) { String name = node.getString(); return locals.contains(name); } return false; } } /** * Gather any names declared in the local scope. */ private static void gatherLocalNames(Node n, Set names) { if (n.isFunction()) { if (NodeUtil.isFunctionDeclaration(n)) { names.add(n.getFirstChild().getString()); } // Don't traverse into inner function scopes; return; } else if (n.isName()) { switch (n.getParent().getType()) { case Token.VAR: case Token.CATCH: names.add(n.getString()); } } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { gatherLocalNames(c, names); } } /** * Get a set of function parameter names. */ private static Set getFunctionParameterSet(Node fnNode) { Set set = Sets.newHashSet(); for (Node n : NodeUtil.getFunctionParameters(fnNode).children()) { set.add(n.getString()); } return set; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/LinkedFlowScope.java0000644000175000017500000003742012115204405026535 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.type.FlowScope; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.SimpleSlot; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * A flow scope that tries to store as little symbol information as possible, * instead delegating to its parents. Optimized for low memory use. * * @author nicksantos@google.com (Nick Santos) */ class LinkedFlowScope implements FlowScope { // The closest flow scope cache. private final FlatFlowScopeCache cache; // The parent flow scope. private final LinkedFlowScope parent; // The distance between this flow scope and the closest flat flow scope. private int depth; static final int MAX_DEPTH = 250; // A FlatFlowScopeCache equivalent to this scope. private FlatFlowScopeCache flattened; // Flow scopes assume that all their ancestors are immutable. // So once a child scope is created, this flow scope may not be modified. private boolean frozen = false; // The last slot defined in this flow instruction, and the head of the // linked list of slots. private LinkedFlowSlot lastSlot; private LinkedFlowScope(FlatFlowScopeCache cache, LinkedFlowScope directParent) { this.cache = cache; if (directParent == null) { this.lastSlot = null; this.depth = 0; this.parent = cache.linkedEquivalent; } else { this.lastSlot = directParent.lastSlot; this.depth = directParent.depth + 1; this.parent = directParent; } } LinkedFlowScope(FlatFlowScopeCache cache) { this(cache, null); } LinkedFlowScope(LinkedFlowScope directParent) { this(directParent.cache, directParent); } /** Gets the function scope for this flow scope. */ private Scope getFunctionScope() { return cache.functionScope; } /** Whether this flows from a bottom scope. */ private boolean flowsFromBottom() { return getFunctionScope().isBottom(); } /** * Creates an entry lattice for the flow. */ public static LinkedFlowScope createEntryLattice(Scope scope) { return new LinkedFlowScope(new FlatFlowScopeCache(scope)); } @Override public void inferSlotType(String symbol, JSType type) { Preconditions.checkState(!frozen); lastSlot = new LinkedFlowSlot(symbol, type, lastSlot); depth++; cache.dirtySymbols.add(symbol); } @Override public void inferQualifiedSlot(Node node, String symbol, JSType bottomType, JSType inferredType) { Scope functionScope = getFunctionScope(); if (functionScope.isLocal()) { if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) { functionScope.declare(symbol, node, bottomType, null); } inferSlotType(symbol, inferredType); } } @Override public JSType getTypeOfThis() { return cache.functionScope.getTypeOfThis(); } @Override public Node getRootNode() { return getFunctionScope().getRootNode(); } @Override public StaticScope getParentScope() { return getFunctionScope().getParentScope(); } /** * Get the slot for the given symbol. */ @Override public StaticSlot getSlot(String name) { if (cache.dirtySymbols.contains(name)) { for (LinkedFlowSlot slot = lastSlot; slot != null; slot = slot.parent) { if (slot.getName().equals(name)) { return slot; } } } return cache.getSlot(name); } @Override public StaticSlot getOwnSlot(String name) { throw new UnsupportedOperationException(); } @Override public FlowScope createChildFlowScope() { frozen = true; if (depth > MAX_DEPTH) { if (flattened == null) { flattened = new FlatFlowScopeCache(this); } return new LinkedFlowScope(flattened); } return new LinkedFlowScope(this); } /** * Iterate through all the linked flow scopes before this one. * If there's one and only one slot defined between this scope * and the blind scope, return it. */ @Override public StaticSlot findUniqueRefinedSlot(FlowScope blindScope) { StaticSlot result = null; for (LinkedFlowScope currentScope = this; currentScope != blindScope; currentScope = currentScope.parent) { for (LinkedFlowSlot currentSlot = currentScope.lastSlot; currentSlot != null && (currentScope.parent == null || currentScope.parent.lastSlot != currentSlot); currentSlot = currentSlot.parent) { if (result == null) { result = currentSlot; } else if (!currentSlot.getName().equals(result.getName())) { return null; } } } return result; } /** * Look through the given scope, and try to find slots where it doesn't * have enough type information. Then fill in that type information * with stuff that we've inferred in the local flow. */ @Override public void completeScope(StaticScope staticScope) { Scope scope = (Scope) staticScope; for (Iterator it = scope.getVars(); it.hasNext();) { Var var = it.next(); if (var.isTypeInferred()) { JSType type = var.getType(); if (type == null || type.isUnknownType()) { JSType flowType = getSlot(var.getName()).getType(); var.setType(flowType); } } } } /** * Remove flow scopes that add nothing to the flow. */ // NOTE(nicksantos): This function breaks findUniqueRefinedSlot, because // findUniqueRefinedSlot assumes that this scope is a direct descendant // of blindScope. This is not necessarily true if this scope has been // optimize()d and blindScope has not. This should be fixed. For now, // we only use optimize() where we know that we won't have to do // a findUniqueRefinedSlot on it. @Override public LinkedFlowScope optimize() { LinkedFlowScope current; for (current = this; current.parent != null && current.lastSlot == current.parent.lastSlot; current = current.parent) {} return current; } /** Join the two FlowScopes. */ static class FlowScopeJoinOp extends JoinOp.BinaryJoinOp { @SuppressWarnings("unchecked") @Override public FlowScope apply(FlowScope a, FlowScope b) { // To join the two scopes, we have to LinkedFlowScope linkedA = (LinkedFlowScope) a; LinkedFlowScope linkedB = (LinkedFlowScope) b; linkedA.frozen = true; linkedB.frozen = true; if (linkedA.optimize() == linkedB.optimize()) { return linkedA.createChildFlowScope(); } return new LinkedFlowScope(new FlatFlowScopeCache(linkedA, linkedB)); } } @Override public boolean equals(Object other) { if (other instanceof LinkedFlowScope) { LinkedFlowScope that = (LinkedFlowScope) other; if (this.optimize() == that.optimize()) { return true; } // If two flow scopes are in the same function, then they could have // two possible function scopes: the real one and the BOTTOM scope. // If they have different function scopes, we *should* iterate through all // the variables in each scope and compare. However, 99.9% of the time, // they're not equal. And the other .1% of the time, we can pretend // they're equal--this just means that data flow analysis will have // to propagate the entry lattice a little bit further than it // really needs to. Everything will still come out ok. if (this.getFunctionScope() != that.getFunctionScope()) { return false; } if (cache == that.cache) { // If the two flow scopes have the same cache, then we can check // equality a lot faster: by just looking at the "dirty" elements // in the cache, and comparing them in both scopes. for (String name : cache.dirtySymbols) { if (diffSlots(getSlot(name), that.getSlot(name))) { return false; } } return true; } Map> myFlowSlots = allFlowSlots(); Map> otherFlowSlots = that.allFlowSlots(); for (StaticSlot slot : myFlowSlots.values()) { if (diffSlots(slot, otherFlowSlots.get(slot.getName()))) { return false; } otherFlowSlots.remove(slot.getName()); } for (StaticSlot slot : otherFlowSlots.values()) { if (diffSlots(slot, myFlowSlots.get(slot.getName()))) { return false; } } return true; } return false; } /** * Determines whether two slots are meaningfully different for the * purposes of data flow analysis. */ private boolean diffSlots(StaticSlot slotA, StaticSlot slotB) { boolean aIsNull = slotA == null || slotA.getType() == null; boolean bIsNull = slotB == null || slotB.getType() == null; if (aIsNull && bIsNull) { return false; } else if (aIsNull ^ bIsNull) { return true; } // Both slots and types must be non-null. return slotA.getType().differsFrom(slotB.getType()); } /** * Gets all the symbols that have been defined before this point * in the current flow. Does not return slots that have not changed during * the flow. * * For example, consider the code: * * var x = 3; * function f() { * var y = 5; * y = 6; // FLOW POINT * var z = y; * return z; * } * * A FlowScope at FLOW POINT will return a slot for y, but not * a slot for x or z. */ private Map> allFlowSlots() { Map> slots = Maps.newHashMap(); for (LinkedFlowSlot slot = lastSlot; slot != null; slot = slot.parent) { if (!slots.containsKey(slot.getName())) { slots.put(slot.getName(), slot); } } for (Map.Entry> symbolEntry : cache.symbols.entrySet()) { if (!slots.containsKey(symbolEntry.getKey())) { slots.put(symbolEntry.getKey(), symbolEntry.getValue()); } } return slots; } /** * A static slot that can be used in a linked list. */ private static class LinkedFlowSlot extends SimpleSlot { final LinkedFlowSlot parent; LinkedFlowSlot(String name, JSType type, LinkedFlowSlot parent) { super(name, type, true); this.parent = parent; } } /** * A map that tries to cache as much symbol table information * as possible in a map. Optimized for fast lookup. */ private static class FlatFlowScopeCache { // The Scope for the entire function or for the global scope. private final Scope functionScope; // The linked flow scope that this cache represents. private final LinkedFlowScope linkedEquivalent; // All the symbols defined before this point in the local flow. // May not include lazily declared qualified names. private Map> symbols = Maps.newHashMap(); // Used to help make lookup faster for LinkedFlowScopes by recording // symbols that may be redefined "soon", for an arbitrary definition // of "soon". ;) // // More rigorously, if a symbol is redefined in a LinkedFlowScope, // and this is the closest FlatFlowScopeCache, then that symbol is marked // "dirty". In this way, we don't waste time looking in the LinkedFlowScope // list for symbols that aren't defined anywhere nearby. final Set dirtySymbols = Sets.newHashSet(); // The cache at the bottom of the lattice. FlatFlowScopeCache(Scope functionScope) { this.functionScope = functionScope; symbols = ImmutableMap.of(); linkedEquivalent = null; } // A cache in the middle of a long scope chain. FlatFlowScopeCache(LinkedFlowScope directParent) { FlatFlowScopeCache cache = directParent.cache; functionScope = cache.functionScope; symbols = directParent.allFlowSlots(); linkedEquivalent = directParent; } // A cache at the join of two scope chains. FlatFlowScopeCache(LinkedFlowScope joinedScopeA, LinkedFlowScope joinedScopeB) { linkedEquivalent = null; // Always prefer the "real" function scope to the faked-out // bottom scope. functionScope = joinedScopeA.flowsFromBottom() ? joinedScopeB.getFunctionScope() : joinedScopeA.getFunctionScope(); Map> slotsA = joinedScopeA.allFlowSlots(); Map> slotsB = joinedScopeB.allFlowSlots(); symbols = slotsA; // There are 5 different join cases: // 1) The type is declared in joinedScopeA, not in joinedScopeB, // and not in functionScope. Just use the one in A. // 2) The type is declared in joinedScopeB, not in joinedScopeA, // and not in functionScope. Just use the one in B. // 3) The type is declared in functionScope and joinedScopeA, but // not in joinedScopeB. Join the two types. // 4) The type is declared in functionScope and joinedScopeB, but // not in joinedScopeA. Join the two types. // 5) The type is declared in joinedScopeA and joinedScopeB. Join // the two types. Set symbolNames = Sets.newHashSet(symbols.keySet()); symbolNames.addAll(slotsB.keySet()); for (String name : symbolNames) { StaticSlot slotA = slotsA.get(name); StaticSlot slotB = slotsB.get(name); JSType joinedType = null; if (slotB == null || slotB.getType() == null) { StaticSlot fnSlot = joinedScopeB.getFunctionScope().getSlot(name); JSType fnSlotType = fnSlot == null ? null : fnSlot.getType(); if (fnSlotType == null) { // Case #1 -- already inserted. } else { // Case #3 joinedType = slotA.getType().getLeastSupertype(fnSlotType); } } else if (slotA == null || slotA.getType() == null) { StaticSlot fnSlot = joinedScopeA.getFunctionScope().getSlot(name); JSType fnSlotType = fnSlot == null ? null : fnSlot.getType(); if (fnSlotType == null) { // Case #2 symbols.put(name, slotB); } else { // Case #4 joinedType = slotB.getType().getLeastSupertype(fnSlotType); } } else { // Case #5 joinedType = slotA.getType().getLeastSupertype(slotB.getType()); } if (joinedType != null) { symbols.put(name, new SimpleSlot(name, joinedType, true)); } } } /** * Get the slot for the given symbol. */ public StaticSlot getSlot(String name) { if (symbols.containsKey(name)) { return symbols.get(name); } else { return functionScope.getSlot(name); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VarCheck.java0000644000175000017500000002352112115204405025170 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Checks that all variables are declared, that file-private variables are * accessed only in the file that declares them, and that any var references * that cross module boundaries respect declared module dependencies. * */ class VarCheck extends AbstractPostOrderCallback implements HotSwapCompilerPass { static final DiagnosticType UNDEFINED_VAR_ERROR = DiagnosticType.error( "JSC_UNDEFINED_VARIABLE", "variable {0} is undeclared"); static final DiagnosticType VIOLATED_MODULE_DEP_ERROR = DiagnosticType.error( "JSC_VIOLATED_MODULE_DEPENDENCY", "module {0} cannot reference {2}, defined in " + "module {1}, since {1} loads after {0}"); static final DiagnosticType MISSING_MODULE_DEP_ERROR = DiagnosticType.warning( "JSC_MISSING_MODULE_DEPENDENCY", "missing module dependency; module {0} should depend " + "on module {1} because it references {2}"); static final DiagnosticType STRICT_MODULE_DEP_ERROR = DiagnosticType.disabled( "JSC_STRICT_MODULE_DEPENDENCY", "module {0} cannot reference {2}, defined in " + "module {1}"); static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR = DiagnosticType.warning( "JSC_UNDEFINED_EXTERN_VAR_ERROR", "name {0} is not undefined in the externs."); private Node synthesizedExternsRoot = null; // Vars that still need to be declared in externs. These will be declared // at the end of the pass, or when we see the equivalent var declared // in the normal code. private final Set varsToDeclareInExterns = Sets.newHashSet(); private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; // Whether extern checks emit error. private final boolean strictExternCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.strictExternCheck = compiler.getErrorLevel( JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { // Don't run externs-checking in sanity check mode. Normalization will // remove duplicate VAR declarations, which will make // externs look like they have assigns. if (!sanityCheck) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); } NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); for (String varName : varsToDeclareInExterns) { createSynthesizedExternVar(varName); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Preconditions.checkState(scriptRoot.isScript()); NodeTraversal t = new NodeTraversal(compiler, this); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other JS files. t.traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); Preconditions.checkState(NodeUtil.isFunctionExpression(parent)); return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (!sanityCheck && varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExternVar(String varName) { Node nameNode = IR.name(varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, I'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( IR.var(nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString()); varsToDeclareInExterns.add(n.getString()); } } break; default: t.report(n, NAME_REFERENCE_IN_EXTERNS_ERROR, n.getString()); Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { varsToDeclareInExterns.add(n.getString()); } break; } } } } /** Lazily create a "new" externs input for undeclared variables. */ private CompilerInput getSynthesizedExternsInput() { return compiler.getSynthesizedExternsInput(); } /** Lazily create a "new" externs root for undeclared variables. */ private Node getSynthesizedExternsRoot() { if (synthesizedExternsRoot == null) { CompilerInput synthesizedExterns = getSynthesizedExternsInput(); synthesizedExternsRoot = synthesizedExterns.getAstRoot(compiler); } return synthesizedExternsRoot; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DefinitionProvider.java0000644000175000017500000000236512115204405027310 0ustar apoapo/* * 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.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.rhino.Node; import java.util.Collection; /** * Maps variable uses sites to variable definition sites. * */ interface DefinitionProvider { /** * Returns a collection of definitions that characterize the * possible values of a variable or property. If information is * unavailable or incomplete, return null. This function should * never return an empty collection. * * @return non-empty definition collection, or null. */ Collection getDefinitionsReferencedAt(Node useSite); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RescopeGlobalSymbols.java0000644000175000017500000002371412115204405027600 0ustar apoapo/* * Copyright 2011 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Finds all references to global symbols and rewrites them to be property * accesses to a special object with the same name as the global symbol. * * Given the name of the global object is NS *

 var a = 1; function b() { return a }
* becomes *
 NS.a = 1; NS.b = function b() { return NS.a }
* * This allows splitting code into modules that depend on each other's * global symbols, without using polluting JavaScript's global scope with those * symbols. * *

This compile step requires moveFunctionDeclarations to be turned on * to guarantee semantics. * *

For lots of examples, see the unit test. * * */ class RescopeGlobalSymbols implements CompilerPass { // Appended to variables names that conflict with globalSymbolNamespace. private static final String DISAMBIGUATION_SUFFIX = "$"; private static final String WINDOW = "window"; private static final Set SPECIAL_EXTERNS = ImmutableSet.of(WINDOW, "eval", "arguments"); private final AbstractCompiler compiler; private final String globalSymbolNamespace; private final boolean addExtern; RescopeGlobalSymbols(AbstractCompiler compiler, String globalSymbolNamespace, boolean addExtern) { this.compiler = compiler; this.globalSymbolNamespace = globalSymbolNamespace; this.addExtern = addExtern; } RescopeGlobalSymbols(AbstractCompiler compiler, String globalSymbolNamespace) { this(compiler, globalSymbolNamespace, true); } private void addExternForGlobalSymbolNamespace() { Node varNode = IR.var(IR.name(globalSymbolNamespace)); CompilerInput input = compiler.newExternInput( "{RescopeGlobalSymbolsNamespaceVar}"); input.getAstRoot(compiler).addChildrenToBack(varNode); compiler.reportCodeChange(); } @Override public void process(Node externs, Node root) { // Make the name of the globalSymbolNamespace an extern. if (addExtern) { addExternForGlobalSymbolNamespace(); } // Rewrite all references to global symbols to properties of a // single symbol by: // (If necessary the 3 traversals could be combined. They are left // separate for readability reasons.) // 1. turning global named function statements into var assignments. NodeTraversal.traverse(compiler, root, new RewriteGlobalFunctionStatementsToVarAssignmentsCallback()); // 2. rewriting all references to be property accesses of the single symbol. NodeTraversal.traverse(compiler, root, new RewriteScopeCallback()); // 3. removing the var from every statement in global scope. NodeTraversal.traverse(compiler, root, new RemoveGlobalVarCallback()); // Extra pass which makes all extern global symbols reference window // explicitly. NodeTraversal.traverse(compiler, root, new MakeExternsReferenceWindowExplicitly()); } /** * Rewrites function statements to var statements + assignment. * *

function test(){}
* becomes *
var test = function (){}
* * After this traversal, the special case of global function statements * can be ignored. */ private class RewriteGlobalFunctionStatementsToVarAssignmentsCallback extends AbstractShallowStatementCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isFunctionDeclaration(n)) { String name = NodeUtil.getFunctionName(n); n.getFirstChild().setString(""); Node prev = parent.getChildBefore(n); n.detachFromParent(); Node var = NodeUtil.newVarNode(name, n); if (prev == null) { parent.addChildToFront(var); } else { parent.addChildAfter(var, prev); } compiler.reportCodeChange(); } } } /** * Visits each NAME token and checks whether it refers to a global variable. * If yes, rewrites the name to be a property access on the * "globalSymbolNamespace". * *
var a = 1, b = 2, c = 3;
* becomes *
var NS.a = 1, NS.b = 2, NS.c = 4
* (The var token is removed in a later traversal.) * *
a + b
* becomes *
NS.a + NS.b
* *
a()
* becomes *
(0,NS.a)()
* Notice the special syntax here to preserve the *this* semantics in the * function call. */ private class RewriteScopeCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String name = n.getString(); // Ignore anonymous functions if (parent.isFunction() && name.length() == 0) { return; } Scope.Var var = t.getScope().getVar(name); if (var == null) { return; } // Don't touch externs. if (var.isExtern()) { return; } // When the globalSymbolNamespace is used as a local variable name // add suffix to avoid shadowing the namespace. Also add a suffix // if a name starts with the name of the globalSymbolNamespace and // the suffix. if (!var.isExtern() && (name.equals(globalSymbolNamespace) || name.indexOf(globalSymbolNamespace + DISAMBIGUATION_SUFFIX) == 0)) { n.setString(name + DISAMBIGUATION_SUFFIX); compiler.reportCodeChange(); } // We only care about global vars. if (!var.isGlobal()) { return; } Node nameNode = var.getNameNode(); // The exception variable (e in try{}catch(e){}) should not be rewritten. if (nameNode != null && nameNode.getParent() != null && nameNode.getParent().isCatch()) { return; } replaceSymbol(n, name); } private void replaceSymbol(Node node, String name) { Node parent = node.getParent(); Node replacement = IR.getprop( IR.name(globalSymbolNamespace).srcref(node), IR.string(name).srcref(node)); replacement.srcref(node); if (node.hasChildren()) { // var declaration list: var a = 1, b = 2; Node assign = IR.assign(replacement, node.removeFirstChild()); parent.replaceChild(node, assign); } else { parent.replaceChild(node, replacement); } compiler.reportCodeChange(); } } /** * Removes every occurrence of var that declares a global variable. * *
var NS.a = 1, NS.b = 2;
* becomes *
NS.a = 1; NS.b = 2;
* *
for (var a = 0, b = 0;;)
* becomes *
for (NS.a = 0, NS.b = 0;;)
* * Declarations without assignments are optimized away: *
var a = 1, b;
* becomes *
NS.a = 1
*/ private class RemoveGlobalVarCallback extends AbstractShallowStatementCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isVar()) { return; } List commas = new ArrayList(); List interestingChildren = new ArrayList(); // Filter out declarations without assignments. // As opposed to regular var nodes, there are always assignments // because the previous traversal in RewriteScopeCallback creates // them. for (Node c : n.children()) { if (c.isAssign() || parent.isFor()) { interestingChildren.add(c); } } for (Node c : interestingChildren) { if (parent.isFor() && parent.getFirstChild() == n) { commas.add(c.cloneTree()); } else { // Var statement outside of for-loop. Node expr = IR.exprResult(c.cloneTree()).srcref(c); parent.addChildBefore(expr, n); } } if (commas.size() > 0) { Node comma = joinOnComma(commas, n); parent.addChildBefore(comma, n); } // Remove the var node. parent.removeChild(n); compiler.reportCodeChange(); } private Node joinOnComma(List commas, Node source) { Node comma = commas.get(0); for (int i = 1; i < commas.size(); i++) { Node nextComma = IR.comma(comma, commas.get(i)); nextComma.copyInformationFrom(source); comma = nextComma; } return comma; } } /** * Rewrites extern names to be explicit children of window instead of only * implicitly referencing it. * This enables injecting window into a scope and make all global symbol * depend on the injected object. */ private class MakeExternsReferenceWindowExplicitly extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String name = n.getString(); Scope.Var var = t.getScope().getVar(name); if (name.length() > 0 && (var == null || var.isExtern()) && !globalSymbolNamespace.equals(name) && !SPECIAL_EXTERNS.contains(name)) { parent.replaceChild(n, IR.getprop(IR.name(WINDOW), IR.string(name)) .srcrefTree(n)); compiler.reportCodeChange(); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameReferenceGraphConstruction.java0000644000175000017500000005073512115204405031605 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NameReferenceGraph.Reference; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.ObjectType; import java.util.ArrayList; import java.util.Collection; import javax.annotation.Nullable; /** * Constructs a name reference graph. * * @see NameReferenceGraph * */ class NameReferenceGraphConstruction implements CompilerPass { private final AbstractCompiler compiler; private final NameReferenceGraph graph; // Maps "foo" -> (curFuncName, unknownObject.foo) if we have no idea what // the unknown object is. After we finish one pass, we must go through all // the nodes that might have a name foo and connect that to the curFuncName. // The accuracy of the analysis will depend heavily on eliminating the need // to resort to this map. private final Multimap unknownNameUse = HashMultimap.create(); // Should we continue even if we found a type checker bug. private static final boolean CONSERVATIVE = false; // The symbol for the current function so we can quickly create a reference // edge when we see a call: Example when this symbol is foo() and we see // bar(), we connect foo -> bar. private final ArrayList currentFunctionStack = new ArrayList(); NameReferenceGraphConstruction(AbstractCompiler compiler) { this.compiler = compiler; this.graph = new NameReferenceGraph(compiler); } NameReferenceGraph getNameReferenceGraph() { return this.graph; } @Override public void process(Node externs, Node root) { // Use the MemoizedScopeCreator instance from TypeCheck if available // as FunctionTypeBuilder warns about existing types if TypedScopeCreator is // ran a second time. ScopeCreator scopeCreator = compiler.getTypedScopeCreator(); if (scopeCreator == null) { // The TypedScopeCreator gives us correct handling of namespaces, // while the default NodeTraversal only gives us a // SyntacticScopeCreator. scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); } NodeTraversal externsTraversal = new NodeTraversal(compiler, new Traversal(true), scopeCreator); NodeTraversal codeTraversal = new NodeTraversal(compiler, new Traversal(false), scopeCreator); Scope topScope = compiler.getTopScope(); if (topScope != null) { externsTraversal.traverseWithScope(externs, topScope); codeTraversal.traverseWithScope(root, topScope); } else { externsTraversal.traverse(externs); codeTraversal.traverse(root); } connectUnknowns(); } private class Traversal implements ScopedCallback { final boolean isExtern; private Traversal(boolean isExtern) { this.isExtern = isExtern; pushContainingFunction(graph.MAIN); } @Override public void enterScope(NodeTraversal t) { Node root = t.getScopeRoot(); Node parent = root.getParent(); // When we are not in a {{GLOBAL MAIN}}, we need to determine what the // current function is. if (!t.inGlobalScope()) { // TODO(user): A global function foo() is treated as the same // function as a inner function named foo(). We should use some clever // naming scheme to avoid this lost of precision. String name = NodeUtil.getFunctionName(root); if (name == null) { // When the name is null, we have a function that is presumably not // reference-able again and should not be modeled in the name graph. // A common example would be (function() { ... })(); pushContainingFunction(graph.UNKNOWN); return; } // If we've done type analysis, then we should be able to get the // correct JSFunctionType for the containing function. If not, // we're probably going to get an unknown type here. JSType type = getType(root); if (parent.isAssign() && NodeUtil.isPrototypeProperty(parent.getFirstChild())) { pushContainingFunction( recordPrototypePropDefinition(parent.getFirstChild(), type, parent)); } else { pushContainingFunction( recordStaticNameDefinition( name, type, root, root.getLastChild())); } } } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { popContainingFunction(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @SuppressWarnings("fallthrough") @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: case Token.GETPROP: if (parent.isGetProp()) { // We will resolve this when we visit parent later in the traversal. return; } else if (parent.isFunction()) { // Function declarations have been taken care of in enterScope(); return; } else if (parent.isAssign()) { // Handled below. return; } if (isLocalNameReference(t, n)) { // Ignore all local variable references unless is creates a closure. return; } if (isPrototypeNameReference(n)) { recordPrototypePropUse(n, parent); } else if (isStaticNameReference(n, t.getScope())) { recordStaticNameUse(n, parent); } else { recordUnknownUse(n, parent); } break; case Token.ASSIGN: Node lhs = n.getFirstChild(); Node rhs = n.getLastChild(); if (rhs.isFunction()) { // These are recorded when entering the scope. return; } if (lhs.isName() || lhs.isGetProp() || rhs.isGetProp()) { if (NodeUtil.isPrototypeProperty(lhs)) { Name name = recordPrototypePropDefinition( lhs, getType(rhs), n); name.setAliased(true); } } maybeAliasNamesOnAssign(lhs, rhs); break; case Token.VAR: // var foo = bar; Node varName = n.getFirstChild(); Node assignedValue = varName.getFirstChild(); if (assignedValue == null) { return; } maybeAliasNamesOnAssign(varName, assignedValue); break; case Token.CALL: Node param = n.getFirstChild(); // We need to alias every name that is passed as a parameter because // they have different names inside the function's scope. while ((param = param.getNext()) != null) { if (param.isName() || param.isGetProp()) { safeAlias(param); } } maybeRecordExport(n); break; } } private boolean containsName(Node n) { return NodeUtil.containsType(n, Token.NAME) || NodeUtil.containsType(n, Token.GETELEM) || NodeUtil.containsType(n, Token.GETPROP); } /** * Given a node, this alias all the names in the node that need aliasing. * This is safer than just calling getQualifiedName() because it can return * null it several situations. * @param n node to alias */ private void safeAlias(Node n) { if (n.isName() || n.isGetProp()) { String name = n.getQualifiedName(); // getQualifiedName can return null in cases like bar[0].baz if (name != null) { defineAndAlias(name); return; } } if (n.isGetProp()) { // var foo = bar[0].baz; defineAndAlias(n.getLastChild().getString()); } else if (n.isAssign()) { // In case of nested assignment, we only consider the name of the // immediate neighbor. safeAlias(n.getFirstChild()); } else if (n.hasChildren()) { Node cur = n.getFirstChild(); do { safeAlias(cur); } while ((cur = cur.getNext()) != null); } else { // No name to alias } } private void maybeAliasNamesOnAssign(Node lhs, Node rhs) { if ((lhs.isName() || lhs.isGetProp()) && containsName(rhs) && !rhs.isFunction() && !rhs.isNew()) { safeAlias(lhs); safeAlias(rhs); } } private void defineAndAlias(String name) { graph.defineNameIfNotExists(name, isExtern).setAliased(true); } private void maybeRecordExport(Node call) { Preconditions.checkArgument(call.isCall()); Node getProp = call.getFirstChild(); if (!getProp.isGetProp()) { return; } String propQName = getProp.getQualifiedName(); if (propQName == null) { return; } // Keep track of calls to "call" and "apply" because they mess up the name // graph. if (propQName.endsWith(".call") || propQName.endsWith(".apply")) { graph.defineNameIfNotExists(getProp.getFirstChild().getQualifiedName(), isExtern).markExposedToCallOrApply(); } if (!"goog.exportSymbol".equals(propQName)) { return; } Node symbol = getProp.getNext(); if (!symbol.isString()) { return; } Node obj = symbol.getNext(); String qName = obj.getQualifiedName(); if (qName == null || obj.getNext() != null) { return; } graph.defineNameIfNotExists(qName, false).markExported(); } /** * @return true if n MUST be a local name reference. */ private boolean isLocalNameReference(NodeTraversal t, Node n) { // TODO(user): What happen if it is a reference to an outer local // variable (closures)? if (n.isName()) { Var v = t.getScope().getVar(n.getString()); return v != null && v.isLocal(); } return false; } /** * @return true if n MUST be a static name reference. */ private boolean isStaticNameReference(Node n, Scope scope) { Preconditions.checkArgument(n.isName() || n.isGetProp()); if (n.isName()) { return true; } String qName = n.getQualifiedName(); if (qName == null) { return false; } // TODO(user): This does not always work due to type system bugs. return scope.isDeclared(qName, true); } /** * @return true if n MUST be a prototype name reference. */ private boolean isPrototypeNameReference(Node n) { if (!n.isGetProp()) { return false; } JSType type = getType(n.getFirstChild()); if (type.isUnknownType() || type.isUnionType()) { return false; } return (type.isInstanceType() || type.autoboxesTo() != null); } private Name recordStaticNameDefinition(String name, JSType type, Node n, Node rValue) { if (getNamedContainingFunction() != graph.MAIN) { // TODO(user): if A.B() defines A.C(), there is a dependence from // A.C() -> A.B(). However, this is not important in module code motion // and will be ignored (for now). } if (type.isConstructor()) { return recordClassConstructorOrInterface( name, type.toMaybeFunctionType(), n, rValue); } else { Name symbol = graph.defineNameIfNotExists(name, isExtern); symbol.setType(type); if (n.isAssign()) { symbol.addAssignmentDeclaration(n); } else { symbol.addFunctionDeclaration(n); } return symbol; } } /** * @param assign The assignment node, null if it is just a "forward" * declaration for recording the rValue's type. */ private Name recordPrototypePropDefinition( Node qName, JSType type, @Nullable Node assign) { JSType constructor = getType(NodeUtil.getPrototypeClassName(qName)); FunctionType classType = null; String className = null; if (constructor != null && constructor.isConstructor()) { // Case where the class has been properly declared with @constructor classType = constructor.toMaybeFunctionType(); className = classType.getReferenceName(); } else { // We'll guess it is a constructor even if it didn't have @constructor classType = compiler.getTypeRegistry().getNativeFunctionType( JSTypeNative.U2U_CONSTRUCTOR_TYPE); className = NodeUtil.getPrototypeClassName(qName).getQualifiedName(); } // In case we haven't seen the function yet. recordClassConstructorOrInterface( className, classType, null, null); String qNameStr = className + ".prototype." + NodeUtil.getPrototypePropertyName(qName); Name prototypeProp = graph.defineNameIfNotExists(qNameStr, isExtern); Preconditions.checkNotNull(prototypeProp, "%s should be in the name graph as a node.", qNameStr); if (assign != null) { prototypeProp.addAssignmentDeclaration(assign); } prototypeProp.setType(type); return prototypeProp; } private Reference recordStaticNameUse( Node n, Node parent) { if (isExtern) { // Don't count reference in extern as a use. return null; } else { Reference reference = new Reference(n, parent); Name name = graph.defineNameIfNotExists(n.getQualifiedName(), isExtern); name.setType(getType(n)); graph.connect(getNamedContainingFunction(), reference, name); return reference; } } private void recordPrototypePropUse(Node n, Node parent) { Preconditions.checkArgument(n.isGetProp()); Node instance = n.getFirstChild(); JSType instanceType = getType(instance); JSType boxedType = instanceType.autoboxesTo(); instanceType = boxedType != null ? boxedType : instanceType; // Retrieves the property. ObjectType objType = instanceType.toObjectType(); Preconditions.checkState(objType != null); if (!isExtern) { // Don't count reference in extern as a use. Reference ref = new Reference(n, parent); FunctionType constructor = objType.getConstructor(); if (constructor != null) { String propName = n.getLastChild().getString(); if (!constructor.getPrototype().hasOwnProperty(propName)) { recordSuperClassPrototypePropUse(constructor, propName, ref); } // TODO(user): TightenType can help a whole lot here. recordSubclassPrototypePropUse(constructor, propName, ref); } else { recordUnknownUse(n, parent); } } } /** * Look for the super class implementation up the tree. */ private void recordSuperClassPrototypePropUse( FunctionType classType, String prop, Reference ref) { FunctionType superClass = classType.getSuperClassConstructor(); while (superClass != null) { if (superClass.getPrototype().hasOwnProperty(prop)) { graph.connect(getNamedContainingFunction(), ref, graph.defineNameIfNotExists( superClass.getReferenceName() + ".prototype." + prop, false)); return; } else { superClass = superClass.getSuperClassConstructor(); } } } /** * Conservatively assumes that all subclass implementation of this property * might be called. */ private void recordSubclassPrototypePropUse( FunctionType classType, String prop, Reference ref) { if (classType.getPrototype().hasOwnProperty(prop)) { graph.connect(getNamedContainingFunction(), ref, graph.defineNameIfNotExists( classType.getReferenceName() + ".prototype." + prop, false)); } if (classType.getSubTypes() != null) { for (FunctionType subclass : classType.getSubTypes()) { recordSubclassPrototypePropUse(subclass, prop, ref); } } } private void recordUnknownUse(Node n, Node parent) { if (isExtern) { // Don't count reference in extern as a use. return; } else { Preconditions.checkArgument(n.isGetProp()); Reference ref = new Reference(n, parent); ref.setUnknown(true); unknownNameUse.put(n.getLastChild().getString(), new NameUse(getNamedContainingFunction(), ref)); } } /** * Creates the name in the graph if it does not already exist. Also puts all * the properties and prototype properties of this name in the graph. */ private Name recordClassConstructorOrInterface( String name, FunctionType type, @Nullable Node n, @Nullable Node rhs) { Preconditions.checkArgument(type.isConstructor() || type.isInterface()); Name symbol = graph.defineNameIfNotExists(name, isExtern); if (rhs != null) { // TODO(user): record the definition. symbol.setType(getType(rhs)); if (n.isAssign()) { symbol.addAssignmentDeclaration(n); } else { symbol.addFunctionDeclaration(n); } } ObjectType prototype = type.getPrototype(); for (String prop : prototype.getOwnPropertyNames()) { graph.defineNameIfNotExists( name + ".prototype." + prop, isExtern); } return symbol; } } private void connectUnknowns() { for (GraphNode node : graph.getNodes()) { Name name = node.getValue(); String propName = name.getPropertyName(); if (propName == null) { continue; } Collection uses = unknownNameUse.get(propName); if (uses != null) { for (NameUse use : uses) { graph.connect(use.name, use.reference, name); } } } } /** * A helper to retrieve the type of a node. */ private JSType getType(Node n) { JSType type = n.getJSType(); if (type == null) { if (CONSERVATIVE) { throw new RuntimeException("Type system failed us :("); } else { return compiler.getTypeRegistry().getNativeType( JSTypeNative.UNKNOWN_TYPE); } } // Null-ability does not affect the name graph's result. return type.restrictByNotNullOrUndefined(); } /** * Mark the provided node as the current function that we are analyzing. * and add it to the stack of scopes we are inside. * * @param functionNode node representing current function. */ private void pushContainingFunction(Name functionNode) { currentFunctionStack.add(functionNode); } /** * Remove the top item off the containing function stack, and restore the * previous containing scope to the be the current containing function. */ private void popContainingFunction() { currentFunctionStack.remove(currentFunctionStack.size() - 1); } /** * Find the first containing function that's not an function expression * closure. */ private Name getNamedContainingFunction() { Name containingFn = null; int pos; for (pos = currentFunctionStack.size() - 1; pos >= 0; pos = pos - 1) { Name cf = currentFunctionStack.get(pos); if (cf != graph.UNKNOWN) { containingFn = cf; break; } } Preconditions.checkNotNull(containingFn); return containingFn; } private static class NameUse { private final Name name; private final Reference reference; private NameUse(Name name, Reference reference) { this.name = name; this.reference = reference; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameAnonymousFunctionsMapped.java0000644000175000017500000001056212115204405031314 0ustar apoapo/* * Copyright 2004 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.ImmutableMap; import com.google.common.collect.Maps; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; import java.util.logging.*; /** * Gives anonymous function names that are optimized to be small and provides a * mapping back to the original names. This makes it way easier to debug because * debuggers and stack traces use the function names. So if you have * * goog.string.htmlEscape = function(str) { * } * * It will become * * goog.string.htmlEscape = function $qv(str) { * } * * And there will be mapping from $qv to goog.string.htmlEscape * */ class NameAnonymousFunctionsMapped implements CompilerPass { private static Logger logger = Logger.getLogger( NameAnonymousFunctionsMapped.class.getName()); static final char PREFIX = '$'; static final String PREFIX_STRING = "$"; private final AbstractCompiler compiler; private final NameGenerator nameGenerator; private final VariableMap previousMap; private final Map renameMap; private int namedCount = 0; private int bytesUsed = 0; NameAnonymousFunctionsMapped( AbstractCompiler compiler, VariableMap previousMap) { this.compiler = compiler; Set reserved = previousMap != null ? previousMap.getNewNameToOriginalNameMap().keySet() : Collections.emptySet(); this.nameGenerator = new NameGenerator(reserved, PREFIX_STRING, null); this.previousMap = previousMap; this.renameMap = Maps.newHashMap(); } @Override public void process(Node externs, Node root) { AnonymousFunctionNamingCallback namingCallback = new AnonymousFunctionNamingCallback(new MappedFunctionNamer()); NodeTraversal.traverse(compiler, root, namingCallback); logger.fine("Named " + namedCount + " anon functions using " + bytesUsed + " bytes"); if (namedCount > 0) { compiler.reportCodeChange(); } } /** * Names anonymous functions. The function names don't have to be globally * unique or even locally unique. We make them somewhat unique because of a * bug in IE (and there may be other bugs we haven't found). See unit test for * more info. */ private class MappedFunctionNamer implements AnonymousFunctionNamingCallback.FunctionNamer { static final char DELIMITER = '.'; @Override public final String getName(Node node) { switch (node.getType()) { case Token.NAME: case Token.STRING: case Token.STRING_KEY: return node.getString(); default: return new CodePrinter.Builder(node).build(); } } @Override public final void setFunctionName(String name, Node fnNode) { Node fnNameNode = fnNode.getFirstChild(); String newName = getAlternateName(name); fnNameNode.setString(newName); namedCount++; bytesUsed += newName.length(); } String getAlternateName(String name) { String newName = renameMap.get(name); if (newName == null) { // Use the previously used name, if possible. if (previousMap != null) { newName = previousMap.lookupNewName(name); } if (newName == null) { // otherwise generate a new name. newName = nameGenerator.generateNextName(); } renameMap.put(name, newName); } return newName; } @Override public final String getCombinedName(String lhs, String rhs) { return lhs + DELIMITER + rhs; } } /** * Gets the function renaming map (the "answer key"). * * @return A mapping from original names to new names */ VariableMap getFunctionMap() { return new VariableMap(ImmutableMap.copyOf(renameMap)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VariableReferenceCheck.java0000644000175000017500000001702212115204405030003 0ustar apoapo/* * 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.Sets; import com.google.javascript.jscomp.ReferenceCollectingCallback.BasicBlock; import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Checks variables to see if they are referenced before their declaration, or * if they are redeclared in a way that is suspicious (i.e. not dictated by * control structures). This is a more aggressive version of {@link VarCheck}, * but it lacks the cross-module checks. * * @author kushal@google.com (Kushal Dave) */ class VariableReferenceCheck implements HotSwapCompilerPass { static final DiagnosticType UNDECLARED_REFERENCE = DiagnosticType.warning( "JSC_REFERENCE_BEFORE_DECLARE", "Variable referenced before declaration: {0}"); static final DiagnosticType REDECLARED_VARIABLE = DiagnosticType.warning( "JSC_REDECLARED_VARIABLE", "Redeclared variable: {0}"); static final DiagnosticType AMBIGUOUS_FUNCTION_DECL = DiagnosticType.disabled("AMBIGUOUS_FUNCTION_DECL", "Ambiguous use of a named function: {0}."); private final AbstractCompiler compiler; private final CheckLevel checkLevel; // NOTE(nicksantos): It's a lot faster to use a shared Set that // we clear after each method call, because the Set never gets too big. private final Set blocksWithDeclarations = Sets.newHashSet(); public VariableReferenceCheck(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; } @Override public void process(Node externs, Node root) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.hotSwapScript(scriptRoot, originalRoot); } /** * Behavior that checks variables for redeclaration or early references * just after they go out of scope. */ private class ReferenceCheckingBehavior implements Behavior { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { // TODO(bashir) In hot-swap version this means that for global scope we // only go through all global variables accessed in the modified file not // all global variables. This should be fixed. // Check all vars after finishing a scope for (Iterator it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); checkVar(v, referenceMap.getReferences(v).references); } } /** * If the variable is declared more than once in a basic block, generate a * warning. Also check if a variable is used in a given scope before it is * declared, which suggest a likely error. Relies on the fact that * references is in parse-tree order. */ private void checkVar(Var v, List references) { blocksWithDeclarations.clear(); boolean isDeclaredInScope = false; boolean isUnhoistedNamedFunction = false; Reference hoistedFn = null; // Look for hoisted functions. for (Reference reference : references) { if (reference.isHoistedFunction()) { blocksWithDeclarations.add(reference.getBasicBlock()); isDeclaredInScope = true; hoistedFn = reference; break; } else if (NodeUtil.isFunctionDeclaration( reference.getNode().getParent())) { isUnhoistedNamedFunction = true; } } for (Reference reference : references) { if (reference == hoistedFn) { continue; } BasicBlock basicBlock = reference.getBasicBlock(); boolean isDeclaration = reference.isDeclaration(); boolean allowDupe = SyntacticScopeCreator.hasDuplicateDeclarationSuppression( reference.getNode(), v); if (isDeclaration && !allowDupe) { // Look through all the declarations we've found so far, and // check if any of them are before this block. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (declaredBlock.provablyExecutesBefore(basicBlock)) { // TODO(johnlenz): Fix AST generating clients that so they would // have property StaticSourceFile attached at each node. Or // better yet, make sure the generated code never violates // the requirement to pass aggressive var check! String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, REDECLARED_VARIABLE, v.name)); break; } } } if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { // Only allow an unhoisted named function to be used within the // block it is declared. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (!declaredBlock.provablyExecutesBefore(basicBlock)) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), AMBIGUOUS_FUNCTION_DECL, v.name)); break; } } } if (!isDeclaration && !isDeclaredInScope) { // Don't check the order of refer in externs files. if (!reference.getNode().isFromExterns()) { // Special case to deal with var goog = goog || {} Node grandparent = reference.getGrandparent(); if (grandparent.isName() && grandparent.getString() == v.name) { continue; } // Only generate warnings if the scopes do not match in order // to deal with possible forward declarations and recursion if (reference.getScope() == v.scope) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, UNDECLARED_REFERENCE, v.name)); } } } if (isDeclaration) { blocksWithDeclarations.add(basicBlock); isDeclaredInScope = true; } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/UnreachableCodeElimination.java0000644000175000017500000002234712115204405030704 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.graph.GraphReachability; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * Removes dead code from a parse tree. The kinds of dead code that this pass * removes are: * - Any code following a return statement, such as the alert * call in: if (x) { return; alert('unreachable'); }. * - Statements that have no side effects, such as: * a.b.MyClass.prototype.propertyName; or true;. * That first kind of statement sometimes appears intentionally, so that * prototype properties can be annotated using JSDoc without actually * being initialized. * */ // TODO(user): Besides dead code after returns, this pass removes useless live // code such as breaks/continues/returns and stms w/out side effects. // These things don't require reachability info, consider making them their own // pass or putting them in some other, more related pass. class UnreachableCodeElimination extends AbstractPostOrderCallback implements CompilerPass, ScopedCallback { private static final Logger logger = Logger.getLogger(UnreachableCodeElimination.class.getName()); private final AbstractCompiler compiler; private final boolean removeNoOpStatements; private boolean codeChanged; UnreachableCodeElimination(AbstractCompiler compiler, boolean removeNoOpStatements) { this.compiler = compiler; this.removeNoOpStatements = removeNoOpStatements; } @Override public void exitScope(NodeTraversal t) { Scope scope = t.getScope(); Node root = scope.getRootNode(); // Computes the control flow graph. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, root); ControlFlowGraph cfg = cfa.getCfg(); new GraphReachability(cfg) .compute(cfg.getEntry().getValue()); if (scope.isLocal()) { root = root.getLastChild(); } do { codeChanged = false; NodeTraversal.traverse(compiler, root, new EliminationPass(cfg)); } while (codeChanged); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } private class EliminationPass extends AbstractShallowCallback { private final ControlFlowGraph cfg; private EliminationPass(ControlFlowGraph cfg) { this.cfg = cfg; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (parent == null || n.isFunction() || n.isScript()) { return; } DiGraphNode gNode = cfg.getDirectedGraphNode(n); if (gNode == null) { // Not in CFG. return; } if (gNode.getAnnotation() != GraphReachability.REACHABLE || (removeNoOpStatements && !NodeUtil.mayHaveSideEffects(n, compiler))) { removeDeadExprStatementSafely(n); return; } tryRemoveUnconditionalBranching(n); } /** * Tries to remove n if it is an unconditional branch node (break, continue, * or return) and the target of n is the same as the the follow of n. * That is, if removing n preserves the control flow. Also if n targets * another unconditional branch, this function will recursively try to * remove the target branch as well. The reason why we want to cascade this * removal is because we only run this pass once. If we have code such as * * break -> break -> break * * where all 3 breaks are useless, then the order of removal matters. When * we first look at the first break, we see that it branches to the 2nd * break. However, if we remove the last break, the 2nd break becomes * useless and finally the first break becomes useless as well. * * @returns The target of this jump. If the target is also useless jump, * the target of that useless jump recursively. */ @SuppressWarnings("fallthrough") private void tryRemoveUnconditionalBranching(Node n) { /* * For each unconditional branching control flow node, check to see * if the ControlFlowAnalysis.computeFollowNode of that node is same as * the branching target. If it is, the branch node is safe to be removed. * * This is not as clever as MinimizeExitPoints because it doesn't do any * if-else conversion but it handles more complicated switch statements * much more nicely. */ // If n is null the target is the end of the function, nothing to do. if (n == null) { return; } DiGraphNode gNode = cfg.getDirectedGraphNode(n); if (gNode == null) { return; } switch (n.getType()) { case Token.RETURN: if (n.hasChildren()) { break; } case Token.BREAK: case Token.CONTINUE: // We are looking for a control flow changing statement that always // branches to the same node. If after removing it control still // branches to the same node, it is safe to remove. List> outEdges = gNode.getOutEdges(); if (outEdges.size() == 1 && // If there is a next node, this jump is not useless. (n.getNext() == null || n.getNext().isFunction())) { Preconditions.checkState( outEdges.get(0).getValue() == Branch.UNCOND); Node fallThrough = computeFollowing(n); Node nextCfgNode = outEdges.get(0).getDestination().getValue(); if (nextCfgNode == fallThrough) { removeNode(n); } } } } private Node computeFollowing(Node n) { Node next = ControlFlowAnalysis.computeFollowNode(n); while (next != null && next.isBlock()) { if (next.hasChildren()) { next = next.getFirstChild(); } else { next = computeFollowing(next); } } return next; } private void removeDeadExprStatementSafely(Node n) { Node parent = n.getParent(); if (n.isEmpty() || (n.isBlock() && !n.hasChildren())) { // Not always trivial to remove, let FoldConstants work its magic later. return; } // TODO(user): This is a problem with removeNoOpStatements. // Every expression in a FOR-IN header looks side effect free on its own. if (NodeUtil.isForIn(parent)) { return; } switch (n.getType()) { // Removing an unreachable DO node is messy b/c it means we still have // to execute one iteration. If the DO's body has breaks in the middle, // it can get even more tricky and code size might actually increase. case Token.DO: return; case Token.BLOCK: // BLOCKs are used in several ways including wrapping CATCH // blocks in TRYs if (parent.isTry() && NodeUtil.isTryCatchNodeContainer(n)) { return; } break; case Token.CATCH: Node tryNode = parent.getParent(); NodeUtil.maybeAddFinally(tryNode); break; } if (n.isVar() && !n.getFirstChild().hasChildren()) { // Very unlikely case, Consider this: // File 1: {throw 1} // File 2: {var x} // The node var x is unreachable in the global scope. // Before we remove the node, redeclareVarsInsideBranch // would basically move var x to the beginning of File 2, // which resulted in zero changes to the AST but triggered // reportCodeChange(). // Instead, we should just ignore dead variable declarations. return; } removeNode(n); } private void removeNode(Node n) { codeChanged = true; NodeUtil.redeclareVarsInsideBranch(n); compiler.reportCodeChange(); if (logger.isLoggable(Level.FINE)) { logger.fine("Removing " + n.toString()); } NodeUtil.removeChild(n.getParent(), n); } } @Override public void visit(NodeTraversal t, Node n, Node parent) {} @Override public void enterScope(NodeTraversal t) {} } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ChainCalls.java0000644000175000017500000001341412115204405025503 0ustar apoapo/* * 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.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.List; import java.util.Set; /** * Chain calls to functions that return this. * */ class ChainCalls implements CompilerPass { private final AbstractCompiler compiler; private final Set badFunctionNodes = Sets.newHashSet(); private final Set goodFunctionNodes = Sets.newHashSet(); private final List callSites = Lists.newArrayList(); private SimpleDefinitionFinder defFinder; private GatherFunctions gatherFunctions = new GatherFunctions(); ChainCalls(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); NodeTraversal.traverse(compiler, root, new GatherCallSites()); for (CallSite callSite : callSites) { callSite.parent.removeChild(callSite.n); callSite.n.removeChild(callSite.callNode); callSite.nextGetPropNode.replaceChild(callSite.nextGetPropFirstChildNode, callSite.callNode); compiler.reportCodeChange(); } } /** * Determines whether a function always returns this. */ private class GatherFunctions implements ScopedCallback { @Override public void enterScope(NodeTraversal t) { ControlFlowGraph cfg = t.getControlFlowGraph(); for (DiGraphEdge s : cfg.getImplicitReturn().getInEdges()) { Node exitNode = s.getSource().getValue(); if (!exitNode.isReturn() || exitNode.getFirstChild() == null || !exitNode.getFirstChild().isThis()) { badFunctionNodes.add(t.getScopeRoot()); return; } } goodFunctionNodes.add(t.getScopeRoot()); } @Override public void exitScope(NodeTraversal t) { } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { } } private class GatherCallSites extends AbstractPostOrderCallback { /** * If the function call returns this and the next statement has the same * target expression, record the call site. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isExprResult()) { return; } Node callNode = n.getFirstChild(); if (!callNode.isCall()) { return; } Node getPropNode = callNode.getFirstChild(); if (!getPropNode.isGetProp()) { return; } Node getPropFirstChildNode = getPropNode.getFirstChild(); Collection definitions = defFinder.getDefinitionsReferencedAt(getPropNode); if (definitions == null) { return; } for (Definition definition : definitions) { Node rValue = definition.getRValue(); if (rValue == null) { return; } if (badFunctionNodes.contains(rValue)) { return; } if (!goodFunctionNodes.contains(rValue)) { NodeTraversal.traverse(compiler, rValue, gatherFunctions); if (badFunctionNodes.contains(rValue)) { return; } } } Node nextNode = n.getNext(); if (nextNode == null || !nextNode.isExprResult()) { return; } Node nextCallNode = nextNode.getFirstChild(); if (!nextCallNode.isCall()) { return; } Node nextGetPropNode = nextCallNode.getFirstChild(); if (!nextGetPropNode.isGetProp()) { return; } Node nextGetPropFirstChildNode = nextGetPropNode.getFirstChild(); if (!compiler.areNodesEqualForInlining( nextGetPropFirstChildNode, getPropFirstChildNode)) { return; } if (NodeUtil.mayEffectMutableState(getPropFirstChildNode)) { return; } // We can't chain immediately as it we wouldn't recognize further // opportunities to chain. callSites.add(new CallSite(parent, n, callNode, nextGetPropNode, nextGetPropFirstChildNode)); } } /** Records a call site to chain. */ private static class CallSite { final Node parent; final Node n; final Node callNode; final Node nextGetPropNode; final Node nextGetPropFirstChildNode; CallSite(Node parent, Node n, Node callNode, Node nextGetPropNode, Node nextGetPropFirstChildNode) { this.parent = parent; this.n = n; this.callNode = callNode; this.nextGetPropNode = nextGetPropNode; this.nextGetPropFirstChildNode = nextGetPropFirstChildNode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypeInferencePass.java0000644000175000017500000001247212115204405027074 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.Node; import java.util.Map; /** * A compiler pass to run the type inference analysis. * */ class TypeInferencePass implements CompilerPass { static final DiagnosticType DATAFLOW_ERROR = DiagnosticType.warning( "JSC_INTERNAL_ERROR_DATAFLOW", "non-monotonic data-flow analysis"); private final AbstractCompiler compiler; private final ReverseAbstractInterpreter reverseInterpreter; private Scope topScope; private MemoizedScopeCreator scopeCreator; private final Map assertionFunctionsMap; TypeInferencePass(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, Scope topScope, MemoizedScopeCreator scopeCreator) { this.compiler = compiler; this.reverseInterpreter = reverseInterpreter; this.topScope = topScope; this.scopeCreator = scopeCreator; assertionFunctionsMap = Maps.newHashMap(); for (AssertionFunctionSpec assertionFucntion : compiler.getCodingConvention().getAssertionFunctions()) { assertionFunctionsMap.put(assertionFucntion.getFunctionName(), assertionFucntion); } } /** * Main entry point for type inference when running over the whole tree. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); inferAllScopes(externsAndJs); } /** Entry point for type inference when running over part of the tree. */ void inferAllScopes(Node node) { // Type analysis happens in two major phases. // 1) Finding all the symbols. // 2) Propagating all the inferred types. // // The order of this analysis is non-obvious. In a complete inference // system, we may need to backtrack arbitrarily far. But the compile-time // costs would be unacceptable. // // We do one pass where we do typed scope creation for all scopes // in pre-order. // // Then we do a second pass where we do all type inference // (type propagation) in pre-order. // // We use a memoized scope creator so that we never create a scope // more than once. // // This will allow us to handle cases like: // var ns = {}; // (function() { /** JSDoc */ ns.method = function() {}; })(); // ns.method(); // In this code, we need to build the symbol table for the inner scope in // order to propagate the type of ns.method in the outer scope. (new NodeTraversal( compiler, new FirstScopeBuildingCallback(), scopeCreator)) .traverseWithScope(node, topScope); (new NodeTraversal( compiler, new SecondScopeBuildingCallback(), scopeCreator)) .traverseWithScope(node, topScope); } void inferScope(Node n, Scope scope) { TypeInference typeInference = new TypeInference( compiler, computeCfg(n), reverseInterpreter, scope, assertionFunctionsMap); try { typeInference.analyze(); // Resolve any new type names found during the inference. compiler.getTypeRegistry().resolveTypesInScope(scope); } catch (DataFlowAnalysis.MaxIterationsExceededException e) { compiler.report(JSError.make(n.getSourceFileName(), n, DATAFLOW_ERROR)); } } private class FirstScopeBuildingCallback extends AbstractScopedCallback { @Override public void enterScope(NodeTraversal t) { t.getScope(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private class SecondScopeBuildingCallback extends AbstractScopedCallback { @Override public void enterScope(NodeTraversal t) { // Only infer the entry root, rather than the scope root. // This ensures that incremental compilation only touches the root // that's been swapped out. inferScope(t.getCurrentNode(), t.getScope()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Do nothing } } private ControlFlowGraph computeCfg(Node n) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false); cfa.process(null, n); return cfa.getCfg(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PassFactory.java0000644000175000017500000000424212115204405025737 0ustar apoapo/* * 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; /** * A factory for creating JSCompiler passes based on the Options * injected. Contains all meta-data about compiler passes (like * whether it can be run multiple times, a human-readable name for * logging, etc.). * * @author nicksantos@google.com (Nick Santos) */ public abstract class PassFactory { private final String name; private final boolean isOneTimePass; /** * @param name The name of the pass that this factory creates. * @param isOneTimePass If true, the pass produced by this factory can * only be run once. */ protected PassFactory(String name, boolean isOneTimePass) { this.name = name; this.isOneTimePass = isOneTimePass; } /** * @return The name of this pass. */ String getName() { return name; } /** * @return Whether the pass produced by this factory can only be run once. */ boolean isOneTimePass() { return isOneTimePass; } /** * Creates a new compiler pass to be run. */ abstract CompilerPass create(AbstractCompiler compiler); /** * Any factory whose CompilerPass has a corresponding hot-swap version should * override this. * * @param compiler The compiler that can has been used to do the full compile. */ HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { // TODO(bashir): If in future most of PassFactory's in DefaultPassConfig // turns out to be DefaultPassConfig.HotSwapPassFactory, we should probably // change the implementation here by the one in HotSwapPassFactory. return null; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AliasExternals.java0000644000175000017500000006144612115204405026431 0ustar apoapo/* * Copyright 2006 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 javax.annotation.Nullable; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Arrays; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** *

AliasExternals provides wrappers and aliases for external globals and * properties to that they can be referenced by their full name only once * instead of in all use sites.

* *

The property alias pass creates function wrappers for properties that need * to be accessed externally. These function wrappers are then used by all * internal calls to the property, and the names will be compressed during the * RenamePrototypes step.

* *

Properties that are accessed externally are either system functions * (i.e. window.document), or used by JavaScript embedded on a page.

* *

Properties that are r-values are changed to use array notation with * a string that has been defined separately and can be compressed * i.e. document.window -> document[PROP_window].

* *

Properties that are l-values and can be renamed are renamed to * SETPROP_prop. I.e. node.innerHTML = '<div>Hello</div>' -> * SETPROP_innerHTML(node, '<div>hello</div>').

* *

Properties will only be renamed if they are used more than requiredUsage_ * times, as there is overhead for adding the accessor and mutator functions. * This is initialized to DEFAULT_REQUIRED_USAGE (=4), but can be * overridden.

* *

Certain usages (increment, decrement) won't be addressed, as they would * require a getprop, setprop, and custom logic, and aren't worth * optimizing.

* *

The global alias pass creates aliases for global variables and functions * that are declared or need to be used externally. These aliases are then used * throughout the code, and will be compressed during the RenameVars step.

* *

Globals are aliased by inserting code like "var GLOBAL_window = window;" * and then replacing all other uses of "window" with "GLOBAL_window."

* *

Globals that are l-values are not aliased.

* */ class AliasExternals implements CompilerPass { /** Number of times a property needs to be accessed in order to alias */ private static final int DEFAULT_REQUIRED_USAGE = 4; /** Number of times a property must be referenced in order to be aliased */ private int requiredUsage = DEFAULT_REQUIRED_USAGE; /** Minimum property size to be worth renaming */ private static final int MIN_PROP_SIZE = 4; /** * The name of the variable used for the "prototype" string value. This is * special-cased to make deobfuscated stack traces shorter and more readable * ("$MyClass$$P$$method$" rather than "$MyClass$$$PROP_prototype$method$"). * @see NameAnonymousFunctions */ static final String PROTOTYPE_PROPERTY_NAME = getArrayNotationNameFor("prototype"); /** Map of all properties that we may be renaming */ private final Map props = Maps.newHashMap(); /** Holds the properties that can be renamed to GETPROP_ */ private final List accessors = Lists.newArrayList(); /** Holds the properties that can be renamed to SETPROP_ */ private final List mutators = Lists.newArrayList(); /** * Map of node replacements - * Identity map because Node implements equals() but not hashCode() */ private final Map replacementMap = new IdentityHashMap(); /** Map of all globals that we may alias */ private final Map globals = Maps.newHashMap(); /** Reference to JS Compiler */ private final AbstractCompiler compiler; /** Reference to module inputs */ private final JSModuleGraph moduleGraph; /** Root in parse tree for adding generated nodes */ private Node defaultRoot; /** Root in each module for adding generated nodes, if using modules */ private Map moduleRoots; /** * A set of globals that can not be aliased since they may be undefined or * can cause errors */ private final Set unaliasableGlobals = Sets.newHashSet( // While "arguments" is declared as a global extern, it really only has // meaning inside function bodies and should not be aliased at a global // level. "arguments", // Eval should not be aliased, per the ECMA-262 spec section 15.1.2.1 "eval", // "NodeFilter" is not defined in IE and throws an error if you try to // do var foo = NodeFilter. "NodeFilter", // Calls to this special function are eliminated by the RenameProperties // compiler pass. "JSCompiler_renameProperty"); /** Whitelist of aliasable externs. */ private final Set aliasableGlobals = Sets.newHashSet(); /** * Creates an instance. * * @param compiler The Compiler * @param moduleGraph The graph of input modules. May be null. If given, we'll * try to push aliased externs into the deepest possible module. */ AliasExternals(AbstractCompiler compiler, JSModuleGraph moduleGraph) { this(compiler, moduleGraph, null, null); } /** * Creates an instance. * * @param compiler The Compiler * @param moduleGraph The graph of input modules. May be null. If given, we'll * try to push aliased externs into the deepest possible module. * @param unaliasableGlobals Comma-separated list of additional globals that * cannot be aliased since they may be undefined or can cause errors * (e.g. "foo,bar"). May be null or the empty string. * @param aliasableGlobals Comma-separated list of globals that * can be aliased. If provided, only this list of globals can be aliased. */ AliasExternals(AbstractCompiler compiler, JSModuleGraph moduleGraph, @Nullable String unaliasableGlobals, @Nullable String aliasableGlobals) { this.compiler = compiler; this.moduleGraph = moduleGraph; if (!Strings.isNullOrEmpty(unaliasableGlobals) && !Strings.isNullOrEmpty(aliasableGlobals)) { throw new IllegalArgumentException( "Cannot pass in both unaliasable and aliasable globals; you must " + "choose one or the other."); } if (!Strings.isNullOrEmpty(unaliasableGlobals)) { this.unaliasableGlobals.addAll( Arrays.asList(unaliasableGlobals.split(","))); } if (!Strings.isNullOrEmpty(aliasableGlobals)) { this.aliasableGlobals.addAll(Arrays.asList(aliasableGlobals.split(","))); } if (moduleGraph != null) { moduleRoots = Maps.newHashMap(); } } /** * Sets the number of times a property needs to be referenced in order to * create an alias for it. * @param usage Number of times */ public void setRequiredUsage(int usage) { this.requiredUsage = usage; } /** * Do all processing on the root node. */ @Override public void process(Node externs, Node root) { defaultRoot = root.getFirstChild(); Preconditions.checkState(defaultRoot.isScript()); aliasProperties(externs, root); aliasGlobals(externs, root); } private void aliasProperties(Node externs, Node root) { // Get the reserved names, filtered by the whitelist. NodeTraversal.traverse(compiler, externs, new GetAliasableNames(aliasableGlobals)); props.put("prototype", newSymbolForProperty("prototype")); // Find the props that can be changed NodeTraversal.traverse(compiler, root, new PropertyGatherer()); // Iterate through the reserved names, decide what to change // This could have been done during property traversal, but // This gives opportunity for review & modification if needed for (Symbol prop : props.values()) { if (prop.name.length() >= MIN_PROP_SIZE) { if (prop.accessorCount >= requiredUsage) { prop.aliasAccessor = true; } if (prop.mutatorCount >= requiredUsage) { prop.aliasMutator = true; } } } // Change the references to the property gets for (Node propInfo : accessors) { replaceAccessor(propInfo); } // Change the references to the property sets for (Node propInfo : mutators) { replaceMutator(propInfo); } // And add the accessor and mutator functions, if needed. Property names are // grouped together so that the CollapseVariableDeclarations pass can put // them in a single variable declaration statement. for (Symbol prop : props.values()) { if (prop.aliasAccessor) { addAccessorPropName(prop.name, getAddingRoot(prop.deepestModuleAccess)); } } for (Symbol prop : props.values()) { if (prop.aliasMutator) { addMutatorFunction(prop.name, getAddingRoot(prop.deepestModuleMutate)); } } } /* * Replaces a GETPROP with array notation, so that * it can be optimized. * I.e. prop.length -> prop[PROP_length] -> prop[a]; */ private void replaceAccessor(Node getPropNode) { /* * BEFORE getprop NODE... string length AFTER getelem NODE... name PROP_length */ Node propNameNode = getPropNode.getLastChild(); String propName = propNameNode.getString(); if (props.get(propName).aliasAccessor) { Node propSrc = getPropNode.getFirstChild(); getPropNode.removeChild(propSrc); Node newNameNode = IR.name(getArrayNotationNameFor(propName)); Node elemNode = IR.getelem(propSrc, newNameNode); replaceNode(getPropNode.getParent(), getPropNode, elemNode); compiler.reportCodeChange(); } } /** * Changes a.prop = b to SETPROP_prop(a, b); */ private void replaceMutator(Node getPropNode) { /* BEFORE exprstmt 1 assign 128 getprop NodeTree A string prop NODE TREE B AFTER exprstmt 1 call name SETPROP_prop NodeTree A NODE TREE B */ Node propNameNode = getPropNode.getLastChild(); Node parentNode = getPropNode.getParent(); Symbol prop = props.get(propNameNode.getString()); if (prop.aliasMutator) { Node propSrc = getPropNode.getFirstChild(); Node propDest = parentNode.getLastChild(); // Remove the orphaned children getPropNode.removeChild(propSrc); getPropNode.removeChild(propNameNode); parentNode.removeChild(propDest); // Create the call GETPROP_prop() node, using the old propSrc as the // one parameter to GETPROP_prop() call. Node callName = IR.name( getMutatorFor(propNameNode.getString())); Node call = IR.call( callName, propSrc, propDest); call.putBooleanProp(Node.FREE_CALL, true); // And replace the assign statement with the new call replaceNode(parentNode.getParent(), parentNode, call); compiler.reportCodeChange(); } } /** * Utility function to replace a Node with another node. * Keeps track of previous replacements so that if you try to replace * a child of a parent that has changed, it replaces on the new parent * @param parent Parent of node to be replaced * @param before Node to be replaced * @param after Replacement node */ private void replaceNode(Node parent, Node before, Node after) { if (replacementMap.containsKey(parent)) { parent = replacementMap.get(parent); } parent.replaceChild(before, after); replacementMap.put(before, after); } /** * Adds a string that can be used to reference properties by array [] * notation. * * PROP_prototype = 'prototype'; * * @param propName Name of property * @param root Root of output tree that function can be added to */ private void addAccessorPropName(String propName, Node root) { /* * Target: var 1 name PROP_length string length */ Node propValue = IR.string(propName); Node propNameNode = IR.name(getArrayNotationNameFor(propName)); propNameNode.addChildToFront(propValue); Node var = IR.var(propNameNode); root.addChildToFront(var); compiler.reportCodeChange(); } /** * Create set property function in JS. Output will be: * SETPROP_prop(a, b) {a.prop = b;} * * @param propName Name of property * @param root Root of output tree that function can be added to */ private void addMutatorFunction(String propName, Node root) { /* function SETPROP_prop name SETPROP_prop lp name a name b block 1 return 1 assign getprop name a string prop name b */ // Function name node String functionName = getMutatorFor(propName); // Function arguments String localPropName = getMutatorFor(propName) + "$a"; String localValueName = getMutatorFor(propName) + "$b"; // Create the function and append to front of output tree Node fnNode = IR.function( IR.name(functionName), IR.paramList(IR.name(localPropName), IR.name(localValueName)), IR.block( IR.returnNode( IR.assign( IR.getprop(IR.name(localPropName), IR.string(propName)), IR.name(localValueName))))); root.addChildToFront(fnNode); compiler.reportCodeChange(); } /** * Gets a SCRIPT node for code insertion in {@code m} or, if {@code m} is * empty, in as deep an ancestor module of {@code m} as possible. Returns * {@code this.defaultRoot} if {@code m} is null. * * @param m The module to find a root in (may be null) * @return A root node */ private Node getAddingRoot(JSModule m) { if (m != null) { Node root = moduleRoots.get(m); if (root != null) { return root; } root = compiler.getNodeForCodeInsertion(m); if (root != null) { moduleRoots.put(m, root); return root; } } return defaultRoot; } /** * Gets the aliasable names from externs.js */ private class GetAliasableNames extends AbstractPostOrderCallback { private final Set whitelist; public GetAliasableNames(final Set whitelist) { this.whitelist = whitelist; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); if (dest.isString() && (whitelist.isEmpty() || whitelist.contains(dest.getString()))) { props.put(dest.getString(), newSymbolForProperty(dest.getString())); } } } } /** * Gets references to all of the replaceable nodes, as well * as counting the usage for each property name. */ private final class PropertyGatherer extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { Node propNameNode = n.getLastChild(); if (canReplaceWithGetProp(propNameNode, n, parent)) { String name = propNameNode.getString(); props.get(name).recordAccessor(t); accessors.add(n); } if (canReplaceWithSetProp(propNameNode, n, parent)) { String name = propNameNode.getString(); props.get(name).recordMutator(t); mutators.add(n); } } } /** * Logic for when a getprop can be replaced. * Can't alias a call to eval per ECMA-262 spec section 15.1.2.1 * Can't be an assign -> no a.b = c; * Can't be inc or dec -> no a.b++; or a.b--; * Must be a GETPROP (NODE, A) where A is a reserved name * @param propNameNode Property name node * @param getPropNode GETPROP node * @param parent parent node * @return True if can be replaced */ private boolean canReplaceWithGetProp(Node propNameNode, Node getPropNode, Node parent) { boolean isCallTarget = (parent.isCall()) && (parent.getFirstChild() == getPropNode); boolean isAssignTarget = NodeUtil.isAssignmentOp(parent) && (parent.getFirstChild() == getPropNode); boolean isIncOrDec = (parent.isInc()) || (parent.isDec()); return (propNameNode.isString()) && !isAssignTarget && (!isCallTarget || !"eval".equals(propNameNode.getString())) && !isIncOrDec && props.containsKey(propNameNode.getString()); } /** * Logic for whether a setprop can be replaced. * * True if it is target of assign (i.e. foo = A.B), and B is a reserved name * @param propNameNode Property name node * @param getPropNode GETPROP node * @param parent parent node * @return True if can be replaced */ private boolean canReplaceWithSetProp(Node propNameNode, Node getPropNode, Node parent) { boolean isAssignTarget = (parent.isAssign()) && (parent.getFirstChild() == getPropNode); return (propNameNode.isString()) && isAssignTarget && props.containsKey(propNameNode.getString()); } } /** * Gets the mutator name for a property. */ private static String getMutatorFor(String prop) { return "SETPROP_" + prop; } /** * Gets the array notation name for a property. */ private static String getArrayNotationNameFor(String prop) { return "$$PROP_" + prop; } private void aliasGlobals(Node externs, Node root) { // Find all the extern globals that we should alias NodeTraversal.traverse(compiler, externs, new GetGlobals()); // Find all the globals that can be changed NodeTraversal.traverse(compiler, root, new GlobalGatherer()); // Iterate through the used globals, decide what to change. for (Symbol global : globals.values()) { if (global.mutatorCount > 0) { continue; } // We assume that each alias variable will end up compressed to two letter // names. There is also the overhead of "var xx=;" int currentBytes = global.name.length() * global.accessorCount; int aliasedBytes = 8 + global.name.length() + 2 * global.accessorCount; if (aliasedBytes < currentBytes) { global.aliasAccessor = true; } } // Change the references to the globals for (Symbol global : globals.values()) { for (Node globalUse : global.uses) { replaceGlobalUse(globalUse); } if (global.aliasAccessor) { addGlobalAliasNode(global, getAddingRoot(global.deepestModuleAccess)); } } } /** * Gets the aliasable names from externs.js */ private class GetGlobals extends NodeTraversal.AbstractShallowCallback { private void getGlobalName(NodeTraversal t, Node dest, Node parent) { if (dest.isName()) { JSDocInfo docInfo = dest.getJSDocInfo() == null ? parent.getJSDocInfo() : dest.getJSDocInfo(); boolean aliasable = !unaliasableGlobals.contains(dest.getString()) && (docInfo == null || !docInfo.isNoAlias()); if (aliasable) { String name = dest.getString(); Scope.Var var = t.getScope().getVar(name); if (var != null && !var.isLocal()) { globals.put(name, newSymbolForGlobalVar(dest)); } } } } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: getGlobalName(t, n.getFirstChild(), n); break; case Token.VAR: for (Node varChild = n.getFirstChild(); varChild != null; varChild = varChild.getNext()) { getGlobalName(t, varChild, n); } break; } } } /** * Gets references to all of the replaceable nodes, as well as counting the * usage for each global. */ private final class GlobalGatherer extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { String name = n.getString(); Scope.Var var = t.getScope().getVar(name); // It's ok for var to be null since it won't be in any scope if it's // an extern if (var != null && var.isLocal()) { return; } Symbol global = globals.get(name); if (global != null) { // If a variable is declared in both externs and normal source, // don't alias it. if (n.getParent().isVar() || n.getParent().isFunction()) { globals.remove(name); } boolean isFirst = parent.getFirstChild() == n; // If a global is being assigned to or otherwise modified, then we // don't want to alias it. // Using "new" with this global is not a mutator, but it's also // something that we want to avoid when aliasing, since we may be // dealing with external objects (e.g. ActiveXObject in MSIE) if ((NodeUtil.isAssignmentOp(parent) && isFirst) || (parent.isNew() && isFirst) || parent.isInc() || parent.isDec()) { global.recordMutator(t); } else { global.recordAccessor(t); } global.uses.add(n); } } } } /** * Replace uses of a global with its aliased name. */ private void replaceGlobalUse(Node globalUse) { String globalName = globalUse.getString(); if (globals.get(globalName).aliasAccessor) { globalUse.setString("GLOBAL_" + globalName); // None of the aliases are marked as @const. // Because we're reusing the original ref node, // we need to update it to reflect this. globalUse.putBooleanProp(Node.IS_CONSTANT_NAME, false); compiler.reportCodeChange(); } } /** * Adds an alias variable for the global: * * var GLOBAL_window = window; * * @param global Name of global * @param root Root of output tree that function can be added to */ private void addGlobalAliasNode(Symbol global, Node root) { /* * Target: var 1 name GLOBAL_window name window */ String globalName = global.name; Node globalValue = IR.name(global.name); globalValue.putBooleanProp(Node.IS_CONSTANT_NAME, global.isConstant); Node globalNameNode = IR.name("GLOBAL_" + globalName); globalNameNode.addChildToFront(globalValue); Node var = IR.var(globalNameNode); root.addChildToFront(var); compiler.reportCodeChange(); } private Symbol newSymbolForGlobalVar(Node name) { return new Symbol( name.getString(), name.getBooleanProp(Node.IS_CONSTANT_NAME)); } private Symbol newSymbolForProperty(String name) { return new Symbol(name, false); } /** Struct to hold information about properties & usage */ private class Symbol { public final String name; public int accessorCount = 0; public int mutatorCount = 0; public boolean aliasMutator = false; public boolean aliasAccessor = false; public final boolean isConstant; JSModule deepestModuleAccess = null; JSModule deepestModuleMutate = null; List uses = Lists.newArrayList(); private Symbol(String name, boolean isConstant) { this.name = name; this.isConstant = isConstant; } void recordAccessor(NodeTraversal t) { accessorCount++; if (moduleGraph != null) { deepestModuleAccess = (deepestModuleAccess == null) ? t.getModule() : moduleGraph.getDeepestCommonDependencyInclusive( t.getModule(), deepestModuleAccess); } } void recordMutator(NodeTraversal t) { mutatorCount++; if (moduleGraph != null) { deepestModuleMutate = (deepestModuleMutate == null) ? t.getModule() : moduleGraph.getDeepestCommonDependencyInclusive( t.getModule(), deepestModuleMutate); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypeInference.java0000644000175000017500000015410112115204405026241 0ustar apoapo/* * 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 static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.type.FlowScope; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.BooleanLiteralSet; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ModificationVisitor; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.TemplateTypeMap; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.UnionType; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Type inference within a script node or a function body, using the data-flow * analysis framework. * */ class TypeInference extends DataFlowAnalysis.BranchedForwardDataFlowAnalysis { // TODO(johnlenz): We no longer make this check, but we should. static final DiagnosticType FUNCTION_LITERAL_UNDEFINED_THIS = DiagnosticType.warning( "JSC_FUNCTION_LITERAL_UNDEFINED_THIS", "Function literal argument refers to undefined this argument"); private final AbstractCompiler compiler; private final JSTypeRegistry registry; private final ReverseAbstractInterpreter reverseInterpreter; private final Scope syntacticScope; private final FlowScope functionScope; private final FlowScope bottomScope; private final Map assertionFunctionsMap; // For convenience private final ObjectType unknownType; TypeInference(AbstractCompiler compiler, ControlFlowGraph cfg, ReverseAbstractInterpreter reverseInterpreter, Scope functionScope, Map assertionFunctionsMap) { super(cfg, new LinkedFlowScope.FlowScopeJoinOp()); this.compiler = compiler; this.registry = compiler.getTypeRegistry(); this.reverseInterpreter = reverseInterpreter; this.unknownType = registry.getNativeObjectType(UNKNOWN_TYPE); this.syntacticScope = functionScope; inferArguments(functionScope); this.functionScope = LinkedFlowScope.createEntryLattice(functionScope); this.assertionFunctionsMap = assertionFunctionsMap; // For each local variable declared with the VAR keyword, the entry // type is VOID. Iterator varIt = functionScope.getDeclarativelyUnboundVarsWithoutTypes(); while (varIt.hasNext()) { Var var = varIt.next(); if (isUnflowable(var)) { continue; } this.functionScope.inferSlotType( var.getName(), getNativeType(VOID_TYPE)); } this.bottomScope = LinkedFlowScope.createEntryLattice( Scope.createLatticeBottom(functionScope.getRootNode())); } /** * Infers all of a function's arguments if their types aren't declared. */ private void inferArguments(Scope functionScope) { Node functionNode = functionScope.getRootNode(); Node astParameters = functionNode.getFirstChild().getNext(); Node iifeArgumentNode = null; if (NodeUtil.isCallOrNewTarget(functionNode)) { iifeArgumentNode = functionNode.getNext(); } FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node parameterTypes = functionType.getParametersNode(); if (parameterTypes != null) { Node parameterTypeNode = parameterTypes.getFirstChild(); for (Node astParameter : astParameters.children()) { Var var = functionScope.getVar(astParameter.getString()); Preconditions.checkNotNull(var); if (var.isTypeInferred() && var.getType() == unknownType) { JSType newType = null; if (iifeArgumentNode != null) { newType = iifeArgumentNode.getJSType(); } else if (parameterTypeNode != null) { newType = parameterTypeNode.getJSType(); } if (newType != null) { var.setType(newType); astParameter.setJSType(newType); } } if (parameterTypeNode != null) { parameterTypeNode = parameterTypeNode.getNext(); } if (iifeArgumentNode != null) { iifeArgumentNode = iifeArgumentNode.getNext(); } } } } } @Override FlowScope createInitialEstimateLattice() { return bottomScope; } @Override FlowScope createEntryLattice() { return functionScope; } @Override FlowScope flowThrough(Node n, FlowScope input) { // If we have not walked a path from to , then we don't // want to infer anything about this scope. if (input == bottomScope) { return input; } FlowScope output = input.createChildFlowScope(); output = traverse(n, output); return output; } @Override @SuppressWarnings({"fallthrough", "incomplete-switch"}) List branchedFlowThrough(Node source, FlowScope input) { // NOTE(nicksantos): Right now, we just treat ON_EX edges like UNCOND // edges. If we wanted to be perfect, we'd actually JOIN all the out // lattices of this flow with the in lattice, and then make that the out // lattice for the ON_EX edge. But it's probably too expensive to be // worthwhile. FlowScope output = flowThrough(source, input); Node condition = null; FlowScope conditionFlowScope = null; BooleanOutcomePair conditionOutcomes = null; List> branchEdges = getCfg().getOutEdges(source); List result = Lists.newArrayListWithCapacity(branchEdges.size()); for (DiGraphEdge branchEdge : branchEdges) { Branch branch = branchEdge.getValue(); FlowScope newScope = output; switch (branch) { case ON_TRUE: if (NodeUtil.isForIn(source)) { // item is assigned a property name, so its type should be string. Node item = source.getFirstChild(); Node obj = item.getNext(); FlowScope informed = traverse(obj, output.createChildFlowScope()); if (item.isVar()) { item = item.getFirstChild(); } if (item.isName()) { JSType iterKeyType = getNativeType(STRING_TYPE); ObjectType objType = getJSType(obj).dereference(); JSType objIndexType = objType == null ? null : objType.getTemplateTypeMap().getTemplateType( registry.getObjectIndexKey()); if (objIndexType != null && !objIndexType.isUnknownType()) { JSType narrowedKeyType = iterKeyType.getGreatestSubtype(objIndexType); if (!narrowedKeyType.isEmptyType()) { iterKeyType = narrowedKeyType; } } redeclareSimpleVar(informed, item, iterKeyType); } newScope = informed; break; } // FALL THROUGH case ON_FALSE: if (condition == null) { condition = NodeUtil.getConditionExpression(source); if (condition == null && source.isCase()) { condition = source; // conditionFlowScope is cached from previous iterations // of the loop. if (conditionFlowScope == null) { conditionFlowScope = traverse( condition.getFirstChild(), output.createChildFlowScope()); } } } if (condition != null) { if (condition.isAnd() || condition.isOr()) { // When handling the short-circuiting binary operators, // the outcome scope on true can be different than the outcome // scope on false. // // TODO(nicksantos): The "right" way to do this is to // carry the known outcome all the way through the // recursive traversal, so that we can construct a // different flow scope based on the outcome. However, // this would require a bunch of code and a bunch of // extra computation for an edge case. This seems to be // a "good enough" approximation. // conditionOutcomes is cached from previous iterations // of the loop. if (conditionOutcomes == null) { conditionOutcomes = condition.isAnd() ? traverseAnd(condition, output.createChildFlowScope()) : traverseOr(condition, output.createChildFlowScope()); } newScope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( condition, conditionOutcomes.getOutcomeFlowScope( condition.getType(), branch == Branch.ON_TRUE), branch == Branch.ON_TRUE); } else { // conditionFlowScope is cached from previous iterations // of the loop. if (conditionFlowScope == null) { conditionFlowScope = traverse(condition, output.createChildFlowScope()); } newScope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( condition, conditionFlowScope, branch == Branch.ON_TRUE); } } break; } result.add(newScope.optimize()); } return result; } private FlowScope traverse(Node n, FlowScope scope) { switch (n.getType()) { case Token.ASSIGN: scope = traverseAssign(n, scope); break; case Token.NAME: scope = traverseName(n, scope); break; case Token.GETPROP: scope = traverseGetProp(n, scope); break; case Token.AND: scope = traverseAnd(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.OR: scope = traverseOr(n, scope).getJoinedFlowScope() .createChildFlowScope(); break; case Token.HOOK: scope = traverseHook(n, scope); break; case Token.OBJECTLIT: scope = traverseObjectLiteral(n, scope); break; case Token.CALL: scope = traverseCall(n, scope); break; case Token.NEW: scope = traverseNew(n, scope); break; case Token.ASSIGN_ADD: case Token.ADD: scope = traverseAdd(n, scope); break; case Token.POS: case Token.NEG: scope = traverse(n.getFirstChild(), scope); // Find types. n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.ARRAYLIT: scope = traverseArrayLiteral(n, scope); break; case Token.THIS: n.setJSType(scope.getTypeOfThis()); break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.BITAND: case Token.BITXOR: case Token.BITOR: case Token.MUL: case Token.SUB: case Token.DEC: case Token.INC: case Token.BITNOT: scope = traverseChildren(n, scope); n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.PARAM_LIST: scope = traverse(n.getFirstChild(), scope); n.setJSType(getJSType(n.getFirstChild())); break; case Token.COMMA: scope = traverseChildren(n, scope); n.setJSType(getJSType(n.getLastChild())); break; case Token.TYPEOF: scope = traverseChildren(n, scope); n.setJSType(getNativeType(STRING_TYPE)); break; case Token.DELPROP: case Token.LT: case Token.LE: case Token.GT: case Token.GE: case Token.NOT: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.INSTANCEOF: case Token.IN: scope = traverseChildren(n, scope); n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.GETELEM: scope = traverseGetElem(n, scope); break; case Token.EXPR_RESULT: scope = traverseChildren(n, scope); if (n.getFirstChild().isGetProp()) { ensurePropertyDeclared(n.getFirstChild()); } break; case Token.SWITCH: scope = traverse(n.getFirstChild(), scope); break; case Token.RETURN: scope = traverseReturn(n, scope); break; case Token.VAR: case Token.THROW: scope = traverseChildren(n, scope); break; case Token.CATCH: scope = traverseCatch(n, scope); break; case Token.CAST: scope = traverseChildren(n, scope); break; } // TODO(johnlenz): remove this after the CAST node change has shaken out. if (!n.isFunction()) { JSDocInfo info = n.getJSDocInfo(); if (info != null && info.hasType()) { JSType castType = info.getType().evaluate(syntacticScope, registry); // A stubbed type declaration on a qualified name should take // effect for all subsequent accesses of that name, // so treat it the same as an assign to that name. if (n.isQualifiedName() && n.getParent().isExprResult()) { updateScopeForTypeChange(scope, n, n.getJSType(), castType); } n.setJSType(castType); } } return scope; } /** * Traverse a return value. */ private FlowScope traverseReturn(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node retValue = n.getFirstChild(); if (retValue != null) { JSType type = functionScope.getRootNode().getJSType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { inferPropertyTypesToMatchConstraint( retValue.getJSType(), fnType.getReturnType()); } } } return scope; } /** * Any value can be thrown, so it's really impossible to determine the type * of a CATCH param. Treat it as the UNKNOWN type. */ private FlowScope traverseCatch(Node catchNode, FlowScope scope) { Node name = catchNode.getFirstChild(); JSType type; // If the catch expression name was declared in the catch use that type, // otherwise use "unknown". JSDocInfo info = name.getJSDocInfo(); if (info != null && info.hasType()) { type = info.getType().evaluate(syntacticScope, registry); } else { type = getNativeType(JSTypeNative.UNKNOWN_TYPE); } redeclareSimpleVar(scope, name, type); name.setJSType(type); return scope; } private FlowScope traverseAssign(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = n.getLastChild(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = getJSType(right); n.setJSType(rightType); updateScopeForTypeChange(scope, left, leftType, rightType); return scope; } /** * Updates the scope according to the result of a type change, like * an assignment or a type cast. */ private void updateScopeForTypeChange( FlowScope scope, Node left, JSType leftType, JSType resultType) { Preconditions.checkNotNull(resultType); switch (left.getType()) { case Token.NAME: String varName = left.getString(); Var var = syntacticScope.getVar(varName); // When looking at VAR initializers for declared VARs, we trust // the declared type over the type it's being initialized to. // This has two purposes: // 1) We avoid re-declaring declared variables so that built-in // types defined in externs are not redeclared. // 2) When there's a lexical closure like // /** @type {?string} */ var x = null; // function f() { x = 'xyz'; } // the inference will ignore the lexical closure, // which is just wrong. This bug needs to be fixed eventually. boolean isVarDeclaration = left.hasChildren(); if (!isVarDeclaration || var == null || var.isTypeInferred()) { redeclareSimpleVar(scope, left, resultType); } left.setJSType(isVarDeclaration || leftType == null ? resultType : null); if (var != null && var.isTypeInferred()) { JSType oldType = var.getType(); var.setType(oldType == null ? resultType : oldType.getLeastSupertype(resultType)); } break; case Token.GETPROP: String qualifiedName = left.getQualifiedName(); if (qualifiedName != null) { scope.inferQualifiedSlot(left, qualifiedName, leftType == null ? unknownType : leftType, resultType); } left.setJSType(resultType); ensurePropertyDefined(left, resultType); break; } } /** * Defines a property if the property has not been defined yet. */ private void ensurePropertyDefined(Node getprop, JSType rightType) { String propName = getprop.getLastChild().getString(); Node obj = getprop.getFirstChild(); JSType nodeType = getJSType(obj); ObjectType objectType = ObjectType.cast( nodeType.restrictByNotNullOrUndefined()); if (objectType == null) { registry.registerPropertyOnType(propName, nodeType); } else { // Don't add the property to @struct objects outside a constructor if (nodeType.isStruct() && !objectType.hasProperty(propName)) { if (!(obj.isThis() && getJSType(syntacticScope.getRootNode()).isConstructor())) { return; } } if (ensurePropertyDeclaredHelper(getprop, objectType)) { return; } if (!objectType.isPropertyTypeDeclared(propName)) { // We do not want a "stray" assign to define an inferred property // for every object of this type in the program. So we use a heuristic // approach to determine whether to infer the property. // // 1) If the property is already defined, join it with the previously // inferred type. // 2) If this isn't an instance object, define it. // 3) If the property of an object is being assigned in the constructor, // define it. // 4) If this is a stub, define it. // 5) Otherwise, do not define the type, but declare it in the registry // so that we can use it for missing property checks. if (objectType.hasProperty(propName) || !objectType.isInstanceType()) { if ("prototype".equals(propName)) { objectType.defineDeclaredProperty(propName, rightType, getprop); } else { objectType.defineInferredProperty(propName, rightType, getprop); } } else if (obj.isThis() && getJSType(syntacticScope.getRootNode()).isConstructor()) { objectType.defineInferredProperty(propName, rightType, getprop); } else { registry.registerPropertyOnType(propName, objectType); } } } } /** * Defines a declared property if it has not been defined yet. * * This handles the case where a property is declared on an object where * the object type is inferred, and so the object type will not * be known in {@code TypedScopeCreator}. */ private void ensurePropertyDeclared(Node getprop) { ObjectType ownerType = ObjectType.cast( getJSType(getprop.getFirstChild()).restrictByNotNullOrUndefined()); if (ownerType != null) { ensurePropertyDeclaredHelper(getprop, ownerType); } } /** * Declares a property on its owner, if necessary. * @return True if a property was declared. */ private boolean ensurePropertyDeclaredHelper( Node getprop, ObjectType objectType) { String propName = getprop.getLastChild().getString(); String qName = getprop.getQualifiedName(); if (qName != null) { Var var = syntacticScope.getVar(qName); if (var != null && !var.isTypeInferred()) { // Handle normal declarations that could not be addressed earlier. if (propName.equals("prototype") || // Handle prototype declarations that could not be addressed earlier. (!objectType.hasOwnProperty(propName) && (!objectType.isInstanceType() || (var.isExtern() && !objectType.isNativeObjectType())))) { return objectType.defineDeclaredProperty( propName, var.getType(), getprop); } } } return false; } private FlowScope traverseName(Node n, FlowScope scope) { String varName = n.getString(); Node value = n.getFirstChild(); JSType type = n.getJSType(); if (value != null) { scope = traverse(value, scope); updateScopeForTypeChange(scope, n, n.getJSType() /* could be null */, getJSType(value)); return scope; } else { StaticSlot var = scope.getSlot(varName); if (var != null) { // There are two situations where we don't want to use type information // from the scope, even if we have it. // 1) The var is escaped and assigned in an inner scope, e.g., // function f() { var x = 3; function g() { x = null } (x); } boolean isInferred = var.isTypeInferred(); boolean unflowable = isInferred && isUnflowable(syntacticScope.getVar(varName)); // 2) We're reading type information from another scope for an // inferred variable. That variable is assigned more than once, // and we can't know which type we're getting. // // var t = null; function f() { (t); } doStuff(); t = {}; // // Notice that this heuristic isn't perfect. For example, you might // have: // // function f() { (t); } f(); var t = 3; // // In this case, we would infer the first reference to t as // type {number}, even though it's undefined. boolean nonLocalInferredSlot = false; if (isInferred && syntacticScope.isLocal()) { Var maybeOuterVar = syntacticScope.getParent().getVar(varName); if (var == maybeOuterVar && !maybeOuterVar.isMarkedAssignedExactlyOnce()) { nonLocalInferredSlot = true; } } if (!unflowable && !nonLocalInferredSlot) { type = var.getType(); if (type == null) { type = unknownType; } } } } n.setJSType(type); return scope; } /** Traverse each element of the array. */ private FlowScope traverseArrayLiteral(Node n, FlowScope scope) { scope = traverseChildren(n, scope); n.setJSType(getNativeType(ARRAY_TYPE)); return scope; } private FlowScope traverseObjectLiteral(Node n, FlowScope scope) { JSType type = n.getJSType(); Preconditions.checkNotNull(type); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { scope = traverse(name.getFirstChild(), scope); } // Object literals can be reflected on other types, or changed with // type casts. // See CodingConvention#getObjectLiteralCase and goog.object.reflect. // Ignore these types of literals. // TODO(nicksantos): There should be an "anonymous object" type that // we can check for here. ObjectType objectType = ObjectType.cast(type); if (objectType == null) { return scope; } boolean hasLendsName = n.getJSDocInfo() != null && n.getJSDocInfo().getLendsName() != null; if (objectType.hasReferenceName() && !hasLendsName) { return scope; } String qObjName = NodeUtil.getBestLValueName( NodeUtil.getBestLValue(n)); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { String memberName = NodeUtil.getObjectLitKeyName(name); if (memberName != null) { JSType rawValueType = name.getFirstChild().getJSType(); JSType valueType = NodeUtil.getObjectLitKeyTypeFromValueType( name, rawValueType); if (valueType == null) { valueType = unknownType; } objectType.defineInferredProperty(memberName, valueType, name); // Do normal flow inference if this is a direct property assignment. if (qObjName != null && name.isStringKey()) { String qKeyName = qObjName + "." + memberName; Var var = syntacticScope.getVar(qKeyName); JSType oldType = var == null ? null : var.getType(); if (var != null && var.isTypeInferred()) { var.setType(oldType == null ? valueType : oldType.getLeastSupertype(oldType)); } scope.inferQualifiedSlot(name, qKeyName, oldType == null ? unknownType : oldType, valueType); } } else { n.setJSType(unknownType); } } return scope; } private FlowScope traverseAdd(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = left.getNext(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = right.getJSType(); JSType type = unknownType; if (leftType != null && rightType != null) { boolean leftIsUnknown = leftType.isUnknownType(); boolean rightIsUnknown = rightType.isUnknownType(); if (leftIsUnknown && rightIsUnknown) { type = unknownType; } else if ((!leftIsUnknown && leftType.isString()) || (!rightIsUnknown && rightType.isString())) { type = getNativeType(STRING_TYPE); } else if (leftIsUnknown || rightIsUnknown) { type = unknownType; } else if (isAddedAsNumber(leftType) && isAddedAsNumber(rightType)) { type = getNativeType(NUMBER_TYPE); } else { type = registry.createUnionType(STRING_TYPE, NUMBER_TYPE); } } n.setJSType(type); if (n.isAssignAdd()) { updateScopeForTypeChange(scope, left, leftType, type); } return scope; } private boolean isAddedAsNumber(JSType type) { return type.isSubtype(registry.createUnionType(VOID_TYPE, NULL_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE)); } private FlowScope traverseHook(Node n, FlowScope scope) { Node condition = n.getFirstChild(); Node trueNode = condition.getNext(); Node falseNode = n.getLastChild(); // verify the condition scope = traverse(condition, scope); // reverse abstract interpret the condition to produce two new scopes FlowScope trueScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, true); FlowScope falseScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, false); // traverse the true node with the trueScope traverse(trueNode, trueScope.createChildFlowScope()); // traverse the false node with the falseScope traverse(falseNode, falseScope.createChildFlowScope()); // meet true and false nodes' types and assign JSType trueType = trueNode.getJSType(); JSType falseType = falseNode.getJSType(); if (trueType != null && falseType != null) { n.setJSType(trueType.getLeastSupertype(falseType)); } else { n.setJSType(null); } return scope.createChildFlowScope(); } private FlowScope traverseCall(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node left = n.getFirstChild(); JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); if (functionType.isFunctionType()) { FunctionType fnType = functionType.toMaybeFunctionType(); n.setJSType(fnType.getReturnType()); backwardsInferenceFromCallSite(n, fnType); } else if (functionType.isEquivalentTo( getNativeType(CHECKED_UNKNOWN_TYPE))) { n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); } scope = tightenTypesAfterAssertions(scope, n); return scope; } private FlowScope tightenTypesAfterAssertions(FlowScope scope, Node callNode) { Node left = callNode.getFirstChild(); Node firstParam = left.getNext(); AssertionFunctionSpec assertionFunctionSpec = assertionFunctionsMap.get(left.getQualifiedName()); if (assertionFunctionSpec == null || firstParam == null) { return scope; } Node assertedNode = assertionFunctionSpec.getAssertedParam(firstParam); if (assertedNode == null) { return scope; } JSType assertedType = assertionFunctionSpec.getAssertedType( callNode, registry); String assertedNodeName = assertedNode.getQualifiedName(); JSType narrowed; // Handle assertions that enforce expressions evaluate to true. if (assertedType == null) { // Handle arbitrary expressions within the assert. scope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( assertedNode, scope, true); // Build the result of the assertExpression narrowed = getJSType(assertedNode).restrictByNotNullOrUndefined(); } else { // Handle assertions that enforce expressions are of a certain type. JSType type = getJSType(assertedNode); narrowed = type.getGreatestSubtype(assertedType); if (assertedNodeName != null && type.differsFrom(narrowed)) { scope = narrowScope(scope, assertedNode, narrowed); } } callNode.setJSType(narrowed); return scope; } private FlowScope narrowScope(FlowScope scope, Node node, JSType narrowed) { if (node.isThis()) { // "this" references don't need to be modeled in the control flow graph. return scope; } scope = scope.createChildFlowScope(); if (node.isGetProp()) { scope.inferQualifiedSlot( node, node.getQualifiedName(), getJSType(node), narrowed); } else { redeclareSimpleVar(scope, node, narrowed); } return scope; } /** * We only do forward type inference. We do not do full backwards * type inference. * * In other words, if we have, * * var x = f(); * g(x); * * a forward type-inference engine would try to figure out the type * of "x" from the return type of "f". A backwards type-inference engine * would try to figure out the type of "x" from the parameter type of "g". * * However, there are a few special syntactic forms where we do some * some half-assed backwards type-inference, because programmers * expect it in this day and age. To take an example from Java, * * List x = Lists.newArrayList(); * * The Java compiler will be able to infer the generic type of the List * returned by newArrayList(). * * In much the same way, we do some special-case backwards inference for * JS. Those cases are enumerated here. */ private void backwardsInferenceFromCallSite(Node n, FunctionType fnType) { boolean updatedFnType = inferTemplatedTypesForCall(n, fnType); if (updatedFnType) { fnType = n.getFirstChild().getJSType().toMaybeFunctionType(); } updateTypeOfParameters(n, fnType); updateBind(n); } /** * When "bind" is called on a function, we infer the type of the returned * "bound" function by looking at the number of parameters in the call site. */ private void updateBind(Node n) { CodingConvention.Bind bind = compiler.getCodingConvention().describeFunctionBind(n, true); if (bind == null) { return; } FunctionType callTargetFn = getJSType(bind.target) .restrictByNotNullOrUndefined().toMaybeFunctionType(); if (callTargetFn == null) { return; } n.setJSType( callTargetFn.getBindReturnType( // getBindReturnType expects the 'this' argument to be included. bind.getBoundParameterCount() + 1)); } /** * For functions with function parameters, type inference will set the type of * a function literal argument from the function parameter type. */ private void updateTypeOfParameters(Node n, FunctionType fnType) { int i = 0; int childCount = n.getChildCount(); for (Node iParameter : fnType.getParameters()) { if (i + 1 >= childCount) { // TypeCheck#visitParametersList will warn so we bail. return; } JSType iParameterType = getJSType(iParameter); Node iArgument = n.getChildAtIndex(i + 1); JSType iArgumentType = getJSType(iArgument); inferPropertyTypesToMatchConstraint(iArgumentType, iParameterType); // TODO(johnlenz): Filter out non-function types // (such as null and undefined) as // we only care about FUNCTION subtypes here. JSType restrictedParameter = iParameterType .restrictByNotNullOrUndefined() .toMaybeFunctionType(); if (restrictedParameter != null) { if (iArgument.isFunction() && iArgumentType.isFunctionType() && iArgument.getJSDocInfo() == null) { iArgument.setJSType(restrictedParameter); } } i++; } } private Map inferTemplateTypesFromParameters( FunctionType fnType, Node call) { if (fnType.getTemplateTypeMap().getTemplateKeys().isEmpty()) { return Collections.emptyMap(); } Map resolvedTypes = Maps.newIdentityHashMap(); Node callTarget = call.getFirstChild(); if (NodeUtil.isGet(callTarget)) { Node obj = callTarget.getFirstChild(); maybeResolveTemplatedType( fnType.getTypeOfThis(), getJSType(obj), resolvedTypes); } if (call.hasMoreThanOneChild()) { maybeResolveTemplateTypeFromNodes( fnType.getParameters(), call.getChildAtIndex(1).siblings(), resolvedTypes); } return resolvedTypes; } private void maybeResolveTemplatedType( JSType paramType, JSType argType, Map resolvedTypes) { if (paramType.isTemplateType()) { // @param {T} resolvedTemplateType( resolvedTypes, paramType.toMaybeTemplateType(), argType); } else if (paramType.isUnionType()) { // @param {Array.|NodeList|Arguments|{length:number}} UnionType unionType = paramType.toMaybeUnionType(); for (JSType alernative : unionType.getAlternates()) { maybeResolveTemplatedType(alernative, argType, resolvedTypes); } } else if (paramType.isFunctionType()) { FunctionType paramFunctionType = paramType.toMaybeFunctionType(); FunctionType argFunctionType = argType .restrictByNotNullOrUndefined() .collapseUnion() .toMaybeFunctionType(); if (argFunctionType != null && argFunctionType.isSubtype(paramType)) { // infer from return type of the function type maybeResolveTemplatedType( paramFunctionType.getTypeOfThis(), argFunctionType.getTypeOfThis(), resolvedTypes); // infer from return type of the function type maybeResolveTemplatedType( paramFunctionType.getReturnType(), argFunctionType.getReturnType(), resolvedTypes); // infer from parameter types of the function type maybeResolveTemplateTypeFromNodes( paramFunctionType.getParameters(), argFunctionType.getParameters(), resolvedTypes); } } else if (paramType.isTemplatizedType()) { // @param {Array.} ObjectType referencedParamType = paramType .toMaybeTemplatizedType() .getReferencedType(); JSType argObjectType = argType .restrictByNotNullOrUndefined() .collapseUnion(); if (argObjectType.isSubtype(referencedParamType)) { // If the argument type is a subtype of the parameter type, resolve any // template types amongst their templatized types. TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap(); TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap(); for (TemplateType key : paramTypeMap.getTemplateKeys()) { maybeResolveTemplatedType( paramTypeMap.getTemplateType(key), argTypeMap.getTemplateType(key), resolvedTypes); } } } } private void maybeResolveTemplateTypeFromNodes( Iterable declParams, Iterable callParams, Map resolvedTypes) { maybeResolveTemplateTypeFromNodes( declParams.iterator(), callParams.iterator(), resolvedTypes); } private void maybeResolveTemplateTypeFromNodes( Iterator declParams, Iterator callParams, Map resolvedTypes) { while (declParams.hasNext() && callParams.hasNext()) { Node declParam = declParams.next(); maybeResolveTemplatedType( getJSType(declParam), getJSType(callParams.next()), resolvedTypes); if (declParam.isVarArgs()) { while (callParams.hasNext()) { maybeResolveTemplatedType( getJSType(declParam), getJSType(callParams.next()), resolvedTypes); } } } } private void resolvedTemplateType( Map map, TemplateType template, JSType resolved) { JSType previous = map.get(template); if (!resolved.isUnknownType()) { if (previous == null) { map.put(template, resolved); } else { JSType join = previous.getLeastSupertype(resolved); map.put(template, join); } } } private static class TemplateTypeReplacer extends ModificationVisitor { private final Map replacements; private final JSTypeRegistry registry; boolean madeChanges = false; TemplateTypeReplacer( JSTypeRegistry registry, Map replacements) { super(registry); this.registry = registry; this.replacements = replacements; } @Override public JSType caseTemplateType(TemplateType type) { madeChanges = true; JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { if (fnType.getTemplateTypeMap().getTemplateKeys().isEmpty()) { return false; } // Try to infer the template types Map inferred = inferTemplateTypesFromParameters( fnType, n); // Replace all template types. If we couldn't find a replacement, we // replace it with UNKNOWN. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return replacer.madeChanges; } private FlowScope traverseNew(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node constructor = n.getFirstChild(); JSType constructorType = constructor.getJSType(); JSType type = null; if (constructorType != null) { constructorType = constructorType.restrictByNotNullOrUndefined(); if (constructorType.isUnknownType()) { type = unknownType; } else { FunctionType ct = constructorType.toMaybeFunctionType(); if (ct == null && constructorType instanceof FunctionType) { // If constructorType is a NoObjectType, then toMaybeFunctionType will // return null. But NoObjectType implements the FunctionType // interface, precisely because it can validly construct objects. ct = (FunctionType) constructorType; } if (ct != null && ct.isConstructor()) { type = ct.getInstanceType(); backwardsInferenceFromCallSite(n, ct); } } } n.setJSType(type); return scope; } private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, true); } private FlowScope traverseChildren(Node n, FlowScope scope) { for (Node el = n.getFirstChild(); el != null; el = el.getNext()) { scope = traverse(el, scope); } return scope; } private FlowScope traverseGetElem(Node n, FlowScope scope) { scope = traverseChildren(n, scope); JSType type = getJSType(n.getFirstChild()).restrictByNotNullOrUndefined(); TemplateTypeMap typeMap = type.getTemplateTypeMap(); if (typeMap.hasTemplateType(registry.getObjectElementKey())) { n.setJSType(typeMap.getTemplateType(registry.getObjectElementKey())); } return dereferencePointer(n.getFirstChild(), scope); } private FlowScope traverseGetProp(Node n, FlowScope scope) { Node objNode = n.getFirstChild(); Node property = n.getLastChild(); scope = traverseChildren(n, scope); n.setJSType( getPropertyType( objNode.getJSType(), property.getString(), n, scope)); return dereferencePointer(n.getFirstChild(), scope); } /** * Suppose X is an object with inferred properties. * Suppose also that X is used in a way where it would only type-check * correctly if some of those properties are widened. * Then we should be polite and automatically widen X's properties for him. * * For a concrete example, consider: * param x {{prop: (number|undefined)}} * function f(x) {} * f({}); * * If we give the anonymous object an inferred property of (number|undefined), * then this code will type-check appropriately. */ private void inferPropertyTypesToMatchConstraint( JSType type, JSType constraint) { if (type == null || constraint == null) { return; } type.matchConstraint(constraint); } /** * If we access a property of a symbol, then that symbol is not * null or undefined. */ private FlowScope dereferencePointer(Node n, FlowScope scope) { if (n.isQualifiedName()) { JSType type = getJSType(n); JSType narrowed = type.restrictByNotNullOrUndefined(); if (type != narrowed) { scope = narrowScope(scope, n, narrowed); } } return scope; } private JSType getPropertyType(JSType objType, String propName, Node n, FlowScope scope) { // We often have a couple of different types to choose from for the // property. Ordered by accuracy, we have // 1) A locally inferred qualified name (which is in the FlowScope) // 2) A globally declared qualified name (which is in the FlowScope) // 3) A property on the owner type (which is on objType) // 4) A name in the type registry (as a last resort) JSType propertyType = null; boolean isLocallyInferred = false; // Scopes sometimes contain inferred type info about qualified names. String qualifiedName = n.getQualifiedName(); StaticSlot var = scope.getSlot(qualifiedName); if (var != null) { JSType varType = var.getType(); if (varType != null) { boolean isDeclared = !var.isTypeInferred(); isLocallyInferred = (var != syntacticScope.getSlot(qualifiedName)); if (isDeclared || isLocallyInferred) { propertyType = varType; } } } if (propertyType == null && objType != null) { JSType foundType = objType.findPropertyType(propName); if (foundType != null) { propertyType = foundType; } } if ((propertyType == null || propertyType.isUnknownType()) && qualifiedName != null) { // If we find this node in the registry, then we can infer its type. ObjectType regType = ObjectType.cast(registry.getType(qualifiedName)); if (regType != null) { propertyType = regType.getConstructor(); } } if (propertyType == null) { return unknownType; } else if (propertyType.isEquivalentTo(unknownType) && isLocallyInferred) { // If the type has been checked in this scope, // then use CHECKED_UNKNOWN_TYPE instead to indicate that. return getNativeType(CHECKED_UNKNOWN_TYPE); } else { return propertyType; } } private BooleanOutcomePair traverseOr(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, false); } private BooleanOutcomePair traverseShortCircuitingBinOp( Node n, FlowScope scope, boolean condition) { Node left = n.getFirstChild(); Node right = n.getLastChild(); // type the left node BooleanOutcomePair leftLiterals = traverseWithinShortCircuitingBinOp(left, scope.createChildFlowScope()); JSType leftType = left.getJSType(); // reverse abstract interpret the left node to produce the correct // scope in which to verify the right node FlowScope rightScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( left, leftLiterals.getOutcomeFlowScope(left.getType(), condition), condition); // type the right node BooleanOutcomePair rightLiterals = traverseWithinShortCircuitingBinOp( right, rightScope.createChildFlowScope()); JSType rightType = right.getJSType(); JSType type; BooleanOutcomePair literals; if (leftType != null && rightType != null) { leftType = leftType.getRestrictedTypeGivenToBooleanOutcome(!condition); if (leftLiterals.toBooleanOutcomes == BooleanLiteralSet.get(!condition)) { // Use the restricted left type, since the right side never gets // evaluated. type = leftType; literals = leftLiterals; } else { // Use the join of the restricted left type knowing the outcome of the // ToBoolean predicate and of the right type. type = leftType.getLeastSupertype(rightType); literals = getBooleanOutcomePair(leftLiterals, rightLiterals, condition); } // Exclude the boolean type if the literal set is empty because a boolean // can never actually be returned. if (literals.booleanValues == BooleanLiteralSet.EMPTY && getNativeType(BOOLEAN_TYPE).isSubtype(type)) { // Exclusion only make sense for a union type. if (type.isUnionType()) { type = type.toMaybeUnionType().getRestrictedUnion( getNativeType(BOOLEAN_TYPE)); } } } else { type = null; literals = new BooleanOutcomePair( BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, leftLiterals.getJoinedFlowScope(), rightLiterals.getJoinedFlowScope()); } n.setJSType(type); return literals; } private BooleanOutcomePair traverseWithinShortCircuitingBinOp(Node n, FlowScope scope) { switch (n.getType()) { case Token.AND: return traverseAnd(n, scope); case Token.OR: return traverseOr(n, scope); default: scope = traverse(n, scope); return newBooleanOutcomePair(n.getJSType(), scope); } } /** * Infers the boolean outcome pair that can be taken by a * short-circuiting binary operation ({@code &&} or {@code ||}). * @see #getBooleanOutcomes(BooleanLiteralSet, BooleanLiteralSet, boolean) */ BooleanOutcomePair getBooleanOutcomePair(BooleanOutcomePair left, BooleanOutcomePair right, boolean condition) { return new BooleanOutcomePair( getBooleanOutcomes(left.toBooleanOutcomes, right.toBooleanOutcomes, condition), getBooleanOutcomes(left.booleanValues, right.booleanValues, condition), left.getJoinedFlowScope(), right.getJoinedFlowScope()); } /** * Infers the boolean literal set that can be taken by a * short-circuiting binary operation ({@code &&} or {@code ||}). * @param left the set of possible {@code ToBoolean} predicate results for * the expression on the left side of the operator * @param right the set of possible {@code ToBoolean} predicate results for * the expression on the right side of the operator * @param condition the left side {@code ToBoolean} predicate result that * causes the right side to get evaluated (i.e. not short-circuited) * @return a set of possible {@code ToBoolean} predicate results for the * entire expression */ static BooleanLiteralSet getBooleanOutcomes(BooleanLiteralSet left, BooleanLiteralSet right, boolean condition) { return right.union(left.intersection(BooleanLiteralSet.get(!condition))); } /** * When traversing short-circuiting binary operations, we need to keep track * of two sets of boolean literals: * 1. {@code toBooleanOutcomes}: boolean literals as converted from any types, * 2. {@code booleanValues}: boolean literals from just boolean types. */ private final class BooleanOutcomePair { final BooleanLiteralSet toBooleanOutcomes; final BooleanLiteralSet booleanValues; // The scope if only half of the expression executed, when applicable. final FlowScope leftScope; // The scope when the whole expression executed. final FlowScope rightScope; // The scope when we don't know how much of the expression is executed. FlowScope joinedScope = null; BooleanOutcomePair( BooleanLiteralSet toBooleanOutcomes, BooleanLiteralSet booleanValues, FlowScope leftScope, FlowScope rightScope) { this.toBooleanOutcomes = toBooleanOutcomes; this.booleanValues = booleanValues; this.leftScope = leftScope; this.rightScope = rightScope; } /** * Gets the safe estimated scope without knowing if all of the * subexpressions will be evaluated. */ FlowScope getJoinedFlowScope() { if (joinedScope == null) { if (leftScope == rightScope) { joinedScope = rightScope; } else { joinedScope = join(leftScope, rightScope); } } return joinedScope; } /** * Gets the outcome scope if we do know the outcome of the entire * expression. */ FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) { if (nodeType == Token.AND && outcome || nodeType == Token.OR && !outcome) { // We know that the whole expression must have executed. return rightScope; } else { return getJoinedFlowScope(); } } } private BooleanOutcomePair newBooleanOutcomePair( JSType jsType, FlowScope flowScope) { if (jsType == null) { return new BooleanOutcomePair( BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope); } return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(), registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ? BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY, flowScope, flowScope); } private void redeclareSimpleVar( FlowScope scope, Node nameNode, JSType varType) { Preconditions.checkState(nameNode.isName()); String varName = nameNode.getString(); if (varType == null) { varType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } if (isUnflowable(syntacticScope.getVar(varName))) { return; } scope.inferSlotType(varName, varType); } private boolean isUnflowable(Var v) { return v != null && v.isLocal() && v.isMarkedEscaped() && // It's OK to flow a variable in the scope where it's escaped. v.getScope() == syntacticScope; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return unknownType; } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JvmMetrics.java0000644000175000017500000002317412115204405025571 0ustar apoapo/* * Copyright 2012 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 java.io.PrintStream; import java.lang.management.CompilationMXBean; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.List; import java.util.StringTokenizer; /** * A class to report jvm/jmx statistics. * Borrowed from: * http://code.google.com/p/dart/source/browse/trunk/dart/compiler/java/com/google/dart/compiler/metrics/JvmMetrics.java */ class JvmMetrics { private static int TABULAR_COLON_POS = 40; private static long ONE_KILO_BYTE = 1L << 10L; private static long ONE_MEGA_BYTE = 1L << 20L; private static long ONE_GIGA_BYTE = 1L << 30L; public static void maybeWriteJvmMetrics(PrintStream out, String options) { if (options == null) { return; } boolean verboseMode = false; boolean prettyMode = false; StringTokenizer st = new StringTokenizer(options, ":"); // options are grouped in order 'detail:format:types' if (st.hasMoreTokens()) { String mode = st.nextToken(); if (mode.equalsIgnoreCase("verbose")) { verboseMode = true; } } if (st.hasMoreTokens()) { String mode = st.nextToken(); if (mode.equalsIgnoreCase("pretty")) { prettyMode = true; } } if (st.hasMoreTokens()) { while (st.hasMoreTokens()) { String types = st.nextToken(); StringTokenizer typeSt = new StringTokenizer(types, ","); while (typeSt.hasMoreElements()) { String type = typeSt.nextToken(); writeMetrics(out, type, verboseMode, prettyMode); } } } else { // the default writeMetrics(out, "all", verboseMode, prettyMode); } } private static void writeMetrics( PrintStream out, String type, boolean verbose, boolean pretty) { if (type.equals("gc") || type.equalsIgnoreCase("all")) { writeGarbageCollectionStats(out, verbose, pretty); } if (type.equals("mem") || type.equalsIgnoreCase("all")) { writeMemoryMetrics(out, verbose, pretty); } if (type.equals("jit") || type.equalsIgnoreCase("all")) { writeJitMetrics(out, verbose, pretty); } } private static void writeJitMetrics( PrintStream out, boolean verbose, boolean pretty) { CompilationMXBean cBean = ManagementFactory.getCompilationMXBean(); String name; if (verbose) { name = cBean.getName(); } else { name = "total"; } if (pretty) { out.println("\nJIT Stats"); out.println(String.format( "\t%s jit time: %d ms", name, cBean.getTotalCompilationTime())); } else { out.println(normalizeTabularColonPos(String.format("%s-jit-time-ms : %d", normalizeName(name), cBean.getTotalCompilationTime()))); } } private static void writeOverallMemoryUsage( PrintStream out, MemoryUsage usage, String prefix, boolean pretty) { if (pretty) { out.format("\t%s\n", prefix); out.format("\t\tavailable : %s\n", formatBytes(usage.getMax())); out.format("\t\tcurrent : %s\n", formatBytes(usage.getUsed())); } else { prefix = normalizeName(prefix); out.println(normalizeTabularColonPos( String.format(prefix + "-available-bytes : %d", usage.getMax()))); out.println(normalizeTabularColonPos( String.format(prefix + "-current-bytes : %d", usage.getUsed()))); } } private static void writePoolMemoryUsage(PrintStream out, MemoryUsage usage, MemoryUsage peakUsage, String prefix, boolean pretty) { if (pretty) { out.format("\t\tavailable : %s\n", formatBytes(usage.getMax())); out.format("\t\tpeak : %s\n", formatBytes(peakUsage.getUsed())); out.format("\t\tcurrent : %s\n", formatBytes(usage.getUsed())); } else { out.println(normalizeTabularColonPos( String.format(prefix + "-available-bytes : %d", usage.getMax()))); out.println(normalizeTabularColonPos( String.format(prefix + "-peak-bytes : %d", peakUsage.getUsed()))); out.println(normalizeTabularColonPos( String.format(prefix + "-current-bytes : %d", usage.getUsed()))); } } private static void writeMemoryMetrics( PrintStream out, boolean verbose, boolean pretty) { if (pretty) { out.println("\nMemory usage"); } // only show overall stats in verbose mode if (verbose) { MemoryMXBean overallMemBean = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = overallMemBean.getHeapMemoryUsage(); writeOverallMemoryUsage(out, usage, "Heap", pretty); usage = overallMemBean.getNonHeapMemoryUsage(); writeOverallMemoryUsage(out, usage, "Non-heap", pretty); } if (verbose) { List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); for (MemoryPoolMXBean mpBean : mpBeans) { MemoryUsage currentUsage = mpBean.getUsage(); MemoryUsage peakUsage = mpBean.getPeakUsage(); if (pretty) { out.println("\tPool " + mpBean.getName()); writePoolMemoryUsage(out, currentUsage, peakUsage, null, true); } else { writePoolMemoryUsage(out, currentUsage, peakUsage, "mem-pool-" + normalizeName(mpBean.getName()), false); } } } else { long available = 0; long current = 0; long peak = 0; List mpBeans = ManagementFactory.getMemoryPoolMXBeans(); for (MemoryPoolMXBean mpBean : mpBeans) { MemoryUsage currentUsage = mpBean.getUsage(); available += currentUsage.getMax(); current += currentUsage.getUsed(); MemoryUsage peakUsage = mpBean.getPeakUsage(); peak += peakUsage.getUsed(); } MemoryUsage summaryUsage = new MemoryUsage( 0, current, current, available); MemoryUsage summaryPeakUsage = new MemoryUsage(0, peak, peak, peak); if (pretty) { out.format("\tAggregate of %d memory pools\n", mpBeans.size()); writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, null, true); } else { writePoolMemoryUsage(out, summaryUsage, summaryPeakUsage, "mem", false); } } } private static void writeGarbageCollectionStats( PrintStream out, boolean verbose, boolean pretty) { List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); if (verbose) { if (pretty) { out.println("\nGarbage collection stats"); for (GarbageCollectorMXBean gcBean : gcBeans) { out.println("\tCollector " + gcBean.getName()); out.format( "\t\tcollection count : %d\n", gcBean.getCollectionCount()); out.format( "\t\tcollection time : %d ms\n", gcBean.getCollectionTime()); } } else { for (GarbageCollectorMXBean gcBean : gcBeans) { String name = normalizeName(gcBean.getName()); out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-count : %d", gcBean.getCollectionCount()))); out.println(normalizeTabularColonPos(String.format("gc-" + name + "-collection-time-ms : %d", gcBean.getCollectionTime()))); } } } else { long collectionCount = 0; long collectionTime = 0; int collectorCount = gcBeans.size(); for (GarbageCollectorMXBean gcBean : gcBeans) { collectionCount += gcBean.getCollectionCount(); collectionTime += gcBean.getCollectionTime(); } if (pretty) { out.println("\nGarbage collection stats"); out.format("\tAggregate of %d collectors\n", collectorCount); out.format("\t\tcollection count : %d\n", collectionCount); out.format("\t\tcollection time : %d ms\n", collectionTime); } else { String name = normalizeName("aggregate"); out.println(normalizeTabularColonPos( String.format("gc-" + name + "-collection-count : %d", collectionCount))); out.println(normalizeTabularColonPos( String.format("gc-" + name + "-collection-time-ms : %d", collectionTime))); } } } private static String normalizeName(String name) { return name.replace(" ", "_").toLowerCase(); } private static String normalizeTabularColonPos(String string) { StringBuilder sb = new StringBuilder(string); int index = sb.indexOf(":"); for (; index < TABULAR_COLON_POS; ++index) { sb.insert(index, ' '); } return sb.toString(); } private static String formatBytes(long numBytes) { if (numBytes < ONE_KILO_BYTE) { return String.format("%d B", numBytes); } else if (numBytes < ONE_MEGA_BYTE) { return String.format("%d KB", numBytes / ONE_KILO_BYTE); } else if (numBytes < ONE_GIGA_BYTE) { return String.format("%d MB", numBytes / ONE_MEGA_BYTE); } else { return String.format("%d GB", numBytes / ONE_GIGA_BYTE); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/WhitelistWarningsGuard.java0000644000175000017500000002220612115204405030151 0ustar apoapo/* * Copyright 2011 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.base.Charsets; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.collect.TreeMultimap; import com.google.common.io.CharStreams; import com.google.common.io.Files; import com.google.common.io.InputSupplier; import com.google.javascript.jscomp.CheckLevel; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.Reader; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * An extension of {@code WarningsGuard} that provides functionality to maintain * a list of warnings (white-list). It is subclasses' responsibility to decide * what to do with the white-list by implementing the {@code level} function. * Warnings are defined by the name of the JS file and the first line of * warnings description. * * @author anatol@google.com (Anatol Pomazau) * @author bashir@google.com (Bashir Sadjad) */ public class WhitelistWarningsGuard extends WarningsGuard { private static final Splitter LINE_SPLITTER = Splitter.on("\n"); /** The set of white-listed warnings, same format as {@code formatWarning}. */ private final Set whitelist; /** Pattern to match line number in error descriptions. */ private static final Pattern LINE_NUMBER = Pattern.compile(":-?\\d+"); /** * This class depends on an input set that contains the white-list. The format * of each white-list string is: * :? * # * * @param whitelist The set of JS-warnings that are white-listed. This is * expected to have similar format as {@code formatWarning(JSError)}. */ public WhitelistWarningsGuard(Set whitelist) { Preconditions.checkNotNull(whitelist); this.whitelist = normalizeWhitelist(whitelist); } /** * Loads legacy warnings list from the set of strings. During development line * numbers are changed very often - we just cut them and compare without ones. * * @return known legacy warnings without line numbers. */ private static Set normalizeWhitelist(Set whitelist) { Set result = Sets.newHashSet(); for (String line : whitelist) { String trimmed = line.trim(); if (trimmed.isEmpty() || trimmed.charAt(0) == '#') { // strip out empty lines and comments. continue; } // Strip line number for matching. result.add(LINE_NUMBER.matcher(trimmed).replaceFirst(":")); } return ImmutableSet.copyOf(result); } @Override public CheckLevel level(JSError error) { if (containWarning(formatWarning(error))) { // If the message matches the guard we use WARNING, so that it // - Shows up on stderr, and // - Gets caught by the WhitelistBuilder downstream in the pipeline return CheckLevel.WARNING; } return null; } /** * Determines whether a given warning is included in the white-list. * * @param formattedWarning the warning formatted by {@code formatWarning} * @return whether the given warning is white-listed or not. */ protected boolean containWarning(String formattedWarning) { return whitelist.contains(formattedWarning); } @Override public int getPriority() { return WarningsGuard.Priority.SUPPRESS_BY_WHITELIST.getValue(); } /** Creates a warnings guard from a file. */ public static WhitelistWarningsGuard fromFile(File file) { return new WhitelistWarningsGuard(loadWhitelistedJsWarnings(file)); } /** * Loads legacy warnings list from the file. * @return The lines of the file. */ public static Set loadWhitelistedJsWarnings(File file) { return loadWhitelistedJsWarnings( Files.newReaderSupplier(file, Charsets.UTF_8)); } /** * Loads legacy warnings list from the file. * @return The lines of the file. */ protected static Set loadWhitelistedJsWarnings( InputSupplier supplier) { try { return loadWhitelistedJsWarnings(supplier.getInput()); } catch (IOException e) { throw new RuntimeException(e); } } /** * Loads legacy warnings list from the file. * @return The lines of the file. */ // TODO(nicksantos): This is a weird API. static Set loadWhitelistedJsWarnings(Reader reader) throws IOException { Preconditions.checkNotNull(reader); Set result = Sets.newHashSet(); for (String line : CharStreams.readLines(reader)) { result.add(line); } return result; } public static String formatWarning(JSError error) { return formatWarning(error, false); } /** * @param withMetaData If true, include metadata that's useful to humans * This metadata won't be used for matching the warning. */ public static String formatWarning(JSError error, boolean withMetaData) { StringBuilder sb = new StringBuilder(); sb.append(error.sourceName).append(":"); if (withMetaData) { sb.append(error.lineNumber); } List lines = ImmutableList.copyOf( LINE_SPLITTER.split(error.description)); sb.append(" ").append(lines.get(0)); // Add the rest of the message as a comment. if (withMetaData) { for (int i = 1; i < lines.size(); i++) { sb.append("\n# ").append(lines.get(i)); } sb.append("\n"); } return sb.toString(); } public static String getFirstLine(String warning) { int lineLength = warning.indexOf('\n'); if (lineLength > 0) { warning = warning.substring(0, lineLength); } return warning; } public static class WhitelistBuilder implements ErrorHandler { private final Set warnings = Sets.newLinkedHashSet(); private String productName = null; private String generatorTarget = null; private String headerNote = null; /** Fill in your product name to get a fun message! */ public WhitelistBuilder setProductName(String name) { this.productName = name; return this; } /** Fill in instructions on how to generate this whitelist. */ public WhitelistBuilder setGeneratorTarget(String name) { this.generatorTarget = name; return this; } /** A note to include at the top of the whitelist file. */ public WhitelistBuilder setNote(String note) { this.headerNote = note; return this; } /** We now always record the line number. */ @Deprecated public WhitelistBuilder setWithLineNumber(boolean line) { return this; } @Override public void report(CheckLevel level, JSError error) { warnings.add(error); } /** * Writes the warnings collected in a format that the WhitelistWarningsGuard * can read back later. */ public void writeWhitelist(File out) throws IOException { PrintStream stream = new PrintStream(out); appendWhitelist(stream); stream.close(); } /** * Writes the warnings collected in a format that the WhitelistWarningsGuard * can read back later. */ public void appendWhitelist(PrintStream out) { out.append( "# This is a list of legacy warnings that have yet to be fixed.\n"); if (productName != null) { out.append("# Please find some time and fix at least one of them " + "and it will be the happiest day for " + productName + ".\n"); } if (generatorTarget != null) { out.append("# When you fix any of these warnings, run " + generatorTarget + " task.\n"); } if (headerNote != null) { out.append("#" + Joiner.on("\n# ").join(Splitter.on("\n").split(headerNote)) + "\n"); } Multimap warningsByType = TreeMultimap.create(); for (JSError warning : warnings) { warningsByType.put( warning.getType(), formatWarning(warning, true /* withLineNumber */)); } for (DiagnosticType type : warningsByType.keySet()) { out.append("\n# Warning ") .append(type.key) .append(": ") .println(Iterables.get( LINE_SPLITTER.split(type.format.toPattern()), 0)); for (String warning : warningsByType.get(type)) { out.println(warning); } } out.flush(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ConvertToDottedProperties.java0000644000175000017500000000412412115204405030644 0ustar apoapo/* * Copyright 2007 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.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Converts property accesses from quoted string syntax to dot syntax, where * possible. Dot syntax is more compact and avoids an object allocation in * IE 6. * */ class ConvertToDottedProperties extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; ConvertToDottedProperties(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETTER_DEF: case Token.SETTER_DEF: case Token.STRING_KEY: if (NodeUtil.isValidPropertyName(n.getString())) { n.putBooleanProp(Node.QUOTED_PROP, false); compiler.reportCodeChange(); } break; case Token.GETELEM: Node left = n.getFirstChild(); Node right = left.getNext(); if (right.isString() && NodeUtil.isValidPropertyName(right.getString())) { n.removeChild(left); n.removeChild(right); parent.replaceChild(n, IR.getprop(left, right)); compiler.reportCodeChange(); } break; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeFoldConstants.java0000644000175000017500000012467612115204405027762 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.head.ScriptRuntime; import com.google.javascript.rhino.jstype.TernaryValue; /** * Peephole optimization to fold constants (e.g. x + 1 + 7 --> x + 8). * */ class PeepholeFoldConstants extends AbstractPeepholeOptimization { // TODO(johnlenz): optimizations should not be emiting errors. Move these to // a check pass. static final DiagnosticType INVALID_GETELEM_INDEX_ERROR = DiagnosticType.warning( "JSC_INVALID_GETELEM_INDEX_ERROR", "Array index not integer: {0}"); static final DiagnosticType INDEX_OUT_OF_BOUNDS_ERROR = DiagnosticType.warning( "JSC_INDEX_OUT_OF_BOUNDS_ERROR", "Array index out of bounds: {0}"); static final DiagnosticType NEGATING_A_NON_NUMBER_ERROR = DiagnosticType.warning( "JSC_NEGATING_A_NON_NUMBER_ERROR", "Can't negate non-numeric value: {0}"); static final DiagnosticType BITWISE_OPERAND_OUT_OF_RANGE = DiagnosticType.warning( "JSC_BITWISE_OPERAND_OUT_OF_RANGE", "Operand out of range, bitwise operation will lose information: {0}"); static final DiagnosticType SHIFT_AMOUNT_OUT_OF_BOUNDS = DiagnosticType.warning( "JSC_SHIFT_AMOUNT_OUT_OF_BOUNDS", "Shift amount out of bounds: {0}"); static final DiagnosticType FRACTIONAL_BITWISE_OPERAND = DiagnosticType.warning( "JSC_FRACTIONAL_BITWISE_OPERAND", "Fractional bitwise operand: {0}"); private static final double MAX_FOLD_NUMBER = Math.pow(2, 53); private final boolean late; /** * @param late When late is false, this mean we are currently running before * most of the other optimizations. In this case we would avoid optimizations * that would make the code harder to analyze. When this is true, we would * do anything to minimize for size. */ PeepholeFoldConstants(boolean late) { this.late = late; } @Override Node optimizeSubtree(Node subtree) { switch(subtree.getType()) { case Token.NEW: return tryFoldCtorCall(subtree); case Token.TYPEOF: return tryFoldTypeof(subtree); case Token.NOT: case Token.POS: case Token.NEG: case Token.BITNOT: tryReduceOperandsForOp(subtree); return tryFoldUnaryOperator(subtree); case Token.VOID: return tryReduceVoid(subtree); default: tryReduceOperandsForOp(subtree); return tryFoldBinaryOperator(subtree); } } private Node tryFoldBinaryOperator(Node subtree) { Node left = subtree.getFirstChild(); if (left == null) { return subtree; } Node right = left.getNext(); if (right == null) { return subtree; } // If we've reached here, node is truly a binary operator. switch(subtree.getType()) { case Token.GETPROP: return tryFoldGetProp(subtree, left, right); case Token.GETELEM: return tryFoldGetElem(subtree, left, right); case Token.INSTANCEOF: return tryFoldInstanceof(subtree, left, right); case Token.AND: case Token.OR: return tryFoldAndOr(subtree, left, right); case Token.LSH: case Token.RSH: case Token.URSH: return tryFoldShift(subtree, left, right); case Token.ASSIGN: return tryFoldAssign(subtree, left, right); case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return tryUnfoldAssignOp(subtree, left, right); case Token.ADD: return tryFoldAdd(subtree, left, right); case Token.SUB: case Token.DIV: case Token.MOD: return tryFoldArithmeticOp(subtree, left, right); case Token.MUL: case Token.BITAND: case Token.BITOR: case Token.BITXOR: Node result = tryFoldArithmeticOp(subtree, left, right); if (result != subtree) { return result; } return tryFoldLeftChildOp(subtree, left, right); case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return tryFoldComparison(subtree, left, right); default: return subtree; } } private Node tryReduceVoid(Node n) { Node child = n.getFirstChild(); if (!child.isNumber() || child.getDouble() != 0.0) { if (!mayHaveSideEffects(n)) { n.replaceChild(child, IR.number(0)); reportCodeChange(); } } return n; } private void tryReduceOperandsForOp(Node n) { switch (n.getType()) { case Token.ADD: Node left = n.getFirstChild(); Node right = n.getLastChild(); if (!NodeUtil.mayBeString(left) && !NodeUtil.mayBeString(right)) { tryConvertOperandsToNumber(n); } break; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: // TODO(johnlenz): convert these to integers. case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_MOD: case Token.ASSIGN_DIV: tryConvertToNumber(n.getLastChild()); break; case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.POS: case Token.NEG: tryConvertOperandsToNumber(n); break; } } private void tryConvertOperandsToNumber(Node n) { Node next; for (Node c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); tryConvertToNumber(c); } } private void tryConvertToNumber(Node n) { switch (n.getType()) { case Token.NUMBER: // Nothing to do return; case Token.AND: case Token.OR: case Token.COMMA: tryConvertToNumber(n.getLastChild()); return; case Token.HOOK: tryConvertToNumber(n.getChildAtIndex(1)); tryConvertToNumber(n.getLastChild()); return; case Token.NAME: if (!NodeUtil.isUndefined(n)) { return; } break; } Double result = NodeUtil.getNumberValue(n); if (result == null) { return; } double value = result; Node replacement = NodeUtil.numberNode(value, n); if (replacement.isEquivalentTo(n)) { return; } n.getParent().replaceChild(n, replacement); reportCodeChange(); } /** * Folds 'typeof(foo)' if foo is a literal, e.g. * typeof("bar") --> "string" * typeof(6) --> "number" */ private Node tryFoldTypeof(Node originalTypeofNode) { Preconditions.checkArgument(originalTypeofNode.isTypeOf()); Node argumentNode = originalTypeofNode.getFirstChild(); if (argumentNode == null || !NodeUtil.isLiteralValue(argumentNode, true)) { return originalTypeofNode; } String typeNameString = null; switch (argumentNode.getType()) { case Token.FUNCTION: typeNameString = "function"; break; case Token.STRING: typeNameString = "string"; break; case Token.NUMBER: typeNameString = "number"; break; case Token.TRUE: case Token.FALSE: typeNameString = "boolean"; break; case Token.NULL: case Token.OBJECTLIT: case Token.ARRAYLIT: typeNameString = "object"; break; case Token.VOID: typeNameString = "undefined"; break; case Token.NAME: // We assume here that programs don't change the value of the // keyword undefined to something other than the value undefined. if ("undefined".equals(argumentNode.getString())) { typeNameString = "undefined"; } break; } if (typeNameString != null) { Node newNode = IR.string(typeNameString); originalTypeofNode.getParent().replaceChild(originalTypeofNode, newNode); reportCodeChange(); return newNode; } return originalTypeofNode; } private Node tryFoldUnaryOperator(Node n) { Preconditions.checkState(n.hasOneChild()); Node left = n.getFirstChild(); Node parent = n.getParent(); if (left == null) { return n; } TernaryValue leftVal = NodeUtil.getPureBooleanValue(left); if (leftVal == TernaryValue.UNKNOWN) { return n; } switch (n.getType()) { case Token.NOT: // Don't fold !0 and !1 back to false. if (late && left.isNumber()) { double numValue = left.getDouble(); if (numValue == 0 || numValue == 1) { return n; } } Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true)); parent.replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; case Token.POS: if (NodeUtil.isNumericResult(left)) { // POS does nothing to numeric values. parent.replaceChild(n, left.detachFromParent()); reportCodeChange(); return left; } return n; case Token.NEG: if (left.isName()) { if (left.getString().equals("Infinity")) { // "-Infinity" is valid and a literal, don't modify it. return n; } else if (left.getString().equals("NaN")) { // "-NaN" is "NaN". n.removeChild(left); parent.replaceChild(n, left); reportCodeChange(); return left; } } if (left.isNumber()) { double negNum = -left.getDouble(); Node negNumNode = IR.number(negNum); parent.replaceChild(n, negNumNode); reportCodeChange(); return negNumNode; } else { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } case Token.BITNOT: try { double val = left.getDouble(); if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { int intVal = (int) val; if (intVal == val) { Node notIntValNode = IR.number(~intVal); parent.replaceChild(n, notIntValNode); reportCodeChange(); return notIntValNode; } else { report(FRACTIONAL_BITWISE_OPERAND, left); return n; } } else { report(BITWISE_OPERAND_OUT_OF_RANGE, left); return n; } } catch (UnsupportedOperationException ex) { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } default: return n; } } /** * Try to fold {@code left instanceof right} into {@code true} * or {@code false}. */ private Node tryFoldInstanceof(Node n, Node left, Node right) { Preconditions.checkArgument(n.isInstanceOf()); // TODO(johnlenz) Use type information if available to fold // instanceof. if (NodeUtil.isLiteralValue(left, true) && !mayHaveSideEffects(right)) { Node replacementNode = null; if (NodeUtil.isImmutableValue(left)) { // Non-object types are never instances. replacementNode = IR.falseNode(); } else if (right.isName() && "Object".equals(right.getString())) { replacementNode = IR.trueNode(); } if (replacementNode != null) { n.getParent().replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; } } return n; } private Node tryFoldAssign(Node n, Node left, Node right) { Preconditions.checkArgument(n.isAssign()); if (!late) { return n; } // Tries to convert x = x + y -> x += y; if (!right.hasChildren() || right.getFirstChild().getNext() != right.getLastChild()) { // RHS must have two children. return n; } if (mayHaveSideEffects(left)) { return n; } Node newRight; if (areNodesEqualForInlining(left, right.getFirstChild())) { newRight = right.getLastChild(); } else if (NodeUtil.isCommutative(right.getType()) && areNodesEqualForInlining(left, right.getLastChild())) { newRight = right.getFirstChild(); } else { return n; } int newType = -1; switch (right.getType()) { case Token.ADD: newType = Token.ASSIGN_ADD; break; case Token.BITAND: newType = Token.ASSIGN_BITAND; break; case Token.BITOR: newType = Token.ASSIGN_BITOR; break; case Token.BITXOR: newType = Token.ASSIGN_BITXOR; break; case Token.DIV: newType = Token.ASSIGN_DIV; break; case Token.LSH: newType = Token.ASSIGN_LSH; break; case Token.MOD: newType = Token.ASSIGN_MOD; break; case Token.MUL: newType = Token.ASSIGN_MUL; break; case Token.RSH: newType = Token.ASSIGN_RSH; break; case Token.SUB: newType = Token.ASSIGN_SUB; break; case Token.URSH: newType = Token.ASSIGN_URSH; break; default: return n; } Node newNode = new Node(newType, left.detachFromParent(), newRight.detachFromParent()); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } private Node tryUnfoldAssignOp(Node n, Node left, Node right) { if (late) { return n; } if (!n.hasChildren() || n.getFirstChild().getNext() != n.getLastChild()) { return n; } if (mayHaveSideEffects(left)) { return n; } // Tries to convert x += y -> x = x + y; int op = NodeUtil.getOpFromAssignmentOp(n); Node replacement = IR.assign(left.detachFromParent(), new Node(op, left.cloneTree(), right.detachFromParent()) .srcref(n)); n.getParent().replaceChild(n, replacement); reportCodeChange(); return replacement; } /** * Try to fold a AND/OR node. */ private Node tryFoldAndOr(Node n, Node left, Node right) { Node parent = n.getParent(); Node result = null; int type = n.getType(); TernaryValue leftVal = NodeUtil.getImpureBooleanValue(left); if (leftVal != TernaryValue.UNKNOWN) { boolean lval = leftVal.toBoolean(true); // (TRUE || x) => TRUE (also, (3 || x) => 3) // (FALSE && x) => FALSE if (lval && type == Token.OR || !lval && type == Token.AND) { result = left; } else if (!mayHaveSideEffects(left)) { // (FALSE || x) => x // (TRUE && x) => x result = right; } } // Note: Right hand side folding is handled by // PeepholeSubstituteAlternateSyntax#tryMinimizeCondition if (result != null) { // Fold it! n.removeChild(result); parent.replaceChild(n, result); reportCodeChange(); return result; } else { return n; } } /** * Expressions such as [foo() + 'a' + 'b'] generate parse trees * where no node has two const children ((foo() + 'a') + 'b'), so * tryFoldAdd() won't fold it -- tryFoldLeftChildAdd() will (for Strings). * Specifically, it folds Add expressions where: * - The left child is also and add expression * - The right child is a constant value * - The left child's right child is a STRING constant. */ private Node tryFoldChildAddString(Node n, Node left, Node right) { if (NodeUtil.isLiteralValue(right, false) && left.isAdd()) { Node ll = left.getFirstChild(); Node lr = ll.getNext(); // Left's right child MUST be a string. We would not want to fold // foo() + 2 + 'a' because we don't know what foo() will return, and // therefore we don't know if left is a string concat, or a numeric add. if (lr.isString()) { String leftString = NodeUtil.getStringValue(lr); String rightString = NodeUtil.getStringValue(right); if (leftString != null && rightString != null) { left.removeChild(ll); String result = leftString + rightString; n.replaceChild(left, ll); n.replaceChild(right, IR.string(result)); reportCodeChange(); return n; } } } if (NodeUtil.isLiteralValue(left, false) && right.isAdd()) { Node rl = right.getFirstChild(); Node rr = right.getLastChild(); // Left's right child MUST be a string. We would not want to fold // foo() + 2 + 'a' because we don't know what foo() will return, and // therefore we don't know if left is a string concat, or a numeric add. if (rl.isString()) { String leftString = NodeUtil.getStringValue(left); String rightString = NodeUtil.getStringValue(rl); if (leftString != null && rightString != null) { right.removeChild(rr); String result = leftString + rightString; n.replaceChild(right, rr); n.replaceChild(left, IR.string(result)); reportCodeChange(); return n; } } } return n; } /** * Try to fold an ADD node with constant operands */ private Node tryFoldAddConstantString(Node n, Node left, Node right) { if (left.isString() || right.isString()) { // Add strings. String leftString = NodeUtil.getStringValue(left); String rightString = NodeUtil.getStringValue(right); if (leftString != null && rightString != null) { Node newStringNode = IR.string(leftString + rightString); n.getParent().replaceChild(n, newStringNode); reportCodeChange(); return newStringNode; } } return n; } /** * Try to fold arithmetic binary operators */ private Node tryFoldArithmeticOp(Node n, Node left, Node right) { Node result = performArithmeticOp(n.getType(), left, right); if (result != null) { result.copyInformationFromForTree(n); n.getParent().replaceChild(n, result); reportCodeChange(); return result; } return n; } /** * Try to fold arithmetic binary operators */ private Node performArithmeticOp(int opType, Node left, Node right) { // Unlike other operations, ADD operands are not always converted // to Number. if (opType == Token.ADD && (NodeUtil.mayBeString(left, false) || NodeUtil.mayBeString(right, false))) { return null; } double result; // TODO(johnlenz): Handle NaN with unknown value. BIT ops convert NaN // to zero so this is a little awkward here. Double lValObj = NodeUtil.getNumberValue(left); if (lValObj == null) { return null; } Double rValObj = NodeUtil.getNumberValue(right); if (rValObj == null) { return null; } double lval = lValObj; double rval = rValObj; switch (opType) { case Token.BITAND: result = ScriptRuntime.toInt32(lval) & ScriptRuntime.toInt32(rval); break; case Token.BITOR: result = ScriptRuntime.toInt32(lval) | ScriptRuntime.toInt32(rval); break; case Token.BITXOR: result = ScriptRuntime.toInt32(lval) ^ ScriptRuntime.toInt32(rval); break; case Token.ADD: result = lval + rval; break; case Token.SUB: result = lval - rval; break; case Token.MUL: result = lval * rval; break; case Token.MOD: if (rval == 0) { return null; } result = lval % rval; break; case Token.DIV: if (rval == 0) { return null; } result = lval / rval; break; default: throw new Error("Unexpected arithmetic operator"); } // TODO(johnlenz): consider removing the result length check. // length of the left and right value plus 1 byte for the operator. if ((String.valueOf(result).length() <= String.valueOf(lval).length() + String.valueOf(rval).length() + 1 // Do not try to fold arithmetic for numbers > 2^53. After that // point, fixed-point math starts to break down and become inaccurate. && Math.abs(result) <= MAX_FOLD_NUMBER) || Double.isNaN(result) || result == Double.POSITIVE_INFINITY || result == Double.NEGATIVE_INFINITY) { return NodeUtil.numberNode(result, null); } return null; } /** * Expressions such as [foo() * 10 * 20] generate parse trees * where no node has two const children ((foo() * 10) * 20), so * performArithmeticOp() won't fold it -- tryFoldLeftChildOp() will. * Specifically, it folds associative expressions where: * - The left child is also an associative expression of the same time. * - The right child is a constant NUMBER constant. * - The left child's right child is a NUMBER constant. */ private Node tryFoldLeftChildOp(Node n, Node left, Node right) { int opType = n.getType(); Preconditions.checkState( (NodeUtil.isAssociative(opType) && NodeUtil.isCommutative(opType)) || n.isAdd()); Preconditions.checkState( !n.isAdd()|| !NodeUtil.mayBeString(n)); // Use getNumberValue to handle constants like "NaN" and "Infinity" // other values are converted to numbers elsewhere. Double rightValObj = NodeUtil.getNumberValue(right); if (rightValObj != null && left.getType() == opType) { Preconditions.checkState(left.getChildCount() == 2); Node ll = left.getFirstChild(); Node lr = ll.getNext(); Node valueToCombine = ll; Node replacement = performArithmeticOp(opType, valueToCombine, right); if (replacement == null) { valueToCombine = lr; replacement = performArithmeticOp(opType, valueToCombine, right); } if (replacement != null) { // Remove the child that has been combined left.removeChild(valueToCombine); // Replace the left op with the remaining child. n.replaceChild(left, left.removeFirstChild()); // New "-Infinity" node need location info explicitly // added. replacement.copyInformationFromForTree(right); n.replaceChild(right, replacement); reportCodeChange(); } } return n; } private Node tryFoldAdd(Node node, Node left, Node right) { Preconditions.checkArgument(node.isAdd()); if (NodeUtil.mayBeString(node, true)) { if (NodeUtil.isLiteralValue(left, false) && NodeUtil.isLiteralValue(right, false)) { // '6' + 7 return tryFoldAddConstantString(node, left, right); } else { // a + 7 or 6 + a return tryFoldChildAddString(node, left, right); } } else { // Try arithmetic add Node result = tryFoldArithmeticOp(node, left, right); if (result != node) { return result; } return tryFoldLeftChildOp(node, left, right); } } /** * Try to fold shift operations */ private Node tryFoldShift(Node n, Node left, Node right) { if (left.isNumber() && right.isNumber()) { double result; double lval = left.getDouble(); double rval = right.getDouble(); // check ranges. We do not do anything that would clip the double to // a 32-bit range, since the user likely does not intend that. if (!(lval >= Integer.MIN_VALUE && lval <= Integer.MAX_VALUE)) { report(BITWISE_OPERAND_OUT_OF_RANGE, left); return n; } // only the lower 5 bits are used when shifting, so don't do anything // if the shift amount is outside [0,32) if (!(rval >= 0 && rval < 32)) { report(SHIFT_AMOUNT_OUT_OF_BOUNDS, right); return n; } // Convert the numbers to ints int lvalInt = (int) lval; if (lvalInt != lval) { report(FRACTIONAL_BITWISE_OPERAND, left); return n; } int rvalInt = (int) rval; if (rvalInt != rval) { report(FRACTIONAL_BITWISE_OPERAND, right); return n; } switch (n.getType()) { case Token.LSH: result = lvalInt << rvalInt; break; case Token.RSH: result = lvalInt >> rvalInt; break; case Token.URSH: // JavaScript handles zero shifts on signed numbers differently than // Java as an Java int can not represent the unsigned 32-bit number // where JavaScript can so use a long here. long lvalLong = lvalInt & 0xffffffffL; result = lvalLong >>> rvalInt; break; default: throw new AssertionError("Unknown shift operator: " + Token.name(n.getType())); } Node newNumber = IR.number(result); n.getParent().replaceChild(n, newNumber); reportCodeChange(); return newNumber; } return n; } /** * Try to fold comparison nodes, e.g == */ @SuppressWarnings("fallthrough") private Node tryFoldComparison(Node n, Node left, Node right) { TernaryValue result = evaluateComparison(n.getType(), left, right); if (result == TernaryValue.UNKNOWN) { return n; } Node newNode = NodeUtil.booleanNode(result.toBoolean(true)); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } static TernaryValue evaluateComparison(int op, Node left, Node right) { boolean leftLiteral = NodeUtil.isLiteralValue(left, true); boolean rightLiteral = NodeUtil.isLiteralValue(right, true); if (!leftLiteral || !rightLiteral) { // We only handle literal operands for LT and GT. if (op != Token.GT && op != Token.LT) { return TernaryValue.UNKNOWN; } } boolean undefinedRight = NodeUtil.isUndefined(right) && rightLiteral; boolean nullRight = right.isNull(); int lhType = getNormalizedNodeType(left); int rhType = getNormalizedNodeType(right); switch (lhType) { case Token.VOID: if (!leftLiteral) { return TernaryValue.UNKNOWN; } else if (!rightLiteral) { return TernaryValue.UNKNOWN; } else { return TernaryValue.forBoolean(compareToUndefined(right, op)); } case Token.NULL: if (rightLiteral && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(right, op)); } // fallthrough case Token.TRUE: case Token.FALSE: if (undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (rhType != Token.TRUE && rhType != Token.FALSE && rhType != Token.NULL) { return TernaryValue.UNKNOWN; } switch (op) { case Token.SHEQ: case Token.EQ: return TernaryValue.forBoolean(lhType == rhType); case Token.SHNE: case Token.NE: return TernaryValue.forBoolean(lhType != rhType); case Token.GE: case Token.LE: case Token.GT: case Token.LT: return compareAsNumbers(op, left, right); } return TernaryValue.UNKNOWN; case Token.THIS: if (!right.isThis()) { return TernaryValue.UNKNOWN; } switch (op) { case Token.SHEQ: case Token.EQ: return TernaryValue.TRUE; case Token.SHNE: case Token.NE: return TernaryValue.FALSE; } // We can only handle == and != here. // GT, LT, GE, LE depend on the type of "this" and how it will // be converted to number. The results are different depending on // whether it is a string, NaN or other number value. return TernaryValue.UNKNOWN; case Token.STRING: if (undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (nullRight && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(left, op)); } if (Token.STRING != right.getType()) { return TernaryValue.UNKNOWN; // Only eval if they are the same type } switch (op) { case Token.SHEQ: case Token.EQ: return areStringsEqual(left.getString(), right.getString()); case Token.SHNE: case Token.NE: return areStringsEqual(left.getString(), right.getString()).not(); } return TernaryValue.UNKNOWN; case Token.NUMBER: if (undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (nullRight && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(left, op)); } if (Token.NUMBER != right.getType()) { return TernaryValue.UNKNOWN; // Only eval if they are the same type } return compareAsNumbers(op, left, right); case Token.NAME: if (leftLiteral && undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (rightLiteral) { boolean undefinedLeft = (left.getString().equals("undefined")); if (undefinedLeft) { return TernaryValue.forBoolean(compareToUndefined(right, op)); } if (leftLiteral && nullRight && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(left, op)); } } if (Token.NAME != right.getType()) { return TernaryValue.UNKNOWN; // Only eval if they are the same type } String ln = left.getString(); String rn = right.getString(); if (!ln.equals(rn)) { return TernaryValue.UNKNOWN; // Not the same value name. } switch (op) { // If we knew the named value wouldn't be NaN, it would be nice // to handle EQ,NE,LE,GE,SHEQ, and SHNE. case Token.LT: case Token.GT: return TernaryValue.FALSE; } return TernaryValue.UNKNOWN; // don't handle that op case Token.NEG: if (leftLiteral) { if (undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (nullRight && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(left, op)); } } // Nothing else for now. return TernaryValue.UNKNOWN; case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: case Token.FUNCTION: if (leftLiteral) { if (undefinedRight) { return TernaryValue.forBoolean(compareToUndefined(left, op)); } if (nullRight && isEqualityOp(op)) { return TernaryValue.forBoolean(compareToNull(left, op)); } } // ignore the rest for now. return TernaryValue.UNKNOWN; default: // assert, this should cover all consts return TernaryValue.UNKNOWN; } } /** Returns whether two JS strings are equal. */ private static TernaryValue areStringsEqual(String a, String b) { // In JS, browsers parse \v differently. So do not consider strings // equal if one contains \v. if (a.indexOf('\u000B') != -1 || b.indexOf('\u000B') != -1) { return TernaryValue.UNKNOWN; } else { return a.equals(b) ? TernaryValue.TRUE : TernaryValue.FALSE; } } /** * @return Translate NOT expressions into TRUE or FALSE when possible. */ private static int getNormalizedNodeType(Node n) { int type = n.getType(); if (type == Token.NOT) { TernaryValue value = NodeUtil.getPureBooleanValue(n); switch (value) { case TRUE: return Token.TRUE; case FALSE: return Token.FALSE; case UNKNOWN: return type; } } return type; } /** * The result of the comparison, or UNKNOWN if the * result could not be determined. */ private static TernaryValue compareAsNumbers(int op, Node left, Node right) { Double leftValue = NodeUtil.getNumberValue(left); if (leftValue == null) { return TernaryValue.UNKNOWN; } Double rightValue = NodeUtil.getNumberValue(right); if (rightValue == null) { return TernaryValue.UNKNOWN; } double lv = leftValue; double rv = rightValue; switch (op) { case Token.SHEQ: case Token.EQ: Preconditions.checkState( left.isNumber() && right.isNumber()); return TernaryValue.forBoolean(lv == rv); case Token.SHNE: case Token.NE: Preconditions.checkState( left.isNumber() && right.isNumber()); return TernaryValue.forBoolean(lv != rv); case Token.LE: return TernaryValue.forBoolean(lv <= rv); case Token.LT: return TernaryValue.forBoolean(lv < rv); case Token.GE: return TernaryValue.forBoolean(lv >= rv); case Token.GT: return TernaryValue.forBoolean(lv > rv); default: return TernaryValue.UNKNOWN; // don't handle that op } } /** * @param value The value to compare to "undefined" * @param op The boolean op to compare with * @return Whether the boolean op is true or false */ private static boolean compareToUndefined(Node value, int op) { Preconditions.checkState(NodeUtil.isLiteralValue(value, true)); boolean valueUndefined = NodeUtil.isUndefined(value); boolean valueNull = (Token.NULL == value.getType()); boolean equivalent = valueUndefined || valueNull; switch (op) { case Token.EQ: // undefined is only equal to null or an undefined value return equivalent; case Token.NE: return !equivalent; case Token.SHEQ: return valueUndefined; case Token.SHNE: return !valueUndefined; case Token.LT: case Token.GT: case Token.LE: case Token.GE: return false; default: throw new IllegalStateException("unexpected."); } } private static boolean isEqualityOp(int op) { switch (op) { case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return true; } return false; } /** * @param value The value to compare to "null" * @param op The boolean op to compare with * @return Whether the boolean op is true or false */ private static boolean compareToNull(Node value, int op) { boolean valueUndefined = NodeUtil.isUndefined(value); boolean valueNull = (Token.NULL == value.getType()); boolean equivalent = valueUndefined || valueNull; switch (op) { case Token.EQ: // undefined is only equal to null or an undefined value return equivalent; case Token.NE: return !equivalent; case Token.SHEQ: return valueNull; case Token.SHNE: return !valueNull; default: throw new IllegalStateException("unexpected."); } } /** * Try to fold away unnecessary object instantiation. * e.g. this[new String('eval')] -> this.eval */ private Node tryFoldCtorCall(Node n) { Preconditions.checkArgument(n.isNew()); // we can remove this for GETELEM calls (anywhere else?) if (inForcedStringContext(n)) { return tryFoldInForcedStringContext(n); } return n; } /** Returns whether this node must be coerced to a string. */ private boolean inForcedStringContext(Node n) { if (n.getParent().isGetElem() && n.getParent().getLastChild() == n) { return true; } // we can fold in the case "" + new String("") if (n.getParent().isAdd()) { return true; } return false; } private Node tryFoldInForcedStringContext(Node n) { // For now, we only know how to fold ctors. Preconditions.checkArgument(n.isNew()); Node objectType = n.getFirstChild(); if (!objectType.isName()) { return n; } if (objectType.getString().equals("String")) { Node value = objectType.getNext(); String stringValue = null; if (value == null) { stringValue = ""; } else { if (!NodeUtil.isImmutableValue(value)) { return n; } stringValue = NodeUtil.getStringValue(value); } if (stringValue == null) { return n; } Node parent = n.getParent(); Node newString = IR.string(stringValue); parent.replaceChild(n, newString); newString.copyInformationFrom(parent); reportCodeChange(); return newString; } return n; } /** * Try to fold array-element. e.g [1, 2, 3][10]; */ private Node tryFoldGetElem(Node n, Node left, Node right) { Preconditions.checkArgument(n.isGetElem()); if (left.isObjectLit()) { return tryFoldObjectPropAccess(n, left, right); } if (left.isArrayLit()) { return tryFoldArrayAccess(n, left, right); } return n; } /** * Try to fold array-length. e.g [1, 2, 3].length ==> 3, [x, y].length ==> 2 */ private Node tryFoldGetProp(Node n, Node left, Node right) { Preconditions.checkArgument(n.isGetProp()); if (left.isObjectLit()) { return tryFoldObjectPropAccess(n, left, right); } if (right.isString() && right.getString().equals("length")) { int knownLength = -1; switch (left.getType()) { case Token.ARRAYLIT: if (mayHaveSideEffects(left)) { // Nope, can't fold this, without handling the side-effects. return n; } knownLength = left.getChildCount(); break; case Token.STRING: knownLength = left.getString().length(); break; default: // Not a foldable case, forget it. return n; } Preconditions.checkState(knownLength != -1); Node lengthNode = IR.number(knownLength); n.getParent().replaceChild(n, lengthNode); reportCodeChange(); return lengthNode; } return n; } private boolean isAssignmentTarget(Node n) { Node parent = n.getParent(); if ((NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || parent.isInc() || parent.isDec()) { // If GETPROP/GETELEM is used as assignment target the object literal is // acting as a temporary we can't fold it here: // "{a:x}.a += 1" is not "x += 1" return true; } return false; } private Node tryFoldArrayAccess(Node n, Node left, Node right) { // If GETPROP/GETELEM is used as assignment target the array literal is // acting as a temporary we can't fold it here: // "[][0] += 1" if (isAssignmentTarget(n)) { return n; } if (!right.isNumber()) { // Sometimes people like to use complex expressions to index into // arrays, or strings to index into array methods. return n; } double index = right.getDouble(); int intIndex = (int) index; if (intIndex != index) { report(INVALID_GETELEM_INDEX_ERROR, right); return n; } if (intIndex < 0) { report(INDEX_OUT_OF_BOUNDS_ERROR, right); return n; } Node current = left.getFirstChild(); Node elem = null; for (int i = 0; current != null; i++) { if (i != intIndex) { if (mayHaveSideEffects(current)) { return n; } } else { elem = current; } current = current.getNext(); } if (elem == null) { report(INDEX_OUT_OF_BOUNDS_ERROR, right); return n; } if (elem.isEmpty()) { elem = NodeUtil.newUndefinedNode(elem); } else { left.removeChild(elem); } // Replace the entire GETELEM with the value n.getParent().replaceChild(n, elem); reportCodeChange(); return elem; } private Node tryFoldObjectPropAccess(Node n, Node left, Node right) { Preconditions.checkArgument(NodeUtil.isGet(n)); if (!left.isObjectLit() || !right.isString()) { return n; } if (isAssignmentTarget(n)) { // If GETPROP/GETELEM is used as assignment target the object literal is // acting as a temporary we can't fold it here: // "{a:x}.a += 1" is not "x += 1" return n; } // find the last definition in the object literal Node key = null; Node value = null; for (Node c = left.getFirstChild(); c != null; c = c.getNext()) { if (c.getString().equals(right.getString())) { switch (c.getType()) { case Token.SETTER_DEF: continue; case Token.GETTER_DEF: case Token.STRING_KEY: if (value != null && mayHaveSideEffects(value)) { // The previously found value had side-effects return n; } key = c; value = key.getFirstChild(); break; default: throw new IllegalStateException(); } } else if (mayHaveSideEffects(c.getFirstChild())) { // We don't handle the side-effects here as they might need a temporary // or need to be reordered. return n; } } // Didn't find a definition of the name in the object literal, it might // be coming from the Object prototype if (value == null) { return n; } if (value.isFunction() && NodeUtil.referencesThis(value)) { // 'this' may refer to the object we are trying to remove return n; } Node replacement = value.detachFromParent(); if (key.isGetterDef()){ replacement = IR.call(replacement); replacement.putBooleanProp(Node.FREE_CALL, true); } n.getParent().replaceChild(n, replacement); reportCodeChange(); return n; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ProcessCommonJSModules.java0000644000175000017500000002266412115204405030066 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.net.URI; import java.net.URISyntaxException; import java.util.Set; import java.util.regex.Pattern; /** * Rewrites a CommonJS module http://wiki.commonjs.org/wiki/Modules/1.1.1 * into a form that can be safely concatenated. * Does not add a function around the module body but instead adds suffixes * to global variables to avoid conflicts. * Calls to require are changed to reference the required module directly. * goog.provide and goog.require are emitted for closure compiler automatic * ordering. */ public class ProcessCommonJSModules implements CompilerPass { // According to the spec, the forward slash should be the delimite on // all platforms. private static final String MODULE_SLASH = "/"; public static final String DEFAULT_FILENAME_PREFIX = "." + MODULE_SLASH; private static final String MODULE_NAME_SEPARATOR = "\\$"; private static final String MODULE_NAME_PREFIX = "module$"; private final AbstractCompiler compiler; private final String filenamePrefix; private final boolean reportDependencies; private JSModule module; ProcessCommonJSModules(AbstractCompiler compiler, String filenamePrefix) { this(compiler, filenamePrefix, true); } ProcessCommonJSModules(AbstractCompiler compiler, String filenamePrefix, boolean reportDependencies) { this.compiler = compiler; this.filenamePrefix = filenamePrefix.endsWith(MODULE_SLASH) ? filenamePrefix : filenamePrefix + MODULE_SLASH; this.reportDependencies = reportDependencies; } @Override public void process(Node externs, Node root) { NodeTraversal .traverse(compiler, root, new ProcessCommonJsModulesCallback()); } String guessCJSModuleName(String filename) { return toModuleName(normalizeSourceName(filename)); } /** * For every file that is being processed this returns the module that * created for it. */ JSModule getModule() { return module; } /** * Turns a filename into a JS identifier that is used for moduleNames in * rewritten code. Removes leading ./, replaces / with $, removes trailing .js * and replaces - with _. All moduleNames get a "module$" prefix. */ public static String toModuleName(String filename) { return MODULE_NAME_PREFIX + filename.replaceAll("^\\." + Pattern.quote(MODULE_SLASH), "") .replaceAll(Pattern.quote(MODULE_SLASH), MODULE_NAME_SEPARATOR) .replaceAll("\\.js$", "").replaceAll("-", "_"); } /** * Turn a filename into a moduleName with support for relative addressing * with ./ and ../ based on currentFilename; */ public static String toModuleName(String requiredFilename, String currentFilename) { requiredFilename = requiredFilename.replaceAll("\\.js$", ""); currentFilename = currentFilename.replaceAll("\\.js$", ""); if (requiredFilename.startsWith("." + MODULE_SLASH) || requiredFilename.startsWith(".." + MODULE_SLASH)) { try { requiredFilename = (new URI(currentFilename)).resolve(new URI(requiredFilename)) .toString(); } catch (URISyntaxException e) { throw new RuntimeException(e); } } return toModuleName(requiredFilename); } private String normalizeSourceName(String filename) { // The DOS command shell will normalize "/" to "\", so we have to // wrestle it back. filename = filename.replace("\\", "/"); if (filename.indexOf(filenamePrefix) == 0) { filename = filename.substring(filenamePrefix.length()); } return filename; } /** * Visits require, every "script" and special module.exports assignments. */ private class ProcessCommonJsModulesCallback extends AbstractPostOrderCallback { private int scriptNodeCount = 0; private Set modulesWithExports = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && n.getChildCount() == 2 && "require".equals(n.getFirstChild().getQualifiedName()) && n.getChildAtIndex(1).isString()) { visitRequireCall(t, n, parent); } if (n.isScript()) { scriptNodeCount++; visitScript(t, n); } if (n.isGetProp() && "module.exports".equals(n.getQualifiedName())) { visitModuleExports(n); } } /** * Visit require calls. Emit corresponding goog.require and rewrite require * to be a direct reference to name of require module. */ private void visitRequireCall(NodeTraversal t, Node require, Node parent) { String moduleName = toModuleName(require.getChildAtIndex(1).getString(), normalizeSourceName(t.getSourceName())); Node moduleRef = IR.name(moduleName).srcref(require); parent.replaceChild(require, moduleRef); Node script = getCurrentScriptNode(parent); if (reportDependencies) { t.getInput().addRequire(moduleName); } // Rewrite require("name"). script.addChildToFront(IR.exprResult( IR.call(IR.getprop(IR.name("goog"), IR.string("require")), IR.string(moduleName))).copyInformationFromForTree(require)); compiler.reportCodeChange(); } /** * Emit goog.provide and add suffix to all global vars to avoid conflicts * with other modules. */ private void visitScript(NodeTraversal t, Node script) { Preconditions.checkArgument(scriptNodeCount == 1, "ProcessCommonJSModules supports only one invocation per " + "CompilerInput / script node"); String moduleName = guessCJSModuleName(script.getSourceFileName()); script.addChildToFront(IR.var(IR.name(moduleName), IR.objectlit()) .copyInformationFromForTree(script)); if (reportDependencies) { CompilerInput ci = t.getInput(); ci.addProvide(moduleName); JSModule m = new JSModule(moduleName); m.addAndOverrideModule(ci); module = m; } script.addChildToFront(IR.exprResult( IR.call(IR.getprop(IR.name("goog"), IR.string("provide")), IR.string(moduleName))).copyInformationFromForTree(script)); emitOptionalModuleExportsOverride(script, moduleName); // Rename vars to not conflict in global scope. NodeTraversal.traverse(compiler, script, new SuffixVarsCallback( moduleName)); compiler.reportCodeChange(); } /** * Emit if (moduleName.module$exports) { * moduleName = moduleName.module$export; * } at end of file. */ private void emitOptionalModuleExportsOverride(Node script, String moduleName) { if (!modulesWithExports.contains(moduleName)) { return; } Node moduleExportsProp = IR.getprop(IR.name(moduleName), IR.string("module$exports")); script.addChildToBack(IR.ifNode( moduleExportsProp, IR.block(IR.exprResult(IR.assign(IR.name(moduleName), moduleExportsProp.cloneTree())))).copyInformationFromForTree( script)); } /** * Rewrite module.exports to moduleName.module$exports. */ private void visitModuleExports(Node prop) { String moduleName = guessCJSModuleName(prop.getSourceFileName()); Node module = prop.getChildAtIndex(0); module.putProp(Node.ORIGINALNAME_PROP, "module"); module.setString(moduleName); Node exports = prop.getChildAtIndex(1); exports.putProp(Node.ORIGINALNAME_PROP, "exports"); exports.setString("module$exports"); modulesWithExports.add(moduleName); } /** * Returns next script node in parents. */ private Node getCurrentScriptNode(Node n) { while (true) { if (n.isScript()) { return n; } n = n.getParent(); } } } /** * Traverses a node tree and appends a suffix to all global variable names. */ private class SuffixVarsCallback extends AbstractPostOrderCallback { private static final String EXPORTS = "exports"; private final String suffix; SuffixVarsCallback(String suffix) { this.suffix = suffix; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { String name = n.getString(); if (suffix.equals(name)) { return; } if (EXPORTS.equals(name)) { n.setString(suffix); n.putProp(Node.ORIGINALNAME_PROP, EXPORTS); } else { Scope.Var var = t.getScope().getVar(name); if (var != null && var.isGlobal()) { n.setString(name + "$$" + suffix); n.putProp(Node.ORIGINALNAME_PROP, name); } } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CustomPassExecutionTime.java0000644000175000017500000000157112115204405030307 0ustar apoapo/* * 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; /** * Custom pass type. Controls the compilation stage at which each * custom pass executes. */ public enum CustomPassExecutionTime { BEFORE_CHECKS, BEFORE_OPTIMIZATIONS, BEFORE_OPTIMIZATION_LOOP, AFTER_OPTIMIZATION_LOOP, } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CompilerInput.java0000644000175000017500000002536012115204405026277 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.deps.DependencyInfo; import com.google.javascript.jscomp.deps.JsFileParser; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; /** * A class for the internal representation of an input to the compiler. * Wraps a {@link SourceAst} and maintain state such as module for the input and * whether the input is an extern. Also calculates provided and required types. * */ public class CompilerInput implements SourceAst, DependencyInfo { private static final long serialVersionUID = 1L; // Info about where the file lives. private JSModule module; final private InputId id; // The AST. private final SourceAst ast; // Provided and required symbols. private final Set provides = Sets.newHashSet(); private final Set requires = Sets.newHashSet(); private boolean generatedDependencyInfoFromSource = false; // An AbstractCompiler for doing parsing. // We do not want to persist this across serialized state. private transient AbstractCompiler compiler; public CompilerInput(SourceAst ast) { this(ast, ast.getSourceFile().getName(), false); } public CompilerInput(SourceAst ast, boolean isExtern) { this(ast, ast.getInputId(), isExtern); } public CompilerInput(SourceAst ast, String inputId, boolean isExtern) { this(ast, new InputId(inputId), isExtern); } public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) { this.ast = ast; this.id = inputId; // TODO(nicksantos): Add a precondition check here. People are passing // in null, but they should not be. if (ast != null && ast.getSourceFile() != null) { ast.getSourceFile().setIsExtern(isExtern); } } public CompilerInput(SourceFile file) { this(file, false); } public CompilerInput(SourceFile file, boolean isExtern) { this(new JsAst(file), isExtern); } /** Returns a name for this input. Must be unique across all inputs. */ @Override public InputId getInputId() { return id; } /** Returns a name for this input. Must be unique across all inputs. */ @Override public String getName() { return id.getIdName(); } public SourceAst getAst() { return ast; } /** Gets the path relative to closure-base, if one is available. */ @Override public String getPathRelativeToClosureBase() { // TODO(nicksantos): Implement me. throw new UnsupportedOperationException(); } @Override public Node getAstRoot(AbstractCompiler compiler) { Node root = ast.getAstRoot(compiler); // The root maybe null if the AST can not be created. if (root != null) { Preconditions.checkState(root.isScript()); Preconditions.checkNotNull(root.getInputId()); } return root; } @Override public void clearAst() { ast.clearAst(); } @Override public SourceFile getSourceFile() { return ast.getSourceFile(); } @Override public void setSourceFile(SourceFile file) { ast.setSourceFile(file); } /** Returns the SourceAst object on which this input is based. */ public SourceAst getSourceAst() { return ast; } /** Sets an abstract compiler for doing parsing. */ public void setCompiler(AbstractCompiler compiler) { this.compiler = compiler; } private void checkErrorManager() { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first: " + this); Preconditions.checkNotNull(compiler.getErrorManager(), "Expected compiler to call an error manager: " + this); } /** Gets a list of types depended on by this input. */ @Override public Collection getRequires() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.unmodifiableSet(requires); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.of(); } } /** Gets a list of types provided by this input. */ @Override public Collection getProvides() { checkErrorManager(); try { regenerateDependencyInfoIfNecessary(); return Collections.unmodifiableSet(provides); } catch (IOException e) { compiler.getErrorManager().report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.of(); } } // TODO(nicksantos): Remove addProvide/addRequire/removeRequire once // there is better support for discovering non-closure dependencies. void addProvide(String provide) { getProvides(); provides.add(provide); } void addRequire(String require) { getRequires(); requires.add(require); } public void removeRequire(String require) { getRequires(); requires.remove(require); } /** * Regenerates the provides/requires if we need to do so. */ private void regenerateDependencyInfoIfNecessary() throws IOException { // If the code is NOT a JsAst, then it was not originally JS code. // Look at the Ast for dependency info. if (!(ast instanceof JsAst)) { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first"); DepsFinder finder = new DepsFinder(); Node root = getAstRoot(compiler); if (root == null) { return; } finder.visitTree(getAstRoot(compiler)); // TODO(nicksantos|user): This caching behavior is a bit // odd, and only works if you assume the exact call flow that // clients are currently using. In that flow, they call // getProvides(), then remove the goog.provide calls from the // AST, and then call getProvides() again. // // This won't work for any other call flow, or any sort of incremental // compilation scheme. The API needs to be fixed so callers aren't // doing weird things like this, and then we should get rid of the // multiple-scan strategy. provides.addAll(finder.provides); requires.addAll(finder.requires); } else { // Otherwise, look at the source code. if (!generatedDependencyInfoFromSource) { // Note: it's OK to use getName() instead of // getPathRelativeToClosureBase() here because we're not using // this to generate deps files. (We're only using it for // symbol dependencies.) DependencyInfo info = (new JsFileParser(compiler.getErrorManager())) .setIncludeGoogBase(true) .parseFile(getName(), getName(), getCode()); provides.addAll(info.getProvides()); requires.addAll(info.getRequires()); generatedDependencyInfoFromSource = true; } } } private static class DepsFinder { private final List provides = Lists.newArrayList(); private final List requires = Lists.newArrayList(); private final CodingConvention codingConvention = new ClosureCodingConvention(); void visitTree(Node n) { visitSubtree(n, null); } void visitSubtree(Node n, Node parent) { if (n.isCall()) { String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent); if (provide != null) { provides.add(provide); } return; } else if (parent != null && !parent.isExprResult() && !parent.isScript()) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { return getSourceFile().getRegion(lineNumber); } public String getCode() throws IOException { return getSourceFile().getCode(); } /** Returns the module to which the input belongs. */ public JSModule getModule() { return module; } /** Sets the module to which the input belongs. */ public void setModule(JSModule module) { // An input may only belong to one module. Preconditions.checkArgument( module == null || this.module == null || this.module == module); this.module = module; } /** Overrides the module to which the input belongs. */ void overrideModule(JSModule module) { this.module = module; } public boolean isExtern() { if (ast == null || ast.getSourceFile() == null) { return false; } return ast.getSourceFile().isExtern(); } void setIsExtern(boolean isExtern) { if (ast == null || ast.getSourceFile() == null) { return; } ast.getSourceFile().setIsExtern(isExtern); } public int getLineOffset(int lineno) { return ast.getSourceFile().getLineOffset(lineno); } /** @return The number of lines in this input. */ public int getNumLines() { return ast.getSourceFile().getNumLines(); } @Override public String toString() { return getName(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JoinOp.java0000644000175000017500000000457712115204405024712 0ustar apoapo/* * 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.common.base.Function; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.graph.LatticeElement; import java.util.List; /** * Defines a way join a list of LatticeElements. */ interface JoinOp extends Function, L> { /** * An implementation of {@code JoinOp} that makes it easy to join to * lattice elements at a time. */ static abstract class BinaryJoinOp implements JoinOp { @Override public final L apply(List values) { Preconditions.checkArgument(!values.isEmpty()); int size = values.size(); if (size == 1) { return values.get(0); } else if (size == 2) { return apply(values.get(0), values.get(1)); } else { int mid = computeMidPoint(size); return apply( apply(values.subList(0, mid)), apply(values.subList(mid, size))); } } /** * Creates a new lattice that will be the join of two input lattices. * * @return The join of {@code latticeA} and {@code latticeB}. */ abstract L apply(L latticeA, L latticeB); /** * Finds the midpoint of a list. The function will favor two lists of * even length instead of two lists of the same odd length. The list * must be at least length two. * * @param size Size of the list. */ static int computeMidPoint(int size) { int midpoint = size >>> 1; if (size > 4) { /* Any list longer than 4 should prefer an even split point * over the true midpoint, so that [0,6] splits at 2, not 3. */ midpoint &= -2; // (0xfffffffe) clears low bit so midpoint is even } return midpoint; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DisambiguateProperties.java0000644000175000017500000011562412115204405030163 0ustar apoapo/* * 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 static com.google.common.base.Preconditions.checkState; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; import com.google.javascript.jscomp.ConcreteType.ConcreteUnionType; import com.google.javascript.jscomp.ConcreteType.ConcreteUniqueType; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.TypeValidator.TypeMismatch; import com.google.javascript.jscomp.graph.StandardUnionFind; import com.google.javascript.jscomp.graph.UnionFind; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.logging.Logger; /** * DisambiguateProperties renames properties to disambiguate between unrelated * fields with the same name. Two properties are considered related if they * share a definition on their prototype chains, or if they are potentially * referenced together via union types. * *

Renamimg only occurs if there are two or more distinct properties with * the same name. * *

This pass allows other passes, such as inlining and code removal to take * advantage of type information implicitly. * *

 *   Foo.a;
 *   Bar.a;
 * 
* *

will become * *

 *   Foo.a$Foo;
 *   Bar.a$Bar;
 * 
* */ class DisambiguateProperties implements CompilerPass { // To prevent the logs from filling up, we cap the number of warnings // that we tell the user to fix per-property. private static final int MAX_INVALDIATION_WARNINGS_PER_PROPERTY = 10; private static final Logger logger = Logger.getLogger( DisambiguateProperties.class.getName()); static class Warnings { // TODO(user): {1} and {2} are not exactly useful for most people. static final DiagnosticType INVALIDATION = DiagnosticType.disabled( "JSC_INVALIDATION", "Property disambiguator skipping all instances of property {0} " + "because of type {1} node {2}. {3}"); static final DiagnosticType INVALIDATION_ON_TYPE = DiagnosticType.disabled( "JSC_INVALIDATION_TYPE", "Property disambiguator skipping instances of property {0} " + "on type {1}. {2}"); } private final AbstractCompiler compiler; private final TypeSystem typeSystem; /** * Map of a type to all the related errors that invalidated the type * for disambiguation. It has be Object because of the generic nature of * this pass. */ private Multimap invalidationMap; /** * In practice any large code base will have thousands and thousands of * type invalidations, which makes reporting all of the errors useless. * However, certain properties are worth specifically guarding because of the * large amount of code that can be removed as dead code. This list contains * the properties (eg: "toString") that we care about; if any of these * properties is invalidated it causes an error. */ private final Map propertiesToErrorFor; private class Property { /** The name of the property. */ final String name; /** All types on which the field exists, grouped together if related. */ private UnionFind types; /** * A set of types for which renaming this field should be skipped. This * list is first filled by fields defined in the externs file. */ Set typesToSkip = Sets.newHashSet(); /** * If true, do not rename any instance of this field, as it has been * referenced from an unknown type. */ boolean skipRenaming; /** Set of nodes for this field that need renaming. */ Set renameNodes = Sets.newHashSet(); /** * Map from node to the highest type in the prototype chain containing the * field for that node. In the case of a union, the type is the highest type * of one of the types in the union. */ final Map rootTypes = Maps.newHashMap(); Property(String name) { this.name = name; } /** Returns the types on which this field is referenced. */ UnionFind getTypes() { if (types == null) { types = new StandardUnionFind(); } return types; } /** * Record that this property is referenced from this type. * @return true if the type was recorded for this property, else false, * which would happen if the type was invalidating. */ boolean addType(T type, T top, T relatedType) { checkState(!skipRenaming, "Attempt to record skipped property: %s", name); if (typeSystem.isInvalidatingType(top)) { invalidate(); return false; } else { if (typeSystem.isTypeToSkip(top)) { addTypeToSkip(top); } if (relatedType == null) { getTypes().add(top); } else { getTypes().union(top, relatedType); } typeSystem.recordInterfaces(type, top, this); return true; } } /** Records the given type as one to skip for this property. */ void addTypeToSkip(T type) { for (T skipType : typeSystem.getTypesToSkipForType(type)) { typesToSkip.add(skipType); getTypes().union(skipType, type); } } /** Invalidates any types related to invalid types. */ void expandTypesToSkip() { // If we are not going to rename any properties, then we do not need to // update the list of invalid types, as they are all invalid. if (shouldRename()) { int count = 0; while (true) { // It should usually only take one time through this do-while. checkState(++count < 10, "Stuck in loop expanding types to skip."); // Make sure that the representative type for each type to skip is // marked as being skipped. Set rootTypesToSkip = Sets.newHashSet(); for (T subType : typesToSkip) { rootTypesToSkip.add(types.find(subType)); } typesToSkip.addAll(rootTypesToSkip); Set newTypesToSkip = Sets.newHashSet(); Set allTypes = types.elements(); int originalTypesSize = allTypes.size(); for (T subType : allTypes) { if (!typesToSkip.contains(subType) && typesToSkip.contains(types.find(subType))) { newTypesToSkip.add(subType); } } for (T newType : newTypesToSkip) { addTypeToSkip(newType); } // If there were not any new types added, we are done here. if (types.elements().size() == originalTypesSize) { break; } } } } /** Returns true if any instance of this property should be renamed. */ boolean shouldRename() { return !skipRenaming && types != null && types.allEquivalenceClasses().size() > 1; } /** * Returns true if this property should be renamed on this type. * expandTypesToSkip() should be called before this, if anything has been * added to the typesToSkip list. */ boolean shouldRename(T type) { return !skipRenaming && !typesToSkip.contains(type); } /** * Invalidates a field from renaming. Used for field references on an * object with unknown type. */ boolean invalidate() { boolean changed = !skipRenaming; skipRenaming = true; types = null; return changed; } /** * Schedule the node to potentially be renamed. * @param node the node to rename * @param type the highest type in the prototype chain for which the * property is defined * @return True if type was accepted without invalidation or if the property * was already invalidated. False if this property was invalidated this * time. */ boolean scheduleRenaming(Node node, T type) { if (!skipRenaming) { if (typeSystem.isInvalidatingType(type)) { invalidate(); return false; } renameNodes.add(node); rootTypes.put(node, type); } return true; } } private Map properties = Maps.newHashMap(); static DisambiguateProperties forJSTypeSystem( AbstractCompiler compiler, Map propertiesToErrorFor) { return new DisambiguateProperties( compiler, new JSTypeSystem(compiler), propertiesToErrorFor); } static DisambiguateProperties forConcreteTypeSystem( AbstractCompiler compiler, TightenTypes tt, Map propertiesToErrorFor) { return new DisambiguateProperties( compiler, new ConcreteTypeSystem(tt, compiler.getCodingConvention()), propertiesToErrorFor); } /** * This constructor should only be called by one of the helper functions * above for either the JSType system, or the concrete type system. */ private DisambiguateProperties(AbstractCompiler compiler, TypeSystem typeSystem, Map propertiesToErrorFor) { this.compiler = compiler; this.typeSystem = typeSystem; this.propertiesToErrorFor = propertiesToErrorFor; if (!this.propertiesToErrorFor.isEmpty()) { this.invalidationMap = LinkedHashMultimap.create(); } else { this.invalidationMap = null; } } @Override public void process(Node externs, Node root) { Preconditions.checkState( compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA, mis.src); addInvalidatingType(mis.typeB, mis.src); } NodeTraversal.traverse(compiler, externs, new FindExternProperties()); NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); renameProperties(); } private void recordInvalidationError(JSType t, JSError error) { if (!t.isObject()) { return; } if (invalidationMap != null) { invalidationMap.put(t, error); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type, JSError error) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt, error); } } else if (type.isEnumElementType()) { addInvalidatingType( type.toMaybeEnumElementType().getPrimitiveType(), error); } else { typeSystem.addInvalidatingType(type); recordInvalidationError(type, error); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); recordInvalidationError(objType.getImplicitPrototype(), error); } } } /** Returns the property for the given name, creating it if necessary. */ protected Property getProperty(String name) { if (!properties.containsKey(name)) { properties.put(name, new Property(name)); } return properties.get(name); } /** Public for testing. */ T getTypeWithProperty(String field, T type) { return typeSystem.getTypeWithProperty(field, type); } /** Tracks the current type system scope while traversing. */ private abstract class AbstractScopingCallback implements ScopedCallback { protected final Stack> scopes = new Stack>(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { scopes.push(typeSystem.getRootScope()); } else { scopes.push(typeSystem.getFunctionScope(t.getScopeRoot())); } } @Override public void exitScope(NodeTraversal t) { scopes.pop(); } /** Returns the current scope at this point in the file. */ protected StaticScope getScope() { return scopes.peek(); } } /** * Finds all properties defined in the externs file and sets them as * ineligible for renaming from the type on which they are defined. */ private class FindExternProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(johnlenz): Support object-literal property definitions. if (n.isGetProp()) { String field = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), field); Property prop = getProperty(field); if (typeSystem.isInvalidatingType(type)) { prop.invalidate(); } else { prop.addTypeToSkip(type); // If this is a prototype property, then we want to skip assignments // to the instance type as well. These assignments are not usually // seen in the extern code itself, so we must handle them here. if ((type = typeSystem.getInstanceFromPrototype(type)) != null) { prop.getTypes().add(type); prop.typesToSkip.add(type); } } } } } /** * Traverses the tree, building a map from field names to Nodes for all * fields that can be renamed. */ private class FindRenameableProperties extends AbstractScopingCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { handleGetProp(t, n); } else if (n.isObjectLit()) { handleObjectLit(t, n); } } /** * Processes a GETPROP node. */ private void handleGetProp(NodeTraversal t, Node n) { String name = n.getLastChild().getString(); T type = typeSystem.getType(getScope(), n.getFirstChild(), name); Property prop = getProperty(name); if (!prop.scheduleRenaming(n.getLastChild(), processProperty(t, prop, type, null))) { if (propertiesToErrorFor.containsKey(name)) { String suggestion = ""; if (type instanceof JSType) { JSType jsType = (JSType) type; if (jsType.isAllType() || jsType.isUnknownType()) { if (n.getFirstChild().isThis()) { suggestion = "The \"this\" object is unknown in the function,"+ "consider using @this"; } else { String qName = n.getFirstChild().getQualifiedName(); suggestion = "Consider casting " + qName + " if you know it's type."; } } else { List errors = Lists.newArrayList(); printErrorLocations(errors, jsType); if (!errors.isEmpty()) { suggestion = "Consider fixing errors for the following types:\n"; suggestion += Joiner.on("\n").join(errors); } } } compiler.report(JSError.make( t.getSourceName(), n, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), suggestion)); } } } /** * Processes a OBJECTLIT node. */ private void handleObjectLit(NodeTraversal t, Node n) { Node child = n.getFirstChild(); while (child != null) { // Maybe STRING, GET, SET // We should never see a mix of numbers and strings. String name = child.getString(); T type = typeSystem.getType(getScope(), n, name); Property prop = getProperty(name); if (!prop.scheduleRenaming(child, processProperty(t, prop, type, null))) { // TODO(user): It doesn't look like the user can do much in this // case right now. if (propertiesToErrorFor.containsKey(name)) { compiler.report(JSError.make( t.getSourceName(), child, propertiesToErrorFor.get(name), Warnings.INVALIDATION, name, (type == null ? "null" : type.toString()), n.toString(), "")); } } child = child.getNext(); } } private void printErrorLocations(List errors, JSType t) { if (!t.isObject() || t.isAllType()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { printErrorLocations(errors, alt); } return; } for (JSError error : invalidationMap.get(t)) { if (errors.size() > MAX_INVALDIATION_WARNINGS_PER_PROPERTY) { return; } errors.add( t.toString() + " at " + error.sourceName + ":" + error.lineNumber); } } /** * Processes a property, adding it to the list of properties to rename. * @return a representative type for the property reference, which will be * the highest type on the prototype chain of the provided type. In the * case of a union type, it will be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) { type = typeSystem.restrictByNotNullOrUndefined(type); if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) { return null; } Iterable alternatives = typeSystem.getTypeAlternatives(type); if (alternatives != null) { T firstType = relatedType; for (T subType : alternatives) { T lastType = processProperty(t, prop, subType, firstType); if (lastType != null) { firstType = firstType == null ? lastType : firstType; } } return firstType; } else { T topType = typeSystem.getTypeWithProperty(prop.name, type); if (typeSystem.isInvalidatingType(topType)) { return null; } prop.addType(type, topType, relatedType); return topType; } } } /** Renames all properties with references on more than one type. */ void renameProperties() { int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0, instancesSkipped = 0, singleTypeProps = 0; Set reported = Sets.newHashSet(); for (Property prop : properties.values()) { if (prop.shouldRename()) { Map propNames = buildPropNames(prop.getTypes(), prop.name); ++propsRenamed; prop.expandTypesToSkip(); for (Node node : prop.renameNodes) { T rootType = prop.rootTypes.get(node); if (prop.shouldRename(rootType)) { String newName = propNames.get(rootType); node.setString(newName); compiler.reportCodeChange(); ++instancesRenamed; } else { ++instancesSkipped; CheckLevel checkLevelForProp = propertiesToErrorFor.get(prop.name); if (checkLevelForProp != null && checkLevelForProp != CheckLevel.OFF && !reported.contains(prop.name)) { reported.add(prop.name); compiler.report(JSError.make( NodeUtil.getSourceName(node), node, checkLevelForProp, Warnings.INVALIDATION_ON_TYPE, prop.name, rootType.toString(), "")); } } } } else { if (prop.skipRenaming) { ++propsSkipped; } else { ++singleTypeProps; } } } logger.fine("Renamed " + instancesRenamed + " instances of " + propsRenamed + " properties."); logger.fine("Skipped renaming " + instancesSkipped + " invalidated " + "properties, " + propsSkipped + " instances of properties " + "that were skipped for specific types and " + singleTypeProps + " properties that were referenced from only one type."); } /** * Chooses a name to use for renaming in each equivalence class and maps * each type in that class to it. */ private Map buildPropNames(UnionFind types, String name) { Map names = Maps.newHashMap(); for (Set set : types.allEquivalenceClasses()) { checkState(!set.isEmpty()); String typeName = null; for (T type : set) { if (typeName == null || type.toString().compareTo(typeName) < 0) { typeName = type.toString(); } } String newName; if ("{...}".equals(typeName)) { newName = name; } else { newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; } for (T type : set) { names.put(type, newName); } } return names; } /** Returns a map from field name to types for which it will be renamed. */ Multimap> getRenamedTypesForTesting() { Multimap> ret = HashMultimap.create(); for (Map.Entry entry: properties.entrySet()) { Property prop = entry.getValue(); if (!prop.skipRenaming) { for (Collection c : prop.getTypes().allEquivalenceClasses()) { if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) { ret.put(entry.getKey(), c); } } } } return ret; } /** Interface for providing the type information needed by this pass. */ private interface TypeSystem { // TODO(user): add a getUniqueName(T type) method that is guaranteed // to be unique, performant and human-readable. /** Returns the top-most scope used by the type system (if any). */ StaticScope getRootScope(); /** Returns the new scope started at the given function node. */ StaticScope getFunctionScope(Node node); /** * Returns the type of the given node. * @param prop Only types with this property need to be returned. In general * with type tightening, this will require no special processing, but in * the case of an unknown JSType, we might need to add in the native * types since we don't track them, but only if they have the given * property. */ T getType(StaticScope scope, Node node, String prop); /** * Returns true if a field reference on this type will invalidate all * references to that field as candidates for renaming. This is true if the * type is unknown or all-inclusive, as variables with such a type could be * references to any object. */ boolean isInvalidatingType(T type); /** * Informs the given type system that a type is invalidating due to a type * mismatch found during type checking. */ void addInvalidatingType(JSType type); /** * Returns a set of types that should be skipped given the given type. * This is necessary for interfaces when using JSTypes, as all super * interfaces must also be skipped. */ ImmutableSet getTypesToSkipForType(T type); /** * Determines whether the given type is one whose properties should not be * considered for renaming. */ boolean isTypeToSkip(T type); /** Remove null and undefined from the options in the given type. */ T restrictByNotNullOrUndefined(T type); /** * Returns the alternatives if this is a type that represents multiple * types, and null if not. Union and interface types can correspond to * multiple other types. */ Iterable getTypeAlternatives(T type); /** * Returns the type in the chain from the given type that contains the given * field or null if it is not found anywhere. */ T getTypeWithProperty(String field, T type); /** * Returns the type of the instance of which this is the prototype or null * if this is not a function prototype. */ T getInstanceFromPrototype(T type); /** * Records that this property could be referenced from any interface that * this type, or any type in its superclass chain, implements. */ void recordInterfaces(T type, T relatedType, DisambiguateProperties.Property p); } /** Implementation of TypeSystem using JSTypes. */ private static class JSTypeSystem implements TypeSystem { private final Set invalidatingTypes; private JSTypeRegistry registry; public JSTypeSystem(AbstractCompiler compiler) { registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope getRootScope() { return null; } @Override public StaticScope getFunctionScope(Node node) { return null; } @Override public JSType getType( StaticScope scope, Node node, String prop) { if (node.getJSType() == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return node.getJSType(); } @Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); } @Override public ImmutableSet getTypesToSkipForType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { Set types = Sets.newHashSet(type); for (JSType alt : type.toMaybeUnionType().getAlternates()) { types.addAll(getTypesToSkipForTypeNonUnion(alt)); } return ImmutableSet.copyOf(types); } else if (type.isEnumElementType()) { return getTypesToSkipForType( type.toMaybeEnumElementType().getPrimitiveType()); } return ImmutableSet.copyOf(getTypesToSkipForTypeNonUnion(type)); } private Set getTypesToSkipForTypeNonUnion(JSType type) { Set types = Sets.newHashSet(); JSType skipType = type; while (skipType != null) { types.add(skipType); ObjectType objSkipType = skipType.toObjectType(); if (objSkipType != null) { skipType = objSkipType.getImplicitPrototype(); } else { break; } } return types; } @Override public boolean isTypeToSkip(JSType type) { return type.isEnumType() || (type.autoboxesTo() != null); } @Override public JSType restrictByNotNullOrUndefined(JSType type) { return type.restrictByNotNullOrUndefined(); } @Override public Iterable getTypeAlternatives(JSType type) { if (type.isUnionType()) { return type.toMaybeUnionType().getAlternates(); } else { ObjectType objType = type.toObjectType(); if (objType != null && objType.getConstructor() != null && objType.getConstructor().isInterface()) { List list = Lists.newArrayList(); for (FunctionType impl : registry.getDirectImplementors(objType)) { list.add(impl.getInstanceType()); } return list; } else { return null; } } } @Override public ObjectType getTypeWithProperty(String field, JSType type) { if (type == null) { return null; } if (type.isEnumElementType()) { return getTypeWithProperty( field, type.toMaybeEnumElementType().getPrimitiveType()); } if (!(type instanceof ObjectType)) { if (type.autoboxesTo() != null) { type = type.autoboxesTo(); } else { return null; } } // Ignore the prototype itself at all times. if ("prototype".equals(field)) { return null; } // We look up the prototype chain to find the highest place (if any) that // this appears. This will make references to overridden properties look // like references to the initial property, so they are renamed alike. ObjectType foundType = null; ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getConstructor() != null && objType.getConstructor().isInterface()) { ObjectType topInterface = FunctionType.getTopDefiningInterface( objType, field); if (topInterface != null && topInterface.getConstructor() != null) { foundType = topInterface.getConstructor().getPrototype(); } } else { while (objType != null && objType.getImplicitPrototype() != objType) { if (objType.hasOwnProperty(field)) { foundType = objType; } objType = objType.getImplicitPrototype(); } } // If the property does not exist on the referenced type but the original // type is an object type, see if any subtype has the property. if (foundType == null) { ObjectType maybeType = ObjectType.cast( registry.getGreatestSubtypeWithProperty(type, field)); // getGreatestSubtypeWithProperty does not guarantee that the property // is defined on the returned type, it just indicates that it might be, // so we have to double check. if (maybeType != null && maybeType.hasOwnProperty(field)) { foundType = maybeType; } } return foundType; } @Override public JSType getInstanceFromPrototype(JSType type) { if (type.isFunctionPrototypeType()) { ObjectType prototype = (ObjectType) type; FunctionType owner = prototype.getOwnerFunction(); if (owner.isConstructor() || owner.isInterface()) { return prototype.getOwnerFunction().getInstanceType(); } } return null; } @Override public void recordInterfaces(JSType type, JSType relatedType, DisambiguateProperties.Property p) { ObjectType objType = ObjectType.cast(type); if (objType != null) { FunctionType constructor; if (objType.isFunctionType()) { constructor = objType.toMaybeFunctionType(); } else if (objType.isFunctionPrototypeType()) { constructor = objType.getOwnerFunction(); } else { constructor = objType.getConstructor(); } while (constructor != null) { for (ObjectType itype : constructor.getImplementedInterfaces()) { JSType top = getTypeWithProperty(p.name, itype); if (top != null) { p.addType(itype, top, relatedType); } else { recordInterfaces(itype, relatedType, p); } // If this interface invalidated this property, return now. if (p.skipRenaming) return; } if (constructor.isInterface() || constructor.isConstructor()) { constructor = constructor.getSuperClassConstructor(); } else { constructor = null; } } } } } /** Implementation of TypeSystem using concrete types. */ private static class ConcreteTypeSystem implements TypeSystem { private final TightenTypes tt; private int nextUniqueId; private CodingConvention codingConvention; private final Set invalidatingTypes = Sets.newHashSet(); // An array of native types that are not tracked by type tightening, and // thus need to be added in if an unknown type is encountered. private static final JSTypeNative [] nativeTypes = new JSTypeNative[] { JSTypeNative.BOOLEAN_OBJECT_TYPE, JSTypeNative.NUMBER_OBJECT_TYPE, JSTypeNative.STRING_OBJECT_TYPE }; public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) { this.tt = tt; this.codingConvention = convention; } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope getRootScope() { return tt.getTopScope(); } @Override public StaticScope getFunctionScope(Node decl) { ConcreteFunctionType func = tt.getConcreteFunction(decl); return (func != null) ? func.getScope() : (StaticScope) null; } @Override public ConcreteType getType( StaticScope scope, Node node, String prop) { if (scope != null) { ConcreteType c = tt.inferConcreteType( (TightenTypes.ConcreteScope) scope, node); return maybeAddAutoboxes(c, node, prop); } else { return null; } } /** * Add concrete types for autoboxing types if necessary. The concrete type * system does not track native types, like string, so add them if they are * present in the JSType for the node. */ private ConcreteType maybeAddAutoboxes( ConcreteType cType, Node node, String prop) { JSType jsType = node.getJSType(); if (jsType == null) { return cType; } else if (jsType.isUnknownType()) { for (JSTypeNative nativeType : nativeTypes) { ConcreteType concrete = tt.getConcreteInstance( tt.getTypeRegistry().getNativeObjectType(nativeType)); if (concrete != null && !concrete.getPropertyType(prop).isNone()) { cType = cType.unionWith(concrete); } } return cType; } return maybeAddAutoboxes(cType, jsType, prop); } private ConcreteType maybeAddAutoboxes( ConcreteType cType, JSType jsType, String prop) { jsType = jsType.restrictByNotNullOrUndefined(); if (jsType.isUnionType()) { for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { cType = maybeAddAutoboxes(cType, alt, prop); } return cType; } else if (jsType.isEnumElementType()) { return maybeAddAutoboxes( cType, jsType.toMaybeEnumElementType().getPrimitiveType(), prop); } if (jsType.autoboxesTo() != null) { JSType autoboxed = jsType.autoboxesTo(); return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed)); } else if (jsType.unboxesTo() != null) { return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType)); } return cType; } @Override public boolean isInvalidatingType(ConcreteType type) { // We will disallow types on functions so that 'prototype' is not renamed. // TODO(user): Support properties on functions as well. return (type == null) || type.isAll() || type.isFunction() || (type.isInstance() && invalidatingTypes.contains(type.toInstance().instanceType)); } @Override public ImmutableSet getTypesToSkipForType(ConcreteType type) { return ImmutableSet.of(type); } @Override public boolean isTypeToSkip(ConcreteType type) { // Skip anonymous object literals and enum types. return type.isInstance() && !(type.toInstance().isFunctionPrototype() || type.toInstance().instanceType.isInstanceType()); } @Override public ConcreteType restrictByNotNullOrUndefined(ConcreteType type) { // These are not represented in concrete types. return type; } @Override public Iterable getTypeAlternatives(ConcreteType type) { if (type.isUnion()) { return ((ConcreteUnionType) type).getAlternatives(); } else { return null; } } @Override public ConcreteType getTypeWithProperty(String field, ConcreteType type) { if (type.isInstance()) { ConcreteInstanceType instanceType = (ConcreteInstanceType) type; return instanceType.getInstanceTypeWithProperty(field); } else if (type.isFunction()) { if ("prototype".equals(field) || codingConvention.isSuperClassReference(field)) { return type; } } else if (type.isNone()) { // If the receiver is none, then this code is never reached. We will // return a new fake type to ensure that this access is renamed // differently from any other, so it can be easily removed. return new ConcreteUniqueType(++nextUniqueId); } else if (type.isUnion()) { // If only one has the property, return that. for (ConcreteType t : ((ConcreteUnionType) type).getAlternatives()) { ConcreteType ret = getTypeWithProperty(field, t); if (ret != null) { return ret; } } } return null; } @Override public ConcreteType getInstanceFromPrototype(ConcreteType type) { if (type.isInstance()) { ConcreteInstanceType instanceType = (ConcreteInstanceType) type; if (instanceType.isFunctionPrototype()) { return instanceType.getConstructorType().getInstanceType(); } } return null; } @Override public void recordInterfaces(ConcreteType type, ConcreteType relatedType, DisambiguateProperties.Property p) { // No need to record interfaces when using concrete types. } } } ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeCollectPropertyAssignments.0000644000175000017500000002004312115204405031664 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * A pass that looks for assignments to properties of an object or array * immediately following its creation using the abbreviated syntax. *

* E.g. {@code var a = [];a[0] = 0} is optimized to {@code var a = [0]} and * similarly for the object constructor. * */ public class PeepholeCollectPropertyAssignments extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node subtree) { if (!subtree.isScript() && !subtree.isBlock()) { return subtree; } boolean codeChanged = false; // Look for variable declarations or simple assignments // and start processing there. for (Node child = subtree.getFirstChild(); child != null; child = child.getNext()) { if (!child.isVar() && !NodeUtil.isExprAssign(child)) { continue; } if (!isPropertyAssignmentToName(child.getNext())) { // Quick check to see if there's anything to collapse. continue; } Preconditions.checkState(child.hasOneChild()); Node name = getName(child); if (!name.isName()) { // The assignment target is not a simple name. continue; } Node value = getValue(child); if (value == null || !isInterestingValue(value)) { // No initializer or not an Object or Array literal. continue; } Node propertyCandidate; while ((propertyCandidate = child.getNext()) != null) { // This does not infinitely loop because collectProperty always // removes propertyCandidate from its parent when it returns true. if (!collectProperty(propertyCandidate, name.getString(), value)) { break; } codeChanged = true; } } if (codeChanged) { reportCodeChange(); } return subtree; } private Node getName(Node n) { if (n.isVar()) { return n.getFirstChild(); } else if (NodeUtil.isExprAssign(n)) { return n.getFirstChild().getFirstChild(); } throw new IllegalStateException(); } private Node getValue(Node n) { if (n.isVar()) { return n.getFirstChild().getFirstChild(); } else if (NodeUtil.isExprAssign(n)) { return n.getFirstChild().getLastChild(); } throw new IllegalStateException(); } boolean isInterestingValue(Node n) { return n.isObjectLit() || n.isArrayLit(); } private boolean isPropertyAssignmentToName(Node propertyCandidate) { if (propertyCandidate == null) { return false; } // Must be an assignment... if (!NodeUtil.isExprAssign(propertyCandidate)) { return false; } Node expr = propertyCandidate.getFirstChild(); // to a property... Node lhs = expr.getFirstChild(); if (!NodeUtil.isGet(lhs)) { return false; } // of a variable. Node obj = lhs.getFirstChild(); if (!obj.isName()) { return false; } return true; } private boolean collectProperty( Node propertyCandidate, String name, Node value) { if (!isPropertyAssignmentToName(propertyCandidate)) { return false; } Node lhs = propertyCandidate.getFirstChild().getFirstChild(); // Must be an assignment to the recent variable... if (!name.equals(lhs.getFirstChild().getString())) { return false; } Node rhs = lhs.getNext(); // with a value that cannot change the values of the variables, if (mayHaveSideEffects(rhs) || NodeUtil.canBeSideEffected(rhs)) { return false; } // and does not have a reference to a variable initialized after it. if (!NodeUtil.isLiteralValue(rhs, true) && mightContainForwardReference(rhs, name)) { return false; } switch (value.getType()) { case Token.ARRAYLIT: if (!collectArrayProperty(value, propertyCandidate)) { return false; } break; case Token.OBJECTLIT: if (!collectObjectProperty(value, propertyCandidate)) { return false; } break; default: throw new IllegalStateException(); } return true; } private boolean collectArrayProperty( Node arrayLiteral, Node propertyCandidate) { Node assignment = propertyCandidate.getFirstChild(); final int sizeOfArrayAtStart = arrayLiteral.getChildCount(); int maxIndexAssigned = sizeOfArrayAtStart - 1; Node lhs = assignment.getFirstChild(); Node rhs = lhs.getNext(); if (!lhs.isGetElem()) { return false; } Node obj = lhs.getFirstChild(); Node property = obj.getNext(); // The left hand side must have a numeric index if (!property.isNumber()) { return false; } // that is a valid array index double dindex = property.getDouble(); if (!(dindex >= 0) // Handles NaN and negatives. || Double.isInfinite(dindex) || dindex > 0x7fffffffL) { return false; } int index = (int) dindex; if (dindex != index) { return false; } // that would not make the array so sparse that they take more space // when rendered than x[9]=1. if (maxIndexAssigned + 4 < index) { return false; } if (index > maxIndexAssigned) { while (maxIndexAssigned < index - 1) { // Pad the array if it is sparse. // So if array is [0] and integer 3 is assigned at index is 2, then // we want to produce [0,,2]. Node emptyNode = IR.empty().srcref(arrayLiteral); arrayLiteral.addChildToBack(emptyNode); ++maxIndexAssigned; } arrayLiteral.addChildToBack(rhs.detachFromParent()); } else { // An out of order assignment. Allow it if it's a hole. Node currentValue = arrayLiteral.getChildAtIndex(index); if (!currentValue.isEmpty()) { // We've already collected a value for this index. return false; } arrayLiteral.replaceChild(currentValue, rhs.detachFromParent()); } propertyCandidate.detachFromParent(); return true; } private boolean collectObjectProperty( Node objectLiteral, Node propertyCandidate) { Node assignment = propertyCandidate.getFirstChild(); Node lhs = assignment.getFirstChild(), rhs = lhs.getNext(); Node obj = lhs.getFirstChild(); Node property = obj.getNext(); // The property must be statically known. if (lhs.isGetElem() && (!property.isString() && !property.isNumber())) { return false; } String propertyName; if (property.isNumber()) { propertyName = NodeUtil.getStringValue(property); } else { propertyName = property.getString(); } Node newProperty = IR.stringKey(propertyName) .copyInformationFrom(property); // Preserve the quotedness of a property reference if (lhs.isGetElem()) { newProperty.setQuotedString(); } Node newValue = rhs.detachFromParent(); newProperty.addChildToBack(newValue); objectLiteral.addChildToBack(newProperty); propertyCandidate.detachFromParent(); return true; } private static boolean mightContainForwardReference( Node node, String varName) { if (node.isName()) { return varName.equals(node.getString()); } for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { if (mightContainForwardReference(child, varName)) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CombinedCompilerPass.java0000644000175000017500000001457212115204405027552 0ustar apoapo/* * Copyright 2007 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.NodeTraversal.Callback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.rhino.Node; import java.util.List; /** *

A compiler pass combining multiple {@link Callback} * and {@link ScopedCallback} objects. This pass can be used to separate * logically different verifications without incurring any additional traversal * and CFG generation costs.

* *

Due to this compiler pass' nature, none of the callbacks may mutate * the parse tree.

* *

TODO(user): * This combined pass is currently limited in the type of callbacks it can * combine due to the difficulty of handling NodeTraversal's methods that * initiate more recursion (e.g., {@link NodeTraversal#traverse(Node)} and * {@link NodeTraversal#traverseInnerNode(Node, Node, Scope)}). The * {@link NodeTraversal} object passed to the individual callbacks should * be instrumented to emulate the correct behavior. For instance, * one could create a {@link NodeTraversal} whose * {@link NodeTraversal#traverseInnerNode(Node, Node, Scope)} ties * back into this compiler pass to give it context about what combined * passes are doing.

* */ final class CombinedCompilerPass implements HotSwapCompilerPass, ScopedCallback { /** The callbacks that this pass combines. */ private final CallbackWrapper[] callbacks; private final AbstractCompiler compiler; /** * Creates a combined compiler pass. * @param compiler the compiler */ CombinedCompilerPass( AbstractCompiler compiler, Callback... callbacks) { this(compiler, Lists.newArrayList(callbacks)); } CombinedCompilerPass( AbstractCompiler compiler, List callbacks) { this.compiler = compiler; this.callbacks = new CallbackWrapper[callbacks.size()]; for (int i = 0; i < callbacks.size(); i++) { this.callbacks[i] = new CallbackWrapper(callbacks.get(i)); } } static void traverse(AbstractCompiler compiler, Node root, List callbacks) { if (callbacks.size() == 1) { NodeTraversal.traverse(compiler, root, callbacks.get(0)); } else { (new CombinedCompilerPass(compiler, callbacks)).process(null, root); } } /** * Maintains information about a callback in order to simulate it being the * exclusive client of the shared {@link NodeTraversal}. In particular, this * class simulates abbreviating the traversal when the wrapped callback * returns false for * {@link Callback#shouldTraverse(NodeTraversal, Node, Node)}. * The callback becomes inactive (i.e., traversal messages are not sent to it) * until the main traversal revisits the node during the post-order visit. * */ private static class CallbackWrapper { /** The callback being wrapped. Never null. */ private final Callback callback; /** * if (callback instanceof ScopedCallback), then scopedCallback points * to an instance of ScopedCallback, otherwise scopedCallback points to null */ private final ScopedCallback scopedCallback; /** * The node that {@link Callback#shouldTraverse(NodeTraversal, Node, Node)} * returned false for. The wrapped callback doesn't receive messages until * after this node is revisited in the post-order traversal. */ private Node waiting = null; private CallbackWrapper(Callback callback) { this.callback = callback; if (callback instanceof ScopedCallback) { scopedCallback = (ScopedCallback) callback; } else { scopedCallback = null; } } /** * Visits the node unless the wrapped callback is inactive. Activates the * callback if appropriate. */ void visitOrMaybeActivate(NodeTraversal t, Node n, Node parent) { if (isActive()) { callback.visit(t, n, parent); } else if (waiting == n) { waiting = null; } } void shouldTraverseIfActive(NodeTraversal t, Node n, Node parent) { if (isActive() && !callback.shouldTraverse(t, n, parent)) { waiting = n; } } void enterScopeIfActive(NodeTraversal t) { if (isActive() && scopedCallback != null) { scopedCallback.enterScope(t); } } void exitScopeIfActive(NodeTraversal t) { if (isActive() && scopedCallback != null) { scopedCallback.exitScope(t); } } boolean isActive() { return waiting == null; } } @Override public final void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { for (CallbackWrapper callback : callbacks) { callback.shouldTraverseIfActive(t, n, parent); } // Note that this method could return false if all callbacks are inactive. // This apparent optimization would make this method more expensive // in the typical case where not all nodes are inactive. It is // very unlikely that many all callbacks would be inactive at the same // time (indeed, there are several checking passes that never return false). return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { for (CallbackWrapper callback : callbacks) { callback.visitOrMaybeActivate(t, n, parent); } } @Override public void enterScope(NodeTraversal t) { for (CallbackWrapper callback : callbacks) { callback.enterScopeIfActive(t); } } @Override public void exitScope(NodeTraversal t) { for (CallbackWrapper callback : callbacks) { callback.exitScopeIfActive(t); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Normalize.java0000644000175000017500000007217212115204405025450 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.MakeDeclaredNamesUnique.BoilerplateRenamer; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; import java.util.Set; /** * The goal with this pass is to simplify the other passes, * by making less complex statements. * * Starting with statements like: * var a = 0, b = foo(); * * Which become: * var a = 0; * var b = foo(); * * The key here is only to break down things that help the other passes * and can be put back together in a form that is at least as small when * all is said and done. * * This pass currently does the following: * 1) Simplifies the AST by splitting var statements, moving initializers * out of for loops, and converting whiles to fors. * 2) Moves hoisted functions to the top of function scopes. * 3) Rewrites unhoisted named function declarations to be var declarations. * 4) Makes all variable names globally unique (extern or otherwise) so that * no value is ever shadowed (note: "arguments" may require special * handling). * 5) Removes duplicate variable declarations. * 6) Marks constants with the IS_CONSTANT_NAME annotation. * 7) Finds properties marked @expose, and rewrites them in [] notation. * * @author johnlenz@google.com (johnlenz) */ // public for ReplaceDebugStringsTest class Normalize implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; private static final boolean CONVERT_WHILE_TO_FOR = true; static final boolean MAKE_LOCAL_NAMES_UNIQUE = true; public static final DiagnosticType CATCH_BLOCK_VAR_ERROR = DiagnosticType.error( "JSC_CATCH_BLOCK_VAR_ERROR", "The use of scope variable {0} is not allowed within a catch block " + "with a catch exception of the same name."); Normalize(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; // TODO(nicksantos): assertOnChange should only be true if the tree // is normalized. } static Node parseAndNormalizeSyntheticCode( AbstractCompiler compiler, String code, String prefix) { Node js = compiler.parseSyntheticCode(code); NodeTraversal.traverse(compiler, js, new Normalize.NormalizeStatements(compiler, false)); NodeTraversal.traverse( compiler, js, new MakeDeclaredNamesUnique( new BoilerplateRenamer( compiler.getCodingConvention(), compiler.getUniqueNameIdSupplier(), prefix))); return js; } static Node parseAndNormalizeTestCode( AbstractCompiler compiler, String code) { Node js = compiler.parseTestCode(code); NodeTraversal.traverse(compiler, js, new Normalize.NormalizeStatements(compiler, false)); NodeTraversal.traverse( compiler, js, new MakeDeclaredNamesUnique()); return js; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public void process(Node externs, Node root) { new NodeTraversal( compiler, new NormalizeStatements(compiler, assertOnChange)) .traverseRoots(externs, root); if (MAKE_LOCAL_NAMES_UNIQUE) { MakeDeclaredNamesUnique renamer = new MakeDeclaredNamesUnique(); NodeTraversal t = new NodeTraversal(compiler, renamer); t.traverseRoots(externs, root); } // It is important that removeDuplicateDeclarations runs after // MakeDeclaredNamesUnique in order for catch block exception names to be // handled properly. Specifically, catch block exception names are // only valid within the catch block, but our current Scope logic // has no concept of this and includes it in the containing function // (or global scope). MakeDeclaredNamesUnique makes the catch exception // names unique so that removeDuplicateDeclarations() will properly handle // cases where a function scope variable conflict with a exception name: // function f() { // try {throw 0;} catch(e) {e; /* catch scope 'e'*/} // var e = 1; // f scope 'e' // } // otherwise 'var e = 1' would be rewritten as 'e = 1'. // TODO(johnlenz): Introduce a separate scope for catch nodes. removeDuplicateDeclarations(externs, root); new PropagateConstantAnnotationsOverVars(compiler, assertOnChange) .process(externs, root); FindExposeAnnotations findExposeAnnotations = new FindExposeAnnotations(); NodeTraversal.traverse(compiler, root, findExposeAnnotations); if (!findExposeAnnotations.exposedProperties.isEmpty()) { NodeTraversal.traverse(compiler, root, new RewriteExposedProperties( findExposeAnnotations.exposedProperties)); } if (!compiler.getLifeCycleStage().isNormalized()) { compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED); } } /** * Find all the @expose annotations. */ private static class FindExposeAnnotations extends AbstractPostOrderCallback { private final Set exposedProperties = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isExprAssign(n)) { Node assign = n.getFirstChild(); Node lhs = assign.getFirstChild(); if (lhs.isGetProp() && isMarkedExpose(assign)) { exposedProperties.add(lhs.getLastChild().getString()); } } else if (n.isStringKey() && isMarkedExpose(n)) { exposedProperties.add(n.getString()); } else if (n.isGetProp() && n.getParent().isExprResult() && isMarkedExpose(n)) { exposedProperties.add(n.getLastChild().getString()); } } private boolean isMarkedExpose(Node n) { JSDocInfo info = n.getJSDocInfo(); return info != null && info.isExpose(); } } /** * Rewrite all exposed properties in [] form. */ private class RewriteExposedProperties extends AbstractPostOrderCallback { private final Set exposedProperties; RewriteExposedProperties(Set exposedProperties) { this.exposedProperties = exposedProperties; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { String propName = n.getLastChild().getString(); if (exposedProperties.contains(propName)) { Node obj = n.removeFirstChild(); Node prop = n.removeFirstChild(); n.getParent().replaceChild(n, IR.getelem(obj, prop)); compiler.reportCodeChange(); } } else if (n.isStringKey()) { String propName = n.getString(); if (exposedProperties.contains(propName)) { n.setQuotedString(); compiler.reportCodeChange(); } } } } /** * Propagate constant annotations over the Var graph. */ static class PropagateConstantAnnotationsOverVars extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; PropagateConstantAnnotationsOverVars( AbstractCompiler compiler, boolean forbidChanges) { this.compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.isName()) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top-level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } boolean shouldBeConstant = (info != null && info.isConstant()) || NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent); boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (shouldBeConstant && !isMarkedConstant) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name %s is not annotated as constant.", name); } else { Preconditions.checkState(expectedConst == isConst, "The name %s should not be annotated as constant.", name); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name %s is not consistently annotated as constant.", name); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. * - Add constant annotations based on coding convention. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription) { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(n); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); Node empty = IR.empty(); empty.copyInformationFrom(n); n.addChildBefore(empty, expr); n.addChildAfter(empty.cloneNode(), expr); reportCodeChange("WHILE node"); } break; case Token.FUNCTION: normalizeFunctionDeclaration(n); break; case Token.NAME: case Token.STRING: case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) { annotateConstantsByConvention(n, parent); } break; case Token.CAST: parent.replaceChild(n, n.removeFirstChild()); break; } } /** * Mark names and properties that are constants by convention. */ private void annotateConstantsByConvention(Node n, Node parent) { Preconditions.checkState( n.isName() || n.isString() || n.isStringKey() || n.isGetterDef() || n.isSetterDef()); // There are only two cases where a string token // may be a variable reference: The right side of a GETPROP // or an OBJECTLIT key. boolean isObjLitKey = NodeUtil.isObjectLitKey(n); boolean isProperty = isObjLitKey || (parent.isGetProp() && parent.getLastChild() == n); if (n.isName() || isProperty) { boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (!isMarkedConstant && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent)) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ private void normalizeFunctionDeclaration(Node n) { Preconditions.checkState(n.isFunction()); if (!NodeUtil.isFunctionExpression(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { rewriteFunctionDeclaration(n); } } /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ private void rewriteFunctionDeclaration(Node n) { // Prepare a spot for the function. Node oldNameNode = n.getFirstChild(); Node fnNameNode = oldNameNode.cloneNode(); Node var = IR.var(fnNameNode).srcref(n); // Prepare the function oldNameNode.setString(""); // Move the function Node parent = n.getParent(); parent.replaceChild(n, var); fnNameNode.addChildToFront(n); reportCodeChange("Function declaration"); } /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations(Node n) { if (n.isLabel()) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.isLabel()) { extractForInitializer(n, null, null); } // Only inspect the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock(n)) { splitVarDeclarations(n); } if (n.isFunction()) { moveNamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.isLabel()); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.FOR: case Token.WHILE: case Token.DO: return; default: Node block = IR.block(); block.copyInformationFrom(last); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (NodeUtil.isForIn(c)) { Node first = c.getFirstChild(); if (first.isVar()) { // Transform: // for (var a = 1 in b) {} // to: // var a = 1; for (a in b) {}; Node newStatement = first; // Clone just the node, to remove any initialization. Node name = newStatement.getFirstChild().cloneNode(); first.getParent().replaceChild(first, name); insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR-IN var declaration"); } } else if (!c.getFirstChild().isEmpty()) { Node init = c.getFirstChild(); Node empty = IR.empty(); empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.isVar()) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.isVar()) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = IR.var(name).srcref(n); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) { Preconditions.checkState( functionBody.getParent().isFunction()); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Read the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node externs, Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverseRoots(externs, root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { private Set hasOkDuplicateDeclaration = Sets.newHashSet(); /** * Remove duplicate VAR declarations encountered discovered during * scope creation. */ @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Var v = s.getVar(name); if (v != null && s.isGlobal()) { // We allow variables to be duplicate declared if one // declaration appears in source and the other in externs. // This deals with issues where a browser built-in is declared // in one browser but not in another. if (v.isExtern() && !input.isExtern()) { if (hasOkDuplicateDeclaration.add(v)) { return; } } } // If name is "arguments", Var maybe null. if (v != null && v.getParentNode().isCatch()) { // Redeclaration of a catch expression variable is hard to model // without support for "with" expressions. // The ECMAScript spec (section 12.14), declares that a catch // "catch (e) {}" is handled like "with ({'e': e}) {}" so that // "var e" would refer to the scope variable, but any following // reference would still refer to "e" of the catch expression. // Until we have support for this disallow it. // Currently the Scope object adds the catch expression to the // function scope, which is technically not true but a good // approximation for most uses. // TODO(johnlenz): Consider improving how scope handles catch // expression. // Use the name of the var before it was made unique. name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName( name); compiler.report( JSError.make( input.getName(), n, CATCH_BLOCK_VAR_ERROR, name)); } else if (v != null && parent.isFunction()) { if (v.getParentNode().isVar()) { s.undeclare(v); s.declare(name, n, n.getJSType(), v.input); replaceVarWithAssignment(v.getNameNode(), v.getParentNode(), v.getParentNode().getParent()); } } else if (parent.isVar()) { Preconditions.checkState(parent.hasOneChild()); replaceVarWithAssignment(n, parent, parent.getParent()); } } /** * Remove the parent VAR. There are three cases that need to be handled: * 1) "var a = b;" which is replaced with "a = b" * 2) "label:var a;" which is replaced with "label:;". Ideally, the * label itself would be removed but that is not possible in the * context in which "onRedeclaration" is called. * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." * Cases we don't need to handle are VARs with multiple children, * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = IR.assign(n, value); replacement.copyInformationFrom(parent); gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); } else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.isFor()) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for (var a;;)..." as those are moved out // as part of the other normalizations. parent.removeChild(n); gramps.replaceChild(parent, n); } else { Preconditions.checkState(gramps.isLabel()); // We should never get here. LABELs with a single VAR statement should // already have been normalized to have a BLOCK. throw new IllegalStateException("Unexpected LABEL"); } } reportCodeChange("Duplicate VAR declaration"); } } /** * A simple class that causes scope to be created. */ private final class ScopeTicklingCallback implements NodeTraversal.ScopedCallback { @Override public void enterScope(NodeTraversal t) { // Cause the scope to be created, which will cause duplicate // to be found. t.getScope(); } @Override public void exitScope(NodeTraversal t) { // Nothing to do. } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Nothing to do. } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DependencyOptions.java0000644000175000017500000001064112115204405027133 0ustar apoapo/* * Copyright 2011 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.Sets; import java.io.Serializable; import java.util.Collection; import java.util.Set; /** * Options for how to manage dependencies between input files. * * Dependency information is usually pulled out from the JS code by * looking for primitive dependency functions (like Closure Library's * goog.provide/goog.require). Analysis of this dependency information is * controlled by {@code CodingConvention}, which lets you define those * dependency primitives. * * This options class determines how we use that dependency information * to change how code is built. * * @author nicksantos@google.com (Nick Santos) */ public class DependencyOptions implements Serializable { private static final long serialVersionUID = 1L; private boolean sortDependencies = false; private boolean pruneDependencies = false; private boolean dropMoochers = false; private final Set entryPoints = Sets.newHashSet(); /** * Enables or disables dependency sorting mode. * * If true, we will sort the input files based on dependency information * in them. Otherwise, we will use the order of files specified * on the command-line. * @return this for easy building. */ public DependencyOptions setDependencySorting(boolean enabled) { this.sortDependencies = enabled; return this; } /** * Enables or disables dependency pruning mode. * * In dependency pruning mode, we will look for all files that provide a * symbol. Unless that file is a transitive dependency of a file that * we're using, we will remove it from the compilation job. * * This does not affect how we handle files that do not provide symbols. * See setMoocherDropping for information on how these are handled. * * @return this for easy chaining. */ public DependencyOptions setDependencyPruning(boolean enabled) { this.pruneDependencies = enabled; return this; } /** * Enables or disables moocher dropping mode. * * A 'moocher' is a file that does not provide any symbols (though they * may require symbols). This is usually because they don't want to * tie themselves to a particular dependency system (e.g., Closure's * goog.provide, CommonJS modules). So they rely on other people to * manage dependencies on them. * * If true, we drop these files when we prune dependencies. * If false, we always keep these files an anything they depend on. * The default is false. * * Notice that this option only makes sense if dependency pruning is on, * and a set of entry points is specified. * * @return this for easy chaining. */ public DependencyOptions setMoocherDropping(boolean enabled) { this.dropMoochers = enabled; return this; } /** * Adds a collection of symbols to always keep. * * In dependency pruning mode, we will automatically keep all the * transitive dependencies of these symbols. * * The syntactic form of a symbol depends on the type of dependency * primitives we're using. For example, goog.provide('foo.bar') * provides the symbol 'foo.bar'. * * Entry points can be scoped to a module by specifying 'mod2:foo.bar'. * * @return this for easy chaining. */ public DependencyOptions setEntryPoints(Collection symbols) { entryPoints.clear(); entryPoints.addAll(symbols); return this; } /** Returns whether re-ordering of files is needed. */ boolean needsManagement() { return sortDependencies || pruneDependencies; } boolean shouldSortDependencies() { return sortDependencies; } boolean shouldPruneDependencies() { return pruneDependencies; } boolean shouldDropMoochers() { return pruneDependencies && dropMoochers; } Collection getEntryPoints() { return entryPoints; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionToBlockMutator.java0000644000175000017500000004120412115204405030117 0ustar apoapo/* * 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 static com.google.javascript.jscomp.FunctionArgumentInjector.THIS_MARKER; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.MakeDeclaredNamesUnique.InlineRenamer; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * A class to transform the body of a function into a generic block suitable * for inlining. * * @author johnlenz@google.com (John Lenz) */ class FunctionToBlockMutator { private AbstractCompiler compiler; private Supplier safeNameIdSupplier; FunctionToBlockMutator( AbstractCompiler compiler, Supplier safeNameIdSupplier) { this.compiler = compiler; this.safeNameIdSupplier = safeNameIdSupplier; } /** * @param fnName The name to use when preparing human readable names. * @param fnNode The function to prepare. * @param callNode The call node that will be replaced. * @param resultName Function results should be assigned to this name. * @param needsDefaultResult Whether the result value must be set. * @param isCallInLoop Whether the function body must be prepared to be * injected into the body of a loop. * @return A clone of the function body mutated to be suitable for injection * as a statement into another code block. */ Node mutate(String fnName, Node fnNode, Node callNode, String resultName, boolean needsDefaultResult, boolean isCallInLoop) { Node newFnNode = fnNode.cloneTree(); // Now that parameter names have been replaced, make sure all the local // names are unique, to allow functions to be inlined multiple times // without causing conflicts. makeLocalNamesUnique(newFnNode, isCallInLoop); // Function declarations must be rewritten as function expressions as // they will be within a block and normalization prevents function // declarations within block as browser implementations vary. rewriteFunctionDeclarations(newFnNode.getLastChild()); // TODO(johnlenz): Mark NAME nodes constant for parameters that are not // modified. Set namesToAlias = FunctionArgumentInjector.findModifiedParameters(newFnNode); LinkedHashMap args = FunctionArgumentInjector.getFunctionCallParameterMap( newFnNode, callNode, this.safeNameIdSupplier); boolean hasArgs = !args.isEmpty(); if (hasArgs) { FunctionArgumentInjector.maybeAddTempsForCallArguments( newFnNode, args, namesToAlias, compiler.getCodingConvention()); } Node newBlock = NodeUtil.getFunctionBody(newFnNode); // Make the newBlock insertable . newBlock.detachFromParent(); if (hasArgs) { Node inlineResult = aliasAndInlineArguments(newBlock, args, namesToAlias); Preconditions.checkState(newBlock == inlineResult); } // // For calls inlined into loops, VAR declarations are not reinitialized to // undefined as they would have been if the function were called, so ensure // that they are properly initialized. // if (isCallInLoop) { fixUnitializedVarDeclarations(newBlock); } String labelName = getLabelNameForFunction(fnName); Node injectableBlock = replaceReturns( newBlock, resultName, labelName, needsDefaultResult); Preconditions.checkState(injectableBlock != null); return injectableBlock; } /** * @param n The node to inspect */ private void rewriteFunctionDeclarations(Node n) { if (n.isFunction()) { if (NodeUtil.isFunctionDeclaration(n)) { // Rewrite: function f() {} ==> var f = function() {} Node fnNameNode = n.getFirstChild(); Node name = IR.name(fnNameNode.getString()).srcref(fnNameNode); Node var = IR.var(name).srcref(n); fnNameNode.setString(""); // Add the VAR, remove the FUNCTION n.getParent().replaceChild(n, var); // readd the function as a function expression name.addChildToFront(n); } return; } for (Node c = n.getFirstChild(), next; c != null; c = next) { next = c.getNext(); // We may rewrite "c" rewriteFunctionDeclarations(c); } } /** * For all VAR node with uninitialized declarations, set * the values to be "undefined". */ private void fixUnitializedVarDeclarations(Node n) { // Inner loop structure must already have logic to initialize its // variables. In particular FOR-IN structures must not be modified. if (NodeUtil.isLoopStructure(n)) { return; } // For all VARs if (n.isVar()) { Node name = n.getFirstChild(); // It isn't initialized. if (!name.hasChildren()) { Node srcLocation = name; name.addChildToBack(NodeUtil.newUndefinedNode(srcLocation)); } return; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { fixUnitializedVarDeclarations(c); } } /** * Fix-up all local names to be unique for this subtree. * @param fnNode A mutable instance of the function to be inlined. */ private void makeLocalNamesUnique(Node fnNode, boolean isCallInLoop) { Supplier idSupplier = compiler.getUniqueNameIdSupplier(); // Make variable names unique to this instance. NodeTraversal.traverse( compiler, fnNode, new MakeDeclaredNamesUnique( new InlineRenamer( compiler.getCodingConvention(), idSupplier, "inline_", isCallInLoop))); // Make label names unique to this instance. new RenameLabels(compiler, new LabelNameSupplier(idSupplier), false) .process(null, fnNode); } static class LabelNameSupplier implements Supplier { final Supplier idSupplier; LabelNameSupplier(Supplier idSupplier) { this.idSupplier = idSupplier; } @Override public String get() { return "JSCompiler_inline_label_" + idSupplier.get(); } } /** * Create a unique label name. */ private String getLabelNameForFunction(String fnName){ String name = (fnName == null || fnName.isEmpty()) ? "anon" : fnName; return "JSCompiler_inline_label_" + name + "_" + safeNameIdSupplier.get(); } /** * Create a unique "this" name. */ private String getUniqueThisName() { return "JSCompiler_inline_this_" + safeNameIdSupplier.get(); } /** * Inlines the arguments within the node tree using the given argument map, * replaces "unsafe" names with local aliases. * * The aliases for unsafe require new VAR declarations, so this function * can not be used in for direct CALL node replacement as VAR nodes can not be * created there. * * @return The node or its replacement. */ private Node aliasAndInlineArguments( Node fnTemplateRoot, LinkedHashMap argMap, Set namesToAlias) { if (namesToAlias == null || namesToAlias.isEmpty()) { // There are no names to alias, just inline the arguments directly. Node result = FunctionArgumentInjector.inject( compiler, fnTemplateRoot, null, argMap); Preconditions.checkState(result == fnTemplateRoot); return result; } else { // Create local alias of names that can not be safely // used directly. // An arg map that will be updated to contain the // safe aliases. Map newArgMap = Maps.newHashMap(argMap); // Declare the alias in the same order as they // are declared. List newVars = Lists.newLinkedList(); // NOTE: argMap is a linked map so we get the parameters in the // order that they were declared. for (Entry entry : argMap.entrySet()) { String name = entry.getKey(); if (namesToAlias.contains(name)) { if (name.equals(THIS_MARKER)) { boolean referencesThis = NodeUtil.referencesThis(fnTemplateRoot); // Update "this", this is only necessary if "this" is referenced // and the value of "this" is not Token.THIS, or the value of "this" // has side effects. Node value = entry.getValue(); if (!value.isThis() && (referencesThis || NodeUtil.mayHaveSideEffects(value, compiler))) { String newName = getUniqueThisName(); Node newValue = entry.getValue().cloneTree(); Node newNode = NodeUtil.newVarNode(newName, newValue) .copyInformationFromForTree(newValue); newVars.add(0, newNode); // Remove the parameter from the list to replace. newArgMap.put(THIS_MARKER, IR.name(newName) .srcrefTree(newValue)); } } else { Node newValue = entry.getValue().cloneTree(); Node newNode = NodeUtil.newVarNode(name, newValue) .copyInformationFromForTree(newValue); newVars.add(0, newNode); // Remove the parameter from the list to replace. newArgMap.remove(name); } } } // Inline the arguments. Node result = FunctionArgumentInjector.inject( compiler, fnTemplateRoot, null, newArgMap); Preconditions.checkState(result == fnTemplateRoot); // Now that the names have been replaced, add the new aliases for // the old names. for (Node n : newVars) { fnTemplateRoot.addChildToFront(n); } return result; } } /** * Convert returns to assignments and breaks, as needed. * For example, with a labelName of 'foo': * { * return a; * } * becomes: * foo: { * a; * break foo; * } * or * foo: { * resultName = a; * break foo; * } * * @param resultMustBeSet Whether the result must always be set to a value. * @return The node containing the transformed block, this may be different * than the passed in node 'block'. */ private static Node replaceReturns( Node block, String resultName, String labelName, boolean resultMustBeSet) { Preconditions.checkNotNull(block); Preconditions.checkNotNull(labelName); Node root = block; boolean hasReturnAtExit = false; int returnCount = NodeUtil.getNodeTypeReferenceCount( block, Token.RETURN, new NodeUtil.MatchShallowStatement()); if (returnCount > 0) { hasReturnAtExit = hasReturnAtExit(block); // TODO(johnlenz): Simpler not to special case this, // and let it be optimized later. if (hasReturnAtExit) { convertLastReturnToStatement(block, resultName); returnCount--; } if (returnCount > 0) { // A label and breaks are needed. // Add the breaks replaceReturnWithBreak(block, null, resultName, labelName); // Add label Node name = IR.labelName(labelName).srcref(block); Node label = IR.label(name, block).srcref(block); Node newRoot = IR.block().srcref(block); newRoot.addChildrenToBack(label); // The label is now the root. root = newRoot; } } // If there wasn't an return at the end of the function block, and we need // a result, add one to the block. if (resultMustBeSet && !hasReturnAtExit && resultName != null) { addDummyAssignment(block, resultName); } return root; } /********************************************************************** * Functions following here are general node transformation functions **********************************************************************/ /** * Example: * a = (void) 0; */ private static void addDummyAssignment(Node node, String resultName) { Preconditions.checkArgument(node.isBlock()); // A result is needed create a dummy value. Node srcLocation = node; Node retVal = NodeUtil.newUndefinedNode(srcLocation); Node resultNode = createAssignStatementNode(resultName, retVal); resultNode.copyInformationFromForTree(node); node.addChildrenToBack(resultNode); } /** * Replace the 'return' statement with its child expression. * "return foo()" becomes "foo()" or "resultName = foo()" * "return" is removed or becomes "resultName = void 0". * * @param block * @param resultName */ private static void convertLastReturnToStatement( Node block, String resultName) { Node ret = block.getLastChild(); Preconditions.checkArgument(ret.isReturn()); Node resultNode = getReplacementReturnStatement(ret, resultName); if (resultNode == null) { block.removeChild(ret); } else { resultNode.copyInformationFromForTree(ret); block.replaceChild(ret, resultNode); } } /** * Create a valid statement Node containing an assignment to name of the * given expression. */ private static Node createAssignStatementNode(String name, Node expression) { // Create 'name = result-expression;' statement. // EXPR (ASSIGN (NAME, EXPRESSION)) Node nameNode = IR.name(name); Node assign = IR.assign(nameNode, expression); return NodeUtil.newExpr(assign); } /** * Replace the 'return' statement with its child expression. * If the result is needed (resultName != null): * "return foo()" becomes "resultName = foo()" * "return" becomes "resultName = void 0". * Otherwise: * "return foo()" becomes "foo()" * "return", null is returned. */ private static Node getReplacementReturnStatement( Node node, String resultName) { Node resultNode = null; Node retVal = null; if (node.hasChildren()) { // Clone the child as the child hasn't been removed // from the node yet. retVal = node.getFirstChild().cloneTree(); } if (resultName == null) { if (retVal != null) { resultNode = NodeUtil.newExpr(retVal); // maybe null. } } else { if (retVal == null) { // A result is needed create a dummy value. Node srcLocation = node; retVal = NodeUtil.newUndefinedNode(srcLocation); } // Create a "resultName = retVal;" statement. resultNode = createAssignStatementNode(resultName, retVal); } return resultNode; } /** * @return Whether the given block end with an return statement. */ private static boolean hasReturnAtExit(Node block) { // Only inline functions that return something (empty returns // will be handled by ConstFolding+EmptyFunctionRemoval) return (block.getLastChild().isReturn()); } /** * Replace the 'return' statement with its child expression. * "return foo()" becomes "{foo(); break;}" or * "{resultName = foo(); break;}" * "return" becomes {break;} or "{resultName = void 0;break;}". */ private static Node replaceReturnWithBreak(Node current, Node parent, String resultName, String labelName) { if (current.isFunction() || current.isExprResult()) { // Don't recurse into functions definitions, and expressions can't // contain RETURN nodes. return current; } if (current.isReturn()) { Preconditions.checkState(NodeUtil.isStatementBlock(parent)); Node resultNode = getReplacementReturnStatement(current, resultName); Node breakNode = IR.breakNode(IR.labelName(labelName)); // Replace the node in parent, and reset current to the first new child. breakNode.copyInformationFromForTree(current); parent.replaceChild(current, breakNode); if (resultNode != null) { resultNode.copyInformationFromForTree(current); parent.addChildBefore(resultNode, breakNode); } current = breakNode; } else { for (Node c = current.getFirstChild(); c != null; c = c.getNext()) { // c may be replaced. c = replaceReturnWithBreak(c, current, resultName, labelName); } } return current; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AnalyzeNameReferences.java0000644000175000017500000001116212115204405027706 0ustar apoapo/* * 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.Sets; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NameReferenceGraph.Reference; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.rhino.Node; /** * Analyzes names and references usage by determining: *

    *
  1. If the name is reachable from the {@link NameReferenceGraph#MAIN}.
  2. *
  3. as well as the deepest common module that references it.
  4. *
* * The two pieces of information will be annotated to {@link NameReferenceGraph} * by {@link NameInfo} objects. * * This is an analysis based on {@link AnalyzeNameReferences} using the more * accurate graph and will soon replace it. * */ class AnalyzeNameReferences implements CompilerPass { private NameReferenceGraph graph; private final JSModuleGraph moduleGraph; private final AbstractCompiler compiler; AnalyzeNameReferences(AbstractCompiler compiler) { this.compiler = compiler; this.moduleGraph = compiler.getModuleGraph(); } @Override public void process(Node externs, Node root) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); gc.process(externs, root); graph = gc.getNameReferenceGraph(); FixedPointGraphTraversal t = FixedPointGraphTraversal.newTraversal(new PropagateReferences()); getInfo(graph.MAIN).markReference(null); t.computeFixedPoint(graph, Sets.newHashSet(graph.MAIN)); } public NameReferenceGraph getGraph() { return graph; } private class PropagateReferences implements EdgeCallback { @Override public boolean traverseEdge(Name start, Reference edge, Name dest) { NameInfo startInfo = getInfo(start); NameInfo destInfo = getInfo(dest); if (startInfo.isReferenced()) { JSModule startModule = startInfo.getDeepestCommonModuleRef(); if (startModule != null && moduleGraph.dependsOn(startModule, edge.getModule())) { return destInfo.markReference(startModule); } else { return destInfo.markReference(edge.getModule()); } } return false; } } private NameInfo getInfo(Name symbol) { GraphNode name = graph.getNode(symbol); NameInfo info = name.getAnnotation(); if (info == null) { info = new NameInfo(); name.setAnnotation(info); } return info; } final class NameInfo implements Annotation { private boolean referenced = false; private JSModule deepestCommonModuleRef = null; /** Determines whether we've marked a reference to this property name. */ boolean isReferenced() { return referenced; } /** * Returns the deepest common module of all the references to this * property. */ JSModule getDeepestCommonModuleRef() { return deepestCommonModuleRef; } /** * Mark a reference in a given module to this property name, and record * the deepest common module reference. * @param module The module where it was referenced. * @return Whether the name info has changed. */ boolean markReference(JSModule module) { boolean hasChanged = false; if (!referenced) { referenced = true; hasChanged = true; } if (moduleGraph != null) { JSModule originalDeepestCommon = deepestCommonModuleRef; if (deepestCommonModuleRef == null) { deepestCommonModuleRef = module; } else { deepestCommonModuleRef = moduleGraph.getDeepestCommonDependencyInclusive( deepestCommonModuleRef, module); } if (originalDeepestCommon != deepestCommonModuleRef) { hasChanged = true; } } return hasChanged; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ScopedAliases.java0000644000175000017500000003771112115204405026227 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CompilerOptions.AliasTransformation; import com.google.javascript.jscomp.CompilerOptions.AliasTransformationHandler; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.SourcePosition; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Process aliases in goog.scope blocks. * * goog.scope(function() { * var dom = goog.dom; * var DIV = dom.TagName.DIV; * * dom.createElement(DIV); * }); * * should become * * goog.dom.createElement(goog.dom.TagName.DIV); * * @author robbyw@google.com (Robby Walker) */ class ScopedAliases implements HotSwapCompilerPass { /** Name used to denote an scoped function block used for aliasing. */ static final String SCOPING_METHOD_NAME = "goog.scope"; private final AbstractCompiler compiler; private final PreprocessorSymbolTable preprocessorSymbolTable; private final AliasTransformationHandler transformationHandler; // Errors static final DiagnosticType GOOG_SCOPE_USED_IMPROPERLY = DiagnosticType.error( "JSC_GOOG_SCOPE_USED_IMPROPERLY", "The call to goog.scope must be alone in a single statement."); static final DiagnosticType GOOG_SCOPE_HAS_BAD_PARAMETERS = DiagnosticType.error( "JSC_GOOG_SCOPE_HAS_BAD_PARAMETERS", "The call to goog.scope must take only a single parameter. It must" + " be an anonymous function that itself takes no parameters."); static final DiagnosticType GOOG_SCOPE_REFERENCES_THIS = DiagnosticType.error( "JSC_GOOG_SCOPE_REFERENCES_THIS", "The body of a goog.scope function cannot reference 'this'."); static final DiagnosticType GOOG_SCOPE_USES_RETURN = DiagnosticType.error( "JSC_GOOG_SCOPE_USES_RETURN", "The body of a goog.scope function cannot use 'return'."); static final DiagnosticType GOOG_SCOPE_USES_THROW = DiagnosticType.error( "JSC_GOOG_SCOPE_USES_THROW", "The body of a goog.scope function cannot use 'throw'."); static final DiagnosticType GOOG_SCOPE_ALIAS_REDEFINED = DiagnosticType.error( "JSC_GOOG_SCOPE_ALIAS_REDEFINED", "The alias {0} is assigned a value more than once."); static final DiagnosticType GOOG_SCOPE_NON_ALIAS_LOCAL = DiagnosticType.error( "JSC_GOOG_SCOPE_NON_ALIAS_LOCAL", "The local variable {0} is in a goog.scope and is not an alias."); ScopedAliases(AbstractCompiler compiler, @Nullable PreprocessorSymbolTable preprocessorSymbolTable, AliasTransformationHandler transformationHandler) { this.compiler = compiler; this.preprocessorSymbolTable = preprocessorSymbolTable; this.transformationHandler = transformationHandler; } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } @Override public void hotSwapScript(Node root, Node originalRoot) { Traversal traversal = new Traversal(); NodeTraversal.traverse(compiler, root, traversal); if (!traversal.hasErrors()) { // Apply the aliases. for (AliasUsage aliasUsage : traversal.getAliasUsages()) { aliasUsage.applyAlias(); } // Remove the alias definitions. for (Node aliasDefinition : traversal.getAliasDefinitionsInOrder()) { if (aliasDefinition.getParent().isVar() && aliasDefinition.getParent().hasOneChild()) { aliasDefinition.getParent().detachFromParent(); } else { aliasDefinition.detachFromParent(); } } // Collapse the scopes. for (Node scopeCall : traversal.getScopeCalls()) { Node expressionWithScopeCall = scopeCall.getParent(); Node scopeClosureBlock = scopeCall.getLastChild().getLastChild(); scopeClosureBlock.detachFromParent(); expressionWithScopeCall.getParent().replaceChild( expressionWithScopeCall, scopeClosureBlock); NodeUtil.tryMergeBlock(scopeClosureBlock); } if (traversal.getAliasUsages().size() > 0 || traversal.getAliasDefinitionsInOrder().size() > 0 || traversal.getScopeCalls().size() > 0) { compiler.reportCodeChange(); } } } private interface AliasUsage { public void applyAlias(); } private class AliasedNode implements AliasUsage { private final Node aliasReference; private final Node aliasDefinition; AliasedNode(Node aliasReference, Node aliasDefinition) { this.aliasReference = aliasReference; this.aliasDefinition = aliasDefinition; } @Override public void applyAlias() { aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode implements AliasUsage { private final Node typeReference; private final Node aliasDefinition; private final String aliasName; AliasedTypeNode(Node typeReference, Node aliasDefinition, String aliasName) { this.typeReference = typeReference; this.aliasDefinition = aliasDefinition; this.aliasName = aliasName; } @Override public void applyAlias() { String typeName = typeReference.getString(); String aliasExpanded = Preconditions.checkNotNull(aliasDefinition.getQualifiedName()); Preconditions.checkState(typeName.startsWith(aliasName)); typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded)); } } private class Traversal implements NodeTraversal.ScopedCallback { // The job of this class is to collect these three data sets. // The order of this list determines the order that aliases are applied. private final List aliasDefinitionsInOrder = Lists.newArrayList(); private final List scopeCalls = Lists.newArrayList(); private final List aliasUsages = Lists.newArrayList(); // This map is temporary and cleared for each scope. private final Map aliases = Maps.newHashMap(); // Suppose you create an alias. // var x = goog.x; // As a side-effect, this means you can shadow the namespace 'goog' // in inner scopes. When we inline the namespaces, we have to rename // these shadows. // // Fortunately, we already have a name uniquifier that runs during tree // normalization (before optimizations). We run it here on a limited // set of variables, but only as a last resort (because this will screw // up warning messages downstream). private final Set forbiddenLocals = Sets.newHashSet(); private boolean hasNamespaceShadows = false; private boolean hasErrors = false; private AliasTransformation transformation = null; Collection getAliasDefinitionsInOrder() { return aliasDefinitionsInOrder; } private List getAliasUsages() { return aliasUsages; } List getScopeCalls() { return scopeCalls; } boolean hasErrors() { return hasErrors; } private boolean isCallToScopeMethod(Node n) { return n.isCall() && SCOPING_METHOD_NAME.equals(n.getFirstChild().getQualifiedName()); } @Override public void enterScope(NodeTraversal t) { Node n = t.getCurrentNode().getParent(); if (n != null && isCallToScopeMethod(n)) { transformation = transformationHandler.logAliasTransformation( n.getSourceFileName(), getSourceRegion(n)); findAliases(t); } } @Override public void exitScope(NodeTraversal t) { if (t.getScopeDepth() > 2) { findNamespaceShadows(t); } if (t.getScopeDepth() == 2) { renameNamespaceShadows(t); aliases.clear(); forbiddenLocals.clear(); transformation = null; hasNamespaceShadows = false; } } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction() && t.inGlobalScope()) { // Do not traverse in to functions except for goog.scope functions. if (parent == null || !isCallToScopeMethod(parent)) { return false; } } return true; } private SourcePosition getSourceRegion(Node n) { Node testNode = n; Node next = null; for (; next != null || testNode.isScript();) { next = testNode.getNext(); testNode = testNode.getParent(); } int endLine = next == null ? Integer.MAX_VALUE : next.getLineno(); int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition pos = new SourcePosition() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); if (parent.isVar() && n.hasChildren() && n.getFirstChild().isQualifiedName()) { String name = n.getString(); Var aliasVar = scope.getVar(name); aliases.put(name, aliasVar); String qualifiedName = aliasVar.getInitialValue().getQualifiedName(); transformation.addAlias(name, qualifiedName); int rootIndex = qualifiedName.indexOf("."); if (rootIndex != -1) { String qNameRoot = qualifiedName.substring(0, rootIndex); if (!aliases.containsKey(qNameRoot)) { forbiddenLocals.add(qNameRoot); } } } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. } else { // TODO(robbyw): Support using locals for private variables. report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString()); } } } /** Find out if there are any local shadows of namespaces. */ private void findNamespaceShadows(NodeTraversal t) { if (hasNamespaceShadows) { return; } Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { if (forbiddenLocals.contains(v.getName())) { hasNamespaceShadows = true; return; } } } /** * Rename any local shadows of namespaces. * This should be a very rare occurrence, so only do this traversal * if we know that we need it. */ private void renameNamespaceShadows(NodeTraversal t) { if (hasNamespaceShadows) { MakeDeclaredNamesUnique.Renamer renamer = new MakeDeclaredNamesUnique.WhitelistedRenamer( new MakeDeclaredNamesUnique.ContextualRenamer(), forbiddenLocals); for (String s : forbiddenLocals) { renamer.addDeclaredName(s); } MakeDeclaredNamesUnique uniquifier = new MakeDeclaredNamesUnique(renamer); NodeTraversal.traverse(compiler, t.getScopeRoot(), uniquifier); } } private void validateScopeCall(NodeTraversal t, Node n, Node parent) { if (preprocessorSymbolTable != null) { preprocessorSymbolTable.addReference(n.getFirstChild()); } if (!parent.isExprResult()) { report(t, n, GOOG_SCOPE_USED_IMPROPERLY); } if (n.getChildCount() != 2) { // The goog.scope call should have exactly 1 parameter. The first // child is the "goog.scope" and the second should be the parameter. report(t, n, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { Node anonymousFnNode = n.getChildAtIndex(1); if (!anonymousFnNode.isFunction() || NodeUtil.getFunctionName(anonymousFnNode) != null || NodeUtil.getFunctionParameters(anonymousFnNode).hasChildren()) { report(t, anonymousFnNode, GOOG_SCOPE_HAS_BAD_PARAMETERS); } else { scopeCalls.add(n); } } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (isCallToScopeMethod(n)) { validateScopeCall(t, n, n.getParent()); } if (t.getScopeDepth() < 2) { return; } int type = n.getType(); Var aliasVar = null; if (type == Token.NAME) { String name = n.getString(); Var lexicalVar = t.getScope().getVar(n.getString()); if (lexicalVar != null && lexicalVar == aliases.get(name)) { aliasVar = lexicalVar; } } // Validate the top-level of the goog.scope block. if (t.getScopeDepth() == 2) { if (aliasVar != null && NodeUtil.isLValue(n)) { if (aliasVar.getNode() == n) { aliasDefinitionsInOrder.add(n); // Return early, to ensure that we don't record a definition // twice. return; } else { report(t, n, GOOG_SCOPE_ALIAS_REDEFINED, n.getString()); } } if (type == Token.RETURN) { report(t, n, GOOG_SCOPE_USES_RETURN); } else if (type == Token.THIS) { report(t, n, GOOG_SCOPE_REFERENCES_THIS); } else if (type == Token.THROW) { report(t, n, GOOG_SCOPE_USES_THROW); } } // Validate all descendent scopes of the goog.scope block. if (t.getScopeDepth() >= 2) { // Check if this name points to an alias. if (aliasVar != null) { // Note, to support the transitive case, it's important we don't // clone aliasedNode here. For example, // var g = goog; var d = g.dom; d.createElement('DIV'); // The node in aliasedNode (which is "g") will be replaced in the // changes pass above with "goog". If we cloned here, we'd end up // with g.dom.createElement('DIV'). Node aliasedNode = aliasVar.getInitialValue(); aliasUsages.add(new AliasedNode(n, aliasedNode)); } JSDocInfo info = n.getJSDocInfo(); if (info != null) { for (Node node : info.getTypeNodes()) { fixTypeNode(node); } } // TODO(robbyw): Error for goog.scope not at root. } } private void fixTypeNode(Node typeNode) { if (typeNode.isString()) { String name = typeNode.getString(); int endIndex = name.indexOf('.'); if (endIndex == -1) { endIndex = name.length(); } String baseName = name.substring(0, endIndex); Var aliasVar = aliases.get(baseName); if (aliasVar != null) { Node aliasedNode = aliasVar.getInitialValue(); aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName)); } } for (Node child = typeNode.getFirstChild(); child != null; child = child.getNext()) { fixTypeNode(child); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DefaultPassConfig.java0000644000175000017500000022744512115204405027056 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.CompilerOptions.LanguageMode; import com.google.javascript.jscomp.ExtractPrototypeMemberDeclarations.Pattern; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.parsing.ParserRunner; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * Pass factories and meta-data for native JSCompiler passes. * * @author nicksantos@google.com (Nick Santos) */ // TODO(nicksantos): This needs state for a variety of reasons. Some of it // is to satisfy the existing API. Some of it is because passes really do // need to share state in non-trivial ways. This should be audited and // cleaned up. public class DefaultPassConfig extends PassConfig { /* For the --mark-as-compiled pass */ private static final String COMPILED_CONSTANT_NAME = "COMPILED"; /* Constant name for Closure's locale */ private static final String CLOSURE_LOCALE_CONSTANT_NAME = "goog.LOCALE"; // Compiler errors when invalid combinations of passes are run. static final DiagnosticType TIGHTEN_TYPES_WITHOUT_TYPE_CHECK = DiagnosticType.error("JSC_TIGHTEN_TYPES_WITHOUT_TYPE_CHECK", "TightenTypes requires type checking. Please use --check_types."); static final DiagnosticType CANNOT_USE_PROTOTYPE_AND_VAR = DiagnosticType.error("JSC_CANNOT_USE_PROTOTYPE_AND_VAR", "Rename prototypes and inline variables cannot be used together"); // Miscellaneous errors. static final DiagnosticType REPORT_PATH_IO_ERROR = DiagnosticType.error("JSC_REPORT_PATH_IO_ERROR", "Error writing compiler report to {0}"); private static final DiagnosticType NAME_REF_GRAPH_FILE_ERROR = DiagnosticType.error("JSC_NAME_REF_GRAPH_FILE_ERROR", "Error \"{1}\" writing name reference graph to \"{0}\"."); private static final DiagnosticType NAME_REF_REPORT_FILE_ERROR = DiagnosticType.error("JSC_NAME_REF_REPORT_FILE_ERROR", "Error \"{1}\" writing name reference report to \"{0}\"."); private static final java.util.regex.Pattern GLOBAL_SYMBOL_NAMESPACE_PATTERN = java.util.regex.Pattern.compile("^[a-zA-Z0-9$_]+$"); /** * A global namespace to share across checking passes. */ private GlobalNamespace namespaceForChecks = null; /** * A symbol table for registering references that get removed during * preprocessing. */ private PreprocessorSymbolTable preprocessorSymbolTable = null; /** * A type-tightener to share across optimization passes. */ private TightenTypes tightenTypes = null; /** Names exported by goog.exportSymbol. */ private Set exportedNames = null; /** * Ids for cross-module method stubbing, so that each method has * a unique id. */ private CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator = new CrossModuleMethodMotion.IdGenerator(); /** * Keys are arguments passed to getCssName() found during compilation; values * are the number of times the key appeared as an argument to getCssName(). */ private Map cssNames = null; /** The variable renaming map */ private VariableMap variableMap = null; /** The property renaming map */ private VariableMap propertyMap = null; /** The naming map for anonymous functions */ private VariableMap anonymousFunctionNameMap = null; /** Fully qualified function names and globally unique ids */ private FunctionNames functionNames = null; /** String replacement map */ private VariableMap stringMap = null; /** Id generator map */ private String idGeneratorMap = null; public DefaultPassConfig(CompilerOptions options) { super(options); } @Override protected State getIntermediateState() { return new State( cssNames == null ? null : Maps.newHashMap(cssNames), exportedNames == null ? null : Collections.unmodifiableSet(exportedNames), crossModuleIdGenerator, variableMap, propertyMap, anonymousFunctionNameMap, stringMap, functionNames, idGeneratorMap); } @Override protected void setIntermediateState(State state) { this.cssNames = state.cssNames == null ? null : Maps.newHashMap(state.cssNames); this.exportedNames = state.exportedNames == null ? null : Sets.newHashSet(state.exportedNames); this.crossModuleIdGenerator = state.crossModuleIdGenerator; this.variableMap = state.variableMap; this.propertyMap = state.propertyMap; this.anonymousFunctionNameMap = state.anonymousFunctionNameMap; this.stringMap = state.stringMap; this.functionNames = state.functionNames; this.idGeneratorMap = state.idGeneratorMap; } GlobalNamespace getGlobalNamespace() { return namespaceForChecks; } PreprocessorSymbolTable getPreprocessorSymbolTable() { return preprocessorSymbolTable; } void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) { if (options.ideMode) { Node root = compiler.getRoot(); if (preprocessorSymbolTable == null || preprocessorSymbolTable.getRootNode() != root) { preprocessorSymbolTable = new PreprocessorSymbolTable(root); } } } @Override protected List getChecks() { List checks = Lists.newArrayList(); checks.add(createEmptyPass("beforeStandardChecks")); if (options.closurePass) { checks.add(closureGoogScopeAliases); checks.add(closureRewriteGoogClass); } if (options.nameAnonymousFunctionsOnly) { if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.MAPPED) { checks.add(nameMappedAnonymousFunctions); } else if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.UNMAPPED) { checks.add(nameUnmappedAnonymousFunctions); } return checks; } if (options.jqueryPass) { checks.add(jqueryAliases); } if (options.angularPass) { checks.add(angularPass); } checks.add(checkSideEffects); if (options.checkSuspiciousCode || options.enables(DiagnosticGroups.GLOBAL_THIS) || options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { checks.add(suspiciousCode); } if (options.checkControlStructures || options.enables(DiagnosticGroups.ES5_STRICT)) { checks.add(checkControlStructures); } if (options.checkRequires.isOn()) { checks.add(checkRequires); } if (options.checkProvides.isOn()) { checks.add(checkProvides); } // The following passes are more like "preprocessor" passes. // It's important that they run before most checking passes. // Perhaps this method should be renamed? if (options.generateExports) { checks.add(generateExports); } if (options.exportTestFunctions) { checks.add(exportTestFunctions); } if (options.closurePass) { checks.add(closurePrimitives); } if (options.closurePass && options.checkMissingGetCssNameLevel.isOn()) { checks.add(closureCheckGetCssName); } if (options.syntheticBlockStartMarker != null) { // This pass must run before the first fold constants pass. checks.add(createSyntheticBlocks); } checks.add(checkVars); if (options.computeFunctionSideEffects) { checks.add(checkRegExp); } if (options.aggressiveVarCheck.isOn()) { checks.add(checkVariableReferences); } // This pass should run before types are assigned. if (options.processObjectPropertyString) { checks.add(objectPropertyStringPreprocess); } if (options.checkTypes || options.inferTypes) { checks.add(resolveTypes); checks.add(inferTypes); if (options.checkTypes) { checks.add(checkTypes); } else { checks.add(inferJsDocInfo); } // We assume that only IDE-mode clients will try to query the // typed scope creator after the compile job. if (!options.ideMode && !options.saveDataStructures) { checks.add(clearTypedScopePass); } } if (options.checkUnreachableCode.isOn() || (options.checkTypes && options.checkMissingReturn.isOn())) { checks.add(checkControlFlow); } // CheckAccessControls only works if check types is on. if (options.checkTypes && (options.enables(DiagnosticGroups.ACCESS_CONTROLS) || options.enables(DiagnosticGroups.CONSTANT_PROPERTY))) { checks.add(checkAccessControls); } if (options.checkGlobalNamesLevel.isOn()) { checks.add(checkGlobalNames); } if (options.enables(DiagnosticGroups.ES5_STRICT) || options.checkCaja) { checks.add(checkStrictMode); } // Replace 'goog.getCssName' before processing defines but after the // other checks have been done. if (options.closurePass) { checks.add(closureReplaceGetCssName); } // i18n // If you want to customize the compiler to use a different i18n pass, // you can create a PassConfig that calls replacePassFactory // to replace this. if (options.replaceMessagesWithChromeI18n) { checks.add(replaceMessagesForChrome); } else if (options.messageBundle != null) { checks.add(replaceMessages); } if (options.getTweakProcessing().isOn()) { checks.add(processTweaks); } // Defines in code always need to be processed. checks.add(processDefines); if (options.instrumentationTemplate != null || options.recordFunctionInformation) { checks.add(computeFunctionNames); } if (options.nameReferenceGraphPath != null && !options.nameReferenceGraphPath.isEmpty()) { checks.add(printNameReferenceGraph); } if (options.nameReferenceReportPath != null && !options.nameReferenceReportPath.isEmpty()) { checks.add(printNameReferenceReport); } checks.add(createEmptyPass("afterStandardChecks")); assertAllOneTimePasses(checks); return checks; } @Override protected List getOptimizations() { List passes = Lists.newArrayList(); passes.add(garbageCollectChecks); // TODO(nicksantos): The order of these passes makes no sense, and needs // to be re-arranged. if (options.runtimeTypeCheck) { passes.add(runtimeTypeCheck); } passes.add(createEmptyPass("beforeStandardOptimizations")); if (options.replaceIdGenerators) { passes.add(replaceIdGenerators); } // Optimizes references to the arguments variable. if (options.optimizeArgumentsArray) { passes.add(optimizeArgumentsArray); } // Abstract method removal works best on minimally modified code, and also // only needs to run once. if (options.closurePass && (options.removeAbstractMethods || options.removeClosureAsserts)) { passes.add(closureCodeRemoval); } // Collapsing properties can undo constant inlining, so we do this before // the main optimization loop. if (options.collapseProperties) { passes.add(collapseProperties); } // ReplaceStrings runs after CollapseProperties in order to simplify // pulling in values of constants defined in enums structures. if (!options.replaceStringsFunctionDescriptions.isEmpty()) { passes.add(replaceStrings); } // Tighten types based on actual usage. if (options.tightenTypes) { passes.add(tightenTypesBuilder); } // Property disambiguation should only run once and needs to be done // soon after type checking, both so that it can make use of type // information and so that other passes can take advantage of the renamed // properties. if (options.disambiguateProperties) { passes.add(disambiguateProperties); } if (options.computeFunctionSideEffects) { passes.add(markPureFunctions); } else if (options.markNoSideEffectCalls) { // TODO(user) The properties that this pass adds to CALL and NEW // AST nodes increase the AST's in-memory size. Given that we are // already running close to our memory limits, we could run into // trouble if we end up using the @nosideeffects annotation a lot // or compute @nosideeffects annotations by looking at function // bodies. It should be easy to propagate @nosideeffects // annotations as part of passes that depend on this property and // store the result outside the AST (which would allow garbage // collection once the pass is done). passes.add(markNoSideEffectCalls); } if (options.chainCalls) { passes.add(chainCalls); } // Constant checking must be done after property collapsing because // property collapsing can introduce new constants (e.g. enum values). // TODO(johnlenz): make checkConsts namespace aware so it can be run // as during the checks phase. passes.add(checkConsts); // The Caja library adds properties to Object.prototype, which breaks // most for-in loops. This adds a check to each loop that skips // any property matching /___$/. if (options.ignoreCajaProperties) { passes.add(ignoreCajaProperties); } assertAllOneTimePasses(passes); if (options.smartNameRemoval || options.reportPath != null) { passes.addAll(getCodeRemovingPasses()); passes.add(smartNamePass); } // This needs to come after the inline constants pass, which is run within // the code removing passes. if (options.closurePass) { passes.add(closureOptimizePrimitives); } // TODO(user): This forces a first crack at crossModuleCodeMotion // before devirtualization. Once certain functions are devirtualized, // it confuses crossModuleCodeMotion ability to recognized that // it is recursive. // TODO(user): This is meant for a temporary quick win. // In the future, we might want to improve our analysis in // CrossModuleCodeMotion so we don't need to do this. if (options.crossModuleCodeMotion) { passes.add(crossModuleCodeMotion); } // Method devirtualization benefits from property disambiguation so // it should run after that pass but before passes that do // optimizations based on global names (like cross module code motion // and inline functions). Smart Name Removal does better if run before // this pass. if (options.devirtualizePrototypeMethods) { passes.add(devirtualizePrototypeMethods); } if (options.customPasses != null) { passes.add(getCustomPasses( CustomPassExecutionTime.BEFORE_OPTIMIZATION_LOOP)); } passes.add(createEmptyPass("beforeMainOptimizations")); passes.addAll(getMainOptimizationLoop()); if (options.specializeInitialModule) { // When specializing the initial module, we want our fixups to be // as lean as possible, so we run the entire optimization loop to a // fixed point before specializing, then specialize, and then run the // main optimization loop again. if (options.crossModuleCodeMotion) { passes.add(crossModuleCodeMotion); } if (options.crossModuleMethodMotion) { passes.add(crossModuleMethodMotion); } passes.add(specializeInitialModule); passes.addAll(getMainOptimizationLoop()); } passes.add(createEmptyPass("beforeModuleMotion")); if (options.crossModuleCodeMotion) { passes.add(crossModuleCodeMotion); } if (options.crossModuleMethodMotion) { passes.add(crossModuleMethodMotion); } passes.add(createEmptyPass("afterModuleMotion")); // Some optimizations belong outside the loop because running them more // than once would either have no benefit or be incorrect. if (options.customPasses != null) { passes.add(getCustomPasses( CustomPassExecutionTime.AFTER_OPTIMIZATION_LOOP)); } if (options.flowSensitiveInlineVariables) { passes.add(flowSensitiveInlineVariables); // After inlining some of the variable uses, some variables are unused. // Re-run remove unused vars to clean it up. if (options.removeUnusedVars || options.removeUnusedLocalVars) { passes.add(removeUnusedVars); } } // Running this pass again is required to have goog.events compile down to // nothing when compiled on its own. if (options.smartNameRemoval) { passes.add(smartNamePass2); } if (options.collapseAnonymousFunctions) { passes.add(collapseAnonymousFunctions); } // Move functions before extracting prototype member declarations. if (options.moveFunctionDeclarations || // renamePrefixNamescape relies on moveFunctionDeclarations // to preserve semantics. options.renamePrefixNamespace != null) { passes.add(moveFunctionDeclarations); } if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.MAPPED) { passes.add(nameMappedAnonymousFunctions); } // The mapped name anonymous function pass makes use of information that // the extract prototype member declarations pass removes so the former // happens before the latter. // // Extracting prototype properties screws up the heuristic renaming // policies, so never run it when those policies are requested. if (options.extractPrototypeMemberDeclarations && (options.propertyRenaming != PropertyRenamingPolicy.HEURISTIC && options.propertyRenaming != PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC)) { passes.add(extractPrototypeMemberDeclarations); } if (options.ambiguateProperties && (options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED)) { passes.add(ambiguateProperties); } if (options.propertyRenaming != PropertyRenamingPolicy.OFF) { passes.add(renameProperties); } // Reserve global names added to the "windows" object. if (options.reserveRawExports) { passes.add(gatherRawExports); } // This comes after property renaming because quoted property names must // not be renamed. if (options.convertToDottedProperties) { passes.add(convertToDottedProperties); } // Property renaming must happen before this pass runs since this // pass may convert dotted properties into quoted properties. It // is beneficial to run before alias strings, alias keywords and // variable renaming. if (options.rewriteFunctionExpressions) { passes.add(rewriteFunctionExpressions); } // This comes after converting quoted property accesses to dotted property // accesses in order to avoid aliasing property names. if (!options.aliasableStrings.isEmpty() || options.aliasAllStrings) { passes.add(aliasStrings); } if (options.aliasExternals) { passes.add(aliasExternals); } if (options.aliasKeywords) { passes.add(aliasKeywords); } // Passes after this point can no longer depend on normalized AST // assumptions. passes.add(markUnnormalized); if (options.coalesceVariableNames) { passes.add(coalesceVariableNames); // coalesceVariables creates identity assignments and more redundant code // that can be removed, rerun the peephole optimizations to clean them // up. if (options.foldConstants) { passes.add(peepholeOptimizations); } } if (options.collapseVariableDeclarations) { passes.add(exploitAssign); passes.add(collapseVariableDeclarations); } // This pass works best after collapseVariableDeclarations. passes.add(denormalize); if (options.instrumentationTemplate != null) { passes.add(instrumentFunctions); } if (options.variableRenaming != VariableRenamingPolicy.ALL) { // If we're leaving some (or all) variables with their old names, // then we need to undo any of the markers we added for distinguishing // local variables ("$$1"). passes.add(invertContextualRenaming); } if (options.variableRenaming != VariableRenamingPolicy.OFF) { passes.add(renameVars); } if (options.groupVariableDeclarations) { passes.add(groupVariableDeclarations); } // This pass should run after names stop changing. if (options.processObjectPropertyString) { passes.add(objectPropertyStringPostprocess); } if (options.labelRenaming) { passes.add(renameLabels); } if (options.foldConstants) { passes.add(latePeepholeOptimizations); } if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.UNMAPPED) { passes.add(nameUnmappedAnonymousFunctions); } passes.add(stripSideEffectProtection); if (options.renamePrefixNamespace != null) { if (!GLOBAL_SYMBOL_NAMESPACE_PATTERN.matcher( options.renamePrefixNamespace).matches()) { throw new IllegalArgumentException( "Illegal character in renamePrefixNamespace name: " + options.renamePrefixNamespace); } passes.add(rescopeGlobalSymbols); } // Safety checks passes.add(sanityCheckAst); passes.add(sanityCheckVars); return passes; } /** Creates the passes for the main optimization loop. */ private List getMainOptimizationLoop() { List passes = Lists.newArrayList(); if (options.inlineGetters) { passes.add(inlineSimpleMethods); } passes.addAll(getCodeRemovingPasses()); if (options.inlineFunctions || options.inlineLocalFunctions) { passes.add(inlineFunctions); } if (options.inlineProperties) { passes.add(inlineProperties); } boolean runOptimizeCalls = options.optimizeCalls || options.optimizeParameters || options.optimizeReturns; if (options.removeUnusedVars || options.removeUnusedLocalVars) { if (options.deadAssignmentElimination) { passes.add(deadAssignmentsElimination); } if (!runOptimizeCalls) { passes.add(removeUnusedVars); } } if (runOptimizeCalls) { passes.add(optimizeCallsAndRemoveUnusedVars); } assertAllLoopablePasses(passes); return passes; } /** Creates several passes aimed at removing code. */ private List getCodeRemovingPasses() { List passes = Lists.newArrayList(); if (options.collapseObjectLiterals && !isInliningForbidden()) { passes.add(collapseObjectLiterals); } if (options.inlineVariables || options.inlineLocalVariables) { passes.add(inlineVariables); } else if (options.inlineConstantVars) { passes.add(inlineConstants); } if (options.foldConstants) { // These used to be one pass. passes.add(minimizeExitPoints); passes.add(peepholeOptimizations); } if (options.removeDeadCode) { passes.add(removeUnreachableCode); } if (options.removeUnusedPrototypeProperties) { passes.add(removeUnusedPrototypeProperties); } if (options.removeUnusedClassProperties && !isInliningForbidden()) { passes.add(removeUnusedClassProperties); } assertAllLoopablePasses(passes); return passes; } /** * Checks for code that is probably wrong (such as stray expressions). */ final HotSwapPassFactory checkSideEffects = new HotSwapPassFactory("checkSideEffects", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { // The current approach to protecting "hidden" side-effects is to // wrap them in a function call that is stripped later, this shouldn't // be done in IDE mode where AST changes may be unexpected. boolean protectHiddenSideEffects = options.protectHiddenSideEffects && !options.ideMode; return new CheckSideEffects(compiler, options.checkSuspiciousCode ? CheckLevel.WARNING : CheckLevel.OFF, protectHiddenSideEffects); } }; /** * Checks for code that is probably wrong (such as stray expressions). */ final PassFactory stripSideEffectProtection = new PassFactory("stripSideEffectProtection", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CheckSideEffects.StripProtection(compiler); } }; /** * Checks for code that is probably wrong (such as stray expressions). */ final HotSwapPassFactory suspiciousCode = new HotSwapPassFactory("suspiciousCode", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { List sharedCallbacks = Lists.newArrayList(); if (options.checkSuspiciousCode) { sharedCallbacks.add(new CheckSuspiciousCode()); } if (options.enables(DiagnosticGroups.GLOBAL_THIS)) { sharedCallbacks.add(new CheckGlobalThis(compiler)); } if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { sharedCallbacks.add(new CheckDebuggerStatement(compiler)); } return combineChecks(compiler, sharedCallbacks); } }; /** Verify that all the passes are one-time passes. */ private void assertAllOneTimePasses(List passes) { for (PassFactory pass : passes) { Preconditions.checkState(pass.isOneTimePass()); } } /** Verify that all the passes are multi-run passes. */ private void assertAllLoopablePasses(List passes) { for (PassFactory pass : passes) { Preconditions.checkState(!pass.isOneTimePass()); } } /** Checks for validity of the control structures. */ final HotSwapPassFactory checkControlStructures = new HotSwapPassFactory("checkControlStructures", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new ControlStructureCheck(compiler); } }; /** Checks that all constructed classes are goog.require()d. */ final HotSwapPassFactory checkRequires = new HotSwapPassFactory("checkRequires", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckRequiresForConstructors(compiler, options.checkRequires); } }; /** Makes sure @constructor is paired with goog.provides(). */ final HotSwapPassFactory checkProvides = new HotSwapPassFactory("checkProvides", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckProvides(compiler, options.checkProvides); } }; private static final DiagnosticType GENERATE_EXPORTS_ERROR = DiagnosticType.error( "JSC_GENERATE_EXPORTS_ERROR", "Exports can only be generated if export symbol/property " + "functions are set."); /** Generates exports for @export annotations. */ final PassFactory generateExports = new PassFactory("generateExports", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null && convention.getExportPropertyFunction() != null) { return new GenerateExports(compiler, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Generates exports for functions associated with JsUnit. */ final PassFactory exportTestFunctions = new PassFactory("exportTestFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null) { return new ExportTestFunctions(compiler, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Raw exports processing pass. */ final PassFactory gatherRawExports = new PassFactory("gatherRawExports", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { final GatherRawExports pass = new GatherRawExports( compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); if (exportedNames == null) { exportedNames = Sets.newHashSet(); } exportedNames.addAll(pass.getExportedVariableNames()); } }; } }; /** Closure pre-processing pass. */ @SuppressWarnings("deprecation") final HotSwapPassFactory closurePrimitives = new HotSwapPassFactory("closurePrimitives", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); final ProcessClosurePrimitives pass = new ProcessClosurePrimitives( compiler, preprocessorSymbolTable, options.brokenClosureRequiresLevel); return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); exportedNames = pass.getExportedVariableNames(); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { pass.hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Expand jQuery Primitives and Aliases pass. */ final PassFactory jqueryAliases = new PassFactory("jqueryAliases", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ExpandJqueryAliases(compiler); } }; /** Process AngularJS-specific annotations. */ final PassFactory angularPass = new PassFactory("angularPass", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AngularPass(compiler); } }; /** * The default i18n pass. * A lot of the options are not configurable, because ReplaceMessages * has a lot of legacy logic. */ final PassFactory replaceMessages = new PassFactory("replaceMessages", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ReplaceMessages(compiler, options.messageBundle, /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false), /* if we can't find a translation, don't worry about it. */ false); } }; final PassFactory replaceMessagesForChrome = new PassFactory("replaceMessages", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ReplaceMessagesForChrome(compiler, new GoogleJsMessageIdGenerator(options.tcProjectId), /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false)); } }; /** Applies aliases and inlines goog.scope. */ final HotSwapPassFactory closureGoogScopeAliases = new HotSwapPassFactory("closureGoogScopeAliases", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); return new ScopedAliases( compiler, preprocessorSymbolTable, options.getAliasTransformationHandler()); } }; /** Rewrites goog.class */ final HotSwapPassFactory closureRewriteGoogClass = new HotSwapPassFactory("closureRewriteGoogClass", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new ClosureRewriteClass(compiler); } }; /** Checks that CSS class names are wrapped in goog.getCssName */ final PassFactory closureCheckGetCssName = new PassFactory("closureCheckGetCssName", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { String blacklist = options.checkMissingGetCssNameBlacklist; Preconditions.checkState(blacklist != null && !blacklist.isEmpty(), "Not checking use of goog.getCssName because of empty blacklist."); return new CheckMissingGetCssName( compiler, options.checkMissingGetCssNameLevel, blacklist); } }; /** * Processes goog.getCssName. The cssRenamingMap is used to lookup * replacement values for the classnames. If null, the raw class names are * inlined. */ final PassFactory closureReplaceGetCssName = new PassFactory("closureReplaceGetCssName", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { Map newCssNames = null; if (options.gatherCssNames) { newCssNames = Maps.newHashMap(); } ReplaceCssNames pass = new ReplaceCssNames( compiler, newCssNames, options.cssRenamingWhitelist); pass.process(externs, jsRoot); cssNames = newCssNames; } }; } }; /** * Creates synthetic blocks to prevent FoldConstants from moving code * past markers in the source. */ final PassFactory createSyntheticBlocks = new PassFactory("createSyntheticBlocks", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CreateSyntheticBlocks(compiler, options.syntheticBlockStartMarker, options.syntheticBlockEndMarker); } }; /** Various peephole optimizations. */ final PassFactory peepholeOptimizations = new PassFactory("peepholeOptimizations", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { final boolean late = false; return new PeepholeOptimizationsPass(compiler, new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late), new PeepholeRemoveDeadCode(), new PeepholeFoldConstants(late), new PeepholeCollectPropertyAssignments()); } }; /** Same as peepholeOptimizations but aggressively merges code together */ final PassFactory latePeepholeOptimizations = new PassFactory("latePeepholeOptimizations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { final boolean late = true; return new PeepholeOptimizationsPass(compiler, new StatementFusion(), new PeepholeRemoveDeadCode(), new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late), new PeepholeFoldConstants(late), new ReorderConstantExpression()); } }; /** Checks that all variables are defined. */ final HotSwapPassFactory checkVars = new HotSwapPassFactory("checkVars", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new VarCheck(compiler); } }; /** Checks for RegExp references. */ final PassFactory checkRegExp = new PassFactory("checkRegExp", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { final CheckRegExp pass = new CheckRegExp(compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); compiler.setHasRegExpGlobalReferences( pass.isGlobalRegExpPropertiesUsed()); } }; } }; /** Checks that references to variables look reasonable. */ final HotSwapPassFactory checkVariableReferences = new HotSwapPassFactory("checkVariableReferences", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new VariableReferenceCheck( compiler, options.aggressiveVarCheck); } }; /** Pre-process goog.testing.ObjectPropertyString. */ final PassFactory objectPropertyStringPreprocess = new PassFactory("ObjectPropertyStringPreprocess", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ObjectPropertyStringPreprocess(compiler); } }; /** Creates a typed scope and adds types to the type registry. */ final HotSwapPassFactory resolveTypes = new HotSwapPassFactory("resolveTypes", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new GlobalTypeResolver(compiler); } }; /** Clears the typed scope when we're done. */ final PassFactory clearTypedScopePass = new PassFactory("clearTypedScopePass", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ClearTypedScope(); } }; /** Runs type inference. */ final HotSwapPassFactory inferTypes = new HotSwapPassFactory("inferTypes", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeTypeInference(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeInference(compiler).inferAllScopes(scriptRoot); } }; } }; final HotSwapPassFactory inferJsDocInfo = new HotSwapPassFactory("inferJsDocInfo", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeInferJsDocInfo(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Checks type usage */ final HotSwapPassFactory checkTypes = new HotSwapPassFactory("checkTypes", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); TypeCheck check = makeTypeCheck(compiler); check.process(externs, root); compiler.getErrorManager().setTypedPercent(check.getTypedPercent()); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeCheck(compiler).check(scriptRoot, false); } }; } }; /** * Checks possible execution paths of the program for problems: missing return * statements and dead code. */ final HotSwapPassFactory checkControlFlow = new HotSwapPassFactory("checkControlFlow", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { List callbacks = Lists.newArrayList(); if (options.checkUnreachableCode.isOn()) { callbacks.add( new CheckUnreachableCode(compiler, options.checkUnreachableCode)); } if (options.checkMissingReturn.isOn() && options.checkTypes) { callbacks.add( new CheckMissingReturn(compiler, options.checkMissingReturn)); } return combineChecks(compiler, callbacks); } }; /** Checks access controls. Depends on type-inference. */ final HotSwapPassFactory checkAccessControls = new HotSwapPassFactory("checkAccessControls", true) { @Override protected HotSwapCompilerPass create(AbstractCompiler compiler) { return new CheckAccessControls(compiler); } }; /** Executes the given callbacks with a {@link CombinedCompilerPass}. */ private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler, List callbacks) { Preconditions.checkArgument(callbacks.size() > 0); Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); return new CombinedCompilerPass(compiler, array); } /** A compiler pass that resolves types in the global scope. */ class GlobalTypeResolver implements HotSwapCompilerPass { private final AbstractCompiler compiler; GlobalTypeResolver(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (topScope == null) { regenerateGlobalTypedScope(compiler, root.getParent()); } else { compiler.getTypeRegistry().resolveTypesInScope(topScope); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { patchGlobalTypedScope(compiler, scriptRoot); } } /** A compiler pass that clears the global scope. */ class ClearTypedScope implements CompilerPass { @Override public void process(Node externs, Node root) { clearTypedScope(); } } /** Checks global name usage. */ final PassFactory checkGlobalNames = new PassFactory("checkGlobalNames", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Create a global namespace for analysis by check passes. // Note that this class does all heavy computation lazily, // so it's OK to create it here. namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot); new CheckGlobalNames(compiler, options.checkGlobalNamesLevel) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Checks that the code is ES5 or Caja compliant. */ final PassFactory checkStrictMode = new PassFactory("checkStrictMode", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new StrictModeCheck(compiler, !options.checkSymbols, // don't check variables twice !options.checkCaja); // disable eval check if not Caja } }; /** Process goog.tweak.getTweak() calls. */ final PassFactory processTweaks = new PassFactory("processTweaks", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { new ProcessTweaks(compiler, options.getTweakProcessing().shouldStrip(), options.getTweakReplacements()).process(externs, jsRoot); } }; } }; /** Override @define-annotated constants. */ final PassFactory processDefines = new PassFactory("processDefines", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { Map replacements = getAdditionalReplacements(options); replacements.putAll(options.getDefineReplacements()); new ProcessDefines(compiler, replacements) .injectNamespace(namespaceForChecks).process(externs, jsRoot); } }; } }; /** Release references to data that is only needed during checks. */ final PassFactory garbageCollectChecks = new HotSwapPassFactory("garbageCollectChecks", true) { @Override protected HotSwapCompilerPass create(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Kill the global namespace so that it can be garbage collected // after all passes are through with it. namespaceForChecks = null; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { process(null, null); } }; } }; /** Checks that all constants are not modified */ final PassFactory checkConsts = new PassFactory("checkConsts", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ConstCheck(compiler); } }; /** Computes the names of functions for later analysis. */ final PassFactory computeFunctionNames = new PassFactory("computeFunctionNames", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return ((functionNames = new FunctionNames(compiler))); } }; /** Skips Caja-private properties in for-in loops */ final PassFactory ignoreCajaProperties = new PassFactory("ignoreCajaProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new IgnoreCajaProperties(compiler); } }; /** Inserts run-time type assertions for debugging. */ final PassFactory runtimeTypeCheck = new PassFactory("runtimeTypeCheck", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RuntimeTypeCheck(compiler, options.runtimeTypeCheckLogFunction); } }; /** Generates unique ids. */ final PassFactory replaceIdGenerators = new PassFactory("replaceIdGenerators", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceIdGenerators pass = new ReplaceIdGenerators( compiler, options.idGenerators, options.generatePseudoNames, options.idGeneratorsMapSerialized); pass.process(externs, root); idGeneratorMap = pass.getSerializedIdMappings(); } }; } }; /** Replace strings. */ final PassFactory replaceStrings = new PassFactory("replaceStrings", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceStrings pass = new ReplaceStrings( compiler, options.replaceStringsPlaceholderToken, options.replaceStringsFunctionDescriptions, options.replaceStringsReservedStrings, options.replaceStringsInputMap); pass.process(externs, root); stringMap = pass.getStringMap(); } }; } }; /** Optimizes the "arguments" array. */ final PassFactory optimizeArgumentsArray = new PassFactory("optimizeArgumentsArray", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new OptimizeArgumentsArray(compiler); } }; /** Remove variables set to goog.abstractMethod. */ final PassFactory closureCodeRemoval = new PassFactory("closureCodeRemoval", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ClosureCodeRemoval(compiler, options.removeAbstractMethods, options.removeClosureAsserts); } }; /** Special case optimizations for closure functions. */ final PassFactory closureOptimizePrimitives = new PassFactory("closureOptimizePrimitives", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new ClosureOptimizePrimitives(compiler); } }; /** Puts global symbols into a single object. */ final PassFactory rescopeGlobalSymbols = new PassFactory("rescopeGlobalSymbols", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RescopeGlobalSymbols(compiler, options.renamePrefixNamespace); } }; /** Collapses names in the global scope. */ final PassFactory collapseProperties = new PassFactory("collapseProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseProperties( compiler, options.collapsePropertiesOnExternTypes, !isInliningForbidden()); } }; /** Rewrite properties as variables. */ final PassFactory collapseObjectLiterals = new PassFactory("collapseObjectLiterals", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineObjectLiterals( compiler, compiler.getUniqueNameIdSupplier()); } }; /** * Try to infer the actual types, which may be narrower * than the declared types. */ final PassFactory tightenTypesBuilder = new PassFactory("tightenTypes", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (!options.checkTypes) { return new ErrorPass(compiler, TIGHTEN_TYPES_WITHOUT_TYPE_CHECK); } tightenTypes = new TightenTypes(compiler); return tightenTypes; } }; /** Devirtualize property names based on type information. */ final PassFactory disambiguateProperties = new PassFactory("disambiguateProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (tightenTypes == null) { return DisambiguateProperties.forJSTypeSystem(compiler, options.propertyInvalidationErrors); } else { return DisambiguateProperties.forConcreteTypeSystem( compiler, tightenTypes, options.propertyInvalidationErrors); } } }; /** * Chain calls to functions that return this. */ final PassFactory chainCalls = new PassFactory("chainCalls", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ChainCalls(compiler); } }; /** * Rewrite instance methods as static methods, to make them easier * to inline. */ final PassFactory devirtualizePrototypeMethods = new PassFactory("devirtualizePrototypeMethods", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DevirtualizePrototypeMethods(compiler); } }; /** * Optimizes unused function arguments, unused return values, and inlines * constant parameters. Also runs RemoveUnusedVars. */ final PassFactory optimizeCallsAndRemoveUnusedVars = new PassFactory("optimizeCalls_and_removeUnusedVars", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { OptimizeCalls passes = new OptimizeCalls(compiler); if (options.optimizeReturns) { // Remove unused return values. passes.addPass(new OptimizeReturns(compiler)); } if (options.optimizeParameters) { // Remove all parameters that are constants or unused. passes.addPass(new OptimizeParameters(compiler)); } if (options.optimizeCalls) { boolean removeOnlyLocals = options.removeUnusedLocalVars && !options.removeUnusedVars; boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; passes.addPass( new RemoveUnusedVars(compiler, !removeOnlyLocals, preserveAnonymousFunctionNames, true)); } return passes; } }; /** * Look for function calls that are pure, and annotate them * that way. */ final PassFactory markPureFunctions = new PassFactory("markPureFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new PureFunctionIdentifier.Driver( compiler, options.debugFunctionSideEffectsPath, false); } }; /** * Look for function calls that have no side effects, and annotate them * that way. */ final PassFactory markNoSideEffectCalls = new PassFactory("markNoSideEffectCalls", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MarkNoSideEffectCalls(compiler); } }; /** Inlines variables heuristically. */ final PassFactory inlineVariables = new PassFactory("inlineVariables", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { if (isInliningForbidden()) { // In old renaming schemes, inlining a variable can change whether // or not a property is renamed. This is bad, and those old renaming // schemes need to die. return new ErrorPass(compiler, CANNOT_USE_PROTOTYPE_AND_VAR); } else { InlineVariables.Mode mode; if (options.inlineVariables) { mode = InlineVariables.Mode.ALL; } else if (options.inlineLocalVariables) { mode = InlineVariables.Mode.LOCALS_ONLY; } else { throw new IllegalStateException("No variable inlining option set."); } return new InlineVariables(compiler, mode, true); } } }; /** Inlines variables that are marked as constants. */ final PassFactory inlineConstants = new PassFactory("inlineConstants", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineVariables( compiler, InlineVariables.Mode.CONSTANTS_ONLY, true); } }; /** * Perform local control flow optimizations. */ final PassFactory minimizeExitPoints = new PassFactory("minimizeExitPoints", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MinimizeExitPoints(compiler); } }; /** * Use data flow analysis to remove dead branches. */ final PassFactory removeUnreachableCode = new PassFactory("removeUnreachableCode", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new UnreachableCodeElimination(compiler, true); } }; /** * Remove prototype properties that do not appear to be used. */ final PassFactory removeUnusedPrototypeProperties = new PassFactory("removeUnusedPrototypeProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedPrototypeProperties( compiler, options.removeUnusedPrototypePropertiesInExterns, !options.removeUnusedVars); } }; /** * Remove prototype properties that do not appear to be used. */ final PassFactory removeUnusedClassProperties = new PassFactory("removeUnusedClassProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RemoveUnusedClassProperties(compiler); } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass = new PassFactory("smartNamePass", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); String reportPath = options.reportPath; if (reportPath != null) { try { Files.write(na.getHtmlReport(), new File(reportPath), Charsets.UTF_8); } catch (IOException e) { compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath)); } } if (options.smartNameRemoval) { na.removeUnreferenced(); } } }; } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass2 = new PassFactory("smartNamePass", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); na.removeUnreferenced(); } }; } }; /** Inlines simple methods, like getters */ final PassFactory inlineSimpleMethods = new PassFactory("inlineSimpleMethods", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineSimpleMethods(compiler); } }; /** Kills dead assignments. */ final PassFactory deadAssignmentsElimination = new PassFactory("deadAssignmentsElimination", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new DeadAssignmentsElimination(compiler); } }; /** Inlines function calls. */ final PassFactory inlineFunctions = new PassFactory("inlineFunctions", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { boolean enableBlockInlining = !isInliningForbidden(); return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), options.inlineFunctions, options.inlineLocalFunctions, enableBlockInlining, options.assumeStrictThis() || options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT, options.assumeClosuresOnlyCaptureReferences); } }; /** Inlines constant properties. */ final PassFactory inlineProperties = new PassFactory("inlineProperties", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new InlineProperties(compiler); } }; /** Removes variables that are never used. */ final PassFactory removeUnusedVars = new PassFactory("removeUnusedVars", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { boolean removeOnlyLocals = options.removeUnusedLocalVars && !options.removeUnusedVars; boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; return new RemoveUnusedVars( compiler, !removeOnlyLocals, preserveAnonymousFunctionNames, false); } }; /** * Move global symbols to a deeper common module */ final PassFactory crossModuleCodeMotion = new PassFactory("crossModuleCodeMotion", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); } }; /** * Move methods to a deeper common module */ final PassFactory crossModuleMethodMotion = new PassFactory("crossModuleMethodMotion", false) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CrossModuleMethodMotion( compiler, crossModuleIdGenerator, // Only move properties in externs if we're not treating // them as exports. options.removeUnusedPrototypePropertiesInExterns); } }; /** * Specialize the initial module at the cost of later modules */ final PassFactory specializeInitialModule = new PassFactory("specializeInitialModule", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new SpecializeModule(compiler, devirtualizePrototypeMethods, inlineFunctions, removeUnusedPrototypeProperties); } }; /** A data-flow based variable inliner. */ final PassFactory flowSensitiveInlineVariables = new PassFactory("flowSensitiveInlineVariables", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new FlowSensitiveInlineVariables(compiler); } }; /** Uses register-allocation algorithms to use fewer variables. */ final PassFactory coalesceVariableNames = new PassFactory("coalesceVariableNames", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CoalesceVariableNames(compiler, options.generatePseudoNames); } }; /** * Some simple, local collapses (e.g., {@code var x; var y;} becomes * {@code var x,y;}. */ final PassFactory exploitAssign = new PassFactory("exploitAssign", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new PeepholeOptimizationsPass(compiler, new ExploitAssigns()); } }; /** * Some simple, local collapses (e.g., {@code var x; var y;} becomes * {@code var x,y;}. */ final PassFactory collapseVariableDeclarations = new PassFactory("collapseVariableDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseVariableDeclarations(compiler); } }; /** * Simple global collapses of variable declarations. */ final PassFactory groupVariableDeclarations = new PassFactory("groupVariableDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new GroupVariableDeclarations(compiler); } }; /** * Extracts common sub-expressions. */ final PassFactory extractPrototypeMemberDeclarations = new PassFactory("extractPrototypeMemberDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ExtractPrototypeMemberDeclarations( compiler, Pattern.USE_GLOBAL_TEMP); } }; /** Rewrites common function definitions to be more compact. */ final PassFactory rewriteFunctionExpressions = new PassFactory("rewriteFunctionExpressions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new FunctionRewriter(compiler); } }; /** Collapses functions to not use the VAR keyword. */ final PassFactory collapseAnonymousFunctions = new PassFactory("collapseAnonymousFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new CollapseAnonymousFunctions(compiler); } }; /** Moves function declarations to the top, to simulate actual hoisting. */ final PassFactory moveFunctionDeclarations = new PassFactory("moveFunctionDeclarations", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new MoveFunctionDeclarations(compiler); } }; final PassFactory nameUnmappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new NameAnonymousFunctions(compiler); } }; final PassFactory nameMappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnonymousFunctionsMapped naf = new NameAnonymousFunctionsMapped( compiler, options.inputAnonymousFunctionNamingMap); naf.process(externs, root); anonymousFunctionNameMap = naf.getFunctionMap(); } }; } }; /** Alias external symbols. */ final PassFactory aliasExternals = new PassFactory("aliasExternals", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasExternals(compiler, compiler.getModuleGraph(), options.unaliasableGlobals, options.aliasableGlobals); } }; /** * Alias string literals with global variables, to avoid creating lots of * transient objects. */ final PassFactory aliasStrings = new PassFactory("aliasStrings", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasStrings( compiler, compiler.getModuleGraph(), options.aliasAllStrings ? null : options.aliasableStrings, options.aliasStringsBlacklist, options.outputJsStringUsage); } }; /** Aliases common keywords (true, false) */ final PassFactory aliasKeywords = new PassFactory("aliasKeywords", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AliasKeywords(compiler); } }; /** Handling for the ObjectPropertyString primitive. */ final PassFactory objectPropertyStringPostprocess = new PassFactory("ObjectPropertyStringPostprocess", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ObjectPropertyStringPostprocess(compiler); } }; /** * Renames properties so that the two properties that never appear on * the same object get the same name. */ final PassFactory ambiguateProperties = new PassFactory("ambiguateProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AmbiguateProperties( compiler, options.anonymousFunctionNaming.getReservedCharacters()); } }; /** * Mark the point at which the normalized AST assumptions no longer hold. */ final PassFactory markUnnormalized = new PassFactory("markUnnormalized", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { compiler.setLifeCycleStage(LifeCycleStage.RAW); } }; } }; /** Denormalize the AST for code generation. */ final PassFactory denormalize = new PassFactory("denormalize", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new Denormalize(compiler); } }; /** Inverting name normalization. */ final PassFactory invertContextualRenaming = new PassFactory("invertContextualRenaming", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); } }; /** * Renames properties. */ final PassFactory renameProperties = new PassFactory("renameProperties", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { final VariableMap prevPropertyMap = options.inputPropertyMap; return new CompilerPass() { @Override public void process(Node externs, Node root) { propertyMap = runPropertyRenaming( compiler, prevPropertyMap, externs, root); } }; } }; private VariableMap runPropertyRenaming( AbstractCompiler compiler, VariableMap prevPropertyMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); switch (options.propertyRenaming) { case HEURISTIC: RenamePrototypes rproto = new RenamePrototypes(compiler, false, reservedChars, prevPropertyMap); rproto.process(externs, root); return rproto.getPropertyMap(); case AGGRESSIVE_HEURISTIC: RenamePrototypes rproto2 = new RenamePrototypes(compiler, true, reservedChars, prevPropertyMap); rproto2.process(externs, root); return rproto2.getPropertyMap(); case ALL_UNQUOTED: RenameProperties rprop = new RenameProperties( compiler, options.propertyAffinity, options.generatePseudoNames, prevPropertyMap, reservedChars); rprop.process(externs, root); return rprop.getPropertyMap(); default: throw new IllegalStateException( "Unrecognized property renaming policy"); } } /** Renames variables. */ final PassFactory renameVars = new PassFactory("renameVars", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { final VariableMap prevVariableMap = options.inputVariableMap; return new CompilerPass() { @Override public void process(Node externs, Node root) { variableMap = runVariableRenaming( compiler, prevVariableMap, externs, root); } }; } }; private VariableMap runVariableRenaming( AbstractCompiler compiler, VariableMap prevVariableMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; Set reservedNames = Sets.newHashSet(); if (exportedNames != null) { reservedNames.addAll(exportedNames); } reservedNames.addAll(ParserRunner.getReservedVars()); RenameVars rn = new RenameVars( compiler, options.renamePrefix, options.variableRenaming == VariableRenamingPolicy.LOCAL, preserveAnonymousFunctionNames, options.generatePseudoNames, options.shadowVariables, prevVariableMap, reservedChars, reservedNames); rn.process(externs, root); return rn.getVariableMap(); } /** Renames labels */ final PassFactory renameLabels = new PassFactory("renameLabels", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new RenameLabels(compiler); } }; /** Convert bracket access to dot access */ final PassFactory convertToDottedProperties = new PassFactory("convertToDottedProperties", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new ConvertToDottedProperties(compiler); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckAst = new PassFactory("sanityCheckAst", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new AstValidator(); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckVars = new PassFactory("sanityCheckVars", true) { @Override protected CompilerPass create(AbstractCompiler compiler) { return new VarCheck(compiler, true); } }; /** Adds instrumentations according to an instrumentation template. */ final PassFactory instrumentFunctions = new PassFactory("instrumentFunctions", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { try { FileReader templateFile = new FileReader(options.instrumentationTemplate); (new InstrumentFunctions( compiler, functionNames, options.instrumentationTemplate, options.appNameStr, templateFile)).process(externs, root); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, options.instrumentationTemplate)); } } }; } }; /** * Create a no-op pass that can only run once. Used to break up loops. */ static PassFactory createEmptyPass(String name) { return new PassFactory(name, true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return runInSerial(); } }; } /** * Runs custom passes that are designated to run at a particular time. */ private PassFactory getCustomPasses( final CustomPassExecutionTime executionTime) { return new PassFactory("runCustomPasses", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return runInSerial(options.customPasses.get(executionTime)); } }; } /** * All inlining is forbidden in heuristic renaming mode, because inlining * will ruin the invariants that it depends on. */ private boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial(final CompilerPass ... passes) { return runInSerial(Lists.newArrayList(passes)); } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial( final Collection passes) { return new CompilerPass() { @Override public void process(Node externs, Node root) { for (CompilerPass pass : passes) { pass.process(externs, root); } } }; } @VisibleForTesting static Map getAdditionalReplacements( CompilerOptions options) { Map additionalReplacements = Maps.newHashMap(); if (options.markAsCompiled || options.closurePass) { additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode()); } if (options.closurePass && options.locale != null) { additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, IR.string(options.locale)); } return additionalReplacements; } final PassFactory printNameReferenceGraph = new PassFactory("printNameReferenceGraph", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); gc.process(externs, jsRoot); String graphFileName = options.nameReferenceGraphPath; try { Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()), new File(graphFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName)); } } }; } }; final PassFactory printNameReferenceReport = new PassFactory("printNameReferenceReport", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); String reportFileName = options.nameReferenceReportPath; try { NameReferenceGraphReport report = new NameReferenceGraphReport(gc.getNameReferenceGraph()); Files.write(report.getHtmlReport(), new File(reportFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_REPORT_FILE_ERROR, e.getMessage(), reportFileName)); } } }; } }; /** * A pass-factory that is good for {@code HotSwapCompilerPass} passes. */ abstract static class HotSwapPassFactory extends PassFactory { HotSwapPassFactory(String name, boolean isOneTimePass) { super(name, isOneTimePass); } @Override protected abstract HotSwapCompilerPass create(AbstractCompiler compiler); @Override HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) { return this.create(compiler); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SuppressDocWarningsGuard.java0000644000175000017500000000670012115204405030450 0ustar apoapo/* * 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.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Filters warnings based on in-code {@code @suppress} annotations. * @author nicksantos@google.com (Nick Santos) */ class SuppressDocWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; /** Warnings guards for each suppressible warnings group, indexed by name. */ private final Map suppressors = Maps.newHashMap(); /** * The suppressible groups, indexed by name. */ SuppressDocWarningsGuard(Map suppressibleGroups) { for (Map.Entry entry : suppressibleGroups.entrySet()) { suppressors.put( entry.getKey(), new DiagnosticGroupWarningsGuard( entry.getValue(), CheckLevel.OFF)); } } @Override public CheckLevel level(JSError error) { Node node = error.node; if (node != null) { boolean visitedFunction = false; for (Node current = node; current != null; current = current.getParent()) { int type = current.getType(); JSDocInfo info = null; if (type == Token.FUNCTION) { info = NodeUtil.getBestJSDocInfo(current); visitedFunction = true; } else if (type == Token.SCRIPT) { info = current.getJSDocInfo(); } else if (current.isVar() || current.isAssign()) { // There's one edge case we're worried about: // if the warning points to an assigment to a function, we // want the suppressions on that function to apply. // It's OK if we double-count some cases. Node rhs = NodeUtil.getRValueOfLValue(current.getFirstChild()); if (rhs != null) { if (rhs.isCast()) { rhs = rhs.getFirstChild(); } if (rhs.isFunction() && !visitedFunction) { info = NodeUtil.getBestJSDocInfo(current); } } } if (info != null) { for (String suppressor : info.getSuppressions()) { WarningsGuard guard = suppressors.get(suppressor); // Some @suppress tags are for other tools, and // may not have a warnings guard. if (guard != null) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { return newLevel; } } } } } } return null; } @Override public int getPriority() { // Happens after path-based filtering, but before other times // of filtering. return WarningsGuard.Priority.SUPPRESS_DOC.value; } } ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.j0000644000175000017500000015560712115204405031731 0ustar apoapo/* * 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.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.javascript.jscomp.CodingConvention.Bind; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; import java.util.regex.Pattern; /** * A peephole optimization that minimizes code by simplifying conditional * expressions, replacing IFs with HOOKs, replacing object constructors * with literals, and simplifying returns. * */ class PeepholeSubstituteAlternateSyntax extends AbstractPeepholeOptimization { private static final int AND_PRECEDENCE = NodeUtil.precedence(Token.AND); private static final int OR_PRECEDENCE = NodeUtil.precedence(Token.OR); private static final int NOT_PRECEDENCE = NodeUtil.precedence(Token.NOT); private static final CodeGenerator REGEXP_ESCAPER = CodeGenerator.forCostEstimation( null /* blow up if we try to produce code */); private final boolean late; private final int STRING_SPLIT_OVERHEAD = ".split('.')".length(); static final DiagnosticType INVALID_REGULAR_EXPRESSION_FLAGS = DiagnosticType.warning( "JSC_INVALID_REGULAR_EXPRESSION_FLAGS", "Invalid flags to RegExp constructor: {0}"); static final Predicate DONT_TRAVERSE_FUNCTIONS_PREDICATE = new Predicate() { @Override public boolean apply(Node input) { return !input.isFunction(); } }; /** * @param late When late is false, this mean we are currently running before * most of the other optimizations. In this case we would avoid optimizations * that would make the code harder to analyze (such as using string splitting, * merging statements with commas, etc). When this is true, we would * do anything to minimize for size. */ PeepholeSubstituteAlternateSyntax(boolean late) { this.late = late; } /** * Tries apply our various peephole minimizations on the passed in node. */ @Override @SuppressWarnings("fallthrough") public Node optimizeSubtree(Node node) { switch(node.getType()) { case Token.RETURN: { Node result = tryRemoveRedundantExit(node); if (result != node) { return result; } result = tryReplaceExitWithBreak(node); if (result != node) { return result; } return tryReduceReturn(node); } case Token.THROW: { Node result = tryRemoveRedundantExit(node); if (result != node) { return result; } return tryReplaceExitWithBreak(node); } // TODO(johnlenz): Maybe remove redundant BREAK and CONTINUE. Overlaps // with MinimizeExitPoints. case Token.NOT: tryMinimizeCondition(node.getFirstChild()); return tryMinimizeNot(node); case Token.IF: tryMinimizeCondition(node.getFirstChild()); return tryMinimizeIf(node); case Token.EXPR_RESULT: tryMinimizeCondition(node.getFirstChild()); return node; case Token.HOOK: tryMinimizeCondition(node.getFirstChild()); return node; case Token.WHILE: case Token.DO: tryMinimizeCondition(NodeUtil.getConditionExpression(node)); return node; case Token.FOR: if (!NodeUtil.isForIn(node)) { tryJoinForCondition(node); tryMinimizeCondition(NodeUtil.getConditionExpression(node)); } return node; case Token.TRUE: case Token.FALSE: return reduceTrueFalse(node); case Token.NEW: node = tryFoldStandardConstructors(node); if (!node.isCall()) { return node; } // Fall through on purpose because tryFoldStandardConstructors() may // convert a NEW node into a CALL node case Token.CALL: Node result = tryFoldLiteralConstructor(node); if (result == node) { result = tryFoldSimpleFunctionCall(node); if (result == node) { result = tryFoldImmediateCallToBoundFunction(node); } } return result; case Token.COMMA: return trySplitComma(node); case Token.NAME: return tryReplaceUndefined(node); case Token.BLOCK: return tryReplaceIf(node); case Token.ARRAYLIT: return tryMinimizeArrayLiteral(node); default: return node; //Nothing changed } } private void tryJoinForCondition(Node n) { if (!late) { return; } Node block = n.getLastChild(); Node maybeIf = block.getFirstChild(); if (maybeIf != null && maybeIf.isIf()) { Node maybeBreak = maybeIf.getChildAtIndex(1).getFirstChild(); if (maybeBreak != null && maybeBreak.isBreak() && !maybeBreak.hasChildren()) { // Preserve the IF ELSE expression is there is one. if (maybeIf.getChildCount() == 3) { block.replaceChild(maybeIf, maybeIf.getLastChild().detachFromParent()); } else { block.removeFirstChild(); } Node ifCondition = maybeIf.removeFirstChild(); Node fixedIfCondition = IR.not(ifCondition) .srcref(ifCondition); // OK, join the IF expression with the FOR expression Node forCondition = NodeUtil.getConditionExpression(n); if (forCondition.isEmpty()) { n.replaceChild(forCondition, fixedIfCondition); } else { Node replacement = new Node(Token.AND); n.replaceChild(forCondition, replacement); replacement.addChildToBack(forCondition); replacement.addChildToBack(fixedIfCondition); } reportCodeChange(); } } } private Node tryFoldSimpleFunctionCall(Node n) { Preconditions.checkState(n.isCall()); Node callTarget = n.getFirstChild(); if (callTarget != null && callTarget.isName() && callTarget.getString().equals("String")) { // Fold String(a) to '' + (a) on immutable literals, // which allows further optimizations // // We can't do this in the general case, because String(a) has // slightly different semantics than '' + (a). See // http://code.google.com/p/closure-compiler/issues/detail?id=759 Node value = callTarget.getNext(); if (value != null && value.getNext() == null && NodeUtil.isImmutableValue(value)) { Node addition = IR.add( IR.string("").srcref(callTarget), value.detachFromParent()); n.getParent().replaceChild(n, addition); reportCodeChange(); return addition; } } return n; } private Node tryFoldImmediateCallToBoundFunction(Node n) { // Rewriting "(fn.bind(a,b))()" to "fn.call(a,b)" makes it inlinable Preconditions.checkState(n.isCall()); Node callTarget = n.getFirstChild(); Bind bind = getCodingConvention().describeFunctionBind(callTarget, false); if (bind != null) { // replace the call target bind.target.detachFromParent(); n.replaceChild(callTarget, bind.target); callTarget = bind.target; // push the parameters addParameterAfter(bind.parameters, callTarget); // add the this value before the parameters if necessary if (bind.thisValue != null && !NodeUtil.isUndefined(bind.thisValue)) { // rewrite from "fn(a, b)" to "fn.call(thisValue, a, b)" Node newCallTarget = IR.getprop( callTarget.cloneTree(), IR.string("call").srcref(callTarget)); n.replaceChild(callTarget, newCallTarget); n.addChildAfter(bind.thisValue.cloneTree(), newCallTarget); n.putBooleanProp(Node.FREE_CALL, false); } else { n.putBooleanProp(Node.FREE_CALL, true); } reportCodeChange(); } return n; } private void addParameterAfter(Node parameterList, Node after) { if (parameterList != null) { // push the last parameter to the head of the list first. addParameterAfter(parameterList.getNext(), after); after.getParent().addChildAfter(parameterList.cloneTree(), after); } } private Node trySplitComma(Node n) { if (late) { return n; } Node parent = n.getParent(); Node left = n.getFirstChild(); Node right = n.getLastChild(); if (parent.isExprResult() && !parent.getParent().isLabel()) { // split comma n.detachChildren(); // Replace the original expression with the left operand. parent.replaceChild(n, left); // Add the right expression afterward. Node newStatement = IR.exprResult(right); newStatement.copyInformationFrom(n); //This modifies outside the subtree, which is not //desirable in a peephole optimization. parent.getParent().addChildAfter(newStatement, parent); reportCodeChange(); return left; } else { return n; } } /** * Use "return x?1:2;" in place of "if(x)return 1;return 2;" */ private Node tryReplaceIf(Node n) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()){ if (child.isIf()){ Node cond = child.getFirstChild(); Node thenBranch = cond.getNext(); Node elseBranch = thenBranch.getNext(); Node nextNode = child.getNext(); if (nextNode != null && elseBranch == null && isReturnBlock(thenBranch) && nextNode.isIf()) { Node nextCond = nextNode.getFirstChild(); Node nextThen = nextCond.getNext(); Node nextElse = nextThen.getNext(); if (thenBranch.isEquivalentToTyped(nextThen)) { // Transform // if (x) return 1; if (y) return 1; // to // if (x||y) return 1; child.detachFromParent(); child.detachChildren(); Node newCond = new Node(Token.OR, cond); nextNode.replaceChild(nextCond, newCond); newCond.addChildToBack(nextCond); reportCodeChange(); } else if (nextElse != null && thenBranch.isEquivalentToTyped(nextElse)) { // Transform // if (x) return 1; if (y) foo() else return 1; // to // if (!x&&y) foo() else return 1; child.detachFromParent(); child.detachChildren(); Node newCond = new Node(Token.AND, IR.not(cond).srcref(cond)); nextNode.replaceChild(nextCond, newCond); newCond.addChildToBack(nextCond); reportCodeChange(); } } else if (nextNode != null && elseBranch == null && isReturnBlock(thenBranch) && isReturnExpression(nextNode)) { Node thenExpr = null; // if(x)return; return 1 -> return x?void 0:1 if (isReturnExpressBlock(thenBranch)) { thenExpr = getBlockReturnExpression(thenBranch); thenExpr.detachFromParent(); } else { thenExpr = NodeUtil.newUndefinedNode(child); } Node elseExpr = nextNode.getFirstChild(); cond.detachFromParent(); elseExpr.detachFromParent(); Node returnNode = IR.returnNode( IR.hook(cond, thenExpr, elseExpr) .srcref(child)); n.replaceChild(child, returnNode); n.removeChild(nextNode); reportCodeChange(); } else if (elseBranch != null && statementMustExitParent(thenBranch)) { child.removeChild(elseBranch); n.addChildAfter(elseBranch, child); reportCodeChange(); } } } return n; } private boolean statementMustExitParent(Node n) { switch (n.getType()) { case Token.THROW: case Token.RETURN: return true; case Token.BLOCK: if (n.hasChildren()) { Node child = n.getLastChild(); return statementMustExitParent(child); } return false; // TODO(johnlenz): handle TRY/FINALLY case Token.FUNCTION: default: return false; } } /** * Use "void 0" in place of "undefined" */ private Node tryReplaceUndefined(Node n) { // TODO(johnlenz): consider doing this as a normalization. if (isASTNormalized() && NodeUtil.isUndefined(n) && !NodeUtil.isLValue(n)) { Node replacement = NodeUtil.newUndefinedNode(n); n.getParent().replaceChild(n, replacement); reportCodeChange(); return replacement; } return n; } /** * Reduce "return undefined" or "return void 0" to simply "return". * * @return The original node, maybe simplified. */ private Node tryReduceReturn(Node n) { Node result = n.getFirstChild(); if (result != null) { switch (result.getType()) { case Token.VOID: Node operand = result.getFirstChild(); if (!mayHaveSideEffects(operand)) { n.removeFirstChild(); reportCodeChange(); } break; case Token.NAME: String name = result.getString(); if (name.equals("undefined")) { n.removeFirstChild(); reportCodeChange(); } break; } } return n; } /** * Replace duplicate exits in control structures. If the node following * the exit node expression has the same effect as exit node, the node can * be replaced or removed. * For example: * "while (a) {return f()} return f();" ==> "while (a) {break} return f();" * "while (a) {throw 'ow'} throw 'ow';" ==> "while (a) {break} throw 'ow';" * * @param n An follow control exit expression (a THROW or RETURN node) * @return The replacement for n, or the original if no change was made. */ private Node tryReplaceExitWithBreak(Node n) { Node result = n.getFirstChild(); // Find the enclosing control structure, if any, that a "break" would exit // from. Node breakTarget = n; for (;!ControlFlowAnalysis.isBreakTarget(breakTarget, null /* no label */); breakTarget = breakTarget.getParent()) { if (breakTarget.isFunction() || breakTarget.isScript()) { // No break target. return n; } } Node follow = ControlFlowAnalysis.computeFollowNode(breakTarget); // Skip pass all the finally blocks because both the break and return will // also trigger all the finally blocks. However, the order of execution is // slightly changed. Consider: // // return a() -> finally { b() } -> return a() // // which would call a() first. However, changing the first return to a // break will result in calling b(). Node prefinallyFollows = follow; follow = skipFinallyNodes(follow); if (prefinallyFollows != follow) { // There were finally clauses if (!isPure(result)) { // Can't defer the exit return n; } } if (follow == null && (n.isThrow() || result != null)) { // Can't complete remove a throw here or a return with a result. return n; } // When follow is null, this mean the follow of a break target is the // end of a function. This means a break is same as return. if (follow == null || areMatchingExits(n, follow)) { Node replacement = IR.breakNode(); n.getParent().replaceChild(n, replacement); this.reportCodeChange(); return replacement; } return n; } /** * Remove duplicate exits. If the node following the exit node expression * has the same effect as exit node, the node can be removed. * For example: * "if (a) {return f()} return f();" ==> "if (a) {} return f();" * "if (a) {throw 'ow'} throw 'ow';" ==> "if (a) {} throw 'ow';" * * @param n An follow control exit expression (a THROW or RETURN node) * @return The replacement for n, or the original if no change was made. */ private Node tryRemoveRedundantExit(Node n) { Node exitExpr = n.getFirstChild(); Node follow = ControlFlowAnalysis.computeFollowNode(n); // Skip pass all the finally blocks because both the fall through and return // will also trigger all the finally blocks. Node prefinallyFollows = follow; follow = skipFinallyNodes(follow); if (prefinallyFollows != follow) { // There were finally clauses if (!isPure(exitExpr)) { // Can't replace the return return n; } } if (follow == null && (n.isThrow() || exitExpr != null)) { // Can't complete remove a throw here or a return with a result. return n; } // When follow is null, this mean the follow of a break target is the // end of a function. This means a break is same as return. if (follow == null || areMatchingExits(n, follow)) { n.detachFromParent(); reportCodeChange(); return null; } return n; } /** * @return Whether the expression does not produces and can not be affected * by side-effects. */ boolean isPure(Node n) { return n == null || (!NodeUtil.canBeSideEffected(n) && !mayHaveSideEffects(n)); } /** * @return n or the node following any following finally nodes. */ Node skipFinallyNodes(Node n) { while (n != null && NodeUtil.isTryFinallyNode(n.getParent(), n)) { n = ControlFlowAnalysis.computeFollowNode(n); } return n; } /** * Check whether one exit can be replaced with another. Verify: * 1) They are identical expressions * 2) If an exception is possible that the statements, the original * and the potential replacement are in the same exception handler. */ boolean areMatchingExits(Node nodeThis, Node nodeThat) { return nodeThis.isEquivalentTo(nodeThat) && (!isExceptionPossible(nodeThis) || getExceptionHandler(nodeThis) == getExceptionHandler(nodeThat)); } boolean isExceptionPossible(Node n) { // TODO(johnlenz): maybe use ControlFlowAnalysis.mayThrowException? Preconditions.checkState(n.isReturn() || n.isThrow()); return n.isThrow() || (n.hasChildren() && !NodeUtil.isLiteralValue(n.getLastChild(), true)); } Node getExceptionHandler(Node n) { return ControlFlowAnalysis.getExceptionHandler(n); } /** * Try to minimize NOT nodes such as !(x==y). * * Returns the replacement for n or the original if no change was made */ private Node tryMinimizeNot(Node n) { Node parent = n.getParent(); Node notChild = n.getFirstChild(); // negative operator of the current one : == -> != for instance. int complementOperator; switch (notChild.getType()) { case Token.EQ: complementOperator = Token.NE; break; case Token.NE: complementOperator = Token.EQ; break; case Token.SHEQ: complementOperator = Token.SHNE; break; case Token.SHNE: complementOperator = Token.SHEQ; break; // GT, GE, LT, LE are not handled in this because !(x=NaN. default: return n; } Node newOperator = n.removeFirstChild(); newOperator.setType(complementOperator); parent.replaceChild(n, newOperator); reportCodeChange(); return newOperator; } /** * Try turning IF nodes into smaller HOOKs * * Returns the replacement for n or the original if no replacement was * necessary. */ private Node tryMinimizeIf(Node n) { Node parent = n.getParent(); Node cond = n.getFirstChild(); /* If the condition is a literal, we'll let other * optimizations try to remove useless code. */ if (NodeUtil.isLiteralValue(cond, true)) { return n; } Node thenBranch = cond.getNext(); Node elseBranch = thenBranch.getNext(); if (elseBranch == null) { if (isFoldableExpressBlock(thenBranch)) { Node expr = getBlockExpression(thenBranch); if (!late && isPropertyAssignmentInExpression(expr)) { // Keep opportunities for CollapseProperties such as // a.longIdentifier || a.longIdentifier = ... -> var a = ...; // until CollapseProperties has been run. return n; } if (cond.isNot()) { // if(!x)bar(); -> x||bar(); if (isLowerPrecedenceInExpression(cond, OR_PRECEDENCE) && isLowerPrecedenceInExpression(expr.getFirstChild(), OR_PRECEDENCE)) { // It's not okay to add two sets of parentheses. return n; } Node or = IR.or( cond.removeFirstChild(), expr.removeFirstChild()).srcref(n); Node newExpr = NodeUtil.newExpr(or); parent.replaceChild(n, newExpr); reportCodeChange(); return newExpr; } // if(x)foo(); -> x&&foo(); if (isLowerPrecedenceInExpression(cond, AND_PRECEDENCE) && isLowerPrecedenceInExpression(expr.getFirstChild(), AND_PRECEDENCE)) { // One additional set of parentheses is worth the change even if // there is no immediate code size win. However, two extra pair of // {}, we would have to think twice. (unless we know for sure the // we can further optimize its parent. return n; } n.removeChild(cond); Node and = IR.and(cond, expr.removeFirstChild()).srcref(n); Node newExpr = NodeUtil.newExpr(and); parent.replaceChild(n, newExpr); reportCodeChange(); return newExpr; } else { // Try to combine two IF-ELSE if (NodeUtil.isStatementBlock(thenBranch) && thenBranch.hasOneChild()) { Node innerIf = thenBranch.getFirstChild(); if (innerIf.isIf()) { Node innerCond = innerIf.getFirstChild(); Node innerThenBranch = innerCond.getNext(); Node innerElseBranch = innerThenBranch.getNext(); if (innerElseBranch == null && !(isLowerPrecedenceInExpression(cond, AND_PRECEDENCE) && isLowerPrecedenceInExpression(innerCond, AND_PRECEDENCE))) { n.detachChildren(); n.addChildToBack( IR.and( cond, innerCond.detachFromParent()) .srcref(cond)); n.addChildrenToBack(innerThenBranch.detachFromParent()); reportCodeChange(); // Not worth trying to fold the current IF-ELSE into && because // the inner IF-ELSE wasn't able to be folded into && anyways. return n; } } } } return n; } /* TODO(dcc) This modifies the siblings of n, which is undesirable for a * peephole optimization. This should probably get moved to another pass. */ tryRemoveRepeatedStatements(n); // if(!x)foo();else bar(); -> if(x)bar();else foo(); // An additional set of curly braces isn't worth it. if (cond.isNot() && !consumesDanglingElse(elseBranch)) { n.replaceChild(cond, cond.removeFirstChild()); n.removeChild(thenBranch); n.addChildToBack(thenBranch); reportCodeChange(); return n; } // if(x)return 1;else return 2; -> return x?1:2; if (isReturnExpressBlock(thenBranch) && isReturnExpressBlock(elseBranch)) { Node thenExpr = getBlockReturnExpression(thenBranch); Node elseExpr = getBlockReturnExpression(elseBranch); n.removeChild(cond); thenExpr.detachFromParent(); elseExpr.detachFromParent(); // note - we ignore any cases with "return;", technically this // can be converted to "return undefined;" or some variant, but // that does not help code size. Node returnNode = IR.returnNode( IR.hook(cond, thenExpr, elseExpr) .srcref(n)); parent.replaceChild(n, returnNode); reportCodeChange(); return returnNode; } boolean thenBranchIsExpressionBlock = isFoldableExpressBlock(thenBranch); boolean elseBranchIsExpressionBlock = isFoldableExpressBlock(elseBranch); if (thenBranchIsExpressionBlock && elseBranchIsExpressionBlock) { Node thenOp = getBlockExpression(thenBranch).getFirstChild(); Node elseOp = getBlockExpression(elseBranch).getFirstChild(); if (thenOp.getType() == elseOp.getType()) { // if(x)a=1;else a=2; -> a=x?1:2; if (NodeUtil.isAssignmentOp(thenOp)) { Node lhs = thenOp.getFirstChild(); if (areNodesEqualForInlining(lhs, elseOp.getFirstChild()) && // if LHS has side effects, don't proceed [since the optimization // evaluates LHS before cond] // NOTE - there are some circumstances where we can // proceed even if there are side effects... !mayEffectMutableState(lhs) && (!mayHaveSideEffects(cond) || (thenOp.isAssign() && thenOp.getFirstChild().isName()))) { n.removeChild(cond); Node assignName = thenOp.removeFirstChild(); Node thenExpr = thenOp.removeFirstChild(); Node elseExpr = elseOp.getLastChild(); elseOp.removeChild(elseExpr); Node hookNode = IR.hook(cond, thenExpr, elseExpr).srcref(n); Node assign = new Node(thenOp.getType(), assignName, hookNode) .srcref(thenOp); Node expr = NodeUtil.newExpr(assign); parent.replaceChild(n, expr); reportCodeChange(); return expr; } } } // if(x)foo();else bar(); -> x?foo():bar() n.removeChild(cond); thenOp.detachFromParent(); elseOp.detachFromParent(); Node expr = IR.exprResult( IR.hook(cond, thenOp, elseOp).srcref(n)); parent.replaceChild(n, expr); reportCodeChange(); return expr; } boolean thenBranchIsVar = isVarBlock(thenBranch); boolean elseBranchIsVar = isVarBlock(elseBranch); // if(x)var y=1;else y=2 -> var y=x?1:2 if (thenBranchIsVar && elseBranchIsExpressionBlock && getBlockExpression(elseBranch).getFirstChild().isAssign()) { Node var = getBlockVar(thenBranch); Node elseAssign = getBlockExpression(elseBranch).getFirstChild(); Node name1 = var.getFirstChild(); Node maybeName2 = elseAssign.getFirstChild(); if (name1.hasChildren() && maybeName2.isName() && name1.getString().equals(maybeName2.getString())) { Node thenExpr = name1.removeChildren(); Node elseExpr = elseAssign.getLastChild().detachFromParent(); cond.detachFromParent(); Node hookNode = IR.hook(cond, thenExpr, elseExpr) .srcref(n); var.detachFromParent(); name1.addChildrenToBack(hookNode); parent.replaceChild(n, var); reportCodeChange(); return var; } // if(x)y=1;else var y=2 -> var y=x?1:2 } else if (elseBranchIsVar && thenBranchIsExpressionBlock && getBlockExpression(thenBranch).getFirstChild().isAssign()) { Node var = getBlockVar(elseBranch); Node thenAssign = getBlockExpression(thenBranch).getFirstChild(); Node maybeName1 = thenAssign.getFirstChild(); Node name2 = var.getFirstChild(); if (name2.hasChildren() && maybeName1.isName() && maybeName1.getString().equals(name2.getString())) { Node thenExpr = thenAssign.getLastChild().detachFromParent(); Node elseExpr = name2.removeChildren(); cond.detachFromParent(); Node hookNode = IR.hook(cond, thenExpr, elseExpr) .srcref(n); var.detachFromParent(); name2.addChildrenToBack(hookNode); parent.replaceChild(n, var); reportCodeChange(); return var; } } return n; } /** * Try to remove duplicate statements from IF blocks. For example: * * if (a) { * x = 1; * return true; * } else { * x = 2; * return true; * } * * becomes: * * if (a) { * x = 1; * } else { * x = 2; * } * return true; * * @param n The IF node to examine. */ private void tryRemoveRepeatedStatements(Node n) { Preconditions.checkState(n.isIf()); Node parent = n.getParent(); if (!NodeUtil.isStatementBlock(parent)) { // If the immediate parent is something like a label, we // can't move the statement, so bail. return; } Node cond = n.getFirstChild(); Node trueBranch = cond.getNext(); Node falseBranch = trueBranch.getNext(); Preconditions.checkNotNull(trueBranch); Preconditions.checkNotNull(falseBranch); while (true) { Node lastTrue = trueBranch.getLastChild(); Node lastFalse = falseBranch.getLastChild(); if (lastTrue == null || lastFalse == null || !areNodesEqualForInlining(lastTrue, lastFalse)) { break; } lastTrue.detachFromParent(); lastFalse.detachFromParent(); parent.addChildAfter(lastTrue, n); reportCodeChange(); } } /** * @return Whether the node is a block with a single statement that is * an expression. */ private boolean isFoldableExpressBlock(Node n) { if (n.isBlock()) { if (n.hasOneChild()) { Node maybeExpr = n.getFirstChild(); if (maybeExpr.isExprResult()) { // IE has a bug where event handlers behave differently when // their return value is used vs. when their return value is in // an EXPR_RESULT. It's pretty freaking weird. See: // http://code.google.com/p/closure-compiler/issues/detail?id=291 // We try to detect this case, and not fold EXPR_RESULTs // into other expressions. if (maybeExpr.getFirstChild().isCall()) { Node calledFn = maybeExpr.getFirstChild().getFirstChild(); // We only have to worry about methods with an implicit 'this' // param, or this doesn't happen. if (calledFn.isGetElem()) { return false; } else if (calledFn.isGetProp() && calledFn.getLastChild().getString().startsWith("on")) { return false; } } return true; } return false; } } return false; } /** * @return The expression node. */ private Node getBlockExpression(Node n) { Preconditions.checkState(isFoldableExpressBlock(n)); return n.getFirstChild(); } /** * @return Whether the node is a block with a single statement that is * an return with or without an expression. */ private boolean isReturnBlock(Node n) { if (n.isBlock()) { if (n.hasOneChild()) { Node first = n.getFirstChild(); return first.isReturn(); } } return false; } /** * @return Whether the node is a block with a single statement that is * an return. */ private boolean isReturnExpressBlock(Node n) { if (n.isBlock()) { if (n.hasOneChild()) { Node first = n.getFirstChild(); if (first.isReturn()) { return first.hasOneChild(); } } } return false; } /** * @return Whether the node is a single return statement. */ private boolean isReturnExpression(Node n) { if (n.isReturn()) { return n.hasOneChild(); } return false; } /** * @return The expression that is part of the return. */ private Node getBlockReturnExpression(Node n) { Preconditions.checkState(isReturnExpressBlock(n)); return n.getFirstChild().getFirstChild(); } /** * @return Whether the node is a block with a single statement that is * a VAR declaration of a single variable. */ private boolean isVarBlock(Node n) { if (n.isBlock()) { if (n.hasOneChild()) { Node first = n.getFirstChild(); if (first.isVar()) { return first.hasOneChild(); } } } return false; } /** * @return The var node. */ private Node getBlockVar(Node n) { Preconditions.checkState(isVarBlock(n)); return n.getFirstChild(); } /** * Does a statement consume a 'dangling else'? A statement consumes * a 'dangling else' if an 'else' token following the statement * would be considered by the parser to be part of the statement. */ private boolean consumesDanglingElse(Node n) { while (true) { switch (n.getType()) { case Token.IF: if (n.getChildCount() < 3) { return true; } // This IF node has no else clause. n = n.getLastChild(); continue; case Token.WITH: case Token.WHILE: case Token.FOR: n = n.getLastChild(); continue; default: return false; } } } /** * Does the expression contain an operator with lower precedence than * the argument? */ private boolean isLowerPrecedenceInExpression(Node n, final int precedence) { Predicate isLowerPrecedencePredicate = new Predicate() { @Override public boolean apply(Node input) { return NodeUtil.precedence(input.getType()) < precedence; } }; return NodeUtil.has(n, isLowerPrecedencePredicate, DONT_TRAVERSE_FUNCTIONS_PREDICATE); } /** * Whether the node type has lower precedence than "precedence" */ private boolean isLowerPrecedence(Node n, final int precedence) { return NodeUtil.precedence(n.getType()) < precedence; } /** * Whether the node type has higher precedence than "precedence" */ private boolean isHigherPrecedence(Node n, final int precedence) { return NodeUtil.precedence(n.getType()) > precedence; } /** * Does the expression contain a property assignment? */ private boolean isPropertyAssignmentInExpression(Node n) { Predicate isPropertyAssignmentInExpressionPredicate = new Predicate() { @Override public boolean apply(Node input) { return (input.isGetProp() && input.getParent().isAssign()); } }; return NodeUtil.has(n, isPropertyAssignmentInExpressionPredicate, DONT_TRAVERSE_FUNCTIONS_PREDICATE); } /** * Try to minimize conditions expressions, as there are additional * assumptions that can be made when it is known that the final result * is a boolean. * * The following transformations are done recursively: * !(x||y) --> !x&&!y * !(x&&y) --> !x||!y * !!x --> x * Thus: * !(x&&!y) --> !x||!!y --> !x||y * * Returns the replacement for n, or the original if no change was made */ private Node tryMinimizeCondition(Node n) { Node parent = n.getParent(); switch (n.getType()) { case Token.NOT: Node first = n.getFirstChild(); switch (first.getType()) { case Token.NOT: { Node newRoot = first.removeFirstChild(); parent.replaceChild(n, newRoot); reportCodeChange(); // No need to traverse, tryMinimizeCondition is called on the // NOT children are handled below. return newRoot; } case Token.AND: case Token.OR: { // !(!x && !y) --> x || y // !(!x || !y) --> x && y // !(!x && y) --> x || !y // !(!x || y) --> x && !y // !(x && !y) --> !x || y // !(x || !y) --> !x && y // !(x && y) --> !x || !y // !(x || y) --> !x && !y Node leftParent = first.getFirstChild(); Node rightParent = first.getLastChild(); Node left, right; // Check special case when such transformation cannot reduce // due to the added () // It only occurs when both of expressions are not NOT expressions if (!leftParent.isNot() && !rightParent.isNot()) { // If an expression has higher precedence than && or ||, // but lower precedence than NOT, an additional () is needed // Thus we do not preceed int op_precedence = NodeUtil.precedence(first.getType()); if ((isLowerPrecedence(leftParent, NOT_PRECEDENCE) && isHigherPrecedence(leftParent, op_precedence)) || (isLowerPrecedence(rightParent, NOT_PRECEDENCE) && isHigherPrecedence(rightParent, op_precedence))) { return n; } } if (leftParent.isNot()) { left = leftParent.removeFirstChild(); } else { leftParent.detachFromParent(); left = IR.not(leftParent).srcref(leftParent); } if (rightParent.isNot()) { right = rightParent.removeFirstChild(); } else { rightParent.detachFromParent(); right = IR.not(rightParent).srcref(rightParent); } int newOp = (first.isAnd()) ? Token.OR : Token.AND; Node newRoot = new Node(newOp, left, right); parent.replaceChild(n, newRoot); reportCodeChange(); // No need to traverse, tryMinimizeCondition is called on the // AND and OR children below. return newRoot; } default: TernaryValue nVal = NodeUtil.getPureBooleanValue(first); if (nVal != TernaryValue.UNKNOWN) { boolean result = nVal.not().toBoolean(true); int equivalentResult = result ? 1 : 0; return maybeReplaceChildWithNumber(n, parent, equivalentResult); } } // No need to traverse, tryMinimizeCondition is called on the NOT // children in the general case in the main post-order traversal. return n; case Token.OR: case Token.AND: { Node left = n.getFirstChild(); Node right = n.getLastChild(); // Because the expression is in a boolean context minimize // the children, this can't be done in the general case. left = tryMinimizeCondition(left); right = tryMinimizeCondition(right); // Remove useless conditionals // Handle four cases: // x || false --> x // x || true --> true // x && true --> x // x && false --> false TernaryValue rightVal = NodeUtil.getPureBooleanValue(right); if (NodeUtil.getPureBooleanValue(right) != TernaryValue.UNKNOWN) { int type = n.getType(); Node replacement = null; boolean rval = rightVal.toBoolean(true); // (x || FALSE) => x // (x && TRUE) => x if (type == Token.OR && !rval || type == Token.AND && rval) { replacement = left; } else if (!mayHaveSideEffects(left)) { replacement = right; } if (replacement != null) { n.detachChildren(); parent.replaceChild(n, replacement); reportCodeChange(); return replacement; } } return n; } case Token.HOOK: { Node condition = n.getFirstChild(); Node trueNode = n.getFirstChild().getNext(); Node falseNode = n.getLastChild(); // Because the expression is in a boolean context minimize // the result children, this can't be done in the general case. // The condition is handled in the general case in #optimizeSubtree trueNode = tryMinimizeCondition(trueNode); falseNode = tryMinimizeCondition(falseNode); // Handle four cases: // x ? true : false --> x // x ? false : true --> !x // x ? true : y --> x || y // x ? y : false --> x && y Node replacement = null; TernaryValue trueNodeVal = NodeUtil.getPureBooleanValue(trueNode); TernaryValue falseNodeVal = NodeUtil.getPureBooleanValue(falseNode); if (trueNodeVal == TernaryValue.TRUE && falseNodeVal == TernaryValue.FALSE) { // Remove useless conditionals, keep the condition condition.detachFromParent(); replacement = condition; } else if (trueNodeVal == TernaryValue.FALSE && falseNodeVal == TernaryValue.TRUE) { // Remove useless conditionals, keep the condition condition.detachFromParent(); replacement = IR.not(condition); } else if (trueNodeVal == TernaryValue.TRUE) { // Remove useless true case. n.detachChildren(); replacement = IR.or(condition, falseNode); } else if (falseNodeVal == TernaryValue.FALSE) { // Remove useless false case n.detachChildren(); replacement = IR.and(condition, trueNode); } if (replacement != null) { parent.replaceChild(n, replacement); n = replacement; reportCodeChange(); } return n; } default: // while(true) --> while(1) TernaryValue nVal = NodeUtil.getPureBooleanValue(n); if (nVal != TernaryValue.UNKNOWN) { boolean result = nVal.toBoolean(true); int equivalentResult = result ? 1 : 0; return maybeReplaceChildWithNumber(n, parent, equivalentResult); } // We can't do anything else currently. return n; } } /** * Replaces a node with a number node if the new number node is not equivalent * to the current node. * * Returns the replacement for n if it was replaced, otherwise returns n. */ private Node maybeReplaceChildWithNumber(Node n, Node parent, int num) { Node newNode = IR.number(num); if (!newNode.isEquivalentTo(n)) { parent.replaceChild(n, newNode); reportCodeChange(); return newNode; } return n; } private static final ImmutableSet STANDARD_OBJECT_CONSTRUCTORS = // String, Number, and Boolean functions return non-object types, whereas // new String, new Number, and new Boolean return object types, so don't // include them here. ImmutableSet.of( "Object", "Array", "RegExp", "Error" ); /** * Fold "new Object()" to "Object()". */ private Node tryFoldStandardConstructors(Node n) { Preconditions.checkState(n.isNew()); // If name normalization has been run then we know that // new Object() does in fact refer to what we think it is // and not some custom-defined Object(). if (isASTNormalized()) { if (n.getFirstChild().isName()) { String className = n.getFirstChild().getString(); if (STANDARD_OBJECT_CONSTRUCTORS.contains(className)) { n.setType(Token.CALL); n.putBooleanProp(Node.FREE_CALL, true); reportCodeChange(); } } } return n; } /** * Replaces a new Array or Object node with an object literal, unless the * call to Array or Object is to a local function with the same name. */ private Node tryFoldLiteralConstructor(Node n) { Preconditions.checkArgument(n.isCall() || n.isNew()); Node constructorNameNode = n.getFirstChild(); Node newLiteralNode = null; // We require the AST to be normalized to ensure that, say, // Object() really refers to the built-in Object constructor // and not a user-defined constructor with the same name. if (isASTNormalized() && Token.NAME == constructorNameNode.getType()) { String className = constructorNameNode.getString(); if ("RegExp".equals(className)) { // "RegExp("boo", "g")" --> /boo/g return tryFoldRegularExpressionConstructor(n); } else { boolean constructorHasArgs = constructorNameNode.getNext() != null; if ("Object".equals(className) && !constructorHasArgs) { // "Object()" --> "{}" newLiteralNode = IR.objectlit(); } else if ("Array".equals(className)) { // "Array(arg0, arg1, ...)" --> "[arg0, arg1, ...]" Node arg0 = constructorNameNode.getNext(); FoldArrayAction action = isSafeToFoldArrayConstructor(arg0); if (action == FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS || action == FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS) { newLiteralNode = IR.arraylit(); n.removeChildren(); if (action == FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS) { newLiteralNode.addChildrenToFront(arg0); } } } if (newLiteralNode != null) { n.getParent().replaceChild(n, newLiteralNode); reportCodeChange(); return newLiteralNode; } } } return n; } private static enum FoldArrayAction { NOT_SAFE_TO_FOLD, SAFE_TO_FOLD_WITH_ARGS, SAFE_TO_FOLD_WITHOUT_ARGS} /** * Checks if it is safe to fold Array() constructor into []. It can be * obviously done, if the initial constructor has either no arguments or * at least two. The remaining case may be unsafe since Array(number) * actually reserves memory for an empty array which contains number elements. */ private FoldArrayAction isSafeToFoldArrayConstructor(Node arg) { FoldArrayAction action = FoldArrayAction.NOT_SAFE_TO_FOLD; if (arg == null) { action = FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS; } else if (arg.getNext() != null) { action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; } else { switch (arg.getType()) { case Token.STRING: // "Array('a')" --> "['a']" action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; break; case Token.NUMBER: // "Array(0)" --> "[]" if (arg.getDouble() == 0) { action = FoldArrayAction.SAFE_TO_FOLD_WITHOUT_ARGS; } break; case Token.ARRAYLIT: // "Array([args])" --> "[[args]]" action = FoldArrayAction.SAFE_TO_FOLD_WITH_ARGS; break; default: } } return action; } private Node tryFoldRegularExpressionConstructor(Node n) { Node parent = n.getParent(); Node constructor = n.getFirstChild(); Node pattern = constructor.getNext(); // e.g. ^foobar$ Node flags = null != pattern ? pattern.getNext() : null; // e.g. gi if (null == pattern || (null != flags && null != flags.getNext())) { // too few or too many arguments return n; } if (// is pattern folded pattern.isString() // make sure empty pattern doesn't fold to // && !"".equals(pattern.getString()) // NOTE(nicksantos): Make sure that the regexp isn't longer than // 100 chars, or it blows up the regexp parser in Opera 9.2. && pattern.getString().length() < 100 && (null == flags || flags.isString()) // don't escape patterns with Unicode escapes since Safari behaves badly // (read can't parse or crashes) on regex literals with Unicode escapes && (isEcmaScript5OrGreater() || !containsUnicodeEscape(pattern.getString()))) { // Make sure that / is escaped, so that it will fit safely in /brackets/ // and make sure that no LineTerminatorCharacters appear literally inside // the pattern. // pattern is a string value with \\ and similar already escaped pattern = makeForwardSlashBracketSafe(pattern); Node regexLiteral; if (null == flags || "".equals(flags.getString())) { // fold to /foobar/ regexLiteral = IR.regexp(pattern); } else { // fold to /foobar/gi if (!areValidRegexpFlags(flags.getString())) { report(INVALID_REGULAR_EXPRESSION_FLAGS, flags); return n; } if (!areSafeFlagsToFold(flags.getString())) { return n; } n.removeChild(flags); regexLiteral = IR.regexp(pattern, flags); } parent.replaceChild(n, regexLiteral); reportCodeChange(); return regexLiteral; } return n; } private Node reduceTrueFalse(Node n) { if (late) { Node not = IR.not(IR.number(n.isTrue() ? 0 : 1)); not.copyInformationFromForTree(n); n.getParent().replaceChild(n, not); reportCodeChange(); return not; } return n; } private Node tryMinimizeArrayLiteral(Node n) { boolean allStrings = true; for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { if (!cur.isString()) { allStrings = false; } } if (allStrings) { return tryMinimizeStringArrayLiteral(n); } else { return n; } } private Node tryMinimizeStringArrayLiteral(Node n) { if(!late) { return n; } int numElements = n.getChildCount(); // We save two bytes per element. int saving = numElements * 2 - STRING_SPLIT_OVERHEAD; if (saving <= 0) { return n; } String[] strings = new String[n.getChildCount()]; int idx = 0; for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { strings[idx++] = cur.getString(); } // These delimiters are chars that appears a lot in the program therefore // probably have a small Huffman encoding. String delimiter = pickDelimiter(strings); if (delimiter != null) { String template = Joiner.on(delimiter).join(strings); Node call = IR.call( IR.getprop( IR.string(template), IR.string("split")), IR.string("" + delimiter)); call.copyInformationFromForTree(n); n.getParent().replaceChild(n, call); reportCodeChange(); return call; } return n; } /** * Find a delimiter that does not occur in the given strings * @param strings The strings that must be separated. * @return a delimiter string or null */ private String pickDelimiter(String[] strings) { boolean allLength1 = true; for (String s : strings) { if (s.length() != 1) { allLength1 = false; break; } } if (allLength1) { return ""; } String[] delimiters = new String[]{" ", ";", ",", "{", "}", null}; int i = 0; NEXT_DELIMITER: for (;delimiters[i] != null; i++) { for (String cur : strings) { if (cur.contains(delimiters[i])) { continue NEXT_DELIMITER; } } break; } return delimiters[i]; } private static final Pattern REGEXP_FLAGS_RE = Pattern.compile("^[gmi]*$"); /** * are the given flags valid regular expression flags? * JavaScript recognizes several suffix flags for regular expressions, * 'g' - global replace, 'i' - case insensitive, 'm' - multi-line. * They are case insensitive, and JavaScript does not recognize the extended * syntax mode, single-line mode, or expression replacement mode from Perl 5. */ private static boolean areValidRegexpFlags(String flags) { return REGEXP_FLAGS_RE.matcher(flags).matches(); } /** * are the given flags safe to fold? * We don't fold the regular expression if global ('g') flag is on, * because in this case it isn't really a constant: its 'lastIndex' * property contains the state of last execution, so replacing * 'new RegExp('foobar','g')' with '/foobar/g' may change the behavior of * the program if the RegExp is used inside a loop, for example. *

* ECMAScript 5 explicitly disallows pooling of regular expression literals so * in ECMAScript 5, {@code /foo/g} and {@code new RegExp('foo', 'g')} are * equivalent. * From section 7.8.5: * "Then each time the literal is evaluated, a new object is created as if by * the expression new RegExp(Pattern, Flags) where RegExp is the standard * built-in constructor with that name." */ private boolean areSafeFlagsToFold(String flags) { return isEcmaScript5OrGreater() || flags.indexOf('g') < 0; } /** * returns a string node that can safely be rendered inside /brackets/. */ private static Node makeForwardSlashBracketSafe(Node n) { String s = n.getString(); // sb contains everything in s[0:pos] StringBuilder sb = null; int pos = 0; boolean isEscaped = false, inCharset = false; for (int i = 0; i < s.length(); ++i) { char ch = s.charAt(i); switch (ch) { case '\\': isEscaped = !isEscaped; continue; case '/': // Escape a literal forward slash if it is not already escaped and is // not inside a character set. // new RegExp('/') -> /\// // but the following do not need extra escaping // new RegExp('\\/') -> /\// // new RegExp('[/]') -> /[/]/ if (!isEscaped && !inCharset) { if (null == sb) { sb = new StringBuilder(s.length() + 16); } sb.append(s, pos, i).append('\\'); pos = i; } break; case '[': if (!isEscaped) { inCharset = true; } break; case ']': if (!isEscaped) { inCharset = false; } break; case '\r': case '\n': case '\u2028': case '\u2029': // LineTerminators cannot appear raw inside a regular // expression literal. // They can't appear legally in a quoted string, but when // the quoted string from // new RegExp('\n') // reaches here, the quoting has been removed. // Requote just these code-points. if (null == sb) { sb = new StringBuilder(s.length() + 16); } if (isEscaped) { sb.append(s, pos, i - 1); } else { sb.append(s, pos, i); } switch (ch) { case '\r': sb.append("\\r"); break; case '\n': sb.append("\\n"); break; case '\u2028': sb.append("\\u2028"); break; case '\u2029': sb.append("\\u2029"); break; } pos = i + 1; break; } isEscaped = false; } if (null == sb) { return n.cloneTree(); } sb.append(s, pos, s.length()); return IR.string(sb.toString()).srcref(n); } /** * true if the JavaScript string would contain a Unicode escape when written * out as the body of a regular expression literal. */ static boolean containsUnicodeEscape(String s) { String esc = REGEXP_ESCAPER.regexpEscape(s); for (int i = -1; (i = esc.indexOf("\\u", i + 1)) >= 0;) { int nSlashes = 0; while (i - nSlashes > 0 && '\\' == esc.charAt(i - nSlashes - 1)) { ++nSlashes; } // if there are an even number of slashes before the \ u then it is a // Unicode literal. if (0 == (nSlashes & 1)) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TransformAMDToCJSModule.java0000644000175000017500000002436312115204405030015 0ustar apoapo/* * Copyright 2011 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 java.util.Iterator; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Iterators; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; /** * Rewrites an AMD module https://github.com/amdjs/amdjs-api/wiki/AMD to a * CommonJS module. See {@link ProcessCommonJSModules} for follow up processing * step. */ class TransformAMDToCJSModule implements CompilerPass { @VisibleForTesting final static DiagnosticType UNSUPPORTED_DEFINE_SIGNATURE_ERROR = DiagnosticType.error( "UNSUPPORTED_DEFINE_SIGNATURE", "Only define(function() ...), define(OBJECT_LITERAL) and define(" + "['dep', 'dep1'], function(d0, d2, [exports, module]) ...) forms " + "are currently supported."); final static DiagnosticType NON_TOP_LEVEL_STATEMENT_DEFINE_ERROR = DiagnosticType.error( "NON_TOP_LEVEL_STATEMENT_DEFINE", "The define function must be called as a top-level statement."); final static DiagnosticType REQUIREJS_PLUGINS_NOT_SUPPORTED_WARNING = DiagnosticType.warning( "REQUIREJS_PLUGINS_NOT_SUPPORTED", "Plugins in define requirements are not supported: {0}"); final static String VAR_RENAME_SUFFIX = "__alias"; private final AbstractCompiler compiler; private int renameIndex = 0; TransformAMDToCJSModule(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new TransformAMDModulesCallback()); } private void unsupportedDefineError(NodeTraversal t, Node n) { t.report(n, UNSUPPORTED_DEFINE_SIGNATURE_ERROR); } /** * The modules "exports", "require" and "module" are virtual in terms of * existing implicitly in CommonJS. */ private boolean isVirtualModuleName(String moduleName) { return "exports".equals(moduleName) || "require".equals(moduleName) || "module".equals(moduleName); } /** * Rewrites calls to define which has to be in void context just below the * current script node. */ private class TransformAMDModulesCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && n.getFirstChild() != null && n.getFirstChild().isName() && "define".equals(n.getFirstChild().getString())) { Scope.Var define = t.getScope().getVar(n.getFirstChild(). getString()); if (define != null && !define.isGlobal()) { // Ignore non-global define. return; } if (!(parent.isExprResult() && parent.getParent().isScript())) { t.report(n, NON_TOP_LEVEL_STATEMENT_DEFINE_ERROR); return; } Node script = parent.getParent(); Node requiresNode = null; Node callback = null; int defineArity = n.getChildCount() - 1; if (defineArity == 0) { unsupportedDefineError(t, n); return; } else if (defineArity == 1) { callback = n.getChildAtIndex(1); if (callback.isObjectLit()) { handleDefineObjectLiteral(parent, callback, script); return; } } else if (defineArity == 2) { requiresNode = n.getChildAtIndex(1); callback = n.getChildAtIndex(2); } else if (defineArity >= 3) { unsupportedDefineError(t, n); return; } if (!callback.isFunction() || (requiresNode != null && !requiresNode.isArrayLit())) { unsupportedDefineError(t, n); return; } handleRequiresAndParamList(t, n, script, requiresNode, callback); Node callbackBlock = callback.getChildAtIndex(2); NodeTraversal.traverse(compiler, callbackBlock, new DefineCallbackReturnCallback()); moveCallbackContentToTopLevel(parent, script, callbackBlock); compiler.reportCodeChange(); } } /** * When define is called with an object literal, assign it to exports and * we're done. */ private void handleDefineObjectLiteral(Node parent, Node onlyExport, Node script) { onlyExport.getParent().removeChild(onlyExport); script.replaceChild(parent, IR.exprResult(IR.assign(IR.name("exports"), onlyExport)) .copyInformationFromForTree(onlyExport)); compiler.reportCodeChange(); } /** * Rewrites the required modules to * var nameInParamList = require("nameFromRequireList"); */ private void handleRequiresAndParamList(NodeTraversal t, Node defineNode, Node script, Node requiresNode, Node callback) { Iterator paramList = callback.getChildAtIndex(1).children(). iterator(); Iterator requires = requiresNode != null ? requiresNode.children().iterator() : Iterators.emptyIterator(); while (paramList.hasNext() || requires.hasNext()) { Node aliasNode = paramList.hasNext() ? paramList.next() : null; Node modNode = requires.hasNext() ? requires.next() : null; handleRequire(t, defineNode, script, callback, aliasNode, modNode); } } /** * Rewrite a single require call. */ private void handleRequire(NodeTraversal t, Node defineNode, Node script, Node callback, Node aliasNode, Node modNode) { String moduleName = null; if (modNode != null) { moduleName = handlePlugins(t, script, modNode.getString(), modNode); } if (isVirtualModuleName(moduleName)) { return; } String aliasName = aliasNode != null ? aliasNode.getString() : null; Scope globalScope = t.getScope(); if (aliasName != null && globalScope.isDeclared(aliasName, true)) { while (true) { String renamed = aliasName + VAR_RENAME_SUFFIX + renameIndex; if (!globalScope.isDeclared(renamed, true)) { NodeTraversal.traverse(compiler, callback, new RenameCallback(aliasName, renamed)); aliasName = renamed; break; } renameIndex++; } } Node requireNode; if (moduleName != null) { Node call = IR.call(IR.name("require"), IR.string(moduleName)); call.putBooleanProp(Node.FREE_CALL, true); if (aliasName != null) { requireNode = IR.var(IR.name(aliasName), call) .copyInformationFromForTree(aliasNode); } else { requireNode = IR.exprResult(call). copyInformationFromForTree(modNode); } } else { // ignore exports, require and module (because they are implicit // in CommonJS); if (isVirtualModuleName(aliasName)) { return; } requireNode = IR.var(IR.name(aliasName), IR.nullNode()) .copyInformationFromForTree(aliasNode); } script.addChildBefore(requireNode, defineNode.getParent()); } /** * Require.js supports a range of plugins that are hard to support * statically. Generally none are supported right now with the * exception of a simple hack to support condition loading. This * was added to make compilation of Dojo work better but will * probably break, so just don't use them :) */ private String handlePlugins(NodeTraversal t, Node script, String moduleName, Node modNode) { if (moduleName.contains("!")) { t.report(modNode, REQUIREJS_PLUGINS_NOT_SUPPORTED_WARNING, moduleName); int condition = moduleName.indexOf('?'); if (condition > 0) { if (moduleName.contains(":")) { return null; } return handlePlugins(t, script, moduleName.substring(condition + 1), modNode); } moduleName = null; } return moduleName; } /** * Moves the statements in the callback to be direct children of the * current script. */ private void moveCallbackContentToTopLevel(Node defineParent, Node script, Node callbackBlock) { int curIndex = script.getIndexOfChild(defineParent); script.removeChild(defineParent); callbackBlock.getParent().removeChild(callbackBlock); Node before = script.getChildAtIndex(curIndex); if (before != null) { script.addChildBefore(callbackBlock, before); } script.addChildToBack(callbackBlock); NodeUtil.tryMergeBlock(callbackBlock); } } /** * Rewrites the return statement of the callback to be an assignment to * module.exports. */ private class DefineCallbackReturnCallback extends NodeTraversal.AbstractShallowStatementCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isReturn() && n.hasChildren()) { Node retVal = n.getFirstChild(); n.removeChild(retVal); parent.replaceChild(n, IR.exprResult( IR.assign( IR.getprop(IR.name("module"), IR.string("exports")), retVal)) .useSourceInfoFromForTree(n)); } } } /** * Renames names; */ private class RenameCallback extends AbstractPostOrderCallback { private final String from; private final String to; public RenameCallback(String from, String to) { this.from = from; this.to = to; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() && from.equals(n.getString())) { n.setString(to); n.putProp(Node.ORIGINALNAME_PROP, from); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SimpleRegion.java0000644000175000017500000000242612115204405026100 0ustar apoapo/* * Copyright 2007 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; /** * Simple region. */ public class SimpleRegion implements Region { private final int beginningLineNumber; private final int endingLineNumber; private final String source; public SimpleRegion(int beginningLineNumber, int endingLineNumber, String source) { this.beginningLineNumber = beginningLineNumber; this.endingLineNumber = endingLineNumber; this.source = source; } @Override public int getBeginningLineNumber() { return beginningLineNumber; } @Override public int getEndingLineNumber() { return endingLineNumber; } @Override public String getSourceExcerpt() { return source; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypedScopeCreator.java0000644000175000017500000022657512115204405027117 0ustar apoapo/* * Copyright 2004 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.javascript.jscomp.TypeCheck.ENUM_NOT_CONSTANT; import static com.google.javascript.jscomp.TypeCheck.MULTIPLE_VAR_DEF; import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.DATE_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.EVAL_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.FUNCTION_INSTANCE_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.GLOBAL_THIS; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.RANGE_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_OBJECT_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.TYPE_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.URI_ERROR_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multiset; import com.google.javascript.jscomp.CodingConvention.DelegateRelationship; import com.google.javascript.jscomp.CodingConvention.ObjectLiteralCast; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.CodingConvention.SubclassType; import com.google.javascript.jscomp.FunctionTypeBuilder.AstFunctionContents; import com.google.javascript.jscomp.NodeTraversal.AbstractScopedCallback; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowStatementCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.FunctionParamBuilder; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.Property; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** * Creates the symbol table of variables available in the current scope and * their types. * * Scopes created by this class are very different from scopes created * by the syntactic scope creator. These scopes have type information, and * include some qualified names in addition to variables * (like Class.staticMethod). * * When building scope information, also declares relevant information * about types in the type registry. * * @author nicksantos@google.com (Nick Santos) */ final class TypedScopeCreator implements ScopeCreator { /** * A suffix for naming delegate proxies differently from their base. */ static final String DELEGATE_PROXY_SUFFIX = ObjectType.createDelegateSuffix("Proxy"); static final DiagnosticType MALFORMED_TYPEDEF = DiagnosticType.warning( "JSC_MALFORMED_TYPEDEF", "Typedef for {0} does not have any type information"); static final DiagnosticType ENUM_INITIALIZER = DiagnosticType.warning( "JSC_ENUM_INITIALIZER_NOT_ENUM", "enum initializer must be an object literal or an enum"); static final DiagnosticType CTOR_INITIALIZER = DiagnosticType.warning( "JSC_CTOR_INITIALIZER_NOT_CTOR", "Constructor {0} must be initialized at declaration"); static final DiagnosticType IFACE_INITIALIZER = DiagnosticType.warning( "JSC_IFACE_INITIALIZER_NOT_IFACE", "Interface {0} must be initialized at declaration"); static final DiagnosticType CONSTRUCTOR_EXPECTED = DiagnosticType.warning( "JSC_REFLECT_CONSTRUCTOR_EXPECTED", "Constructor expected as first argument"); static final DiagnosticType UNKNOWN_LENDS = DiagnosticType.warning( "JSC_UNKNOWN_LENDS", "Variable {0} not declared before @lends annotation."); static final DiagnosticType LENDS_ON_NON_OBJECT = DiagnosticType.warning( "JSC_LENDS_ON_NON_OBJECT", "May only lend properties to object types. {0} has type {1}."); private final AbstractCompiler compiler; private final ErrorReporter typeParsingErrorReporter; private final TypeValidator validator; private final CodingConvention codingConvention; private final JSTypeRegistry typeRegistry; private final List delegateProxyPrototypes = Lists.newArrayList(); private final Map delegateCallingConventions = Maps.newHashMap(); // Simple properties inferred about functions. private final Map functionAnalysisResults = Maps.newHashMap(); // For convenience private final ObjectType unknownType; /** * Defer attachment of types to nodes until all type names * have been resolved. Then, we can resolve the type and attach it. */ private class DeferredSetType { final Node node; final JSType type; DeferredSetType(Node node, JSType type) { Preconditions.checkNotNull(node); Preconditions.checkNotNull(type); this.node = node; this.type = type; // Other parts of this pass may read off the node. // (like when we set the LHS of an assign with a typed RHS function.) node.setJSType(type); } void resolve(Scope scope) { node.setJSType(type.resolve(typeParsingErrorReporter, scope)); } } TypedScopeCreator(AbstractCompiler compiler) { this(compiler, compiler.getCodingConvention()); } TypedScopeCreator(AbstractCompiler compiler, CodingConvention codingConvention) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.codingConvention = codingConvention; this.typeRegistry = compiler.getTypeRegistry(); this.typeParsingErrorReporter = typeRegistry.getErrorReporter(); this.unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); } /** * Creates a scope with all types declared. Declares newly discovered types * and type properties in the type registry. */ @Override public Scope createScope(Node root, Scope parent) { // Constructing the global scope is very different than constructing // inner scopes, because only global scopes can contain named classes that // show up in the type registry. Scope newScope = null; AbstractScopeBuilder scopeBuilder = null; if (parent == null) { JSType globalThis = typeRegistry.getNativeObjectType(JSTypeNative.GLOBAL_THIS); // Mark the main root, the externs root, and the src root // with the global this type. root.setJSType(globalThis); root.getFirstChild().setJSType(globalThis); root.getLastChild().setJSType(globalThis); // Run a first-order analysis over the syntax tree. (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults)) .process(root.getFirstChild(), root.getLastChild()); // Find all the classes in the global scope. newScope = createInitialScope(root); GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); scopeBuilder = globalScopeBuilder; NodeTraversal.traverse(compiler, root, scopeBuilder); } else { newScope = new Scope(parent, root); LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); scopeBuilder = localScopeBuilder; localScopeBuilder.build(); } scopeBuilder.resolveStubDeclarations(); scopeBuilder.resolveTypes(); // Gather the properties in each function that we found in the // global scope, if that function has a @this type that we can // build properties on. for (Node functionNode : scopeBuilder.nonExternFunctions) { JSType type = functionNode.getJSType(); if (type != null && type.isFunctionType()) { FunctionType fnType = type.toMaybeFunctionType(); JSType fnThisType = fnType.getTypeOfThis(); if (!fnThisType.isUnknownType()) { NodeTraversal.traverse(compiler, functionNode.getLastChild(), scopeBuilder.new CollectProperties(fnThisType)); } } } if (parent == null) { codingConvention.defineDelegateProxyPrototypeProperties( typeRegistry, newScope, delegateProxyPrototypes, delegateCallingConventions); } return newScope; } /** * Patches a given global scope by removing variables previously declared in * a script and re-traversing a new version of that script. * * @param globalScope The global scope generated by {@code createScope}. * @param scriptRoot The script that is modified. */ void patchGlobalScope(Scope globalScope, Node scriptRoot) { // Preconditions: This is supposed to be called only on (named) SCRIPT nodes // and a global typed scope should have been generated already. Preconditions.checkState(scriptRoot.isScript()); Preconditions.checkNotNull(globalScope); Preconditions.checkState(globalScope.isGlobal()); String scriptName = NodeUtil.getSourceName(scriptRoot); Preconditions.checkNotNull(scriptName); for (Node node : ImmutableList.copyOf(functionAnalysisResults.keySet())) { if (scriptName.equals(NodeUtil.getSourceName(node))) { functionAnalysisResults.remove(node); } } (new FirstOrderFunctionAnalyzer( compiler, functionAnalysisResults)).process(null, scriptRoot); // TODO(bashir): Variable declaration is not the only side effect of last // global scope generation but here we only wipe that part off! // Remove all variables that were previously declared in this scripts. // First find all vars to remove then remove them because of iterator! Iterator varIter = globalScope.getVars(); List varsToRemove = Lists.newArrayList(); while (varIter.hasNext()) { Var oldVar = varIter.next(); if (scriptName.equals(oldVar.getInputName())) { varsToRemove.add(oldVar); } } for (Var var : varsToRemove) { globalScope.undeclare(var); globalScope.getTypeOfThis().toObjectType().removeProperty(var.getName()); } // Now re-traverse the given script. GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope); NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder); } /** * Create the outermost scope. This scope contains native binding such as * {@code Object}, {@code Date}, etc. */ @VisibleForTesting Scope createInitialScope(Node root) { NodeTraversal.traverse( compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); Scope s = Scope.createGlobalScope(root); declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, DATE_FUNCTION_TYPE); declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // There is no longer a need to special case ActiveXObject // but this remains here until we can get the extern forks // cleaned up. declareNativeValueType(s, "ActiveXObject", FUNCTION_INSTANCE_TYPE); return s; } private void declareNativeFunctionType(Scope scope, JSTypeNative tId) { FunctionType t = typeRegistry.getNativeFunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, NodeUtil.getBestJSDocInfo(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.isAssign()) { identifyNameNode( firstChild.getFirstChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(nativeType); } private abstract class AbstractScopeBuilder implements NodeTraversal.Callback { /** * The scope that we're building. */ final Scope scope; private final List deferredSetTypes = Lists.newArrayList(); /** * Functions that we found in the global scope and not in externs. */ private final List nonExternFunctions = Lists.newArrayList(); /** * Object literals with a @lends annotation aren't analyzed until we * reach the root of the statement they're defined in. * * This ensures that if there are any @lends annotations on the object * literals, the type on the @lends annotation resolves correctly. * * For more information, see * http://code.google.com/p/closure-compiler/issues/detail?id=314 */ private List lentObjectLiterals = null; /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List stubDeclarations = Lists.newArrayList(); /** * The current source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n); break; case Token.VAR: defineVar(n); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); } break; } // Analyze any @lends object literals in this statement. if (n.getParent() != null && NodeUtil.isStatement(n) && lentObjectLiterals != null) { for (Node objLit : lentObjectLiterals) { defineObjectLiteral(objLit); } lentObjectLiterals.clear(); } } private void attachLiteralTypes(Node n) { switch (n.getType()) { case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: n.setJSType(getNativeType(STRING_TYPE)); break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.getLendsName() != null) { if (lentObjectLiterals == null) { lentObjectLiterals = Lists.newArrayList(); } lentObjectLiterals.add(n); } else { defineObjectLiteral(n); } break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else { type = lendsVar.getType(); if (type == null) { type = unknownType; } if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { compiler.report( JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT, lendsName, type.toString())); type = null; } else { objectLit.setJSType(type); } } } info = NodeUtil.getBestJSDocInfo(objectLit); Node lValue = NodeUtil.getBestLValue(objectLit); String lValueName = NodeUtil.getBestLValueName(lValue); boolean createdEnumType = false; if (info != null && info.hasEnumParameterType()) { type = createEnumTypeFromNodes(objectLit, lValueName, info, lValue); createdEnumType = true; } if (type == null) { type = typeRegistry.createAnonymousObjectType(info); } setDeferredType(objectLit, type); // If this is an enum, the properties were already taken care of above. processObjectLitProperties( objectLit, ObjectType.cast(objectLit.getJSType()), !createdEnumType); } /** * Process an object literal and all the types on it. * @param objLit The OBJECTLIT node. * @param objLitType The type of the OBJECTLIT node. This might be a named * type, because of the lends annotation. * @param declareOnOwner If true, declare properties on the objLitType as * well. If false, the caller should take care of this. */ void processObjectLitProperties( Node objLit, ObjectType objLitType, boolean declareOnOwner) { for (Node keyNode = objLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { Node value = keyNode.getFirstChild(); String memberName = NodeUtil.getObjectLitKeyName(keyNode); JSDocInfo info = keyNode.getJSDocInfo(); JSType valueType = getDeclaredType(info, keyNode, value); JSType keyType = objLitType.isEnumType() ? objLitType.toMaybeEnumType().getElementsType() : NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType); // Try to declare this property in the current scope if it // has an authoritative name. String qualifiedName = NodeUtil.getBestLValueName(keyNode); if (qualifiedName != null) { boolean inferred = keyType == null; defineSlot(keyNode, objLit, qualifiedName, keyType, inferred); } else if (keyType != null) { setDeferredType(keyNode, keyType); } if (keyType != null && objLitType != null && declareOnOwner) { // Declare this property on its object literal. objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(Node node, JSDocInfo info) { JSType jsType = null; if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's OK to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, getDeclaredType( catchName.getJSDocInfo(), catchName, null)); } /** * Defines a VAR initialization. */ void defineVar(Node n) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node name : n.children()) { defineName(name, n, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n) { assertDefinitionNode(n, Token.FUNCTION); // Determine the name and JSDocInfo and l-value for the function. // Any of these may be null. Node lValue = NodeUtil.getBestLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); String functionName = NodeUtil.getBestLValueName(lValue); FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? unknownType : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue)) { return false; } return scope.isGlobal() || !type.isReturnTypeInferred(); } /** * Creates a new function type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - A function literal that needs a type attached to it. * - An assignment expression with function-type info in the JsDoc. * * All parameters are optional, and we will do the best we can to create * a function type. * * This function will always create a function type, so only call it if * you're sure that's what you want. * * @param rValue The function node. * @param name the function's name * @param info the {@link JSDocInfo} attached to the function definition * @param lvalueNode The node where this function is being * assigned. For example, {@code A.prototype.foo = ...} would be used to * determine that this function is a method of A.prototype. May be * null to indicate that this is not being assigned to a qualified name. */ private FunctionType createFunctionTypeFromNodes( @Nullable Node rValue, @Nullable String name, @Nullable JSDocInfo info, @Nullable Node lvalueNode) { FunctionType functionType = null; // Global ctor aliases should be registered with the type registry. if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() != null && var.getType().isFunctionType()) { FunctionType aliasedType = var.getType().toMaybeFunctionType(); if ((aliasedType.isConstructor() || aliasedType.isInterface()) && !aliasedType.isNativeObjectType()) { functionType = aliasedType; if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, functionType.getInstanceType()); } } } } if (functionType == null) { Node errorRoot = rValue == null ? lvalueNode : rValue; boolean isFnLiteral = rValue != null && rValue.isFunction(); Node fnRoot = isFnLiteral ? rValue : null; Node parametersNode = isFnLiteral ? rValue.getFirstChild().getNext() : null; if (info != null && info.hasType()) { JSType type = info.getType().evaluate(scope, typeRegistry); // Known to be not null since we have the FUNCTION token there. type = type.restrictByNotNullOrUndefined(); if (type.isFunctionType()) { functionType = type.toMaybeFunctionType(); functionType.setJSDocInfo(info); } } if (functionType == null) { // Find the type of any overridden function. Node ownerNode = NodeUtil.getBestLValueOwner(lvalueNode); String ownerName = NodeUtil.getBestLValueName(ownerNode); Var ownerVar = null; String propName = null; ObjectType ownerType = null; if (ownerName != null) { ownerVar = scope.getVar(ownerName); if (ownerVar != null) { ownerType = ObjectType.cast(ownerVar.getType()); } if (name != null) { propName = name.substring(ownerName.length() + 1); } } FunctionType overriddenType = null; if (ownerType != null && propName != null) { overriddenType = findOverriddenFunction(ownerType, propName); } FunctionTypeBuilder builder = new FunctionTypeBuilder(name, compiler, errorRoot, sourceName, scope) .setContents(getFunctionAnalysisResults(fnRoot)) .inferFromOverriddenFunction(overriddenType, parametersNode) .inferTemplateTypeName(info) .inferReturnType(info) .inferInheritance(info); // Infer the context type. boolean searchedForThisType = false; if (ownerType != null && ownerType.isFunctionPrototypeType() && ownerType.getOwnerFunction().hasInstanceType()) { builder.inferThisType( info, ownerType.getOwnerFunction().getInstanceType()); searchedForThisType = true; } else if (ownerNode != null && ownerNode.isThis()) { // If 'this' has a type, use that instead. // This is a hack, necessary because CollectProperties (below) // doesn't run with the scope that it's building, // so scope.getTypeOfThis() will be wrong. JSType injectedThisType = ownerNode.getJSType(); builder.inferThisType( info, injectedThisType == null ? scope.getTypeOfThis() : injectedThisType); searchedForThisType = true; } if (!searchedForThisType) { builder.inferThisType(info); } functionType = builder .inferParameterTypes(parametersNode, info) .buildAndRegister(); } } // all done return functionType; } /** * Find the function that's being overridden on this type, if any. */ private FunctionType findOverriddenFunction( ObjectType ownerType, String propName) { // First, check to see if the property is implemented // on a superclass. JSType propType = ownerType.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } else { // If it's not, then check to see if it's implemented // on an implemented interface. for (ObjectType iface : ownerType.getCtorImplementedInterfaces()) { propType = iface.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } } } return null; } /** * Creates a new enum type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the JsDoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lValueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null && rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations of NAMEs and qualified names. // Object literal keys will have to compute their names themselves. if (n.isName()) { Preconditions.checkArgument( parent.isFunction() || parent.isVar() || parent.isParamList() || parent.isCatch()); } else { Preconditions.checkArgument( n.isGetProp() && (parent.isAssign() || parent.isExprResult())); } defineSlot(n, parent, n.getQualifiedName(), type, inferred); } /** * Defines a symbol in the current scope. * * @param n the defining NAME or GETPROP or object literal key node. * @param parent the {@code n}'s parent. * @param variableName The name that this should be known by. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. * @param inferred Whether the type is inferred or declared. */ void defineSlot(Node n, Node parent, String variableName, JSType type, boolean inferred) { Preconditions.checkArgument(!variableName.isEmpty()); boolean isGlobalVar = n.isName() && scope.isGlobal(); boolean shouldDeclareOnGlobalThis = isGlobalVar && (parent.isVar() || parent.isFunction()); // If n is a property, then we should really declare it in the // scope where the root object appears. This helps out people // who declare "global" names in an anonymous namespace. Scope scopeToDeclareIn = scope; if (n.isGetProp() && !scope.isGlobal() && isQnameRootedInGlobalScope(n)) { Scope globalScope = scope.getGlobalScope(); // don't try to declare in the global scope if there's // already a symbol there with this name. if (!globalScope.isDeclared(variableName, false)) { scopeToDeclareIn = scope.getGlobalScope(); } } // The input may be null if we are working with a AST snippet. So read // the extern info from the node. Var newVar = null; // declared in closest scope? CompilerInput input = compiler.getInput(inputId); if (scopeToDeclareIn.isDeclared(variableName, false)) { Var oldVar = scopeToDeclareIn.getVar(variableName); newVar = validator.expectUndeclaredVariable( sourceName, input, n, parent, oldVar, variableName, type); } else { if (type != null) { setDeferredType(n, type); } newVar = scopeToDeclareIn.declare(variableName, n, type, input, inferred); if (type instanceof EnumType) { Node initialValue = newVar.getInitialValue(); boolean isValidValue = initialValue != null && (initialValue.isObjectLit() || initialValue.isQualifiedName()); if (!isValidValue) { compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); } } } // We need to do some additional work for constructors and interfaces. FunctionType fnType = JSType.toMaybeFunctionType(type); if (fnType != null && // We don't want to look at empty function types. !type.isEmptyType()) { // We want to make sure that when we declare a new instance type // (with @constructor) that there's actually a ctor for it. // This doesn't apply to structural constructors (like // function(new:Array). Checking the constructed type against // the variable name is a sufficient check for this. if ((fnType.isConstructor() || fnType.isInterface()) && variableName.equals(fnType.getReferenceName())) { finishConstructorDefinition(n, variableName, fnType, scopeToDeclareIn, input, newVar); } } if (shouldDeclareOnGlobalThis) { ObjectType globalThis = typeRegistry.getNativeObjectType(GLOBAL_THIS); if (inferred) { globalThis.defineInferredProperty(variableName, type == null ? getNativeType(JSTypeNative.NO_TYPE) : type, n); } else { globalThis.defineDeclaredProperty(variableName, type, n); } } if (isGlobalVar && "Window".equals(variableName) && type != null && type.isFunctionType() && type.isConstructor()) { FunctionType globalThisCtor = typeRegistry.getNativeObjectType(GLOBAL_THIS).getConstructor(); globalThisCtor.getInstanceType().clearCachedValues(); globalThisCtor.getPrototype().clearCachedValues(); globalThisCtor .setPrototypeBasedOn((type.toMaybeFunctionType()).getInstanceType()); } } private void finishConstructorDefinition( Node n, String variableName, FunctionType fnType, Scope scopeToDeclareIn, CompilerInput input, Var newVar) { // Declare var.prototype in the scope chain. FunctionType superClassCtor = fnType.getSuperClassConstructor(); Property prototypeSlot = fnType.getSlot("prototype"); // When we declare the function prototype implicitly, we // want to make sure that the function and its prototype // are declared at the same node. We also want to make sure // that the if a symbol has both a Var and a JSType, they have // the same node. // // This consistency is helpful to users of SymbolTable, // because everything gets declared at the same place. prototypeSlot.setNode(n); String prototypeName = variableName + ".prototype"; // There are some rare cases where the prototype will already // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. // Fortunately, other warnings will complain if this happens. Var prototypeVar = scopeToDeclareIn.getVar(prototypeName); if (prototypeVar != null && prototypeVar.scope == scopeToDeclareIn) { scopeToDeclareIn.undeclare(prototypeVar); } scopeToDeclareIn.declare(prototypeName, n, prototypeSlot.getType(), input, /* declared iff there's an explicit supertype */ superClassCtor == null || superClassCtor.getInstanceType().isEquivalentTo( getNativeType(OBJECT_TYPE))); // Make sure the variable is initialized to something if // it constructs itself. if (newVar.getInitialValue() == null && !n.isFromExterns()) { compiler.report( JSError.make(sourceName, n, fnType.isConstructor() ? CTOR_INITIALIZER : IFACE_INITIALIZER, variableName)); } } /** * Check if the given node is a property of a name in the global scope. */ private boolean isQnameRootedInGlobalScope(Node n) { Scope scope = getQnameRootScope(n); return scope != null && scope.isGlobal(); } /** * Return the scope for the name of the given node. */ private Scope getQnameRootScope(Node n) { Node root = NodeUtil.getRootOfQualifiedName(n); if (root.isName()) { Var var = scope.getVar(root.getString()); if (var != null) { return var.getScope(); } } return null; } /** * Look for a type declaration on a property assignment * (in an ASSIGN or an object literal key). * @param info The doc info for this property. * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(JSDocInfo info, Node lValue, @Nullable Node rValue) { if (info != null && info.hasType()) { return getDeclaredTypeInAnnotation(lValue, info); } else if (rValue != null && rValue.isFunction() && shouldUseFunctionLiteralType( JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) { return rValue.getJSType(); } else if (info != null) { if (info.hasEnumParameterType()) { if (rValue != null && rValue.isObjectLit()) { return rValue.getJSType(); } else { return createEnumTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } else if (info.isConstructor() || info.isInterface()) { return createFunctionTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } // Check if this is constant, and if it has a known type. if (isConstantSymbol(info, lValue)) { if (rValue != null) { JSDocInfo rValueInfo = rValue.getJSDocInfo(); if (rValueInfo != null && rValueInfo.hasType()) { // If rValue has a type-cast, we use the type in the type-cast. return rValueInfo.getType().evaluate(scope, typeRegistry); } else if (rValue.getJSType() != null && !rValue.getJSType().isUnknownType()) { // If rValue's type was already computed during scope creation, // then we can safely use that. return rValue.getJSType(); } else if (rValue.isOr()) { // Check for a very specific JS idiom: // var x = x || TYPE; // This is used by Closure's base namespace for esoteric // reasons. Node firstClause = rValue.getFirstChild(); Node secondClause = firstClause.getNext(); boolean namesMatch = firstClause.isName() && lValue.isName() && firstClause.getString().equals(lValue.getString()); if (namesMatch && secondClause.getJSType() != null && !secondClause.getJSType().isUnknownType()) { return secondClause.getJSType(); } } } } return getDeclaredTypeInAnnotation(lValue, info); } private FunctionType getFunctionType(@Nullable Var v) { JSType t = v == null ? null : v.getType(); ObjectType o = t == null ? null : t.dereference(); return JSType.toMaybeFunctionType(o); } /** * Look for calls that set a delegate method's calling convention. */ private void checkForCallingConventionDefiningCalls( Node n, Map delegateCallingConventions) { codingConvention.checkForCallingConventionDefiningCalls(n, delegateCallingConventions); } /** * Look for class-defining calls. * Because JS has no 'native' syntax for defining classes, * this is often very coding-convention dependent and business-logic heavy. */ private void checkForClassDefiningCalls(NodeTraversal t, Node n) { SubclassRelationship relationship = codingConvention.getClassesDefinedByCall(n); if (relationship != null) { FunctionType superCtor = getFunctionType( scope.getVar(relationship.superclassName)); FunctionType subCtor = getFunctionType( scope.getVar(relationship.subclassName)); if (superCtor != null && superCtor.isConstructor() && subCtor != null && subCtor.isConstructor()) { ObjectType superClass = superCtor.getInstanceType(); ObjectType subClass = subCtor.getInstanceType(); // superCtor and subCtor might be structural constructors // (like {function(new:Object)}) so we need to resolve them back // to the original ctor objects. superCtor = superClass.getConstructor(); subCtor = subClass.getConstructor(); if (relationship.type == SubclassType.INHERITS && !superClass.isEmptyType() && !subClass.isEmptyType()) { validator.expectSuperType(t, n, superClass, subClass); } if (superCtor != null && subCtor != null) { codingConvention.applySubclassRelationship( superCtor, subCtor, relationship.type); } } } String singletonGetterClassName = codingConvention.getSingletonGetterClassName(n); if (singletonGetterClassName != null) { ObjectType objectType = ObjectType.cast( typeRegistry.getType(singletonGetterClassName)); if (objectType != null) { FunctionType functionType = objectType.getConstructor(); if (functionType != null) { FunctionType getterType = typeRegistry.createFunctionType(objectType); codingConvention.applySingletonGetter(functionType, getterType, objectType); } } } DelegateRelationship delegateRelationship = codingConvention.getDelegateRelationship(n); if (delegateRelationship != null) { applyDelegateRelationship(delegateRelationship); } ObjectLiteralCast objectLiteralCast = codingConvention.getObjectLiteralCast(n); if (objectLiteralCast != null) { if (objectLiteralCast.diagnosticType == null) { ObjectType type = ObjectType.cast( typeRegistry.getType(objectLiteralCast.typeName)); if (type != null && type.getConstructor() != null) { setDeferredType(objectLiteralCast.objectNode, type); } else { compiler.report(JSError.make(t.getSourceName(), n, CONSTRUCTOR_EXPECTED)); } } else { compiler.report(JSError.make(t.getSourceName(), n, objectLiteralCast.diagnosticType)); } } } /** * Apply special properties that only apply to delegates. */ private void applyDelegateRelationship( DelegateRelationship delegateRelationship) { ObjectType delegatorObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegator)); ObjectType delegateBaseObject = ObjectType.cast( typeRegistry.getType(delegateRelationship.delegateBase)); ObjectType delegateSuperObject = ObjectType.cast( typeRegistry.getType(codingConvention.getDelegateSuperclassName())); if (delegatorObject != null && delegateBaseObject != null && delegateSuperObject != null) { FunctionType delegatorCtor = delegatorObject.getConstructor(); FunctionType delegateBaseCtor = delegateBaseObject.getConstructor(); FunctionType delegateSuperCtor = delegateSuperObject.getConstructor(); if (delegatorCtor != null && delegateBaseCtor != null && delegateSuperCtor != null) { FunctionParamBuilder functionParamBuilder = new FunctionParamBuilder(typeRegistry); functionParamBuilder.addRequiredParams( getNativeType(U2U_CONSTRUCTOR_TYPE)); FunctionType findDelegate = typeRegistry.createFunctionType( typeRegistry.createDefaultObjectUnion(delegateBaseObject), functionParamBuilder.build()); FunctionType delegateProxy = typeRegistry.createConstructorType( delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX, null, null, null, null); delegateProxy.setPrototypeBasedOn(delegateBaseObject); codingConvention.applyDelegateRelationship( delegateSuperObject, delegateBaseObject, delegatorObject, delegateProxy, findDelegate); delegateProxyPrototypes.add(delegateProxy.getPrototype()); } } } /** * Declare the symbol for a qualified name in the global scope. * * @param info The doc info for this property. * @param n A top-level GETPROP node (it should not be contained inside * another GETPROP). * @param parent The parent of {@code n}. * @param rhsValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { Node ownerNode = n.getFirstChild(); String ownerName = ownerNode.getQualifiedName(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); Preconditions.checkArgument(qName != null && ownerName != null); // Precedence of type information on GETPROPs: // 1) @type annotation / @enum annotation // 2) ASSIGN to FUNCTION literal // 3) @param/@return annotation (with no function literal) // 4) ASSIGN to something marked @const // 5) ASSIGN to anything else // // 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff // the function has JsDoc or has not been declared before. // // FUNCTION literals are special because TypedScopeCreator is very smart // about getting as much type information as possible for them. // Determining type for #1 + #2 + #3 + #4 JSType valueType = getDeclaredType(info, n, rhsValue); if (valueType == null && rhsValue != null) { // Determining type for #5 valueType = rhsValue.getJSType(); } // Function prototypes are special. // It's a common JS idiom to do: // F.prototype = { ... }; // So if F does not have an explicitly declared super type, // allow F.prototype to be redefined arbitrarily. if ("prototype".equals(propName)) { Var qVar = scope.getVar(qName); if (qVar != null) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to an object literal, // then they are responsible for making sure that the object literal's // implicit prototype is set up appropriately. We just obey // the @extends tag. ObjectType qVarType = ObjectType.cast(qVar.getType()); if (qVarType != null && rhsValue != null && rhsValue.isObjectLit()) { typeRegistry.resetImplicitPrototype( rhsValue.getJSType(), qVarType.getImplicitPrototype()); } else if (!qVar.isTypeInferred()) { // If the programmer has declared that F inherits from Super, // and they assign F.prototype to some arbitrary expression, // there's not much we can do. We just ignore the expression, // and hope they've annotated their code in a way to tell us // what props are going to be on that prototype. return; } if (qVar.getScope() == scope) { scope.undeclare(qVar); } } } if (valueType == null) { if (parent.isExprResult()) { stubDeclarations.add(new StubDeclaration( n, t.getInput() != null && t.getInput().isExtern(), ownerName)); } return; } boolean inferred = isQualifiedNameInferred( qName, n, info, rhsValue, valueType); if (!inferred) { ObjectType ownerType = getObjectSlot(ownerName); if (ownerType != null) { // Only declare this as an official property if it has not been // declared yet. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); if ((!ownerType.hasOwnProperty(propName) || ownerType.isPropertyTypeInferred(propName)) && ((isExtern && !ownerType.isNativeObjectType()) || !ownerType.isInstanceType())) { // If the property is undeclared or inferred, declare it now. ownerType.defineDeclaredProperty(propName, valueType, n); } } // If the property is already declared, the error will be // caught when we try to declare it in the current scope. defineSlot(n, parent, valueType, inferred); } else if (rhsValue != null && rhsValue.isTrue()) { // We declare these for delegate proxy method properties. FunctionType ownerType = JSType.toMaybeFunctionType(getObjectSlot(ownerName)); if (ownerType != null) { JSType ownerTypeOfThis = ownerType.getTypeOfThis(); String delegateName = codingConvention.getDelegateSuperclassName(); JSType delegateType = delegateName == null ? null : typeRegistry.getType(delegateName); if (delegateType != null && ownerTypeOfThis.isSubtype(delegateType)) { defineSlot(n, parent, getNativeType(BOOLEAN_TYPE), true); } } } } /** * Determines whether a qualified name is inferred. * NOTE(nicksantos): Determining whether a property is declared or not * is really really obnoxious. * * The problem is that there are two (equally valid) coding styles: * * (function() { * /* The authoritative definition of goog.bar. / * goog.bar = function() {}; * })(); * * function f() { * goog.bar(); * /* Reset goog.bar to a no-op. / * goog.bar = function() {}; * } * * In a dynamic language with first-class functions, it's very difficult * to know which one the user intended without looking at lots of * contextual information (the second example demonstrates a small case * of this, but there are some really pathological cases as well). * * The current algorithm checks if either the declaration has * JsDoc type information, or @const with a known type, * or a function literal with a name we haven't seen before. */ private boolean isQualifiedNameInferred( String qName, Node n, JSDocInfo info, Node rhsValue, JSType valueType) { if (valueType == null) { return true; } boolean inferred = true; if (info != null) { inferred = !(info.hasType() || info.hasEnumParameterType() || (isConstantSymbol(info, n) && valueType != null && !valueType.isUnknownType()) || FunctionTypeBuilder.isFunctionTypeDeclaration(info)); } if (inferred && rhsValue != null && rhsValue.isFunction()) { if (info != null) { return false; } else if (!scope.isDeclared(qName, false) && n.isUnscopedQualifiedName()) { // Check if this is in a conditional block. // Functions assigned in conditional blocks are inferred. for (Node current = n.getParent(); !(current.isScript() || current.isFunction()); current = current.getParent()) { if (NodeUtil.isControlStructure(current)) { return true; } } // Check if this is assigned in an inner scope. // Functions assigned in inner scopes are inferred. AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents == null || !contents.getEscapedQualifiedNames().contains(qName)) { return false; } } } return inferred; } private boolean isConstantSymbol(JSDocInfo info, Node node) { if (info != null && info.isConstant()) { return true; } switch (node.getType()) { case Token.NAME: return NodeUtil.isConstantByConvention( compiler.getCodingConvention(), node, node.getParent()); case Token.GETPROP: return node.isQualifiedName() && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), node.getLastChild(), node); } return false; } /** * Find the ObjectType associated with the given slot. * @param slotName The name of the slot to find the type in. * @return An object type, or null if this slot does not contain an object. */ private ObjectType getObjectSlot(String slotName) { Var ownerVar = scope.getVar(slotName); if (ownerVar != null) { JSType ownerVarType = ownerVar.getType(); return ObjectType.cast(ownerVarType == null ? null : ownerVarType.restrictByNotNullOrUndefined()); } return null; } /** * Resolve any stub declarations to unknown types if we could not * find types for them during traversal. */ void resolveStubDeclarations() { for (StubDeclaration stub : stubDeclarations) { Node n = stub.node; Node parent = n.getParent(); String qName = n.getQualifiedName(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final JSType thisType; CollectProperties(JSType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(child.getFirstChild(), child, child.getLastChild()); break; case Token.GETPROP: maybeCollectMember(child, child, null); break; } } } private void maybeCollectMember(Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || !member.isGetProp() || !member.getFirstChild().isThis()) { return; } member.getFirstChild().setJSType(thisType); JSType jsType = getDeclaredType(info, member, value); Node name = member.getLastChild(); if (jsType != null && (name.isName() || name.isString()) && thisType.toObjectType() != null) { thisType.toObjectType().defineDeclaredProperty( name.getString(), jsType, member); } } } // end CollectProperties } /** * A stub declaration without any type information. */ private static final class StubDeclaration { private final Node node; private final boolean isExtern; private final String ownerName; private StubDeclaration(Node node, boolean isExtern, String ownerName) { this.node = node; this.isExtern = isExtern; this.ownerName = ownerName; } } /** * A shallow traversal of the global scope to build up all classes, * functions, and methods. */ private final class GlobalScopeBuilder extends AbstractScopeBuilder { private GlobalScopeBuilder(Scope scope) { super(scope); } /** * Visit a node in the global scope, and add anything it declares to the * global symbol table. * * @param t The current traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); switch (n.getType()) { case Token.VAR: // Handle typedefs. if (n.hasOneChild()) { checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo()); } break; } } @Override void maybeDeclareQualifiedName( NodeTraversal t, JSDocInfo info, Node n, Node parent, Node rhsValue) { checkForTypedef(t, n, info); super.maybeDeclareQualifiedName(t, info, n, parent, rhsValue); } /** * Handle typedefs. * @param t The current traversal. * @param candidate A qualified name node. * @param info JSDoc comments. */ private void checkForTypedef( NodeTraversal t, Node candidate, JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recursive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, unknownType); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're building. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } for (Multiset.Entry entry : contents.getAssignedNameCounts().entrySet()) { Var v = scope.getVar(entry.getElement()); Preconditions.checkState(v.getScope() == scope); if (entry.getCount() == 1) { v.markAssignedExactlyOnce(); } } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions. Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) { Node astParameters = functionNode.getFirstChild().getNext(); Node iifeArgumentNode = null; if (NodeUtil.isCallOrNewTarget(functionNode)) { iifeArgumentNode = functionNode.getNext(); } FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { JSType paramType = jsDocParameter == null ? unknownType : jsDocParameter.getJSType(); boolean inferred = paramType == null || paramType == unknownType; if (iifeArgumentNode != null && inferred) { String argumentName = iifeArgumentNode.getQualifiedName(); Var argumentVar = argumentName == null || scope.getParent() == null ? null : scope.getParent().getVar(argumentName); if (argumentVar != null && !argumentVar.isTypeInferred()) { paramType = argumentVar.getType(); } } if (paramType == null) { paramType = unknownType; } defineSlot(astParameter, functionNode, paramType, inferred); if (jsDocParameter != null) { jsDocParameter = jsDocParameter.getNext(); } if (iifeArgumentNode != null) { iifeArgumentNode = iifeArgumentNode.getNext(); } } } } } // end declareArguments } // end LocalScopeBuilder /** * Does a first-order function analysis that just looks at simple things * like what variables are escaped, and whether 'this' is used. */ private static class FirstOrderFunctionAnalyzer extends AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; private final Map data; FirstOrderFunctionAnalyzer( AbstractCompiler compiler, Map outParam) { this.compiler = compiler; this.data = outParam; } @Override public void process(Node externs, Node root) { if (externs == null) { NodeTraversal.traverse(compiler, root, this); } else { NodeTraversal.traverseRoots( compiler, ImmutableList.of(externs, root), this); } } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); data.put(n, new AstFunctionContents(n)); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { return; } if (n.isReturn() && n.getFirstChild() != null) { data.get(t.getScopeRoot()).recordNonEmptyReturn(); } if (t.getScopeDepth() <= 1) { // The first-order function analyzer looks at two types of variables: // // 1) Local variables that are assigned in inner scopes ("escaped vars") // // 2) Local variables that are assigned more than once. // // We treat all global variables as escaped by default, so there's // no reason to do this extra computation for them. return; } if (n.isName() && NodeUtil.isLValue(n) && // Be careful of bleeding functions, which create variables // in the inner scope, not the scope where the name appears. !NodeUtil.isBleedingFunctionName(n)) { String name = n.getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordAssignedName(name); } if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordEscapedVarName(name); } } } else if (n.isGetProp() && n.isUnscopedQualifiedName() && NodeUtil.isLValue(n)) { String name = NodeUtil.getRootOfQualifiedName(n).getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()) .recordEscapedQualifiedName(n.getQualifiedName()); } } } } } private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) { if (n == null) { return null; } // Sometimes this will return null in things like // NameReferenceGraphConstruction that build partial scopes. return functionAnalysisResults.get(n); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckSideEffects.java0000644000175000017500000001453312115204405026627 0ustar apoapo/* * Copyright 2006 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.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfoBuilder; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; /** * Checks for non side effecting statements such as *

 * var s = "this string is "
 *         "continued on the next line but you forgot the +";
 * x == foo();  // should that be '='?
 * foo();;  // probably just a stray-semicolon. Doesn't hurt to check though
 * 

* and generates warnings. * */ final class CheckSideEffects extends AbstractPostOrderCallback implements HotSwapCompilerPass { static final DiagnosticType USELESS_CODE_ERROR = DiagnosticType.warning( "JSC_USELESS_CODE", "Suspicious code. {0}"); static final String PROTECTOR_FN = "JSCOMPILER_PRESERVE"; private final CheckLevel level; private final List problemNodes = Lists.newArrayList(); private final AbstractCompiler compiler; private final boolean protectSideEffectFreeCode; CheckSideEffects(AbstractCompiler compiler, CheckLevel level, boolean protectSideEffectFreeCode) { this.compiler = compiler; this.level = level; this.protectSideEffectFreeCode = protectSideEffectFreeCode; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); // Code with hidden side-effect code is common, for example // accessing "el.offsetWidth" forces a reflow in browsers, to allow this // will still allowing local dead code removal in general, // protect the "side-effect free" code in the source. // if (protectSideEffectFreeCode) { protectSideEffects(); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // VOID nodes appear when there are extra semicolons at the BLOCK level. // I've been unable to think of any cases where this indicates a bug, // and apparently some people like keeping these semicolons around, // so we'll allow it. if (n.isEmpty() || n.isComma()) { return; } if (parent == null) { return; } // Do not try to remove a block or an expr result. We already handle // these cases when we visit the child, and the peephole passes will // fix up the tree in more clever ways when these are removed. if (n.isExprResult() || n.isBlock()) { return; } // This no-op statement was there so that JSDoc information could // be attached to the name. This check should not complain about it. if (n.isQualifiedName() && n.getJSDocInfo() != null) { return; } boolean isResultUsed = NodeUtil.isExpressionResultUsed(n); boolean isSimpleOp = NodeUtil.isSimpleOperator(n); if (!isResultUsed && (isSimpleOp || !NodeUtil.mayHaveSideEffects(n, t.getCompiler()))) { String msg = "This code lacks side-effects. Is there a bug?"; if (n.isString()) { msg = "Is there a missing '+' on the previous line?"; } else if (isSimpleOp) { msg = "The result of the '" + Token.name(n.getType()).toLowerCase() + "' operator is not being used."; } t.getCompiler().report( t.makeError(n, level, USELESS_CODE_ERROR, msg)); // TODO(johnlenz): determine if it is necessary to // try to protect side-effect free statements as well. if (!NodeUtil.isStatement(n)) { problemNodes.add(n); } } } /** * Protect side-effect free nodes by making them parameters * to a extern function call. This call will be removed * after all the optimizations passes have run. */ private void protectSideEffects() { if (!problemNodes.isEmpty()) { addExtern(); for (Node n : problemNodes) { Node name = IR.name(PROTECTOR_FN).srcref(n); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node replacement = IR.call(name).srcref(n); replacement.putBooleanProp(Node.FREE_CALL, true); n.getParent().replaceChild(n, replacement); replacement.addChildToBack(n); } compiler.reportCodeChange(); } } private void addExtern() { Node name = IR.name(PROTECTOR_FN); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node var = IR.var(name); // Add "@noalias" so we can strip the method when AliasExternals is enabled. JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordNoAlias(); var.setJSDocInfo(builder.build(var)); CompilerInput input = compiler.getSynthesizedExternsInput(); input.getAstRoot(compiler).addChildrenToBack(var); compiler.reportCodeChange(); } /** * Remove side-effect sync functions. */ static class StripProtection extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; StripProtection(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { Node target = n.getFirstChild(); // TODO(johnlenz): add this to the coding convention // so we can remove goog.reflect.sinkValue as well. if (target.isName() && target.getString().equals(PROTECTOR_FN)) { Node expr = n.getLastChild(); n.detachChildren(); parent.replaceChild(n, expr); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypeValidator.java0000644000175000017500000007402512115204405026276 0ustar apoapo/* * 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 static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_STRING; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.UnknownType; import java.text.MessageFormat; import java.util.Iterator; import java.util.List; /** * A central reporter for all type violations: places where the programmer * has annotated a variable (or property) with one type, but has assigned * another type to it. * * Also doubles as a central repository for all type violations, so that * type-based optimizations (like AmbiguateProperties) can be fault-tolerant. * * @author nicksantos@google.com (Nick Santos) */ class TypeValidator { private final AbstractCompiler compiler; private final JSTypeRegistry typeRegistry; private final JSType allValueTypes; private boolean shouldReport = true; private final JSType nullOrUndefined; // TODO(nicksantos): Provide accessors to better filter the list of type // mismatches. For example, if we pass (Cake|null) where only Cake is // allowed, that doesn't mean we should invalidate all Cakes. private final List mismatches = Lists.newArrayList(); // User warnings private static final String FOUND_REQUIRED = "{0}\n" + "found : {1}\n" + "required: {2}"; // TODO(johnlenz): reenable this after after the next release. static final DiagnosticType INVALID_CAST = DiagnosticType.disabled("JSC_INVALID_CAST", "invalid cast - must be a subtype or supertype\n" + "from: {0}\n" + "to : {1}"); static final DiagnosticType TYPE_MISMATCH_WARNING = DiagnosticType.warning( "JSC_TYPE_MISMATCH", "{0}"); static final DiagnosticType MISSING_EXTENDS_TAG_WARNING = DiagnosticType.warning( "JSC_MISSING_EXTENDS_TAG", "Missing @extends tag on type {0}"); static final DiagnosticType DUP_VAR_DECLARATION = DiagnosticType.warning("JSC_DUP_VAR_DECLARATION", "variable {0} redefined with type {1}, " + "original definition at {2}:{3} with type {4}"); static final DiagnosticType HIDDEN_PROPERTY_MISMATCH = DiagnosticType.warning("JSC_HIDDEN_PROPERTY_MISMATCH", "mismatch of the {0} property type and the type " + "of the property it overrides from superclass {1}\n" + "original: {2}\n" + "override: {3}"); static final DiagnosticType INTERFACE_METHOD_NOT_IMPLEMENTED = DiagnosticType.warning( "JSC_INTERFACE_METHOD_NOT_IMPLEMENTED", "property {0} on interface {1} is not implemented by type {2}"); static final DiagnosticType HIDDEN_INTERFACE_PROPERTY_MISMATCH = DiagnosticType.warning( "JSC_HIDDEN_INTERFACE_PROPERTY_MISMATCH", "mismatch of the {0} property type and the type " + "of the property it overrides from interface {1}\n" + "original: {2}\n" + "override: {3}"); static final DiagnosticType UNKNOWN_TYPEOF_VALUE = DiagnosticType.warning("JSC_UNKNOWN_TYPEOF_VALUE", "unknown type: {0}"); static final DiagnosticType ILLEGAL_PROPERTY_ACCESS = DiagnosticType.warning("JSC_ILLEGAL_PROPERTY_ACCESS", "Cannot do {0} access on a {1}"); static final DiagnosticGroup ALL_DIAGNOSTICS = new DiagnosticGroup( INVALID_CAST, TYPE_MISMATCH_WARNING, MISSING_EXTENDS_TAG_WARNING, DUP_VAR_DECLARATION, HIDDEN_PROPERTY_MISMATCH, INTERFACE_METHOD_NOT_IMPLEMENTED, HIDDEN_INTERFACE_PROPERTY_MISMATCH, UNKNOWN_TYPEOF_VALUE, ILLEGAL_PROPERTY_ACCESS); TypeValidator(AbstractCompiler compiler) { this.compiler = compiler; this.typeRegistry = compiler.getTypeRegistry(); this.allValueTypes = typeRegistry.createUnionType( STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE, NULL_TYPE, VOID_TYPE); this.nullOrUndefined = typeRegistry.createUnionType( NULL_TYPE, VOID_TYPE); } /** * Gets a list of type violations. * * For each violation, one element is the expected type and the other is * the type that is actually found. Order is not significant. */ Iterable getMismatches() { return mismatches; } void setShouldReport(boolean report) { this.shouldReport = report; } // All non-private methods should have the form: // expectCondition(NodeTraversal t, Node n, ...); // If there is a mismatch, the {@code expect} method should issue // a warning and attempt to correct the mismatch, when possible. void expectValidTypeofName(NodeTraversal t, Node n, String found) { report(JSError.make(t.getSourceName(), n, UNKNOWN_TYPEOF_VALUE, found)); } /** * Expect the type to be an object, or a type convertible to object. If the * expectation is not met, issue a warning at the provided node's source code * position. * @return True if there was no warning, false if there was a mismatch. */ boolean expectObject(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesObjectContext()) { mismatch(t, n, msg, type, OBJECT_TYPE); return false; } return true; } /** * Expect the type to be an object. Unlike expectObject, a type convertible * to object is not acceptable. */ void expectActualObject(NodeTraversal t, Node n, JSType type, String msg) { if (!type.isObject()) { mismatch(t, n, msg, type, OBJECT_TYPE); } } /** * Expect the type to contain an object sometimes. If the expectation is * not met, issue a warning at the provided node's source code position. */ void expectAnyObject(NodeTraversal t, Node n, JSType type, String msg) { JSType anyObjectType = getNativeType(NO_OBJECT_TYPE); if (!anyObjectType.isSubtype(type) && !type.isEmptyType()) { mismatch(t, n, msg, type, anyObjectType); } } /** * Expect the type to be a string, or a type convertible to string. If the * expectation is not met, issue a warning at the provided node's source code * position. */ void expectString(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesStringContext()) { mismatch(t, n, msg, type, STRING_TYPE); } } /** * Expect the type to be a number, or a type convertible to number. If the * expectation is not met, issue a warning at the provided node's source code * position. */ void expectNumber(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext()) { mismatch(t, n, msg, type, NUMBER_TYPE); } } /** * Expect the type to be a valid operand to a bitwise operator. This includes * numbers, any type convertible to a number, or any other primitive type * (undefined|null|boolean|string). */ void expectBitwiseable(NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext() && !type.isSubtype(allValueTypes)) { mismatch(t, n, msg, type, allValueTypes); } } /** * Expect the type to be a number or string, or a type convertible to a number * or string. If the expectation is not met, issue a warning at the provided * node's source code position. */ void expectStringOrNumber( NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext() && !type.matchesStringContext()) { mismatch(t, n, msg, type, NUMBER_STRING); } } /** * Expect the type to be anything but the null or void type. If the * expectation is not met, issue a warning at the provided node's * source code position. Note that a union type that includes the * void type and at least one other type meets the expectation. * @return Whether the expectation was met. */ boolean expectNotNullOrUndefined( NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) { if (!type.isNoType() && !type.isUnknownType() && type.isSubtype(nullOrUndefined) && !containsForwardDeclaredUnresolvedName(type)) { // There's one edge case right now that we don't handle well, and // that we don't want to warn about. // if (this.x == null) { // this.initializeX(); // this.x.foo(); // } // In this case, we incorrectly type x because of how we // infer properties locally. See issue 109. // http://code.google.com/p/closure-compiler/issues/detail?id=109 // // We do not do this inference globally. if (n.isGetProp() && !t.inGlobalScope() && type.isNullType()) { return true; } mismatch(t, n, msg, type, expectedType); return false; } return true; } private boolean containsForwardDeclaredUnresolvedName(JSType type) { if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { if (containsForwardDeclaredUnresolvedName(alt)) { return true; } } } return type.isNoResolvedType(); } /** * Expect that the type of a switch condition matches the type of its * case condition. */ void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType, JSType caseType) { // ECMA-262, page 68, step 3 of evaluation of CaseBlock, // but allowing extra autoboxing. // TODO(user): remove extra conditions when type annotations // in the code base have adapted to the change in the compiler. if (!switchType.canTestForShallowEqualityWith(caseType) && (caseType.autoboxesTo() == null || !caseType.autoboxesTo().isSubtype(switchType))) { mismatch(t, n.getFirstChild(), "case expression doesn't match switch", caseType, switchType); } } /** * Expect that the first type can be addressed with GETELEM syntax, * and that the second type is the right type for an index into the * first type. * * @param t The node traversal. * @param n The GETELEM node to issue warnings on. * @param objType The type of the left side of the GETELEM. * @param indexType The type inside the brackets of the GETELEM. */ void expectIndexMatch(NodeTraversal t, Node n, JSType objType, JSType indexType) { Preconditions.checkState(n.isGetElem()); Node indexNode = n.getLastChild(); if (objType.isStruct()) { report(JSError.make(t.getSourceName(), indexNode, ILLEGAL_PROPERTY_ACCESS, "'[]'", "struct")); } if (objType.isUnknownType()) { expectStringOrNumber(t, indexNode, indexType, "property access"); } else { ObjectType dereferenced = objType.dereference(); if (dereferenced != null && dereferenced .getTemplateTypeMap() .hasTemplateKey(typeRegistry.getObjectIndexKey())) { expectCanAssignTo(t, indexNode, indexType, dereferenced .getTemplateTypeMap().getTemplateType(typeRegistry.getObjectIndexKey()), "restricted index type"); } else if (dereferenced != null && dereferenced.isArrayType()) { expectNumber(t, indexNode, indexType, "array access"); } else if (objType.matchesObjectContext()) { expectString(t, indexNode, indexType, "property access"); } else { mismatch(t, n, "only arrays or objects can be accessed", objType, typeRegistry.createUnionType(ARRAY_TYPE, OBJECT_TYPE)); } } } /** * Expect that the first type can be assigned to a symbol of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param rightType The type on the RHS of the assign. * @param leftType The type of the symbol on the LHS of the assign. * @param owner The owner of the property being assigned to. * @param propName The name of the property being assigned to. * @return True if the types matched, false otherwise. */ boolean expectCanAssignToPropertyOf(NodeTraversal t, Node n, JSType rightType, JSType leftType, Node owner, String propName) { // The NoType check is a hack to make typedefs work OK. if (!leftType.isNoType() && !rightType.isSubtype(leftType)) { // Do not type-check interface methods, because we expect that // they will have dummy implementations that do not match the type // annotations. JSType ownerType = getJSType(owner); if (ownerType.isFunctionPrototypeType()) { FunctionType ownerFn = ownerType.toObjectType().getOwnerFunction(); if (ownerFn.isInterface() && rightType.isFunctionType() && leftType.isFunctionType()) { return true; } } mismatch(t, n, "assignment to property " + propName + " of " + getReadableJSTypeName(owner, true), rightType, leftType); return false; } return true; } /** * Expect that the first type can be assigned to a symbol of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param rightType The type on the RHS of the assign. * @param leftType The type of the symbol on the LHS of the assign. * @param msg An extra message for the mismatch warning, if necessary. * @return True if the types matched, false otherwise. */ boolean expectCanAssignTo(NodeTraversal t, Node n, JSType rightType, JSType leftType, String msg) { if (!rightType.isSubtype(leftType)) { mismatch(t, n, msg, rightType, leftType); return false; } return true; } /** * Expect that the type of an argument matches the type of the parameter * that it's fulfilling. * * @param t The node traversal. * @param n The node to issue warnings on. * @param argType The type of the argument. * @param paramType The type of the parameter. * @param callNode The call node, to help with the warning message. * @param ordinal The argument ordinal, to help with the warning message. */ void expectArgumentMatchesParameter(NodeTraversal t, Node n, JSType argType, JSType paramType, Node callNode, int ordinal) { if (!argType.isSubtype(paramType)) { mismatch(t, n, String.format("actual parameter %d of %s does not match " + "formal parameter", ordinal, getReadableJSTypeName(callNode.getFirstChild(), false)), argType, paramType); } } /** * Expect that the first type can override a property of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param overridingType The overriding type. * @param hiddenType The type of the property being overridden. * @param propertyName The name of the property, for use in the * warning message. * @param ownerType The type of the owner of the property, for use * in the warning message. */ void expectCanOverride(NodeTraversal t, Node n, JSType overridingType, JSType hiddenType, String propertyName, JSType ownerType) { if (!overridingType.isSubtype(hiddenType)) { registerMismatch(overridingType, hiddenType, report(t.makeError(n, HIDDEN_PROPERTY_MISMATCH, propertyName, ownerType.toString(), hiddenType.toString(), overridingType.toString()))); } } /** * Expect that the first type is the direct superclass of the second type. * * @param t The node traversal. * @param n The node where warnings should point to. * @param superObject The expected super instance type. * @param subObject The sub instance type. */ void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, ObjectType subObject) { FunctionType subCtor = subObject.getConstructor(); ObjectType implicitProto = subObject.getImplicitPrototype(); ObjectType declaredSuper = implicitProto == null ? null : implicitProto.getImplicitPrototype(); if (declaredSuper != null && !(superObject instanceof UnknownType) && !declaredSuper.isEquivalentTo(superObject)) { if (declaredSuper.isEquivalentTo(getNativeType(OBJECT_TYPE))) { registerMismatch(superObject, declaredSuper, report( t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString()))); } else { mismatch(t.getSourceName(), n, "mismatch in declaration of superclass type", superObject, declaredSuper); } // Correct the super type. if (!subCtor.hasCachedValues()) { subCtor.setPrototypeBasedOn(superObject); } } } /** * Expect that the first type can be cast to the second type. The first type * must have some relationship with the second. * * @param t The node traversal. * @param n The node where warnings should point. * @param type The type being cast from. * @param castType The type being cast to. */ void expectCanCast(NodeTraversal t, Node n, JSType castType, JSType type) { if (!type.canCastTo(castType)) { registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST, type.toString(), castType.toString()))); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. * @return The variable we end up with. Most of the time, this will just * be {@code var}, but in some rare cases we will need to declare * a new var with new source info. */ Var expectUndeclaredVariable(String sourceName, CompilerInput input, Node n, Node parent, Var var, String variableName, JSType newType) { Var newVar = var; boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.isEquivalentTo(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the property in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { StaticSlot propSlot = instance.getSlot(prop); if (propSlot == null) { // Not implemented String sourceName = n.getSourceFileName(); sourceName = sourceName == null ? "" : sourceName; registerMismatch(instance, implementedInterface, report(JSError.make(sourceName, n, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implementedInterface.toString(), instance.toString()))); } else { Node propNode = propSlot.getDeclaration() == null ? null : propSlot.getDeclaration().getNode(); // Fall back on the constructor node if we can't find a node for the // property. propNode = propNode == null ? n : propNode; JSType found = propSlot.getType(); JSType required = implementedInterface.getImplicitPrototype().getPropertyType(prop); found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (!found.isSubtype(required)) { // Implemented, but not correctly typed FunctionType constructor = implementedInterface.toObjectType().getConstructor(); registerMismatch(found, required, report(t.makeError(propNode, HIDDEN_INTERFACE_PROPERTY_MISMATCH, prop, constructor.getTopMostDefiningType(prop).toString(), required.toString(), found.toString()))); } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); } private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSTypeNative required) { mismatch(t, n, msg, found, getNativeType(required)); } private void mismatch(String sourceName, Node n, String msg, JSType found, JSType required) { registerMismatch(found, required, report( JSError.make(sourceName, n, TYPE_MISMATCH_WARNING, formatFoundRequired(msg, found, required)))); } private void registerMismatch(JSType found, JSType required, JSError error) { // Don't register a mismatch for differences in null or undefined or if the // code didn't downcast. found = found.restrictByNotNullOrUndefined(); required = required.restrictByNotNullOrUndefined(); if (found.isSubtype(required) || required.isSubtype(found)) { return; } mismatches.add(new TypeMismatch(found, required, error)); if (found.isFunctionType() && required.isFunctionType()) { FunctionType fnTypeA = found.toMaybeFunctionType(); FunctionType fnTypeB = required.toMaybeFunctionType(); Iterator paramItA = fnTypeA.getParameters().iterator(); Iterator paramItB = fnTypeB.getParameters().iterator(); while (paramItA.hasNext() && paramItB.hasNext()) { registerIfMismatch(paramItA.next().getJSType(), paramItB.next().getJSType(), error); } registerIfMismatch( fnTypeA.getReturnType(), fnTypeB.getReturnType(), error); } } private void registerIfMismatch( JSType found, JSType required, JSError error) { if (found != null && required != null && !found.isSubtype(required)) { registerMismatch(found, required, error); } } /** * Formats a found/required error message. */ private String formatFoundRequired(String description, JSType found, JSType required) { return MessageFormat.format(FOUND_REQUIRED, description, found, required); } /** * Given a node, get a human-readable name for the type of that node so * that will be easy for the programmer to find the original declaration. * * For example, if SubFoo's property "bar" might have the human-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.isGetProp()) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); if (objectType.getConstructor() != null && objectType.getConstructor().isInterface()) { objectType = FunctionType.getTopDefiningInterface( objectType, propName); } else { // classes while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type.isFunctionType()) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } private JSError report(JSError error) { if (shouldReport) { compiler.report(error); } return error; } /** * Signals that the first type and the second type have been * used interchangeably. * * Type-based optimizations should take this into account * so that they don't wreck code with type warnings. */ static class TypeMismatch { final JSType typeA; final JSType typeB; final JSError src; /** * It's the responsibility of the class that creates the * {@code TypeMismatch} to ensure that {@code a} and {@code b} are * non-matching types. */ TypeMismatch(JSType a, JSType b, JSError src) { this.typeA = a; this.typeB = b; this.src = src; } @Override public boolean equals(Object object) { if (object instanceof TypeMismatch) { TypeMismatch that = (TypeMismatch) object; return (that.typeA.isEquivalentTo(this.typeA) && that.typeB.isEquivalentTo(this.typeB)) || (that.typeB.isEquivalentTo(this.typeA) && that.typeA.isEquivalentTo(this.typeB)); } return false; } @Override public int hashCode() { return Objects.hashCode(typeA, typeB); } @Override public String toString() { return "(" + typeA + ", " + typeB + ")"; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DiagnosticGroup.java0000644000175000017500000000707612115204405026612 0ustar apoapo/* * 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.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.Serializable; import java.util.Arrays; import java.util.Map; import java.util.Set; /** * Group a set of related diagnostic types together, so that they can * be toggled on and off as one unit. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroup implements Serializable { private static final long serialVersionUID = 1; // The set of types represented by this group, hashed by key. private final Set types; // A human-readable name for the group. private final String name; /** * Create a group that matches all errors of the given types. */ DiagnosticGroup(String name, DiagnosticType ...types) { this.name = name; this.types = ImmutableSet.copyOf(Arrays.asList(types)); } /** * Create a group that matches all errors of the given types. */ public DiagnosticGroup(DiagnosticType ...types) { this(null, types); } /** * Create a diagnostic group with no name that only matches the given type. */ private DiagnosticGroup(DiagnosticType type) { this.name = null; this.types = ImmutableSet.of(type); } // DiagnosticGroups with only a single DiagnosticType. private static final Map singletons = Maps.newHashMap(); /** Create a diagnostic group that matches only the given type. */ public static DiagnosticGroup forType(DiagnosticType type) { if (!singletons.containsKey(type)) { singletons.put(type, new DiagnosticGroup(type)); } return singletons.get(type); } /** * Create a composite group. */ public DiagnosticGroup(DiagnosticGroup ...groups) { this(null, groups); } /** * Create a composite group. */ public DiagnosticGroup(String name, DiagnosticGroup ...groups) { Set set = Sets.newHashSet(); for (DiagnosticGroup group : groups) { set.addAll(group.types); } this.name = name; this.types = ImmutableSet.copyOf(set); } /** * Returns whether the given error's type matches a type * in this group. */ public boolean matches(JSError error) { return matches(error.getType()); } /** * Returns whether the given type matches a type in this group. */ public boolean matches(DiagnosticType type) { return types.contains(type); } /** * Returns whether all of the types in the given group are in this group. */ boolean isSubGroup(DiagnosticGroup group) { for (DiagnosticType type : group.types) { if (!matches(type)) { return false; } } return true; } /** * Returns an iterable over all the types in this group. */ public Iterable getTypes() { return types; } @Override public String toString() { return name == null ? super.toString() : "DiagnosticGroup<" + name + ">"; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ErrorHandler.java0000644000175000017500000000205212115204405026065 0ustar apoapo/* * Copyright 2012 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.CheckLevel; /** * The error handler is any generic sink for warnings and errors, * after they've passed through any filtering {@code WarningsGuard}s. * * @author nicksantos@google.com (Nick Santos) */ public interface ErrorHandler { /** * @param level the reporting level * @param error the error to report */ void report(CheckLevel level, JSError error); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/LoggerErrorManager.java0000644000175000017500000000443712115204405027233 0ustar apoapo/* * Copyright 2007 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.CheckLevel; import java.util.logging.Logger; import java.util.logging.Level; /** * An error manager that logs errors and warnings using a logger in addition to * collecting them in memory. Errors are logged at the SEVERE level and warnings * are logged at the WARNING level. * */ public class LoggerErrorManager extends BasicErrorManager { private final MessageFormatter formatter; private final Logger logger; /** * Creates an instance. */ public LoggerErrorManager(MessageFormatter formatter, Logger logger) { this.formatter = formatter; this.logger = logger; } /** * Creates an instance with a source-less error formatter. */ public LoggerErrorManager(Logger logger) { this(ErrorFormat.SOURCELESS.toFormatter(null, false), logger); } @Override public void println(CheckLevel level, JSError error) { switch (level) { case ERROR: logger.severe(error.format(level, formatter)); break; case WARNING: logger.warning(error.format(level, formatter)); break; case OFF: break; } } @Override protected void printSummary() { Level level = (getErrorCount() + getWarningCount() == 0) ? Level.INFO : Level.WARNING; if (getTypedPercent() > 0.0) { logger.log(level, "{0} error(s), {1} warning(s), {2,number,#.#}% typed", new Object[] {getErrorCount(), getWarningCount(), getTypedPercent()}); } else { if (getErrorCount() + getWarningCount() > 0) { logger.log(level, "{0} error(s), {1} warning(s)", new Object[] {getErrorCount(), getWarningCount()}); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NodeNameExtractor.java0000644000175000017500000000656312115204405027073 0ustar apoapo/* * Copyright 2004 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.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; /** * Utility class that extracts the qualified name out of a node. * Useful when trying to get a human-friendly string representation of * a property node that can be used to describe the node or name * related nodes based on it (as done by the NameAnonymousFunctions * compiler pass). * */ class NodeNameExtractor { private final char delimiter; private int nextUniqueInt = 0; NodeNameExtractor(char delimiter) { this.delimiter = delimiter; } /** * Returns a qualified name of the specified node. Dots and brackets * are changed to the delimiter passed in when constructing the * NodeNameExtractor object. We also replace ".prototype" with the * delimiter to keep names short, while still differentiating them * from static properties. (Prototype properties will end up * looking like "a$b$$c" if this.delimiter = '$'.) */ String getName(Node node) { switch (node.getType()) { case Token.FUNCTION: Node functionNameNode = node.getFirstChild(); return functionNameNode.getString(); case Token.GETPROP: Node lhsOfDot = node.getFirstChild(); Node rhsOfDot = lhsOfDot.getNext(); String lhsOfDotName = getName(lhsOfDot); String rhsOfDotName = getName(rhsOfDot); if ("prototype".equals(rhsOfDotName)) { return lhsOfDotName + delimiter; } else { return lhsOfDotName + delimiter + rhsOfDotName; } case Token.GETELEM: Node outsideBrackets = node.getFirstChild(); Node insideBrackets = outsideBrackets.getNext(); String nameOutsideBrackets = getName(outsideBrackets); String nameInsideBrackets = getName(insideBrackets); if ("prototype".equals(nameInsideBrackets)) { return nameOutsideBrackets + delimiter; } else { return nameOutsideBrackets + delimiter + nameInsideBrackets; } case Token.NAME: return node.getString(); case Token.STRING: case Token.STRING_KEY: return TokenStream.isJSIdentifier(node.getString()) ? node.getString() : ("__" + nextUniqueInt++); case Token.NUMBER: return NodeUtil.getStringValue(node); case Token.THIS: return "this"; case Token.CALL: return getName(node.getFirstChild()); default: StringBuilder sb = new StringBuilder(); for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { if (sb.length() > 0) { sb.append(delimiter); } sb.append(getName(child)); } return sb.toString(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckGlobalNames.java0000644000175000017500000002347412115204405026633 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.GlobalNamespace.Name; import com.google.javascript.jscomp.GlobalNamespace.Ref; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Set; /** * Checks references to undefined properties of global variables. * * @author nicksantos@google.com (Nick Santos) */ class CheckGlobalNames implements CompilerPass { private final AbstractCompiler compiler; private final CodingConvention convention; private final CheckLevel level; private GlobalNamespace namespace = null; private final Set objectPrototypeProps = Sets.newHashSet(); private final Set functionPrototypeProps = Sets.newHashSet(); // Warnings static final DiagnosticType UNDEFINED_NAME_WARNING = DiagnosticType.warning( "JSC_UNDEFINED_NAME", "{0} is never defined"); static final DiagnosticType NAME_DEFINED_LATE_WARNING = DiagnosticType.warning( "JSC_NAME_DEFINED_LATE", "{0} defined before its owner. {1} is defined at {2}:{3}"); static final DiagnosticType STRICT_MODULE_DEP_QNAME = DiagnosticType.disabled( "JSC_STRICT_MODULE_DEP_QNAME", "module {0} cannot reference {2}, defined in " + "module {1}"); /** * Creates a pass to check global name references at the given warning level. */ CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); this.level = level; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns this for easy chaining. */ CheckGlobalNames injectNamespace(GlobalNamespace namespace) { Preconditions.checkArgument(namespace.hasExternsRoot()); this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, externs, root); } // Find prototype properties that will affect our analysis. Preconditions.checkState(namespace.hasExternsRoot()); findPrototypeProps("Object", objectPrototypeProps); findPrototypeProps("Function", functionPrototypeProps); objectPrototypeProps.addAll( convention.getIndirectlyDeclaredProperties()); for (Name name : namespace.getNameForest()) { // Skip extern names. Externs are often not runnable as real code, // and will do things like: // var x; // x.method; // which this check forbids. if (name.inExterns) { continue; } checkDescendantNames(name, name.globalSets + name.localSets > 0); } } private void findPrototypeProps(String type, Set props) { Name slot = namespace.getSlot(type); if (slot != null) { for (Ref ref : slot.getRefs()) { if (ref.type == Ref.Type.PROTOTYPE_GET) { Node fullName = ref.getNode().getParent().getParent(); if (fullName.isGetProp()) { props.add(fullName.getLastChild().getString()); } } } } } /** * Checks to make sure all the descendants of a name are defined if they * are referenced. * * @param name A global name. * @param nameIsDefined If true, {@code name} is defined. Otherwise, it's * undefined, and any references to descendant names should emit warnings. */ private void checkDescendantNames(Name name, boolean nameIsDefined) { if (name.props != null) { for (Name prop : name.props) { // if the ancestor of a property is not defined, then we should emit // warnings for all references to the property. boolean propIsDefined = false; if (nameIsDefined) { // if the ancestor of a property is defined, then let's check that // the property is also explicitly defined if it needs to be. propIsDefined = (!propertyMustBeInitializedByFullName(prop) || prop.globalSets + prop.localSets > 0); } validateName(prop, propIsDefined); checkDescendantNames(prop, propIsDefined); } } } private void validateName(Name name, boolean isDefined) { // If the name is not defined, emit warnings for each reference. While // we're looking through each reference, check all the module dependencies. Ref declaration = name.getDeclaration(); Name parent = name.parent; JSModuleGraph moduleGraph = compiler.getModuleGraph(); for (Ref ref : name.getRefs()) { // Don't worry about global exprs. boolean isGlobalExpr = ref.getNode().getParent().isExprResult(); if (!isDefined && !isTypedef(ref)) { if (!isGlobalExpr) { reportRefToUndefinedName(name, ref); } } else if (declaration != null && ref.getModule() != declaration.getModule() && !moduleGraph.dependsOn( ref.getModule(), declaration.getModule())) { reportBadModuleReference(name, ref); } else { // Check for late references. if (ref.scope.isGlobal()) { // Prototype references are special, because in our reference graph, // A.prototype counts as a reference to A. boolean isPrototypeGet = (ref.type == Ref.Type.PROTOTYPE_GET); Name owner = isPrototypeGet ? name : parent; boolean singleGlobalParentDecl = owner != null && owner.getDeclaration() != null && owner.localSets == 0; if (singleGlobalParentDecl && owner.getDeclaration().preOrderIndex > ref.preOrderIndex) { String refName = isPrototypeGet ? name.getFullName() + ".prototype" : name.getFullName(); compiler.report( JSError.make(ref.source.getName(), ref.node, NAME_DEFINED_LATE_WARNING, refName, owner.getFullName(), owner.getDeclaration().source.getName(), String.valueOf(owner.getDeclaration().node.getLineno()))); } } } } } private boolean isTypedef(Ref ref) { // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.isExprResult()) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return true; } } return false; } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.source.getName(), ref.node, STRICT_MODULE_DEP_QNAME, ref.getModule().getName(), name.getDeclaration().getModule().getName(), name.getFullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } compiler.report( JSError.make(ref.getSourceName(), ref.node, level, UNDEFINED_NAME_WARNING, name.getFullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private boolean propertyMustBeInitializedByFullName(Name name) { // If an object or function literal in the global namespace is never // aliased, then its properties can only come from one of 2 places: // 1) From its prototype chain, or // 2) From an assignment to its fully qualified name. // If we assume #1 is not the case, then #2 implies that its // properties must all be modeled in the GlobalNamespace as well. // // We assume that for global object literals and types (constructors and // interfaces), we can find all the properties inherited from the prototype // chain of functions and objects. if (name.parent == null) { return false; } boolean parentIsAliased = false; if (name.parent.aliasingGets > 0) { for (Ref ref : name.parent.getRefs()) { if (ref.type == Ref.Type.ALIASING_GET) { Node aliaser = ref.getNode().getParent(); // We don't need to worry about known aliased, because // they're already covered by the getIndirectlyDeclaredProperties // call at the top. boolean isKnownAlias = aliaser.isCall() && (convention.getClassesDefinedByCall(aliaser) != null || convention.getSingletonGetterClassName(aliaser) != null); if (!isKnownAlias) { parentIsAliased = true; } } } } if (parentIsAliased) { return false; } if (objectPrototypeProps.contains(name.getBaseName())) { return false; } if (name.parent.type == Name.Type.OBJECTLIT) { return true; } if (name.parent.type == Name.Type.FUNCTION && name.parent.isDeclaredType() && !functionPrototypeProps.contains(name.getBaseName())) { return true; } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/0000755000175000017500000000000012115204405023567 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/DepsGenerator.java0000644000175000017500000003542212115204405027202 0ustar apoapo/* * 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.deps; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.JSError; import com.google.javascript.jscomp.SourceFile; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; /** * Generates deps.js files by scanning JavaScript files for * calls to goog.provide(), goog.require() and goog.addDependency(). * * @author agrieve@google.com (Andrew Grieve) */ public class DepsGenerator { public static enum InclusionStrategy { ALWAYS, WHEN_IN_SRCS, DO_NOT_DUPLICATE } private static Logger logger = Logger.getLogger(DepsGenerator.class.getName()); // See the Flags in MakeJsDeps for descriptions of these. private final Collection srcs; private final Collection deps; private final String closurePathAbs; private final InclusionStrategy mergeStrategy; final ErrorManager errorManager; static final DiagnosticType SAME_FILE_WARNING = DiagnosticType.warning( "DEPS_SAME_FILE", "Namespace \"{0}\" is both required and provided in the same file."); static final DiagnosticType NEVER_PROVIDED_ERROR = DiagnosticType.error( "DEPS_NEVER_PROVIDED", "Namespace \"{0}\" is required but never provided."); static final DiagnosticType DUPE_PROVIDES_WARNING = DiagnosticType.warning( "DEPS_DUPE_PROVIDES", "Multiple calls to goog.provide(\"{0}\")"); static final DiagnosticType MULTIPLE_PROVIDES_ERROR = DiagnosticType.error( "DEPS_DUPE_PROVIDES", "Namespace \"{0}\" is already provided in other file {1}"); static final DiagnosticType DUPE_REQUIRE_WARNING = DiagnosticType.warning( "DEPS_DUPE_REQUIRES", "Namespace \"{0}\" is required multiple times"); static final DiagnosticType NO_DEPS_WARNING = DiagnosticType.warning( "DEPS_NO_DEPS", "No dependencies found in file"); /** * Creates a new DepsGenerator. */ public DepsGenerator( Collection deps, Collection srcs, InclusionStrategy mergeStrategy, String closurePathAbs, ErrorManager errorManager) { this.deps = deps; this.srcs = srcs; this.mergeStrategy = mergeStrategy; this.closurePathAbs = closurePathAbs; this.errorManager = errorManager; } /** * Performs the parsing inputs and writing of outputs. * @throws IOException Occurs upon an IO error. * @return Returns a String of goog.addDependency calls that will build * the dependency graph. Returns null if there was an error. */ public String computeDependencyCalls() throws IOException { // Build a map of closure-relative path -> DepInfo. Map depsFiles = parseDepsFiles(); logger.fine("preparsedFiles: " + depsFiles); // Find all goog.provides & goog.requires in src files Map jsFiles = parseSources(depsFiles.keySet()); // Check if there were any parse errors. if (errorManager.getErrorCount() > 0) { return null; } cleanUpDuplicatedFiles(depsFiles, jsFiles); // Check for missing provides or other semantic inconsistencies. validateDependencies(depsFiles.values(), jsFiles.values()); if (errorManager.getErrorCount() > 0) { return null; } ByteArrayOutputStream output = new ByteArrayOutputStream(); writeDepsContent(depsFiles, jsFiles, new PrintStream(output)); return new String(output.toByteArray()); } /** * Removes duplicated depsInfo from jsFiles if this info already present in * some of the parsed deps.js * * @param depsFiles DepsInfo from deps.js dependencies * @param jsFiles DepsInfo from some of jsSources */ protected void cleanUpDuplicatedFiles(Map depsFiles, Map jsFiles) { Set depsPathsCopy = Sets.newHashSet(depsFiles.keySet()); for (String path : depsPathsCopy) { if (mergeStrategy != InclusionStrategy.WHEN_IN_SRCS) { jsFiles.remove(path); } } for (String path : jsFiles.keySet()) { // If a generated file appears in both the jsFiles and in depsFiles, then // remove it from depsFiles in order to get the full path the generated // file. depsFiles.remove(path); } } /** * Reports if there are any dependency problems with the given dependency * information. Reported problems include: * - A namespace being provided more than once * - A namespace being required multiple times from within one file * - A namespace being provided and required in the same file * - A namespace being required that is never provided * @param preparsedFileDepedencies Dependency information from existing * deps.js files. * @param parsedFileDependencies Dependency information from parsed .js files. */ private void validateDependencies(Iterable preparsedFileDepedencies, Iterable parsedFileDependencies) { // Create a map of namespace -> file providing it. // Also report any duplicate provides. Map providesMap = Maps.newHashMap(); addToProvideMap(preparsedFileDepedencies, providesMap); addToProvideMap(parsedFileDependencies, providesMap); // For each require in the parsed sources: for (DependencyInfo depInfo : parsedFileDependencies) { List requires = Lists.newArrayList(depInfo.getRequires()); for (int i = 0, l = requires.size(); i < l; ++i) { String namespace = requires.get(i); // Check for multiple requires. if (requires.subList(i + 1, l).contains(namespace)) { reportDuplicateRequire(namespace, depInfo); } // Check for missing provides. DependencyInfo provider = providesMap.get(namespace); if (provider == null) { reportUndefinedNamespace(namespace, depInfo); } else if (provider == depInfo) { reportSameFile(namespace, depInfo); } } } } private void reportSameFile(String namespace, DependencyInfo depInfo) { errorManager.report(CheckLevel.WARNING, JSError.make(depInfo.getName(), -1, -1, SAME_FILE_WARNING, namespace)); } private void reportUndefinedNamespace( String namespace, DependencyInfo depInfo) { errorManager.report(CheckLevel.ERROR, JSError.make(depInfo.getName(), -1, -1, NEVER_PROVIDED_ERROR, namespace)); } private void reportDuplicateProvide(String namespace, DependencyInfo firstDep, DependencyInfo secondDep) { if (firstDep == secondDep) { errorManager.report(CheckLevel.WARNING, JSError.make(firstDep.getName(), -1, -1, DUPE_PROVIDES_WARNING, namespace)); } else { errorManager.report(CheckLevel.ERROR, JSError.make(secondDep.getName(), -1, -1, MULTIPLE_PROVIDES_ERROR, namespace, firstDep.getName())); } } private void reportDuplicateRequire( String namespace, DependencyInfo depInfo) { errorManager.report(CheckLevel.WARNING, JSError.make(depInfo.getName(), -1, -1, DUPE_REQUIRE_WARNING, namespace)); } private void reportNoDepsInDepsFile(String filePath) { errorManager.report(CheckLevel.WARNING, JSError.make(filePath, -1, -1, NO_DEPS_WARNING)); } /** * Adds the given DependencyInfos to the given providesMap. Also checks for * and reports duplicate provides. */ private void addToProvideMap(Iterable depInfos, Map providesMap) { for (DependencyInfo depInfo : depInfos) { for (String provide : depInfo.getProvides()) { DependencyInfo prevValue = providesMap.put(provide, depInfo); // Check for duplicate provides. if (prevValue != null) { reportDuplicateProvide(provide, prevValue, depInfo); } } } } protected DepsFileParser createDepsFileParser() { DepsFileParser depsParser = new DepsFileParser(errorManager); depsParser.setShortcutMode(true); return depsParser; } /** * Returns whether we should ignore dependency info in the given deps file. */ protected boolean shouldSkipDepsFile(SourceFile file) { return false; } /** * Parses all deps.js files in the deps list and creates a map of * closure-relative path -> DependencyInfo. */ private Map parseDepsFiles() throws IOException { DepsFileParser depsParser = createDepsFileParser(); Map depsFiles = Maps.newHashMap(); for (SourceFile file : deps) { if (!shouldSkipDepsFile(file)) { List depInfos = depsParser.parseFileReader( file.getName(), file.getCodeReader()); if (depInfos.isEmpty()) { reportNoDepsInDepsFile(file.getName()); } else { for (DependencyInfo info : depInfos) { depsFiles.put(info.getPathRelativeToClosureBase(), info); } } } } // If a deps file also appears in srcs, our build tools will move it // into srcs. So we need to scan all the src files for addDependency // calls as well. for (SourceFile src : srcs) { if ((new File(src.getName())).exists() && !shouldSkipDepsFile(src)) { List srcInfos = depsParser.parseFileReader(src.getName(), src.getCodeReader()); for (DependencyInfo info : srcInfos) { depsFiles.put(info.getPathRelativeToClosureBase(), info); } } } return depsFiles; } /** * Parses all source files for dependency information. * @param preparsedFiles A set of closure-relative paths. * Files in this set are not parsed if they are encountered in srcs. * @return Returns a map of closure-relative paths -> DependencyInfo for the * newly parsed files. * @throws IOException Occurs upon an IO error. */ private Map parseSources( Set preparsedFiles) throws IOException { Map parsedFiles = Maps.newHashMap(); JsFileParser jsParser = new JsFileParser(errorManager); for (SourceFile file : srcs) { String closureRelativePath = PathUtil.makeRelative( closurePathAbs, PathUtil.makeAbsolute(file.getName())); logger.fine("Closure-relative path: " + closureRelativePath); if (InclusionStrategy.WHEN_IN_SRCS == mergeStrategy || !preparsedFiles.contains(closureRelativePath)) { DependencyInfo depInfo = jsParser.parseFile( file.getName(), closureRelativePath, file.getCode()); // Kick the source out of memory. file.clearCachedSource(); parsedFiles.put(closureRelativePath, depInfo); } } return parsedFiles; } /** * Creates the content to put into the output deps.js file. If mergeDeps is * true, then all of the dependency information in the providedDeps will be * included in the output. * @throws IOException Occurs upon an IO error. */ private void writeDepsContent(Map depsFiles, Map jsFiles, PrintStream out) throws IOException { // Print all dependencies extracted from srcs. writeDepInfos(out, jsFiles.values()); // Print all dependencies extracted from deps. if (mergeStrategy == InclusionStrategy.ALWAYS) { // This multimap is just for splitting DepsInfo objects by // it's definition deps.js file Multimap infosIndex = Multimaps.index( depsFiles.values(), new Function() { @Override public String apply(DependencyInfo from) { return from.getName(); } }); for (String depsPath : infosIndex.keySet()) { String path = formatPathToDepsFile(depsPath); out.println("\n// Included from: " + path); writeDepInfos(out, infosIndex.get(depsPath)); } } } /** * Format the deps file path so that it can be included in the output file. */ protected String formatPathToDepsFile(String path) { return path; } /** * Writes goog.addDependency() lines for each DependencyInfo in depInfos. * @throws IOException Occurs upon an IO error. */ private void writeDepInfos(PrintStream out, Collection depInfos ) throws IOException { // Print dependencies. // Lines look like this: // goog.addDependency('../../path/to/file.js', ['goog.Delay'], // ['goog.Disposable', 'goog.Timer']); for (DependencyInfo depInfo : depInfos) { Collection provides = depInfo.getProvides(); Collection requires = depInfo.getRequires(); out.print("goog.addDependency('" + depInfo.getPathRelativeToClosureBase() + "', "); writeJsArray(out, provides); out.print(", "); writeJsArray(out, requires); out.println(");"); } } /** * Prints a list of strings formatted as a JavaScript array of string * literals. */ private static void writeJsArray(PrintStream out, Collection values) { if (values.isEmpty()) { out.print("[]"); } else { out.print("['"); out.print(Joiner.on("', '").join(values)); out.print("']"); } } static List createSourceFilesFromPaths( Collection paths) { List files = Lists.newArrayList(); for (String path : paths) { files.add(SourceFile.fromFile(path)); } return files; } static List createSourceFilesFromPaths( String ... paths) { return createSourceFilesFromPaths(Arrays.asList(paths)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/JsFileLineParser.java0000644000175000017500000002132612115204405027577 0ustar apoapo/* * 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.deps; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.JSError; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Base class for classes that parse JavaScript sources on a line-by-line basis. Strips comments * from files and records all parsing errors. * * @author agrieve@google.com (Andrew Grieve) */ public abstract class JsFileLineParser { static final DiagnosticType PARSE_WARNING = DiagnosticType.warning( "DEPS_PARSE_WARNING", "{0}\n{1}"); static final DiagnosticType PARSE_ERROR = DiagnosticType.error( "DEPS_PARSE_ERROR", "{0}\n{1}"); boolean shortcutMode = false; /** * Thrown by base classes to signify a problem parsing a line. */ static class ParseException extends Exception { public static final long serialVersionUID = 1L; private boolean fatal; /** * Constructor. * * @param message A description of what caused the exception. * @param fatal Whether the exception is recoverable. */ public ParseException(String message, boolean fatal) { super(message); this.fatal = fatal; } public boolean isFatal() { return fatal; } } /** Pattern for matching JavaScript string literals. */ private static final Pattern STRING_LITERAL_PATTERN = Pattern.compile( "\\s*(?:'((?:\\\\'|[^'])*?)'|\"((?:\\\\\"|[^\"])*?)\")\\s*"); /** Matcher used in the parsing string literals. */ private Matcher valueMatcher = STRING_LITERAL_PATTERN.matcher(""); /** Path of the file currently being parsed. */ String filePath; /** The line number of the line currently being parsed. */ int lineNum; /** Handles error messages. */ ErrorManager errorManager; /** Did our parse succeed. */ boolean parseSucceeded; /** * Constructor. * * @param errorManager Parse error handler. */ public JsFileLineParser(ErrorManager errorManager) { this.errorManager = errorManager; } /** * In shortcut mode, the file line parser can stop reading early if * it thinks it found enough information. * * For example, many parsers assume that dependency information never * shows up after "real" code. */ public void setShortcutMode(boolean mode) { this.shortcutMode = mode; } public boolean didParseSucceed() { return parseSucceeded; } /** * Performs the line-by-line parsing of the given fileContents. This method * strips out JavaScript comments and then uses the abstract parseLine() * method to do the line parsing. * * @param filePath The path to the file being parsed. Used for reporting parse * exceptions. * @param fileContents A reader for the contents of the file. */ void doParse(String filePath, Reader fileContents) { this.filePath = filePath; parseSucceeded = true; BufferedReader lineBuffer = new BufferedReader(fileContents); // Parse all lines. String line = null; lineNum = 0; boolean inMultilineComment = false; try { while (null != (line = lineBuffer.readLine())) { ++lineNum; try { String revisedLine = line; if (inMultilineComment) { int endOfComment = revisedLine.indexOf("*/"); if (endOfComment != -1) { revisedLine = revisedLine.substring(endOfComment + 2); inMultilineComment = false; } else { revisedLine = ""; } } if (!inMultilineComment) { while (true) { int startOfLineComment = revisedLine.indexOf("//"); int startOfMultilineComment = revisedLine.indexOf("/*"); if (startOfLineComment != -1 && (startOfMultilineComment == -1 || startOfLineComment < startOfMultilineComment)) { revisedLine = revisedLine.substring(0, startOfLineComment); break; } else if (startOfMultilineComment != -1) { int endOfMultilineComment = revisedLine.indexOf("*/", startOfMultilineComment + 2); if (endOfMultilineComment == -1) { revisedLine = revisedLine.substring( 0, startOfMultilineComment); inMultilineComment = true; break; } else { revisedLine = revisedLine.substring(0, startOfMultilineComment) + revisedLine.substring(endOfMultilineComment + 2); } } else { break; } } } if (!revisedLine.isEmpty()) { // This check for shortcut mode should be redundant, but // it's done for safety reasons. if (!parseLine(revisedLine) && shortcutMode) { break; } } } catch (ParseException e) { // Inform the error handler of the exception. errorManager.report( e.isFatal() ? CheckLevel.ERROR : CheckLevel.WARNING, JSError.make(filePath, lineNum, 0 /* char offset */, e.isFatal() ? PARSE_ERROR : PARSE_WARNING, e.getMessage(), line)); parseSucceeded = parseSucceeded && !e.isFatal(); } } } catch (IOException e) { errorManager.report(CheckLevel.ERROR, JSError.make(filePath, 0, 0 /* char offset */, PARSE_ERROR, "Error reading file: " + filePath)); parseSucceeded = false; } } /** * Called for each line of the file being parsed. * * @param line The line to parse. * @return true to keep going, false otherwise. * @throws ParseException Should be thrown to signify a problem with the line. */ abstract boolean parseLine(String line) throws ParseException; /** * Parses a JS string literal. * * @param jsStringLiteral The literal. Must look like "asdf" or 'asdf' * @throws ParseException Thrown if there is a string literal that cannot be * parsed. */ String parseJsString(String jsStringLiteral) throws ParseException { valueMatcher.reset(jsStringLiteral); if (!valueMatcher.matches()) { throw new ParseException("Syntax error in JS String literal", true /* fatal */); } return valueMatcher.group(1) != null ? valueMatcher.group(1) : valueMatcher.group(2); } /** * Parses a JavaScript array of string literals. (eg: ['a', 'b', "c"]). * @param input A string containing a JavaScript array of string literals. * @return A list of parsed string literals. * @throws ParseException Thrown if there is a syntax error with the input. */ List parseJsStringArray(String input) throws ParseException { List results = Lists.newArrayList(); int indexStart = input.indexOf('['); int indexEnd = input.lastIndexOf(']'); if ((indexStart == -1) || (indexEnd == -1)) { throw new ParseException("Syntax error when parsing JS array", true /* fatal */); } String innerValues = input.substring(indexStart + 1, indexEnd); if (!innerValues.trim().isEmpty()) { valueMatcher.reset(innerValues); for (;;) { // Parse the current string literal. if (!valueMatcher.lookingAt()) { throw new ParseException("Syntax error in JS String literal", true /* fatal */); } // Add it to the results. results.add(valueMatcher.group(1) != null ? valueMatcher.group(1) : valueMatcher.group(2)); if (valueMatcher.hitEnd()) { break; } // Ensure there is a comma after the value. if (innerValues.charAt(valueMatcher.end()) != ',') { throw new ParseException("Missing comma in string array", true /* fatal */); } // Move to the next value. valueMatcher.region(valueMatcher.end() + 1, valueMatcher.regionEnd()); } } return results; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/JsFunctionParser.java0000644000175000017500000001031012115204405027664 0ustar apoapo/* * 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.deps; import com.google.common.base.CharMatcher; import com.google.common.collect.Lists; import com.google.javascript.jscomp.ErrorManager; import java.io.Reader; import java.io.StringReader; import java.util.Collection; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A parser that can extract dependency information from a .js file. * * @author agrieve@google.com (Andrew Grieve) * @author ielashi@google.com (Islam El-Ashi) */ public class JsFunctionParser extends JsFileLineParser { public static class SymbolInfo { public final String functionName; public final String symbol; private SymbolInfo(String functionName, String symbol) { this.functionName = functionName; this.symbol = symbol; } } private static Logger logger = Logger.getLogger(JsFunctionParser.class.getName()); /** Pattern for matching functions. */ private Pattern pattern; /** Matcher used in the parsing. */ private Matcher matcher; /** Symbols parsed. */ private Collection symbols; /** Functions to parse */ private Collection functionsToParse; /** * Constructor * * @param functions Functions to parse. * @param errorManager Handles parse errors. */ public JsFunctionParser( Collection functions, ErrorManager errorManager) { super(errorManager); functionsToParse = functions; pattern = getPattern(functions); matcher = pattern.matcher(""); } /** * Constructs a pattern to extract the arguments of the given functions. * * @param functions Functions to parse. * @return A pattern to extract {@code functions}' arguments. */ private Pattern getPattern(Collection functions) { StringBuilder sb = new StringBuilder("(?:^|;)\\s*("); for (String function : functions) { sb.append(Pattern.quote(function) + "|"); } // remove last '|' sb.deleteCharAt(sb.length() - 1); sb.append(")\\s*\\((.*?)\\)"); return Pattern.compile(sb.toString()); } /** * Parses the given file and returns the dependency information that it * contained. * * @param filePath Path to the file to parse. * @param fileContents The contents to parse. * @return A collection containing all symbols found in the * file. */ public Collection parseFile( String filePath, String fileContents) { return parseReader(filePath, new StringReader(fileContents)); } private Collection parseReader( String filePath, Reader fileContents) { symbols = Lists.newArrayList(); logger.fine("Parsing Source: " + filePath); doParse(filePath, fileContents); return symbols; } /** * Parses a line of JavaScript, extracting dependency information. */ @Override protected boolean parseLine(String line) throws ParseException { boolean hasFunctions = false; boolean parseLine = false; // Quick sanity check that will catch most cases. This is a performance // win for people with a lot of JS. for (String function : functionsToParse) { if (line.indexOf(function) != -1) { parseLine = true; break; } } if (parseLine) { matcher.reset(line); while (matcher.find()) { hasFunctions = true; String functionName = matcher.group(1); String arg = parseJsString(matcher.group(2)); // Parse the param. symbols.add(new SymbolInfo(functionName, arg)); } } return !shortcutMode || hasFunctions || CharMatcher.WHITESPACE.matchesAllOf(line); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/DepsFileParser.java0000644000175000017500000001341312115204405027304 0ustar apoapo/* * 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.deps; import com.google.common.base.CharMatcher; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.ErrorManager; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A parser that can extract dependency information from existing deps.js files. * *

See //javascript/closure/deps.js for an example file.

* * @author agrieve@google.com (Andrew Grieve) */ public class DepsFileParser extends JsFileLineParser { private static Logger logger = Logger.getLogger(DepsFileParser.class.getName()); /** * Pattern for matching JavaScript string literals. The group is: * goog.addDependency({1}); */ private final Matcher depMatcher = Pattern.compile("\\s*goog.addDependency\\((.*)\\);?\\s*").matcher(""); /** * Pattern for matching the args of a goog.addDependency(). The group is: * goog.addDependency({1}, {2}, {3}); */ private final Matcher depArgsMatch = Pattern.compile("\\s*([^,]*), (\\[[^\\]]*\\]), (\\[[^\\]]*\\])\\s*").matcher(""); /** * The dependency information extracted from the current file. */ private List depInfos; /** Translates paths in different build systems. */ private final Function pathTranslator; /** * Constructor * * @param errorManager Handles parse errors. */ public DepsFileParser(ErrorManager errorManager) { this(Functions.identity(), errorManager); } /** * @param pathTranslator Translates paths in different build systems. * @param errorManager Handles parse errors. */ public DepsFileParser(Function pathTranslator, ErrorManager errorManager) { super(errorManager); this.pathTranslator = pathTranslator; } /** * Parses the given file and returns a list of dependency information that it * contained. * * @param filePath Path to the file to parse. * @return A list of DependencyInfo objects. * @throws IOException Thrown if the file could not be read. */ public List parseFile(String filePath) throws IOException { return parseFileReader(filePath, new FileReader(filePath)); } /** * Parses the given file and returns a list of dependency information that it * contained. * It uses the passed in fileContents instead of reading the file. * * @param filePath Path to the file to parse. * @param fileContents The contents to parse. * @return A list of DependencyInfo objects. */ public List parseFile(String filePath, String fileContents) { return parseFileReader(filePath, new StringReader(fileContents)); } /** * Parses the file from the given reader and returns a list of * dependency information that it contained. * * @param filePath Path to the file to parse. * @param reader A reader for the file. * @return A list of DependencyInfo objects. */ public List parseFileReader(String filePath, Reader reader) { depInfos = Lists.newArrayList(); logger.fine("Parsing Dep: " + filePath); doParse(filePath, reader); return depInfos; } /** * Extracts dependency information from lines that look like * goog.addDependency('pathRelativeToClosure', ['provides'], ['requires']); * Adds the dependencies to depInfos. * * @throws ParseException Thrown if the given line has a malformed * goog.addDependency(). */ @Override protected boolean parseLine(String line) throws ParseException { boolean hasDependencies = false; // Quick sanity check that will catch most cases. This is a performance // win for people with a lot of JS. if (line.indexOf("addDependency") != -1) { depMatcher.reset(line); // See if the line looks like: goog.addDependency(...) if (depMatcher.matches()) { hasDependencies = true; String addDependencyParams = depMatcher.group(1); depArgsMatch.reset(addDependencyParams); // Extract the three parameters. if (!depArgsMatch.matches()) { // Although we could recover, we mark this as fatal since there should // not be problems with generated deps.js files. throw new ParseException("Invalid arguments to goog.addDependency(). Found: " + addDependencyParams, true); } // Parse the file path. String path = pathTranslator.apply(parseJsString(depArgsMatch.group(1))); DependencyInfo depInfo = new SimpleDependencyInfo(path, filePath, // Parse the provides. parseJsStringArray(depArgsMatch.group(2)), // Parse the requires. parseJsStringArray(depArgsMatch.group(3))); if (logger.isLoggable(Level.FINE)) { logger.fine("Found dep: " + depInfo); } depInfos.add(depInfo); } } return !shortcutMode || hasDependencies || CharMatcher.WHITESPACE.matchesAllOf(line); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/JsFileParser.java0000644000175000017500000001424312115204405026767 0ustar apoapo/* * 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.deps; import com.google.common.base.CharMatcher; import com.google.common.collect.Lists; import com.google.javascript.jscomp.ErrorManager; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.List; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * A parser that can extract goog.require() and goog.provide() dependency * information from a .js file. * * @author agrieve@google.com (Andrew Grieve) */ public class JsFileParser extends JsFileLineParser { private static Logger logger = Logger.getLogger(JsFileParser.class.getName()); /** Pattern for matching goog.provide(*) and goog.require(*). */ private static final Pattern GOOG_PROVIDE_REQUIRE_PATTERN = Pattern.compile( "(?:^|;)\\s*goog\\.(provide|require|addDependency)\\s*\\((.*?)\\)"); /** The first non-comment line of base.js */ private static final String BASE_JS_START = "var COMPILED = false;"; /** Matchers used in the parsing. */ private Matcher googMatcher = GOOG_PROVIDE_REQUIRE_PATTERN.matcher(""); /** The info for the file we are currently parsing. */ private List provides; private List requires; private boolean fileHasProvidesOrRequires; /** Whether to provide/require the root namespace. */ private boolean includeGoogBase = false; /** * Constructor * * @param errorManager Handles parse errors. */ public JsFileParser(ErrorManager errorManager) { super(errorManager); } /** * Sets whether we should create implicit provides and requires of the * root namespace. * * When generating deps files, you do not want this behavior. Deps files * need base.js to run anyway, so they don't need information about it. * * When generating abstract build graphs, you probably do want this behavior. * It will create an implicit dependency of all files with provides/requires * on base.js. * * @return this for easy chaining. */ public JsFileParser setIncludeGoogBase(boolean include) { includeGoogBase = include; return this; } /** * Parses the given file and returns the dependency information that it * contained. * * @param filePath Path to the file to parse. * @param closureRelativePath Path of the file relative to closure. * @return A DependencyInfo containing all provides/requires found in the * file. * @throws IOException Thrown if there was an problem reading the given file. */ public DependencyInfo parseFile(String filePath, String closureRelativePath) throws IOException { return parseReader(filePath, closureRelativePath, new FileReader(filePath)); } /** * Parses the given file and returns the dependency information that it * contained. * * @param filePath Path to the file to parse. * @param closureRelativePath Path of the file relative to closure. * @param fileContents The contents to parse. * @return A DependencyInfo containing all provides/requires found in the * file. */ public DependencyInfo parseFile(String filePath, String closureRelativePath, String fileContents) { return parseReader(filePath, closureRelativePath, new StringReader(fileContents)); } private DependencyInfo parseReader(String filePath, String closureRelativePath, Reader fileContents) { provides = Lists.newArrayList(); requires = Lists.newArrayList(); fileHasProvidesOrRequires = false; logger.fine("Parsing Source: " + filePath); doParse(filePath, fileContents); DependencyInfo dependencyInfo = new SimpleDependencyInfo( closureRelativePath, filePath, provides, requires); logger.fine("DepInfo: " + dependencyInfo); return dependencyInfo; } /** * Parses a line of JavaScript, extracting goog.provide and goog.require * information. */ @Override protected boolean parseLine(String line) throws ParseException { boolean lineHasProvidesOrRequires = false; // Quick sanity check that will catch most cases. This is a performance // win for people with a lot of JS. if (line.indexOf("provide") != -1 || line.indexOf("require") != -1 || line.indexOf("addDependency") != -1) { // Iterate over the provides/requires. googMatcher.reset(line); while (googMatcher.find()) { lineHasProvidesOrRequires = true; if (includeGoogBase && !fileHasProvidesOrRequires) { fileHasProvidesOrRequires = true; requires.add("goog"); } // See if it's a require or provide. char firstChar = googMatcher.group(1).charAt(0); boolean isProvide = firstChar == 'p'; boolean isRequire = firstChar == 'r'; if (isProvide || isRequire) { // Parse the param. String arg = parseJsString(googMatcher.group(2)); // Add the dependency. if (isRequire) { // goog is always implicit. // TODO(nicksantos): I'm pretty sure we don't need this anymore. // Remove this later. if (!"goog".equals(arg)) { requires.add(arg); } } else { provides.add(arg); } } } } else if (includeGoogBase && line.startsWith(BASE_JS_START) && provides.isEmpty() && requires.isEmpty()) { provides.add("goog"); // base.js can't provide or require anything else. return false; } return !shortcutMode || lineHasProvidesOrRequires || CharMatcher.WHITESPACE.matchesAllOf(line); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/SimpleDependencyInfo.java0000644000175000017500000000635212115204405030504 0ustar apoapo/* * 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.deps; import com.google.common.base.Objects; import java.util.Collection; import java.util.Collections; import java.util.List; /** * A class to hold JS dependency information for a single .js file. * * @author agrieve@google.com (Andrew Grieve) */ public class SimpleDependencyInfo implements DependencyInfo { /** A list of provided symbols. */ private final List provides; /** A list of required symbols. */ private final List requires; /** The path of the file relative to closure. */ private final String srcPathRelativeToClosure; /** The path to the file from which we extracted the dependency information.*/ private final String pathOfDefiningFile; /** * Constructs a DependencyInfo object with the given list of provides & * requires. This does *not* copy the given lists, but uses them directly. * * @param srcPathRelativeToClosure The closure-relative path of the file * associated with this DependencyInfo. * @param pathOfDefiningFile The path to the file from which this dependency * information was extracted. * @param provides List of provided symbols. * @param requires List of required symbols. */ public SimpleDependencyInfo( String srcPathRelativeToClosure, String pathOfDefiningFile, List provides, List requires) { this.srcPathRelativeToClosure = srcPathRelativeToClosure; this.pathOfDefiningFile = pathOfDefiningFile; this.provides = provides; this.requires = requires; } @Override public String getName() { return pathOfDefiningFile; } @Override public String getPathRelativeToClosureBase() { return srcPathRelativeToClosure; } @Override public Collection getProvides() { return Collections.unmodifiableList(provides); } @Override public Collection getRequires() { return Collections.unmodifiableList(requires); } @Override public boolean equals(Object obj) { if (!(obj instanceof SimpleDependencyInfo)) { return false; } SimpleDependencyInfo other = (SimpleDependencyInfo)obj; return Objects.equal(other.srcPathRelativeToClosure, srcPathRelativeToClosure) && Objects.equal(other.pathOfDefiningFile, pathOfDefiningFile) && Objects.equal(other.requires, this.requires) && Objects.equal(other.provides, this.provides); } @Override public String toString() { return String.format("DependencyInfo(relativePath='%1$s', path='%2$s', " + "provides=%3$s, requires=%4$s)", srcPathRelativeToClosure, pathOfDefiningFile, provides, requires); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/SortedDependencies.java0000644000175000017500000002311012115204405030176 0ustar apoapo/* * 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.deps; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import java.util.ArrayDeque; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; /** * A sorted list of inputs with dependency information. Uses a stable * topological sort to make sure that an input always comes after its * dependencies. * * Also exposes other information about the inputs, like which inputs * do not provide symbols. * * @author nicksantos@google.com (Nick Santos) */ public class SortedDependencies { private final List inputs; // A topologically sorted list of the inputs. private final List sortedList; // A list of all the inputs that do not have provides. private final List noProvides; private final Map provideMap = Maps.newHashMap(); public SortedDependencies(List inputs) throws CircularDependencyException { this.inputs = Lists.newArrayList(inputs); noProvides = Lists.newArrayList(); // Collect all symbols provided in these files. for (INPUT input : inputs) { Collection currentProvides = input.getProvides(); if (currentProvides.isEmpty()) { noProvides.add(input); } for (String provide : currentProvides) { provideMap.put(provide, input); } } // Get the direct dependencies. final Multimap deps = HashMultimap.create(); for (INPUT input : inputs) { for (String req : input.getRequires()) { INPUT dep = provideMap.get(req); if (dep != null && dep != input) { deps.put(input, dep); } } } // Sort the inputs by sucking in 0-in-degree nodes until we're done. sortedList = topologicalStableSort(inputs, deps); // The dependency graph of inputs has a cycle iff sortedList is a proper // subset of inputs. Also, it has a cycle iff the subgraph // (inputs - sortedList) has a cycle. It's fairly easy to prove this // by the lemma that a graph has a cycle iff it has a subgraph where // no nodes have out-degree 0. I'll leave the proof of this as an exercise // to the reader. if (sortedList.size() < inputs.size()) { List subGraph = Lists.newArrayList(inputs); subGraph.removeAll(sortedList); throw new CircularDependencyException( cycleToString(findCycle(subGraph, deps))); } } /** * Return the input that gives us the given symbol. * @throws MissingProvideException An exception if there is no * input for this symbol. */ public INPUT getInputProviding(String symbol) throws MissingProvideException { if (provideMap.containsKey(symbol)) { return provideMap.get(symbol); } throw new MissingProvideException(symbol); } /** * Return the input that gives us the given symbol, or null. */ public INPUT maybeGetInputProviding(String symbol) { return provideMap.get(symbol); } /** * Returns the first circular dependency found. Expressed as a list of * items in reverse dependency order (the second element depends on the * first, etc.). */ private List findCycle( List subGraph, Multimap deps) { return findCycle(subGraph.get(0), Sets.newHashSet(subGraph), deps, Sets.newHashSet()); } private List findCycle( INPUT current, Set subGraph, Multimap deps, Set covered) { if (covered.add(current)) { List cycle = findCycle( findRequireInSubGraphOrFail(current, subGraph), subGraph, deps, covered); // Don't add the input to the list if the cycle has closed already. if (cycle.get(0) != cycle.get(cycle.size() - 1)) { cycle.add(current); } return cycle; } else { // Explicitly use the add() method, to prevent a generics constructor // warning that is dumb. The condition it's protecting is // obscure, and I think people have proposed that it be removed. List cycle = Lists.newArrayList(); cycle.add(current); return cycle; } } private INPUT findRequireInSubGraphOrFail(INPUT input, Set subGraph) { for (String symbol : input.getRequires()) { INPUT candidate = provideMap.get(symbol); if (subGraph.contains(candidate)) { return candidate; } } throw new IllegalStateException("no require found in subgraph"); } /** * @param cycle A cycle in reverse-dependency order. */ private String cycleToString(List cycle) { List symbols = Lists.newArrayList(); for (int i = cycle.size() - 1; i >= 0; i--) { symbols.add(cycle.get(i).getProvides().iterator().next()); } symbols.add(symbols.get(0)); return Joiner.on(" -> ").join(symbols); } public List getSortedList() { return Collections.unmodifiableList(sortedList); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. */ public List getSortedDependenciesOf(List roots) { return getDependenciesOf(roots, true); } /** * Gets all the dependencies of the given roots. The inputs must be returned * in a stable order. In other words, if A comes before B, and A does not * transitively depend on B, then A must also come before B in the returned * list. * * @param sorted If true, get them in topologically sorted order. If false, * get them in the original order they were passed to the compiler. */ public List getDependenciesOf(List roots, boolean sorted) { Preconditions.checkArgument(inputs.containsAll(roots)); Set included = Sets.newHashSet(); Deque worklist = new ArrayDeque(roots); while (!worklist.isEmpty()) { INPUT current = worklist.pop(); if (included.add(current)) { for (String req : current.getRequires()) { INPUT dep = provideMap.get(req); if (dep != null) { worklist.add(dep); } } } } ImmutableList.Builder builder = ImmutableList.builder(); for (INPUT current : (sorted ? sortedList : inputs)) { if (included.contains(current)) { builder.add(current); } } return builder.build(); } public List getInputsWithoutProvides() { return Collections.unmodifiableList(noProvides); } private static List topologicalStableSort( List items, Multimap deps) { if (items.size() == 0) { // Priority queue blows up if we give it a size of 0. Since we need // to special case this either way, just bail out. return Lists.newArrayList(); } final Map originalIndex = Maps.newHashMap(); for (int i = 0; i < items.size(); i++) { originalIndex.put(items.get(i), i); } PriorityQueue inDegreeZero = new PriorityQueue(items.size(), new Comparator() { @Override public int compare(T a, T b) { return originalIndex.get(a).intValue() - originalIndex.get(b).intValue(); } }); List result = Lists.newArrayList(); Multiset inDegree = HashMultiset.create(); Multimap reverseDeps = ArrayListMultimap.create(); Multimaps.invertFrom(deps, reverseDeps); // First, add all the inputs with in-degree 0. for (T item : items) { Collection itemDeps = deps.get(item); inDegree.add(item, itemDeps.size()); if (itemDeps.isEmpty()) { inDegreeZero.add(item); } } // Then, iterate to a fixed point over the reverse dependency graph. while (!inDegreeZero.isEmpty()) { T item = inDegreeZero.remove(); result.add(item); for (T inWaiting : reverseDeps.get(item)) { inDegree.remove(inWaiting, 1); if (inDegree.count(inWaiting) == 0) { inDegreeZero.add(inWaiting); } } } return result; } public static class CircularDependencyException extends Exception { CircularDependencyException(String message) { super(message); } } public static class MissingProvideException extends Exception { MissingProvideException(String provide) { super(provide); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/DependencyInfo.java0000644000175000017500000000233012115204405027322 0ustar apoapo/* * 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.deps; import java.util.Collection; /** * A data structure for JS dependency information for a single .js file. * * @author agrieve@google.com (Andrew Grieve) */ public interface DependencyInfo { /** Gets the unique name / path of this file. */ public String getName(); /** Gets the path of this file relative to Closure's base.js file. */ public String getPathRelativeToClosureBase(); /** Gets the symbols provided by this file. */ public Collection getProvides(); /** Gets the symbols required by this file. */ public Collection getRequires(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/PathUtil.java0000644000175000017500000001600212115204405026163 0ustar apoapo/* * 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.deps; import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.List; /** * Utility methods for manipulation of UNIX-like paths. * NOTE: According to kevinb, equivalent methods will be in the standard library once * jsr203 is ready. * */ public final class PathUtil { private static final CharMatcher SLASH_MATCHER = CharMatcher.is('/'); private static final CharMatcher NON_SLASH_MATCHER = CharMatcher.isNot('/'); private PathUtil() { } /** * Removes all ../ and ./ entries from within the given path. If there are extra ..s that move * beyond the first directory given, they are removed. * * Examples: * "a/b/../c" results in "a/c" * "./foo/./../bar" results in "bar" * "a/.." results in "" * "a/../../foo" results in "foo" * * @param path The path to remove dots from. * @return The path with all dots collapsed. */ public static String collapseDots(String path) { path = removeExtraneousSlashes(path); // Optimization: Most paths don't contain dots. if (!path.contains(".")) { return path; } String[] srcFragments = path.split("/"); List dstFragments = Lists.newArrayList(); for (String fragment : srcFragments) { if (fragment.equals("..")) { if (!dstFragments.isEmpty()) { dstFragments.remove(dstFragments.size() - 1); } } else if (!fragment.equals(".")) { dstFragments.add(fragment); } } // Special case for Join.join([""]); -> "/" if (dstFragments.size() == 1 && dstFragments.get(0).isEmpty()) { return "/"; } return Joiner.on("/").join(dstFragments); } /** * Determines if a path is absolute or not by testing for the presence of "/" * at the front of the string. * * @param path The path to test * @return true if the path starts with DELIMITER, false otherwise. */ static boolean isAbsolute(String path) { return path.startsWith("/"); } /** * Removes extra slashes from a path. Leading slash is preserved, trailing * slash is stripped, and any runs of more than one slash in the middle is * replaced by a single slash. */ static String removeExtraneousSlashes(String s) { int lastNonSlash = NON_SLASH_MATCHER.lastIndexIn(s); if (lastNonSlash != -1) { s = s.substring(0, lastNonSlash + 1); } return SLASH_MATCHER.collapseFrom(s, '/'); } /** * Converts the given path into an absolute one. This prepends the current * working directory and removes all .'s from the path. If an absolute path * is given, it will not be prefixed. * *

Unlike File.getAbsolutePath(), this function does remove .'s from the * path and unlike File.getCanonicalPath(), this function does not resolve * symlinks and does not use filesystem calls.

* * @param path The path to make absolute. * @return The path made absolute. */ public static String makeAbsolute(String path) { return makeAbsolute(path, System.getProperty("user.dir")); } /** * Converts the given path into an absolute one. This prepends the given * rootPath and removes all .'s from the path. If an absolute path is given, * it will not be prefixed. * *

Unlike File.getAbsolutePath(), this function does remove .'s from the * path and unlike File.getCanonicalPath(), this function does not resolve * symlinks and does not use filesystem calls.

* * @param rootPath The path to prefix to path if path is not already absolute. * @param path The path to make absolute. * @return The path made absolute. */ public static String makeAbsolute(String path, String rootPath) { if (!isAbsolute(path)) { path = rootPath + "/" + path; } return collapseDots(path); } /** * Returns targetPath relative to basePath. * *

basePath and targetPath must either both be relative, or both be * absolute paths.

* *

This function is different from makeRelative * in that it is able to add in ../ components and collapse existing ones as well.

* * Examples: * base="some/relative/path" target="some/relative/path/foo" return="foo" * base="some/relative/path" target="some/relative" return=".." * base="some/relative/path" target="foo/bar" return="../../../foo/bar" * base="/some/abs/path" target="/foo/bar" return="../../../foo/bar" * * @param basePath The path to make targetPath relative to. * @param targetPath The path to make relative. * @return A path relative to targetPath. The returned value will never start * with a slash. */ public static String makeRelative(String basePath, String targetPath) { // Ensure the paths are both absolute or both relative. if (isAbsolute(basePath) != isAbsolute(targetPath)) { throw new IllegalArgumentException( "Paths must both be relative or both absolute.\n" + " basePath: " + basePath + "\n" + " targetPath: " + targetPath); } basePath = collapseDots(basePath); targetPath = collapseDots(targetPath); String[] baseFragments = basePath.split("/"); String[] targetFragments = targetPath.split("/"); int i = -1; do { i += 1; if (i == baseFragments.length && i == targetFragments.length) { // Eg) base: /java/com/google // target: /java/com/google // result: . <-- . is better than "" since "" + "/path" = "/path" return "."; } else if (i == baseFragments.length) { // Eg) base: /java/com/google // target: /java/com/google/c/ui // result: c/ui return Joiner.on("/").join( Lists.newArrayList( Arrays.asList(targetFragments).listIterator(i))); } else if (i == targetFragments.length) { // Eg) base: /java/com/google/c/ui // target: /java/com/google // result: ../.. return Strings.repeat("../", baseFragments.length - i - 1) + ".."; } } while (baseFragments[i].equals(targetFragments[i])); // Eg) base: /java/com/google/c // target: /java/com/google/common/base // result: ../common/base return Strings.repeat("../", baseFragments.length - i) + Joiner.on("/").join( Lists.newArrayList(Arrays.asList(targetFragments).listIterator(i))); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/deps/package.html0000644000175000017500000000016612115204405026053 0ustar apoapo Analyzes information about dependencies between files. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/UseSite.java0000644000175000017500000000257012115204405025064 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Information about the context in which a Definition is used. * Includes the referring node, and context in which the reference * occurs - including the module in which the reference appears. * */ class UseSite { final Node node; final Scope scope; final JSModule module; UseSite(Node node, Scope scope, JSModule module) { this.node = node; this.scope = scope; this.module = module; } // Use the node as the identifying feature to make the UseSite recreatable. @Override public int hashCode() { return this.node.hashCode(); } @Override public boolean equals(Object o) { return (o instanceof UseSite && ((UseSite)(o)).node.equals(this.node)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SimpleFunctionAliasAnalysis.java0000644000175000017500000000744712115204405031130 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Uses {@link SimpleDefinitionFinder} to determine if a function has been * aliased or exposed to .call() or .apply(). * * @author dcc@google.com (Devin Coughlin) */ class SimpleFunctionAliasAnalysis { private Set aliasedFunctions; private Set functionsExposedToCallOrApply; /** * Returns true if the function is aliased. * * Must only be called after {@link #analyze(SimpleDefinitionFinder)} * has been called. */ public boolean isAliased(Node functionNode) { Preconditions.checkNotNull(aliasedFunctions); Preconditions.checkArgument(functionNode.isFunction()); return aliasedFunctions.contains(functionNode); } /** * Returns true if the function ever exposed to .call() or .apply(). * * Must only be called after {@link #analyze(SimpleDefinitionFinder)} * has been called. */ public boolean isExposedToCallOrApply(Node functionNode) { Preconditions.checkNotNull(functionsExposedToCallOrApply); Preconditions.checkArgument(functionNode.isFunction()); return functionsExposedToCallOrApply.contains(functionNode); } /** * Uses the provided {@link SimpleDefinitionFinder} to determine * which functions are aliased or exposed to .call() or .apply(). */ public void analyze(SimpleDefinitionFinder finder) { Preconditions.checkState(aliasedFunctions == null); aliasedFunctions = Sets.newHashSet(); functionsExposedToCallOrApply = Sets.newHashSet(); for (DefinitionSite definitionSite : finder.getDefinitionSites()) { Definition definition = definitionSite.definition; if (!definition.isExtern()) { Node rValue = definition.getRValue(); if (rValue != null && rValue.isFunction()) { // rValue is a Token.FUNCTION from a definition for (UseSite useSite : finder.getUseSites(definition)) { updateFunctionForUse(rValue, useSite.node); } } } } } /** * Updates alias and exposure information based a site where the function is * used. * * Note: this method may be called multiple times per Function, each time * with a different useNode. */ private void updateFunctionForUse(Node function, Node useNode) { Node useParent = useNode.getParent(); int parentType = useParent.getType(); if ((parentType == Token.CALL || parentType == Token.NEW) && useParent.getFirstChild() == useNode) { // Regular call sites don't count as aliases } else if (NodeUtil.isGet(useParent)) { // GET{PROP,ELEM} don't count as aliases // but we have to check for using them in .call and .apply. if (useParent.isGetProp()) { Node gramps = useParent.getParent(); if (NodeUtil.isFunctionObjectApply(gramps) || NodeUtil.isFunctionObjectCall(gramps)) { functionsExposedToCallOrApply.add(function); } } } else { aliasedFunctions.add(function); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ClosureRewriteClass.java0000644000175000017500000003123412115204405027446 0ustar apoapo/* * Copyright 2012 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; /** * Rewrites "goog.defineClass" into a form that is suitable for * type checking and dead code elimination. * * @author johnlenz@google.com (John Lenz) */ class ClosureRewriteClass extends AbstractPostOrderCallback implements HotSwapCompilerPass { // Errors static final DiagnosticType GOOG_CLASS_TARGET_INVALID = DiagnosticType.error( "JSC_GOOG_CLASS_TARGET_INVALID", "Unsupported class definition expression."); static final DiagnosticType GOOG_CLASS_SUPER_CLASS_NOT_VALID = DiagnosticType.error( "JSC_GOOG_CLASS_SUPER_CLASS_NOT_VALID", "The super class must be null or a valid name reference"); static final DiagnosticType GOOG_CLASS_DESCRIPTOR_NOT_VALID = DiagnosticType.error( "JSC_GOOG_CLASS_DESCRIPTOR_NOT_VALID", "The class descriptor must be an object literal"); static final DiagnosticType GOOG_CLASS_CONSTRUCTOR_MISING = DiagnosticType.error( "JSC_GOOG_CLASS_CONSTRUCTOR_MISING", "The constructor expression is missing for the class descriptor"); static final DiagnosticType GOOG_CLASS_STATICS_NOT_VALID = DiagnosticType.error( "JSC_GOOG_CLASS_STATICS_NOT_VALID", "The class statics descriptor must be an object or function literal"); static final DiagnosticType GOOG_CLASS_UNEXPECTED_PARAMS = DiagnosticType.error( "JSC_GOOG_CLASS_UNEXPECTED_PARAMS", "The class definition has too many arguments."); private final AbstractCompiler compiler; public ClosureRewriteClass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { this.compiler.process(this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && isGoogDefineClass(n)) { if (!validateUsage(n)) { compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); } } maybeRewriteClassDefinition(n); } private boolean validateUsage(Node n) { // There are only three valid usage patterns for of goog.defineClass // var ClassName = googDefineClass // namespace.ClassName = googDefineClass // and within an objectlit, used by the goog.defineClass. Node parent = n.getParent(); switch (parent.getType()) { case Token.NAME: return true; case Token.ASSIGN: return n == parent.getLastChild() && parent.getParent().isExprResult(); case Token.STRING_KEY: return isContainedInGoogDefineClass(parent); } return false; } private boolean isContainedInGoogDefineClass(Node n) { while (n != null) { n = n.getParent(); if (n.isCall()) { if (isGoogDefineClass(n)) { return true; } } else if (!n.isObjectLit() && !n.isStringKey()) { break; } } return false; } private void maybeRewriteClassDefinition(Node n) { if (n.isVar()) { Node target = n.getFirstChild(); Node value = target.getFirstChild(); maybeRewriteClassDefinition(n, target, value); } else if (NodeUtil.isExprAssign(n)) { Node assign = n.getFirstChild(); Node target = assign.getFirstChild(); Node value = assign.getLastChild(); maybeRewriteClassDefinition(n, target, value); } } private void maybeRewriteClassDefinition( Node n, Node target, Node value) { if (isGoogDefineClass(value)) { if (!target.isQualifiedName()) { compiler.report(JSError.make(n, GOOG_CLASS_TARGET_INVALID)); } ClassDefinition def = extractClassDefinition(target, value); if (def != null) { value.detachFromParent(); target.detachFromParent(); rewriteGoogDefineClass(n, def); } } } private static class MemberDefinition { final JSDocInfo info; final Node name; final Node value; MemberDefinition(JSDocInfo info, Node name, Node value) { this.info = info; this.name = name; this.value = value; } } private final class ClassDefinition { final Node name; final Node superClass; final MemberDefinition constructor; final List staticProps; final List props; final Node classModifier; ClassDefinition( Node name, Node superClass, MemberDefinition constructor, List staticProps, List props, Node classModifier) { this.name = name; this.superClass = superClass; this.constructor = constructor; this.staticProps = staticProps; this.props = props; this.classModifier = classModifier; } } /** * Validates the class definition and if valid, destructively extracts * the class definition from the AST. */ private ClassDefinition extractClassDefinition( Node targetName, Node callNode) { // name = goog.defineClass(superClass, {...}, [modifier, ...]) Node superClass = NodeUtil.getArgumentForCallOrNew(callNode, 0); if (superClass == null || (!superClass.isNull() && !superClass.isQualifiedName())) { compiler.report(JSError.make(callNode, GOOG_CLASS_SUPER_CLASS_NOT_VALID)); return null; } if (NodeUtil.isNullOrUndefined(superClass)) { superClass = null; } Node description = NodeUtil.getArgumentForCallOrNew(callNode, 1); if (description == null || !description.isObjectLit() || !validateObjLit(description)) { // report bad class definition compiler.report(JSError.make(callNode, GOOG_CLASS_DESCRIPTOR_NOT_VALID)); return null; } int paramCount = callNode.getChildCount() -1; if (paramCount > 2) { compiler.report(JSError.make(callNode, GOOG_CLASS_UNEXPECTED_PARAMS)); return null; } Node constructor = extractProperty(description, "constructor"); if (constructor == null) { // report missing constructor compiler.report(JSError.make(description, GOOG_CLASS_CONSTRUCTOR_MISING)); return null; } JSDocInfo info = NodeUtil.getBestJSDocInfo(constructor); Node classModifier = null; Node statics = null; Node staticsProp = extractProperty(description, "statics"); if (staticsProp != null) { if (staticsProp.isObjectLit() && validateObjLit(staticsProp)) { statics = staticsProp; } else if (staticsProp.isFunction()) { classModifier = staticsProp; } else { compiler.report( JSError.make(staticsProp, GOOG_CLASS_STATICS_NOT_VALID)); return null; } } if (statics == null) { statics = IR.objectlit(); } // Ok, now rip apart the definition into its component pieces. // Remove the "special" property key nodes. maybeDetach(constructor.getParent()); maybeDetach(statics.getParent()); if (classModifier != null) { maybeDetach(classModifier.getParent()); } ClassDefinition def = new ClassDefinition( targetName, maybeDetach(superClass), new MemberDefinition(info, null, maybeDetach(constructor)), objectLitToList(maybeDetach(statics)), objectLitToList(description), maybeDetach(classModifier)); return def; } private Node maybeDetach(Node node) { if (node != null && node.getParent() != null) { node.detachFromParent(); } return node; } // Only unquoted plain properties are currently supported. private boolean validateObjLit(Node objlit) { for (Node key : objlit.children()) { if (!key.isStringKey() || key.isQuotedString()) { return false; } } return true; } /** * @return The first property in the objlit that matches the key. */ private Node extractProperty(Node objlit, String keyName) { for (Node keyNode : objlit.children()) { if (keyNode.getString().equals(keyName)) { return keyNode.isStringKey() ? keyNode.getFirstChild() : null; } } return null; } private List objectLitToList( Node objlit) { List result = Lists.newArrayList(); for (Node keyNode : objlit.children()) { result.add( new MemberDefinition( NodeUtil.getBestJSDocInfo(keyNode), keyNode, keyNode.removeFirstChild())); } objlit.detachChildren(); return result; } private void rewriteGoogDefineClass(Node exprRoot, ClassDefinition cls) { // For simplicity add everything into a block, before adding it to the AST. Node block = IR.block(); if (exprRoot.isVar()) { // example: var ctr = function(){} block.addChildToBack( IR.var( cls.name.cloneTree(), cls.constructor.value) .srcref(exprRoot).setJSDocInfo(cls.constructor.info)); } else { // example: ns.ctr = function(){} block.addChildToBack( fixupSrcref(IR.exprResult( IR.assign( cls.name.cloneTree(), cls.constructor.value) .srcref(exprRoot).setJSDocInfo(cls.constructor.info) .srcref(exprRoot))).setJSDocInfo(cls.constructor.info)); } if (cls.superClass != null) { // example: goog.inherits(ctr, superClass) block.addChildToBack( fixupSrcref(IR.exprResult( IR.call( NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), "goog.inherits") .srcrefTree(cls.superClass), cls.name.cloneTree(), cls.superClass.cloneTree()).srcref(cls.superClass)))); } for (MemberDefinition def : cls.staticProps) { // example: ctr.prop = value block.addChildToBack( fixupSrcref(IR.exprResult( fixupSrcref(IR.assign( IR.getprop(cls.name.cloneTree(), IR.string(def.name.getString()).srcref(def.name)) .srcref(def.name), def.value)).setJSDocInfo(def.info)))); // Handle inner class definitions. maybeRewriteClassDefinition(block.getLastChild()); } for (MemberDefinition def : cls.props) { // example: ctr.prototype.prop = value block.addChildToBack( fixupSrcref(IR.exprResult( fixupSrcref(IR.assign( IR.getprop( fixupSrcref(IR.getprop(cls.name.cloneTree(), IR.string("prototype").srcref(def.name))), IR.string(def.name.getString()).srcref(def.name)) .srcref(def.name), def.value)).setJSDocInfo(def.info)))); // Handle inner class definitions. maybeRewriteClassDefinition(block.getLastChild()); } if (cls.classModifier != null) { // example: modifier(ctr) block.addChildToBack( IR.exprResult( fixupFreeCall( IR.call( cls.classModifier, cls.name.cloneTree()) .srcref(cls.classModifier))) .srcref(cls.classModifier)); } exprRoot.getParent().replaceChild(exprRoot, block); compiler.reportCodeChange(); } private Node fixupSrcref(Node node) { node.srcref(node.getFirstChild()); return node; } private Node fixupFreeCall(Node call) { Preconditions.checkState(call.isCall()); call.putBooleanProp(Node.FREE_CALL, true); return call; } /** * @return Whether the call represents a class definition. */ private boolean isGoogDefineClass(Node value) { if (value != null && value.isCall()) { String targetName = value.getFirstChild().getQualifiedName(); return ("goog.defineClass".equals(targetName) || "goog.labs.classdef.defineClass".equals(targetName)); } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ConstCheck.java0000644000175000017500000000736012115204405025531 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; } case Token.INC: case Token.DEC: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { reportError(t, n, name); } } break; } } } /** * Gets whether a variable is a constant initialized to a literal value at * the point where it is declared. */ private boolean isConstant(Scope.Var var) { return var != null && var.isConst(); } /** * Reports a reassigned constant error. */ void reportError(NodeTraversal t, Node n, String name) { compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReferenceCollectingCallback.java0000644000175000017500000005112012115204405031015 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A helper class for passes that want to access all information about where a * variable is referenced and declared at once and then make a decision as to * how it should be handled, possibly inlining, reordering, or generating * warnings. Callers do this by providing {@link Behavior} and then * calling {@link #process(Node, Node)}. * * @author kushal@google.com (Kushal Dave) */ class ReferenceCollectingCallback implements ScopedCallback, HotSwapCompilerPass, StaticSymbolTable { /** * Maps a given variable to a collection of references to that name. Note that * Var objects are not stable across multiple traversals (unlike scope root or * name). */ private final Map referenceMap = Maps.newHashMap(); /** * The stack of basic blocks and scopes the current traversal is in. */ private final Deque blockStack = new ArrayDeque(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * JavaScript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } /** * Same as process but only runs on a part of AST associated to one script. */ @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } /** * Gets the variables that were referenced in this callback. */ @Override public Iterable getAllSymbols() { return referenceMap.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block stack and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explicit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map referenceMap; public ReferenceMapWrapper(Map referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of references. Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable { List references = Lists.newArrayList(); @Override public Iterator iterator() { return references.iterator(); } void add(Reference reference) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a definitive judgment. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore( references.get(i).getBasicBlock())) { return false; } } return true; } /** * Whether the variable is escaped into an inner scope. */ boolean isEscaped() { Scope scope = null; for (Reference ref : references) { if (scope == null) { scope = ref.scope; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a = 2", "function a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit.isInitializingDeclaration()) { // This is a declaration that represents the initial value. // Specifically, var declarations without assignments such as "var a;" // are not. return true; } return false; } /** * @param index The index into the references array to look for an * initialized assignment reference. That is, an assignment immediately * follow a variable declaration that itself does not initialize the * variable. */ private boolean isInitializingAssignmentAt(int index) { if (index < references.size() && index > 0) { Reference maybeDecl = references.get(index-1); if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } } } return false; } /** * @return The reference that provides the value for the variable at the * time of the first read, if known, otherwise null. * * This is either the variable declaration ("var a = ...") or first * reference following the declaration if it is an assignment. */ Reference getInitializingReference() { if (isInitializingDeclarationAt(0)) { return references.get(0); } else if (isInitializingAssignmentAt(1)) { return references.get(1); } return null; } /** * Constants are allowed to be defined after their first use. */ Reference getInitializingReferenceForConstants() { int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. for (BasicBlock block = ref.getBasicBlock(); block != null; block = block.getParent()) { if (block.isFunction) { break; } else if (block.isLoop) { return false; } } return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { if (assignment == null) { assignment = ref; } else { return null; } } } return assignment; } /** * @return Whether the variable is never assigned a value. */ boolean isNeverAssigned() { int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { return false; } } return true; } boolean firstReferenceIsAssigningDeclaration() { int size = references.size(); if (size > 0 && references.get(0).isInitializingDeclaration()) { return true; } return false; } } /** * Represents a single declaration or reference to a variable. */ static final class Reference implements StaticReference { private static final Set DECLARATION_PARENTS = ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); private final Node nameNode; private final BasicBlock basicBlock; private final Scope scope; private final InputId inputId; private final StaticSourceFile sourceFile; Reference(Node nameNode, NodeTraversal t, BasicBlock basicBlock) { this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId()); } // Bleeding functions are weird, because the declaration does // not appear inside their scope. So they need their own constructor. static Reference newBleedingFunction(NodeTraversal t, BasicBlock basicBlock, Node func) { return new Reference(func.getFirstChild(), basicBlock, t.getScope(), t.getInput().getInputId()); } /** * Creates a variable reference in a given script file name, used in tests. * * @return The created reference. */ @VisibleForTesting static Reference createRefForTest(CompilerInput input) { return new Reference(new Node(Token.NAME), null, null, input.getInputId()); } private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) { this.nameNode = nameNode; this.basicBlock = basicBlock; this.scope = scope; this.inputId = inputId; this.sourceFile = nameNode.getStaticSourceFile(); } /** * Makes a copy of the current reference using a new Scope instance. */ Reference cloneWithNewScope(Scope newScope) { return new Reference(nameNode, basicBlock, newScope, inputId); } @Override public Var getSymbol() { return scope.getVar(nameNode.getString()); } @Override public Node getNode() { return nameNode; } public InputId getInputId() { return inputId; } @Override public StaticSourceFile getSourceFile() { return sourceFile; } boolean isDeclaration() { Node parent = getParent(); Node grandparent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType()) || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && !getParent().isVar() || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue(nameNode); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); this.isFunction = root.isFunction(); if (root.getParent() != null) { int pType = root.getParent().getType(); this.isLoop = pType == Token.DO || pType == Token.WHILE || pType == Token.FOR; } else { this.isLoop = false; } } BasicBlock getParent() { return parent; } /** * Determines whether this block is equivalent to the very first block that * is created when reference collection traversal enters global scope. Note * that when traversing a single script in a hot-swap fashion a new instance * of {@code BasicBlock} is created. * * @return true if this is global scope block. */ boolean isGlobalScopeBlock() { return getParent() == null; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If thatBlock is a descendant of this block, and there are no hoisted // blocks between them, then this block must start before thatBlock. BasicBlock currentBlock; for (currentBlock = thatBlock; currentBlock != null && currentBlock != this; currentBlock = currentBlock.getParent()) { if (currentBlock.isHoisted) { return false; } } if (currentBlock == this) { return true; } if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) { return true; } return false; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReplaceCssNames.java0000644000175000017500000002152612115204405026515 0ustar apoapo/* * 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 static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * ReplaceCssNames replaces occurrences of goog.getCssName('foo') with * a shorter version from the passed in renaming map. There are two * styles of operation: for 'BY_WHOLE' we look up the whole string in the * renaming map. For 'BY_PART', all the class name's components, * separated by '-', are renamed individually and then recombined. * * Given the renaming map: * { * once: 'a', * upon: 'b', * atime: 'c', * long: 'd', * time: 'e', * ago: 'f' * } * * The following outputs are expected with the 'BY_PART' renaming style: * * goog.getCssName('once') -> 'a' * goog.getCssName('once-upon-atime') -> 'a-b-c' * * var baseClass = goog.getCssName('long-time'); * el.className = goog.getCssName(baseClass, 'ago'); * -> * var baseClass = 'd-e'; * el.className = baseClass + '-f'; * * However if we have the following renaming map with the 'BY_WHOLE' renaming style: * { * once: 'a', * upon-atime: 'b', * long-time: 'c', * ago: 'd' * } * * Then we would expect: * * goog.getCssName('once') -> 'a' * * var baseClass = goog.getCssName('long-time'); * el.className = goog.getCssName(baseClass, 'ago'); * -> * var baseClass = 'c'; * el.className = baseClass + '-d'; * * In addition, the CSS names before replacement can optionally be gathered. * */ class ReplaceCssNames implements CompilerPass { static final String GET_CSS_NAME_FUNCTION = "goog.getCssName"; static final DiagnosticType INVALID_NUM_ARGUMENTS_ERROR = DiagnosticType.error("JSC_GETCSSNAME_NUM_ARGS", "goog.getCssName called with \"{0}\" arguments, expected 1 or 2."); static final DiagnosticType STRING_LITERAL_EXPECTED_ERROR = DiagnosticType.error("JSC_GETCSSNAME_STRING_LITERAL_EXPECTED", "goog.getCssName called with invalid argument, string literal " + "expected. Was \"{0}\"."); static final DiagnosticType UNEXPECTED_STRING_LITERAL_ERROR = DiagnosticType.error("JSC_GETCSSNAME_UNEXPECTED_STRING_LITERAL", "goog.getCssName called with invalid arguments, string literal " + "passed as first of two arguments. Did you mean " + "goog.getCssName(\"{0}-{1}\")?"); static final DiagnosticType UNKNOWN_SYMBOL_WARNING = DiagnosticType.warning("JSC_GETCSSNAME_UNKNOWN_CSS_SYMBOL", "goog.getCssName called with unrecognized symbol \"{0}\" in class " + "\"{1}\"."); private final AbstractCompiler compiler; private final Map cssNames; private CssRenamingMap symbolMap; private final Set whitelist; private final JSType nativeStringType; ReplaceCssNames(AbstractCompiler compiler, @Nullable Map cssNames, @Nullable Set whitelist) { this.compiler = compiler; this.cssNames = cssNames; this.whitelist = whitelist; this.nativeStringType = compiler.getTypeRegistry() .getNativeType(STRING_TYPE); } @Override public void process(Node externs, Node root) { // The CssRenamingMap may not have been available from the compiler when // this ReplaceCssNames pass was constructed, so getCssRenamingMap() should // only be called before this pass is actually run. symbolMap = getCssRenamingMap(); NodeTraversal.traverse(compiler, root, new Traversal()); } @VisibleForTesting protected CssRenamingMap getCssRenamingMap() { return compiler.getCssRenamingMap(); } private class Traversal extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall() && GET_CSS_NAME_FUNCTION.equals(n.getFirstChild().getQualifiedName())) { int count = n.getChildCount(); Node first = n.getFirstChild().getNext(); switch (count) { case 2: // Replace the function call with the processed argument. if (first.isString()) { processStringNode(t, first); n.removeChild(first); parent.replaceChild(n, first); compiler.reportCodeChange(); } else { compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, Token.name(first.getType()))); } break; case 3: // Replace function call with concatenation of two args. It's // assumed the first arg has already been processed. Node second = first.getNext(); if (!second.isString()) { compiler.report(t.makeError(n, STRING_LITERAL_EXPECTED_ERROR, Token.name(second.getType()))); } else if (first.isString()) { compiler.report(t.makeError( n, UNEXPECTED_STRING_LITERAL_ERROR, first.getString(), second.getString())); } else { processStringNode(t, second); n.removeChild(first); Node replacement = IR.add(first, IR.string("-" + second.getString()) .copyInformationFrom(second)) .copyInformationFrom(n); replacement.setJSType(nativeStringType); parent.replaceChild(n, replacement); compiler.reportCodeChange(); } break; default: compiler.report(t.makeError( n, INVALID_NUM_ARGUMENTS_ERROR, String.valueOf(count))); } } } /** * Processes a string argument to goog.getCssName(). The string will be * renamed based off the symbol map. If there is no map or any part of the * name can't be renamed, a warning is reported to the compiler and the node * is left unchanged. * * If the type is unexpected then an error is reported to the compiler. * * @param t The node traversal. * @param n The string node to process. */ private void processStringNode(NodeTraversal t, Node n) { String name = n.getString(); if (whitelist != null && whitelist.contains(name)) { // We apply the whitelist before splitting on dashes, and not after. // External substitution maps should do the same. return; } String[] parts = name.split("-"); if (symbolMap != null) { String replacement = null; switch (symbolMap.getStyle()) { case BY_WHOLE: replacement = symbolMap.get(name); if (replacement == null) { compiler.report( t.makeError(n, UNKNOWN_SYMBOL_WARNING, name, name)); return; } break; case BY_PART: String[] replaced = new String[parts.length]; for (int i = 0; i < parts.length; i++) { String part = symbolMap.get(parts[i]); if (part == null) { // If we can't encode all parts, don't encode any of it. compiler.report( t.makeError(n, UNKNOWN_SYMBOL_WARNING, parts[i], name)); return; } replaced[i] = part; } replacement = Joiner.on("-").join(replaced); break; default: throw new IllegalStateException( "Unknown replacement style: " + symbolMap.getStyle()); } n.setString(replacement); } if (cssNames != null) { // We still want to collect statistics even if we've already // done the full replace. The statistics are collected on a // per-part basis. for (int i = 0; i < parts.length; i++) { Integer count = cssNames.get(parts[i]); if (count == null) { count = Integer.valueOf(0); } cssNames.put(parts[i], count.intValue() + 1); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java0000644000175000017500000020234512115204405030551 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.javascript.jscomp.CompilerOptions.TweakProcessing; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.TokenStream; import com.google.protobuf.CodedOutputStream; import java.io.BufferedWriter; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.Flushable; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import javax.annotation.Nullable; /** * Implementations of AbstractCommandLineRunner translate flags into Java * API calls on the Compiler. AbstractCompiler contains common flags and logic * to make that happen. * * This class may be extended and used to create other Java classes * that behave the same as running the Compiler from the command line. Example: * *
 * class MyCommandLineRunner extends
 *     AbstractCommandLineRunner {
 *   MyCommandLineRunner(String[] args) {
 *     super(args);
 *   }
 *
 *   @Override
 *   protected MyOptions createOptions() {
 *     MyOptions options = new MyOptions();
 *     CompilerFlagTranslator.setOptionsFromFlags(options);
 *     addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
 *     return options;
 *   }
 *
 *   @Override
 *   protected MyCompiler createCompiler() {
 *     return new MyCompiler();
 *   }
 *
 *   public static void main(String[] args) {
 *     (new MyCommandLineRunner(args)).run();
 *   }
 * }
 * 
* * @author bolinfest@google.com (Michael Bolin) */ abstract class AbstractCommandLineRunner { static final DiagnosticType OUTPUT_SAME_AS_INPUT_ERROR = DiagnosticType.error( "JSC_OUTPUT_SAME_AS_INPUT_ERROR", "Bad output file (already listed as input file): {0}"); private final CommandLineConfig config; private Appendable jsOutput; private final PrintStream err; private A compiler; private Charset inputCharset; // NOTE(nicksantos): JSCompiler has always used ASCII as the default // output charset. This was done to solve legacy problems with // bad proxies, etc. We are not sure if these issues are still problems, // and changing the encoding would require a big obnoxious migration plan. // // New outputs should use outputCharset2, which is how I would have // designed this if I had a time machine. private Charset outputCharset2; private String legacyOutputCharset; private boolean testMode = false; private Supplier> externsSupplierForTesting = null; private Supplier> inputsSupplierForTesting = null; private Supplier> modulesSupplierForTesting = null; private Function exitCodeReceiverForTesting = null; private Map rootRelativePathsMap = null; private Map parsedModuleWrappers = null; // Bookkeeping to measure optimal phase orderings. private static final int NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER = 100; private static final String OUTPUT_MARKER = "%output%"; private static final String OUTPUT_MARKER_JS_STRING = "%output|jsstring%"; private final RunTimeStats runTimeStats = new RunTimeStats(); AbstractCommandLineRunner() { this(System.out, System.err); } AbstractCommandLineRunner(PrintStream out, PrintStream err) { this.config = new CommandLineConfig(); this.jsOutput = Preconditions.checkNotNull(out); this.err = Preconditions.checkNotNull(err); } /** * Put the command line runner into test mode. In test mode, * all outputs will be blackholed. * @param externsSupplier A provider for externs. * @param inputsSupplier A provider for source inputs. * @param modulesSupplier A provider for modules. Only one of inputsSupplier * and modulesSupplier may be non-null. * @param exitCodeReceiver A receiver for the status code that would * have been passed to System.exit in non-test mode. */ @VisibleForTesting void enableTestMode( Supplier> externsSupplier, Supplier> inputsSupplier, Supplier> modulesSupplier, Function exitCodeReceiver) { Preconditions.checkArgument( inputsSupplier == null ^ modulesSupplier == null); testMode = true; this.externsSupplierForTesting = externsSupplier; this.inputsSupplierForTesting = inputsSupplier; this.modulesSupplierForTesting = modulesSupplier; this.exitCodeReceiverForTesting = exitCodeReceiver; } /** * Returns whether we're in test mode. */ protected boolean isInTestMode() { return testMode; } /** * Get the command line config, so that it can be initialized. */ protected CommandLineConfig getCommandLineConfig() { return config; } /** * Returns the instance of the Compiler to use when {@link #run()} is * called. */ protected abstract A createCompiler(); /** * Returns the instance of the Options to use when {@link #run()} is called. * createCompiler() is called before createOptions(), so getCompiler() * will not return null when createOptions() is called. */ protected abstract B createOptions(); /** * The warning classes that are available from the command-line. */ protected DiagnosticGroups getDiagnosticGroups() { if (compiler == null) { return new DiagnosticGroups(); } return compiler.getDiagnosticGroups(); } /** * A helper function for creating the dependency options object. */ static DependencyOptions createDependencyOptions( boolean manageClosureDependencies, boolean onlyClosureDependencies, boolean processCommonJSModules, List closureEntryPoints) throws FlagUsageException { if (onlyClosureDependencies) { if (closureEntryPoints.isEmpty()) { throw new FlagUsageException("When only_closure_dependencies is " + "on, you must specify at least one closure_entry_point"); } return new DependencyOptions() .setDependencyPruning(true) .setDependencySorting(true) .setMoocherDropping(true) .setEntryPoints(closureEntryPoints); } else if (processCommonJSModules) { return new DependencyOptions() .setDependencyPruning(false) .setDependencySorting(true) .setMoocherDropping(false) .setEntryPoints(closureEntryPoints); } else if (manageClosureDependencies || closureEntryPoints.size() > 0) { return new DependencyOptions() .setDependencyPruning(true) .setDependencySorting(true) .setMoocherDropping(false) .setEntryPoints(closureEntryPoints); } return null; } /** * Sets options based on the configurations set flags API. * Called during the run() run() method. * If you want to ignore the flags API, or interpret flags your own way, * then you should override this method. */ protected void setRunOptions(CompilerOptions options) throws FlagUsageException, IOException { DiagnosticGroups diagnosticGroups = getDiagnosticGroups(); if (config.warningGuards != null) { for (WarningGuardSpec.Entry entry : config.warningGuards.entries) { diagnosticGroups.setWarningLevel(options, entry.groupName, entry.level); } } if (!config.warningsWhitelistFile.isEmpty()) { options.addWarningsGuard( WhitelistWarningsGuard.fromFile( new File(config.warningsWhitelistFile))); } createDefineOrTweakReplacements(config.define, options, false); options.setTweakProcessing(config.tweakProcessing); createDefineOrTweakReplacements(config.tweak, options, true); DependencyOptions depOptions = createDependencyOptions( config.manageClosureDependencies, config.onlyClosureDependencies, config.processCommonJSModules, config.closureEntryPoints); if (depOptions != null) { options.setDependencyOptions(depOptions); } options.devMode = config.jscompDevMode; options.setCodingConvention(config.codingConvention); options.setSummaryDetailLevel(config.summaryDetailLevel); options.setTrustedStrings(true); legacyOutputCharset = options.outputCharset = getLegacyOutputCharset(); outputCharset2 = getOutputCharset2(); inputCharset = getInputCharset(); if (config.jsOutputFile.length() > 0) { if (config.skipNormalOutputs) { throw new FlagUsageException("skip_normal_outputs and js_output_file" + " cannot be used together."); } } if (config.skipNormalOutputs && config.printAst) { throw new FlagUsageException("skip_normal_outputs and print_ast cannot" + " be used together."); } if (config.skipNormalOutputs && config.printTree) { throw new FlagUsageException("skip_normal_outputs and print_tree cannot" + " be used together."); } if (config.createSourceMap.length() > 0) { options.sourceMapOutputPath = config.createSourceMap; } options.sourceMapDetailLevel = config.sourceMapDetailLevel; options.sourceMapFormat = config.sourceMapFormat; if (!config.variableMapInputFile.equals("")) { options.inputVariableMap = VariableMap.load(config.variableMapInputFile); } if (!config.propertyMapInputFile.equals("")) { options.inputPropertyMap = VariableMap.load(config.propertyMapInputFile); } if (config.languageIn.length() > 0) { CompilerOptions.LanguageMode languageMode = CompilerOptions.LanguageMode.fromString(config.languageIn); if (languageMode != null) { options.setLanguageIn(languageMode); } else { throw new FlagUsageException("Unknown language `" + config.languageIn + "' specified."); } } if (!config.outputManifests.isEmpty()) { Set uniqueNames = Sets.newHashSet(); for (String filename : config.outputManifests) { if (!uniqueNames.add(filename)) { throw new FlagUsageException("output_manifest flags specify " + "duplicate file names: " + filename); } } } if (!config.outputBundles.isEmpty()) { Set uniqueNames = Sets.newHashSet(); for (String filename : config.outputBundles) { if (!uniqueNames.add(filename)) { throw new FlagUsageException("output_bundle flags specify " + "duplicate file names: " + filename); } } } options.acceptConstKeyword = config.acceptConstKeyword; options.transformAMDToCJSModules = config.transformAMDToCJSModules; options.processCommonJSModules = config.processCommonJSModules; options.commonJSModulePathPrefix = config.commonJSModulePathPrefix; options.angularPass = config.angularPass; } final protected A getCompiler() { return compiler; } /** * Runs the Compiler and calls System.exit() with the exit status of the * compiler. */ final public void run() { int result = 0; int runs = 1; if (config.computePhaseOrdering) { runs = NUM_RUNS_TO_DETERMINE_OPTIMAL_ORDER; PhaseOptimizer.randomizeLoops(); } try { for (int i = 0; i < runs && result == 0; i++) { runTimeStats.recordStartRun(); result = doRun(); runTimeStats.recordEndRun(); } } catch (AbstractCommandLineRunner.FlagUsageException e) { System.err.println(e.getMessage()); result = -1; } catch (Throwable t) { t.printStackTrace(); result = -2; } if (config.computePhaseOrdering) { runTimeStats.outputBestPhaseOrdering(); } try { if (jsOutput instanceof Closeable) { ((Closeable) jsOutput).close(); } } catch (IOException e) { throw Throwables.propagate(e); } if (testMode) { exitCodeReceiverForTesting.apply(result); } else { System.exit(result); } } /** * Returns the PrintStream for writing errors associated with this * AbstractCommandLineRunner. */ protected PrintStream getErrorPrintStream() { return err; } /** * An exception thrown when command-line flags are used incorrectly. */ public static class FlagUsageException extends Exception { private static final long serialVersionUID = 1L; public FlagUsageException(String message) { super(message); } } /** * Creates inputs from a list of files. * * Can be overridden by subclasses who want to pull files from different * places. * * @param files A list of filenames * @param allowStdIn Whether '-' is allowed appear as a filename to represent * stdin. If true, '-' is only allowed to appear once. * @return An array of inputs */ protected List createInputs(List files, boolean allowStdIn) throws FlagUsageException, IOException { List inputs = new ArrayList(files.size()); boolean usingStdin = false; for (String filename : files) { if (!"-".equals(filename)) { SourceFile newFile = SourceFile.fromFile(filename, inputCharset); inputs.add(newFile); } else { if (!allowStdIn) { throw new FlagUsageException("Can't specify stdin."); } if (usingStdin) { throw new FlagUsageException("Can't specify stdin twice."); } if (!config.outputManifests.isEmpty()) { throw new FlagUsageException("Manifest files cannot be generated " + "when the input is from stdin."); } if (!config.outputBundles.isEmpty()) { throw new FlagUsageException("Bundle files cannot be generated " + "when the input is from stdin."); } inputs.add(SourceFile.fromInputStream("stdin", System.in)); usingStdin = true; } } return inputs; } /** * Creates JS source code inputs from a list of files. */ private List createSourceInputs(List files) throws FlagUsageException, IOException { if (isInTestMode()) { return inputsSupplierForTesting.get(); } if (files.isEmpty()) { files = Collections.singletonList("-"); } try { return createInputs(files, true); } catch (FlagUsageException e) { throw new FlagUsageException("Bad --js flag. " + e.getMessage()); } } /** * Creates JS extern inputs from a list of files. */ private List createExternInputs(List files) throws FlagUsageException, IOException { if (files.isEmpty()) { return ImmutableList.of(SourceFile.fromCode("/dev/null", "")); } try { return createInputs(files, false); } catch (FlagUsageException e) { throw new FlagUsageException("Bad --externs flag. " + e.getMessage()); } } /** * Creates module objects from a list of module specifications. * * @param specs A list of module specifications, not null or empty. The spec * format is: name:num-js-files[:[dep,...][:]]. Module * names must not contain the ':' character. * @param jsFiles A list of JS file paths, not null * @return An array of module objects */ List createJsModules( List specs, List jsFiles) throws FlagUsageException, IOException { if (isInTestMode()) { return modulesSupplierForTesting.get(); } Preconditions.checkState(specs != null); Preconditions.checkState(!specs.isEmpty()); Preconditions.checkState(jsFiles != null); final int totalNumJsFiles = jsFiles.size(); int nextJsFileIndex = 0; Map modulesByName = Maps.newLinkedHashMap(); for (String spec : specs) { // Format is ":[:[,...][:]]". String[] parts = spec.split(":"); if (parts.length < 2 || parts.length > 4) { throw new FlagUsageException("Expected 2-4 colon-delimited parts in " + "module spec: " + spec); } // Parse module name. String name = parts[0]; checkModuleName(name); if (modulesByName.containsKey(name)) { throw new FlagUsageException("Duplicate module name: " + name); } JSModule module = new JSModule(name); // Parse module inputs. int numJsFiles = -1; try { numJsFiles = Integer.parseInt(parts[1]); } catch (NumberFormatException ignored) { numJsFiles = -1; } // We will allow modules of zero input. if (numJsFiles < 0) { throw new FlagUsageException("Invalid JS file count '" + parts[1] + "' for module: " + name); } if (nextJsFileIndex + numJsFiles > totalNumJsFiles) { throw new FlagUsageException("Not enough JS files specified. Expected " + (nextJsFileIndex + numJsFiles - totalNumJsFiles) + " more in module:" + name); } List moduleJsFiles = jsFiles.subList(nextJsFileIndex, nextJsFileIndex + numJsFiles); for (SourceFile input : createInputs(moduleJsFiles, false)) { module.add(input); } nextJsFileIndex += numJsFiles; if (parts.length > 2) { // Parse module dependencies. String depList = parts[2]; if (depList.length() > 0) { String[] deps = depList.split(","); for (String dep : deps) { JSModule other = modulesByName.get(dep); if (other == null) { throw new FlagUsageException("Module '" + name + "' depends on unknown module '" + dep + "'. Be sure to list modules in dependency order."); } module.addDependency(other); } } } modulesByName.put(name, module); } if (nextJsFileIndex < totalNumJsFiles) { throw new FlagUsageException("Too many JS files specified. Expected " + nextJsFileIndex + " but found " + totalNumJsFiles); } return Lists.newArrayList(modulesByName.values()); } /** * Validates the module name. Can be overridden by subclasses. * @param name The module name * @throws FlagUsageException if the validation fails */ protected void checkModuleName(String name) throws FlagUsageException { if (!TokenStream.isJSIdentifier(name)) { throw new FlagUsageException("Invalid module name: '" + name + "'"); } } /** * Parses module wrapper specifications. * * @param specs A list of module wrapper specifications, not null. The spec * format is: name:wrapper. Wrappers. * @param modules The JS modules whose wrappers are specified * @return A map from module name to module wrapper. Modules with no wrapper * will have the empty string as their value in this map. */ static Map parseModuleWrappers(List specs, List modules) throws FlagUsageException { Preconditions.checkState(specs != null); Map wrappers = Maps.newHashMapWithExpectedSize(modules.size()); // Prepopulate the map with module names. for (JSModule m : modules) { wrappers.put(m.getName(), ""); } for (String spec : specs) { // Format is ":". int pos = spec.indexOf(':'); if (pos == -1) { throw new FlagUsageException("Expected module wrapper to have " + ": format: " + spec); } // Parse module name. String name = spec.substring(0, pos); if (!wrappers.containsKey(name)) { throw new FlagUsageException("Unknown module: '" + name + "'"); } String wrapper = spec.substring(pos + 1); if (!wrapper.contains("%s")) { throw new FlagUsageException("No %s placeholder in module wrapper: '" + wrapper + "'"); } wrappers.put(name, wrapper); } return wrappers; } private String getModuleOutputFileName(JSModule m) { return config.moduleOutputPathPrefix + m.getName() + ".js"; } @VisibleForTesting void writeModuleOutput(Appendable out, JSModule m) throws FlagUsageException, IOException { if (parsedModuleWrappers == null) { parsedModuleWrappers = parseModuleWrappers( config.moduleWrapper, Lists.newArrayList( compiler.getDegenerateModuleGraph().getAllModules())); } String fileName = getModuleOutputFileName(m); String baseName = new File(fileName).getName(); writeOutput(out, compiler, compiler.toSource(m), parsedModuleWrappers.get(m.getName()).replace("%basename%", baseName), "%s", null); } /** * Writes code to an output stream, optionally wrapping it in an arbitrary * wrapper that contains a placeholder where the code should be inserted. */ static void writeOutput(Appendable out, Compiler compiler, String code, String wrapper, String codePlaceholder, @Nullable Function escaper) throws IOException { int pos = wrapper.indexOf(codePlaceholder); if (pos != -1) { String prefix = ""; if (pos > 0) { prefix = wrapper.substring(0, pos); out.append(prefix); } out.append(escaper == null ? code : escaper.apply(code)); int suffixStart = pos + codePlaceholder.length(); if (suffixStart != wrapper.length()) { // Something after placeholder? out.append(wrapper.substring(suffixStart)); } // Make sure we always end output with a line feed. out.append('\n'); // If we have a source map, adjust its offsets to match // the code WITHIN the wrapper. if (compiler != null && compiler.getSourceMap() != null) { compiler.getSourceMap().setWrapperPrefix(prefix); } } else { out.append(code); out.append('\n'); } } /** * Creates any directories necessary to write a file that will have a given * path prefix. */ private static void maybeCreateDirsForPath(String pathPrefix) { if (pathPrefix.length() > 0) { String dirName = pathPrefix.charAt(pathPrefix.length() - 1) == File.separatorChar ? pathPrefix.substring(0, pathPrefix.length() - 1) : new File( pathPrefix).getParent(); if (dirName != null) { new File(dirName).mkdirs(); } } } /** * Parses command-line arguments and runs the compiler. * * @return system exit status */ protected int doRun() throws FlagUsageException, IOException { Compiler.setLoggingLevel(Level.parse(config.loggingLevel)); List externs = createExterns(); compiler = createCompiler(); B options = createOptions(); List modules = null; Result result = null; setRunOptions(options); boolean writeOutputToFile = !config.jsOutputFile.isEmpty(); List outputFileNames = Lists.newArrayList(); if (writeOutputToFile) { outputFileNames.add(config.jsOutputFile); jsOutput = fileNameToLegacyOutputWriter(config.jsOutputFile); } else if (jsOutput instanceof OutputStream) { jsOutput = streamToLegacyOutputWriter((OutputStream) jsOutput); } List jsFiles = config.js; List moduleSpecs = config.module; boolean createCommonJsModules = false; if (options.processCommonJSModules) { if (moduleSpecs.size() == 1 && "auto".equals(moduleSpecs.get(0))) { createCommonJsModules = true; moduleSpecs.remove(0); } } if (!moduleSpecs.isEmpty()) { modules = createJsModules(moduleSpecs, jsFiles); for (JSModule m : modules) { outputFileNames.add(getModuleOutputFileName(m)); } if (config.skipNormalOutputs) { compiler.initModules(externs, modules, options); } else { result = compiler.compileModules(externs, modules, options); } } else { List inputs = createSourceInputs(jsFiles); if (config.skipNormalOutputs) { compiler.init(externs, inputs, options); } else { result = compiler.compile(externs, inputs, options); } } if (createCommonJsModules) { // For CommonJS modules construct modules from actual inputs. modules = Lists.newArrayList(compiler.getDegenerateModuleGraph() .getAllModules()); for (JSModule m : modules) { outputFileNames.add(getModuleOutputFileName(m)); } } for (String outputFileName : outputFileNames) { if (compiler.getSourceFileByName(outputFileName) != null) { compiler.report( JSError.make(OUTPUT_SAME_AS_INPUT_ERROR, outputFileName)); return 1; } } int errCode = processResults(result, modules, options); // Flush the output if we are writing to a file. // We can't close yet, because we may need to write phase ordering // info to it later. if (jsOutput instanceof Flushable) { ((Flushable) jsOutput).flush(); } return errCode; } /** * Processes the results of the compile job, and returns an error code. */ int processResults(Result result, List modules, B options) throws FlagUsageException, IOException { if (config.computePhaseOrdering) { return 0; } if (config.printPassGraph) { if (compiler.getRoot() == null) { return 1; } else { jsOutput.append( DotFormatter.toDot(compiler.getPassConfig().getPassGraph())); jsOutput.append('\n'); return 0; } } if (config.printAst) { if (compiler.getRoot() == null) { return 1; } else { ControlFlowGraph cfg = compiler.computeCFG(); DotFormatter.appendDot( compiler.getRoot().getLastChild(), cfg, jsOutput); jsOutput.append('\n'); return 0; } } if (config.printTree) { if (compiler.getRoot() == null) { jsOutput.append("Code contains errors; no tree was generated.\n"); return 1; } else { compiler.getRoot().appendStringTree(jsOutput); jsOutput.append("\n"); return 0; } } rootRelativePathsMap = constructRootRelativePathsMap(); if (config.skipNormalOutputs) { // Output the manifest and bundle files if requested. outputManifest(); outputBundle(); outputModuleGraphJson(); return 0; } else if (result.success) { outputModuleGraphJson(); if (modules == null) { outputSingleBinary(); // Output the source map if requested. outputSourceMap(options, config.jsOutputFile); } else { outputModuleBinaryAndSourceMaps(modules, options); } // Output the externs if required. if (options.externExportsPath != null) { Writer eeOut = openExternExportsStream(options, config.jsOutputFile); eeOut.append(result.externExport); eeOut.close(); } // Output the variable and property name maps if requested. outputNameMaps(); // Output the manifest and bundle files if requested. outputManifest(); outputBundle(); } // return 0 if no errors, the error count otherwise return Math.min(result.errors.length, 0x7f); } Function getJavascriptEscaper() { throw new UnsupportedOperationException( "SourceCodeEscapers is not in the standard release of Guava yet :("); } void outputSingleBinary() throws IOException { Function escaper = null; String marker = OUTPUT_MARKER; if (config.outputWrapper.contains(OUTPUT_MARKER_JS_STRING)) { marker = OUTPUT_MARKER_JS_STRING; escaper = getJavascriptEscaper(); } writeOutput( jsOutput, compiler, compiler.toSource(), config.outputWrapper, marker, escaper); } private void outputModuleBinaryAndSourceMaps( List modules, B options) throws FlagUsageException, IOException { parsedModuleWrappers = parseModuleWrappers( config.moduleWrapper, modules); maybeCreateDirsForPath(config.moduleOutputPathPrefix); // If the source map path is in fact a pattern for each // module, create a stream per-module. Otherwise, create // a single source map. Writer mapOut = null; if (!shouldGenerateMapPerModule(options)) { mapOut = fileNameToOutputWriter2(expandSourceMapPath(options, null)); } for (JSModule m : modules) { if (shouldGenerateMapPerModule(options)) { mapOut = fileNameToOutputWriter2(expandSourceMapPath(options, m)); } Writer writer = fileNameToLegacyOutputWriter(getModuleOutputFileName(m)); if (options.sourceMapOutputPath != null) { compiler.getSourceMap().reset(); } writeModuleOutput(writer, m); if (options.sourceMapOutputPath != null) { compiler.getSourceMap().appendTo(mapOut, m.getName()); } writer.close(); if (shouldGenerateMapPerModule(options) && mapOut != null) { mapOut.close(); mapOut = null; } } if (mapOut != null) { mapOut.close(); } } /** * Query the flag for the input charset, and return a Charset object * representing the selection. * * @return Charset to use when reading inputs * @throws FlagUsageException if flag is not a valid Charset name. */ private Charset getInputCharset() throws FlagUsageException { if (!config.charset.isEmpty()) { if (!Charset.isSupported(config.charset)) { throw new FlagUsageException(config.charset + " is not a valid charset name."); } return Charset.forName(config.charset); } return Charsets.UTF_8; } /** * Query the flag for the output charset. * * Let the outputCharset be the same as the input charset... except if * we're reading in UTF-8 by default. By tradition, we've always * output ASCII to avoid various hiccups with different browsers, * proxies and firewalls. * * @return Name of the charset to use when writing outputs. Guaranteed to * be a supported charset. * @throws FlagUsageException if flag is not a valid Charset name. */ private String getLegacyOutputCharset() throws FlagUsageException { if (!config.charset.isEmpty()) { if (!Charset.isSupported(config.charset)) { throw new FlagUsageException(config.charset + " is not a valid charset name."); } return config.charset; } return "US-ASCII"; } /** * Query the flag for the output charset. Defaults to UTF-8. * @throws FlagUsageException if flag is not a valid Charset name. */ private Charset getOutputCharset2() throws FlagUsageException { if (!config.charset.isEmpty()) { if (!Charset.isSupported(config.charset)) { throw new FlagUsageException(config.charset + " is not a valid charset name."); } return Charset.forName(config.charset); } return Charsets.UTF_8; } protected List createExterns() throws FlagUsageException, IOException { return isInTestMode() ? externsSupplierForTesting.get() : createExternInputs(config.externs); } /** * Returns true if and only if a source map file should be generated for each * module, as opposed to one unified map. This is specified by having the * source map pattern include the %outname% variable. */ private boolean shouldGenerateMapPerModule(B options) { return options.sourceMapOutputPath != null && options.sourceMapOutputPath.contains("%outname%"); } /** * Returns a stream for outputting the generated externs file. * * @param options The options to the Compiler. * @param path The path of the generated JS source file. * * @return The stream or null if no extern-ed exports are being generated. */ private Writer openExternExportsStream(B options, String path) throws IOException { if (options.externExportsPath == null) { return null; } String exPath = options.externExportsPath; if (!exPath.contains(File.separator)) { File outputFile = new File(path); exPath = outputFile.getParent() + File.separatorChar + exPath; } return fileNameToOutputWriter2(exPath); } /** * Expand a file path specified on the command-line. * * Most file paths on the command-line allow an %outname% placeholder. * The placeholder will expand to a different value depending on * the current output mode. There are three scenarios: * * 1) Single JS output, single extra output: sub in jsOutputPath. * 2) Multiple JS output, single extra output: sub in the base module name. * 3) Multiple JS output, multiple extra output: sub in the module output * file. * * Passing a JSModule to this function automatically triggers case #3. * Otherwise, we'll use strategy #1 or #2 based on the current output mode. */ private String expandCommandLinePath( String path, JSModule forModule) { String sub; if (forModule != null) { sub = config.moduleOutputPathPrefix + forModule.getName() + ".js"; } else if (!config.module.isEmpty()) { sub = config.moduleOutputPathPrefix; } else { sub = config.jsOutputFile; } return path.replace("%outname%", sub); } /** Expansion function for source map. */ @VisibleForTesting String expandSourceMapPath(B options, JSModule forModule) { if (Strings.isNullOrEmpty(options.sourceMapOutputPath)) { return null; } return expandCommandLinePath(options.sourceMapOutputPath, forModule); } /** * Converts a file name into a Writer taking in account the output charset. * Returns null if the file name is null. */ private Writer fileNameToLegacyOutputWriter(String fileName) throws IOException { if (fileName == null) { return null; } if (testMode) { return new StringWriter(); } return streamToLegacyOutputWriter(filenameToOutputStream(fileName)); } /** * Converts a file name into a Writer taking in account the output charset. * Returns null if the file name is null. */ private Writer fileNameToOutputWriter2(String fileName) throws IOException { if (fileName == null) { return null; } if (testMode) { return new StringWriter(); } return streamToOutputWriter2(filenameToOutputStream(fileName)); } /** * Converts a file name into a Outputstream. * Returns null if the file name is null. */ protected OutputStream filenameToOutputStream(String fileName) throws IOException { if (fileName == null){ return null; } return new FileOutputStream(fileName); } /** * Create a writer with the legacy output charset. */ private Writer streamToLegacyOutputWriter(OutputStream stream) throws IOException { if (legacyOutputCharset == null) { return new BufferedWriter( new OutputStreamWriter(stream)); } else { return new BufferedWriter( new OutputStreamWriter(stream, legacyOutputCharset)); } } /** * Create a writer with the newer output charset. */ private Writer streamToOutputWriter2(OutputStream stream) { if (outputCharset2 == null) { return new BufferedWriter( new OutputStreamWriter(stream)); } else { return new BufferedWriter( new OutputStreamWriter(stream, outputCharset2)); } } /** * Outputs the source map found in the compiler to the proper path if one * exists. * * @param options The options to the Compiler. */ private void outputSourceMap(B options, String associatedName) throws IOException { if (Strings.isNullOrEmpty(options.sourceMapOutputPath)) { return; } String outName = expandSourceMapPath(options, null); Writer out = fileNameToOutputWriter2(outName); compiler.getSourceMap().appendTo(out, associatedName); out.close(); } /** * Returns the path at which to output map file(s) based on the path at which * the JS binary will be placed. * * @return The path in which to place the generated map file(s). */ private String getMapPath(String outputFile) { String basePath = ""; if (outputFile.equals("")) { // If we have a js_module_binary rule, output the maps // at modulename_props_map.out, etc. if (!config.moduleOutputPathPrefix.equals("")) { basePath = config.moduleOutputPathPrefix; } else { basePath = "jscompiler"; } } else { // Get the path of the output file. File file = new File(outputFile); String outputFileName = file.getName(); // Strip the .js from the name. if (outputFileName.endsWith(".js")) { outputFileName = outputFileName.substring(0, outputFileName.length() - 3); } basePath = file.getParent() + File.separatorChar + outputFileName; } return basePath; } /** * Outputs the variable and property name maps for the specified compiler if * the proper FLAGS are set. */ private void outputNameMaps() throws FlagUsageException, IOException { String propertyMapOutputPath = null; String variableMapOutputPath = null; String functionInformationMapOutputPath = null; // Check the create_name_map_files FLAG. if (config.createNameMapFiles) { String basePath = getMapPath(config.jsOutputFile); propertyMapOutputPath = basePath + "_props_map.out"; variableMapOutputPath = basePath + "_vars_map.out"; functionInformationMapOutputPath = basePath + "_functions_map.out"; } // Check the individual FLAGS. if (!config.variableMapOutputFile.equals("")) { if (variableMapOutputPath != null) { throw new FlagUsageException("The flags variable_map_output_file and " + "create_name_map_files cannot both be used simultaniously."); } variableMapOutputPath = config.variableMapOutputFile; } if (!config.propertyMapOutputFile.equals("")) { if (propertyMapOutputPath != null) { throw new FlagUsageException("The flags property_map_output_file and " + "create_name_map_files cannot both be used simultaniously."); } propertyMapOutputPath = config.propertyMapOutputFile; } // Output the maps. if (variableMapOutputPath != null) { if (compiler.getVariableMap() != null) { compiler.getVariableMap().save(variableMapOutputPath); } } if (propertyMapOutputPath != null) { if (compiler.getPropertyMap() != null) { compiler.getPropertyMap().save(propertyMapOutputPath); } } if (functionInformationMapOutputPath != null) { if (compiler.getFunctionalInformationMap() != null) { OutputStream file = filenameToOutputStream(functionInformationMapOutputPath); CodedOutputStream outputStream = CodedOutputStream.newInstance(file); compiler.getFunctionalInformationMap().writeTo(outputStream); outputStream.flush(); file.flush(); file.close(); } } } /** * Create a map of constant names to constant values from a textual * description of the map. * * @param definitions A list of overriding definitions for defines in * the form [=], where is a number, boolean, or * single-quoted string without single quotes. */ @VisibleForTesting static void createDefineOrTweakReplacements(List definitions, CompilerOptions options, boolean tweaks) { // Parse the definitions for (String override : definitions) { String[] assignment = override.split("=", 2); String defName = assignment[0]; if (defName.length() > 0) { String defValue = assignment.length == 1 ? "true" : assignment[1]; boolean isTrue = defValue.equals("true"); boolean isFalse = defValue.equals("false"); if (isTrue || isFalse) { if (tweaks) { options.setTweakToBooleanLiteral(defName, isTrue); } else { options.setDefineToBooleanLiteral(defName, isTrue); } continue; } else if (defValue.length() > 1 && ((defValue.charAt(0) == '\'' && defValue.charAt(defValue.length() - 1) == '\'') || (defValue.charAt(0) == '\"' && defValue.charAt(defValue.length() - 1) == '\"'))) { // If the value starts and ends with a single quote, // we assume that it's a string. String maybeStringVal = defValue.substring(1, defValue.length() - 1); if (maybeStringVal.indexOf(defValue.charAt(0)) == -1) { if (tweaks) { options.setTweakToStringLiteral(defName, maybeStringVal); } else { options.setDefineToStringLiteral(defName, maybeStringVal); } continue; } } else { try { double value = Double.parseDouble(defValue); if (tweaks) { options.setTweakToDoubleLiteral(defName, value); } else { options.setDefineToDoubleLiteral(defName, value); } continue; } catch (NumberFormatException e) { // do nothing, it will be caught at the end } } } if (tweaks) { throw new RuntimeException( "--tweak flag syntax invalid: " + override); } throw new RuntimeException( "--define flag syntax invalid: " + override); } } /** * Returns true if and only if a manifest or bundle should be generated * for each module, as opposed to one unified manifest. */ private boolean shouldGenerateOutputPerModule(String output) { return !config.module.isEmpty() && output != null && output.contains("%outname%"); } private void outputManifest() throws IOException { outputManifestOrBundle(config.outputManifests, true); } private void outputBundle() throws IOException { outputManifestOrBundle(config.outputBundles, false); } /** * Writes the manifest or bundle of all compiler input files that survived * manage_closure_dependencies, if requested. */ private void outputManifestOrBundle(List outputFiles, boolean isManifest) throws IOException { if (outputFiles.isEmpty()) { return; } for (String output : outputFiles) { if (output.isEmpty()) { continue; } if (shouldGenerateOutputPerModule(output)) { // Generate per-module manifests or bundles JSModuleGraph graph = compiler.getDegenerateModuleGraph(); Iterable modules = graph.getAllModules(); for (JSModule module : modules) { Writer out = fileNameToOutputWriter2( expandCommandLinePath(output, module)); if (isManifest) { printManifestTo(module.getInputs(), out); } else { printBundleTo(module.getInputs(), out); } out.close(); } } else { // Generate a single file manifest or bundle. Writer out = fileNameToOutputWriter2( expandCommandLinePath(output, null)); if (config.module.isEmpty()) { if (isManifest) { printManifestTo(compiler.getInputsInOrder(), out); } else { printBundleTo(compiler.getInputsInOrder(), out); } } else { printModuleGraphManifestOrBundleTo( compiler.getDegenerateModuleGraph(), out, isManifest); } out.close(); } } } /** * Creates a file containing the current module graph in JSON serialization. */ private void outputModuleGraphJson() throws IOException { if (config.outputModuleDependencies != null && config.outputModuleDependencies != "") { Writer out = fileNameToOutputWriter2(config.outputModuleDependencies); printModuleGraphJsonTo(out); out.close(); } } /** * Prints the current module graph as JSON. */ @VisibleForTesting void printModuleGraphJsonTo(Appendable out) throws IOException { out.append(compiler.getDegenerateModuleGraph().toJson().toString()); } /** * Prints a set of modules to the manifest or bundle file. */ @VisibleForTesting void printModuleGraphManifestOrBundleTo(JSModuleGraph graph, Appendable out, boolean isManifest) throws IOException { Joiner commas = Joiner.on(","); boolean requiresNewline = false; for (JSModule module : graph.getAllModules()) { if (requiresNewline) { out.append("\n"); } if (isManifest) { // See CommandLineRunnerTest to see what the format of this // manifest looks like. String dependencies = commas.join(module.getSortedDependencyNames()); out.append( String.format("{%s%s}\n", module.getName(), dependencies.isEmpty() ? "" : ":" + dependencies)); printManifestTo(module.getInputs(), out); } else { printBundleTo(module.getInputs(), out); } requiresNewline = true; } } /** * Prints a list of input names (using root-relative paths), delimited by * newlines, to the manifest file. */ private void printManifestTo(Iterable inputs, Appendable out) throws IOException { for (CompilerInput input : inputs) { String rootRelativePath = rootRelativePathsMap.get(input.getName()); String displayName = rootRelativePath != null ? rootRelativePath : input.getName(); out.append(displayName); out.append("\n"); } } /** * Prints all the input contents, starting with a comment that specifies * the input file name (using root-relative paths) before each file. */ private void printBundleTo(Iterable inputs, Appendable out) throws IOException { for (CompilerInput input : inputs) { // Every module has an empty file in it. This makes it easier to implement // cross-module code motion. // // But it also leads to a weird edge case because // a) If we don't have a module spec, we create a singleton module, and // b) If we print a bundle file, we copy the original input files. // // This means that in the (rare) case where we have no inputs, and no // module spec, and we're printing a bundle file, we'll have a fake // input file that shouldn't be copied. So we special-case this, to // make all the other cases simpler. if (input.getName().equals( Compiler.createFillFileName(Compiler.SINGLETON_MODULE_NAME))) { Preconditions.checkState(1 == Iterables.size(inputs)); return; } String rootRelativePath = rootRelativePathsMap.get(input.getName()); String displayName = rootRelativePath != null ? rootRelativePath : input.getName(); File file = new File(input.getName()); out.append("//"); out.append(displayName); out.append("\n"); Files.copy(file, inputCharset, out); out.append("\n"); } } /** * Construct and return the input root path map. The key is the exec path of * each input file, and the value is the corresponding root relative path. */ private Map constructRootRelativePathsMap() { Map rootRelativePathsMap = Maps.newLinkedHashMap(); for (String mapString : config.manifestMaps) { int colonIndex = mapString.indexOf(':'); Preconditions.checkState(colonIndex > 0); String execPath = mapString.substring(0, colonIndex); String rootRelativePath = mapString.substring(colonIndex + 1); Preconditions.checkState(rootRelativePath.indexOf(':') == -1); rootRelativePathsMap.put(execPath, rootRelativePath); } return rootRelativePathsMap; } private class RunTimeStats { private long bestRunTime = Long.MAX_VALUE; private long worstRunTime = Long.MIN_VALUE; private long lastStartTime = 0; private List> loopedPassesInBestRun = null; /** * Record the start of a run. */ private void recordStartRun() { lastStartTime = System.currentTimeMillis(); PhaseOptimizer.clearLoopsRun(); } /** * Record the end of a run. */ private void recordEndRun() { long endTime = System.currentTimeMillis(); long length = endTime - lastStartTime; worstRunTime = Math.max(length, worstRunTime); if (length < bestRunTime) { loopedPassesInBestRun = PhaseOptimizer.getLoopsRun(); bestRunTime = length; } } /** * Print the best phase loop to stderr. */ private void outputBestPhaseOrdering() { try { jsOutput.append("Best time: " + bestRunTime + "\n"); jsOutput.append("Worst time: " + worstRunTime + "\n"); int i = 1; for (List loop : loopedPassesInBestRun) { jsOutput.append( "\nLoop " + i + ":\n" + Joiner.on("\n").join(loop)+ "\n"); i++; } } catch (IOException e) { throw new RuntimeException("unexpected exception", e); } } } /** * Configurations for the command line configs. Designed for easy * building, so that we can decouple the flags-parsing library from * the actual configuration options. * * By design, these configurations must match one-to-one with * command-line flags. */ static class CommandLineConfig { private boolean printTree = false; /** Prints out the parse tree and exits */ CommandLineConfig setPrintTree(boolean printTree) { this.printTree = printTree; return this; } private boolean computePhaseOrdering = false; /** * Runs the compile job many times, then prints out the best phase * ordering from this run */ CommandLineConfig setComputePhaseOrdering(boolean computePhaseOrdering) { this.computePhaseOrdering = computePhaseOrdering; return this; } private boolean printAst = false; /** * Prints a dot file describing the internal abstract syntax tree * and exits */ CommandLineConfig setPrintAst(boolean printAst) { this.printAst = printAst; return this; } private boolean printPassGraph = false; /** Prints a dot file describing the passes that will get run and exits */ CommandLineConfig setPrintPassGraph(boolean printPassGraph) { this.printPassGraph = printPassGraph; return this; } private CompilerOptions.DevMode jscompDevMode = CompilerOptions.DevMode.OFF; /** Turns on extra sanity checks */ CommandLineConfig setJscompDevMode(CompilerOptions.DevMode jscompDevMode) { this.jscompDevMode = jscompDevMode; return this; } private String loggingLevel = Level.WARNING.getName(); /** * The logging level (standard java.util.logging.Level * values) for Compiler progress. Does not control errors or * warnings for the JavaScript code under compilation */ CommandLineConfig setLoggingLevel(String loggingLevel) { this.loggingLevel = loggingLevel; return this; } private final List externs = Lists.newArrayList(); /** * The file containing JavaScript externs. You may specify multiple. */ CommandLineConfig setExterns(List externs) { this.externs.clear(); this.externs.addAll(externs); return this; } private final List js = Lists.newArrayList(); /** * The JavaScript filename. You may specify multiple. */ CommandLineConfig setJs(List js) { this.js.clear(); this.js.addAll(js); return this; } private String jsOutputFile = ""; /** * Primary output filename. If not specified, output is written to stdout */ CommandLineConfig setJsOutputFile(String jsOutputFile) { this.jsOutputFile = jsOutputFile; return this; } private final List module = Lists.newArrayList(); /** * A JavaScript module specification. The format is * :[:[,...][:]]]. Module names must be * unique. Each dep is the name of a module that this module * depends on. Modules must be listed in dependency order, and JS * source files must be listed in the corresponding order. Where * --module flags occur in relation to --js flags is unimportant */ CommandLineConfig setModule(List module) { this.module.clear(); this.module.addAll(module); return this; } private String variableMapInputFile = ""; /** * File containing the serialized version of the variable renaming * map produced by a previous compilation */ CommandLineConfig setVariableMapInputFile(String variableMapInputFile) { this.variableMapInputFile = variableMapInputFile; return this; } private String propertyMapInputFile = ""; /** * File containing the serialized version of the property renaming * map produced by a previous compilation */ CommandLineConfig setPropertyMapInputFile(String propertyMapInputFile) { this.propertyMapInputFile = propertyMapInputFile; return this; } private String variableMapOutputFile = ""; /** * File where the serialized version of the variable renaming map * produced should be saved */ CommandLineConfig setVariableMapOutputFile(String variableMapOutputFile) { this.variableMapOutputFile = variableMapOutputFile; return this; } private boolean createNameMapFiles = false; /** * If true, variable renaming and property renaming map * files will be produced as {binary name}_vars_map.out and * {binary name}_props_map.out. Note that this flag cannot be used * in conjunction with either variable_map_output_file or * property_map_output_file */ CommandLineConfig setCreateNameMapFiles(boolean createNameMapFiles) { this.createNameMapFiles = createNameMapFiles; return this; } private String propertyMapOutputFile = ""; /** * File where the serialized version of the property renaming map * produced should be saved */ CommandLineConfig setPropertyMapOutputFile(String propertyMapOutputFile) { this.propertyMapOutputFile = propertyMapOutputFile; return this; } private CodingConvention codingConvention = CodingConventions.getDefault(); /** * Sets rules and conventions to enforce. */ CommandLineConfig setCodingConvention(CodingConvention codingConvention) { this.codingConvention = codingConvention; return this; } private int summaryDetailLevel = 1; /** * Controls how detailed the compilation summary is. Values: * 0 (never print summary), 1 (print summary only if there are * errors or warnings), 2 (print summary if type checking is on, * see --check_types), 3 (always print summary). The default level * is 1 */ CommandLineConfig setSummaryDetailLevel(int summaryDetailLevel) { this.summaryDetailLevel = summaryDetailLevel; return this; } private String outputWrapper = ""; /** * Interpolate output into this string at the place denoted * by the marker token %output%, or %output|jsstring% */ CommandLineConfig setOutputWrapper(String outputWrapper) { this.outputWrapper = outputWrapper; return this; } private final List moduleWrapper = Lists.newArrayList(); /** * An output wrapper for a JavaScript module (optional). See the flag * description for formatting requirements. */ CommandLineConfig setModuleWrapper(List moduleWrapper) { this.moduleWrapper.clear(); this.moduleWrapper.addAll(moduleWrapper); return this; } private String moduleOutputPathPrefix = ""; /** * Prefix for filenames of compiled JS modules. * .js will be appended to this prefix. Directories * will be created as needed. Use with --module */ CommandLineConfig setModuleOutputPathPrefix(String moduleOutputPathPrefix) { this.moduleOutputPathPrefix = moduleOutputPathPrefix; return this; } private String createSourceMap = ""; /** * If specified, a source map file mapping the generated * source files back to the original source file will be * output to the specified path. The %outname% placeholder will * expand to the name of the output file that the source map * corresponds to. */ CommandLineConfig setCreateSourceMap(String createSourceMap) { this.createSourceMap = createSourceMap; return this; } private SourceMap.DetailLevel sourceMapDetailLevel = SourceMap.DetailLevel.ALL; /** * The detail supplied in the source map file, if generated. */ CommandLineConfig setSourceMapDetailLevel(SourceMap.DetailLevel level) { this.sourceMapDetailLevel = level; return this; } private SourceMap.Format sourceMapFormat = SourceMap.Format.DEFAULT; /** * The detail supplied in the source map file, if generated. */ CommandLineConfig setSourceMapFormat(SourceMap.Format format) { this.sourceMapFormat = format; return this; } private WarningGuardSpec warningGuards = null; /** * Add warning guards. */ CommandLineConfig setWarningGuardSpec(WarningGuardSpec spec) { this.warningGuards = spec; return this; } private final List define = Lists.newArrayList(); /** * Override the value of a variable annotated @define. * The format is [=], where is the name of a @define * variable and is a boolean, number, or a single-quoted string * that contains no single quotes. If [=] is omitted, * the variable is marked true */ CommandLineConfig setDefine(List define) { this.define.clear(); this.define.addAll(define); return this; } private final List tweak = Lists.newArrayList(); /** * Override the default value of a registered tweak. The format is * [=], where is the ID of a tweak and is a boolean, * number, or a single-quoted string that contains no single quotes. If * [=] is omitted, then true is assumed. */ CommandLineConfig setTweak(List tweak) { this.tweak.clear(); this.tweak.addAll(tweak); return this; } private TweakProcessing tweakProcessing = TweakProcessing.OFF; /** * Sets the kind of processing to do for goog.tweak functions. */ CommandLineConfig setTweakProcessing(TweakProcessing tweakProcessing) { this.tweakProcessing = tweakProcessing; return this; } private String charset = ""; /** * Input charset for all files. */ CommandLineConfig setCharset(String charset) { this.charset = charset; return this; } private boolean manageClosureDependencies = false; /** * Sets whether to sort files by their goog.provide/require deps, * and prune inputs that are not required. */ CommandLineConfig setManageClosureDependencies(boolean newVal) { this.manageClosureDependencies = newVal; return this; } private boolean onlyClosureDependencies = false; /** * Sets whether to sort files by their goog.provide/require deps, * and prune inputs that are not required, and drop all non-closure * files. */ CommandLineConfig setOnlyClosureDependencies(boolean newVal) { this.onlyClosureDependencies = newVal; return this; } private List closureEntryPoints = ImmutableList.of(); /** * Set closure entry points, which makes the compiler only include * those files and sort them in dependency order. */ CommandLineConfig setClosureEntryPoints(List entryPoints) { Preconditions.checkNotNull(entryPoints); this.closureEntryPoints = entryPoints; return this; } private List outputManifests = ImmutableList.of(); /** * Sets whether to print output manifest files. * Filter out empty file names. */ CommandLineConfig setOutputManifest(List outputManifests) { this.outputManifests = Lists.newArrayList(); for (String manifestName : outputManifests) { if (!manifestName.isEmpty()) { this.outputManifests.add(manifestName); } } this.outputManifests = ImmutableList.copyOf(this.outputManifests); return this; } private String outputModuleDependencies = null; /** * Sets whether a JSON file representing the dependencies between modules * should be created. */ CommandLineConfig setOutputModuleDependencies(String outputModuleDependencies) { this.outputModuleDependencies = outputModuleDependencies; return this; } private List outputBundles = ImmutableList.of(); /** * Sets whether to print output bundle files. */ CommandLineConfig setOutputBundle(List outputBundles) { this.outputBundles = outputBundles; return this; } private boolean acceptConstKeyword = false; /** * Sets whether to accept usage of 'const' keyword. */ CommandLineConfig setAcceptConstKeyword(boolean acceptConstKeyword) { this.acceptConstKeyword = acceptConstKeyword; return this; } private String languageIn = ""; /** * Sets whether to accept input files as ECMAScript5 compliant. * Otherwise, input files are treated as ECMAScript3 compliant. */ CommandLineConfig setLanguageIn(String languageIn) { this.languageIn = languageIn; return this; } private boolean skipNormalOutputs = false; /** * Sets whether the normal outputs of compilation should be skipped. */ CommandLineConfig setSkipNormalOutputs(boolean skipNormalOutputs) { this.skipNormalOutputs = skipNormalOutputs; return this; } private List manifestMaps = ImmutableList.of(); /** * Sets the execPath:rootRelativePath mappings */ CommandLineConfig setManifestMaps(List manifestMaps) { this.manifestMaps = manifestMaps; return this; } private boolean transformAMDToCJSModules = false; /** * Set whether to transform AMD to CommonJS modules. */ CommandLineConfig setTransformAMDToCJSModules( boolean transformAMDToCJSModules) { this.transformAMDToCJSModules = transformAMDToCJSModules; return this; } private boolean processCommonJSModules = false; /** * Sets whether to process CommonJS modules. */ CommandLineConfig setProcessCommonJSModules( boolean processCommonJSModules) { this.processCommonJSModules = processCommonJSModules; return this; } private String commonJSModulePathPrefix = ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; /** * Sets the CommonJS module path prefix. */ CommandLineConfig setCommonJSModulePathPrefix( String commonJSModulePathPrefix) { this.commonJSModulePathPrefix = commonJSModulePathPrefix; return this; } private String warningsWhitelistFile = ""; /** * Sets a whitelist file that suppresses warnings. */ CommandLineConfig setWarningsWhitelistFile(String fileName) { this.warningsWhitelistFile = fileName; return this; } private boolean angularPass = false; /** * Sets whether to process AngularJS-specific annotations. */ CommandLineConfig setAngularPass(boolean angularPass) { this.angularPass = angularPass; return this; } } /** * A little helper class to make it easier to collect warning types * from --jscomp_error, --jscomp_warning, and --jscomp_off. */ protected static class WarningGuardSpec { private static class Entry { private final CheckLevel level; private final String groupName; private Entry(CheckLevel level, String groupName) { this.level = level; this.groupName = groupName; } } // The entries, in the order that they were added. private final List entries = Lists.newArrayList(); protected void add(CheckLevel level, String groupName) { entries.add(new Entry(level, groupName)); } protected void clear() { entries.clear(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/StrictWarningsGuard.java0000644000175000017500000000242112115204405027442 0ustar apoapo/* * 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; /** * All warnings should be reported as errors. * * @author anatol@google.com (Anatol Pomazau) */ public class StrictWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; static final DiagnosticType UNRAISABLE_WARNING = DiagnosticType.warning("JSC_UNRAISABLE_WARNING", "{0}"); @Override public CheckLevel level(JSError error) { if (error.getType() == UNRAISABLE_WARNING) { return null; } return error.getDefaultLevel().isOn() ? CheckLevel.ERROR : null; } @Override protected int getPriority() { return WarningsGuard.Priority.STRICT.value; // applied last } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ScopeCreator.java0000644000175000017500000000214212115204405026067 0ustar apoapo/* * Copyright 2006 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.rhino.Node; /** * This interface defines how objects capable of creating scopes from the parse * tree behave. * */ interface ScopeCreator { /** * Creates a {@link Scope} object. * * @param n the root node (either a FUNCTION node, a SCRIPT node, or a * synthetic block node whose children are all SCRIPT nodes) * @param parent the parent Scope object (may be null) */ Scope createScope(Node n, Scope parent); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionNames.java0000644000175000017500000001225412115204405026254 0ustar apoapo/* * 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.Maps; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import java.io.Serializable; import java.util.*; /** * Extract a list of all function nodes defined in a JavaScript * program, assigns them globally unique ids and computes their fully * qualified names. Function names are derived from the property they * are assigned to and the scope they are defined in. For instance, * the following code * * goog.widget = function(str) { * this.member_fn = function() {} * local_fn = function() {} * goog.array.map(arr, function(){}); * } * * defines the following functions * * goog.widget * goog.widget.member_fn * goog.widget::local_fn * goog.widget:: * */ class FunctionNames implements CompilerPass, Serializable { private static final long serialVersionUID = 1L; private final transient AbstractCompiler compiler; private final Map functionMap = Maps.newLinkedHashMap(); private final transient FunctionListExtractor functionListExtractor; FunctionNames(AbstractCompiler compiler) { this.compiler = compiler; this.functionListExtractor = new FunctionListExtractor(functionMap); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, functionListExtractor); FunctionExpressionNamer namer = new FunctionExpressionNamer(functionMap); AnonymousFunctionNamingCallback namingCallback = new AnonymousFunctionNamingCallback(namer); NodeTraversal.traverse(compiler, root, namingCallback); } public Iterable getFunctionNodeList() { return functionMap.keySet(); } public int getFunctionId(Node f) { FunctionRecord record = functionMap.get(f); if (record != null) { return record.id; } else { return -1; } } public String getFunctionName(Node f) { FunctionRecord record = functionMap.get(f); if (record == null) { // Function node was added during compilation and has no name. return null; } String str = record.name; if (str.isEmpty()) { str = ""; } Node parent = record.parent; if (parent != null) { str = getFunctionName(parent) + "::" + str; } // this.foo -> foo str = str.replaceAll("::this\\.", "."); // foo.prototype.bar -> foo.bar // AnonymousFunctionNamingCallback already replaces ".prototype." // with "..", just remove the extra dot. str = str.replaceAll("\\.\\.", "."); // remove toplevel anonymous blocks, if they exists. str = str.replaceFirst("^(::)*", ""); return str; } private static class FunctionRecord implements Serializable { private static final long serialVersionUID = 1L; public final int id; public final Node parent; public String name; FunctionRecord(int id, Node parent, String name) { this.id = id; this.parent = parent; this.name = name; } } private static class FunctionListExtractor extends AbstractPostOrderCallback { private final Map functionMap; private int nextId = 0; FunctionListExtractor(Map functionMap) { this.functionMap = functionMap; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { Node functionNameNode = n.getFirstChild(); String functionName = functionNameNode.getString(); Node enclosingFunction = t.getEnclosingFunction(); functionMap.put(n, new FunctionRecord(nextId, enclosingFunction, functionName)); nextId++; } } } private static class FunctionExpressionNamer implements AnonymousFunctionNamingCallback.FunctionNamer { private static final char DELIMITER = '.'; private static final NodeNameExtractor extractor = new NodeNameExtractor(DELIMITER); private final Map functionMap; FunctionExpressionNamer(Map functionMap) { this.functionMap = functionMap; } @Override public final String getName(Node node) { return extractor.getName(node); } @Override public final void setFunctionName(String name, Node fnNode) { FunctionRecord record = functionMap.get(fnNode); assert(record != null); assert(record.name.isEmpty()); record.name = name; } @Override public final String getCombinedName(String lhs, String rhs) { return lhs + DELIMITER + rhs; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PrepareAst.java0000644000175000017500000001451712115204405025555 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Prepare the AST before we do any checks or optimizations on it. * * This pass must run. It should bring the AST into a consistent state, * and add annotations where necessary. It should not make any transformations * on the tree that would lose source information, since we need that source * information for checks. * * @author johnlenz@google.com (John Lenz) */ class PrepareAst implements CompilerPass { private final AbstractCompiler compiler; private final boolean checkOnly; PrepareAst(AbstractCompiler compiler) { this(compiler, false); } PrepareAst(AbstractCompiler compiler, boolean checkOnly) { this.compiler = compiler; this.checkOnly = checkOnly; } private void reportChange() { if (checkOnly) { Preconditions.checkState(false, "normalizeNodeType constraints violated"); } } @Override public void process(Node externs, Node root) { if (checkOnly) { normalizeNodeTypes(root); } else { // Don't perform "PrepareAnnotations" when doing checks as // they currently aren't valid during sanity checks. In particular, // they DIRECT_EVAL shouldn't be applied after inlining has been // performed. if (externs != null) { NodeTraversal.traverse( compiler, externs, new PrepareAnnotations()); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations()); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { PrepareAnnotations() { } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState(objlit.isObjectLit()); for (Node key = objlit.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); normalizeObjectLiteralKeyAnnotations(objlit, key, value); } } /** * There are two types of calls we are interested in calls without explicit * "this" values (what we are call "free" calls) and direct call to eval. */ private void annotateCalls(Node n) { Preconditions.checkState(n.isCall()); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = n.getFirstChild(); if (!NodeUtil.isGet(first)) { n.putBooleanProp(Node.FREE_CALL, true); } // Keep track of the context in which eval is called. It is important // to distinguish between "(0, eval)()" and "eval()". if (first.isName() && "eval".equals(first.getString())) { first.putBooleanProp(Node.DIRECT_EVAL, true); } } /** * Translate dispatcher info into the property expected node. */ private void annotateDispatchers(Node n, Node parent) { Preconditions.checkState(n.isFunction()); if (parent.getJSDocInfo() != null && parent.getJSDocInfo().isJavaDispatch()) { if (parent.isAssign()) { Preconditions.checkState(parent.getLastChild() == n); n.putBooleanProp(Node.IS_DISPATCHER, true); } } } /** * In the AST that Rhino gives us, it needs to make a distinction * between JsDoc on the object literal node and JsDoc on the object literal * value. For example, *
     * var x = {
     *   / JSDOC /
     *   a: 'b',
     *   c: / JSDOC / 'd'
     * };
     * 
* * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ private void normalizeObjectLiteralKeyAnnotations( Node objlit, Node key, Node value) { Preconditions.checkState(objlit.isObjectLit()); if (key.getJSDocInfo() != null && value.isFunction()) { value.setJSDocInfo(key.getJSDocInfo()); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckPathsBetweenNodes.java0000644000175000017500000001627012115204405030025 0ustar apoapo/* * 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.base.Predicate; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; /** * See constructor, {@link #CheckPathsBetweenNodes(DiGraph, * DiGraphNode, DiGraphNode, Predicate, Predicate)}, for a * description of this algorithm. * * * @param The node type. * @param The edge type. */ class CheckPathsBetweenNodes { private final Predicate nodePredicate; private final Predicate> edgePredicate; private final boolean inclusive; // This algorithm works in two stages. First, the depth-first search (DFS) // tree is calculated with A as the root. During when constructing the DFS // tree, back edges are recorded. A back edge is a non-tree edge (X -> Y) // where X is an descendant of Y in the DFS tree. The second step does a // recursive traversal of the graph. Back edges are ignored during the // recursive traversal, thus no cycles are encountered. Any recursive branch // that encounters B without yet satisfying the predicate represents a path // from the entry node to the exit without any nodes that satisfy the // predicate. // // The implementation of discoverBackEdges follows the DFS-Visit algorithm in // "Introduction to Algorithms" by Cormen, Leiseron, Rivest, and Stein, 2nd // ed., on page 541. The calculation of back edges is described on page 546. // A non-tree edge in the DFS that connects a node to one of its ancestors. private static final Annotation BACK_EDGE = new Annotation() {}; private static final Annotation VISITED_EDGE = new Annotation() {}; // Not yet visited. private static final Annotation WHITE = null; // Being visited. private static final Annotation GRAY = new Annotation() {}; // Finished visiting. private static final Annotation BLACK = new Annotation() {}; private final DiGraph graph; private final DiGraphNode start; private final DiGraphNode end; /** * Given a graph G with nodes A and B, this algorithm determines if all paths * from A to B contain at least one node satisfying a given predicate. * * Note that nodePredicate is not necessarily called for all nodes in G nor is * edgePredicate called for all edges in G. * * @param graph Graph G to analyze. * @param a The node A. * @param b The node B. * @param nodePredicate Predicate which at least one node on each path from an * A node to B (inclusive) must match. * @param edgePredicate Edges to consider as part of the graph. Edges in * graph that don't match edgePredicate will be ignored. * @param inclusive Includes node A and B in the test for the node predicate. */ CheckPathsBetweenNodes(DiGraph graph, DiGraphNode a, DiGraphNode b, Predicate nodePredicate, Predicate> edgePredicate, boolean inclusive) { this.graph = graph; this.start = a; this.end = b; this.nodePredicate = nodePredicate; this.edgePredicate = edgePredicate; this.inclusive = inclusive; } /** * Inclusive check. */ CheckPathsBetweenNodes(DiGraph graph, DiGraphNode a, DiGraphNode b, Predicate nodePredicate, Predicate> edgePredicate) { this(graph, a, b, nodePredicate, edgePredicate, true); } /** * @return true iff all paths contain at least one node that satisfy the * predicate */ public boolean allPathsSatisfyPredicate() { setUp(); boolean result = checkAllPathsWithoutBackEdges(start, end); tearDown(); return result; } /** * @return true iff some paths contain at least one node that satisfy the * predicate */ public boolean somePathsSatisfyPredicate() { setUp(); boolean result = checkSomePathsWithoutBackEdges(start, end); tearDown(); return result; } private void setUp() { graph.pushNodeAnnotations(); graph.pushEdgeAnnotations(); discoverBackEdges(this.start); } private void tearDown() { graph.popNodeAnnotations(); graph.popEdgeAnnotations(); } private void discoverBackEdges(DiGraphNode u) { u.setAnnotation(GRAY); for (DiGraphEdge e : u.getOutEdges()) { if (ignoreEdge(e)) { continue; } DiGraphNode v = e.getDestination(); if (v.getAnnotation() == WHITE) { discoverBackEdges(v); } else if (v.getAnnotation() == GRAY) { e.setAnnotation(BACK_EDGE); } } u.setAnnotation(BLACK); } private boolean ignoreEdge(DiGraphEdge e) { return !edgePredicate.apply(e); } /** * Verify that all non-looping paths from {@code a} to {@code b} pass * through at least one node where {@code nodePredicate} is true. */ private boolean checkAllPathsWithoutBackEdges(DiGraphNode a, DiGraphNode b) { if (nodePredicate.apply(a.getValue()) && (inclusive || (a != start && a != end))) { return true; } if (a == b) { return false; } for (DiGraphEdge e : a.getOutEdges()) { // Once we visited that edge once, we no longer need to // re-visit it again. if (e.getAnnotation() == VISITED_EDGE) { continue; } e.setAnnotation(VISITED_EDGE); if (ignoreEdge(e)) { continue; } if (e.getAnnotation() == BACK_EDGE) { continue; } DiGraphNode next = e.getDestination(); if (!checkAllPathsWithoutBackEdges(next, b)) { return false; } } return true; } /** * Verify that some non-looping paths from {@code a} to {@code b} pass * through at least one node where {@code nodePredicate} is true. */ private boolean checkSomePathsWithoutBackEdges(DiGraphNode a, DiGraphNode b) { if (nodePredicate.apply(a.getValue()) && (inclusive || (a != start && a != end))) { return true; } if (a == b) { return false; } for (DiGraphEdge e : a.getOutEdges()) { // Once we visited that edge once, we no longer need to // re-visit it again. if (e.getAnnotation() == VISITED_EDGE) { continue; } e.setAnnotation(VISITED_EDGE); if (ignoreEdge(e)) { continue; } if (e.getAnnotation() == BACK_EDGE) { continue; } DiGraphNode next = e.getDestination(); if (checkSomePathsWithoutBackEdges(next, b)) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PassConfig.java0000644000175000017500000002231312115204405025534 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Iterables; import com.google.javascript.jscomp.graph.GraphvizGraph; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; /** * Pass factories and meta-data for native Compiler passes. * * @author nicksantos@google.com (Nick Santos) */ public abstract class PassConfig { // Used by subclasses in this package. final CompilerOptions options; /** * A memoized version of scopeCreator. It must be memoized so that * we can make two separate passes over the AST, one for inferring types * and one for checking types. */ private MemoizedScopeCreator typedScopeCreator; /** * This is the scope creator that {@code TypedScopeCreator} delegates to. */ private TypedScopeCreator internalScopeCreator; /** The global typed scope. */ Scope topScope = null; public PassConfig(CompilerOptions options) { this.options = options; } /** * Regenerates the top scope from scratch. * * @param compiler The compiler for which the global scope is regenerated. * @param root The root of the AST. */ void regenerateGlobalTypedScope(AbstractCompiler compiler, Node root) { internalScopeCreator = new TypedScopeCreator(compiler); typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator); topScope = typedScopeCreator.createScope(root, null); } void clearTypedScope() { internalScopeCreator = null; typedScopeCreator = null; topScope = null; } /** * Regenerates the top scope potentially only for a sub-tree of AST and then * copies information for the old global scope. * * @param compiler The compiler for which the global scope is generated. * @param scriptRoot The root of the AST used to generate global scope. */ void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) { Preconditions.checkNotNull(internalScopeCreator); internalScopeCreator.patchGlobalScope(topScope, scriptRoot); } /** * Gets the scope creator for typed scopes. */ MemoizedScopeCreator getTypedScopeCreator() { return typedScopeCreator; } /** * Gets the global scope, with type information. */ Scope getTopScope() { return topScope; } /** * Gets the checking passes to run. * * Checking passes revolve around emitting warnings and errors. * They also may include pre-processor passes needed to do * error analysis more effectively. * * Clients that only want to analyze code (like IDEs) and not emit * code will only run checks and not optimizations. */ abstract protected List getChecks(); /** * Gets the optimization passes to run. * * Optimization passes revolve around producing smaller and faster code. * They should always run after checking passes. */ abstract protected List getOptimizations(); /** * Gets a graph of the passes run. For debugging. */ GraphvizGraph getPassGraph() { LinkedDirectedGraph graph = LinkedDirectedGraph.createWithoutAnnotations(); Iterable allPasses = Iterables.concat(getChecks(), getOptimizations()); String lastPass = null; String loopStart = null; for (PassFactory pass : allPasses) { String passName = pass.getName(); int i = 1; while (graph.hasNode(passName)) { passName = pass.getName() + (i++); } graph.createNode(passName); if (loopStart == null && !pass.isOneTimePass()) { loopStart = passName; } else if (loopStart != null && pass.isOneTimePass()) { graph.connect(lastPass, "loop", loopStart); loopStart = null; } if (lastPass != null) { graph.connect(lastPass, "", passName); } lastPass = passName; } return graph; } /** * Create a type inference pass. */ final TypeInferencePass makeTypeInference(AbstractCompiler compiler) { return new TypeInferencePass( compiler, compiler.getReverseAbstractInterpreter(), topScope, typedScopeCreator); } final InferJSDocInfo makeInferJsDocInfo(AbstractCompiler compiler) { return new InferJSDocInfo(compiler); } /** * Create a type-checking pass. */ final TypeCheck makeTypeCheck(AbstractCompiler compiler) { return new TypeCheck( compiler, compiler.getReverseAbstractInterpreter(), compiler.getTypeRegistry(), topScope, typedScopeCreator, options.reportMissingOverride, options.reportUnknownTypes) .reportMissingProperties(options.enables( DiagnosticGroup.forType(TypeCheck.INEXISTENT_PROPERTY))); } /** * Insert the given pass factory before the factory of the given name. */ final static void addPassFactoryBefore( List factoryList, PassFactory factory, String passName) { factoryList.add( findPassIndexByName(factoryList, passName), factory); } /** * Find a pass factory with the same name as the given one, and replace it. */ final static void replacePassFactory( List factoryList, PassFactory factory) { factoryList.set( findPassIndexByName(factoryList, factory.getName()), factory); } /** * Throws an exception if no pass with the given name exists. */ private static int findPassIndexByName( List factoryList, String name) { for (int i = 0; i < factoryList.size(); i++) { if (factoryList.get(i).getName().equals(name)) { return i; } } throw new IllegalArgumentException( "No factory named '" + name + "' in the factory list"); } /** * Find the first pass provider that does not have a delegate. */ final PassConfig getBasePassConfig() { PassConfig current = this; while (current instanceof PassConfigDelegate) { current = ((PassConfigDelegate) current).delegate; } return current; } /** * Get intermediate state for a running pass config, so it can * be paused and started again later. */ protected abstract State getIntermediateState(); /** * Set the intermediate state for a pass config, to restart * a compilation process that had been previously paused. */ protected abstract void setIntermediateState(State state); /** * An implementation of PassConfig that just proxies all its method calls * into an inner class. */ static class PassConfigDelegate extends PassConfig { private final PassConfig delegate; PassConfigDelegate(PassConfig delegate) { super(delegate.options); this.delegate = delegate; } @Override protected List getChecks() { return delegate.getChecks(); } @Override protected List getOptimizations() { return delegate.getOptimizations(); } @Override MemoizedScopeCreator getTypedScopeCreator() { return delegate.getTypedScopeCreator(); } @Override Scope getTopScope() { return delegate.getTopScope(); } @Override protected State getIntermediateState() { return delegate.getIntermediateState(); } @Override protected void setIntermediateState(State state) { delegate.setIntermediateState(state); } } /** * Intermediate state for a running pass configuration. */ public static class State implements Serializable { private static final long serialVersionUID = 1L; final Map cssNames; final Set exportedNames; final CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator; final VariableMap variableMap; final VariableMap propertyMap; final VariableMap anonymousFunctionNameMap; final VariableMap stringMap; final FunctionNames functionNames; final String idGeneratorMap; public State(Map cssNames, Set exportedNames, CrossModuleMethodMotion.IdGenerator crossModuleIdGenerator, VariableMap variableMap, VariableMap propertyMap, VariableMap anonymousFunctionNameMap, VariableMap stringMap, FunctionNames functionNames, String idGeneratorMap) { this.cssNames = cssNames; this.exportedNames = exportedNames; this.crossModuleIdGenerator = crossModuleIdGenerator; this.variableMap = variableMap; this.propertyMap = propertyMap; this.anonymousFunctionNameMap = anonymousFunctionNameMap; this.stringMap = stringMap; this.idGeneratorMap = idGeneratorMap; this.functionNames = functionNames; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PureFunctionIdentifier.java0000644000175000017500000010730412115204405030130 0ustar apoapo/* * 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.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Compiler pass that computes function purity. A function is pure if * it has no outside visible side effects, and the result of the * computation does not depend on external factors that are beyond the * control of the application; repeated calls to the function should * return the same value as long as global state hasn't changed. * * Date.now is an example of a function that has no side effects but * is not pure. * * * We will prevail, in peace and freedom from fear, and in true * health, through the purity and essence of our natural... fluids. * - General Turgidson */ class PureFunctionIdentifier implements CompilerPass { static final DiagnosticType INVALID_NO_SIDE_EFFECT_ANNOTATION = DiagnosticType.error( "JSC_INVALID_NO_SIDE_EFFECT_ANNOTATION", "@nosideeffects may only appear in externs files."); static final DiagnosticType INVALID_MODIFIES_ANNOTATION = DiagnosticType.error( "JSC_INVALID_MODIFIES_ANNOTATION", "@modifies may only appear in externs files."); private final AbstractCompiler compiler; private final DefinitionProvider definitionProvider; // Function node -> function side effects map private final Map functionSideEffectMap; // List of all function call sites; used to iterate in markPureFunctionCalls. private final List allFunctionCalls; // Externs and ast tree root, for use in getDebugReport. These two // fields are null until process is called. private Node externs; private Node root; public PureFunctionIdentifier(AbstractCompiler compiler, DefinitionProvider definitionProvider) { this.compiler = compiler; this.definitionProvider = definitionProvider; this.functionSideEffectMap = Maps.newHashMap(); this.allFunctionCalls = Lists.newArrayList(); this.externs = null; this.root = null; } @Override public void process(Node externsAst, Node srcAst) { if (externs != null || root != null) { throw new IllegalStateException( "It is illegal to call PureFunctionIdentifier.process " + "twice the same instance. Please use a new " + "PureFunctionIdentifier instance each time."); } externs = externsAst; root = srcAst; NodeTraversal.traverse(compiler, externs, new FunctionAnalyzer(true)); NodeTraversal.traverse(compiler, root, new FunctionAnalyzer(false)); propagateSideEffects(); markPureFunctionCalls(); } /** * Compute debug report that includes: * - List of all pure functions. * - Reasons we think the remaining functions have side effects. */ String getDebugReport() { Preconditions.checkNotNull(externs); Preconditions.checkNotNull(root); StringBuilder sb = new StringBuilder(); FunctionNames functionNames = new FunctionNames(compiler); functionNames.process(null, externs); functionNames.process(null, root); sb.append("Pure functions:\n"); for (Map.Entry entry : functionSideEffectMap.entrySet()) { Node function = entry.getKey(); FunctionInformation functionInfo = entry.getValue(); boolean isPure = functionInfo.mayBePure() && !functionInfo.mayHaveSideEffects(); if (isPure) { sb.append(" " + functionNames.getFunctionName(function) + "\n"); } } sb.append("\n"); for (Map.Entry entry : functionSideEffectMap.entrySet()) { Node function = entry.getKey(); FunctionInformation functionInfo = entry.getValue(); Set depFunctionNames = Sets.newHashSet(); for (Node callSite : functionInfo.getCallsInFunctionBody()) { Collection defs = getCallableDefinitions(definitionProvider, callSite.getFirstChild()); if (defs == null) { depFunctionNames.add(""); continue; } for (Definition def : defs) { depFunctionNames.add( functionNames.getFunctionName(def.getRValue())); } } sb.append(functionNames.getFunctionName(function) + " " + functionInfo.toString() + " Calls: " + depFunctionNames + "\n"); } return sb.toString(); } /** * Query the DefinitionProvider for the list of definitions that * correspond to a given qualified name subtree. Return null if * DefinitionProvider does not contain an entry for a given name, * one or more of the values returned by getDeclarations is not * callable, or the "name" node is not a GETPROP or NAME. * * @param definitionProvider The name reference graph * @param name Query node * @return non-empty definition list or null */ private static Collection getCallableDefinitions( DefinitionProvider definitionProvider, Node name) { if (name.isGetProp() || name.isName()) { List result = Lists.newArrayList(); Collection decls = definitionProvider.getDefinitionsReferencedAt(name); if (decls == null) { return null; } for (Definition current : decls) { Node rValue = current.getRValue(); if ((rValue != null) && rValue.isFunction()) { result.add(current); } else { return null; } } return result; } else if (name.isOr() || name.isHook()) { Node firstVal; if (name.isHook()) { firstVal = name.getFirstChild().getNext(); } else { firstVal = name.getFirstChild(); } Collection defs1 = getCallableDefinitions(definitionProvider, firstVal); Collection defs2 = getCallableDefinitions(definitionProvider, firstVal.getNext()); if (defs1 != null && defs2 != null) { defs1.addAll(defs2); return defs1; } else { return null; } } else if (NodeUtil.isFunctionExpression(name)) { // The anonymous function reference is also the definition. // TODO(user) Change SimpleDefinitionFinder so it is possible to query for // function expressions by function node. // isExtern is false in the call to the constructor for the // FunctionExpressionDefinition below because we know that // getCallableDefinitions() will only be called on the first // child of a call and thus the function expression // definition will never be an extern. return Lists.newArrayList( (Definition) new DefinitionsRemover.FunctionExpressionDefinition(name, false)); } else { return null; } } /** * Propagate side effect information by building a graph based on * call site information stored in FunctionInformation and the * DefinitionProvider and then running GraphReachability to * determine the set of functions that have side effects. */ private void propagateSideEffects() { // Nodes are function declarations; Edges are function call sites. DiGraph sideEffectGraph = LinkedDirectedGraph.createWithoutAnnotations(); // create graph nodes for (FunctionInformation functionInfo : functionSideEffectMap.values()) { sideEffectGraph.createNode(functionInfo); } // add connections to called functions and side effect root. for (FunctionInformation functionInfo : functionSideEffectMap.values()) { if (!functionInfo.mayHaveSideEffects()) { continue; } for (Node callSite : functionInfo.getCallsInFunctionBody()) { Node callee = callSite.getFirstChild(); Collection defs = getCallableDefinitions(definitionProvider, callee); if (defs == null) { // Definition set is not complete or eligible. Possible // causes include: // * "callee" is not of type NAME or GETPROP. // * One or more definitions are not functions. // * One or more definitions are complex. // (e.i. return value of a call that returns a function). functionInfo.setTaintsUnknown(); break; } for (Definition def : defs) { Node defValue = def.getRValue(); FunctionInformation dep = functionSideEffectMap.get(defValue); Preconditions.checkNotNull(dep); sideEffectGraph.connect(dep, callSite, functionInfo); } } } // Propagate side effect information to a fixed point. FixedPointGraphTraversal.newTraversal(new SideEffectPropagationCallback()) .computeFixedPoint(sideEffectGraph); // Mark remaining functions "pure". for (FunctionInformation functionInfo : functionSideEffectMap.values()) { if (functionInfo.mayBePure()) { functionInfo.setIsPure(); } } } /** * Set no side effect property at pure-function call sites. */ private void markPureFunctionCalls() { for (Node callNode : allFunctionCalls) { Node name = callNode.getFirstChild(); Collection defs = getCallableDefinitions(definitionProvider, name); // Default to side effects, non-local results Node.SideEffectFlags flags = new Node.SideEffectFlags(); if (defs == null) { flags.setMutatesGlobalState(); flags.setThrows(); flags.setReturnsTainted(); } else { flags.clearAllFlags(); for (Definition def : defs) { FunctionInformation functionInfo = functionSideEffectMap.get(def.getRValue()); Preconditions.checkNotNull(functionInfo); // TODO(johnlenz): set the arguments separately from the // global state flag. if (functionInfo.mutatesGlobalState()) { flags.setMutatesGlobalState(); } if (functionInfo.functionThrows) { flags.setThrows(); } if (!callNode.isNew()) { if (functionInfo.taintsThis) { flags.setMutatesThis(); } } if (functionInfo.taintsReturn) { flags.setReturnsTainted(); } if (flags.areAllFlagsSet()) { break; } } } // Handle special cases (Math, RegExp) if (callNode.isCall()) { Preconditions.checkState(compiler != null); if (!NodeUtil.functionCallHasSideEffects(callNode, compiler)) { flags.clearSideEffectFlags(); } } else if (callNode.isNew()) { // Handle known cases now (Object, Date, RegExp, etc) if (!NodeUtil.constructorCallHasSideEffects(callNode)) { flags.clearSideEffectFlags(); } } callNode.setSideEffectFlags(flags.valueOf()); } } /** * Gather list of functions, functions with @nosideeffects * annotations, call sites, and functions that may mutate variables * not defined in the local scope. */ private class FunctionAnalyzer implements ScopedCallback { private final boolean inExterns; FunctionAnalyzer(boolean inExterns) { this.inExterns = inExterns; } @Override public boolean shouldTraverse(NodeTraversal traversal, Node node, Node parent) { // Functions need to be processed as part of pre-traversal so an // entry for the enclosing function exists in the // FunctionInformation map when processing assignments and calls // inside visit. if (node.isFunction()) { Node gramp = parent.getParent(); visitFunction(traversal, node, parent, gramp); } return true; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { if (inExterns) { return; } if (!NodeUtil.nodeTypeMayHaveSideEffects(node) && !node.isReturn()) { return; } if (node.isCall() || node.isNew()) { allFunctionCalls.add(node); } Node enclosingFunction = traversal.getEnclosingFunction(); if (enclosingFunction != null) { FunctionInformation sideEffectInfo = functionSideEffectMap.get(enclosingFunction); Preconditions.checkNotNull(sideEffectInfo); if (NodeUtil.isAssignmentOp(node)) { visitAssignmentOrUnaryOperator( sideEffectInfo, traversal.getScope(), node, node.getFirstChild(), node.getLastChild()); } else { switch(node.getType()) { case Token.CALL: case Token.NEW: visitCall(sideEffectInfo, node); break; case Token.DELPROP: case Token.DEC: case Token.INC: visitAssignmentOrUnaryOperator( sideEffectInfo, traversal.getScope(), node, node.getFirstChild(), null); break; case Token.NAME: // Variable definition are not side effects. // Just check that the name appears in the context of a // variable declaration. Preconditions.checkArgument( NodeUtil.isVarDeclaration(node)); Node value = node.getFirstChild(); // Assignment to local, if the value isn't a safe local value, // new object creation or literal or known primitive result // value, add it to the local blacklist. if (value != null && !NodeUtil.evaluatesToLocalValue(value)) { Scope scope = traversal.getScope(); Var var = scope.getVar(node.getString()); sideEffectInfo.blacklistLocal(var); } break; case Token.THROW: visitThrow(sideEffectInfo); break; case Token.RETURN: if (node.hasChildren() && !NodeUtil.evaluatesToLocalValue(node.getFirstChild())) { sideEffectInfo.setTaintsReturn(); } break; default: throw new IllegalArgumentException( "Unhandled side effect node type " + Token.name(node.getType())); } } } } @Override public void enterScope(NodeTraversal t) { // Nothing to do. } @Override public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } // Handle deferred local variable modifications: // FunctionInformation sideEffectInfo = functionSideEffectMap.get(t.getScopeRoot()); if (sideEffectInfo.mutatesGlobalState()){ sideEffectInfo.resetLocalVars(); return; } for (Iterator i = t.getScope().getVars(); i.hasNext();) { Var v = i.next(); boolean localVar = false; // Parameters and catch values come can from other scopes. if (v.getParentNode().isVar()) { // TODO(johnlenz): create a useful parameter list sideEffectInfo.knownLocals.add(v.getName()); localVar = true; } // Take care of locals that might have been tainted. if (!localVar || sideEffectInfo.blacklisted.contains(v)) { if (sideEffectInfo.taintedLocals.contains(v)) { // If the function has global side-effects // don't bother with the local side-effects. sideEffectInfo.setTaintsUnknown(); sideEffectInfo.resetLocalVars(); break; } } } sideEffectInfo.taintedLocals = null; sideEffectInfo.blacklisted = null; } /** * Record information about the side effects caused by an * assignment or mutating unary operator. * * If the operation modifies this or taints global state, mark the * enclosing function as having those side effects. * @param op operation being performed. * @param lhs The store location (name or get) being operated on. * @param rhs The right have value, if any. */ private void visitAssignmentOrUnaryOperator( FunctionInformation sideEffectInfo, Scope scope, Node op, Node lhs, Node rhs) { if (lhs.isName()) { Var var = scope.getVar(lhs.getString()); if (var == null || var.scope != scope) { sideEffectInfo.setTaintsGlobalState(); } else { // Assignment to local, if the value isn't a safe local value, // a literal or new object creation, add it to the local blacklist. // parameter values depend on the caller. // Note: other ops result in the name or prop being assigned a local // value (x++ results in a number, for instance) Preconditions.checkState( NodeUtil.isAssignmentOp(op) || isIncDec(op) || op.isDelProp()); if (rhs != null && op.isAssign() && !NodeUtil.evaluatesToLocalValue(rhs)) { sideEffectInfo.blacklistLocal(var); } } } else if (NodeUtil.isGet(lhs)) { if (lhs.getFirstChild().isThis()) { sideEffectInfo.setTaintsThis(); } else { Var var = null; Node objectNode = lhs.getFirstChild(); if (objectNode.isName()) { var = scope.getVar(objectNode.getString()); } if (var == null || var.scope != scope) { sideEffectInfo.setTaintsUnknown(); } else { // Maybe a local object modification. We won't know for sure until // we exit the scope and can validate the value of the local. // sideEffectInfo.addTaintedLocalObject(var); } } } else { // TODO(johnlenz): track down what is inserting NULL on the LHS // of an assign. // The only valid LHS expressions are NAME, GETELEM, or GETPROP. // throw new IllegalStateException( // "Unexpected LHS expression:" + lhs.toStringTree() // + ", parent: " + op.toStringTree() ); sideEffectInfo.setTaintsUnknown(); } } /** * Record information about a call site. */ private void visitCall(FunctionInformation sideEffectInfo, Node node) { // Handle special cases (Math, RegExp) if (node.isCall() && !NodeUtil.functionCallHasSideEffects(node, compiler)) { return; } // Handle known cases now (Object, Date, RegExp, etc) if (node.isNew() && !NodeUtil.constructorCallHasSideEffects(node)) { return; } sideEffectInfo.appendCall(node); } /** * Record function and check for @nosideeffects annotations. */ private void visitFunction(NodeTraversal traversal, Node node, Node parent, Node gramp) { Preconditions.checkArgument(!functionSideEffectMap.containsKey(node)); FunctionInformation sideEffectInfo = new FunctionInformation(inExterns); functionSideEffectMap.put(node, sideEffectInfo); if (inExterns) { JSType jstype = node.getJSType(); boolean knownLocalResult = false; FunctionType functionType = JSType.toMaybeFunctionType(jstype); if (functionType != null) { JSType jstypeReturn = functionType.getReturnType(); if (isLocalValueType(jstypeReturn)) { knownLocalResult = true; } } if (!knownLocalResult) { sideEffectInfo.setTaintsReturn(); } } JSDocInfo info = getJSDocInfoForFunction(node, parent, gramp); if (info != null) { boolean hasSpecificSideEffects = false; if (hasSideEffectsThisAnnotation(info)) { if (inExterns) { hasSpecificSideEffects = true; sideEffectInfo.setTaintsThis(); } else { traversal.report(node, INVALID_MODIFIES_ANNOTATION); } } if (hasSideEffectsArgumentsAnnotation(info)) { if (inExterns) { hasSpecificSideEffects = true; sideEffectInfo.setTaintsArguments(); } else { traversal.report(node, INVALID_MODIFIES_ANNOTATION); } } if (inExterns && !info.getThrownTypes().isEmpty()) { hasSpecificSideEffects = true; sideEffectInfo.setFunctionThrows(); } if (!hasSpecificSideEffects) { if (hasNoSideEffectsAnnotation(info)) { if (inExterns) { sideEffectInfo.setIsPure(); } else { traversal.report(node, INVALID_NO_SIDE_EFFECT_ANNOTATION); } } else if (inExterns) { sideEffectInfo.setTaintsGlobalState(); } } } else { if (inExterns) { sideEffectInfo.setTaintsGlobalState(); } } } /** * @return Whether the jstype is something known to be a local value. */ private boolean isLocalValueType(JSType jstype) { Preconditions.checkNotNull(jstype); JSType subtype = jstype.getGreatestSubtype( compiler.getTypeRegistry().getNativeType(JSTypeNative.OBJECT_TYPE)); // If the type includes anything related to a object type, don't assume // anything about the locality of the value. return subtype.isNoType(); } /** * Record that the enclosing function throws. */ private void visitThrow(FunctionInformation sideEffectInfo) { sideEffectInfo.setFunctionThrows(); } /** * Get the doc info associated with the function. */ private JSDocInfo getJSDocInfoForFunction( Node node, Node parent, Node gramp) { JSDocInfo info = node.getJSDocInfo(); if (info != null) { return info; } else if (parent.isName()) { return gramp.hasOneChild() ? gramp.getJSDocInfo() : null; } else if (parent.isAssign()) { return parent.getJSDocInfo(); } else { return null; } } /** * Get the value of the @nosideeffects annotation stored in the * doc info. */ private boolean hasNoSideEffectsAnnotation(JSDocInfo docInfo) { Preconditions.checkNotNull(docInfo); return docInfo.isNoSideEffects(); } /** * Get the value of the @modifies{this} annotation stored in the * doc info. */ private boolean hasSideEffectsThisAnnotation(JSDocInfo docInfo) { Preconditions.checkNotNull(docInfo); return (docInfo.getModifies().contains("this")); } /** * @returns Whether the @modifies annotation includes "arguments" * or any named parameters. */ private boolean hasSideEffectsArgumentsAnnotation(JSDocInfo docInfo) { Preconditions.checkNotNull(docInfo); Set modifies = docInfo.getModifies(); // TODO(johnlenz): if we start tracking parameters individually // this should simply be a check for "arguments". return (modifies.size() > 1 || (modifies.size() == 1 && !modifies.contains("this"))); } } private static boolean isIncDec(Node n) { int type = n.getType(); return (type == Token.INC || type == Token.DEC); } /** * @return Whether the node is known to be a value that is not a reference * outside the local scope. */ @SuppressWarnings("unused") private static boolean isKnownLocalValue(final Node value) { Predicate taintingPredicate = new Predicate() { @Override public boolean apply(Node value) { switch (value.getType()) { case Token.ASSIGN: // The assignment might cause an alias, look at the LHS. return false; case Token.THIS: // TODO(johnlenz): maybe redirect this to be a tainting list for 'this'. return false; case Token.NAME: // TODO(johnlenz): add to local tainting list, if the NAME // is known to be a local. return false; case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return false; case Token.CALL: // TODO(johnlenz): add to local tainting list, if the call result // is not known to be a local result. return false; } return false; } }; return NodeUtil.evaluatesToLocalValue(value, taintingPredicate); } /** * Callback that propagates side effect information across call sites. */ private static class SideEffectPropagationCallback implements EdgeCallback { @Override public boolean traverseEdge(FunctionInformation callee, Node callSite, FunctionInformation caller) { Preconditions.checkArgument(callSite.isCall() || callSite.isNew()); boolean changed = false; if (!caller.mutatesGlobalState() && callee.mutatesGlobalState()) { caller.setTaintsGlobalState(); changed = true; } if (!caller.functionThrows() && callee.functionThrows()) { caller.setFunctionThrows(); changed = true; } if (callee.mutatesThis()) { // Side effects only propagate via regular calls. // Calling a constructor that modifies "this" has no side effects. if (!callSite.isNew()) { Node objectNode = getCallThisObject(callSite); if (objectNode != null && objectNode.isName() && !isCallOrApply(callSite)) { // Exclude ".call" and ".apply" as the value may still be // null or undefined. We don't need to worry about this with a // direct method call because null and undefined don't have any // properties. // TODO(nicksantos): Turn this back on when locals-tracking // is fixed. See testLocalizedSideEffects11. //if (!caller.knownLocals.contains(name)) { if (!caller.mutatesGlobalState()) { caller.setTaintsGlobalState(); changed = true; } //} } else if (objectNode != null && objectNode.isThis()) { if (!caller.mutatesThis()) { caller.setTaintsThis(); changed = true; } } else if (objectNode != null && NodeUtil.evaluatesToLocalValue(objectNode) && !isCallOrApply(callSite)) { // Modifying 'this' on a known local object doesn't change any // significant state. // TODO(johnlenz): We can improve this by including literal values // that we know for sure are not null. } else if (!caller.mutatesGlobalState()) { caller.setTaintsGlobalState(); changed = true; } } } return changed; } } /** * Analyze a call site and extract the node that will be act as * "this" inside the call, which is either the object part of the * qualified function name, the first argument to the call in the * case of ".call" and ".apply" or null if object is not specified * in either of those ways. * * @return node that will act as "this" for the call. */ private static Node getCallThisObject(Node callSite) { Node callTarget = callSite.getFirstChild(); if (!NodeUtil.isGet(callTarget)) { // "this" is not specified explicitly; call modifies global "this". return null; } String propString = callTarget.getLastChild().getString(); if (propString.equals("call") || propString.equals("apply")) { return callTarget.getNext(); } else { return callTarget.getFirstChild(); } } private static boolean isCallOrApply(Node callSite) { Node callTarget = callSite.getFirstChild(); if (NodeUtil.isGet(callTarget)) { String propString = callTarget.getLastChild().getString(); if (propString.equals("call") || propString.equals("apply")) { return true; } } return false; } /** * Keeps track of a function's known side effects by type and the * list of calls that appear in a function's body. */ private static class FunctionInformation { private final boolean extern; private final List callsInFunctionBody = Lists.newArrayList(); private Set blacklisted = Sets.newHashSet(); private Set taintedLocals = Sets.newHashSet(); private Set knownLocals = Sets.newHashSet(); private boolean pureFunction = false; private boolean functionThrows = false; private boolean taintsGlobalState = false; private boolean taintsThis = false; private boolean taintsArguments = false; private boolean taintsUnknown = false; private boolean taintsReturn = false; FunctionInformation(boolean extern) { this.extern = extern; checkInvariant(); } /** * @param var */ void addTaintedLocalObject(Var var) { taintedLocals.add(var); } void resetLocalVars() { blacklisted = null; taintedLocals = null; knownLocals = Collections.emptySet(); } /** * @param var */ public void blacklistLocal(Var var) { blacklisted.add(var); } /** * @returns false if function known to have side effects. */ boolean mayBePure() { return !(functionThrows || taintsGlobalState || taintsThis || taintsArguments || taintsUnknown); } /** * @returns false if function known to be pure. */ boolean mayHaveSideEffects() { return !pureFunction; } /** * Mark the function as being pure. */ void setIsPure() { pureFunction = true; checkInvariant(); } /** * Marks the function as having "modifies globals" side effects. */ void setTaintsGlobalState() { taintsGlobalState = true; checkInvariant(); } /** * Marks the function as having "modifies this" side effects. */ void setTaintsThis() { taintsThis = true; checkInvariant(); } /** * Marks the function as having "modifies arguments" side effects. */ void setTaintsArguments() { taintsArguments = true; checkInvariant(); } /** * Marks the function as having "throw" side effects. */ void setFunctionThrows() { functionThrows = true; checkInvariant(); } /** * Marks the function as having "complex" side effects that are * not otherwise explicitly tracked. */ void setTaintsUnknown() { taintsUnknown = true; checkInvariant(); } /** * Marks the function as having non-local return result. */ void setTaintsReturn() { taintsReturn = true; checkInvariant(); } /** * Returns true if function mutates global state. */ boolean mutatesGlobalState() { // TODO(johnlenz): track arguments separately. return taintsGlobalState || taintsArguments || taintsUnknown; } /** * Returns true if function mutates "this". */ boolean mutatesThis() { return taintsThis; } /** * Returns true if function has an explicit "throw". */ boolean functionThrows() { return functionThrows; } /** * Verify internal consistency. Should be called at the end of * every method that mutates internal state. */ private void checkInvariant() { boolean invariant = mayBePure() || mayHaveSideEffects(); if (!invariant) { throw new IllegalStateException("Invariant failed. " + toString()); } } /** * Add a CALL or NEW node to the list of calls this function makes. */ void appendCall(Node callNode) { callsInFunctionBody.add(callNode); } /** * Gets the list of CALL and NEW nodes. */ List getCallsInFunctionBody() { return callsInFunctionBody; } @Override public String toString() { List status = Lists.newArrayList(); if (extern) { status.add("extern"); } if (pureFunction) { status.add("pure"); } if (taintsThis) { status.add("this"); } if (taintsGlobalState) { status.add("global"); } if (functionThrows) { status.add("throw"); } if (taintsUnknown) { status.add("complex"); } return "Side effects: " + status.toString(); } } /** * A compiler pass that constructs a reference graph and drives * the PureFunctionIdentifier across it. */ static class Driver implements CompilerPass { private final AbstractCompiler compiler; private final String reportPath; private final boolean useNameReferenceGraph; Driver(AbstractCompiler compiler, String reportPath, boolean useNameReferenceGraph) { this.compiler = compiler; this.reportPath = reportPath; this.useNameReferenceGraph = useNameReferenceGraph; } @Override public void process(Node externs, Node root) { DefinitionProvider definitionProvider = null; if (useNameReferenceGraph) { NameReferenceGraphConstruction graphBuilder = new NameReferenceGraphConstruction(compiler); graphBuilder.process(externs, root); definitionProvider = graphBuilder.getNameReferenceGraph(); } else { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); definitionProvider = defFinder; } PureFunctionIdentifier pureFunctionIdentifier = new PureFunctionIdentifier(compiler, definitionProvider); pureFunctionIdentifier.process(externs, root); if (reportPath != null) { try { Files.write(pureFunctionIdentifier.getDebugReport(), new File(reportPath), Charsets.UTF_8); } catch (IOException e) { throw new RuntimeException(e); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DiagnosticType.java0000644000175000017500000000715312115204405026433 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; import java.io.Serializable; import java.text.MessageFormat; /** * The type of a compile or analysis error. * */ public class DiagnosticType implements Comparable, Serializable { private static final long serialVersionUID = 1; /** * The error type. Used as the BugPattern and BugInstance types by * BugBot's XML */ public final String key; /** The default way to format errors */ public final MessageFormat format; /** Default level */ public final CheckLevel defaultLevel; /** Reporting level, initially the defaultLevel but may be changed. */ public CheckLevel level; /** * Create a DiagnosticType at level CheckLevel.ERROR * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType error(String name, String descriptionFormat) { return make(name, CheckLevel.ERROR, descriptionFormat); } /** * Create a DiagnosticType at level CheckLevel.WARNING * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType warning(String name, String descriptionFormat) { return make(name, CheckLevel.WARNING, descriptionFormat); } /** * Create a DiagnosticType at level CheckLevel.OFF * * @param name An identifier * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType disabled(String name, String descriptionFormat) { return make(name, CheckLevel.OFF, descriptionFormat); } /** * Create a DiagnosticType at a given CheckLevel. * * @param name An identifier * @param level Either CheckLevel.ERROR or CheckLevel.WARNING * @param descriptionFormat A format string * @return A new DiagnosticType */ public static DiagnosticType make(String name, CheckLevel level, String descriptionFormat) { return new DiagnosticType(name, level, new MessageFormat(descriptionFormat)); } /** * Create a DiagnosticType. Private to force use of static factory methods. */ private DiagnosticType(String key, CheckLevel level, MessageFormat format) { this.key = key; this.defaultLevel = level; this.format = format; this.level = this.defaultLevel; } /** * Create a description from the MessageFormat and the arguments. * Used by unit tests. */ String format(Object ... arguments) { return format.format(arguments); } @Override public boolean equals(Object type) { return type instanceof DiagnosticType && ((DiagnosticType) type).key.equals(key); } @Override public int hashCode() { return key.hashCode(); } @Override public int compareTo(DiagnosticType diagnosticType) { return key.compareTo(diagnosticType.key); } @Override public String toString() { return key + ": " + format.toPattern(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MaybeReachingVariableUse.java0000644000175000017500000002243012115204405030321 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Set; /** * Computes "may be" reaching use for all definitions of each variables. * * A use of {@code A} in {@code alert(A)} is a "may be" reaching use of * the definition of {@code A} at {@code A = foo()} if at least one path from * the use node reaches that definition and it is the last definition before * the use on that path. * */ class MaybeReachingVariableUse extends DataFlowAnalysis { // The scope of the function that we are analyzing. private final Scope jsScope; private final Set escaped; MaybeReachingVariableUse( ControlFlowGraph cfg, Scope jsScope, AbstractCompiler compiler) { super(cfg, new ReachingUsesJoinOp()); this.jsScope = jsScope; this.escaped = Sets.newHashSet(); // TODO(user): Maybe compute it somewhere else and re-use the escape // local set here. computeEscaped(jsScope, escaped, compiler); } /** * May use definition lattice representation. It captures a product * lattice for each local (non-escaped) variable. The sub-lattice is * a n + 2 power set element lattice with all the Nodes in the program, * TOP and BOTTOM. This is better explained with an example: * * Consider: A sub-lattice element representing the variable A represented * by { N_4, N_5} where N_x is a Node in the program. This implies at * that particular point in the program the content of A is "upward exposed" * at point N_4 and N_5. * * Example: * * A = 1; * ... * N_3: * N_4: print(A); * N_5: y = A; * N_6: A = 1; * N_7: print(A); * * At N_3, reads of A in {N_4, N_5} are said to be upward exposed. */ static final class ReachingUses implements LatticeElement { final Multimap mayUseMap; public ReachingUses() { mayUseMap = HashMultimap.create(); } /** * Copy constructor. * * @param other The constructed object is a replicated copy of this element. */ public ReachingUses(ReachingUses other) { mayUseMap = HashMultimap.create(other.mayUseMap); } @Override public boolean equals(Object other) { return (other instanceof ReachingUses) && ((ReachingUses) other).mayUseMap.equals(this.mayUseMap); } @Override public int hashCode() { return mayUseMap.hashCode(); } } /** * The join is a simple union because of the "may be" nature of the analysis. * * Consider: A = 1; if (x) { A = 2 }; alert(A); * * The read of A "may be" exposed to A = 1 in the beginning. */ private static class ReachingUsesJoinOp implements JoinOp { @Override public ReachingUses apply(List from) { ReachingUses result = new ReachingUses(); for (ReachingUses uses : from) { result.mayUseMap.putAll(uses.mayUseMap); } return result; } } @Override boolean isForward() { return false; } @Override ReachingUses createEntryLattice() { return new ReachingUses(); } @Override ReachingUses createInitialEstimateLattice() { return new ReachingUses(); } @Override ReachingUses flowThrough(Node n, ReachingUses input) { ReachingUses output = new ReachingUses(input); // If there's an ON_EX edge, this cfgNode may or may not get executed. // We can express this concisely by just pretending this happens in // a conditional. boolean conditional = hasExceptionHandler(n); computeMayUse(n, n, output, conditional); return output; } private boolean hasExceptionHandler(Node cfgNode) { List> branchEdges = getCfg().getOutEdges(cfgNode); for (DiGraphEdge edge : branchEdges) { if (edge.getValue() == Branch.ON_EX) { return true; } } return false; } private void computeMayUse( Node n, Node cfgNode, ReachingUses output, boolean conditional) { switch (n.getType()) { case Token.BLOCK: case Token.FUNCTION: return; case Token.NAME: addToUseIfLocal(n.getString(), cfgNode, output); return; case Token.WHILE: case Token.DO: case Token.IF: computeMayUse( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeMayUse( NodeUtil.getConditionExpression(n), cfgNode, output, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (lhs.isVar()) { lhs = lhs.getLastChild(); // for(var x in y) {...} } if (lhs.isName() && !conditional) { removeFromUseIfLocal(lhs.getString(), output); } computeMayUse(rhs, cfgNode, output, conditional); } return; case Token.AND: case Token.OR: computeMayUse(n.getLastChild(), cfgNode, output, true); computeMayUse(n.getFirstChild(), cfgNode, output, conditional); return; case Token.HOOK: computeMayUse(n.getLastChild(), cfgNode, output, true); computeMayUse(n.getFirstChild().getNext(), cfgNode, output, true); computeMayUse(n.getFirstChild(), cfgNode, output, conditional); return; case Token.VAR: Node varName = n.getFirstChild(); Preconditions.checkState(n.hasChildren(), "AST should be normalized"); if (varName.hasChildren()) { computeMayUse(varName.getFirstChild(), cfgNode, output, conditional); if (!conditional) { removeFromUseIfLocal(varName.getString(), output); } } return; default: if (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) { Node name = n.getFirstChild(); if (!conditional) { removeFromUseIfLocal(name.getString(), output); } // In case of a += "Hello". There is a read of a. if (!n.isAssign()) { addToUseIfLocal(name.getString(), cfgNode, output); } computeMayUse(name.getNext(), cfgNode, output, conditional); } else { /* * We want to traverse in reverse order because we want the LAST * definition in the sub-tree.... * But we have no better way to traverse in reverse other :'( */ for (Node c = n.getLastChild(); c != null; c = n.getChildBefore(c)) { computeMayUse(c, cfgNode, output, conditional); } } } } /** * Sets the variable for the given name to the node value in the upward * exposed lattice. Do nothing if the variable name is one of the escaped * variable. */ private void addToUseIfLocal(String name, Node node, ReachingUses use) { Var var = jsScope.getVar(name); if (var == null || var.scope != jsScope) { return; } if (!escaped.contains(var)) { use.mayUseMap.put(var, node); } } /** * Removes the variable for the given name from the node value in the upward * exposed lattice. Do nothing if the variable name is one of the escaped * variable. */ private void removeFromUseIfLocal(String name, ReachingUses use) { Var var = jsScope.getVar(name); if (var == null || var.scope != jsScope) { return; } if (!escaped.contains(var)) { use.mayUseMap.removeAll(var); } } /** * Gets a list of nodes that may be using the value assigned to {@code name} * in {@code defNode}. {@code defNode} must be one of the control flow graph * nodes. * * @param name name of the variable. It can only be names of local variable * that are not function parameters, escaped variables or variables * declared in catch. * @param defNode The list of upward exposed use for the variable. */ Collection getUses(String name, Node defNode) { GraphNode n = getCfg().getNode(defNode); Preconditions.checkNotNull(n); FlowState state = n.getAnnotation(); return state.getOut().mayUseMap.get(jsScope.getVar(name)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SpecializeModule.java0000644000175000017500000006064612115204405026751 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * Beginnings of an optimization to specialize the initial module at the cost of * increasing code in later modules. This is still very experimental. * * High-level overview: * * This optimization replaces functions in the initial module with specialized * versions that are only safe in the initial module. The original, general, * versions of the functions are "fixed up" in later modules. This optimization * can shrink the initial module significantly but the fixup code in later * modules increases overall code size. * * Implementation approach: * * We take a ridiculously naive approach: remove the initial module * from the rest of the AST, optimize it with existing optimization passes * (recording which functions have been specialized), put it back in the AST, * and add fixups restoring the general versions of the functions in each module * that depends on the initial module. * * Since it is only safe to specialize functions that can be fixed up, we * don't allow specialization of local functions and functions that * are aliased. * * We currently run three optimizations on the isolated AST: InlineFunctions, * DevirtualizePrototypeMethods, and RemoveUnusedPrototypeProperties. * * These optimizations rely on a coarse-grained name-based analysis to * maintain safety properties and thus are likely to see some benefit when * applied in isolation. * * InlineFunctions is truly specializing -- it replaces functions with * versions that have calls to other functions inlined into them, while * RemoveUnusedPrototypeProperties is really just removing properties that * aren't used in the initial module and adding copies further down in the * module graph. It would probably be more elegant to give * CrossModuleMethodMotion permission to make copies of methods instead. * * There are additional passes that might benefit from being made * specialization-aware: * * - OptimizeParameters * * - Any pass that is too slow to run over the entire AST but might * be acceptable on only the initial module: * - RemoveUnusedNames * * - Also, any pass that uses the results of PureFunctionIdentifier to * determine when it is safe to remove code might benefit (e.g. the peephole * passes), since PureFunctionIdentifier relies on SimpleDefinitionFinder, * which would be more precise when running on only the initial module. * * @author dcc@google.com (Devin Coughlin) */ class SpecializeModule implements CompilerPass { private AbstractCompiler compiler; private Map specializedInputRootsByOriginal; private Map functionInfoBySpecializedFunctionNode; private SpecializationState specializationState; private final PassFactory[] specializationPassFactories; public SpecializeModule(AbstractCompiler compiler, PassFactory ...specializationPassFactories) { this.compiler = compiler; this.specializationPassFactories = specializationPassFactories; } /** * Performs initial module specialization. * * The process is as follows: * * 1) Make a copy of each of the inputs in the initial root and put them * in a fake AST that looks like it is the whole program. * * 2) Run the specializing compiler passes over the fake initial module AST * until it reaches a fixed point, recording which functions are specialized * or removed. * * 3) Replace the original input roots with the specialized input roots * * 4) For each module that directly depends on the initial module, add * fixups for the specialized and removed functions. Right now we add * fixups for for every function that was specialized or removed -- we could * be smarter about this and for each dependent module only add the functions * that it needs. * * 5) Add dummy variables declaring the removed function to the end of * the now-specialized initial module. This is needed to keep * {@link VarCheck} from complaining. */ @Override public void process(Node externs, Node root) { JSModuleGraph moduleGraph = compiler.getModuleGraph(); // Can't perform optimization without a module graph! if (moduleGraph == null) { return; } JSModule module = moduleGraph.getRootModule(); Node fakeModuleRoot = copyModuleInputs(module); SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, fakeModuleRoot); SimpleFunctionAliasAnalysis initialModuleFunctionAliasAnalysis = new SimpleFunctionAliasAnalysis(); initialModuleFunctionAliasAnalysis.analyze(defFinder); specializationState = new SpecializationState(initialModuleFunctionAliasAnalysis); do { specializationState.resetHasChanged(); for (SpecializationAwareCompilerPass pass : createSpecializingPasses()) { pass.enableSpecialization(specializationState); pass.process(externs, fakeModuleRoot); } } while(specializationState.hasChanged()); // We must always add dummy variables before replacing the original module. addDummyVarDeclarationsToInitialModule(module); replaceOriginalModuleInputsWithSpecialized(); addOriginalFunctionVersionsToDependentModules(module); } /** * Returns a collection of new instances of specializing passes. */ private Collection createSpecializingPasses() { Collection passes = Lists.newLinkedList(); for (PassFactory passFactory : specializationPassFactories) { CompilerPass pass = passFactory.create(compiler); Preconditions.checkState(pass instanceof SpecializationAwareCompilerPass); passes.add((SpecializationAwareCompilerPass) pass); } return passes; } /** * Creates an AST that consists solely of copies of the input roots for the * passed in module. * * Also records a map in {@link #functionInfoBySpecializedFunctionNode} * of information about the original function keyed on the copies of the * functions to specialized. */ private Node copyModuleInputs(JSModule module) { specializedInputRootsByOriginal = Maps.newLinkedHashMap(); functionInfoBySpecializedFunctionNode = Maps.newLinkedHashMap(); Node syntheticModuleJsRoot = IR.block(); syntheticModuleJsRoot.setIsSyntheticBlock(true); for (CompilerInput input : module.getInputs()) { Node originalInputRoot = input.getAstRoot(compiler); Node copiedInputRoot = originalInputRoot.cloneTree(); copiedInputRoot.copyInformationFromForTree(originalInputRoot); specializedInputRootsByOriginal.put(originalInputRoot, copiedInputRoot); matchTopLevelFunctions(originalInputRoot, copiedInputRoot); syntheticModuleJsRoot.addChildToBack(copiedInputRoot); } // The jsRoot needs a parent (in a normal compilation this would be the // node that contains jsRoot and the externs). Node syntheticExternsAndJsRoot = IR.block(); syntheticExternsAndJsRoot.addChildToBack(syntheticModuleJsRoot); return syntheticModuleJsRoot; } /** * Records information about original functions and creates a map from * the specialized functions to this information. * * This information is only recorded for global functions since non-global * functions cannot be inlined. * * @param original An original input root. * @param toBeSpecialized A copy of the input root (the copy to be * specialized) */ private void matchTopLevelFunctions(Node original, Node toBeSpecialized) { new NodeMatcher() { @Override public void reportMatch(Node original, Node specialized) { if (original.isFunction()) { OriginalFunctionInformation functionInfo = new OriginalFunctionInformation(original); functionInfoBySpecializedFunctionNode.put(specialized, functionInfo); } } @Override public boolean shouldTraverse(Node n1, Node n2) { return !n1.isFunction(); } }.match(original, toBeSpecialized); } /** * Replaces the original input roots of the initial module with * their specialized versions. * * (Since {@link JsAst} holds a pointer to original inputs roots, we actually * replace the all the children of the root rather than swapping the * root pointers). */ private void replaceOriginalModuleInputsWithSpecialized() { for (Node original : specializedInputRootsByOriginal.keySet()) { Node specialized = specializedInputRootsByOriginal.get(original); original.removeChildren(); while (specialized.getFirstChild() != null) { original.addChildToBack(specialized.removeFirstChild()); } } } /** * Adds dummy variable declarations for all the function declarations we've * removed to the end of the initial module. * * We do this to make {@link VarCheck} happy, since it requires variables to * be declared before they are used in the whole program AST and doesn't * like it when they are declared multiple times. * * TODO(dcc): Be smarter about whether we need a VAR here or not. */ private void addDummyVarDeclarationsToInitialModule(JSModule module) { for (Node modifiedFunction : functionInfoBySpecializedFunctionNode.keySet()) { if (specializationState.getRemovedFunctions().contains(modifiedFunction)) { OriginalFunctionInformation originalInfo = functionInfoBySpecializedFunctionNode.get(modifiedFunction); if (originalInfo.name != null && originalInfo.originalWasDeclaration()) { Node block = specializationState.removedFunctionToBlock.get( modifiedFunction); // Declaring block might be null if no fix-up declarations is needed. // For example, InlineFunction can inline an anonymous function call or // anything with prototype property requires no dummy declaration // fix-ups afterward. if (block != null) { block.addChildrenToBack(originalInfo.generateDummyDeclaration()); } } } } } /** * Adds a copy of the original versions of specialized/removed functions * to each of the dependents of module. * * Currently we add all of these functions to all dependents; it * would be more efficient to only add the functions that could be used. * * TODO(dcc): Only add fixup functions where needed. */ private void addOriginalFunctionVersionsToDependentModules(JSModule module) { for (JSModule directDependent : getDirectDependents(module)) { CompilerInput firstInput = directDependent.getInputs().get(0); Node firstInputRootNode = firstInput.getAstRoot(compiler); // We don't iterate specializedFunctions directly because want to maintain // and specializedFunctions in source order, rather than // in the order that some optimization specialized the function. // So since we're adding to the front of the module each time, we // have to iterate in reverse source order. List possiblyModifiedFunctions = Lists.newArrayList(functionInfoBySpecializedFunctionNode.keySet()); Collections.reverse(possiblyModifiedFunctions); for (Node modifiedFunction : possiblyModifiedFunctions) { boolean declarationWasSpecialized = specializationState.getSpecializedFunctions() .contains(modifiedFunction); boolean declarationWasRemoved = specializationState.getRemovedFunctions() .contains(modifiedFunction); if (declarationWasSpecialized || declarationWasRemoved) { OriginalFunctionInformation originalInfo = functionInfoBySpecializedFunctionNode.get(modifiedFunction); // Don't add unspecialized versions of anonymous functions if (originalInfo.name != null) { Node newDefinition = originalInfo.generateFixupDefinition(); firstInputRootNode.addChildrenToFront(newDefinition); } } } } } /** * Returns a list of modules that directly depend on the given module. * * This probably deserves to be in JSModuleGraph. */ public Collection getDirectDependents(JSModule module) { Set directDependents = Sets.newHashSet(); for (JSModule possibleDependent : compiler.getModuleGraph().getAllModules()) { if (possibleDependent.getDependencies().contains(module)) { directDependents.add(possibleDependent); } } return directDependents; } /** * A simple abstract classes that takes two isomorphic ASTs and walks * each of them together, reporting matches to subclasses. * * This could probably be hardened and moved to NodeUtil */ private abstract static class NodeMatcher { /** * Calls {@link #reportMatch(Node, Node)} for each pair of matching nodes * from the two ASTs. * * The two ASTs must be isomorphic. Currently no error checking is * performed to ensure that this is the case. */ public void match(Node ast1, Node ast2) { // Just blunder ahead and assume that the two nodes actually match reportMatch(ast1, ast2); if (shouldTraverse(ast1, ast2)) { Node childOf1 = ast1.getFirstChild(); Node childOf2 = ast2.getFirstChild(); while (childOf1 != null) { match(childOf1, childOf2); childOf1 = childOf1.getNext(); childOf2 = childOf2.getNext(); } } } /** * Subclasses should override to add their own behavior when two nodes * are matched. * @param n1 A node from the AST passed as ast1 in * {@link #match(Node, Node)}. * @param n2 A node from the AST passed as ast1 in * {@link #match(Node, Node)}. */ public abstract void reportMatch(Node n1, Node n2); /** * Subclasses should override to determine whether matching should proceed * under a subtree. */ public boolean shouldTraverse(Node node1, Node n2) { return true; } } /** * A class that stores information about the original version of a * function that will be/was specialized or removed. * * This class stores: * - how the function was defined * - a copy of the original function */ private class OriginalFunctionInformation { private String name; /** * a = function() {} if true; * function a() {} otherwise */ private boolean isAssignFunction; private boolean assignHasVar; private Node originalFunctionCopy; public OriginalFunctionInformation(Node originalFunction) { name = NodeUtil.getFunctionName(originalFunction); originalFunctionCopy = originalFunction.cloneTree(); originalFunctionCopy.copyInformationFromForTree(originalFunction); Node originalParent = originalFunction.getParent(); isAssignFunction = originalParent.isAssign() || originalParent.isName(); assignHasVar = isAssignFunction && originalParent.getParent().isVar(); } private Node copiedOriginalFunction() { // Copy of a copy Node copy = originalFunctionCopy.cloneTree(); copy.copyInformationFromForTree(originalFunctionCopy); return copy; } /** * Did the original function add its name to scope? * (If so, and specialization removes it, then we'll have to * add a VAR for it so VarCheck doesn't complain). */ private boolean originalWasDeclaration() { return (!isAssignFunction) || (assignHasVar); } /** * Generates a definition of the original function that can be added as * a fixup in the modules that directly depend on the specialized module. * *
     * The trick here is that even if the original function is declared as:
     *
     * function foo() {
     *   // stuff
     * }
     *
     * the fixup will have to be of the form
     *
     * foo = function() {
     *   // stuff
     * }
     * 
* */ private Node generateFixupDefinition() { Node functionCopy = copiedOriginalFunction(); Node nameNode; if (isAssignFunction) { nameNode = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), name, functionCopy, name); } else { // Grab the name node from the original function and make that // function anonymous. nameNode = functionCopy.getFirstChild(); functionCopy.replaceChild(nameNode, NodeUtil.newName(compiler.getCodingConvention(), "", nameNode)); } Node assignment = IR.assign(nameNode, functionCopy); assignment.copyInformationFrom(functionCopy); return NodeUtil.newExpr(assignment); } /** * Returns a new dummy var declaration for the function with no initial * value: * * var name; */ private Node generateDummyDeclaration() { Node declaration = NodeUtil.newVarNode(name, null); declaration.copyInformationFromForTree(originalFunctionCopy); return declaration; } } /** * A class to hold state during SpecializeModule. An instance of this class * is passed to specialization-aware compiler passes -- they use it to * communicate with SpecializeModule. * * SpecializationAware optimizations are required to keep track of the * functions they remove and the functions that they modify so that the fixups * can be added. However, not all functions can be fixed up. * * Specialization-aware classes *must* call * {@link #reportSpecializedFunction} when a function is modified during * specialization and {@link #reportRemovedFunction} when one is removed. * * Also, when specializing, they must query {@link #canFixupFunction} * before modifying a function. * * This two-way communication, is the reason we don't use * {@link AstChangeProxy} to report code changes. */ public static class SpecializationState { /** * The functions that the pass has specialized. These functions will * be fixed up in non-specialized modules to their more general versions. * * This field is also used to determine whether specialization is enabled. * If not null, specialization is enabled, otherwise it is disabled. */ private Set specializedFunctions; /** * The functions that the pass has removed. These functions will be * redefined in non-specialized modules. */ private Set removedFunctions; private Map removedFunctionToBlock; private SimpleFunctionAliasAnalysis initialModuleAliasAnalysis; /** Will be true if any new functions have been removed or specialized since * {@link #resetHasChanged}. */ private boolean hasChanged = false; public SpecializationState(SimpleFunctionAliasAnalysis initialModuleAliasAnalysis) { this.initialModuleAliasAnalysis = initialModuleAliasAnalysis; specializedFunctions = Sets.newLinkedHashSet(); removedFunctions = Sets.newLinkedHashSet(); removedFunctionToBlock = Maps.newLinkedHashMap(); } /** * Returns true if any new functions have been reported as removed or * specialized since {@link #resetHasChanged()} was last called. */ private boolean hasChanged() { return hasChanged; } private void resetHasChanged() { hasChanged = false; } /** * Returns the functions specialized by this compiler pass. */ public Set getSpecializedFunctions() { return specializedFunctions; } /** * Reports that a function has been specialized. * * @param functionNode A specialized AST node with type Token.FUNCTION */ public void reportSpecializedFunction(Node functionNode) { if (specializedFunctions.add(functionNode)) { hasChanged = true; } } /** * Reports that the function containing the node has been specialized. */ public void reportSpecializedFunctionContainingNode(Node node) { Node containingFunction = containingFunction(node); if (containingFunction != null) { reportSpecializedFunction(containingFunction); } } /** * The functions removed by this compiler pass. */ public Set getRemovedFunctions() { return removedFunctions; } /** * Reports that a function has been removed. * * @param functionNode A removed AST node with type Token.FUNCTION * @param declaringBlock If the function declaration puts a variable in the * scope, we need to have a VAR statement in the scope where the * function is declared. Null if the function does not put a name * in the scope. */ public void reportRemovedFunction(Node functionNode, Node declaringBlock) { // Depends when we were notified, functionNode.getParent might or might // not be null. We are going to force the user to tell us the parent // instead. if (removedFunctions.add(functionNode)) { hasChanged = true; removedFunctionToBlock.put(functionNode, declaringBlock); } } /** * Returns true if the function can be fixed up (that is, if it can be * safely removed or specialized). * *

In order to be safely fixed up, a function must be: *

     * - in the global scope
     * - not aliased in the initial module
     * - of one of the following forms:
     *    function f() {}
     *    var f = function() {}
     *    f = function(){}
     *    var ns = {}; ns.f = function() {}
     *    SomeClass.prototype.foo = function() {};
     * 
* *

Anonymous functions cannot be safely fixed up, nor can functions * that have been aliased. * *

Some functions declared as object literals could be safely fixed up, * however we do not currently support this. */ public boolean canFixupFunction(Node functionNode) { Preconditions.checkArgument(functionNode.isFunction()); if (!nodeIsInGlobalScope(functionNode) || initialModuleAliasAnalysis.isAliased(functionNode)) { return false; } if (NodeUtil.isStatement(functionNode)) { // function F() {} return true; } Node parent = functionNode.getParent(); Node gramps = parent.getParent(); if (parent.isName() && gramps.isVar()) { // var f = function() {} return true; } if (NodeUtil.isExprAssign(gramps) && parent.getChildAtIndex(1) == functionNode) { // f = function() {} // ns.f = function() {} return true; } return false; } /** * Returns true if the function containing n can be fixed up. * Also returns true if n is in the global scope -- since it is always safe * to specialize the global scope. */ public boolean canFixupSpecializedFunctionContainingNode(Node n) { Node containingFunction = containingFunction(n); if (containingFunction != null) { return canFixupFunction(containingFunction); } else { // Always safe to specialize the global scope return true; } } /** * Returns true if a node is in the global scope; false otherwise. */ private boolean nodeIsInGlobalScope(Node node) { return containingFunction(node) == null; } /** * Returns the function containing the node, or null if none exists. */ private Node containingFunction(Node node) { for (Node ancestor : node.getAncestors()) { if (ancestor.isFunction()) { return ancestor; } } return null; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CompilerPass.java0000644000175000017500000000244212115204405026102 0ustar apoapo/* * Copyright 2006 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.rhino.Node; /** *

Interface for classes that can compile JS.

* *

Class has single function "process", which is passed * the root node of the parsed JS tree, as well as the * root node of the external JS tree (used to provide a public API * and prevent renaming of system functions).

* *

Use this class to support testing with BaseCompilerTest

* */ public interface CompilerPass { /** * Process the JS with root node root. * Can modify the contents of each Node tree * @param externs Top of external JS tree * @param root Top of JS tree */ void process(Node externs, Node root); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeFoldWithTypes.java0000644000175000017500000000725312115204405027735 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; /** * Performs type-aware peephole optimizations. * * These peephole optimizations are in their own class because * type information may not always be available (such as during pre-processing) * or may not be turned on. * * Currently only Token.TYPEOF is folded -- in the future it may be possible to * fold Token.INSTANCEOF as well. Another possibility is folding when * non-nullable objects are used in Boolean logic, such as: * "if (x) {" or "(!x) ? a : b" or "x && foo()" * * TODO(dcc): Support folding Token.INSTANCEOF and non-nullable objects * in Boolean logic. * * @author dcc@google.com (Devin Coughlin) */ class PeepholeFoldWithTypes extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node subtree) { switch (subtree.getType()) { case Token.TYPEOF: return tryFoldTypeof(subtree); default: return subtree; } } /** * Folds "typeof expression" based on the JSType of "expression" if the * expression has no side effects. * *

E.g., *

   * var x = 6;
   * if (typeof(x) == "number") {
   * }
   * 
* folds to *
   * var x = 6;
   * if ("number" == "number") {
   * }
   * 
* *

This method doesn't fold literal values -- we leave that to * PeepholeFoldConstants. */ private Node tryFoldTypeof(Node typeofNode) { Preconditions.checkArgument(typeofNode.isTypeOf()); Preconditions.checkArgument(typeofNode.getFirstChild() != null); Node argumentNode = typeofNode.getFirstChild(); // We'll let PeepholeFoldConstants handle folding literals // and we can't remove arguments with possible side effects. if (!NodeUtil.isLiteralValue(argumentNode, true) && !mayHaveSideEffects(argumentNode)) { JSType argumentType = argumentNode.getJSType(); String typeName = null; if (argumentType != null) { // typeof null is "object" in JavaScript if (argumentType.isObject() || argumentType.isNullType()) { typeName = "object"; } else if (argumentType.isStringValueType()) { typeName = "string"; } else if (argumentType.isNumberValueType()) { typeName = "number"; } else if (argumentType.isBooleanValueType()) { typeName = "boolean"; } else if (argumentType.isVoidType()) { typeName = "undefined"; } else if (argumentType.isUnionType()) { // TODO(dcc): We don't handle union types, for now, // but could support, say, unions of different object types // in the future. typeName = null; } if (typeName != null) { Node newNode = IR.string(typeName); typeofNode.getParent().replaceChild(typeofNode, newNode); reportCodeChange(); return newNode; } } } return typeofNode; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckMissingGetCssName.java0000644000175000017500000000755412115204405027773 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Ensures string literals matching certain patterns are only used as * goog.getCssName parameters. * * @author mkretzschmar@google.com (Martin Kretzschmar) */ class CheckMissingGetCssName extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final CheckLevel level; private final Matcher blacklist; static final String GET_CSS_NAME_FUNCTION = "goog.getCssName"; static final String GET_UNIQUE_ID_FUNCTION = ".getUniqueId"; static final DiagnosticType MISSING_GETCSSNAME = DiagnosticType.disabled( "JSC_MISSING_GETCSSNAME", "missing goog.getCssName around literal ''{0}''"); CheckMissingGetCssName(AbstractCompiler compiler, CheckLevel level, String blacklistRegex) { this.compiler = compiler; this.level = level; this.blacklist = Pattern.compile("\\b(?:" + blacklistRegex + ")").matcher(""); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isString() && !parent.isGetProp() && !parent.isRegExp()) { String s = n.getString(); for (blacklist.reset(s); blacklist.find();) { if (insideGetCssNameCall(n)) { continue; } if (insideGetUniqueIdCall(n)) { continue; } if (insideAssignmentToIdConstant(n)) { continue; } compiler.report(t.makeError(n, level, MISSING_GETCSSNAME, blacklist.group())); } } } /** Returns whether the node is an argument of a goog.getCssName call. */ private boolean insideGetCssNameCall(Node n) { Node parent = n.getParent(); return parent.isCall() && GET_CSS_NAME_FUNCTION.equals( parent.getFirstChild().getQualifiedName()); } /** * Returns whether the node is an argument of a function that returns * a unique id (the last part of the qualified name matches * GET_UNIQUE_ID_FUNCTION). */ private boolean insideGetUniqueIdCall(Node n) { Node parent = n.getParent(); String name = parent.isCall() ? parent.getFirstChild().getQualifiedName() : null; return name != null && name.endsWith(GET_UNIQUE_ID_FUNCTION); } /** * Returns whether the node is the right hand side of an assignment or * initialization of a variable named *_ID of *_ID_. */ private boolean insideAssignmentToIdConstant(Node n) { Node parent = n.getParent(); if (parent.isAssign()) { String qname = parent.getFirstChild().getQualifiedName(); return qname != null && isIdName(qname); } else if (parent.isName()) { Node grandParent = parent.getParent(); if (grandParent != null && grandParent.isVar()) { String name = parent.getString(); return isIdName(name); } else { return false; } } else { return false; } } private boolean isIdName(String name) { return name.endsWith("ID") || name.endsWith("ID_"); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckMissingReturn.java0000644000175000017500000001415212115204405027251 0ustar apoapo/* * 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.base.Predicate; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.TernaryValue; /** * Checks functions for missing return statements. Return statements are only * expected for functions with return type information. Functions with empty * bodies are ignored. * */ class CheckMissingReturn implements ScopedCallback { static final DiagnosticType MISSING_RETURN_STATEMENT = DiagnosticType.warning( "JSC_MISSING_RETURN_STATEMENT", "Missing return statement. Function expected to return {0}."); private final AbstractCompiler compiler; private final CheckLevel level; private static final Predicate IS_RETURN = new Predicate() { @Override public boolean apply(Node input) { // Check for null because the control flow graph's implicit return node is // represented by null, so this value might be input. return input != null && input.isReturn(); } }; /* Skips all exception edges and impossible edges. */ private static final Predicate> GOES_THROUGH_TRUE_CONDITION_PREDICATE = new Predicate>() { @Override public boolean apply(DiGraphEdge input) { // First skill all exceptions. Branch branch = input.getValue(); if (branch == Branch.ON_EX) { return false; } else if (branch.isConditional()) { Node condition = NodeUtil.getConditionExpression( input.getSource().getValue()); // TODO(user): We CAN make this bit smarter just looking at // constants. We DO have a full blown ReverseAbstractInterupter and // type system that can evaluate some impressions' boolean value but // for now we will keep this pass lightweight. if (condition != null) { TernaryValue val = NodeUtil.getImpureBooleanValue(condition); if (val != TernaryValue.UNKNOWN) { return val.toBoolean(true) == (Branch.ON_TRUE == branch); } } } return true; } }; /** * @param level level of severity to report when a missing return statement * is discovered */ CheckMissingReturn(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; } @Override public void enterScope(NodeTraversal t) { JSType returnType = explicitReturnExpected(t.getScopeRoot()); if (returnType == null) { return; } if (fastAllPathsReturnCheck(t.getControlFlowGraph())) { return; } CheckPathsBetweenNodes test = new CheckPathsBetweenNodes( t.getControlFlowGraph(), t.getControlFlowGraph().getEntry(), t.getControlFlowGraph().getImplicitReturn(), IS_RETURN, GOES_THROUGH_TRUE_CONDITION_PREDICATE); if (!test.allPathsSatisfyPredicate()) { compiler.report( t.makeError(t.getScopeRoot(), level, MISSING_RETURN_STATEMENT, returnType.toString())); } } /** * Fast check to see if all execution paths contain a return statement. * May spuriously report that a return statement is missing. * * @return true if all paths return, converse not necessarily true */ private static boolean fastAllPathsReturnCheck(ControlFlowGraph cfg) { for (DiGraphEdge s : cfg.getImplicitReturn().getInEdges()) { if (!s.getSource().getValue().isReturn()) { return false; } } return true; } @Override public void exitScope(NodeTraversal t) { } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { } /** * Determines if the given scope should explicitly return. All functions * with non-void or non-unknown return types must have explicit returns. * @return If a return type is expected, returns it. Otherwise, returns null. */ private JSType explicitReturnExpected(Node scope) { FunctionType scopeType = JSType.toMaybeFunctionType(scope.getJSType()); if (scopeType == null) { return null; } if (isEmptyFunction(scope)) { return null; } JSType returnType = scopeType.getReturnType(); if (returnType == null) { return null; } if (!isVoidOrUnknown(returnType)) { return returnType; } return null; } /** * @return {@code true} if function represents a JavaScript function * with an empty body */ private static boolean isEmptyFunction(Node function) { return function.getChildCount() == 3 && !function.getFirstChild().getNext().getNext().hasChildren(); } /** * @return {@code true} if returnType is void, unknown, or a union * containing void or unknown */ private boolean isVoidOrUnknown(JSType returnType) { final JSType voidType = compiler.getTypeRegistry().getNativeType(JSTypeNative.VOID_TYPE); return voidType.isSubtype(returnType); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckProvides.java0000644000175000017500000001044412115204405026233 0ustar apoapo/* * 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.Maps; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Insures '@constructor X' has a 'goog.provide("X")' . * */ class CheckProvides implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CheckLevel checkLevel; private final CodingConvention codingConvention; static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled( "JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')"); CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; this.codingConvention = compiler.getCodingConvention(); } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { CheckProvidesCallback callback = new CheckProvidesCallback(codingConvention); new NodeTraversal(compiler, callback).traverse(scriptRoot); } private class CheckProvidesCallback extends AbstractShallowCallback { private final Map provides = Maps.newHashMap(); private final Map ctors = Maps.newHashMap(); private final CodingConvention convention; CheckProvidesCallback(CodingConvention convention){ this.convention = convention; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: String providedClassName = codingConvention.extractClassNameIfProvide(n, parent); if (providedClassName != null) { provides.put(providedClassName, n); } break; case Token.FUNCTION: visitFunctionNode(n, parent); break; case Token.SCRIPT: visitScriptNode(); } } private void visitFunctionNode(Node n, Node parent) { Node name = null; JSDocInfo info = parent.getJSDocInfo(); if (info != null && info.isConstructor()) { name = parent.getFirstChild(); } else { // look to the child, maybe it's a named function info = n.getJSDocInfo(); if (info != null && info.isConstructor()) { name = n.getFirstChild(); } } if (name != null && name.isQualifiedName()) { String qualifiedName = name.getQualifiedName(); if (!this.convention.isPrivate(qualifiedName)) { Visibility visibility = info.getVisibility(); if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) { ctors.put(qualifiedName, name); } } } } private void visitScriptNode() { for (Map.Entry ctorEntry : ctors.entrySet()) { String ctor = ctorEntry.getKey(); int index = -1; boolean found = false; do { index = ctor.indexOf('.', index +1); String provideKey = index == -1 ? ctor : ctor.substring(0, index); if (provides.containsKey(provideKey)) { found = true; break; } } while (index != -1); if (!found) { Node n = ctorEntry.getValue(); compiler.report( JSError.make(n.getSourceFileName(), n, checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey())); } } provides.clear(); ctors.clear(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RuntimeTypeCheck.java0000644000175000017500000003016612115204405026730 0ustar apoapo/* * 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.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.util.Collection; import java.util.Comparator; import java.util.TreeSet; import javax.annotation.Nullable; /** * Inserts run-time type assertions. * *

We add markers to user-defined interfaces and classes in order to check if * an object conforms to that type. * *

For each function, we insert a run-time type assertion for each parameter * and return value for which the compiler has a type. * *

The JavaScript code which implements the type assertions is in * js/runtime-type-check.js. * */ class RuntimeTypeCheck implements CompilerPass { private static final Comparator ALPHA = new Comparator() { @Override public int compare(JSType t1, JSType t2) { return getName(t1).compareTo(getName(t2)); } private String getName(JSType type) { if (type.isInstanceType()) { return ((ObjectType) type).getReferenceName(); } else if (type.isNullType() || type.isBooleanValueType() || type.isNumberValueType() || type.isStringValueType() || type.isVoidType()) { return type.toString(); } else { // Type unchecked at runtime, so we don't care about the sorting order. return ""; } } }; private final AbstractCompiler compiler; private final String logFunction; RuntimeTypeCheck(AbstractCompiler compiler, @Nullable String logFunction) { this.compiler = compiler; this.logFunction = logFunction; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new AddMarkers(compiler)); NodeTraversal.traverse(compiler, root, new AddChecks()); addBoilerplateCode(); } /** * Inserts marker properties for user-defined interfaces and classes. * *

For example, for a class C, we add * {@code C.prototype['instance_of__C']}, and for each interface I it * implements , we add {@code C.prototype['implements__I']}. * *

Since interfaces are not a run-time JS concept, we use these markers to * recognize an interface implementation at runtime. We also use markers for * user-defined classes, so that we can easily recognize them independently of * which module they are defined in and whether the module is loaded. */ private static class AddMarkers extends NodeTraversal.AbstractPostOrderCallback { private final AbstractCompiler compiler; private AddMarkers(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { visitFunction(n); } } private void visitFunction(Node n) { FunctionType funType = n.getJSType().toMaybeFunctionType(); if (funType != null && !funType.isConstructor()) { return; } Node nodeToInsertAfter = findNodeToInsertAfter(n); nodeToInsertAfter = addMarker(funType, nodeToInsertAfter, null); TreeSet stuff = Sets.newTreeSet(ALPHA); Iterables.addAll(stuff, funType.getAllImplementedInterfaces()); for (ObjectType interfaceType : stuff) { nodeToInsertAfter = addMarker(funType, nodeToInsertAfter, interfaceType); } } private Node addMarker( FunctionType funType, Node nodeToInsertAfter, @Nullable ObjectType interfaceType) { if (funType.getSource() == null) { return nodeToInsertAfter; } String className = NodeUtil.getFunctionName(funType.getSource()); // This can happen with anonymous classes declared with the type // {@code Function}. if (className == null) { return nodeToInsertAfter; } Node classNode = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), className); Node marker = IR.string( interfaceType == null ? "instance_of__" + className : "implements__" + interfaceType.getReferenceName()); Node assign = IR.exprResult(IR.assign( IR.getelem( IR.getprop( classNode, IR.string("prototype")), marker), IR.trueNode())); nodeToInsertAfter.getParent().addChildAfter(assign, nodeToInsertAfter); compiler.reportCodeChange(); nodeToInsertAfter = assign; return nodeToInsertAfter; } /** * Find the node to insert the markers after. Typically, this node * corresponds to the constructor declaration, but we want to skip any of * the white-listed function calls. * * @param n the constructor function node * @return the node to insert after */ private Node findNodeToInsertAfter(Node n) { Node nodeToInsertAfter = findEnclosingConstructorDeclaration(n); Node next = nodeToInsertAfter.getNext(); while (next != null && isClassDefiningCall(next)) { nodeToInsertAfter = next; next = nodeToInsertAfter.getNext(); } return nodeToInsertAfter; } private Node findEnclosingConstructorDeclaration(Node n) { while (!n.getParent().isScript() && !n.getParent().isBlock()) { n = n.getParent(); } return n; } private boolean isClassDefiningCall(Node next) { return NodeUtil.isExprCall(next) && compiler.getCodingConvention().getClassesDefinedByCall( next.getFirstChild()) != null; } } /** * Insert calls to the run-time type checking function {@code checkType}, which * takes an expression to check and a list of checkers (one of which must * match). It returns the expression back to facilitate checking of return * values. We have checkers for value types, class types (user-defined and * externed), and interface types. */ private class AddChecks extends NodeTraversal.AbstractPostOrderCallback { private AddChecks() { } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { visitFunction(n); } else if (n.isReturn()) { visitReturn(t, n); } } /** * Insert checks for the parameters of the function. */ private void visitFunction(Node n) { FunctionType funType = JSType.toMaybeFunctionType(n.getJSType()); Node block = n.getLastChild(); Node paramName = NodeUtil.getFunctionParameters(n).getFirstChild(); Node insertionPoint = null; // To satisfy normalization constraints, the type checking must be // added after any inner function declarations. for (Node next = block.getFirstChild(); next != null && NodeUtil.isFunctionDeclaration(next); next = next.getNext()) { insertionPoint = next; } for (Node paramType : funType.getParameters()) { // Can this ever happen? if (paramName == null) { return; } Node checkNode = createCheckTypeCallNode( paramType.getJSType(), paramName.cloneTree()); if (checkNode == null) { // We don't know how to check this parameter type. paramName = paramName.getNext(); continue; } checkNode = IR.exprResult(checkNode); if (insertionPoint == null) { block.addChildToFront(checkNode); } else { block.addChildAfter(checkNode, insertionPoint); } compiler.reportCodeChange(); paramName = paramName.getNext(); insertionPoint = checkNode; } } private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); FunctionType funType = function.getJSType().toMaybeFunctionType(); Node retValue = n.getFirstChild(); if (retValue == null) { return; } Node checkNode = createCheckTypeCallNode( funType.getReturnType(), retValue.cloneTree()); if (checkNode == null) { return; } n.replaceChild(retValue, checkNode); compiler.reportCodeChange(); } /** * Creates a function call to check that the given expression matches the * given type at runtime. * *

For example, if the type is {@code (string|Foo)}, the function call is * {@code checkType(expr, [valueChecker('string'), classChecker('Foo')])}. * * @return the function call node or {@code null} if the type is not checked */ private Node createCheckTypeCallNode(JSType type, Node expr) { Node arrayNode = IR.arraylit(); Collection alternates; if (type.isUnionType()) { alternates = Sets.newTreeSet(ALPHA); Iterables.addAll(alternates, type.toMaybeUnionType().getAlternates()); } else { alternates = ImmutableList.of(type); } for (JSType alternate : alternates) { Node checkerNode = createCheckerNode(alternate); if (checkerNode == null) { return null; } arrayNode.addChildToBack(checkerNode); } return IR.call(jsCode("checkType"), expr, arrayNode); } /** * Creates a node which evaluates to a checker for the given type (which * must not be a union). We have checkers for value types, classes and * interfaces. * * @return the checker node or {@code null} if the type is not checked */ private Node createCheckerNode(JSType type) { if (type.isNullType()) { return jsCode("nullChecker"); } else if (type.isBooleanValueType() || type.isNumberValueType() || type.isStringValueType() || type.isVoidType()) { return IR.call( jsCode("valueChecker"), IR.string(type.toString())); } else if (type.isInstanceType()) { ObjectType objType = (ObjectType) type; String refName = objType.getReferenceName(); StaticSourceFile sourceFile = NodeUtil.getSourceFile(objType.getConstructor().getSource()); if (sourceFile == null || sourceFile.isExtern()) { return IR.call( jsCode("externClassChecker"), IR.string(refName)); } return IR.call( jsCode(objType.getConstructor().isInterface() ? "interfaceChecker" : "classChecker"), IR.string(refName)); } else { // We don't check this type (e.g. unknown & all types). return null; } } } private void addBoilerplateCode() { Node newNode = compiler.ensureLibraryInjected("runtime_type_check"); if (newNode != null && logFunction != null) { // Inject the custom log function. Node logOverride = IR.exprResult( IR.assign( NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), "$jscomp.typecheck.log"), NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), logFunction))); newNode.getParent().addChildAfter(logOverride, newNode); compiler.reportCodeChange(); } } private Node jsCode(String prop) { return NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), "$jscomp.typecheck." + prop); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/LightweightMessageFormatter.java0000644000175000017500000001237612115204405031160 0ustar apoapo/* * Copyright 2007 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.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter; import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; /** * Lightweight message formatter. The format of messages this formatter * produces is very compact and to the point. * */ public class LightweightMessageFormatter extends AbstractMessageFormatter { private SourceExcerpt excerpt; private static final ExcerptFormatter excerptFormatter = new LineNumberingFormatter(); /** * A constructor for when the client doesn't care about source information. */ private LightweightMessageFormatter() { super(null); this.excerpt = LINE; } public LightweightMessageFormatter(SourceExcerptProvider source) { this(source, LINE); } public LightweightMessageFormatter(SourceExcerptProvider source, SourceExcerpt excerpt) { super(source); Preconditions.checkNotNull(source); this.excerpt = excerpt; } static LightweightMessageFormatter withoutSource() { return new LightweightMessageFormatter(); } @Override public String formatError(JSError error) { return format(error, false); } @Override public String formatWarning(JSError warning) { return format(warning, true); } private String format(JSError error, boolean warning) { // extract source excerpt SourceExcerptProvider source = getSource(); String sourceExcerpt = source == null ? null : excerpt.get( source, error.sourceName, error.lineNumber, excerptFormatter); // formatting the message StringBuilder b = new StringBuilder(); if (error.sourceName != null) { b.append(error.sourceName); if (error.lineNumber > 0) { b.append(':'); b.append(error.lineNumber); } b.append(": "); } b.append(getLevelName(warning ? CheckLevel.WARNING : CheckLevel.ERROR)); b.append(" - "); b.append(error.description); b.append('\n'); if (sourceExcerpt != null) { b.append(sourceExcerpt); b.append('\n'); int charno = error.getCharno(); // padding equal to the excerpt and arrow at the end // charno == sourceExpert.length() means something is missing // at the end of the line if (excerpt.equals(LINE) && 0 <= charno && charno <= sourceExcerpt.length()) { for (int i = 0; i < charno; i++) { char c = sourceExcerpt.charAt(i); if (Character.isWhitespace(c)) { b.append(c); } else { b.append(' '); } } b.append("^\n"); } } return b.toString(); } /** * Formats a region by appending line numbers in front, e.g. *

   9| if (foo) {
   *   10|   alert('bar');
   *   11| }
* and return line excerpt without any modification. */ static class LineNumberingFormatter implements ExcerptFormatter { @Override public String formatLine(String line, int lineNumber) { return line; } @Override public String formatRegion(Region region) { if (region == null) { return null; } String code = region.getSourceExcerpt(); if (code.length() == 0) { return null; } // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int start = 0; int end = code.indexOf('\n', start); int lineNumber = region.getBeginningLineNumber(); while (start >= 0) { // line extraction String line; if (end < 0) { line = code.substring(start); if (line.length() == 0) { return builder.substring(0, builder.length() - 1); } } else { line = code.substring(start, end); } builder.append(" "); // nice spaces for the line number int spaces = numberLength - Integer.toString(lineNumber).length(); builder.append(Strings.repeat(" ", spaces)); builder.append(lineNumber); builder.append("| "); // end & update if (end < 0) { builder.append(line); start = -1; } else { builder.append(line); builder.append('\n'); start = end + 1; end = code.indexOf('\n', start); lineNumber++; } } return builder.toString(); } } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SpecializationAwareCompilerPass.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SpecializationAwareCompilerPass.jav0000644000175000017500000000174112115204405031621 0ustar apoapo/* * 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; /** * Interface indicating a CompilerPass is specialization aware. * * See {@link SpecializeModule} for details of module specialization. * * @author dcc@google.com (Devin Coughlin) * */ interface SpecializationAwareCompilerPass extends CompilerPass { public void enableSpecialization(SpecializeModule.SpecializationState state); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JsMessageVisitor.java0000644000175000017500000007617112115204405026754 0ustar apoapo/* * 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 static com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.common.base.CaseFormat; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.JsMessage.Builder; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; import java.util.regex.*; import javax.annotation.Nullable; /** * Traverses across parsed tree and finds I18N messages. Then it passes it to * {@link JsMessageVisitor#processJsMessage(JsMessage, JsMessageDefinition)}. * * @author anatol@google.com (Anatol Pomazau) */ abstract class JsMessageVisitor extends AbstractPostOrderCallback implements CompilerPass { private static final String MSG_FUNCTION_NAME = "goog.getMsg"; private static final String MSG_FALLBACK_FUNCTION_NAME = "goog.getMsgWithFallback"; static final DiagnosticType MESSAGE_HAS_NO_DESCRIPTION = DiagnosticType.warning("JSC_MSG_HAS_NO_DESCRIPTION", "Message {0} has no description. Add @desc JsDoc tag."); static final DiagnosticType MESSAGE_HAS_NO_TEXT = DiagnosticType.warning("JSC_MSG_HAS_NO_TEXT", "Message value of {0} is just an empty string. " + "Empty messages are forbidden." ); static final DiagnosticType MESSAGE_TREE_MALFORMED = DiagnosticType.error("JSC_MSG_TREE_MALFORMED", "Message parse tree malformed. {0}"); static final DiagnosticType MESSAGE_HAS_NO_VALUE = DiagnosticType.error("JSC_MSG_HAS_NO_VALUE", "message node {0} has no value"); static final DiagnosticType MESSAGE_DUPLICATE_KEY = DiagnosticType.error("JSC_MSG_KEY_DUPLICATED", "duplicate message variable name found for {0}, " + "initial definition {1}:{2}"); static final DiagnosticType MESSAGE_NODE_IS_ORPHANED = DiagnosticType.warning("JSC_MSG_ORPHANED_NODE", MSG_FUNCTION_NAME + "() function could be used only with MSG_* property or variable"); static final DiagnosticType MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX = DiagnosticType.error("JSC_MSG_NOT_INITIALIZED_USING_NEW_SYNTAX", "message not initialized using " + MSG_FUNCTION_NAME); static final DiagnosticType BAD_FALLBACK_SYNTAX = DiagnosticType.error("JSC_MSG_BAD_FALLBACK_SYNTAX", String.format( "Bad syntax. " + "Expected syntax: goog.getMsgWithFallback(MSG_1, MSG_2)", MSG_FALLBACK_FUNCTION_NAME)); static final DiagnosticType FALLBACK_ARG_ERROR = DiagnosticType.error("JSC_MSG_FALLBACK_ARG_ERROR", "Could not find message entry for fallback argument {0}"); private static final String PH_JS_PREFIX = "{$"; private static final String PH_JS_SUFFIX = "}"; static final String MSG_PREFIX = "MSG_"; /** * Pattern for unnamed messages. *

* All JS messages in JS code should have unique name but messages in * generated code (i.e. from soy template) could have duplicated message names. * Later we replace the message names with ids constructed as a hash of the * message content. *

* * Soy generates messages with names MSG_UNNAMED_ . This * pattern recognizes such messages. */ private static final Pattern MSG_UNNAMED_PATTERN = Pattern.compile("MSG_UNNAMED_\\d+"); private static final Pattern CAMELCASE_PATTERN = Pattern.compile("[a-z][a-zA-Z\\d]*[_\\d]*"); static final String HIDDEN_DESC_PREFIX = "@hidden"; // For old-style JS messages private static final String DESC_SUFFIX = "_HELP"; private final boolean needToCheckDuplications; private final JsMessage.Style style; private final JsMessage.IdGenerator idGenerator; final AbstractCompiler compiler; /** * The names encountered associated with their defining node and source. We * use it for tracking duplicated message ids in the source code. */ private final Map messageNames = Maps.newHashMap(); private final Map unnamedMessages = Maps.newHashMap(); /** * List of found goog.getMsg call nodes. * * When we visit goog.getMsg() node we add a pair node:sourcename and later * when we visit its parent we remove this pair. All nodes that are left at * the end of traversing are orphaned nodes. It means have no corresponding * var or property node. */ private final Map googMsgNodes = Maps.newHashMap(); private final CheckLevel checkLevel; /** * Creates JS message visitor. * * @param compiler the compiler instance * @param needToCheckDuplications whether to check duplicated messages in * traversed * @param style style that should be used during parsing * @param idGenerator generator that used for creating unique ID for the * message */ JsMessageVisitor(AbstractCompiler compiler, boolean needToCheckDuplications, JsMessage.Style style, JsMessage.IdGenerator idGenerator) { this.compiler = compiler; this.needToCheckDuplications = needToCheckDuplications; this.style = style; this.idGenerator = idGenerator; checkLevel = (style == JsMessage.Style.CLOSURE) ? CheckLevel.ERROR : CheckLevel.WARNING; // TODO(anatol): add flag that decides whether to process UNNAMED messages. // Some projects would not want such functionality (unnamed) as they don't // use SOY templates. } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Map.Entry msgNode : googMsgNodes.entrySet()) { compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), checkLevel, MESSAGE_NODE_IS_ORPHANED)); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { String messageKey; boolean isVar; Node msgNode, msgNodeParent; switch (node.getType()) { case Token.NAME: // var MSG_HELLO = 'Message' if ((parent != null) && (parent.isVar())) { messageKey = node.getString(); isVar = true; } else { return; } msgNode = node.getFirstChild(); msgNodeParent = node; break; case Token.ASSIGN: // somenamespace.someclass.MSG_HELLO = 'Message' isVar = false; Node getProp = node.getFirstChild(); if (!getProp.isGetProp()) { return; } Node propNode = getProp.getLastChild(); messageKey = propNode.getString(); msgNode = node.getLastChild(); msgNodeParent = node; break; case Token.CALL: // goog.getMsg() String fnName = node.getFirstChild().getQualifiedName(); if (MSG_FUNCTION_NAME.equals(fnName)) { googMsgNodes.put(node, traversal.getSourceName()); } else if (MSG_FALLBACK_FUNCTION_NAME.equals(fnName)) { visitFallbackFunctionCall(traversal, node); } return; default: return; } // Is this a message name? boolean isNewStyleMessage = msgNode != null && msgNode.isCall(); if (!isMessageName(messageKey, isNewStyleMessage)) { return; } if (msgNode == null) { compiler.report( traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey)); return; } // Just report a warning if a qualified messageKey that looks like a message // (e.g. "a.b.MSG_X") doesn't use goog.getMsg(). if (isNewStyleMessage) { googMsgNodes.remove(msgNode); } else if (style != JsMessage.Style.LEGACY) { compiler.report(traversal.makeError(node, checkLevel, MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX)); } boolean isUnnamedMsg = isUnnamedMessageName(messageKey); Builder builder = new Builder( isUnnamedMsg ? null : messageKey); builder.setSourceName(traversal.getSourceName()); try { if (isVar) { extractMessageFromVariable(builder, node, parent, parent.getParent()); } else { extractMessageFromProperty(builder, node.getFirstChild(), node); } } catch (MalformedException ex) { compiler.report(traversal.makeError(ex.getNode(), MESSAGE_TREE_MALFORMED, ex.getMessage())); return; } JsMessage extractedMessage = builder.build(idGenerator); // If asked to check named internal messages. if (needToCheckDuplications && !isUnnamedMsg && !extractedMessage.isExternal()) { checkIfMessageDuplicated(messageKey, msgNode); } trackMessage(traversal, extractedMessage, messageKey, msgNode, isUnnamedMsg); if (extractedMessage.isEmpty()) { // value of the message is an empty string. Translators do not like it. compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_TEXT, messageKey)); } // New-style messages must have descriptions. We don't emit a warning // for legacy-style messages, because there are thousands of // them in legacy code that are not worth the effort to fix, since they've // already been translated anyway. String desc = extractedMessage.getDesc(); if (isNewStyleMessage && (desc == null || desc.trim().isEmpty()) && !extractedMessage.isExternal()) { compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_DESCRIPTION, messageKey)); } JsMessageDefinition msgDefinition = new JsMessageDefinition( node, msgNode, msgNodeParent); processJsMessage(extractedMessage, msgDefinition); } /** * Track a message for later retrieval. * * This is used for tracking duplicates, and for figuring out message * fallback. Not all message types are trackable, because that would * require a more sophisticated analysis. e.g., * function f(s) { s.MSG_UNNAMED_X = 'Some untrackable message'; } */ private void trackMessage( NodeTraversal t, JsMessage message, String msgName, Node msgNode, boolean isUnnamedMessage) { if (!isUnnamedMessage) { MessageLocation location = new MessageLocation(message, msgNode); messageNames.put(msgName, location); } else if (msgNode.isName()) { Var var = t.getScope().getVar(msgName); if (var != null) { unnamedMessages.put(var, message); } } } /** Get a previously tracked message. */ private JsMessage getTrackedMessage(NodeTraversal t, String msgName) { boolean isUnnamedMessage = isUnnamedMessageName(msgName); if (!isUnnamedMessage) { MessageLocation location = messageNames.get(msgName); return location == null ? null : location.message; } else { Var var = t.getScope().getVar(msgName); if (var != null) { return unnamedMessages.get(var); } } return null; } /** * Checks if message already processed. If so - it generates 'message * duplicated' compiler error. * * @param msgName the name of the message * @param msgNode the node that represents JS message */ private void checkIfMessageDuplicated(String msgName, Node msgNode) { if (messageNames.containsKey(msgName)) { MessageLocation location = messageNames.get(msgName); compiler.report(JSError.make(msgNode, MESSAGE_DUPLICATE_KEY, msgName, location.messageNode.getSourceFileName(), Integer.toString(location.messageNode.getLineno()))); } } /** * Creates a {@link JsMessage} for a JS message defined using a JS variable * declaration (e.g var MSG_X = ...;). * * @param builder the message builder * @param nameNode a NAME node for a JS message variable * @param parentNode a VAR node, parent of {@code nameNode} * @param grandParentNode the grandparent of {@code nameNode}. This node is * only used to get meta data about the message that might be * surrounding it (e.g. a message description). This argument may be * null if the meta data is not needed. * @throws MalformedException if {@code varNode} does not * correspond to a valid JS message VAR node */ private void extractMessageFromVariable( Builder builder, Node nameNode, Node parentNode, @Nullable Node grandParentNode) throws MalformedException { // Determine the message's value Node valueNode = nameNode.getFirstChild(); switch (valueNode.getType()) { case Token.STRING: case Token.ADD: maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, grandParentNode); builder.appendStringPart(extractStringFromStringExprNode(valueNode)); break; case Token.FUNCTION: maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode, grandParentNode); extractFromFunctionNode(builder, valueNode); break; case Token.CALL: maybeInitMetaDataFromJsDoc(builder, parentNode); extractFromCallNode(builder, valueNode); break; default: throw new MalformedException("Cannot parse value of message " + builder.getKey(), valueNode); } } /** * Creates a {@link JsMessage} for a JS message defined using an assignment to * a qualified name (e.g a.b.MSG_X = goog.getMsg(...);). * * @param builder the message builder * @param getPropNode a GETPROP node in a JS message assignment * @param assignNode an ASSIGN node, parent of {@code getPropNode}. * @throws MalformedException if {@code getPropNode} does not * correspond to a valid JS message node */ private void extractMessageFromProperty( Builder builder, Node getPropNode, Node assignNode) throws MalformedException { Node callNode = getPropNode.getNext(); maybeInitMetaDataFromJsDoc(builder, assignNode); extractFromCallNode(builder, callNode); } /** * Initializes the meta data in a JsMessage by examining the nodes just before * and after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param varNode the message VAR node * @param parentOfVarNode {@code varNode}'s parent node */ private void maybeInitMetaDataFromJsDocOrHelpVar( Builder builder, Node varNode, @Nullable Node parentOfVarNode) throws MalformedException { // First check description in @desc if (maybeInitMetaDataFromJsDoc(builder, varNode)) { return; } // Check the preceding node for meta data if ((parentOfVarNode != null) && maybeInitMetaDataFromHelpVar(builder, parentOfVarNode.getChildBefore(varNode))) { return; } // Check the subsequent node for meta data maybeInitMetaDataFromHelpVar(builder, varNode.getNext()); } /** * Initializes the meta data in a JsMessage by examining a node just before or * after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param sibling a node adjacent to the message VAR node * @return true iff message has corresponding description variable */ private boolean maybeInitMetaDataFromHelpVar(Builder builder, @Nullable Node sibling) throws MalformedException { if ((sibling != null) && (sibling.isVar())) { Node nameNode = sibling.getFirstChild(); String name = nameNode.getString(); if (name.equals(builder.getKey() + DESC_SUFFIX)) { Node valueNode = nameNode.getFirstChild(); String desc = extractStringFromStringExprNode(valueNode); if (desc.startsWith(HIDDEN_DESC_PREFIX)) { builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim()); builder.setIsHidden(true); } else { builder.setDesc(desc); } return true; } } return false; } /** * Initializes the meta data in a message builder given a node that may * contain JsDoc properties. * * @param builder the message builder whose meta data will be initialized * @param node the node with the message's JSDoc properties * @return true if message has JsDoc with valid description in @desc * annotation */ private boolean maybeInitMetaDataFromJsDoc(Builder builder, Node node) { boolean messageHasDesc = false; JSDocInfo info = node.getJSDocInfo(); if (info != null) { String desc = info.getDescription(); if (desc != null) { builder.setDesc(desc); messageHasDesc = true; } if (info.isHidden()) { builder.setIsHidden(true); } if (info.getMeaning() != null) { builder.setMeaning(info.getMeaning()); } } return messageHasDesc; } /** * Returns the string value associated with a node representing a JS string or * several JS strings added together (e.g. {@code 'str'} or {@code 's' + 't' + * 'r'}). * * @param node the node from where we extract the string * @return String representation of the node * @throws MalformedException if the parsed message is invalid */ private static String extractStringFromStringExprNode(Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: return node.getString(); case Token.ADD: StringBuilder sb = new StringBuilder(); for (Node child : node.children()) { sb.append(extractStringFromStringExprNode(child)); } return sb.toString(); default: throw new MalformedException("STRING or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a FUNCTION node. *

*

   * The tree should look something like:
   *
   * function
   *  |-- name
   *  |-- lp
   *  |   |-- name 
   *  |    -- name 
   *   -- block
   *      |
   *       --return
   *           |
   *            --add
   *               |-- string foo
   *                -- name 
   * 
* * @param builder the message builder * @param node the function node that contains a message * @throws MalformedException if the parsed message is invalid */ private void extractFromFunctionNode(Builder builder, Node node) throws MalformedException { Set phNames = Sets.newHashSet(); for (Node fnChild : node.children()) { switch (fnChild.getType()) { case Token.NAME: // This is okay. The function has a name, but it is empty. break; case Token.PARAM_LIST: // Parse the placeholder names from the function argument list. for (Node argumentNode : fnChild.children()) { if (argumentNode.isName()) { String phName = argumentNode.getString(); if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, argumentNode); } else { phNames.add(phName); } } } break; case Token.BLOCK: // Build the message's value by examining the return statement Node returnNode = fnChild.getFirstChild(); if (!returnNode.isReturn()) { throw new MalformedException("RETURN node expected; found: " + getReadableTokenName(returnNode), returnNode); } for (Node child : returnNode.children()) { extractFromReturnDescendant(builder, child); } // Check that all placeholders from the message text have appropriate // object literal keys for (String phName : builder.getPlaceholders()) { if(!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, returnNode); } } break; default: throw new MalformedException( "NAME, LP, or BLOCK node expected; found: " + getReadableTokenName(node), fnChild); } } } /** * Appends value parts to the message builder by traversing the descendants * of the given RETURN node. * * @param builder the message builder * @param node the node from where we extract a message * @throws MalformedException if the parsed message is invalid */ private void extractFromReturnDescendant(Builder builder, Node node) throws MalformedException { switch (node.getType()) { case Token.STRING: builder.appendStringPart(node.getString()); break; case Token.NAME: builder.appendPlaceholderReference(node.getString()); break; case Token.ADD: for (Node child : node.children()) { extractFromReturnDescendant(builder, child); } break; default: throw new MalformedException( "STRING, NAME, or ADD node expected; found: " + getReadableTokenName(node), node); } } /** * Initializes a message builder from a CALL node. *

* The tree should look something like: * *

   * call
   *  |-- getprop
   *  |   |-- name 'goog'
   *  |   +-- string 'getMsg'
   *  |
   *  |-- string 'Hi {$userName}! Welcome to {$product}.'
   *  +-- objlit
   *      |-- string 'userName'
   *      |-- name 'someUserName'
   *      |-- string 'product'
   *      +-- call
   *          +-- name 'getProductName'
   * 
* * @param builder the message builder * @param node the call node from where we extract the message * @throws MalformedException if the parsed message is invalid */ private void extractFromCallNode(Builder builder, Node node) throws MalformedException { // Check the function being called if (!node.isCall()) { throw new MalformedException( "Message must be initialized using " + MSG_FUNCTION_NAME + " function.", node); } Node fnNameNode = node.getFirstChild(); if (!MSG_FUNCTION_NAME.equals(fnNameNode.getQualifiedName())) { throw new MalformedException( "Message initialized using unrecognized function. " + "Please use " + MSG_FUNCTION_NAME + "() instead.", fnNameNode); } // Get the message string Node stringLiteralNode = fnNameNode.getNext(); if (stringLiteralNode == null) { throw new MalformedException("Message string literal expected", stringLiteralNode); } // Parse the message string and append parts to the builder parseMessageTextNode(builder, stringLiteralNode); Node objLitNode = stringLiteralNode.getNext(); Set phNames = Sets.newHashSet(); if (objLitNode != null) { // Register the placeholder names if (!objLitNode.isObjectLit()) { throw new MalformedException("OBJLIT node expected", objLitNode); } for (Node aNode = objLitNode.getFirstChild(); aNode != null; aNode = aNode.getNext()) { if (!aNode.isStringKey()) { throw new MalformedException("STRING_KEY node expected as OBJLIT key", aNode); } String phName = aNode.getString(); if (!isLowerCamelCaseWithNumericSuffixes(phName)) { throw new MalformedException( "Placeholder name not in lowerCamelCase: " + phName, aNode); } if (phNames.contains(phName)) { throw new MalformedException("Duplicate placeholder name: " + phName, aNode); } phNames.add(phName); } } // Check that all placeholders from the message text have appropriate objlit // values Set usedPlaceholders = builder.getPlaceholders(); for (String phName : usedPlaceholders) { if(!phNames.contains(phName)) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phName, objLitNode); } } // Check that objLiteral have only names that are present in the // message text for (String phName : phNames) { if(!usedPlaceholders.contains(phName)) { throw new MalformedException( "Unused message placeholder: " + phName, objLitNode); } } } /** * Appends the message parts in a JS message value extracted from the given * text node. * * @param builder the JS message builder to append parts to * @param node the node with string literal that contains the message text * @throws MalformedException if {@code value} contains a reference to * an unregistered placeholder */ private void parseMessageTextNode(Builder builder, Node node) throws MalformedException { String value = extractStringFromStringExprNode(node); while(true) { int phBegin = value.indexOf(PH_JS_PREFIX); if (phBegin < 0) { // Just a string literal builder.appendStringPart(value); return; } else { if (phBegin > 0) { // A string literal followed by a placeholder builder.appendStringPart(value.substring(0, phBegin)); } // A placeholder. Find where it ends int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); if (phEnd < 0) { throw new MalformedException( "Placeholder incorrectly formatted in: " + builder.getKey(), node); } String phName = value.substring(phBegin + PH_JS_PREFIX.length(), phEnd); builder.appendPlaceholderReference(phName); int nextPos = phEnd + PH_JS_SUFFIX.length(); if (nextPos < value.length()) { // Iterate on the rest of the message value value = value.substring(nextPos); } else { // The message is parsed return; } } } } /** Visit a call to goog.getMsgWithFallback. */ private void visitFallbackFunctionCall(NodeTraversal t, Node call) { // Check to make sure the function call looks like: // goog.getMsgWithFallback(MSG_1, MSG_2); if (call.getChildCount() != 3 || !call.getChildAtIndex(1).isName() || !call.getChildAtIndex(2).isName()) { compiler.report(t.makeError(call, BAD_FALLBACK_SYNTAX)); return; } Node firstArg = call.getChildAtIndex(1); JsMessage firstMessage = getTrackedMessage(t, firstArg.getString()); if (firstMessage == null) { compiler.report( t.makeError(firstArg, FALLBACK_ARG_ERROR, firstArg.getString())); return; } Node secondArg = firstArg.getNext(); JsMessage secondMessage = getTrackedMessage( t, call.getChildAtIndex(2).getString()); if (secondMessage == null) { compiler.report( t.makeError(secondArg, FALLBACK_ARG_ERROR, secondArg.getString())); return; } processMessageFallback(call, firstMessage, secondMessage); } /** * Processes found JS message. Several examples of "standard" processing * routines are: *
    *
  1. extract all JS messages *
  2. replace JS messages with localized versions for some specific language *
  3. check that messages have correct syntax and present in localization * bundle *
* * @param message the found message * @param definition the definition of the object and usually contains all * additional message information like message node/parent's node */ abstract void processJsMessage(JsMessage message, JsMessageDefinition definition); /** * Processes the goog.getMsgWithFallback primitive. * goog.getMsgWithFallback(MSG_1, MSG_2); * * By default, does nothing. */ void processMessageFallback(Node callNode, JsMessage message1, JsMessage message2) {} /** * Returns whether the given JS identifier is a valid JS message name. */ boolean isMessageName(String identifier, boolean isNewStyleMessage) { return identifier.startsWith(MSG_PREFIX) && (style == JsMessage.Style.CLOSURE || isNewStyleMessage || !identifier.endsWith(DESC_SUFFIX)); } /** * Returns whether the given message name is in the unnamed namespace. */ private static boolean isUnnamedMessageName(String identifier) { return MSG_UNNAMED_PATTERN.matcher(identifier).matches(); } /** * Returns whether a string is nonempty, begins with a lowercase letter, and * contains only digits and underscores after the first underscore. */ static boolean isLowerCamelCaseWithNumericSuffixes(String input) { return CAMELCASE_PATTERN.matcher(input).matches(); } /** * Returns human-readable name of the given node's type. */ private static String getReadableTokenName(Node node) { return Token.name(node.getType()); } /** * Converts the given string from upper-underscore case to lower-camel case, * preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" -> * "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23". */ static String toLowerCamelCaseWithNumericSuffixes(String input) { // Determine where the numeric suffixes begin int suffixStart = input.length(); while (suffixStart > 0) { char ch = '\0'; int numberStart = suffixStart; while (numberStart > 0) { ch = input.charAt(numberStart - 1); if (Character.isDigit(ch)) { numberStart--; } else { break; } } if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) { suffixStart = numberStart - 1; } else { break; } } if (suffixStart == input.length()) { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input); } else { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input.substring(0, suffixStart)) + input.substring(suffixStart); } } /** * Checks a node's type. * * @throws MalformedException if the node is null or the wrong type */ protected void checkNode(@Nullable Node node, int type) throws MalformedException { if (node == null) { throw new MalformedException( "Expected node type " + type + "; found: null", node); } if (node.getType() != type) { throw new MalformedException( "Expected node type " + type + "; found: " + node.getType(), node); } } static class MalformedException extends Exception { private static final long serialVersionUID = 1L; private final Node node; MalformedException(String message, Node node) { super(message); this.node = node; } Node getNode() { return node; } } private static class MessageLocation { private final JsMessage message; private final Node messageNode; private MessageLocation(JsMessage message, Node messageNode) { this.message = message; this.messageNode = messageNode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ControlFlowGraph.java0000644000175000017500000001515412115204405026737 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Comparator; /** * Control flow graph. * * * @param The instruction type of the control flow graph. */ class ControlFlowGraph extends LinkedDirectedGraph { /** * A special node marked by the node value key null to a singleton * "return" when control is transferred outside of the current control flow * graph. */ private final DiGraphNode implicitReturn; private final DiGraphNode entry; /** * Constructor. */ ControlFlowGraph( N entry, boolean nodeAnnotations, boolean edgeAnnotations) { super(nodeAnnotations, edgeAnnotations); implicitReturn = createDirectedGraphNode(null); this.entry = createDirectedGraphNode(entry); } /** * Gets the implicit return node. * * @return Return node. */ public DiGraphNode getImplicitReturn() { return implicitReturn; } /** * Gets the entry point of the control flow graph. In general, this should be * the beginning of the global script or beginning of a function. * * @return The entry point. */ public DiGraphNode getEntry() { return entry; } /** * Checks whether node is the implicit return. * * @param node Node. * @return True if the node is the implicit return. */ public boolean isImplicitReturn( DiGraphNode node) { return node == implicitReturn; } /** * Connects the node to the explicit return. * * @param srcValue Node. * @param edgeValue Edge. */ public void connectToImplicitReturn(N srcValue, Branch edgeValue) { super.connect(srcValue, edgeValue, null); } /** * Gets a comparator for the nodes. The default implementation returns * {@code null}. See {@link ControlFlowGraph#getOptionalNodeComparator}. * @param isForward Whether the comparator sorts the nodes in the direction of * the flow. * @return a comparator or null (in particular, if not overridden) */ public Comparator> getOptionalNodeComparator( boolean isForward) { return null; } /** * The edge object for the control flow graph. */ public static enum Branch { /** Edge is taken if the condition is true. */ ON_TRUE, /** Edge is taken if the condition is false. */ ON_FALSE, /** Unconditional branch. */ UNCOND, /** * Exception-handling code paths. * Conflates two kind of control flow passing: * - An exception is thrown, and falls into a catch or finally block * - During exception handling, a finally block finishes and control * passes to the next finally block. * In theory, we need 2 different edge types. In practice, we * can just treat them as "the edges we can't really optimize". */ ON_EX, /** Possible folded-away template */ SYN_BLOCK; public boolean isConditional() { return this == ON_TRUE || this == ON_FALSE; } } /** * Abstract callback to visit a control flow graph node without going into * subtrees of the node that are also represented by other * control flow graph nodes. * *

For example, traversing an IF node as root will visit the two subtrees * pointed by the {@link ControlFlowGraph.Branch#ON_TRUE} and * {@link ControlFlowGraph.Branch#ON_FALSE} edges. */ public abstract static class AbstractCfgNodeTraversalCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { if (parent == null) { return true; } return !isEnteringNewCfgNode(n); } } /** * @return True if n should be represented by a new CFG node in the control * flow graph. */ public static boolean isEnteringNewCfgNode(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.SCRIPT: case Token.TRY: return true; case Token.FUNCTION: // A function node represents the start of a function where the name // bleeds into the local scope and parameters are assigned // to the formal argument names. The node includes the name of the // function and the LP list since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body, represented by the second // child. return n != parent.getFirstChild().getNext(); case Token.WHILE: case Token.DO: case Token.IF: // These control structures are represented by a node that holds the // condition. Each of them is a branch node based on its condition. return NodeUtil.getConditionExpression(parent) != n; case Token.FOR: // The FOR(;;) node differs from other control structures in that // it has an initialization and an increment statement. Those // two statements have corresponding CFG nodes to represent them. // The FOR node only represents the condition check for each iteration. // That way the following: // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } if (NodeUtil.isForIn(parent)) { // TODO(user): Investigate how we should handle the case where // we have a very complex expression inside the FOR-IN header. return n != parent.getFirstChild(); } else { return NodeUtil.getConditionExpression(parent) != n; } case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.WITH: return n != parent.getFirstChild(); default: return false; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JSModuleGraph.java0000644000175000017500000004410012115204405026142 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.deps.SortedDependencies; import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException; import com.google.javascript.jscomp.deps.SortedDependencies.MissingProvideException; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * A {@link JSModule} dependency graph that assigns a depth to each module and * can answer depth-related queries about them. For the purposes of this class, * a module's depth is defined as the number of hops in the longest path from * the module to a module with no dependencies. * */ public class JSModuleGraph { private List modules; /** * Lists of modules at each depth. modulesByDepth.get(3) is a * list of the modules at depth 3, for example. */ private List> modulesByDepth; /** * dependencyMap is a cache of dependencies that makes the dependsOn * function faster. Each map entry associates a starting * JSModule with the set of JSModules that are transitively dependent on the * starting module. * * If the cache returns null, then the entry hasn't been filled in for that * module. * * dependencyMap should be filled from leaf to root so that * getTransitiveDepsDeepestFirst can use its results directly. */ private Map> dependencyMap = Maps.newHashMap(); /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(JSModule[] modulesInDepOrder) { this(ImmutableList.copyOf(modulesInDepOrder)); } /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(List modulesInDepOrder) { Preconditions.checkState( modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(), "Found duplicate modules"); modules = ImmutableList.copyOf(modulesInDepOrder); modulesByDepth = Lists.newArrayList(); for (JSModule module : modulesInDepOrder) { int depth = 0; for (JSModule dep : module.getDependencies()) { int depDepth = dep.getDepth(); if (depDepth < 0) { throw new ModuleDependenceException(String.format( "Modules not in dependency order: %s preceded %s", module.getName(), dep.getName()), module, dep); } depth = Math.max(depth, depDepth + 1); } module.setDepth(depth); if (depth == modulesByDepth.size()) { modulesByDepth.add(new ArrayList()); } modulesByDepth.get(depth).add(module); } } /** * Gets an iterable over all modules in dependency order. */ Iterable getAllModules() { return modules; } /** * Gets all modules indexed by name. */ Map getModulesByName() { Map result = Maps.newHashMap(); for (JSModule m : modules) { result.put(m.getName(), m); } return result; } /** * Gets the total number of modules. */ int getModuleCount() { return modules.size(); } /** * Gets the root module. */ JSModule getRootModule() { return Iterables.getOnlyElement(modulesByDepth.get(0)); } /** * Returns a JSON representation of the JSModuleGraph. Specifically a * JSONArray of "Modules" where each module has a * - "name" * - "dependencies" (list of module names) * - "transitive-dependencies" (list of module names, deepest first) * - "inputs" (list of file names) * @return List of module JSONObjects. */ JSONArray toJson() { JSONArray modules = new JSONArray(); for (JSModule module : getAllModules()) { JSONObject node = new JSONObject(); try { node.put("name", module.getName()); JSONArray deps = new JSONArray(); node.put("dependencies", deps); for (JSModule m : module.getDependencies()) { deps.put(m.getName()); } JSONArray transitiveDeps = new JSONArray(); node.put("transitive-dependencies", transitiveDeps); for (JSModule m : getTransitiveDepsDeepestFirst(module)) { transitiveDeps.put(m.getName()); } JSONArray inputs = new JSONArray(); node.put("inputs", inputs); for (CompilerInput input : module.getInputs()) { inputs.put(input.getSourceFile().getOriginalPath()); } modules.put(node); } catch (JSONException e) { Throwables.propagate(e); } } return modules; } /** * Determines whether this module depends on a given module. Note that a * module never depends on itself, as that dependency would be cyclic. */ public boolean dependsOn(JSModule src, JSModule m) { Set deps = dependencyMap.get(src); if (deps == null) { deps = getTransitiveDepsDeepestFirst(src); dependencyMap.put(src, deps); } return deps.contains(m); } /** * Finds the deepest common dependency of two modules, not including the two * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) { int m1Depth = m1.getDepth(); int m2Depth = m2.getDepth(); // According our definition of depth, the result must have a strictly // smaller depth than either m1 or m2. for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) { List modulesAtDepth = modulesByDepth.get(depth); // Look at the modules at this depth in reverse order, so that we use the // original ordering of the modules to break ties (later meaning deeper). for (int i = modulesAtDepth.size() - 1; i >= 0; i--) { JSModule m = modulesAtDepth.get(i); if (dependsOn(m1, m) && dependsOn(m2, m)) { return m; } } } return null; } /** * Finds the deepest common dependency of two modules, including the * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ public JSModule getDeepestCommonDependencyInclusive( JSModule m1, JSModule m2) { if (m2 == m1 || dependsOn(m2, m1)) { return m1; } else if (dependsOn(m1, m2)) { return m2; } return getDeepestCommonDependency(m1, m2); } /** Returns the deepest common dependency of the given modules. */ public JSModule getDeepestCommonDependencyInclusive( Collection modules) { Iterator iter = modules.iterator(); JSModule dep = iter.next(); while (iter.hasNext()) { dep = getDeepestCommonDependencyInclusive(dep, iter.next()); } return dep; } /** * Creates an iterable over the transitive dependencies of module {@code m} * in a non-increasing depth ordering. The result does not include the module * {@code m}. * * @param m A module in this graph * @return The transitive dependencies of module {@code m} */ Set getTransitiveDepsDeepestFirst(JSModule m) { Set deps = dependencyMap.get(m); if (deps != null) { return deps; } deps = new TreeSet(new InverseDepthComparator()); addDeps(deps, m); dependencyMap.put(m, deps); return deps; } /** * Adds a module's transitive dependencies to a set. */ private void addDeps(Set deps, JSModule m) { for (JSModule dep : m.getDependencies()) { deps.add(dep); addDeps(deps, dep); } } /** * Replaces any files that are found multiple times with a single instance in * the closest parent module that is common to all modules where it appears. * * JSCompiler normally errors if you attempt to compile modules containing the * same file. This method can be used to remove duplicates before compiling * to avoid such an error. */ public void coalesceDuplicateFiles() { Multimap fileRefs = LinkedHashMultimap.create(); for (JSModule module : modules) { for (CompilerInput jsFile : module.getInputs()) { fileRefs.put(jsFile.getName(), module); } } for (String path : fileRefs.keySet()) { Collection refModules = fileRefs.get(path); if (refModules.size() > 1) { JSModule depModule = getDeepestCommonDependencyInclusive(refModules); CompilerInput file = refModules.iterator().next().getByName(path); for (JSModule module : refModules) { if (module != depModule) { module.removeByName(path); } } if (!refModules.contains(depModule)) { depModule.add(file); } } } } /** * Applies a DependencyOptions in "dependency sorting" and "dependency pruning" * mode to the given list of inputs. Returns a new list with the files sorted * and removed. This module graph will be updated to reflect the new list. * * If you need more fine-grained dependency management, you should create your * own DependencyOptions and call * {@code manageDependencies(DependencyOptions, List)}. * * @param entryPoints The entry points into the program. * Expressed as JS symbols. * @param inputs The original list of sources. Used to ensure that the sort * is stable. * @throws CircularDependencyException if there is a circular dependency * between the provides and requires. * @throws MissingProvideException if an entry point was not provided * by any of the inputs. * @see DependencyOptions for more info on how this works. */ public List manageDependencies( List entryPoints, List inputs) throws CircularDependencyException, MissingModuleException, MissingProvideException { DependencyOptions depOptions = new DependencyOptions(); depOptions.setDependencySorting(true); depOptions.setDependencyPruning(true); depOptions.setEntryPoints(entryPoints); return manageDependencies(depOptions, inputs); } /** * Apply the dependency options to the list of sources, returning a new * source list re-ordering and dropping files as necessary. * This module graph will be updated to reflect the new list. * * @param inputs The original list of sources. Used to ensure that the sort * is stable. * @throws CircularDependencyException if there is a circular dependency * between the provides and requires. * @throws MissingProvideException if an entry point was not provided * by any of the inputs. * @see DependencyOptions for more info on how this works. */ public List manageDependencies( DependencyOptions depOptions, List inputs) throws CircularDependencyException, MissingProvideException, MissingModuleException { SortedDependencies sorter = new SortedDependencies(inputs); Iterable entryPointInputs = createEntryPointInputs( depOptions, inputs, sorter); // The order of inputs, sorted independently of modules. List absoluteOrder = sorter.getDependenciesOf(inputs, depOptions.shouldSortDependencies()); // Figure out which sources *must* be in each module. ListMultimap entryPointInputsPerModule = LinkedListMultimap.create(); for (CompilerInput input : entryPointInputs) { JSModule module = input.getModule(); Preconditions.checkNotNull(module); entryPointInputsPerModule.put(module, input); } // Clear the modules of their inputs. This also nulls out // the input's reference to its module. for (JSModule module : getAllModules()) { module.removeAll(); } // Figure out which sources *must* be in each module, or in one // of that module's dependencies. for (JSModule module : entryPointInputsPerModule.keySet()) { List transitiveClosure = sorter.getDependenciesOf( entryPointInputsPerModule.get(module), depOptions.shouldSortDependencies()); for (CompilerInput input : transitiveClosure) { JSModule oldModule = input.getModule(); if (oldModule == null) { input.setModule(module); } else { input.setModule(null); input.setModule( getDeepestCommonDependencyInclusive(oldModule, module)); } } } // All the inputs are pointing to the modules that own them. Yeah! // Update the modules to reflect this. for (CompilerInput input : absoluteOrder) { JSModule module = input.getModule(); if (module != null) { module.add(input); } } // Now, generate the sorted result. List result = Lists.newArrayList(); for (JSModule module : getAllModules()) { result.addAll(module.getInputs()); } return result; } private Collection createEntryPointInputs( DependencyOptions depOptions, List inputs, SortedDependencies sorter) throws MissingModuleException, MissingProvideException { Set entryPointInputs = Sets.newLinkedHashSet(); Map modulesByName = getModulesByName(); if (depOptions.shouldPruneDependencies()) { if (!depOptions.shouldDropMoochers()) { entryPointInputs.addAll(sorter.getInputsWithoutProvides()); } for (String entryPoint : depOptions.getEntryPoints()) { // An entry point is either formatted as: // 'foo.bar' - peg foo.bar to its current module // 'modC:foo.bar' - peg foo.bar to modC String inputName = entryPoint; int splitPoint = entryPoint.indexOf(':'); CompilerInput entryPointInput = null; if (splitPoint != -1) { String moduleName = entryPoint.substring(0, splitPoint); inputName = entryPoint.substring( Math.min(splitPoint + 1, entryPoint.length() - 1)); JSModule module = modulesByName.get(moduleName); if (module == null) { throw new MissingModuleException(moduleName); } else { entryPointInput = sorter.getInputProviding(inputName); entryPointInput.overrideModule(module); } } else { entryPointInput = sorter.getInputProviding(inputName); } entryPointInputs.add(entryPointInput); } CompilerInput baseJs = sorter.maybeGetInputProviding("goog"); if (baseJs != null) { entryPointInputs.add(baseJs); } } else { entryPointInputs.addAll(inputs); } return entryPointInputs; } LinkedDirectedGraph toGraphvizGraph() { LinkedDirectedGraph graphViz = LinkedDirectedGraph.create(); for (JSModule module : getAllModules()) { graphViz.createNode(module); for (JSModule dep : module.getDependencies()) { graphViz.createNode(dep); graphViz.connect(module, "->", dep); } } return graphViz; } /** * A module depth comparator that considers a deeper module to be "less than" * a shallower module. Uses module names to consistently break ties. */ private class InverseDepthComparator implements Comparator { @Override public int compare(JSModule m1, JSModule m2) { return depthCompare(m2, m1); } } private int depthCompare(JSModule m1, JSModule m2) { if (m1 == m2) { return 0; } int d1 = m1.getDepth(); int d2 = m2.getDepth(); return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1; } /* * Exception class for declaring when the modules being fed into a * JSModuleGraph as input aren't in dependence order, and so can't be * processed for caching of various dependency-related queries. */ protected static class ModuleDependenceException extends IllegalArgumentException { private static final long serialVersionUID = 1; private final JSModule module; private final JSModule dependentModule; protected ModuleDependenceException(String message, JSModule module, JSModule dependentModule) { super(message); this.module = module; this.dependentModule = dependentModule; } public JSModule getModule() { return module; } public JSModule getDependentModule() { return dependentModule; } } public static class MissingModuleException extends Exception { MissingModuleException(String moduleName) { super(moduleName); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckRequiresForConstructors.java0000644000175000017500000001663512115204405031347 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * This pass walks the AST to create a Collection of 'new' nodes and * 'goog.require' nodes. It reconciles these Collections, creating a * warning for each discrepancy. * */ class CheckRequiresForConstructors implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CodingConvention codingConvention; private final CheckLevel level; // Warnings static final DiagnosticType MISSING_REQUIRE_WARNING = DiagnosticType.disabled( "JSC_MISSING_REQUIRE_WARNING", "''{0}'' used but not goog.require''d"); CheckRequiresForConstructors(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.codingConvention = compiler.getCodingConvention(); this.level = level; } /** * Uses Collections of new and goog.require nodes to create a compiler warning * for each new class name without a corresponding goog.require(). */ @Override public void process(Node externs, Node root) { Callback callback = new CheckRequiresForConstructorsCallback(); new NodeTraversal(compiler, callback).traverseRoots(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Callback callback = new CheckRequiresForConstructorsCallback(); new NodeTraversal(compiler, callback).traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); } // Return true if the name is a class name (starts with an uppercase // character, but is not in all caps). private static boolean isClassName(String name) { return (name != null && name.length() > 1 && Character.isUpperCase(name.charAt(0)) && !name.equals(name.toUpperCase())); } // Return the shortest prefix of the className that refers to a class, // or null if no part refers to a class. private static String getOutermostClassName(String className) { for (String part : className.split("\\.")) { if (isClassName(part)) { return className.substring(0, className.indexOf(part) + part.length()); } } return null; } /** * This class "records" each constructor and goog.require visited and creates * a warning for each new node without an appropriate goog.require node. * */ private class CheckRequiresForConstructorsCallback implements Callback { private final List constructors = Lists.newArrayList(); private final List requires = Lists.newArrayList(); private final List newNodes = Lists.newArrayList(); @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return parent == null || !parent.isScript() || !t.getInput().isExtern(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo info; switch (n.getType()) { case Token.ASSIGN: info = (JSDocInfo) n.getProp(Node.JSDOC_INFO_PROP); if (info != null && info.isConstructor()) { String qualifiedName = n.getFirstChild().getQualifiedName(); constructors.add(qualifiedName); } break; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { if (parent.isName()) { String functionName = parent.getString(); info = (JSDocInfo) parent.getProp(Node.JSDOC_INFO_PROP); if (info != null && info.isConstructor()) { constructors.add(functionName); } else { Node gramps = parent.getParent(); Preconditions.checkState( gramps != null && gramps.isVar()); info = (JSDocInfo) gramps.getProp(Node.JSDOC_INFO_PROP); if (info != null && info.isConstructor()) { constructors.add(functionName); } } } } else { info = (JSDocInfo) n.getProp(Node.JSDOC_INFO_PROP); if (info != null && info.isConstructor()) { String functionName = n.getFirstChild().getString(); constructors.add(functionName); } } break; case Token.CALL: visitCallNode(n, parent); break; case Token.SCRIPT: visitScriptNode(t); break; case Token.NEW: visitNewNode(t, n); } } private void visitScriptNode(NodeTraversal t) { Set classNames = Sets.newHashSet(); for (Node node : newNodes) { String className = node.getFirstChild().getQualifiedName(); String outermostClassName = getOutermostClassName(className); boolean notProvidedByConstructors = (constructors == null || !constructors.contains(className)); boolean notProvidedByRequires = (requires == null || (!requires.contains(className) && !requires.contains(outermostClassName))); if (notProvidedByConstructors && notProvidedByRequires && !classNames.contains(className)) { compiler.report( t.makeError(node, level, MISSING_REQUIRE_WARNING, className)); classNames.add(className); } } // for the next script, if there is one, we don't want the new, ctor, and // require nodes to spill over. this.newNodes.clear(); this.requires.clear(); this.constructors.clear(); } private void visitCallNode(Node n, Node parent) { String required = codingConvention.extractClassNameIfRequire(n, parent); if (required != null) { requires.add(required); } } private void visitNewNode(NodeTraversal t, Node n) { Node qNameNode = n.getFirstChild(); // If the ctor is something other than a qualified name, ignore it. if (!qNameNode.isQualifiedName()) { return; } // Grab the root ctor namespace. Node nameNode = qNameNode; for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {} // We only consider programmer-defined constructors that are // global variables, or are defined on global variables. if (!nameNode.isName()) { return; } String name = nameNode.getString(); Scope.Var var = t.getScope().getVar(name); if (var == null || var.isLocal() || var.isExtern()) { return; } newNodes.add(n); } } } ././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExtractPrototypeMemberDeclarations.0000644000175000017500000003033612115204405031663 0ustar apoapo/* * 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.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.LinkedList; import java.util.List; /** * When there are multiple prototype member declarations to the same class, * use a temp variable to alias the prototype object. * * Example: * *

 * function B() { ... }                 \
 * B.prototype.foo = function() { ... }  \___ {@link ExtractionInstance}
 * ...                                   /
 * B.prototype.bar = function() { ... } /
 *          ^---------------------------------{@link PrototypeMemberDeclaration}
 * 
*

becomes *

 * function B() { ... }
 * x = B.prototype;
 * x.foo = function() { ... }
 * ...
 * x.bar = function() { ... }
 * 
* *

Works almost like a redundant load elimination but limited to only * recognizing the class prototype declaration idiom. First it only works within * a basic block because we avoided {@link DataFlowAnalysis} for compilation * performance. Secondly, we can avoid having to compute how long to * sub-expressing has to be. Example: *

 * a.b.c.d = ...
 * a.b.c = ...
 * a.b = ...
 * a.b.c = ...
 * 
*

Further more, we only introduce one temp variable to hold a single * prototype at a time. So all the {@link PrototypeMemberDeclaration} * to be extracted must be in a single line. We call this a single * {@link ExtractionInstance}. * *

Alternatively, for users who do not want a global variable to be * introduced, we will create an anonymous function instead. *

 * function B() { ... }
 * (function (x) {
 *   x.foo = function() { ... }
 *   ...
 *   x.bar = function() { ... }
 * )(B.prototype)
 * 
* * The RHS of the declarations can have side effects, however, one good way to * break this is the following: *
 * function B() { ... }
 * B.prototype.foo = (function() { B.prototype = somethingElse(); return 0 })();
 * ...
 * 
* Such logic is highly unlikely and we will assume that it never occurs. * */ class ExtractPrototypeMemberDeclarations implements CompilerPass { // The name of variable that will temporary hold the pointer to the prototype // object. Of cause, we assume that it'll be renamed by RenameVars. private String prototypeAlias = "JSCompiler_prototypeAlias"; private final AbstractCompiler compiler; private final Pattern pattern; enum Pattern { USE_GLOBAL_TEMP( // Global Overhead. // We need a temp variable to hold all the prototype. "var t;".length(), // Per Extract overhead: // Every extraction instance must first use the temp variable to point // to the prototype object. "t=y.prototype;".length(), // TODO(user): Check to to see if AliasExterns is on // The gain we get per prototype declaration. Assuming it can be // aliased. "t.y=".length() - "x[p].y=".length()), USE_ANON_FUNCTION( // Global Overhead: 0, // Per-extraction overhead: // This is the cost of a single anoynmous function. "(function(t){})(y.prototype);".length(), // Per-prototype member declaration overhead: // Here we assumes that they don't have AliasExterns on (in SIMPLE mode). "t.y=".length() - "x.prototype.y=".length()); private final int globalOverhead; private final int perExtractionOverhead; private final int perMemberOverhead; Pattern(int globalOverHead, int perExtractionOverhead, int perMemberOverhead) { this.globalOverhead = globalOverHead; this.perExtractionOverhead = perExtractionOverhead; this.perMemberOverhead = perMemberOverhead; } } ExtractPrototypeMemberDeclarations(AbstractCompiler compiler, Pattern pattern) { this.compiler = compiler; this.pattern = pattern; } @Override public void process(Node externs, Node root) { GatherExtractionInfo extractionInfo = new GatherExtractionInfo(); NodeTraversal.traverse(compiler, root, extractionInfo); if (extractionInfo.shouldExtract()) { doExtraction(extractionInfo); compiler.reportCodeChange(); } } /** * Declares the temp variable to point to prototype objects and iterates * through all ExtractInstance and performs extraction there. */ private void doExtraction(GatherExtractionInfo info) { // Insert a global temp if we are using the USE_GLOBAL_TEMP pattern. if (pattern == Pattern.USE_GLOBAL_TEMP) { Node injectionPoint = compiler.getNodeForCodeInsertion(null); Node var = NodeUtil.newVarNode(prototypeAlias, null) .copyInformationFromForTree(injectionPoint); injectionPoint.addChildrenToFront(var); } // Go through all extraction instances and extract each of them. for (ExtractionInstance instance : info.instances) { extractInstance(instance); } } /** * At a given ExtractionInstance, stores and prototype object in the temp * variable and rewrite each member declaration to assign to the temp variable * instead. */ private void extractInstance(ExtractionInstance instance) { PrototypeMemberDeclaration first = instance.declarations.getFirst(); String className = first.qualifiedClassName; if (pattern == Pattern.USE_GLOBAL_TEMP) { // Use the temp variable to hold the prototype. Node stmt = new Node(first.node.getType(), IR.assign( IR.name(prototypeAlias), NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), className + ".prototype", instance.parent, className + ".prototype"))) .copyInformationFromForTree(first.node); instance.parent.addChildBefore(stmt, first.node); } else if (pattern == Pattern.USE_ANON_FUNCTION){ Node block = IR.block(); Node func = IR.function( IR.name(""), IR.paramList(IR.name(prototypeAlias)), block); Node call = IR.call(func, NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), className + ".prototype", instance.parent, className + ".prototype")); call.putIntProp(Node.FREE_CALL, 1); Node stmt = new Node(first.node.getType(), call); stmt.copyInformationFromForTree(first.node); instance.parent.addChildBefore(stmt, first.node); for (PrototypeMemberDeclaration declar : instance.declarations) { block.addChildToBack(declar.node.detachFromParent()); } } // Go thought each member declaration and replace it with an assignment // to the prototype variable. for (PrototypeMemberDeclaration declar : instance.declarations) { replacePrototypeMemberDeclaration(declar); } } /** * Replaces a member declaration to an assignment to the temp prototype * object. */ private void replacePrototypeMemberDeclaration( PrototypeMemberDeclaration declar) { // x.prototype.y = ... -> t.y = ... Node assignment = declar.node.getFirstChild(); Node lhs = assignment.getFirstChild(); Node name = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), prototypeAlias + "." + declar.memberName, declar.node, declar.memberName); // Save the full prototype path on the left hand side of the assignment // for debugging purposes. // declar.lhs = x.prototype.y so first child of the first child // is 'x'. Node accessNode = declar.lhs.getFirstChild().getFirstChild(); Object originalName = accessNode.getProp(Node.ORIGINALNAME_PROP); String className = "?"; if (originalName != null) { className = originalName.toString(); } NodeUtil.setDebugInformation(name.getFirstChild(), lhs, className + ".prototype"); assignment.replaceChild(lhs, name); } /** * Collects all the possible extraction instances in a node traversal. */ private class GatherExtractionInfo extends AbstractShallowCallback { private List instances = Lists.newLinkedList(); private int totalDelta = pattern.globalOverhead; @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isScript() && !n.isBlock()) { return; } for (Node cur = n.getFirstChild(); cur != null; cur = cur.getNext()) { PrototypeMemberDeclaration prototypeMember = PrototypeMemberDeclaration.extractDeclaration(cur); if (prototypeMember == null) { continue; } // Found a good site here. The constructor will computes the chain of // declarations that is qualified for extraction. ExtractionInstance instance = new ExtractionInstance(prototypeMember, n); cur = instance.declarations.getLast().node; // Only add it to our work list if the extraction at this instance // makes the code smaller. if (instance.isFavorable()) { instances.add(instance); totalDelta += instance.delta; } } } /** * @return <@code true> if the sum of all the extraction instance gain * outweighs the overhead of the temp variable declaration. */ private boolean shouldExtract() { return totalDelta < 0; } } private class ExtractionInstance { LinkedList declarations = Lists.newLinkedList(); private int delta = 0; private final Node parent; private ExtractionInstance(PrototypeMemberDeclaration head, Node parent) { this.parent = parent; declarations.add(head); delta = pattern.perExtractionOverhead + pattern.perMemberOverhead; for (Node cur = head.node.getNext(); cur != null; cur = cur.getNext()) { // We can skip over any named functions because they have no effect on // the control flow. In fact, they are lifted to the beginning of the // block. This happens a lot when devirtualization breaks the whole // chain. if (cur.isFunction()) { continue; } PrototypeMemberDeclaration prototypeMember = PrototypeMemberDeclaration.extractDeclaration(cur); if (prototypeMember == null || !head.isSameClass(prototypeMember)) { break; } declarations.add(prototypeMember); delta += pattern.perMemberOverhead; } } /** * @return {@code true} if extracting all the declarations at this instance * will overweight the overhead of aliasing the prototype object. */ boolean isFavorable() { return delta <= 0; } } /** * Abstraction for a prototype member declaration. * *

{@code a.b.c.prototype.d = ....} */ private static class PrototypeMemberDeclaration { final String memberName; final Node node; final String qualifiedClassName; final Node lhs; private PrototypeMemberDeclaration(Node lhs, Node node) { this.lhs = lhs; this.memberName = NodeUtil.getPrototypePropertyName(lhs); this.node = node; this.qualifiedClassName = NodeUtil.getPrototypeClassName(lhs).getQualifiedName(); } private boolean isSameClass(PrototypeMemberDeclaration other) { return qualifiedClassName.equals(other.qualifiedClassName); } /** * @return A prototype member declaration representation if there is one * else it returns {@code null}. */ private static PrototypeMemberDeclaration extractDeclaration(Node n) { if (!NodeUtil.isPrototypePropertyDeclaration(n)) { return null; } Node lhs = n.getFirstChild().getFirstChild(); return new PrototypeMemberDeclaration(lhs, n); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FieldCleanupPass.java0000644000175000017500000001077612115204405026674 0ustar apoapo/* * Copyright 2011 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.AbstractShallowCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; /** * A CleanupPass implementation that will remove all field declarations on * JSTypes contributed by the original file. *

* This pass is expected to clear out declarations contributed to any JSType, * even if the constructor declaration is not provided in the file being * updated. * * @author tylerg@google.com (Tyler Goodwin) */ public class FieldCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public FieldCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { String srcName = originalRoot.getSourceFileName(); Callback cb = new QualifiedNameSearchTraversal(compiler.getTypeRegistry(), srcName); new NodeTraversal(compiler, cb).traverse(originalRoot); } @Override public void process(Node externs, Node root) { // FieldCleanupPass should not do work during process. } /** * Search for fields to cleanup by looking for nodes in the tree which are * root nodes of qualified names and getting the final token of the qualified * name as a candidate field. *

* Once a candidate field is found, ask the {@code JSTypeRegistry} for all * JSTypes that have a field with the same name, and check if the field on * that type is defined in the file the compiler is cleaning up. If so, remove * the field, and update the {@code JSTypeRegistry} to no longer associate the * type with the field. *

* This algorithm was chosen for simplicity and is less than optimally * efficient in two ways: *

* 1) All types with a matching field name are iterated over (when only types * that extend or implement the JSType indicated by the containing object in * the found Qualified Name need to be checked). *

* 2) All Qualified Names are checked, even those which are not L-Values or * single declarations of an Type Expression. In general field should only be * declared as part of an assignment ('ns.Type.a = 3;') or stand alone name * declaration ('ns.Type.a;'). */ static class QualifiedNameSearchTraversal extends AbstractShallowCallback { private final JSTypeRegistry typeRegistry; private final String srcName; public QualifiedNameSearchTraversal( JSTypeRegistry typeRegistry, String srcName) { this.typeRegistry = typeRegistry; this.srcName = srcName; } @Override public void visit(NodeTraversal t, Node n, Node p) { // We are a root GetProp if (n.isGetProp() && !p.isGetProp()) { String propName = getFieldName(n); JSType type = n.getFirstChild().getJSType(); if (type == null || type.toObjectType() == null) { // Note cases like .field return; } removeProperty(type.toObjectType(), propName); } if (n.getJSDocInfo() != null) { n.getJSDocInfo().setAssociatedNode(null); } } /** * Removes a given property from a type and updates type-registry. * * @param type the object type to be updated, should not be null * @param propName the property to remove */ private void removeProperty(ObjectType type, String propName) { Node pNode = type.getPropertyNode(propName); if (pNode != null && srcName.equals(pNode.getSourceFileName())) { typeRegistry.unregisterPropertyOnType(propName, type); type.removeProperty(propName); } } private String getFieldName(Node n) { return n.getLastChild().getString(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/StripCode.java0000644000175000017500000005032612115204405025401 0ustar apoapo/* * Copyright 2007 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 javax.annotation.Nullable; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * A pass for stripping a list of provided JavaScript object types. * * The stripping strategy is as follows: * - Provide: 1) a list of types that should be stripped, and 2) a list of * suffixes of field/variable names that should be stripped. * - Remove declarations of variables that are initialized using static * methods of strip types (e.g. var x = goog.debug.Logger.getLogger(...);). * - Remove all references to variables that are stripped. * - Remove all object literal keys with strip names. * - Remove all assignments to 1) field names that are strip names and * 2) qualified names that begin with strip types. * - Remove all statements containing calls to static methods of strip types. * */ class StripCode implements CompilerPass { // TODO(user): Try eliminating the need for a list of strip names by instead // recording which field names are assigned to debug types in each JS input. private final AbstractCompiler compiler; private final Set stripTypes; private final Set stripNameSuffixes; private final Set stripTypePrefixes; private final Set stripNamePrefixes; private final Set varsToRemove; static final DiagnosticType STRIP_TYPE_INHERIT_ERROR = DiagnosticType.error( "JSC_STRIP_TYPE_INHERIT_ERROR", "Non-strip type {0} cannot inherit from strip type {1}"); static final DiagnosticType STRIP_ASSIGNMENT_ERROR = DiagnosticType.error( "JSC_STRIP_ASSIGNMENT_ERROR", "Unable to strip assignment to {0}"); /** * Creates an instance. * * @param compiler The compiler */ StripCode(AbstractCompiler compiler, Set stripTypes, Set stripNameSuffixes, Set stripTypePrefixes, Set stripNamePrefixes) { this.compiler = compiler; this.stripTypes = Sets.newHashSet(stripTypes); this.stripNameSuffixes = Sets.newHashSet(stripNameSuffixes); this.stripTypePrefixes = Sets.newHashSet(stripTypePrefixes); this.stripNamePrefixes = Sets.newHashSet(stripNamePrefixes); this.varsToRemove = Sets.newHashSet(); } /** * Enables stripping of goog.tweak functions. */ public void enableTweakStripping() { stripTypes.add("goog.tweak"); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new Strip()); } // ------------------------------------------------------------------------- /** * A callback that strips debug code from a JavaScript parse tree. */ private class Strip extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.VAR: removeVarDeclarationsByNameOrRvalue(t, n, parent); break; case Token.NAME: maybeRemoveReferenceToRemovedVariable(t, n, parent); break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: maybeEliminateAssignmentByLvalueName(t, n, parent); break; case Token.CALL: case Token.NEW: maybeRemoveCall(t, n, parent); break; case Token.OBJECTLIT: eliminateKeysWithStripNamesFromObjLit(t, n); break; case Token.EXPR_RESULT: maybeEliminateExpressionByName(t, n, parent); break; } } /** * Removes declarations of any variables whose names are strip names or * whose whose r-values are static method calls on strip types. Builds a set * of removed variables so that all references to them can be removed. * * @param t The traversal * @param n A VAR node * @param parent {@code n}'s parent */ void removeVarDeclarationsByNameOrRvalue(NodeTraversal t, Node n, Node parent) { for (Node nameNode = n.getFirstChild(); nameNode != null; nameNode = nameNode.getNext()) { String name = nameNode.getString(); if (isStripName(name) || isCallWhoseReturnValueShouldBeStripped(nameNode.getFirstChild())) { // Remove the NAME. Scope scope = t.getScope(); varsToRemove.add(scope.getVar(name)); n.removeChild(nameNode); compiler.reportCodeChange(); } } if (!n.hasChildren()) { // Must also remove the VAR. replaceWithEmpty(n, parent); compiler.reportCodeChange(); } } /** * Removes a reference if it is a reference to a removed variable. * * @param t The traversal * @param n A NAME node * @param parent {@code n}'s parent */ void maybeRemoveReferenceToRemovedVariable(NodeTraversal t, Node n, Node parent) { switch (parent.getType()) { case Token.VAR: // This is a variable declaration, not a reference. break; case Token.GETPROP: // GETPROP // NAME // STRING (property name) case Token.GETELEM: // GETELEM // NAME // NUMBER|STRING|NAME|... if (parent.getFirstChild() == n && isReferenceToRemovedVar(t, n)) { replaceHighestNestedCallWithNull(parent, parent.getParent()); } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: if (isReferenceToRemovedVar(t, n)) { if (parent.getFirstChild() == n) { Node gramps = parent.getParent(); if (gramps.isExprResult()) { // Remove the assignment. Node greatGramps = gramps.getParent(); replaceWithEmpty(gramps, greatGramps); compiler.reportCodeChange(); } else { // Substitute the r-value for the assignment. Node rvalue = n.getNext(); parent.removeChild(rvalue); gramps.replaceChild(parent, rvalue); compiler.reportCodeChange(); } } else { // The var reference is the r-value. Replace it with null. replaceWithNull(n, parent); compiler.reportCodeChange(); } } break; default: if (isReferenceToRemovedVar(t, n)) { replaceWithNull(n, parent); compiler.reportCodeChange(); } break; } } /** * Use a while loop to get up out of any nested calls. For example, * if we have just detected that we need to remove the a.b() call * in a.b().c().d(), we'll have to remove all of the calls, and it * will take a few iterations through this loop to get up to d(). */ void replaceHighestNestedCallWithNull(Node node, Node parent) { Node ancestor = parent; Node ancestorChild = node; while (true) { if (ancestor.getFirstChild() != ancestorChild) { replaceWithNull(ancestorChild, ancestor); break; } if (ancestor.isExprResult()) { // Remove the entire expression statement. Node ancParent = ancestor.getParent(); replaceWithEmpty(ancestor, ancParent); break; } int type = ancestor.getType(); if (type != Token.GETPROP && type != Token.GETELEM && type != Token.CALL) { replaceWithNull(ancestorChild, ancestor); break; } ancestorChild = ancestor; ancestor = ancestor.getParent(); } compiler.reportCodeChange(); } /** * Eliminates an assignment if the l-value is: * - A field name that's a strip name * - A qualified name that begins with a strip type * * @param t The traversal * @param n An ASSIGN node * @param parent {@code n}'s parent */ void maybeEliminateAssignmentByLvalueName(NodeTraversal t, Node n, Node parent) { // ASSIGN // l-value // r-value Node lvalue = n.getFirstChild(); if (nameEndsWithFieldNameToStrip(lvalue) || qualifiedNameBeginsWithStripType(lvalue)) { // Limit to EXPR_RESULT because it is not // safe to eliminate assignment in complex expressions, // e.g. in ((x = 7) + 8) if (parent.isExprResult()) { Node gramps = parent.getParent(); replaceWithEmpty(parent, gramps); compiler.reportCodeChange(); } else { t.report(n, STRIP_ASSIGNMENT_ERROR, lvalue.getQualifiedName()); } } } /** * Eliminates an expression if it refers to: * - A field name that's a strip name * - A qualified name that begins with a strip type * This gets rid of construct like: * a.prototype.logger; (used instead of a.prototype.logger = null;) * This expression is not an assignment and so will not be caught by * maybeEliminateAssignmentByLvalueName. * @param t The traversal * @param n An EXPR_RESULT node * @param parent {@code n}'s parent */ void maybeEliminateExpressionByName(NodeTraversal t, Node n, Node parent) { // EXPR_RESULT // expression Node expression = n.getFirstChild(); if (nameEndsWithFieldNameToStrip(expression) || qualifiedNameBeginsWithStripType(expression)) { if (parent.isExprResult()) { Node gramps = parent.getParent(); replaceWithEmpty(parent, gramps); } else { replaceWithEmpty(n, parent); } compiler.reportCodeChange(); } } /** * Removes a method call if {@link #isMethodOrCtorCallThatTriggersRemoval} * indicates that it should be removed. * * @param t The traversal * @param n A CALL node * @param parent {@code n}'s parent */ void maybeRemoveCall(NodeTraversal t, Node n, Node parent) { // CALL/NEW // function // arguments if (isMethodOrCtorCallThatTriggersRemoval(t, n, parent)) { replaceHighestNestedCallWithNull(n, parent); } } /** * Eliminates any object literal keys in an object literal declaration that * have strip names. * * @param t The traversal * @param n An OBJLIT node */ void eliminateKeysWithStripNamesFromObjLit(NodeTraversal t, Node n) { // OBJLIT // key1 // value1 // key2 // ... Node key = n.getFirstChild(); while (key != null) { if (isStripName(key.getString())) { Node next = key.getNext(); n.removeChild(key); key = next; compiler.reportCodeChange(); } else { key = key.getNext(); } } } /** * Gets whether a node is a CALL node whose return value should be * stripped. A call's return value should be stripped if the function * getting called is a static method in a class that gets stripped. For * example, if "goog.debug.Logger" is a strip name, then this function * returns true for a call such as "goog.debug.Logger.getLogger(...)". It * may also simply be a function that is getting stripped. For example, * if "getLogger" is a strip name, but not "goog.debug.Logger", this will * still return true. * * @param n A node (typically a CALL node) * @return Whether the call's return value should be stripped */ boolean isCallWhoseReturnValueShouldBeStripped(@Nullable Node n) { return n != null && (n.isCall() || n.isNew()) && n.hasChildren() && (qualifiedNameBeginsWithStripType(n.getFirstChild()) || nameEndsWithFieldNameToStrip(n.getFirstChild())); } /** * Gets whether a qualified name begins with a strip name. The names * "goog.debug", "goog.debug.Logger", and "goog.debug.Logger.Level" are * examples of strip names that would result in this function returning * true for a node representing the name "goog.debug.Logger.Level". * * @param n A node (typically a NAME or GETPROP node) * @return Whether the name begins with a strip name */ boolean qualifiedNameBeginsWithStripType(Node n) { String name = n.getQualifiedName(); return qualifiedNameBeginsWithStripType(name); } /** * Gets whether a qualified name begins with a strip name. The names * "goog.debug", "goog.debug.Logger", and "goog.debug.Logger.Level" are * examples of strip names that would result in this function returning * true for a node representing the name "goog.debug.Logger.Level". * * @param name A qualified class name * @return Whether the name begins with a strip name */ boolean qualifiedNameBeginsWithStripType(String name) { if (name != null) { for (String type : stripTypes) { if (name.equals(type) || name.startsWith(type + ".")) { return true; } } for (String type : stripTypePrefixes) { if (name.startsWith(type)) { return true; } } } return false; } /** * Determines whether a NAME node represents a reference to a variable that * has been removed. * * @param t The traversal * @param n A NAME node * @return Whether the variable was removed */ boolean isReferenceToRemovedVar(NodeTraversal t, Node n) { String name = n.getString(); Scope scope = t.getScope(); Scope.Var var = scope.getVar(name); return varsToRemove.contains(var); } /** * Gets whether a CALL node triggers statement removal, based on the name * of the object whose method is being called, or the name of the method. * Checks whether the name begins with a strip type, ends with a field name * that's a strip name, or belongs to the set of global class-defining * functions (e.g. goog.inherits). * * @param t The traversal * @param n A CALL node * @return Whether the node triggers statement removal */ boolean isMethodOrCtorCallThatTriggersRemoval( NodeTraversal t, Node n, Node parent) { // CALL/NEW // GETPROP (function) <-- we're interested in this, the function // GETPROP (callee object) <-- or the object on which it is called // ... // STRING (field name) // STRING (method name) // ... (arguments) Node function = n.getFirstChild(); if (function == null || !function.isGetProp()) { // We are only interested in calls on object references that are // properties. We don't need to eliminate method calls on variables // that are getting removed, since that's already done by the code // that removes all references to those variables. return false; } if (parent != null && parent.isName()) { Node gramps = parent.getParent(); if (gramps != null && gramps.isVar()) { // The call's return value is being used to initialize a newly // declared variable. We should leave the call intact for now. // That way, when the traversal reaches the variable declaration, // we'll recognize that the variable and all references to it need // to be eliminated. return false; } } Node callee = function.getFirstChild(); return nameEndsWithFieldNameToStrip(callee) || nameEndsWithFieldNameToStrip(function) || qualifiedNameBeginsWithStripType(function) || actsOnStripType(t, n); } /** * Gets whether a name ends with a field name that should be stripped. For * example, this function would return true when passed "this.logger" or * "a.b.c.myLogger" if "logger" is a strip name. * * @param n A node (typically a GETPROP node) * @return Whether the name ends with a field name that should be stripped */ boolean nameEndsWithFieldNameToStrip(@Nullable Node n) { if (n != null && n.isGetProp()) { Node propNode = n.getLastChild(); return propNode != null && propNode.isString() && isStripName(propNode.getString()); } return false; } /** * Determines whether the given node helps to define a * strip type. For example, goog.inherits(stripType, Object) * would be such a call. * * Also reports an error if a non-strip type inherits from a strip type. * * @param t The current traversal * @param callNode The CALL node */ private boolean actsOnStripType(NodeTraversal t, Node callNode) { SubclassRelationship classes = compiler.getCodingConvention().getClassesDefinedByCall(callNode); if (classes != null) { // It's okay to strip a type that inherits from a non-stripped type // e.g. goog.inherits(goog.debug.Logger, Object) if (qualifiedNameBeginsWithStripType(classes.subclassName)) { return true; } // report an error if a non-strip type inherits from a // strip type. if (qualifiedNameBeginsWithStripType(classes.superclassName)) { t.report(callNode, STRIP_TYPE_INHERIT_ERROR, classes.subclassName, classes.superclassName); } } return false; } /** * Gets whether a JavaScript identifier is the name of a variable or * property that should be stripped. * * @param name A JavaScript identifier * @return Whether {@code name} is a name that triggers removal */ boolean isStripName(String name) { if (stripNameSuffixes.contains(name) || stripNamePrefixes.contains(name)) { return true; } if ((name.length() == 0) || Character.isUpperCase(name.charAt(0))) { return false; } String lcName = name.toLowerCase(); for (String stripName : stripNamePrefixes) { if (lcName.startsWith(stripName.toLowerCase())) { return true; } } for (String stripName : stripNameSuffixes) { if (lcName.endsWith(stripName.toLowerCase())) { return true; } } return false; } /** * Replaces a node with a NULL node. This is useful where a value is * expected. * * @param n A node * @param parent {@code n}'s parent */ void replaceWithNull(Node n, Node parent) { parent.replaceChild(n, IR.nullNode()); } /** * Replaces a node with an EMPTY node. This is useful where a statement is * expected. * * @param n A node * @param parent {@code n}'s parent */ void replaceWithEmpty(Node n, Node parent) { NodeUtil.removeChild(parent, n); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckDebuggerStatement.java0000644000175000017500000000354512115204405030055 0ustar apoapo/* * Copyright 2011 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.DiagnosticType; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; /** * {@link CheckDebuggerStatement} checks for the presence of the "debugger" * statement in JavaScript code. It is appropriate to use this statement while * developing JavaScript; however, it is generally undesirable to include it in * production code. * * @author bolinfest@google.com (Michael Bolin) */ class CheckDebuggerStatement extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType DEBUGGER_STATEMENT_PRESENT = DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT", "Using the debugger statement can halt your application if the user " + "has a JavaScript debugger running."); private final AbstractCompiler compiler; public CheckDebuggerStatement(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isDebugger()) { t.report(n, DEBUGGER_STATEMENT_PRESENT); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/OptimizeParameters.java0000644000175000017500000004115712115204405027333 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; /** * Optimize function calls and function signatures. * *

    *
  • Removes optional parameters if no caller specifies it as argument.
  • *
  • Removes arguments at call site to function that * ignores the parameter.
  • *
  • Inline a parameter if the function is always called with that constant. *
  • *
* */ class OptimizeParameters implements CompilerPass, OptimizeCalls.CallGraphCompilerPass { private final AbstractCompiler compiler; private List removedNodes = Lists.newArrayList(); OptimizeParameters(AbstractCompiler compiler) { this.compiler = compiler; } @Override @VisibleForTesting public void process(Node externs, Node root) { Preconditions.checkState( compiler.getLifeCycleStage() == LifeCycleStage.NORMALIZED); SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); process(externs, root, defFinder); } @Override public void process( Node externs, Node root, SimpleDefinitionFinder definitions) { for (DefinitionSite defSite : definitions.getDefinitionSites()) { if (canChangeSignature(defSite, definitions)) { tryEliminateConstantArgs(defSite, definitions); tryEliminateOptionalArgs(defSite, definitions); } } // Remove any references or definitions that have been removed to keep it // in a consistent state for the next pass. for (Node n : removedNodes) { definitions.removeReferences(n); } } /** * @return Whether the definitionSite represents a function whose call * signature can be modified. */ private boolean canChangeSignature( DefinitionSite definitionSite, SimpleDefinitionFinder defFinder) { Definition definition = definitionSite.definition; if (definitionSite.inExterns) { return false; } // Only functions may be rewritten. // Functions that access "arguments" are not eligible since // rewrite changes the structure of this object. Node rValue = definition.getRValue(); if (rValue == null || !rValue.isFunction() || NodeUtil.isVarArgsFunction(rValue)) { return false; } // TODO(johnlenz): support rewriting methods defined as part of // object literals (they are generally problematic because they may be // maps of functions use in for-in expressions, etc). // Be conservative, don't try to optimize any declaration that isn't as // simple function declaration or assignment. if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(rValue)) { return false; } // Assume an exported method result is used. if (!defFinder.canModifyDefinition(definition)) { return false; } Collection useSites = defFinder.getUseSites(definition); if (useSites.isEmpty()) { return false; } for (UseSite site : useSites) { // Any non-call reference maybe introducing an alias. Don't try to // change the function signature, if all the aliases can't also be // changed. // TODO(johnlenz): Support .call signature changes. if (!SimpleDefinitionFinder.isCallOrNewSite(site)) { return false; } // TODO(johnlenz): support specialization // Multiple definitions prevent rewrite. // TODO(johnlenz): Allow rewrite all definitions are valid. Node nameNode = site.node; Collection singleSiteDefinitions = defFinder.getDefinitionsReferencedAt(nameNode); if (singleSiteDefinitions.size() > 1) { return false; } Preconditions.checkState(!singleSiteDefinitions.isEmpty()); Preconditions.checkState(singleSiteDefinitions.contains(definition)); } return true; } /** * Removes any optional parameters if no callers specifies it as an argument. */ private void tryEliminateOptionalArgs( DefinitionSite defSite, SimpleDefinitionFinder defFinder) { // Count the maximum number of arguments passed into this function all // all points of the program. int maxArgs = -1; Definition definition = defSite.definition; Collection useSites = defFinder.getUseSites(definition); for (UseSite site : useSites) { Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); Node call = site.node.getParent(); int numArgs = call.getChildCount() - 1; if (numArgs > maxArgs) { maxArgs = numArgs; } } eliminateParamsAfter(definition.getRValue(), maxArgs); } /** * Eliminate parameters if they are always constant. * * function foo(a, b) {...} * foo(1,2); * foo(1,3) * becomes * function foo(b) { var a = 1 ... } * foo(2); * foo(3); */ private void tryEliminateConstantArgs( DefinitionSite defSite, SimpleDefinitionFinder defFinder) { List parameters = Lists.newArrayList(); boolean firstCall = true; // Build a list of parameters to remove Definition definition = defSite.definition; Collection useSites = defFinder.getUseSites(definition); boolean continueLooking = false; for (UseSite site : useSites) { Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); Node call = site.node.getParent(); Node cur = call.getFirstChild(); if (firstCall) { // Use the first call to construct a list of parameters of the // function. continueLooking = buildParameterList(parameters, cur, site.scope); firstCall = false; } else { continueLooking= findFixedParameters(parameters, cur); } if (!continueLooking) { return; } } continueLooking = adjustForSideEffects(parameters); if (!continueLooking) { return; } // Remove the constant parameters in all the calls for (UseSite site : useSites) { Preconditions.checkState(SimpleDefinitionFinder.isCallOrNewSite(site)); Node call = site.node.getParent(); optimizeCallSite(defFinder, parameters, call); } // Remove the constant parameters in the definitions and add it as a local // variable. Node function = definition.getRValue(); if (function.isFunction()) { optimizeFunctionDefinition(parameters, function); } } /** * Adjust the parameters to move based on the side-effects seen. * @return Whether there are any movable parameters. */ private boolean adjustForSideEffects(List parameters) { // If a parameter is moved, that has side-effect no parameters that // can be effected by side-effects can be left. // A parameter can be moved if it can't be side-effected (immutable), // or there are no following side-effects, that aren't moved. boolean anyMovable = false; boolean seenUnmovableSideEffects = false; boolean seenUnmoveableSideEfffected = false; for (int i = parameters.size() - 1; i >= 0; i--) { Parameter current = parameters.get(i); // Preserve side-effect ordering, don't move this parameter if: // * the current parameter has side-effects and a following // parameters that will not be move can be effected. // * the current parameter can be effected and a following // parameter that will not be moved has side-effects if (current.shouldRemove && ((seenUnmovableSideEffects && current.canBeSideEffected()) || (seenUnmoveableSideEfffected && current.hasSideEffects()))) { current.shouldRemove = false; } if (current.shouldRemove) { anyMovable = true; } else { if (current.canBeSideEffected) { seenUnmoveableSideEfffected = true; } if (current.hasSideEffects) { seenUnmovableSideEffects = true; } } } return anyMovable; } /** * Determine which parameters use the same expression. * @return Whether any parameter was found that can be updated. */ private boolean findFixedParameters(List parameters, Node cur) { boolean anyMovable = false; int index = 0; while ((cur = cur.getNext()) != null) { Parameter p; if (index >= parameters.size()) { p = new Parameter(cur, false); parameters.add(p); } else { p = parameters.get(index); if (p.shouldRemove()) { Node value = p.getArg(); if (!cur.isEquivalentTo(value)) { p.setShouldRemove(false); } else { anyMovable = true; } } } setParameterSideEffectInfo(p, cur); index++; } for (;index < parameters.size(); index++) { parameters.get(index).setShouldRemove(false); } return anyMovable; } /** * @return Whether any parameter was movable. */ private boolean buildParameterList( List parameters, Node cur, Scope s) { boolean anyMovable = false; while ((cur = cur.getNext()) != null) { boolean movable = isMovableValue(cur, s); Parameter p = new Parameter(cur, movable); setParameterSideEffectInfo(p, cur); parameters.add(p); if (movable) { anyMovable = true; } } return anyMovable; } private void setParameterSideEffectInfo(Parameter p, Node value) { if (!p.hasSideEffects()) { p.setHasSideEffects(NodeUtil.mayHaveSideEffects(value, compiler)); } if (!p.canBeSideEffected()) { p.setCanBeSideEffected(NodeUtil.canBeSideEffected(value)); } } /** * @return Whether the expression can be safely moved to another function * in another scope. */ private boolean isMovableValue(Node n, Scope s) { // Things that can change value or are inaccessible can't be moved, these // are "this", "arguments", local names, and functions that capture local // values. switch (n.getType()) { case Token.THIS: return false; case Token.FUNCTION: // Don't move function closures. // TODO(johnlenz): Closure that only contain global reference can be // moved. return false; case Token.NAME: if (n.getString().equals("arguments")) { return false; } else { Var v = s.getVar(n.getString()); // Make sure that the variable is global. A caught exception, while // it is in the global scope object in the compiler, it is not a // global variable. if (v != null && (v.isLocal() || v.nameNode.getParent().isCatch())) { return false; } } break; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!isMovableValue(c, s)) { return false; } } return true; } private void optimizeFunctionDefinition(List parameters, Node function) { for (int index = parameters.size() - 1; index >= 0; index--) { if (parameters.get(index).shouldRemove()) { Node paramName = eliminateFunctionParamAt(function, index); addVariableToFunction(function, paramName, parameters.get(index).getArg()); } } } private void optimizeCallSite( SimpleDefinitionFinder defFinder, List parameters, Node call) { for (int index = parameters.size() - 1; index >= 0; index--) { Parameter p = parameters.get(index); if (p.shouldRemove()) { eliminateCallParamAt(defFinder, p, call, index); } } } /** * Simple container class that keeps tracks of a parameter and whether it * should be removed. */ private static class Parameter { private final Node arg; private boolean shouldRemove; private boolean hasSideEffects; private boolean canBeSideEffected; public Parameter(Node arg, boolean shouldRemove) { this.shouldRemove = shouldRemove; this.arg = arg; } public Node getArg() { return arg; } public boolean shouldRemove() { return shouldRemove; } public void setShouldRemove(boolean value) { shouldRemove = value; } public void setHasSideEffects(boolean hasSideEffects) { this.hasSideEffects = hasSideEffects; } public boolean hasSideEffects() { return hasSideEffects; } public void setCanBeSideEffected(boolean canBeSideEffected) { this.canBeSideEffected = canBeSideEffected; } public boolean canBeSideEffected() { return canBeSideEffected; } } /** * Adds a variable to the top of a function block. * @param function A function node. * @param varName The name of the variable. * @param value The initial value of the variable. */ private void addVariableToFunction(Node function, Node varName, Node value) { Preconditions.checkArgument(function.isFunction(), "Node must be a function."); Node block = function.getLastChild(); Preconditions.checkArgument(block.isBlock(), "Node must be a block."); Preconditions.checkState(value.getParent() == null); Node stmt; if (varName != null) { stmt = NodeUtil.newVarNode(varName.getString(), value); } else { stmt = IR.exprResult(value); } block.addChildToFront(stmt); compiler.reportCodeChange(); } /** * Removes all formal parameters starting at argIndex. * @return true if a parameter has been removed. */ private boolean eliminateParamsAfter(Node function, int argIndex) { Node formalArgPtr = function.getFirstChild().getNext().getFirstChild(); while (argIndex != 0 && formalArgPtr != null) { formalArgPtr = formalArgPtr.getNext(); argIndex--; } return eliminateParamsAfter(function, formalArgPtr); } private boolean eliminateParamsAfter(Node fnNode, Node argNode) { if (argNode != null) { // Keep the args in the same order, do the last first. eliminateParamsAfter(fnNode, argNode.getNext()); argNode.detachFromParent(); Node var = IR.var(argNode).copyInformationFrom(argNode); fnNode.getLastChild().addChildrenToFront(var); compiler.reportCodeChange(); return true; } return false; } /** * Eliminates the parameter from a function definition. * @param function The function node * @param argIndex The index of the the argument to remove. * @return The Node of the argument removed. */ private Node eliminateFunctionParamAt(Node function, int argIndex) { Preconditions.checkArgument(function.isFunction(), "Node must be a function."); Node formalArgPtr = NodeUtil.getArgumentForFunction( function, argIndex); if (formalArgPtr != null) { function.getFirstChild().getNext().removeChild(formalArgPtr); } return formalArgPtr; } /** * Eliminates the parameter from a function call. * @param defFinder * @param p * @param call The function call node * @param argIndex The index of the the argument to remove. * @return The Node of the argument removed. */ private Node eliminateCallParamAt( SimpleDefinitionFinder defFinder, Parameter p, Node call, int argIndex) { Preconditions.checkArgument( NodeUtil.isCallOrNew(call), "Node must be a call or new."); Node formalArgPtr = NodeUtil.getArgumentForCallOrNew( call, argIndex); if (formalArgPtr != null) { call.removeChild(formalArgPtr); // The value in the parameter object is the one that is being moved into // function definition leave that one's references. For everything else, // remove any references. if (p.getArg() != formalArgPtr) { removedNodes.add(formalArgPtr); } compiler.reportCodeChange(); } return formalArgPtr; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InferJSDocInfo.java0000644000175000017500000001625712115204405026254 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import javax.annotation.Nullable; /** * Set the JSDocInfo on all types. * * Propagates JSDoc across the type graph, but not across the symbol graph. * This means that if you have: * * var x = new Foo(); * x.bar; * * then the JSType attached to x.bar may get associated JSDoc, but the * Node and Var will not. * * JSDoc is initially attached to AST Nodes at parse time. * There are 3 ways that JSDoc get propagated across the type system. * 1) Nominal types (e.g., constructors) may contain JSDocInfo for their * declaration. * 2) Object types have a JSDocInfo slot for each property on that type. * 3) Shape types (like structural functions) may have JSDocInfo. * * #1 and #2 should be self-explanatory, and non-controversial. #3 is * a bit trickier. It means that if you have: * * /** @param {number} x / * Foo.prototype.bar = goog.abstractMethod; * * the JSDocInfo will appear in two places in the type system: in the 'bar' * slot of Foo.prototype, and on the function expression type created by * this expression. * * @author nicksantos@google.com (Nick Santos) */ class InferJSDocInfo extends AbstractPostOrderCallback implements HotSwapCompilerPass { private final AbstractCompiler compiler; @SuppressWarnings("unused") private boolean inExterns; InferJSDocInfo(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (externs != null) { inExterns = true; NodeTraversal.traverse(compiler, externs, this); } if (root != null) { inExterns = false; NodeTraversal.traverse(compiler, root, this); } } @Override public void hotSwapScript(Node root, Node originalRoot) { Preconditions.checkNotNull(root); Preconditions.checkState(root.isScript()); inExterns = false; NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo; switch (n.getType()) { // Infer JSDocInfo on types of all type declarations on variables. case Token.NAME: if (parent == null) { return; } // Only allow JSDoc on VARs, function declarations, and assigns. if (!parent.isVar() && !NodeUtil.isFunctionDeclaration(parent) && !(parent.isAssign() && n == parent.getFirstChild())) { return; } // There are four places the doc info could live. // 1) A FUNCTION node. // /** ... */ function f() { ... } // 2) An ASSIGN parent. // /** ... */ x = function () { ... } // 3) A NAME parent. // var x, /** ... */ y = function() { ... } // 4) A VAR gramps. // /** ... */ var x = function() { ... } docInfo = n.getJSDocInfo(); if (docInfo == null && !(parent.isVar() && !parent.hasOneChild())) { docInfo = parent.getJSDocInfo(); } // Try to find the type of the NAME. JSType varType = n.getJSType(); if (varType == null && parent.isFunction()) { varType = parent.getJSType(); } // If we have no type to attach JSDocInfo to, then there's nothing // we can do. if (varType == null || docInfo == null) { return; } // Dereference the type. If the result is not an object, or already // has docs attached, then do nothing. ObjectType objType = dereferenceToObject(varType); if (objType == null || objType.getJSDocInfo() != null) { return; } attachJSDocInfoToNominalTypeOrShape(objType, docInfo, n.getString()); break; case Token.GETPROP: // Infer JSDocInfo on properties. // There are two ways to write doc comments on a property. // // 1) // /** @deprecated */ // obj.prop = ... // // 2) // /** @deprecated */ // obj.prop; if (parent.isExprResult() || (parent.isAssign() && parent.getFirstChild() == n)) { docInfo = n.getJSDocInfo(); if (docInfo == null) { docInfo = parent.getJSDocInfo(); } if (docInfo != null) { ObjectType lhsType = dereferenceToObject(n.getFirstChild().getJSType()); if (lhsType != null) { // Put the JSDoc in the property slot, if there is one. String propName = n.getLastChild().getString(); if (lhsType.hasOwnProperty(propName)) { lhsType.setPropertyJSDocInfo(propName, docInfo); } // Put the JSDoc in any constructors or function shapes as well. ObjectType propType = dereferenceToObject(lhsType.getPropertyType(propName)); if (propType != null) { attachJSDocInfoToNominalTypeOrShape( propType, docInfo, n.getQualifiedName()); } } } } break; } } /** * Dereferences the given type to an object, or returns null. */ private ObjectType dereferenceToObject(JSType type) { return ObjectType.cast(type == null ? null : type.dereference()); } /** * Handle cases #1 and #3 in the class doc. */ private void attachJSDocInfoToNominalTypeOrShape( ObjectType objType, JSDocInfo docInfo, @Nullable String qName) { if (objType.isConstructor() || objType.isEnumType() || objType.isInterface()) { // Named types. if (objType.hasReferenceName() && objType.getReferenceName().equals(qName)) { objType.setJSDocInfo(docInfo); if (objType.isConstructor() || objType.isInterface()) { JSType.toMaybeFunctionType(objType).getInstanceType().setJSDocInfo( docInfo); } else if (objType instanceof EnumType) { ((EnumType) objType).getElementsType().setJSDocInfo(docInfo); } } } else if (!objType.isNativeObjectType() && objType.isFunctionType()) { // Structural functions. objType.setJSDocInfo(docInfo); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ObjectPropertyStringPreprocess.java0000644000175000017500000001202412115204405031706 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Rewrites new goog.testing.ObjectPropertyString(foo, 'bar') to * new JSCompiler_ObjectPropertyString(window, foo.bar). * * These two passes are for use with goog.testing.PropertyReplacer. * * * var ops = new goog.testing.ObjectPropertyString(foo.prototype, 'bar'); * propertyReplacer.set(ops,object, ops.propertyString, baz); * * * @see ObjectPropertyStringPostprocess * */ public class ObjectPropertyStringPreprocess implements CompilerPass { static final String OBJECT_PROPERTY_STRING = "goog.testing.ObjectPropertyString"; public static final String EXTERN_OBJECT_PROPERTY_STRING = "JSCompiler_ObjectPropertyString"; static final DiagnosticType INVALID_NUM_ARGUMENTS_ERROR = DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_NUM_ARGS", "goog.testing.ObjectPropertyString instantiated with \"{0}\" " + "arguments, expected 2."); static final DiagnosticType QUALIFIED_NAME_EXPECTED_ERROR = DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_QUALIFIED_NAME_EXPECTED", "goog.testing.ObjectPropertyString instantiated with invalid " + "argument, qualified name expected. Was \"{0}\"."); static final DiagnosticType STRING_LITERAL_EXPECTED_ERROR = DiagnosticType.error("JSC_OBJECT_PROPERTY_STRING_STRING_LITERAL_EXPECTED", "goog.testing.ObjectPropertyString instantiated with invalid " + "argument, string literal expected. Was \"{0}\"."); private final AbstractCompiler compiler; ObjectPropertyStringPreprocess(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { addExternDeclaration(externs, IR.var( IR.name(EXTERN_OBJECT_PROPERTY_STRING))); NodeTraversal.traverse(compiler, root, new Callback()); } private void addExternDeclaration(Node externs, Node declarationStmt) { Node script = externs.getLastChild(); if (script == null || !script.isScript()) { script = IR.script(); externs.addChildToBack(script); } script.addChildToBack(declarationStmt); } private class Callback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (OBJECT_PROPERTY_STRING.equals(n.getQualifiedName())) { Node newName = IR.name(EXTERN_OBJECT_PROPERTY_STRING); newName.copyInformationFrom(n); parent.replaceChild(n, newName); compiler.reportCodeChange(); return; } // Rewrite "new goog.testing.ObjectPropertyString(foo, 'bar')" to // "new goog.testing.ObjectPropertyString(window, foo.bar)" and // issues errors if bad arguments are encountered. if (!n.isNew()) { return; } Node objectName = n.getFirstChild(); if (!EXTERN_OBJECT_PROPERTY_STRING.equals( objectName.getQualifiedName())) { return; } if (n.getChildCount() != 3) { compiler.report(t.makeError(n, INVALID_NUM_ARGUMENTS_ERROR, "" + n.getChildCount())); return; } Node firstArgument = objectName.getNext(); if (!firstArgument.isQualifiedName()) { compiler.report(t.makeError(firstArgument, QUALIFIED_NAME_EXPECTED_ERROR, Token.name(firstArgument.getType()))); return; } Node secondArgument = firstArgument.getNext(); if (!secondArgument.isString()) { compiler.report(t.makeError(secondArgument, STRING_LITERAL_EXPECTED_ERROR, Token.name(secondArgument.getType()))); return; } Node newFirstArgument = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), compiler.getCodingConvention().getGlobalObject()) .srcrefTree(firstArgument); Node newSecondArgument = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), firstArgument.getQualifiedName() + "." + firstArgument.getNext().getString()) .srcrefTree(secondArgument); n.replaceChild(firstArgument, newFirstArgument); n.replaceChild(secondArgument, newSecondArgument); compiler.reportCodeChange(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameGenerator.java0000644000175000017500000001114412115204405026227 0ustar apoapo/* * Copyright 2005 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.rhino.TokenStream; import javax.annotation.Nullable; import com.google.common.collect.Sets; import com.google.common.primitives.Chars; import java.util.*; /** * A simple class for generating unique JavaScript variable/property names. * *

This class is not thread safe. * */ final class NameGenerator { /** Generate short name with this first character */ static final char[] FIRST_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$".toCharArray(); /** These appear after after the first character */ static final char[] NONFIRST_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789$" .toCharArray(); private final Set reservedNames; private final String prefix; private int nameCount; private final char[] firstChars; private final char[] nonFirstChars; /** * Creates a NameGenerator. * * @param reservedNames set of names that are reserved; generated names will * not include these names. This set is referenced rather than copied, * so changes to the set will be reflected in how names are generated. * @param prefix all generated names begin with this prefix. * @param reservedCharacters If specified these characters won't be used in * generated names */ NameGenerator(Set reservedNames, String prefix, @Nullable char[] reservedCharacters) { this.reservedNames = reservedNames; this.prefix = prefix; // build the character arrays to use this.firstChars = reserveCharacters(FIRST_CHAR, reservedCharacters); this.nonFirstChars = reserveCharacters(NONFIRST_CHAR, reservedCharacters); checkPrefix(prefix); } /** * Provides the array of available characters based on the specified arrays. * @param chars The list of characters that are legal * @param reservedCharacters The characters that should not be used * @return An array of characters to use. Will return the chars array if * reservedCharacters is null or empty, otherwise creates a new array. */ static char[] reserveCharacters(char[] chars, char[] reservedCharacters) { if (reservedCharacters == null || reservedCharacters.length == 0) { return chars; } Set charSet = Sets.newLinkedHashSet(Chars.asList(chars)); for (char reservedCharacter : reservedCharacters) { charSet.remove(reservedCharacter); } return Chars.toArray(charSet); } /** Validates a name prefix. */ private void checkPrefix(String prefix) { if (prefix.length() > 0) { // Make sure that prefix starts with a legal character. if (!contains(firstChars, prefix.charAt(0))) { throw new IllegalArgumentException("prefix must start with one of: " + Arrays.toString(firstChars)); } for (int pos = 1; pos < prefix.length(); ++pos) { if (!contains(nonFirstChars, prefix.charAt(pos))) { throw new IllegalArgumentException("prefix has invalid characters, " + "must be one of: " + Arrays.toString(nonFirstChars)); } } } } private boolean contains(char[] arr, char c) { for (int i = 0; i < arr.length; i++) { if (arr[i] == c) { return true; } } return false; } /** * Generates the next short name. */ String generateNextName() { while (true) { String name = prefix; int i = nameCount; if (name.isEmpty()) { int pos = i % firstChars.length; name += firstChars[pos]; i /= firstChars.length; } while (i > 0) { i--; int pos = i % nonFirstChars.length; name += nonFirstChars[pos]; i /= nonFirstChars.length; } nameCount++; // Make sure it's not a JS keyword or reserved name. if (TokenStream.isKeyword(name) || reservedNames.contains(name)) { continue; } return name; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Region.java0000644000175000017500000000165412115204405024730 0ustar apoapo/* * Copyright 2007 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; /** * Source code region. * */ public interface Region { /** * Get the source region. */ String getSourceExcerpt(); /** * Get the beginning line number. */ int getBeginningLineNumber(); /** * Get the ending line number. */ int getEndingLineNumber(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CollapseProperties.java0000644000175000017500000010357512115204405027331 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.GlobalNamespace.Name; import com.google.javascript.jscomp.GlobalNamespace.Ref; import com.google.javascript.jscomp.GlobalNamespace.Ref.Type; import com.google.javascript.jscomp.ReferenceCollectingCallback; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import com.google.javascript.rhino.jstype.JSType; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Set; /** * Flattens global objects/namespaces by replacing each '.' with '$' in * their names. This reduces the number of property lookups the browser has * to do and allows the {@link RenameVars} pass to shorten namespaced names. * For example, goog.events.handleEvent() -> goog$events$handleEvent() -> Za(). * *

If a global object's name is assigned to more than once, or if a property * is added to the global object in a complex expression, then none of its * properties will be collapsed (for safety/correctness). * *

If, after a global object is declared, it is never referenced except when * its properties are read or set, then the object will be removed after its * properties have been collapsed. * *

Uninitialized variable stubs are created at a global object's declaration * site for any of its properties that are added late in a local scope. * *

If, after an object is declared, it is referenced directly in a way that * might create an alias for it, then none of its properties will be collapsed. * This behavior is a safeguard to prevent the values associated with the * flattened names from getting out of sync with the object's actual property * values. For example, in the following case, an alias a$b, if created, could * easily keep the value 0 even after a.b became 5: * a = {b: 0}; c = a; c.b = 5; . * *

This pass doesn't flatten property accesses of the form: a[b]. * *

For lots of examples, see the unit test. * */ class CollapseProperties implements CompilerPass { // Warnings static final DiagnosticType UNSAFE_NAMESPACE_WARNING = DiagnosticType.warning( "JSC_UNSAFE_NAMESPACE", "incomplete alias created for namespace {0}"); static final DiagnosticType NAMESPACE_REDEFINED_WARNING = DiagnosticType.warning( "JSC_NAMESPACE_REDEFINED", "namespace {0} should not be redefined"); static final DiagnosticType UNSAFE_THIS = DiagnosticType.warning( "JSC_UNSAFE_THIS", "dangerous use of 'this' in static method {0}"); private AbstractCompiler compiler; /** Global namespace tree */ private List globalNames; /** Maps names (e.g. "a.b.c") to nodes in the global namespace tree */ private Map nameMap; private final boolean collapsePropertiesOnExternTypes; private final boolean inlineAliases; /** * Creates an instance. * * @param compiler The JSCompiler, for reporting code changes * @param collapsePropertiesOnExternTypes if true, will rename user-defined * static properties on externed typed. E.g. String.foo. * @param inlineAliases Whether we're allowed to inline local aliases of * namespaces, etc. */ CollapseProperties(AbstractCompiler compiler, boolean collapsePropertiesOnExternTypes, boolean inlineAliases) { this.compiler = compiler; this.collapsePropertiesOnExternTypes = collapsePropertiesOnExternTypes; this.inlineAliases = inlineAliases; } @Override public void process(Node externs, Node root) { GlobalNamespace namespace; if (collapsePropertiesOnExternTypes) { namespace = new GlobalNamespace(compiler, externs, root); } else { namespace = new GlobalNamespace(compiler, root); } if (inlineAliases) { inlineAliases(namespace); } nameMap = namespace.getNameIndex(); globalNames = namespace.getNameForest(); checkNamespaces(); for (Name n : globalNames) { flattenReferencesToCollapsibleDescendantNames(n, n.getBaseName()); } // We collapse property definitions after collapsing property references // because this step can alter the parse tree above property references, // invalidating the node ancestry stored with each reference. for (Name n : globalNames) { collapseDeclarationOfNameAndDescendants(n, n.getBaseName()); } } /** * For each qualified name N in the global scope, we check if: * (a) No ancestor of N is ever aliased or assigned an unknown value type. * (If N = "a.b.c", "a" and "a.b" are never aliased). * (b) N has exactly one write, and it lives in the global scope. * (c) N is aliased in a local scope. * * If (a) is true, then GlobalNamespace must know all the writes to N. * If (a) and (b) are true, then N cannot change during the execution of * a local scope. * If (a) and (b) and (c) are true, then the alias can be inlined if the * alias obeys the usual rules for how we decide whether a variable is * inlineable. * @see InlineVariables */ private void inlineAliases(GlobalNamespace namespace) { // Invariant: All the names in the worklist meet condition (a). Deque workList = new ArrayDeque(namespace.getNameForest()); while (!workList.isEmpty()) { Name name = workList.pop(); // Don't attempt to inline a getter or setter property as a variable. if (name.type == Name.Type.GET || name.type == Name.Type.SET) { continue; } if (name.globalSets == 1 && name.localSets == 0 && name.aliasingGets > 0) { // {@code name} meets condition (b). Find all of its local aliases // and try to inline them. List refs = Lists.newArrayList(name.getRefs()); for (Ref ref : refs) { if (ref.type == Type.ALIASING_GET && ref.scope.isLocal()) { // {@code name} meets condition (c). Try to inline it. if (inlineAliasIfPossible(ref, namespace)) { name.removeRef(ref); } } } } // Check if {@code name} has any aliases left after the // local-alias-inlining above. if ((name.type == Name.Type.OBJECTLIT || name.type == Name.Type.FUNCTION) && name.aliasingGets == 0 && name.props != null) { // All of {@code name}'s children meet condition (a), so they can be // added to the worklist. workList.addAll(name.props); } } } private boolean inlineAliasIfPossible(Ref alias, GlobalNamespace namespace) { // Ensure that the alias is assigned to a local variable at that // variable's declaration. If the alias's parent is a NAME, // then the NAME must be the child of a VAR node, and we must // be in a VAR assignment. Node aliasParent = alias.node.getParent(); if (aliasParent.isName()) { // Ensure that the local variable is well defined and never reassigned. Scope scope = alias.scope; Var aliasVar = scope.getVar(aliasParent.getString()); ReferenceCollectingCallback collector = new ReferenceCollectingCallback(compiler, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR, Predicates.equalTo(aliasVar)); (new NodeTraversal(compiler, collector)).traverseAtScope(scope); ReferenceCollection aliasRefs = collector.getReferences(aliasVar); if (aliasRefs.isWellDefined() && aliasRefs.firstReferenceIsAssigningDeclaration() && aliasRefs.isAssignedOnceInLifetime()) { // The alias is well-formed, so do the inlining now. int size = aliasRefs.references.size(); Set newNodes = Sets.newHashSetWithExpectedSize(size - 1); for (int i = 1; i < size; i++) { ReferenceCollectingCallback.Reference aliasRef = aliasRefs.references.get(i); Node newNode = alias.node.cloneTree(); aliasRef.getParent().replaceChild(aliasRef.getNode(), newNode); newNodes.add(newNode); } // just set the original alias to null. aliasParent.replaceChild(alias.node, IR.nullNode()); compiler.reportCodeChange(); // Inlining the variable may have introduced new references // to descendants of {@code name}. So those need to be collected now. namespace.scanNewNodes(alias.scope, newNodes); return true; } } return false; } /** * Runs through all namespaces (prefixes of classes and enums), and checks if * any of them have been used in an unsafe way. */ private void checkNamespaces() { for (Name name : nameMap.values()) { if (name.isNamespace() && (name.aliasingGets > 0 || name.localSets + name.globalSets > 1 || name.deleteProps > 0)) { boolean initialized = name.getDeclaration() != null; for (Ref ref : name.getRefs()) { if (ref == name.getDeclaration()) { continue; } if (ref.type == Ref.Type.DELETE_PROP) { if (initialized) { warnAboutNamespaceRedefinition(name, ref); } } else if ( ref.type == Ref.Type.SET_FROM_GLOBAL || ref.type == Ref.Type.SET_FROM_LOCAL) { if (initialized) { warnAboutNamespaceRedefinition(name, ref); } initialized = true; } else if (ref.type == Ref.Type.ALIASING_GET) { warnAboutNamespaceAliasing(name, ref); } } } } } /** * Reports a warning because a namespace was aliased. * * @param nameObj A namespace that is being aliased * @param ref The reference that forced the alias */ private void warnAboutNamespaceAliasing(Name nameObj, Ref ref) { compiler.report( JSError.make(ref.getSourceName(), ref.node, UNSAFE_NAMESPACE_WARNING, nameObj.getFullName())); } /** * Reports a warning because a namespace was redefined. * * @param nameObj A namespace that is being redefined * @param ref The reference that set the namespace */ private void warnAboutNamespaceRedefinition(Name nameObj, Ref ref) { compiler.report( JSError.make(ref.getSourceName(), ref.node, NAMESPACE_REDEFINED_WARNING, nameObj.getFullName())); } /** * Flattens all references to collapsible properties of a global name except * their initial definitions. Recurses on subnames. * * @param n An object representing a global name * @param alias The flattened name for {@code n} */ private void flattenReferencesToCollapsibleDescendantNames( Name n, String alias) { if (n.props == null) return; for (Name p : n.props) { String propAlias = appendPropForAlias(alias, p.getBaseName()); if (p.canCollapse()) { flattenReferencesTo(p, propAlias); } else if (p.isSimpleStubDeclaration()) { flattenSimpleStubDeclaration(p, propAlias); } flattenReferencesToCollapsibleDescendantNames(p, propAlias); } } /** * Flattens a stub declaration. * This is mostly a hack to support legacy users. */ private void flattenSimpleStubDeclaration(Name name, String alias) { Ref ref = Iterables.getOnlyElement(name.getRefs()); Node nameNode = NodeUtil.newName( compiler.getCodingConvention(), alias, ref.node, name.getFullName()); Node varNode = IR.var(nameNode).copyInformationFrom(nameNode); Preconditions.checkState( ref.node.getParent().isExprResult()); Node parent = ref.node.getParent(); Node gramps = parent.getParent(); gramps.replaceChild(parent, varNode); compiler.reportCodeChange(); } /** * Flattens all references to a collapsible property of a global name except * its initial definition. * * @param n A global property name (e.g. "a.b" or "a.b.c.d") * @param alias The flattened name (e.g. "a$b" or "a$b$c$d") */ private void flattenReferencesTo(Name n, String alias) { String originalName = n.getFullName(); for (Ref r : n.getRefs()) { if (r == n.getDeclaration()) { // Declarations are handled separately. continue; } Node rParent = r.node.getParent(); // There are two cases when we shouldn't flatten a reference: // 1) Object literal keys, because duplicate keys show up as refs. // 2) References inside a complex assign. (a = x.y = 0). These are // called TWIN references, because they show up twice in the // reference list. Only collapse the set, not the alias. if (!NodeUtil.isObjectLitKey(r.node) && (r.getTwin() == null || r.isSet())) { flattenNameRef(alias, r.node, rParent, originalName); } } // Flatten all occurrences of a name as a prefix of its subnames. For // example, if {@code n} corresponds to the name "a.b", then "a.b" will be // replaced with "a$b" in all occurrences of "a.b.c", "a.b.c.d", etc. if (n.props != null) { for (Name p : n.props) { flattenPrefixes(alias, p, 1); } } } /** * Flattens all occurrences of a name as a prefix of subnames beginning * with a particular subname. * * @param n A global property name (e.g. "a.b.c.d") * @param alias A flattened prefix name (e.g. "a$b") * @param depth The difference in depth between the property name and * the prefix name (e.g. 2) */ private void flattenPrefixes(String alias, Name n, int depth) { // Only flatten the prefix of a name declaration if the name being // initialized is fully qualified (i.e. not an object literal key). String originalName = n.getFullName(); Ref decl = n.getDeclaration(); if (decl != null && decl.node != null && decl.node.isGetProp()) { flattenNameRefAtDepth(alias, decl.node, depth, originalName); } for (Ref r : n.getRefs()) { if (r == decl) { // Declarations are handled separately. continue; } // References inside a complex assign (a = x.y = 0) // have twins. We should only flatten one of the twins. if (r.getTwin() == null || r.isSet()) { flattenNameRefAtDepth(alias, r.node, depth, originalName); } } if (n.props != null) { for (Name p : n.props) { flattenPrefixes(alias, p, depth + 1); } } } /** * Flattens a particular prefix of a single name reference. * * @param alias A flattened prefix name (e.g. "a$b") * @param n The node corresponding to a subproperty name (e.g. "a.b.c.d") * @param depth The difference in depth between the property name and * the prefix name (e.g. 2) * @param originalName String version of the property name. */ private void flattenNameRefAtDepth(String alias, Node n, int depth, String originalName) { // This method has to work for both GETPROP chains and, in rare cases, // OBJLIT keys, possibly nested. That's why we check for children before // proceeding. In the OBJLIT case, we don't need to do anything. int nType = n.getType(); boolean isQName = nType == Token.NAME || nType == Token.GETPROP; boolean isObjKey = NodeUtil.isObjectLitKey(n); Preconditions.checkState(isObjKey || isQName); if (isQName) { for (int i = 1; i < depth && n.hasChildren(); i++) { n = n.getFirstChild(); } if (n.hasChildren()) { flattenNameRef(alias, n.getFirstChild(), n, originalName); } } } /** * Replaces a GETPROP a.b.c with a NAME a$b$c. * * @param alias A flattened prefix name (e.g. "a$b") * @param n The GETPROP node corresponding to the original name (e.g. "a.b") * @param parent {@code n}'s parent * @param originalName String version of the property name. */ private void flattenNameRef(String alias, Node n, Node parent, String originalName) { // BEFORE: // getprop // getprop // name a // string b // string c // AFTER: // name a$b$c Node ref = NodeUtil.newName( compiler.getCodingConvention(), alias, n, originalName); NodeUtil.copyNameAnnotations(n.getLastChild(), ref); if (parent.isCall() && n == parent.getFirstChild()) { // The node was a call target, we are deliberately flatten these as // we node the "this" isn't provided by the namespace. Mark it as such: parent.putBooleanProp(Node.FREE_CALL, true); } JSType type = n.getJSType(); if (type != null) { ref.setJSType(type); } parent.replaceChild(n, ref); compiler.reportCodeChange(); } /** * Collapses definitions of the collapsible properties of a global name. * Recurses on subnames that also represent JavaScript objects with * collapsible properties. * * @param n A node representing a global name * @param alias The flattened name for {@code n} */ private void collapseDeclarationOfNameAndDescendants(Name n, String alias) { boolean canCollapseChildNames = n.canCollapseUnannotatedChildNames(); // Handle this name first so that nested object literals get unrolled. if (n.canCollapse()) { updateObjLitOrFunctionDeclaration(n, alias, canCollapseChildNames); } if (n.props != null) { for (Name p : n.props) { // Recurse first so that saved node ancestries are intact when needed. collapseDeclarationOfNameAndDescendants( p, appendPropForAlias(alias, p.getBaseName())); if (!p.inExterns && canCollapseChildNames && p.getDeclaration() != null && p.canCollapse() && p.getDeclaration().node != null && p.getDeclaration().node.getParent() != null && p.getDeclaration().node.getParent().isAssign()) { updateSimpleDeclaration( appendPropForAlias(alias, p.getBaseName()), p, p.getDeclaration()); } } } } /** * Updates the initial assignment to a collapsible property at global scope * by changing it to a variable declaration (e.g. a.b = 1 -> var a$b = 1). * The property's value may either be a primitive or an object literal or * function whose properties aren't collapsible. * * @param alias The flattened property name (e.g. "a$b") * @param refName The name for the reference being updated. * @param ref An object containing information about the assignment getting * updated */ private void updateSimpleDeclaration(String alias, Name refName, Ref ref) { Node rvalue = ref.node.getNext(); Node parent = ref.node.getParent(); Node gramps = parent.getParent(); Node greatGramps = gramps.getParent(); if (rvalue != null && rvalue.isFunction()) { checkForHosedThisReferences(rvalue, refName.docInfo, refName); } // Create the new alias node. Node nameNode = NodeUtil.newName( compiler.getCodingConvention(), alias, gramps.getFirstChild(), refName.getFullName()); NodeUtil.copyNameAnnotations(ref.node.getLastChild(), nameNode); if (gramps.isExprResult()) { // BEFORE: a.b.c = ...; // exprstmt // assign // getprop // getprop // name a // string b // string c // NODE // AFTER: var a$b$c = ...; // var // name a$b$c // NODE // Remove the r-value (NODE). parent.removeChild(rvalue); nameNode.addChildToFront(rvalue); Node varNode = IR.var(nameNode); greatGramps.replaceChild(gramps, varNode); } else { // This must be a complex assignment. Preconditions.checkNotNull(ref.getTwin()); // BEFORE: // ... (x.y = 3); // // AFTER: // var x$y; // ... (x$y = 3); Node current = gramps; Node currentParent = gramps.getParent(); for (; !currentParent.isScript() && !currentParent.isBlock(); current = currentParent, currentParent = currentParent.getParent()) {} // Create a stub variable declaration right // before the current statement. Node stubVar = IR.var(nameNode.cloneTree()) .copyInformationFrom(nameNode); currentParent.addChildBefore(stubVar, current); parent.replaceChild(ref.node, nameNode); } compiler.reportCodeChange(); } /** * Updates the first initialization (a.k.a "declaration") of a global name. * This involves flattening the global name (if it's not just a global * variable name already), collapsing object literal keys into global * variables, declaring stub global variables for properties added later * in a local scope. * * It may seem odd that this function also takes care of declaring stubs * for direct children. The ultimate goal of this function is to eliminate * the global name entirely (when possible), so that "middlemen" namespaces * disappear, and to do that we need to make sure that all the direct children * will be collapsed as well. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") * @param canCollapseChildNames Whether it's possible to collapse children of * this name. (This is mostly passed for convenience; it's equivalent to * n.canCollapseChildNames()). */ private void updateObjLitOrFunctionDeclaration( Name n, String alias, boolean canCollapseChildNames) { Ref decl = n.getDeclaration(); if (decl == null) { // Some names do not have declarations, because they // are only defined in local scopes. return; } if (decl.getTwin() != null) { // Twin declarations will get handled when normal references // are handled. return; } switch (decl.node.getParent().getType()) { case Token.ASSIGN: updateObjLitOrFunctionDeclarationAtAssignNode( n, alias, canCollapseChildNames); break; case Token.VAR: updateObjLitOrFunctionDeclarationAtVarNode(n, canCollapseChildNames); break; case Token.FUNCTION: updateFunctionDeclarationAtFunctionNode(n, canCollapseChildNames); break; } } /** * Updates the first initialization (a.k.a "declaration") of a global name * that occurs at an ASSIGN node. See comment for * {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name for {@code n} (e.g. "a", "a$b$c") */ private void updateObjLitOrFunctionDeclarationAtAssignNode( Name n, String alias, boolean canCollapseChildNames) { // NOTE: It's important that we don't add additional nodes // (e.g. a var node before the exprstmt) because the exprstmt might be // the child of an if statement that's not inside a block). Ref ref = n.getDeclaration(); Node rvalue = ref.node.getNext(); Node varNode = new Node(Token.VAR); Node varParent = ref.node.getAncestor(3); Node gramps = ref.node.getAncestor(2); boolean isObjLit = rvalue.isObjectLit(); boolean insertedVarNode = false; if (isObjLit && n.canEliminate()) { // Eliminate the object literal altogether. varParent.replaceChild(gramps, varNode); ref.node = null; insertedVarNode = true; } else if (!n.isSimpleName()) { // Create a VAR node to declare the name. if (rvalue.isFunction()) { checkForHosedThisReferences(rvalue, n.docInfo, n); } ref.node.getParent().removeChild(rvalue); Node nameNode = NodeUtil.newName( compiler.getCodingConvention(), alias, ref.node.getAncestor(2), n.getFullName()); JSDocInfo info = ref.node.getParent().getJSDocInfo(); if (ref.node.getLastChild().getBooleanProp(Node.IS_CONSTANT_NAME) || (info != null && info.isConstant())) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } if (info != null) { varNode.setJSDocInfo(info); } varNode.addChildToBack(nameNode); nameNode.addChildToFront(rvalue); varParent.replaceChild(gramps, varNode); // Update the node ancestry stored in the reference. ref.node = nameNode; insertedVarNode = true; } if (canCollapseChildNames) { if (isObjLit) { declareVarsForObjLitValues( n, alias, rvalue, varNode, varParent.getChildBefore(varNode), varParent); } addStubsForUndeclaredProperties(n, alias, varParent, varNode); } if (insertedVarNode) { if (!varNode.hasChildren()) { varParent.removeChild(varNode); } compiler.reportCodeChange(); } } /** * Warns about any references to "this" in the given FUNCTION. The function * is getting collapsed, so the references will change. */ private void checkForHosedThisReferences(Node function, JSDocInfo docInfo, final Name name) { // A function is getting collapsed. Make sure that if it refers to // "this", it must be a constructor or documented with @this. if (docInfo == null || (!docInfo.isConstructor() && !docInfo.hasThisType())) { NodeTraversal.traverse(compiler, function.getLastChild(), new NodeTraversal.AbstractShallowCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis()) { compiler.report( JSError.make(name.getDeclaration().getSourceName(), n, UNSAFE_THIS, name.getFullName())); } } }); } } /** * Updates the first initialization (a.k.a "declaration") of a global name * that occurs at a VAR node. See comment for * {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a") */ private void updateObjLitOrFunctionDeclarationAtVarNode( Name n, boolean canCollapseChildNames) { if (!canCollapseChildNames) { return; } Ref ref = n.getDeclaration(); String name = ref.node.getString(); Node rvalue = ref.node.getFirstChild(); Node varNode = ref.node.getParent(); Node gramps = varNode.getParent(); boolean isObjLit = rvalue.isObjectLit(); int numChanges = 0; if (isObjLit) { numChanges += declareVarsForObjLitValues( n, name, rvalue, varNode, gramps.getChildBefore(varNode), gramps); } numChanges += addStubsForUndeclaredProperties(n, name, gramps, varNode); if (isObjLit && n.canEliminate()) { varNode.removeChild(ref.node); if (!varNode.hasChildren()) { gramps.removeChild(varNode); } numChanges++; // Clear out the object reference, since we've eliminated it from the // parse tree. ref.node = null; } if (numChanges > 0) { compiler.reportCodeChange(); } } /** * Updates the first initialization (a.k.a "declaration") of a global name * that occurs at a FUNCTION node. See comment for * {@link #updateObjLitOrFunctionDeclaration}. * * @param n An object representing a global name (e.g. "a") */ private void updateFunctionDeclarationAtFunctionNode( Name n, boolean canCollapseChildNames) { if (!canCollapseChildNames) { return; } Ref ref = n.getDeclaration(); String fnName = ref.node.getString(); addStubsForUndeclaredProperties( n, fnName, ref.node.getAncestor(2), ref.node.getParent()); } /** * Declares global variables to serve as aliases for the values in an object * literal, optionally removing all of the object literal's keys and values. * * @param alias The object literal's flattened name (e.g. "a$b$c") * @param objlit The OBJLIT node * @param varNode The VAR node to which new global variables should be added * as children * @param nameToAddAfter The child of {@code varNode} after which new * variables should be added (may be null) * @param varParent {@code varNode}'s parent * @return The number of variables added */ private int declareVarsForObjLitValues( Name objlitName, String alias, Node objlit, Node varNode, Node nameToAddAfter, Node varParent) { int numVars = 0; int arbitraryNameCounter = 0; boolean discardKeys = !objlitName.shouldKeepKeys(); for (Node key = objlit.getFirstChild(), nextKey; key != null; key = nextKey) { Node value = key.getFirstChild(); nextKey = key.getNext(); // A get or a set can not be rewritten as a VAR. if (key.isGetterDef() || key.isSetterDef()) { continue; } // We generate arbitrary names for keys that aren't valid JavaScript // identifiers, since those keys are never referenced. (If they were, // this object literal's child names wouldn't be collapsible.) The only // reason that we don't eliminate them entirely is the off chance that // their values are expressions that have side effects. boolean isJsIdentifier = !key.isNumber() && TokenStream.isJSIdentifier(key.getString()); String propName = isJsIdentifier ? key.getString() : String.valueOf(++arbitraryNameCounter); // If the name cannot be collapsed, skip it. String qName = objlitName.getFullName() + '.' + propName; Name p = nameMap.get(qName); if (p != null && !p.canCollapse()) { continue; } String propAlias = appendPropForAlias(alias, propName); Node refNode = null; if (discardKeys) { objlit.removeChild(key); value.detachFromParent(); } else { // Substitute a reference for the value. refNode = IR.name(propAlias); if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) { refNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } key.replaceChild(value, refNode); } // Declare the collapsed name as a variable with the original value. Node nameNode = IR.name(propAlias); nameNode.addChildToFront(value); if (key.getBooleanProp(Node.IS_CONSTANT_NAME)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } Node newVar = IR.var(nameNode) .copyInformationFromForTree(key); if (nameToAddAfter != null) { varParent.addChildAfter(newVar, nameToAddAfter); } else { varParent.addChildBefore(newVar, varNode); } compiler.reportCodeChange(); nameToAddAfter = newVar; // Update the global name's node ancestry if it hasn't already been // done. (Duplicate keys in an object literal can bring us here twice // for the same global name.) if (isJsIdentifier && p != null) { if (!discardKeys) { Ref newAlias = p.getDeclaration().cloneAndReclassify(Ref.Type.ALIASING_GET); newAlias.node = refNode; p.addRef(newAlias); } p.getDeclaration().node = nameNode; if (value.isFunction()) { checkForHosedThisReferences(value, value.getJSDocInfo(), p); } } numVars++; } return numVars; } /** * Adds global variable "stubs" for any properties of a global name that are * only set in a local scope or read but never set. * * @param n An object representing a global name (e.g. "a", "a.b.c") * @param alias The flattened name of the object whose properties we are * adding stubs for (e.g. "a$b$c") * @param parent The node to which new global variables should be added * as children * @param addAfter The child of after which new * variables should be added (may be null) * @return The number of variables added */ private int addStubsForUndeclaredProperties( Name n, String alias, Node parent, Node addAfter) { Preconditions.checkState(n.canCollapseUnannotatedChildNames()); Preconditions.checkArgument(NodeUtil.isStatementBlock(parent)); Preconditions.checkNotNull(addAfter); int numStubs = 0; if (n.props != null) { for (Name p : n.props) { if (p.needsToBeStubbed()) { String propAlias = appendPropForAlias(alias, p.getBaseName()); Node nameNode = IR.name(propAlias); Node newVar = IR.var(nameNode) .copyInformationFromForTree(addAfter); parent.addChildAfter(newVar, addAfter); addAfter = newVar; numStubs++; compiler.reportCodeChange(); // Determine if this is a constant var by checking the first // reference to it. Don't check the declaration, as it might be null. if (p.getRefs().get(0).node.getLastChild().getBooleanProp( Node.IS_CONSTANT_NAME)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } return numStubs; } private static String appendPropForAlias(String root, String prop) { if (prop.indexOf('$') != -1) { // Encode '$' in a property as '$0'. Because '0' cannot be the // start of an identifier, this will never conflict with our // encoding from '.' -> '$'. prop = prop.replace("$", "$0"); } return root + '$' + prop; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GroupVariableDeclarations.java0000644000175000017500000001413712115204405030600 0ustar apoapo/* * 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.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Iterator; import java.util.Set; /** * Groups multiple variable declarations (that may or may not be contiguous) * in the same scope into a single one. i.e. * *

 * var a, b = 10;
 * f1();
 * var c, d;
 * ... some other code ...
 * var x, y, z = 100;
 * ... some other code ...
 * var p = 200, q = 300;
 * 
* * becomes: * *
 * var a, b = 10, c, d, x, y, z;
 * f1();
 * ... some other code ...
 * z = 100;
 * ... some other code ...
 * var p = 200, q = 300;
 * 
* * This reduces the generated uncompressed code size. * * For any scope, we use the first VAR statement as the statement to collapse * the other declarations into. For other VAR statements in the scope, we only * consider ones that either (a) have no variable that is initialized in the * the statement, or (b) have exactly one variable that is initialized (e.g. * the "var x, y, z = 100;" statement in the example above. VAR statements * with more than one variable initialization are not collapsed. This is * because doing so would increase uncompressed code size. * */ class GroupVariableDeclarations implements CompilerPass, ScopedCallback { private final AbstractCompiler compiler; GroupVariableDeclarations(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void enterScope(NodeTraversal t) { Set varNodes = Sets.newLinkedHashSet(); Iterator scopeVarIter = t.getScope().getVars(); while (scopeVarIter.hasNext()) { Node parentNode = scopeVarIter.next().getParentNode(); if (parentNode.isVar()) { varNodes.add(parentNode); } } if (varNodes.size() <= 1) { return; } Iterator varNodeIter = varNodes.iterator(); Node firstVarNode = varNodeIter.next(); while (varNodeIter.hasNext()) { Node varNode = varNodeIter.next(); applyGroupingToVar(firstVarNode, varNode); } } @Override public void exitScope(NodeTraversal t) { } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { } /** * Attempts to collapse groupVar. This can only happen if groupVar has at most * one variable initialization (it may have multiple variable declarations). * If successful, then detaches groupVar's children and appends them to * firstVar * * @param firstVar The first VAR {@code Node} in that scope. This is the node * that we want to collapse groupVar into * @param groupVar The VAR {@code Node} that we want to try collapsing * into the first VAR node of that scope */ private void applyGroupingToVar(Node firstVar, Node groupVar) { Node child = groupVar.getFirstChild(); // if some variable is initialized, then the corresponding NAME node will be // stored here Node initializedName = null; while (child != null) { if (child.hasChildren()) { // check that no more than one var is initialized if (initializedName != null) { return; } initializedName = child; } child = child.getNext(); } // we will be modifying the groupVar subtree so get its parent Node groupVarParent = groupVar.getParent(); if (initializedName != null) { if (NodeUtil.isForIn(groupVarParent)) { // The target of the for-in expression must be an assignable expression. return; } // we have an initialized var in the VAR node. We will replace the // VAR node with an assignment. // first create a detached childless clone of initializedName. Node clone = initializedName.cloneNode(); // replace groupVar.replaceChild(initializedName, clone); // add the assignment now. Node initializedVal = initializedName.removeFirstChild(); Node assignmentNode = IR.assign(initializedName, initializedVal); if (groupVarParent.isFor()) { // Handle For and For-In Loops specially. For these, we do not need // to construct an EXPR_RESULT node. groupVarParent.replaceChild(groupVar, assignmentNode); } else { Node exprNode = NodeUtil.newExpr(assignmentNode); groupVarParent.replaceChild(groupVar, exprNode); } } else { // There is no initialized var. But we need to handle FOR and // FOR-IN loops specially if (groupVarParent.isFor()) { if (NodeUtil.isForIn(groupVarParent)) { // In For-In loop, we replace the VAR node with a NAME node Node nameNodeClone = groupVar.getFirstChild().cloneNode(); groupVarParent.replaceChild(groupVar, nameNodeClone); } else { // In For loop, we replace the VAR node with an EMPTY node Node emptyNode = IR.empty(); groupVarParent.replaceChild(groupVar, emptyNode); } } else { // we can safely remove the VAR node groupVarParent.removeChild(groupVar); } } Node children = groupVar.removeChildren(); firstVar.addChildrenToBack(children); compiler.reportCodeChange(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveUnusedClassProperties.java0000644000175000017500000001153612115204405031171 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * Look for internal properties set using "this" but never read. Explicitly * ignored is the possibility that these properties * may be indirectly referenced using "for-in" or "Object.keys". This is the * same assumption used with RemoveUnusedPrototypeProperties but is by slightly * wider in scope. * * @author johnlenz@google.com (John Lenz) */ class RemoveUnusedClassProperties implements CompilerPass, NodeTraversal.Callback { final AbstractCompiler compiler; private boolean inExterns; private Set used = Sets.newHashSet(); private List candidates = Lists.newArrayList(); RemoveUnusedClassProperties(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots(compiler, this, externs, root); removeUnused(); } private void removeUnused() { for (Node n : candidates) { Preconditions.checkState(n.isGetProp()); if (!used.contains(n.getLastChild().getString())) { Node parent = n.getParent(); if (NodeUtil.isAssignmentOp(parent)) { Node assign = parent; Preconditions.checkState(assign != null && NodeUtil.isAssignmentOp(assign) && assign.getFirstChild() == n); // 'this.x = y' to 'y' assign.getParent().replaceChild(assign, assign.getLastChild().detachFromParent()); } else if (parent.isInc() || parent.isDec()) { parent.getParent().replaceChild(parent, IR.number(0)); } else { throw new IllegalStateException("unexpected: "+ parent); } compiler.reportCodeChange(); } } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isScript()) { this.inExterns = n.getStaticSourceFile().isExtern(); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: { String propName = n.getLastChild().getString(); if (inExterns || isPinningPropertyUse(n)) { used.add(propName); } else { // This is a definition of a property but it is only removable // if it is defined on "this". if (n.getFirstChild().isThis()) { candidates.add(n); } } break; } case Token.CALL: // Look for properties referenced through "JSCompiler_propertyRename". Node target = n.getFirstChild(); if (n.hasMoreThanOneChild() && target.isName() && target.getString().equals(NodeUtil.JSC_PROPERTY_NAME_FN)) { Node propName = target.getNext(); if (propName.isString()) { used.add(propName.getString()); } } break; } } /** * @return Whether the property is used in a way that prevents its removal. */ private boolean isPinningPropertyUse(Node n) { // Rather than looking for cases that are uses, we assume all references are // pinning uses unless they are: // - a simple assignment (x.a = 1) // - a compound assignment or increment (x++, x += 1) whose result is // otherwise unused Node parent = n.getParent(); if (n == parent.getFirstChild()) { if (parent.isAssign()) { // A simple assignment doesn't pin the property. return false; } else if (NodeUtil.isAssignmentOp(parent) || parent.isInc() || parent.isDec()) { // In general, compound assignments are both reads and writes, but // if the property is never otherwise read we can consider it simply // a write. // However if the assign expression is used as part of a larger // expression, we much consider it a read. For example: // x = (y.a += 1); return NodeUtil.isExpressionResultUsed(parent); } } return true; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/instrumentation_template.proto0000644000175000017500000000300012115204405031050 0ustar apoapo// Copyright 2008 Google Inc. All Rights Reserved. // Author: avd@google.com (Antonio Vicente) // // Provides JS Compiler with parameters for the instrumentation pass syntax = "proto2"; package jscomp; option java_package = "com.google.javascript.jscomp"; option java_multiple_files = true; message Instrumentation { // name of function(ID = ); // used to inform the harness about the contents of a module optional string report_defined = 1; // name of function(ID = ); // used to inform the harness about a function call optional string report_call = 2; // name of function(ID = , VAL = ); // used to inform the harness about a function exit. Must return // its second argument. // // @returns VAL optional string report_exit = 6; // List of variable declarations in the application's source code // that should be replaced by variables with the same name that are // part of the instrumentation harness. The presence of these // declarations in the original code allows debug UIs that access // these variables to compile when the instrumentation pass is // disabled. repeated string declaration_to_remove = 3; // Definition of functions used to report module contents and // function calls. Will be added to the start of the app's main // module. repeated string init = 4; // name of function(); // used to inform the harness about the app name optional string app_name_setter = 5; } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MoveFunctionDeclarations.java0000644000175000017500000000527712115204405030457 0ustar apoapo/* * 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.common.collect.Maps; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * Moves top-level function declarations to the top. * * Enable this pass if a try catch block wraps the output after compilation, * and the output runs on Firefox because function declarations are only * defined when reached inside a try catch block on Firefox. * * On Firefox, this code works: * * var g = f; * function f() {} * * but this code does not work: * * try { * var g = f; * function f() {} * } catch(e) {} * */ class MoveFunctionDeclarations implements Callback, CompilerPass { private final AbstractCompiler compiler; private final Map> functions; MoveFunctionDeclarations(AbstractCompiler compiler) { this.compiler = compiler; functions = Maps.newHashMap(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Entry> entry : functions.entrySet()) { JSModule module = entry.getKey(); Node addingRoot = compiler.getNodeForCodeInsertion(module); for (Node n : Lists.reverse(entry.getValue())) { addingRoot.addChildToFront(n); } } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { Node gramps = n.getAncestor(2); return gramps == null || !gramps.isScript(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (parent == null || !parent.isScript()) { return; } if (NodeUtil.isFunctionDeclaration(n)) { parent.removeChild(n); compiler.reportCodeChange(); JSModule module = t.getModule(); List moduleFunctions = functions.get(module); if (moduleFunctions == null) { moduleFunctions = Lists.newArrayList(); functions.put(module, moduleFunctions); } moduleFunctions.add(n); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AstValidator.java0000644000175000017500000005213112115204405026076 0ustar apoapo/* * Copyright 2011 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.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * This class walks the AST and validates that the structure is correct. * * @author johnlenz@google.com (John Lenz) */ public class AstValidator implements CompilerPass { // Possible enhancements: // * verify NAME, LABEL_NAME, GETPROP property name and unquoted // object-literal keys are valid JavaScript identifiers. // * optionally verify every node has source location information. // * optionally verify every node has an assigned JSType // public interface ViolationHandler { void handleViolation(String message, Node n); } private final ViolationHandler violationHandler; public AstValidator(ViolationHandler handler) { this.violationHandler = handler; } public AstValidator() { this.violationHandler = new ViolationHandler() { @Override public void handleViolation(String message, Node n) { throw new IllegalStateException( message + " Reference node " + n.toString()); } }; } @Override public void process(Node externs, Node root) { if (externs != null) { validateCodeRoot(externs); } if (root != null) { validateCodeRoot(root); } } public void validateRoot(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); validateChildCount(n, 2); validateCodeRoot(n.getFirstChild()); validateCodeRoot(n.getLastChild()); } public void validateCodeRoot(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateScript(c); } } public void validateScript(Node n) { validateNodeType(Token.SCRIPT, n); validateHasSourceName(n); validateHasInputId(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } public void validateStatement(Node n) { switch (n.getType()) { case Token.LABEL: validateLabel(n); return; case Token.BLOCK: validateBlock(n); return; case Token.FUNCTION: validateFunctionStatement(n); return; case Token.WITH: validateWith(n); return; case Token.FOR: validateFor(n); return; case Token.WHILE: validateWhile(n); return; case Token.DO: validateDo(n); return; case Token.SWITCH: validateSwitch(n); return; case Token.IF: validateIf(n); return; case Token.VAR: validateVar(n); return; case Token.EXPR_RESULT: validateExprStmt(n); return; case Token.RETURN: validateReturn(n); return; case Token.THROW: validateThrow(n); return; case Token.TRY: validateTry(n); return; case Token.BREAK: validateBreak(n); return; case Token.CONTINUE: validateContinue(n); return; case Token.EMPTY: validateChildless(n); return; case Token.DEBUGGER: validateChildless(n); return; default: violation("Expected statement but was " + Token.name(n.getType()) + ".", n); } } public void validateExpression(Node n) { switch (n.getType()) { // Childless expressions case Token.FALSE: case Token.NULL: case Token.THIS: case Token.TRUE: validateChildless(n); return; // General unary ops case Token.DELPROP: case Token.POS: case Token.NEG: case Token.NOT: case Token.INC: case Token.DEC: case Token.TYPEOF: case Token.VOID: case Token.BITNOT: case Token.CAST: validateUnaryOp(n); return; // General binary ops case Token.COMMA: case Token.OR: case Token.AND: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.ADD: case Token.MUL: case Token.MOD: case Token.DIV: validateBinaryOp(n); return; // Assignments case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: validateAssignmentExpression(n); return; case Token.HOOK: validateTrinaryOp(n); return; // Node types that require special handling case Token.STRING: validateString(n); return; case Token.NUMBER: validateNumber(n); return; case Token.NAME: validateName(n); return; case Token.GETELEM: validateBinaryOp(n); return; case Token.GETPROP: validateGetProp(n); return; case Token.ARRAYLIT: validateArrayLit(n); return; case Token.OBJECTLIT: validateObjectLit(n); return; case Token.REGEXP: validateRegExpLit(n); return; case Token.CALL: validateCall(n); return; case Token.NEW: validateNew(n); return; case Token.FUNCTION: validateFunctionExpression(n); return; default: violation("Expected expression but was " + Token.name(n.getType()), n); } } private void validateBlock(Node n) { validateNodeType(Token.BLOCK, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } private void validateSyntheticBlock(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateStatement(c); } } private void validateIsSynthetic(Node n) { if (!n.getBooleanProp(Node.SYNTHETIC_BLOCK_PROP)) { violation("Missing 'synthetic block' annotation.", n); } } private void validateHasSourceName(Node n) { String sourceName = n.getSourceFileName(); if (sourceName == null || sourceName.isEmpty()) { violation("Missing 'source name' annotation.", n); } } private void validateHasInputId(Node n) { InputId inputId = n.getInputId(); if (inputId == null) { violation("Missing 'input id' annotation.", n); } } private void validateLabel(Node n) { validateNodeType(Token.LABEL, n); validateChildCount(n, 2); validateLabelName(n.getFirstChild()); validateStatement(n.getLastChild()); } private void validateLabelName(Node n) { validateNodeType(Token.LABEL_NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateNonEmptyString(Node n) { validateNonNullString(n); if (n.getString().isEmpty()) { violation("Expected non-empty string.", n); } } private void validateNonNullString(Node n) { if (n.getString() == null) { violation("Expected non-null string.", n); } } private void validateName(Node n) { validateNodeType(Token.NAME, n); validateNonEmptyString(n); validateChildCount(n, 0); } private void validateOptionalName(Node n) { validateNodeType(Token.NAME, n); validateNonNullString(n); validateChildCount(n, 0); } private void validateFunctionStatement(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateFunctionExpression(Node n) { validateNodeType(Token.FUNCTION, n); validateChildCount(n, 3); validateOptionalName(n.getFirstChild()); validateParameters(n.getChildAtIndex(1)); validateBlock(n.getLastChild()); } private void validateParameters(Node n) { validateNodeType(Token.PARAM_LIST, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateName(c); } } private void validateCall(Node n) { validateNodeType(Token.CALL, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateNew(Node n) { validateNodeType(Token.NEW, n); validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateExpression(c); } } private void validateVar(Node n) { validateNodeType(Token.VAR, n); this.validateMinimumChildCount(n, 1); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // Don't use the validateName here as the NAME is allowed to have // a child. validateNodeType(Token.NAME, c); validateNonEmptyString(c); validateMaximumChildCount(c, 1); if (c.hasChildren()) { validateExpression(c.getFirstChild()); } } } private void validateFor(Node n) { validateNodeType(Token.FOR, n); validateMinimumChildCount(n, 3); validateMaximumChildCount(n, 4); if (NodeUtil.isForIn(n)) { // FOR-IN validateChildCount(n, 3); validateVarOrAssignmentTarget(n.getFirstChild()); validateExpression(n.getChildAtIndex(1)); } else { // FOR validateChildCount(n, 4); validateVarOrOptionalExpression(n.getFirstChild()); validateOptionalExpression(n.getChildAtIndex(1)); validateOptionalExpression(n.getChildAtIndex(2)); } validateBlock(n.getLastChild()); } private void validateVarOrOptionalExpression(Node n) { if (n.isVar()) { validateVar(n); } else { validateOptionalExpression(n); } } private void validateVarOrAssignmentTarget(Node n) { if (n.isVar()) { // Only one NAME can be declared for FOR-IN expressions. this.validateChildCount(n, 1); validateVar(n); } else { validateAssignmentTarget(n); } } private void validateWith(Node n) { validateNodeType(Token.WITH, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateWhile(Node n) { validateNodeType(Token.WHILE, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateDo(Node n) { validateNodeType(Token.DO, n); validateChildCount(n, 2); validateBlock(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateIf(Node n) { validateNodeType(Token.IF, n); validateMinimumChildCount(n, 2); validateMaximumChildCount(n, 3); validateExpression(n.getFirstChild()); validateBlock(n.getChildAtIndex(1)); if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); } } private void validateExprStmt(Node n) { validateNodeType(Token.EXPR_RESULT, n); validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateReturn(Node n) { validateNodeType(Token.RETURN, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) { validateExpression(n.getFirstChild()); } } private void validateThrow(Node n) { validateNodeType(Token.THROW, n); validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateBreak(Node n) { validateNodeType(Token.BREAK, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) { validateLabelName(n.getFirstChild()); } } private void validateContinue(Node n) { validateNodeType(Token.CONTINUE, n); validateMaximumChildCount(n, 1); if (n.hasChildren()) { validateLabelName(n.getFirstChild()); } } private void validateTry(Node n) { validateNodeType(Token.TRY, n); validateMinimumChildCount(n, 2); validateMaximumChildCount(n, 3); validateBlock(n.getFirstChild()); boolean seenCatchOrFinally = false; // Validate catch Node catches = n.getChildAtIndex(1); validateNodeType(Token.BLOCK, catches); validateMaximumChildCount(catches, 1); if (catches.hasChildren()) { validateCatch(catches.getFirstChild()); seenCatchOrFinally = true; } // Validate finally if (n.getChildCount() == 3) { validateBlock(n.getLastChild()); seenCatchOrFinally = true; } if (!seenCatchOrFinally) { violation("Missing catch or finally for try statement.", n); } } private void validateCatch(Node n) { validateNodeType(Token.CATCH, n); validateChildCount(n, 2); validateName(n.getFirstChild()); validateBlock(n.getLastChild()); } private void validateSwitch(Node n) { validateNodeType(Token.SWITCH, n); validateMinimumChildCount(n, 1); validateExpression(n.getFirstChild()); int defaults = 0; for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { validateSwitchMember(n.getLastChild()); if (c.isDefaultCase()) { defaults++; } } if (defaults > 1) { violation("Expected at most 1 'default' in switch but was " + defaults, n); } } private void validateSwitchMember(Node n) { switch (n.getType()) { case Token.CASE: validateCase(n); return; case Token.DEFAULT_CASE: validateDefault(n); return; default: violation("Expected switch member but was " + Token.name(n.getType()), n); } } private void validateDefault(Node n) { validateNodeType(Token.DEFAULT_CASE, n); validateChildCount(n, 1); validateSyntheticBlock(n.getLastChild()); } private void validateCase(Node n) { validateNodeType(Token.CASE, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateSyntheticBlock(n.getLastChild()); } private void validateOptionalExpression(Node n) { if (n.isEmpty()) { validateChildless(n); } else { validateExpression(n); } } private void validateChildless(Node n) { validateChildCount(n, 0); } private void validateAssignmentExpression(Node n) { validateChildCount(n, 2); validateAssignmentTarget(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateAssignmentTarget(Node n) { switch (n.getType()) { case Token.NAME: case Token.GETELEM: case Token.GETPROP: validateExpression(n); return; default: violation("Expected assignment target expression but was " + Token.name(n.getType()), n); } } private void validateGetProp(Node n) { validateNodeType(Token.GETPROP, n); validateChildCount(n, 2); validateExpression(n.getFirstChild()); Node prop = n.getLastChild(); validateNodeType(Token.STRING, prop); validateNonEmptyString(prop); } private void validateRegExpLit(Node n) { validateNodeType(Token.REGEXP, n); validateMinimumChildCount(n, 1); validateMaximumChildCount(n, 2); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateString(c); } } private void validateString(Node n) { validateNodeType(Token.STRING, n); validateChildCount(n, 0); try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("Invalid STRING node.", n); } } private void validateNumber(Node n) { validateNodeType(Token.NUMBER, n); validateChildCount(n, 0); try { // Validate that getDouble doesn't throw n.getDouble(); } catch (UnsupportedOperationException e) { violation("Invalid NUMBER node.", n); } } private void validateArrayLit(Node n) { validateNodeType(Token.ARRAYLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { // EMPTY is allowed to represent empty slots. validateOptionalExpression(c); } } private void validateObjectLit(Node n) { validateNodeType(Token.OBJECTLIT, n); for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { validateObjectLitKey(c); } } private void validateObjectLitKey(Node n) { switch (n.getType()) { case Token.GETTER_DEF: validateObjectLitGetKey(n); return; case Token.SETTER_DEF: validateObjectLitSetKey(n); return; case Token.STRING_KEY: validateObjectLitStringKey(n); return; default: violation("Expected object literal key expression but was " + Token.name(n.getType()), n); } } private void validateObjectLitGetKey(Node n) { validateNodeType(Token.GETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit get functions must be nameless, and must have zero parameters. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (functionParams.hasChildren()) { violation("get methods must not have parameters.", n); } } private void validateObjectLitSetKey(Node n) { validateNodeType(Token.SETTER_DEF, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); Node function = n.getFirstChild(); validateFunctionExpression(function); // objlit set functions must be nameless, and must have 1 parameter. if (!function.getFirstChild().getString().isEmpty()) { violation("Expected unnamed function expression.", n); } Node functionParams = function.getChildAtIndex(1); if (!functionParams.hasOneChild()) { violation("set methods must have exactly one parameter.", n); } } private void validateObjectLitStringKey(Node n) { validateNodeType(Token.STRING_KEY, n); validateChildCount(n, 1); validateObjectLiteralKeyName(n); validateExpression(n.getFirstChild()); } private void validateObjectLiteralKeyName(Node n) { if (n.isQuotedString()) { try { // Validate that getString doesn't throw n.getString(); } catch (UnsupportedOperationException e) { violation("getString failed for" + Token.name(n.getType()), n); } } else { validateNonEmptyString(n); } } private void validateUnaryOp(Node n) { validateChildCount(n, 1); validateExpression(n.getFirstChild()); } private void validateBinaryOp(Node n) { validateChildCount(n, 2); validateExpression(n.getFirstChild()); validateExpression(n.getLastChild()); } private void validateTrinaryOp(Node n) { validateChildCount(n, 3); Node first = n.getFirstChild(); validateExpression(first); validateExpression(first.getNext()); validateExpression(n.getLastChild()); } private void violation(String message, Node n) { violationHandler.handleViolation(message, n); } private void validateNodeType(int type, Node n) { if (n.getType() != type) { violation( "Expected " + Token.name(type) + " but was " + Token.name(n.getType()), n); } } private void validateChildCount(Node n, int i) { boolean valid = false; if (i == 0) { valid = !n.hasChildren(); } else if (i == 1) { valid = n.hasOneChild(); } else { valid = (n.getChildCount() == i); } if (!valid) { violation( "Expected " + i + " children, but was " + n.getChildCount(), n); } } private void validateMinimumChildCount(Node n, int i) { boolean valid = false; if (i == 1) { valid = n.hasChildren(); } else if (i == 2) { valid = n.hasMoreThanOneChild(); } else { valid = n.getChildCount() >= i; } if (!valid) { violation( "Expected at least " + i + " children, but was " + n.getChildCount(), n); } } private void validateMaximumChildCount(Node n, int i) { boolean valid = false; if (i == 1) { valid = !n.hasMoreThanOneChild(); } else { valid = n.getChildCount() <= i; } if (!valid) { violation( "Expected no more than " + i + " children, but was " + n.getChildCount(), n); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/OptimizeArgumentsArray.java0000644000175000017500000002422412115204405030170 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Deque; import java.util.List; /** * Optimization for functions that have {@code var_args} or access the * arguments array. * *

Example: *

 * function() { alert(arguments[0] + argument[1]) }
 * 
* to: *
 * function(a, b) { alert(a, b) }
 * 
* * Each newly inserted variable name will be unique very much like the output * of the AST found after the {@link Normalize} pass. * */ class OptimizeArgumentsArray implements CompilerPass, ScopedCallback { // The arguments object as described by ECMAScript version 3 // section 10.1.8 private static final String ARGUMENTS = "arguments"; // To ensure that the newly introduced parameter names are unique. We will // use this string as prefix unless the caller specify a different prefix. private static final String PARAMETER_PREFIX = "JSCompiler_OptimizeArgumentsArray_p"; // The prefix for the newly introduced parameter name. private final String paramPredix; // To make each parameter name unique in the function. We append an // unique integer at the end. private int uniqueId = 0; // Reference to the compiler object to notify any changes to source code AST. private final AbstractCompiler compiler; // A stack of arguments access list to the corresponding outer functions. private final Deque> argumentsAccessStack = Lists.newLinkedList(); // This stores a list of argument access in the current scope. private List currentArgumentsAccess = null; /** * Construct this pass and use {@link #PARAMETER_PREFIX} as the prefix for * all parameter names that it introduces. */ OptimizeArgumentsArray(AbstractCompiler compiler) { this(compiler, PARAMETER_PREFIX); } /** * @param paramPrefix the prefix to use for all parameter names that this * pass introduces */ OptimizeArgumentsArray(AbstractCompiler compiler, String paramPrefix) { this.compiler = Preconditions.checkNotNull(compiler); this.paramPredix = Preconditions.checkNotNull(paramPrefix); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, Preconditions.checkNotNull(root), this); } @Override public void enterScope(NodeTraversal traversal) { Preconditions.checkNotNull(traversal); // This optimization is valid only within a function so we are going to // skip over the initial entry to the global scope. Node function = traversal.getScopeRoot(); if (!function.isFunction()) { return; } // Introduces a new access list and stores the access list of the outer // scope in the stack if necessary. if (currentArgumentsAccess != null) { argumentsAccessStack.push(currentArgumentsAccess); } currentArgumentsAccess = Lists.newLinkedList(); } @Override public void exitScope(NodeTraversal traversal) { Preconditions.checkNotNull(traversal); // This is the case when we are exiting the global scope where we had never // collected argument access list. Since we do not perform this optimization // for the global scope, we will skip this exit point. if (currentArgumentsAccess == null) { return; } // Attempt to replace the argument access and if the AST has been change, // report back to the compiler. if (tryReplaceArguments(traversal.getScope())) { traversal.getCompiler().reportCodeChange(); } // After the attempt to replace the arguments. The currentArgumentsAccess // is stale and as we exit the Scope, no longer holds all the access to the // current scope anymore. We'll pop the access list from the outer scope // and set it as currentArgumentsAcess if the outer scope is not the global // scope. if (!argumentsAccessStack.isEmpty()) { currentArgumentsAccess = argumentsAccessStack.pop(); } else { currentArgumentsAccess = null; } } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node node, Node parent) { // We will continuously recurse down the AST regardless of the node types. return true; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Preconditions.checkNotNull(traversal); Preconditions.checkNotNull(node); // Searches for all the references to the arguments array. // We don't have an arguments list set up for this scope. This implies we // are currently in the global scope so we will not record any arguments // array access. if (currentArgumentsAccess == null) { return; } // Otherwise, we are in a function scope and we should record if the current // name is referring to the implicit arguments array. if (node.isName() && ARGUMENTS.equals(node.getString())) { currentArgumentsAccess.add(node); } } /** * Tries to optimize all the arguments array access in this scope by assigning * a name to each element. * * @param scope scope of the function * @return true if any modification has been done to the AST */ private boolean tryReplaceArguments(Scope scope) { Node parametersList = scope.getRootNode().getFirstChild().getNext(); Preconditions.checkState(parametersList.isParamList()); // Keep track of rather this function modified the AST and needs to be // reported back to the compiler later. boolean changed = false; // Number of parameter that can be accessed without using the arguments // array. int numNamedParameter = parametersList.getChildCount(); // We want to guess what the highest index that has been access from the // arguments array. We will guess that it does not use anything index higher // than the named parameter list first until we see other wise. int highestIndex = numNamedParameter - 1; // Iterate through all the references to arguments array in the function to // determine the real highestIndex. for (Node ref : currentArgumentsAccess) { Node getElem = ref.getParent(); // Bail on anything but argument[c] access where c is a constant. // TODO(user): We might not need to bail out all the time, there might // be more cases that we can cover. if (!getElem.isGetElem()) { return false; } Node index = ref.getNext(); // We have something like arguments[x] where x is not a constant. That // means at least one of the access is not known. if (!index.isNumber()) { // TODO(user): Its possible not to give up just yet. The type // inference did a 'semi value propagation'. If we know that string // is never a subclass of the type of the index. We'd know that // it is never 'callee'. return false; // Give up. } Node getElemParent = getElem.getParent(); // When we have argument[0](), replacing it with a() is semantically // different if argument[0] is a function call that refers to 'this' if (getElemParent.isCall() && getElemParent.getFirstChild() == getElem) { // TODO(user): We can consider using .call() if aliasing that // argument allows shorter alias for other arguments. return false; } // Replace the highest index if we see an access that has a higher index // than all the one we saw before. int value = (int) index.getDouble(); if (value > highestIndex) { highestIndex = value; } } // Number of extra arguments we need. // For example: function() { arguments[3] } access index 3 so // it will need 4 extra named arguments to changed into: // function(a,b,c,d) { d }. int numExtraArgs = highestIndex - numNamedParameter + 1; // Temporary holds the new names as string for quick access later. String[] argNames = new String[numExtraArgs]; // Insert the formal parameter to the method's signature. // Example: function() --> function(r0, r1, r2) for (int i = 0; i < numExtraArgs; i++) { String name = getNewName(); argNames[i] = name; parametersList.addChildrenToBack(IR.name(name)); changed = true; } // This loop performs the replacement of arguments[x] -> a if x is known. for (Node ref : currentArgumentsAccess) { Node index = ref.getNext(); // Skip if it is unknown. if (!index.isNumber()) { continue; } int value = (int) index.getDouble(); // Unnamed parameter. if (value >= numNamedParameter) { ref.getParent().getParent().replaceChild(ref.getParent(), IR.name(argNames[value - numNamedParameter])); } else { // Here, for no apparent reason, the user is accessing a named parameter // with arguments[idx]. We can replace it with the actual name for them. Node name = parametersList.getFirstChild(); // This is a linear search for the actual name from the signature. // It is not necessary to make this fast because chances are the user // will not deliberately write code like this. for (int i = 0; i < value; i++) { name = name.getNext(); } ref.getParent().getParent().replaceChild(ref.getParent(), IR.name(name.getString())); } changed = true; } return changed; } /** * Generate a unique name for the next parameter. */ private String getNewName() { return paramPredix + uniqueId++; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GlobalVarReferenceMap.java0000644000175000017500000002161512115204405027632 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * An implementation for {@code ReferenceMap} that is specific to global scope * and can be used in different passes. In other words instead of relying on * Var object it relies on the name of the variable. It also supports hot-swap * update of reference map for a specific script. * * @see ReferenceCollectingCallback#exitScope(NodeTraversal) * * @author bashir@google.com (Bashir Sadjad) */ class GlobalVarReferenceMap implements ReferenceMap { private Map refMap = null; private final Map inputOrder; /** * @param inputs The ordered list of all inputs for the compiler. */ GlobalVarReferenceMap(List inputs, List externs) { inputOrder = Maps.newHashMap(); int ind = 0; for (CompilerInput extern : externs) { inputOrder.put(extern.getInputId(), ind); ind++; } for (CompilerInput input : inputs) { inputOrder.put(input.getInputId(), ind); ind++; } } @Override public ReferenceCollection getReferences(Var var) { if (!var.isGlobal()) { return null; } return refMap.get(var.getName()); } /** * Resets global var reference map with the new provide map. * * @param globalRefMap The reference map result of a * {@link ReferenceCollectingCallback} pass collected from the whole AST. */ private void resetGlobalVarReferences( Map globalRefMap) { refMap = Maps.newHashMap(); for (Entry entry : globalRefMap.entrySet()) { Var var = entry.getKey(); if (var.isGlobal()) { refMap.put(var.getName(), entry.getValue()); } } } /** * Updates the internal reference map based on the provided parameters. If * {@code scriptRoot} is not SCRIPT, it basically replaces the internal map * with the new one, otherwise it replaces all the information associated to * the given script. * * @param refMapPatch The reference map result of a * {@link ReferenceCollectingCallback} pass which might be collected from * the whole AST or just a sub-tree associated to a SCRIPT node. * @param root AST sub-tree root on which reference collection was done. */ void updateGlobalVarReferences(Map refMapPatch, Node root) { if (refMap == null || !root.isScript()) { resetGlobalVarReferences(refMapPatch); return; } InputId inputId = root.getInputId(); Preconditions.checkNotNull(inputId); // Note there are two assumptions here (i) the order of compiler inputs // has not changed and (ii) all references are in the order they appear // in AST (this is enforced in ReferenceCollectionCallback). removeScriptReferences(inputId); for (Entry entry : refMapPatch.entrySet()) { Var var = entry.getKey(); if (var.isGlobal()) { replaceReferences(var.getName(), inputId, entry.getValue()); } } } private void removeScriptReferences(InputId inputId) { Preconditions.checkNotNull(inputId); if (!inputOrder.containsKey(inputId)) { return; // Input did not exist when last computed, so skip } // TODO(bashir): If this is too slow it is not too difficult to make it // faster with keeping an index for variables accessed in sourceName. for (ReferenceCollection collection : refMap.values()) { if (collection == null) { continue; } List oldRefs = collection.references; SourceRefRange range = findSourceRefRange(oldRefs, inputId); List newRefs = Lists.newArrayList(range.refsBefore()); newRefs.addAll(range.refsAfter()); collection.references = newRefs; } } private void replaceReferences(String varName, InputId inputId, ReferenceCollection newSourceCollection) { ReferenceCollection combined = new ReferenceCollection(); List combinedRefs = combined.references; ReferenceCollection oldCollection = refMap.get(varName); refMap.put(varName, combined); if (oldCollection == null) { combinedRefs.addAll(newSourceCollection.references); return; } // otherwise replace previous references that are from sourceName SourceRefRange range = findSourceRefRange(oldCollection.references, inputId); combinedRefs.addAll(range.refsBefore()); combinedRefs.addAll(newSourceCollection.references); combinedRefs.addAll(range.refsAfter()); } /** * Finds the range of references associated to {@code sourceName}. Note that * even if there is no sourceName references the returned information can be * used to decide where to insert new sourceName refs. */ private SourceRefRange findSourceRefRange(List refList, InputId inputId) { Preconditions.checkNotNull(inputId); // TODO(bashir): We can do binary search here, but since this is fast enough // right now, we just do a linear search for simplicity. int lastBefore = -1; int firstAfter = refList.size(); int index = 0; Preconditions.checkState(inputOrder.containsKey(inputId), inputId.getIdName()); int sourceInputOrder = inputOrder.get(inputId); for (Reference ref : refList) { Preconditions.checkNotNull(ref.getInputId()); int order = inputOrder.get(ref.getInputId()); if (order < sourceInputOrder) { lastBefore = index; } else if (order > sourceInputOrder) { firstAfter = index; break; } index++; } return new SourceRefRange(refList, lastBefore, firstAfter); } private static class SourceRefRange { private final int lastBefore; private final int firstAfter; private final List refList; SourceRefRange(List refList, int lastBefore, int firstAfter) { this.lastBefore = Math.max(lastBefore, -1); this.firstAfter = Math.min(firstAfter, refList.size()); this.refList = refList; } /** Note that the returned list is backed by {@code refList}! */ List refsBefore() { return refList.subList(0, lastBefore + 1); } /** Note that the returned list is backed by {@code refList}! */ List refsAfter() { return refList.subList(firstAfter, refList.size()); } } /** * @param globalScope a new Global Scope to replace the scope of references * with. */ public void updateReferencesWithGlobalScope(Scope globalScope) { for (ReferenceCollection collection : refMap.values()) { List newRefs = Lists.newArrayListWithCapacity(collection.references.size()); for (Reference ref : collection.references) { if (ref.getScope() != globalScope) { newRefs.add(ref.cloneWithNewScope(globalScope)); } else { newRefs.add(ref); } } collection.references = newRefs; } } /** * A CleanupPass implementation that will replace references to old Syntactic * Global Scopes generated in previous compile runs with references to the * Global Typed Scope. * * @author tylerg@google.com (Tyler Goodwin) */ static class GlobalVarRefCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public GlobalVarRefCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { GlobalVarReferenceMap refMap = compiler.getGlobalVarReferences(); if (refMap != null) { refMap.updateReferencesWithGlobalScope(compiler.getTopScope()); } } @Override public void process(Node externs, Node root) { // GlobalVarRefCleanupPass should not do work during process. } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/WarningsGuard.java0000644000175000017500000000646112115204405026261 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; import java.io.Serializable; /** * Class that allows to flexibly manage what to do with a reported * warning/error. * * Guard has several choices: * - return OFF - suppress the warning/error * - return WARNING * - return ERROR report it with high severity * - return null. Does not know what to do with it. Lets the other guard * decide what to do with it. * * Although the interface is very simple, it allows you easily customize what * warnings you are interested in. * * For example there are could be several implementations: * StrictGuard - {return ERROR}. All warnings should be treat as errors. * SilentGuard - {if (WARNING) return OFF}. Suppress all warnings but still * fail if JS has errors. * WhitelistGuard (if !whitelistErrors.contains(error) return ERROR) return * error if it does not present in the whitelist. * * @author anatol@google.com (Anatol Pomazau) */ public abstract class WarningsGuard implements Serializable { public static enum Priority { MAX(1), MIN(100), STRICT(100), DEFAULT(50), SUPPRESS_BY_WHITELIST(40), SUPPRESS_DOC(20), FILTER_BY_PATH(1); final int value; Priority(int value) { this.value = value; } public int getValue() { return value; } } /** * Returns a new check level for a given error. OFF - suppress it, ERROR - * report as error. null means that this guard does not know what to do * with the error. Null is extremely helpful when you have a chain of * guards. If current guard returns null, then the next in the chain should * process it. * * @param error a reported error. * @return what level given error should have. */ public abstract CheckLevel level(JSError error); /** * The priority in which warnings guards are applied. Lower means the * guard will be applied sooner. Expressed on a scale of 1 to 100. */ protected int getPriority() { return Priority.DEFAULT.value; } /** * Returns whether all warnings in the given diagnostic group will be * filtered out. Used to determine which passes to skip. * * @param group A group of DiagnosticTypes. * @return Whether all warnings of these types are disabled by this guard. */ protected boolean disables(DiagnosticGroup group) { return false; } /** * Returns whether any of the warnings in the given diagnostic group will be * upgraded to a warning or error. * * @param group A group of DiagnosticTypes. * @return Whether any warnings of these types are enabled by this guard. */ protected boolean enables(DiagnosticGroup group) { return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ClosureCodeRemoval.java0000644000175000017500000001546612115204405027250 0ustar apoapo/* * 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.common.collect.Sets; import com.google.javascript.jscomp.CodingConvention.AssertionFunctionSpec; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import java.util.List; import java.util.Set; /** *

Compiler pass that removes Closure-specific code patterns.

* *

Currently does the following:

* *
    *
  • Instead of setting abstract methods to a function that throws an * informative error, this pass allows some binary size reduction by * removing these methods altogether for production builds.
  • *
  • Remove calls to assertion functions (like goog.asserts.assert). * If the return value of the assertion function is used, then * the first argument (the asserted value) will be directly inlined. * Otherwise, the entire call will be removed. It is well-known that * this is not provably safe, much like the equivalent assert * statement in Java.
  • *
* * @author robbyw@google.com (Robby Walker) */ final class ClosureCodeRemoval implements CompilerPass { /** Reference to the JS compiler */ private final AbstractCompiler compiler; /** Name used to denote an abstract function */ static final String ABSTRACT_METHOD_NAME = "goog.abstractMethod"; private final boolean removeAbstractMethods; private final boolean removeAssertionCalls; /** * List of names referenced in successive generations of finding referenced * nodes. */ private final List abstractMethodAssignmentNodes = Lists.newArrayList(); /** * List of assertion functions. */ private final List assertionCalls = Lists.newArrayList(); /** * Utility class to track a node and its parent. */ private class RemovableAssignment { /** * The node */ final Node node; /** * Its parent */ final Node parent; /** * Full chain of ASSIGN ancestors */ final List assignAncestors = Lists.newArrayList(); /** * The last ancestor */ final Node lastAncestor; /** * Data structure for information about a removable assignment. * * @param nameNode The LHS * @param assignNode The parent ASSIGN node * @param traversal Access to further levels, assumed to start at 1 */ public RemovableAssignment(Node nameNode, Node assignNode, NodeTraversal traversal) { this.node = nameNode; this.parent = assignNode; Node ancestor = assignNode; do { ancestor = ancestor.getParent(); assignAncestors.add(ancestor); } while (ancestor.isAssign() && ancestor.getFirstChild().isQualifiedName()); lastAncestor = ancestor.getParent(); } /** * Remove this node. */ public void remove() { Node rhs = node.getNext(); Node last = parent; for (Node ancestor : assignAncestors) { if (ancestor.isExprResult()) { lastAncestor.removeChild(ancestor); } else { rhs.detachFromParent(); ancestor.replaceChild(last, rhs); } last = ancestor; } compiler.reportCodeChange(); } } /** * Identifies all assignments of the abstract method to a variable. */ private class FindAbstractMethods extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isAssign()) { Node nameNode = n.getFirstChild(); Node valueNode = n.getLastChild(); if (nameNode.isQualifiedName() && valueNode.isQualifiedName() && ABSTRACT_METHOD_NAME.equals(valueNode.getQualifiedName())) { abstractMethodAssignmentNodes.add(new RemovableAssignment( n.getFirstChild(), n, t)); } } } } /** * Identifies all assertion calls. */ private class FindAssertionCalls extends AbstractPostOrderCallback { Set assertionNames = Sets.newHashSet(); FindAssertionCalls() { for (AssertionFunctionSpec spec : compiler.getCodingConvention().getAssertionFunctions()) { assertionNames.add(spec.getFunctionName()); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { String fnName = n.getFirstChild().getQualifiedName(); if (assertionNames.contains(fnName)) { assertionCalls.add(n); } } } } /** * Creates a Closure code remover. * * @param compiler The AbstractCompiler * @param removeAbstractMethods Remove declarations of abstract methods. * @param removeAssertionCalls Remove calls to goog.assert functions. */ ClosureCodeRemoval(AbstractCompiler compiler, boolean removeAbstractMethods, boolean removeAssertionCalls) { this.compiler = compiler; this.removeAbstractMethods = removeAbstractMethods; this.removeAssertionCalls = removeAssertionCalls; } @Override public void process(Node externs, Node root) { List passes = Lists.newArrayList(); if (removeAbstractMethods) { passes.add(new FindAbstractMethods()); } if (removeAssertionCalls) { passes.add(new FindAssertionCalls()); } CombinedCompilerPass.traverse(compiler, root, passes); for (RemovableAssignment assignment : abstractMethodAssignmentNodes) { assignment.remove(); } for (Node call : assertionCalls) { // If the assertion is an expression, just strip the whole thing. Node parent = call.getParent(); if (parent.isExprResult()) { parent.getParent().removeChild(parent); } else { // Otherwise, replace the assertion with its first argument, // which is the return value of the assertion. Node firstArg = call.getFirstChild().getNext(); if (firstArg == null) { parent.replaceChild(call, NodeUtil.newUndefinedNode(call)); } else { parent.replaceChild(call, firstArg.detachFromParent()); } } compiler.reportCodeChange(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SourceAst.java0000644000175000017500000000350312115204405025410 0ustar apoapo/* * 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.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.io.Serializable; /** * An interface for accessing the AST root of an input. * */ public interface SourceAst extends Serializable { /** * Gets the root node of the AST for the source file this represents. The AST * is lazily instantiated and cached. */ public Node getAstRoot(AbstractCompiler compiler); /** * Removes any references to root node of the AST. If it is requested again, * another parse will be performed. This method is needed to allow the ASTs * to be garbage collected if the inputs are still around after compilation. */ public void clearAst(); /** @return The input id associated with this AST */ public InputId getInputId(); /** Returns the source file the generated AST represents. */ public SourceFile getSourceFile(); /** * Sets the source file the generated AST represents. This can be called after * deserializing if access to the source file is needed. If a different file * is provided than that with which this was created, an IllegalStateException * will be thrown. */ public void setSourceFile(SourceFile file); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MessageBundle.java0000644000175000017500000000317512115204405026223 0ustar apoapo/* * Copyright 2004 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; /** * An interface for providing alternative values for user-visible messages in * JavaScript code. * */ public interface MessageBundle { /** * Gets the message ID generator to use to compute message IDs for this * type of bundle. * @return idGenerator instance or null if we do not want to use any custom * id generation. In case if idGenerator is null caller should decide how * to create id by itself. In the most cases using the message key is * enough. */ public JsMessage.IdGenerator idGenerator(); /** * Gets a message replacement. * * @param id the id of the message being replaced; the key is message ID * generated by {@link JsMessage.IdGenerator} * @return the message replacement, which may be null. */ public JsMessage getMessage(String id); /** * Returns an iterable over the keys that this object has replacements for. * @return all messages from this bundle. */ public Iterable getAllMessages(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ComposeWarningsGuard.java0000644000175000017500000001224112115204405027600 0ustar apoapo/* * 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.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CheckLevel; import java.io.Serializable; import java.util.*; import java.util.Map; import java.util.TreeSet; /** * WarningsGuard that represents just a chain of other guards. For example we * could have following chain * 1) all warnings outside of /foo/ should be suppressed * 2) errors with key JSC_BAR should be marked as warning * 3) the rest should be reported as error * * This class is designed for such behavior. * * @author anatol@google.com (Anatol Pomazau) */ public class ComposeWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; // The order that the guards were added in. private final Map orderOfAddition = Maps.newHashMap(); private int numberOfAdds = 0; private final Comparator guardComparator = new GuardComparator(orderOfAddition); private boolean demoteErrors = false; private static class GuardComparator implements Comparator, Serializable { private static final long serialVersionUID = 1L; private final Map orderOfAddition; private GuardComparator(Map orderOfAddition) { this.orderOfAddition = orderOfAddition; } @Override public int compare(WarningsGuard a, WarningsGuard b) { int priorityDiff = a.getPriority() - b.getPriority(); if (priorityDiff != 0) { return priorityDiff; } // If the warnings guards have the same priority, the one that // was added last wins. return orderOfAddition.get(b).intValue() - orderOfAddition.get(a).intValue(); } } // The order that the guards are applied in. private final TreeSet guards = new TreeSet(guardComparator); public ComposeWarningsGuard(List guards) { addGuards(guards); } public ComposeWarningsGuard(WarningsGuard... guards) { this(Lists.newArrayList(guards)); } void addGuard(WarningsGuard guard) { if (guard instanceof ComposeWarningsGuard) { ComposeWarningsGuard composeGuard = (ComposeWarningsGuard) guard; if (composeGuard.demoteErrors) { this.demoteErrors = composeGuard.demoteErrors; } // Reverse the guards, so that they have the same order in the result. addGuards(Lists.newArrayList(composeGuard.guards.descendingSet())); } else { numberOfAdds++; orderOfAddition.put(guard, numberOfAdds); guards.remove(guard); guards.add(guard); } } private void addGuards(Iterable guards) { for (WarningsGuard guard : guards) { addGuard(guard); } } @Override public CheckLevel level(JSError error) { for (WarningsGuard guard : guards) { CheckLevel newLevel = guard.level(error); if (newLevel != null) { if (demoteErrors && newLevel == CheckLevel.ERROR) { return CheckLevel.WARNING; } return newLevel; } } return null; } @Override public boolean disables(DiagnosticGroup group) { nextSingleton: for (DiagnosticType type : group.getTypes()) { DiagnosticGroup singleton = DiagnosticGroup.forType(type); for (WarningsGuard guard : guards) { if (guard.disables(singleton)) { continue nextSingleton; } else if (guard.enables(singleton)) { return false; } } return false; } return true; } /** * Determines whether this guard will "elevate" the status of any disabled * diagnostic type in the group to a warning or an error. */ @Override public boolean enables(DiagnosticGroup group) { for (WarningsGuard guard : guards) { if (guard.enables(group)) { return true; } else if (guard.disables(group)) { return false; } } return false; } List getGuards() { return Collections.unmodifiableList(Lists.newArrayList(guards)); } /** * Make a warnings guard that's the same as this one but with * all escalating guards turned down. */ ComposeWarningsGuard makeEmergencyFailSafeGuard() { ComposeWarningsGuard safeGuard = new ComposeWarningsGuard(); safeGuard.demoteErrors = true; for (WarningsGuard guard : guards.descendingSet()) { safeGuard.addGuard(guard); } return safeGuard; } @Override public String toString() { return Joiner.on(", ").join(guards); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RenamePrototypes.java0000644000175000017500000003445412115204405027031 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.Nullable; /** * RenamePrototypes renames custom properties (including methods) of custom * prototypes and object literals. Externed property names are never renamed. * * To ensure that a prototype property or object literal property gets renamed, * end it with an underscore. * * To ensure that a prototype property is not renamed, give it a leading * underscore. * * For custom prototype property names that lack leading and trailing * underscores: * - To always rename these, use aggressive renaming. * - If aggressive renaming is off, we use a heuristic to decide whether to * rename (to avoid most built-in JS methods). We rename if the original name * contains at least one character that is not a lowercase letter. * * When a property name is used both in a prototype definition and as an object * literal key, we rename it only if it satisfies both renaming policies. * */ class RenamePrototypes implements CompilerPass { private final AbstractCompiler compiler; private final boolean aggressiveRenaming; private final char[] reservedCharacters; /** Previously used prototype renaming map. */ private final VariableMap prevUsedRenameMap; /** * The Property class encapsulates the information needed for renaming * a method or member. */ private class Property { String oldName; String newName; int prototypeCount; int objLitCount; int refCount; Property(String name) { this.oldName = name; this.newName = null; this.prototypeCount = 0; this.objLitCount = 0; this.refCount = 0; } int count() { return prototypeCount + objLitCount + refCount; } boolean canRename() { if (this.prototypeCount > 0 && this.objLitCount == 0) { return canRenamePrototypeProperty(); } if (this.objLitCount > 0 && this.prototypeCount == 0) { return canRenameObjLitProperty(); } // We're not sure what kind of property this is, so we're conservative. // Note that we still want to try renaming the property even when both // counts are zero. It may be a property added to an object at runtime, // like: o.newProp = x; return canRenamePrototypeProperty() && canRenameObjLitProperty(); } private boolean canRenamePrototypeProperty() { if (compiler.getCodingConvention().isExported(oldName)) { // an externally visible name should not be renamed. return false; } if (compiler.getCodingConvention().isPrivate(oldName)) { // private names can be safely renamed. Rename! return true; } if (aggressiveRenaming) { return true; } for (int i = 0, n = oldName.length(); i < n; i++) { char ch = oldName.charAt(i); if (Character.isUpperCase(ch) || !Character.isLetter(ch)) { return true; } } return false; } private boolean canRenameObjLitProperty() { if (compiler.getCodingConvention().isExported(oldName)) { // an externally visible name should not be renamed. return false; } if (compiler.getCodingConvention().isPrivate(oldName)) { // private names can be safely renamed. Rename! return true; } // NOTE(user): We should probably have more aggressive options, like // renaming all obj lit properties that are not quoted. return false; } } /** * Sorts Property objects by their count, breaking ties alphabetically to * ensure a deterministic total ordering. */ private static final Comparator FREQUENCY_COMPARATOR = new Comparator() { @Override public int compare(Property a1, Property a2) { int n1 = a1.count(); int n2 = a2.count(); if (n1 != n2) { return n2 - n1; } return a1.oldName.compareTo(a2.oldName); } }; // Set of String nodes to rename private final Set stringNodes = new HashSet(); // Mapping of property names to Property objects private final Map properties = new HashMap(); // Set of names not to rename. Externed properties/methods are added later. private final Set reservedNames = new HashSet(Arrays.asList( "indexOf", "lastIndexOf", "toString", "valueOf")); // Set of OBJLIT nodes that are assigned to prototypes private final Set prototypeObjLits = new HashSet(); /** * Creates an instance. * * @param compiler The JSCompiler * @param aggressiveRenaming Whether to rename aggressively * @param reservedCharacters If specified these characters won't be used in * generated names * @param prevUsedRenameMap The rename map used in the previous compilation */ RenamePrototypes(AbstractCompiler compiler, boolean aggressiveRenaming, @Nullable char[] reservedCharacters, @Nullable VariableMap prevUsedRenameMap) { this.compiler = compiler; this.aggressiveRenaming = aggressiveRenaming; this.reservedCharacters = reservedCharacters; this.prevUsedRenameMap = prevUsedRenameMap; } /** * Does property/method renaming. * * @param externs The root of the externs parse tree * @param root The root of the main code parse tree */ @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, externs, new ProcessExternedProperties()); NodeTraversal.traverse(compiler, root, new ProcessProperties()); // Gather the properties to rename, sorted by count. SortedSet propsByFrequency = new TreeSet(FREQUENCY_COMPARATOR); for (Iterator> it = properties.entrySet().iterator(); it.hasNext(); ) { Property a = it.next().getValue(); if (a.canRename() && !reservedNames.contains(a.oldName)) { propsByFrequency.add(a); } else { it.remove(); // If we're not renaming this, make sure we don't name something // else to this name. reservedNames.add(a.oldName); } } // Try and reuse as many names from the previous compilation as possible. if (prevUsedRenameMap != null) { reusePrototypeNames(propsByFrequency); } // Generate new names. NameGenerator nameGen = new NameGenerator(reservedNames, "", reservedCharacters); StringBuilder debug = new StringBuilder(); for (Property a : propsByFrequency) { if (a.newName == null) { a.newName = nameGen.generateNextName(); reservedNames.add(a.newName); } debug.append(a.oldName).append(" => ").append(a.newName).append('\n'); } compiler.addToDebugLog("JS property assignments:\n" + debug); // Update the string nodes. boolean changed = false; for (Node n : stringNodes) { String oldName = n.getString(); Property a = properties.get(oldName); if (a != null && a.newName != null) { n.setString(a.newName); changed = changed || !a.newName.equals(oldName); } } if (changed) { compiler.reportCodeChange(); } compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED_OBFUSCATED); } /** * Runs through the list of properties and tries to rename as many as possible * with names that were used for them in the previous compilation. * {@code reservedNames} is updated with the set of reused names. * @param properties The set of properties to attempt to rename. */ private void reusePrototypeNames(Set properties) { for (Property prop : properties) { String prevName = prevUsedRenameMap.lookupNewName(prop.oldName); if (prevName != null) { if (reservedNames.contains(prevName)) { continue; } prop.newName = prevName; reservedNames.add(prevName); } } } /** * Iterate through the nodes, collect all of the STRING nodes that are * children of GETPROP or GETELEM and mark them as externs. */ private class ProcessExternedProperties extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); if (dest.isString()) { reservedNames.add(dest.getString()); } } } } /** * Iterate through the nodes, collect all of the STRING nodes that are * children of GETPROP, GETELEM, or OBJLIT, and also count the number of * times each STRING is referenced. * * Also collects OBJLIT assignments of prototypes as candidates for renaming. */ private class ProcessProperties extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); if (dest.isString()) { String s = dest.getString(); if (s.equals("prototype")) { processPrototypeParent(parent, t.getInput()); } else { markPropertyAccessCandidate(dest, t.getInput()); } } break; case Token.OBJECTLIT: if (!prototypeObjLits.contains(n)) { // Object literals have their property name/value pairs as a flat // list as their children. We want every other node in order to get // only the property names. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (TokenStream.isJSIdentifier(child.getString())) { markObjLitPropertyCandidate(child, t.getInput()); } } } break; } } /** * Processes the parent of a GETPROP prototype, which can either be * another GETPROP (in the case of Foo.prototype.bar), or can be * an assignment (in the case of Foo.prototype = ...). */ private void processPrototypeParent(Node n, CompilerInput input) { switch (n.getType()) { // Foo.prototype.getBar = function() { ... } case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); if (dest.isString()) { markPrototypePropertyCandidate(dest, input); } break; // Foo.prototype = { "getBar" : function() { ... } } case Token.ASSIGN: case Token.CALL: Node map; if (n.isAssign()) { map = n.getFirstChild().getNext(); } else { map = n.getLastChild(); } if (map.isObjectLit()) { // Remember this node so that we can avoid processing it again when // the traversal reaches it. prototypeObjLits.add(map); for (Node key = map.getFirstChild(); key != null; key = key.getNext()) { if (TokenStream.isJSIdentifier(key.getString())) { // May be STRING, GET, or SET markPrototypePropertyCandidate(key, input); } } } break; } } /** * Remembers the given String node and increments the property name's * access count. * * @param n A STRING node * @param input The Input that the node came from */ private void markPrototypePropertyCandidate(Node n, CompilerInput input) { stringNodes.add(n); getProperty(n.getString()).prototypeCount++; } /** * Remembers the given String node and increments the property name's * access count. * * @param n A STRING node * @param input The Input that the node came from */ private void markObjLitPropertyCandidate(Node n, CompilerInput input) { stringNodes.add(n); getProperty(n.getString()).objLitCount++; } /** * Remembers the given String node and increments the property name's * access count. * * @param n A STRING node * @param input The Input that the node came from */ private void markPropertyAccessCandidate(Node n, CompilerInput input) { stringNodes.add(n); getProperty(n.getString()).refCount++; } /** * Gets the current property for the given name, creating a new one if * none exists. */ private Property getProperty(String name) { Property prop = properties.get(name); if (prop == null) { prop = new Property(name); properties.put(name, prop); } return prop; } } /** * Gets the property renaming map. * * @return A mapping from original names to new names */ VariableMap getPropertyMap() { ImmutableMap.Builder map = ImmutableMap.builder(); for (Property p : properties.values()) { if (p.newName != null) { map.put(p.oldName, p.newName); } } return new VariableMap(map.build()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/regex/0000755000175000017500000000000012115204405023746 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/regex/CharRanges.java0000644000175000017500000003415412115204405026635 0ustar apoapo/* * Copyright 2011 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.regex; import java.util.Arrays; /** * An immutable sparse bitset that deals well where the data is chunky: * where P(bit[x+1] == bit[x]). E.g. [101,102,103,104,105,1001,1002,1003,1004] * is chunky. * * @author mikesamuel@gmail.com (Mike Samuel) */ final class CharRanges { /** * A strictly increasing set of bit indices where even members are the * inclusive starts of ranges, and odd members are the exclusive ends. *

* E.g., { 1, 5, 6, 10 } represents the set ( 1, 2, 3, 4, 6, 7, 8, 9 ). */ private final int[] ranges; public static final CharRanges EMPTY = new CharRanges(new int[0]); public static final CharRanges ALL_CODE_UNITS = new CharRanges(new int[] { 0, 0x10000 }); public static CharRanges inclusive(int start, int end) { if (start > end) { throw new IndexOutOfBoundsException(start + " > " + end); } return new CharRanges(new int[] { start, end + 1 }); } /** * Returns an instance containing all and only the given members. */ public static CharRanges withMembers(int... members) { return new CharRanges(intArrayToRanges(members.clone())); } /** * Returns an instance containing the given ranges. * @param ranges An even-length ordered sequence of non-overlapping, * non-contiguous, [inclusive start, exclusive end) ranges. */ public static CharRanges withRanges(int... ranges) { ranges = ranges.clone(); if ((ranges.length & 1) != 0) { throw new IllegalArgumentException(); } for (int i = 1; i < ranges.length; ++i) { if (ranges[i] <= ranges[i - 1]) { throw new IllegalArgumentException(ranges[i] + " > " + ranges[i - 1]); } } return new CharRanges(ranges); } private CharRanges(int[] ranges) { this.ranges = ranges; } private static int[] intArrayToRanges(int[] members) { int nMembers = members.length; if (nMembers == 0) { return new int[0]; } Arrays.sort(members); // Count the number of runs. int nRuns = 1; for (int i = 1; i < nMembers; ++i) { int current = members[i], last = members[i - 1]; if (current == last) { continue; } if (current != last + 1) { ++nRuns; } } int[] ranges = new int[nRuns * 2]; ranges[0] = members[0]; int k = 0; for (int i = 1; k + 2 < ranges.length; ++i) { int current = members[i], last = members[i - 1]; if (current == last) { continue; } if (current != last + 1) { ranges[++k] = last + 1; // add 1 to make end exclusive ranges[++k] = current; } } ranges[++k] = members[nMembers - 1] + 1; // add 1 to make end exclusive return ranges; } public boolean contains(int bit) { return (Arrays.binarySearch(ranges, bit) & 1) == 0; // By the contract of Arrays.binarySearch, its result is either the position // of bit in ranges or it is the bitwise inverse of the position of the // least element greater than bit. // Two cases // case (idx >= 0) // We ended up exactly on a range boundary. // Starts are inclusive and ends are both exclusive, so this contains // bit iff idx is even. // // case (idx < 0) // If the least element greater than bit is an odd element, // then bit must be greater than a start and less than an end, so // contained. // // If bit is greater than all elements, then idx will be past the end of // the array, and will be even since ranges.length is even. // // Otherwise, bit must be in the space between two runs, so not // contained. // // In all cases, oddness is equivalent to containedness. // Those two cases lead to // idx >= 0 ? ((idx & 1) == 0) : ((~idx & 1) == 1) // But ~n & bit == bit <=> n & bit == 0, so // idx >= 0 ? ((idx & 1) == 0) : ((~idx & 1) == 1) // => idx >= 0 ? ((idx & 1) == 0) : ((idx & 1) == 0) // => (idx & 1) == 0 } public int minSetBit() { return ranges.length >= 0 ? ranges[0] : Integer.MIN_VALUE; } public boolean isEmpty() { return ranges.length == 0; } public int getNumRanges() { return ranges.length >> 1; } public int start(int i) { return ranges[i << 1]; } public int end(int i) { return ranges[(i << 1) | 1]; } public CharRanges union(CharRanges other) { // Index of the input ranges int[] q = this.ranges, r = other.ranges; // Lengths of the inputs int m = q.length, n = r.length; if (m == 0) { return other; } if (n == 0) { return this; } // The output array. The length is m+n in the worst case when all the // ranges in a are disjoint from the ranges in b. int[] out = new int[m + n]; // Indexes into the various arrays int i = 0, j = 0, k = 0; // Since there are three arrays, and indices into them the following // should never occur in this function: // (1) q[j] or q[k] -- q is indexed by i // (2) r[i] or r[k] -- r is indexed by j // (3) out[i] or out[j] -- out is indexed by k // (4) i < n or j < m -- index compared to wrong limit // This loop exits because we always increment at least one of i,j. while (i < m && j < n) { // Range starts and ends. int a0 = q[i], a1 = q[i + 1], b0 = r[j], b1 = r[j + 1]; if (a1 < b0) { // [a0, a1) ends before [b0, b1) starts out[k++] = a0; out[k++] = a1; i += 2; } else if (b1 < a0) { // [b0, b1) ends before [a0, a1) starts out[k++] = b0; out[k++] = b1; j += 2; } else { // ranges overlap // We need to compute a new range based on the set of ranges that // transitively overlap. // AAAAAAAAA AAA // BBB BBB* BBB // In the range above, the start comes from one set, and the end from // another. The range with the asterisk next to it is subsumed entirely // by a range from the other, and so not all ranges on the input // contribute a value to the output. // The last BBB run serves only as a bridge -- it overlaps two // disjoint ranges in the other one so establishes that they // transitively overlap. int start = Math.min(a0, b0); // Guess at the end, and lookahead to come up with a more complete // estimate. int end = Math.max(a1, b1); i += 2; j += 2; while (i < m || j < n) { if (i < m && q[i] <= end) { end = Math.max(end, q[i + 1]); i += 2; } else if (j < n && r[j] <= end) { end = Math.max(end, r[j + 1]); j += 2; } else { break; } } out[k++] = start; out[k++] = end; } } // There may be unprocessed ranges at the end of one of the inputs. if (i < m) { System.arraycopy(q, i, out, k, m - i); k += m - i; } else if (j < n) { System.arraycopy(r, j, out, k, n - j); k += n - j; } // We guessed at the output length above. Cut off the tail. if (k != out.length) { int[] clipped = new int[k]; System.arraycopy(out, 0, clipped, 0, k); out = clipped; } return new CharRanges(out); } public CharRanges intersection(CharRanges other) { int[] aRanges = ranges, bRanges = other.ranges; int aLen = aRanges.length, bLen = bRanges.length; if (aLen == 0) { return this; } if (bLen == 0) { return other; } int aIdx = 0, bIdx = 0; int[] intersection = new int[Math.min(aLen, bLen)]; int intersectionIdx = 0; int pos = Math.min(aRanges[0], bRanges[0]); while (aIdx < aLen && bIdx < bLen) { if (aRanges[aIdx + 1] <= pos) { aIdx += 2; } else if (bRanges[bIdx + 1] <= pos) { bIdx += 2; } else { int start = Math.max(aRanges[aIdx], bRanges[bIdx]); if (pos < start) { // Advance to start of common block. pos = start; } else { // Now we know that pos is less than the ends of the two ranges and // greater or equal to the starts of the two ranges. int end = Math.min(aRanges[aIdx + 1], bRanges[bIdx + 1]); if (intersectionIdx != 0 && pos == intersection[intersectionIdx - 1]) { intersection[intersectionIdx - 1] = end; } else { if (intersectionIdx == intersection.length) { int[] newArr = new int[intersectionIdx * 2]; System.arraycopy(intersection, 0, newArr, 0, intersectionIdx); intersection = newArr; } intersection[intersectionIdx++] = pos; intersection[intersectionIdx++] = end; } pos = end; } } } if (intersectionIdx != intersection.length) { int[] newArr = new int[intersectionIdx]; System.arraycopy(intersection, 0, newArr, 0, intersectionIdx); intersection = newArr; } return new CharRanges(intersection); } public CharRanges difference(CharRanges subtrahendRanges) { // difference = minuend - subtrahend int[] minuend = this.ranges; int[] subtrahend = subtrahendRanges.ranges; int mn = minuend.length, sn = subtrahend.length; if (mn == 0 || sn == 0) { return this; } int[] difference = new int[minuend.length]; // Indices into minuend.ranges, subtrahend.ranges, and difference. int mIdx = 0, sIdx = 0, dIdx = 0; int pos = minuend[0]; while (mIdx < mn) { if (pos >= minuend[mIdx + 1]) { mIdx += 2; } else if (pos < minuend[mIdx]) { // Skip gaps in the minuend. pos = minuend[mIdx]; } else if (sIdx < sn && pos >= subtrahend[sIdx]) { // Skip over a removed part. pos = subtrahend[sIdx + 1]; sIdx += 2; } else { // Now we know that pos is between [minuend[i], minuend[i + 1]) // and outside [subtrahend[j], subtrahend[j + 1]). int end = sIdx < sn ? Math.min(minuend[mIdx + 1], subtrahend[sIdx]) : minuend[mIdx + 1]; if (dIdx != 0 && difference[dIdx - 1] == pos) { difference[dIdx - 1] = pos; } else { if (dIdx == difference.length) { int[] newArr = new int[dIdx * 2]; System.arraycopy(difference, 0, newArr, 0, dIdx); difference = newArr; } difference[dIdx++] = pos; difference[dIdx++] = end; } pos = end; } } if (dIdx != difference.length) { int[] newArr = new int[dIdx]; System.arraycopy(difference, 0, newArr, 0, dIdx); difference = newArr; } return new CharRanges(difference); } public boolean containsAll(CharRanges sub) { int[] superRanges = this.ranges; int[] subRanges = sub.ranges; int superIdx = 0, subIdx = 0; int superLen = superRanges.length, subLen = subRanges.length; while (subIdx < subLen) { if (superIdx == superLen) { return false; } if (superRanges[superIdx + 1] <= subRanges[subIdx]) { // Super range ends before subRange starts. superIdx += 2; } else if (superRanges[superIdx] > subRanges[subIdx]) { // Uncontained portion at start of sub range. return false; } else if (superRanges[superIdx + 1] >= subRanges[subIdx + 1]) { // A sub range is completely contained in the super range. // We know this because of the above condition and we have already // ruled out that subRanges[subIdx] < superRanges[superIdx]. subIdx += 2; } else { // Uncontained portion at end of sub range. return false; } } return subIdx == subLen; } /** * Shifts the bits matched by the given delta. * So if this has the bits (a, b, c, ..., z) set then the result has the bits * ((a - delta), (b - delta), (c - delta), ...., (z - delta)) set. * * @throws IndexOutOfBoundsException if shifting by delta would cause an * overflow or underflow in a 32 bit {@code signed int} range boundary. * Since the end boundaries of ranges are exclusive, even if there is no * range containing {@link Integer#MAX_VALUE}, shifting by a delta of 1 * can cause an overflow. */ public CharRanges shift(int delta) { int n = ranges.length; if (delta == 0 || n == 0) { return this; } // Test overflow/underflow if (delta < 0) { long lmin = ranges[0] + delta; if (lmin < Integer.MIN_VALUE) { throw new IndexOutOfBoundsException(); } } else { long lmax = ranges[n - 1] + delta; if (lmax > Integer.MAX_VALUE) { throw new IndexOutOfBoundsException(); } } // Create a shifted range. int[] shiftedRanges = new int[n]; for (int i = n; --i >= 0;) { shiftedRanges[i] = ranges[i] + delta; } return new CharRanges(shiftedRanges); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); for (int i = 0; i < ranges.length; ++i) { if ((i & 1) != 0 && ranges[i] == ranges[i - 1] + 1) { continue; } if (i != 0) { sb.append((i & 1) == 0 ? ' ' : '-'); } sb.append("0x").append(Integer.toString(ranges[i] - (i & 1), 16)); } sb.append(']'); return sb.toString(); } @Override public boolean equals(Object o) { if (!(o instanceof CharRanges)) { return false; } return Arrays.equals(this.ranges, ((CharRanges) o).ranges); } @Override public int hashCode() { int hc = 0; for (int i = 0, n = Math.min(16, ranges.length); i < n; ++i) { hc = (hc << 2) + ranges[i]; } return hc; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/regex/CaseCanonicalize.java0000644000175000017500000007473712115204405030026 0ustar apoapo/* * Copyright 2011 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.regex; import com.google.common.collect.ImmutableList; /** * Implements the ECMAScript 5 * Canonicalize operation * used to specify how case-insensitive regular expressions match. * *

* From section 15.10.2.9, *

* The abstract operation Canonicalize takes a character parameter ch and * performs the following steps: *
    *
  • If IgnoreCase is false, return ch. *
  • Let u be ch converted to upper case as if by calling the standard * built-in method {@code String.prototype.toUpperCase} on the one-character * String ch. *
  • If u does not consist of a single character, return ch. *
  • Let cu be u's character. *
  • If ch's code unit value is greater than or equal to decimal 128 and * cu's code unit value is less than decimal 128, then return ch. *
  • Return cu. *
* * @author Mike Samuel */ public final class CaseCanonicalize { private CaseCanonicalize() { // Uninstantiable. } // Below are tables that implement the Canonicalize operation. // We cannot use java.lang.Character.toUpperCase since that is based on // a more modern version of Unicode than that required by the ECMAScript spec. /** * Set of code units that are case-insensitively equivalent to some other * code unit according to the EcmaScript * Canonicalize operation * described in section 15.10.2.8. * The case sensitive characters are the ones that canonicalize to a character * other than themselves or have a character that canonicalizes to them. * Canonicalize is based on the definition of * {@code String.prototype.toUpperCase} which is itself based on Unicode 3.0.0 * as specified at * * UnicodeData-3.0.0 * * and * SpecialCasings-2.txt * . * *

* This table was generated by running the below on Chrome: *

*
   * for (var cc = 0; cc < 0x10000; ++cc) {
   *   var ch = String.fromCharCode(cc);
   *   var u = ch.toUpperCase();
   *   if (ch != u && u.length === 1) {
   *     var cu = u.charCodeAt(0);
   *     if (cc <= 128 || u.charCodeAt(0) > 128) {
   *       print('0x' + cc.toString(16) + ', 0x' + cu.toString(16) + ',');
   *     }
   *   }
   * }
   * 
*/ public static final CharRanges CASE_SENSITIVE = CharRanges.withRanges( 0x41, 0x5b, 0x61, 0x7b, 0xb5, 0xb6, 0xc0, 0xd7, 0xd8, 0xdf, 0xe0, 0xf7, 0xf8, 0x130, 0x132, 0x138, 0x139, 0x149, 0x14a, 0x17f, 0x180, 0x18d, 0x18e, 0x19b, 0x19c, 0x1aa, 0x1ac, 0x1ba, 0x1bc, 0x1be, 0x1bf, 0x1c0, 0x1c4, 0x1f0, 0x1f1, 0x221, 0x222, 0x234, 0x23a, 0x23f, 0x241, 0x250, 0x253, 0x255, 0x256, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x260, 0x261, 0x263, 0x264, 0x268, 0x26a, 0x26b, 0x26c, 0x26f, 0x270, 0x272, 0x273, 0x275, 0x276, 0x27d, 0x27e, 0x280, 0x281, 0x283, 0x284, 0x288, 0x28d, 0x292, 0x293, 0x345, 0x346, 0x37b, 0x37e, 0x386, 0x387, 0x388, 0x38b, 0x38c, 0x38d, 0x38e, 0x390, 0x391, 0x3a2, 0x3a3, 0x3b0, 0x3b1, 0x3cf, 0x3d0, 0x3d2, 0x3d5, 0x3d7, 0x3d8, 0x3f3, 0x3f5, 0x3f6, 0x3f7, 0x3fc, 0x3fd, 0x482, 0x48a, 0x514, 0x531, 0x557, 0x561, 0x587, 0x10a0, 0x10c6, 0x1d7d, 0x1d7e, 0x1e00, 0x1e96, 0x1e9b, 0x1e9c, 0x1ea0, 0x1efa, 0x1f00, 0x1f16, 0x1f18, 0x1f1e, 0x1f20, 0x1f46, 0x1f48, 0x1f4e, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f7e, 0x1fb0, 0x1fb2, 0x1fb8, 0x1fbc, 0x1fbe, 0x1fbf, 0x1fc8, 0x1fcc, 0x1fd0, 0x1fd2, 0x1fd8, 0x1fdc, 0x1fe0, 0x1fe2, 0x1fe5, 0x1fe6, 0x1fe8, 0x1fed, 0x1ff8, 0x1ffc, 0x2132, 0x2133, 0x214e, 0x214f, 0x2160, 0x2180, 0x2183, 0x2185, 0x24b6, 0x24ea, 0x2c00, 0x2c2f, 0x2c30, 0x2c5f, 0x2c60, 0x2c6d, 0x2c75, 0x2c77, 0x2c80, 0x2ce4, 0x2d00, 0x2d26, 0xff21, 0xff3b, 0xff41, 0xff5b ); /** * Returns the case canonical version of the given string. */ public static String caseCanonicalize(String s) { for (int i = 0, n = s.length(); i < n; ++i) { char ch = s.charAt(i); char cu = caseCanonicalize(ch); if (cu != ch) { StringBuilder sb = new StringBuilder(s); sb.setCharAt(i, cu); while (++i < n) { sb.setCharAt(i, caseCanonicalize(s.charAt(i))); } return sb.toString(); } } return s; } /** * Returns the case canonical version of the given code-unit. ECMAScript 5 * explicitly says that code-units are to be treated as their code-point * equivalent, even surrogates. */ public static char caseCanonicalize(char ch) { if (ch < 0x80) { // Normal case. return ('A' <= ch && ch <= 'Z') ? (char) (ch | 32) : ch; } // Non-ASCII case. if (CASE_SENSITIVE.contains(ch)) { for (DeltaSet ds : CANON_DELTA_SETS) { if (ds.codeUnits.contains(ch)) { return (char) (ch - ds.delta); } } } return ch; } /** * Given a character range that may include case sensitive code-units, * such as {@code [0-9B-M]}, returns the character range that includes all * the code-units in the input and those that are case-insensitively * equivalent to a code-unit in the input. */ public static CharRanges expandToAllMatched(CharRanges ranges) { CharRanges caseSensitive = ranges.intersection(CASE_SENSITIVE); if (caseSensitive.isEmpty()) { return ranges; } CharRanges expanded = CharRanges.EMPTY; for (DeltaSet ds : DELTA_SETS) { expanded = expanded.union( caseSensitive.intersection(ds.codeUnits).shift(-ds.delta)); } return ranges.union(expanded); } private static final CharRanges UCASE_ASCII_LETTERS = CharRanges.inclusive('A', 'Z'); /** * Given a character range that may include case sensitive code-units, * such as {@code [0-9B-M]}, returns the character range that includes * the minimal set of code units such that for every code unit in the * input there is a case-sensitively equivalent canonical code unit in the * output. */ public static CharRanges reduceToMinimum(CharRanges ranges) { CharRanges caseSensitive = ranges.intersection(CASE_SENSITIVE); if (caseSensitive.isEmpty()) { return ranges; } CharRanges expanded = CharRanges.EMPTY; for (DeltaSet ds : CANON_DELTA_SETS) { expanded = expanded.union( caseSensitive.intersection(ds.codeUnits).shift(-ds.delta)); } // Letters a-z gzip better than uppercase A-Z since JavaScript keywords // are lower-case, so, even though the definition of Canonicalize is // based on String.prototype.toUpperCase, we use lowercase ASCII characters // in the minimal form. expanded = expanded.difference(UCASE_ASCII_LETTERS).union( expanded.intersection(UCASE_ASCII_LETTERS).shift(32)); return ranges.difference(caseSensitive).union(expanded); } /** * Sets of code units broken down by delta that are case-insensitively * equivalent to another code unit that differs from the first by that delta. */ private static final ImmutableList DELTA_SETS = ImmutableList.of( new DeltaSet(-10795, CharRanges.withMembers(0x23a)), new DeltaSet(-10792, CharRanges.withMembers(0x23e)), new DeltaSet(-10743, CharRanges.withMembers(0x26b)), new DeltaSet(-10727, CharRanges.withMembers(0x27d)), new DeltaSet(-7264, CharRanges.withRanges(0x10a0, 0x10c6)), new DeltaSet(-7205, CharRanges.withMembers(0x399)), new DeltaSet(-3814, CharRanges.withMembers(0x1d7d)), new DeltaSet(-743, CharRanges.withMembers(0xb5)), new DeltaSet(-219, CharRanges.withMembers(0x1b7)), new DeltaSet(-218, CharRanges.withMembers(0x1a6, 0x1a9, 0x1ae)), new DeltaSet(-217, CharRanges.withRanges(0x1b1, 0x1b3)), new DeltaSet(-214, CharRanges.withMembers(0x19f)), new DeltaSet(-213, CharRanges.withMembers(0x19d)), new DeltaSet(-211, CharRanges.withMembers(0x196, 0x19c)), new DeltaSet(-210, CharRanges.withMembers(0x181)), new DeltaSet(-209, CharRanges.withMembers(0x197)), new DeltaSet(-207, CharRanges.withMembers(0x194)), new DeltaSet(-206, CharRanges.withMembers(0x186)), new DeltaSet(-205, CharRanges.withRanges(0x189, 0x18b, 0x193, 0x194)), new DeltaSet(-203, CharRanges.withMembers(0x190)), new DeltaSet(-202, CharRanges.withMembers(0x18f)), new DeltaSet(-195, CharRanges.withMembers(0x180)), new DeltaSet(-163, CharRanges.withMembers(0x19a)), new DeltaSet(-130, CharRanges.withRanges(0x19e, 0x19f, 0x37b, 0x37e)), new DeltaSet(-128, CharRanges.withRanges(0x1f78, 0x1f7a)), new DeltaSet(-126, CharRanges.withRanges(0x1f7c, 0x1f7e)), new DeltaSet(-121, CharRanges.withMembers(0xff)), new DeltaSet(-112, CharRanges.withRanges(0x1f7a, 0x1f7c)), new DeltaSet(-100, CharRanges.withRanges(0x1f76, 0x1f78)), new DeltaSet(-97, CharRanges.withMembers(0x195)), new DeltaSet(-96, CharRanges.withMembers(0x395)), new DeltaSet(-86, CharRanges.withRanges(0x39a, 0x39b, 0x1f72, 0x1f76)), new DeltaSet(-84, CharRanges.withMembers(0x345)), new DeltaSet(-80, CharRanges.withRanges(0x3a1, 0x3a2, 0x400, 0x410)), new DeltaSet(-79, CharRanges.withMembers(0x18e)), new DeltaSet(-74, CharRanges.withRanges(0x1f70, 0x1f72)), new DeltaSet(-71, CharRanges.withMembers(0x245)), new DeltaSet(-69, CharRanges.withMembers(0x244)), new DeltaSet(-64, CharRanges.withMembers(0x38c)), new DeltaSet(-63, CharRanges.withRanges(0x38e, 0x390)), new DeltaSet(-62, CharRanges.withMembers(0x392)), new DeltaSet(-59, CharRanges.withMembers(0x1e60)), new DeltaSet(-57, CharRanges.withMembers(0x398)), new DeltaSet(-56, CharRanges.withMembers(0x1bf)), new DeltaSet(-54, CharRanges.withMembers(0x3a0)), new DeltaSet(-48, CharRanges.withRanges(0x531, 0x557, 0x2c00, 0x2c2f)), new DeltaSet(-47, CharRanges.withMembers(0x3a6)), new DeltaSet(-38, CharRanges.withMembers(0x386)), new DeltaSet(-37, CharRanges.withRanges(0x388, 0x38b)), new DeltaSet(-32, CharRanges.withRanges( 0x41, 0x5b, 0xc0, 0xd7, 0xd8, 0xdf, 0x391, 0x3a2, 0x3a3, 0x3ac, 0x410, 0x430, 0xff21, 0xff3b)), new DeltaSet(-31, CharRanges.withMembers(0x3a3)), new DeltaSet(-28, CharRanges.withMembers(0x2132)), new DeltaSet(-26, CharRanges.withRanges(0x24b6, 0x24d0)), new DeltaSet(-16, CharRanges.withRanges(0x2160, 0x2170)), new DeltaSet(-15, CharRanges.withMembers(0x4c0)), new DeltaSet(-8, CharRanges.withRanges( 0x1f00, 0x1f08, 0x1f10, 0x1f16, 0x1f20, 0x1f28, 0x1f30, 0x1f38, 0x1f40, 0x1f46, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57, 0x1f58, 0x1f60, 0x1f68, 0x1fb0, 0x1fb2, 0x1fd0, 0x1fd2, 0x1fe0, 0x1fe2)), new DeltaSet(-7, CharRanges.withMembers(0x3f2, 0x1fe5)), new DeltaSet(-2, CharRanges.withMembers(0x1c4, 0x1c7, 0x1ca, 0x1f1)), new DeltaSet(-1, CharRanges.withMembers( 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x132, 0x134, 0x136, 0x139, 0x13b, 0x13d, 0x13f, 0x141, 0x143, 0x145, 0x147, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17b, 0x17d, 0x182, 0x184, 0x187, 0x18b, 0x191, 0x198, 0x1a0, 0x1a2, 0x1a4, 0x1a7, 0x1ac, 0x1af, 0x1b3, 0x1b5, 0x1b8, 0x1bc, 0x1c4, 0x1c7, 0x1ca, 0x1cd, 0x1cf, 0x1d1, 0x1d3, 0x1d5, 0x1d7, 0x1d9, 0x1db, 0x1de, 0x1e0, 0x1e2, 0x1e4, 0x1e6, 0x1e8, 0x1ea, 0x1ec, 0x1ee, 0x1f1, 0x1f4, 0x1f8, 0x1fa, 0x1fc, 0x1fe, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20a, 0x20c, 0x20e, 0x210, 0x212, 0x214, 0x216, 0x218, 0x21a, 0x21c, 0x21e, 0x222, 0x224, 0x226, 0x228, 0x22a, 0x22c, 0x22e, 0x230, 0x232, 0x23b, 0x241, 0x246, 0x248, 0x24a, 0x24c, 0x24e, 0x3d8, 0x3da, 0x3dc, 0x3de, 0x3e0, 0x3e2, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ee, 0x3f7, 0x3fa, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46a, 0x46c, 0x46e, 0x470, 0x472, 0x474, 0x476, 0x478, 0x47a, 0x47c, 0x47e, 0x480, 0x48a, 0x48c, 0x48e, 0x490, 0x492, 0x494, 0x496, 0x498, 0x49a, 0x49c, 0x49e, 0x4a0, 0x4a2, 0x4a4, 0x4a6, 0x4a8, 0x4aa, 0x4ac, 0x4ae, 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba, 0x4bc, 0x4be, 0x4c1, 0x4c3, 0x4c5, 0x4c7, 0x4c9, 0x4cb, 0x4cd, 0x4d0, 0x4d2, 0x4d4, 0x4d6, 0x4d8, 0x4da, 0x4dc, 0x4de, 0x4e0, 0x4e2, 0x4e4, 0x4e6, 0x4e8, 0x4ea, 0x4ec, 0x4ee, 0x4f0, 0x4f2, 0x4f4, 0x4f6, 0x4f8, 0x4fa, 0x4fc, 0x4fe, 0x500, 0x502, 0x504, 0x506, 0x508, 0x50a, 0x50c, 0x50e, 0x510, 0x512, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, 0x1e0c, 0x1e0e, 0x1e10, 0x1e12, 0x1e14, 0x1e16, 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, 0x1e20, 0x1e22, 0x1e24, 0x1e26, 0x1e28, 0x1e2a, 0x1e2c, 0x1e2e, 0x1e30, 0x1e32, 0x1e34, 0x1e36, 0x1e38, 0x1e3a, 0x1e3c, 0x1e3e, 0x1e40, 0x1e42, 0x1e44, 0x1e46, 0x1e48, 0x1e4a, 0x1e4c, 0x1e4e, 0x1e50, 0x1e52, 0x1e54, 0x1e56, 0x1e58, 0x1e5a, 0x1e5c, 0x1e5e, 0x1e60, 0x1e62, 0x1e64, 0x1e66, 0x1e68, 0x1e6a, 0x1e6c, 0x1e6e, 0x1e70, 0x1e72, 0x1e74, 0x1e76, 0x1e78, 0x1e7a, 0x1e7c, 0x1e7e, 0x1e80, 0x1e82, 0x1e84, 0x1e86, 0x1e88, 0x1e8a, 0x1e8c, 0x1e8e, 0x1e90, 0x1e92, 0x1e94, 0x1ea0, 0x1ea2, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eaa, 0x1eac, 0x1eae, 0x1eb0, 0x1eb2, 0x1eb4, 0x1eb6, 0x1eb8, 0x1eba, 0x1ebc, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ec8, 0x1eca, 0x1ecc, 0x1ece, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1eda, 0x1edc, 0x1ede, 0x1ee0, 0x1ee2, 0x1ee4, 0x1ee6, 0x1ee8, 0x1eea, 0x1eec, 0x1eee, 0x1ef0, 0x1ef2, 0x1ef4, 0x1ef6, 0x1ef8, 0x2183, 0x2c60, 0x2c67, 0x2c69, 0x2c6b, 0x2c75, 0x2c80, 0x2c82, 0x2c84, 0x2c86, 0x2c88, 0x2c8a, 0x2c8c, 0x2c8e, 0x2c90, 0x2c92, 0x2c94, 0x2c96, 0x2c98, 0x2c9a, 0x2c9c, 0x2c9e, 0x2ca0, 0x2ca2, 0x2ca4, 0x2ca6, 0x2ca8, 0x2caa, 0x2cac, 0x2cae, 0x2cb0, 0x2cb2, 0x2cb4, 0x2cb6, 0x2cb8, 0x2cba, 0x2cbc, 0x2cbe, 0x2cc0, 0x2cc2, 0x2cc4, 0x2cc6, 0x2cc8, 0x2cca, 0x2ccc, 0x2cce, 0x2cd0, 0x2cd2, 0x2cd4, 0x2cd6, 0x2cd8, 0x2cda, 0x2cdc, 0x2cde, 0x2ce0, 0x2ce2)), new DeltaSet(1, CharRanges.withMembers( 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0x17a, 0x17c, 0x17e, 0x183, 0x185, 0x188, 0x18c, 0x192, 0x199, 0x1a1, 0x1a3, 0x1a5, 0x1a8, 0x1ad, 0x1b0, 0x1b4, 0x1b6, 0x1b9, 0x1bd, 0x1c5, 0x1c8, 0x1cb, 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f2, 0x1f5, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x223, 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x23c, 0x242, 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3f8, 0x3fb, 0x461, 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, 0x513, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x2184, 0x2c61, 0x2c68, 0x2c6a, 0x2c6c, 0x2c76, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, 0x2ce3)), new DeltaSet(2, CharRanges.withMembers(0x1c6, 0x1c9, 0x1cc, 0x1f3)), new DeltaSet(7, CharRanges.withMembers(0x3f9, 0x1fec)), new DeltaSet(8, CharRanges.withRanges( 0x1f08, 0x1f10, 0x1f18, 0x1f1e, 0x1f28, 0x1f30, 0x1f38, 0x1f40, 0x1f48, 0x1f4e, 0x1f59, 0x1f5a, 0x1f5b, 0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f60, 0x1f68, 0x1f70, 0x1fb8, 0x1fba, 0x1fd8, 0x1fda, 0x1fe8, 0x1fea)), new DeltaSet(15, CharRanges.withMembers(0x4cf)), new DeltaSet(16, CharRanges.withRanges(0x2170, 0x2180)), new DeltaSet(26, CharRanges.withRanges(0x24d0, 0x24ea)), new DeltaSet(28, CharRanges.withMembers(0x214e)), new DeltaSet(31, CharRanges.withMembers(0x3c2)), new DeltaSet(32, CharRanges.withRanges( 0x61, 0x7b, 0xe0, 0xf7, 0xf8, 0xff, 0x3b1, 0x3c2, 0x3c3, 0x3cc, 0x430, 0x450, 0xff41, 0xff5b)), new DeltaSet(37, CharRanges.withRanges(0x3ad, 0x3b0)), new DeltaSet(38, CharRanges.withMembers(0x3ac)), new DeltaSet(47, CharRanges.withMembers(0x3d5)), new DeltaSet(48, CharRanges.withRanges(0x561, 0x587, 0x2c30, 0x2c5f)), new DeltaSet(54, CharRanges.withMembers(0x3d6)), new DeltaSet(56, CharRanges.withMembers(0x1f7)), new DeltaSet(57, CharRanges.withMembers(0x3d1)), new DeltaSet(59, CharRanges.withMembers(0x1e9b)), new DeltaSet(62, CharRanges.withMembers(0x3d0)), new DeltaSet(63, CharRanges.withRanges(0x3cd, 0x3cf)), new DeltaSet(64, CharRanges.withMembers(0x3cc)), new DeltaSet(69, CharRanges.withMembers(0x289)), new DeltaSet(71, CharRanges.withMembers(0x28c)), new DeltaSet(74, CharRanges.withRanges(0x1fba, 0x1fbc)), new DeltaSet(79, CharRanges.withMembers(0x1dd)), new DeltaSet(80, CharRanges.withRanges(0x3f1, 0x3f2, 0x450, 0x460)), new DeltaSet(84, CharRanges.withMembers(0x399)), new DeltaSet(86, CharRanges.withRanges(0x3f0, 0x3f1, 0x1fc8, 0x1fcc)), new DeltaSet(96, CharRanges.withMembers(0x3f5)), new DeltaSet(97, CharRanges.withMembers(0x1f6)), new DeltaSet(100, CharRanges.withRanges(0x1fda, 0x1fdc)), new DeltaSet(112, CharRanges.withRanges(0x1fea, 0x1fec)), new DeltaSet(121, CharRanges.withMembers(0x178)), new DeltaSet(126, CharRanges.withRanges(0x1ffa, 0x1ffc)), new DeltaSet(128, CharRanges.withRanges(0x1ff8, 0x1ffa)), new DeltaSet(130, CharRanges.withRanges(0x220, 0x221, 0x3fd, 0x400)), new DeltaSet(163, CharRanges.withMembers(0x23d)), new DeltaSet(195, CharRanges.withMembers(0x243)), new DeltaSet(202, CharRanges.withMembers(0x259)), new DeltaSet(203, CharRanges.withMembers(0x25b)), new DeltaSet(205, CharRanges.withRanges(0x256, 0x258, 0x260, 0x261)), new DeltaSet(206, CharRanges.withMembers(0x254)), new DeltaSet(207, CharRanges.withMembers(0x263)), new DeltaSet(209, CharRanges.withMembers(0x268)), new DeltaSet(210, CharRanges.withMembers(0x253)), new DeltaSet(211, CharRanges.withMembers(0x269, 0x26f)), new DeltaSet(213, CharRanges.withMembers(0x272)), new DeltaSet(214, CharRanges.withMembers(0x275)), new DeltaSet(217, CharRanges.withRanges(0x28a, 0x28c)), new DeltaSet(218, CharRanges.withMembers(0x280, 0x283, 0x288)), new DeltaSet(219, CharRanges.withMembers(0x292)), new DeltaSet(743, CharRanges.withMembers(0x39c)), new DeltaSet(3814, CharRanges.withMembers(0x2c63)), new DeltaSet(7205, CharRanges.withMembers(0x1fbe)), new DeltaSet(7264, CharRanges.withRanges(0x2d00, 0x2d26)), new DeltaSet(10727, CharRanges.withMembers(0x2c64)), new DeltaSet(10743, CharRanges.withMembers(0x2c62)), new DeltaSet(10792, CharRanges.withMembers(0x2c66)), new DeltaSet(10795, CharRanges.withMembers(0x2c65)) ); private static final ImmutableList CANON_DELTA_SETS = ImmutableList.of( new DeltaSet(-10743, CharRanges.withMembers(0x26b)), new DeltaSet(-10727, CharRanges.withMembers(0x27d)), new DeltaSet(-3814, CharRanges.withMembers(0x1d7d)), new DeltaSet(-743, CharRanges.withMembers(0xb5)), new DeltaSet(-195, CharRanges.withMembers(0x180)), new DeltaSet(-163, CharRanges.withMembers(0x19a)), new DeltaSet(-130, CharRanges.withRanges(0x19e, 0x19f, 0x37b, 0x37e)), new DeltaSet(-128, CharRanges.withRanges(0x1f78, 0x1f7a)), new DeltaSet(-126, CharRanges.withRanges(0x1f7c, 0x1f7e)), new DeltaSet(-121, CharRanges.withMembers(0xff)), new DeltaSet(-112, CharRanges.withRanges(0x1f7a, 0x1f7c)), new DeltaSet(-100, CharRanges.withRanges(0x1f76, 0x1f78)), new DeltaSet(-97, CharRanges.withMembers(0x195)), new DeltaSet(-86, CharRanges.withRanges(0x1f72, 0x1f76)), new DeltaSet(-84, CharRanges.withMembers(0x345)), new DeltaSet(-74, CharRanges.withRanges(0x1f70, 0x1f72)), new DeltaSet(-56, CharRanges.withMembers(0x1bf)), new DeltaSet(-8, CharRanges.withRanges( 0x1f00, 0x1f08, 0x1f10, 0x1f16, 0x1f20, 0x1f28, 0x1f30, 0x1f38, 0x1f40, 0x1f46, 0x1f51, 0x1f52, 0x1f53, 0x1f54, 0x1f55, 0x1f56, 0x1f57, 0x1f58, 0x1f60, 0x1f68, 0x1fb0, 0x1fb2, 0x1fd0, 0x1fd2, 0x1fe0, 0x1fe2)), new DeltaSet(-7, CharRanges.withMembers(0x3f2, 0x1fe5)), new DeltaSet(1, CharRanges.withMembers( 0x101, 0x103, 0x105, 0x107, 0x109, 0x10b, 0x10d, 0x10f, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11b, 0x11d, 0x11f, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12b, 0x12d, 0x12f, 0x133, 0x135, 0x137, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14b, 0x14d, 0x14f, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15b, 0x15d, 0x15f, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16b, 0x16d, 0x16f, 0x171, 0x173, 0x175, 0x177, 0x17a, 0x17c, 0x17e, 0x183, 0x185, 0x188, 0x18c, 0x192, 0x199, 0x1a1, 0x1a3, 0x1a5, 0x1a8, 0x1ad, 0x1b0, 0x1b4, 0x1b6, 0x1b9, 0x1bd, 0x1c5, 0x1c8, 0x1cb, 0x1ce, 0x1d0, 0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1df, 0x1e1, 0x1e3, 0x1e5, 0x1e7, 0x1e9, 0x1eb, 0x1ed, 0x1ef, 0x1f2, 0x1f5, 0x1f9, 0x1fb, 0x1fd, 0x1ff, 0x201, 0x203, 0x205, 0x207, 0x209, 0x20b, 0x20d, 0x20f, 0x211, 0x213, 0x215, 0x217, 0x219, 0x21b, 0x21d, 0x21f, 0x223, 0x225, 0x227, 0x229, 0x22b, 0x22d, 0x22f, 0x231, 0x233, 0x23c, 0x242, 0x247, 0x249, 0x24b, 0x24d, 0x24f, 0x3d9, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e5, 0x3e7, 0x3e9, 0x3eb, 0x3ed, 0x3ef, 0x3f8, 0x3fb, 0x461, 0x463, 0x465, 0x467, 0x469, 0x46b, 0x46d, 0x46f, 0x471, 0x473, 0x475, 0x477, 0x479, 0x47b, 0x47d, 0x47f, 0x481, 0x48b, 0x48d, 0x48f, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49b, 0x49d, 0x49f, 0x4a1, 0x4a3, 0x4a5, 0x4a7, 0x4a9, 0x4ab, 0x4ad, 0x4af, 0x4b1, 0x4b3, 0x4b5, 0x4b7, 0x4b9, 0x4bb, 0x4bd, 0x4bf, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4cc, 0x4ce, 0x4d1, 0x4d3, 0x4d5, 0x4d7, 0x4d9, 0x4db, 0x4dd, 0x4df, 0x4e1, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x4fd, 0x4ff, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50b, 0x50d, 0x50f, 0x511, 0x513, 0x1e01, 0x1e03, 0x1e05, 0x1e07, 0x1e09, 0x1e0b, 0x1e0d, 0x1e0f, 0x1e11, 0x1e13, 0x1e15, 0x1e17, 0x1e19, 0x1e1b, 0x1e1d, 0x1e1f, 0x1e21, 0x1e23, 0x1e25, 0x1e27, 0x1e29, 0x1e2b, 0x1e2d, 0x1e2f, 0x1e31, 0x1e33, 0x1e35, 0x1e37, 0x1e39, 0x1e3b, 0x1e3d, 0x1e3f, 0x1e41, 0x1e43, 0x1e45, 0x1e47, 0x1e49, 0x1e4b, 0x1e4d, 0x1e4f, 0x1e51, 0x1e53, 0x1e55, 0x1e57, 0x1e59, 0x1e5b, 0x1e5d, 0x1e5f, 0x1e61, 0x1e63, 0x1e65, 0x1e67, 0x1e69, 0x1e6b, 0x1e6d, 0x1e6f, 0x1e71, 0x1e73, 0x1e75, 0x1e77, 0x1e79, 0x1e7b, 0x1e7d, 0x1e7f, 0x1e81, 0x1e83, 0x1e85, 0x1e87, 0x1e89, 0x1e8b, 0x1e8d, 0x1e8f, 0x1e91, 0x1e93, 0x1e95, 0x1ea1, 0x1ea3, 0x1ea5, 0x1ea7, 0x1ea9, 0x1eab, 0x1ead, 0x1eaf, 0x1eb1, 0x1eb3, 0x1eb5, 0x1eb7, 0x1eb9, 0x1ebb, 0x1ebd, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ec9, 0x1ecb, 0x1ecd, 0x1ecf, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ed9, 0x1edb, 0x1edd, 0x1edf, 0x1ee1, 0x1ee3, 0x1ee5, 0x1ee7, 0x1ee9, 0x1eeb, 0x1eed, 0x1eef, 0x1ef1, 0x1ef3, 0x1ef5, 0x1ef7, 0x1ef9, 0x2184, 0x2c61, 0x2c68, 0x2c6a, 0x2c6c, 0x2c76, 0x2c81, 0x2c83, 0x2c85, 0x2c87, 0x2c89, 0x2c8b, 0x2c8d, 0x2c8f, 0x2c91, 0x2c93, 0x2c95, 0x2c97, 0x2c99, 0x2c9b, 0x2c9d, 0x2c9f, 0x2ca1, 0x2ca3, 0x2ca5, 0x2ca7, 0x2ca9, 0x2cab, 0x2cad, 0x2caf, 0x2cb1, 0x2cb3, 0x2cb5, 0x2cb7, 0x2cb9, 0x2cbb, 0x2cbd, 0x2cbf, 0x2cc1, 0x2cc3, 0x2cc5, 0x2cc7, 0x2cc9, 0x2ccb, 0x2ccd, 0x2ccf, 0x2cd1, 0x2cd3, 0x2cd5, 0x2cd7, 0x2cd9, 0x2cdb, 0x2cdd, 0x2cdf, 0x2ce1, 0x2ce3)), new DeltaSet(2, CharRanges.withMembers(0x1c6, 0x1c9, 0x1cc, 0x1f3)), new DeltaSet(15, CharRanges.withMembers(0x4cf)), new DeltaSet(16, CharRanges.withRanges(0x2170, 0x2180)), new DeltaSet(26, CharRanges.withRanges(0x24d0, 0x24ea)), new DeltaSet(28, CharRanges.withMembers(0x214e)), new DeltaSet(31, CharRanges.withMembers(0x3c2)), new DeltaSet(32, CharRanges.withRanges( 0x61, 0x7b, 0xe0, 0xf7, 0xf8, 0xff, 0x3b1, 0x3c2, 0x3c3, 0x3cc, 0x430, 0x450, 0xff41, 0xff5b)), new DeltaSet(37, CharRanges.withRanges(0x3ad, 0x3b0)), new DeltaSet(38, CharRanges.withMembers(0x3ac)), new DeltaSet(47, CharRanges.withMembers(0x3d5)), new DeltaSet(48, CharRanges.withRanges(0x561, 0x587, 0x2c30, 0x2c5f)), new DeltaSet(54, CharRanges.withMembers(0x3d6)), new DeltaSet(57, CharRanges.withMembers(0x3d1)), new DeltaSet(59, CharRanges.withMembers(0x1e9b)), new DeltaSet(62, CharRanges.withMembers(0x3d0)), new DeltaSet(63, CharRanges.withRanges(0x3cd, 0x3cf)), new DeltaSet(64, CharRanges.withMembers(0x3cc)), new DeltaSet(69, CharRanges.withMembers(0x289)), new DeltaSet(71, CharRanges.withMembers(0x28c)), new DeltaSet(79, CharRanges.withMembers(0x1dd)), new DeltaSet(80, CharRanges.withRanges(0x3f1, 0x3f2, 0x450, 0x460)), new DeltaSet(86, CharRanges.withMembers(0x3f0)), new DeltaSet(96, CharRanges.withMembers(0x3f5)), new DeltaSet(202, CharRanges.withMembers(0x259)), new DeltaSet(203, CharRanges.withMembers(0x25b)), new DeltaSet(205, CharRanges.withRanges(0x256, 0x258, 0x260, 0x261)), new DeltaSet(206, CharRanges.withMembers(0x254)), new DeltaSet(207, CharRanges.withMembers(0x263)), new DeltaSet(209, CharRanges.withMembers(0x268)), new DeltaSet(210, CharRanges.withMembers(0x253)), new DeltaSet(211, CharRanges.withMembers(0x269, 0x26f)), new DeltaSet(213, CharRanges.withMembers(0x272)), new DeltaSet(214, CharRanges.withMembers(0x275)), new DeltaSet(217, CharRanges.withRanges(0x28a, 0x28c)), new DeltaSet(218, CharRanges.withMembers(0x280, 0x283, 0x288)), new DeltaSet(219, CharRanges.withMembers(0x292)), new DeltaSet(7205, CharRanges.withMembers(0x1fbe)), new DeltaSet(7264, CharRanges.withRanges(0x2d00, 0x2d26)), new DeltaSet(10792, CharRanges.withMembers(0x2c66)), new DeltaSet(10795, CharRanges.withMembers(0x2c65)) ); /** * A group of code units such that for all cu in codeUnits, cu is equivalent, * case-insensitively, to cu + delta. */ private static final class DeltaSet { final int delta; final CharRanges codeUnits; DeltaSet(int delta, CharRanges codeUnits) { this.delta = delta; this.codeUnits = codeUnits; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/regex/RegExpTree.java0000644000175000017500000015546212115204405026640 0ustar apoapo/* * Copyright 2011 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.regex; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * An AST for JavaScript regular expressions. * * @author Mike Samuel */ public abstract class RegExpTree { /** * Returns a simpler regular expression that is semantically the same assuming * the given flags. * @param flags Regular expression flags, e.g. {@code "igm"}. */ public abstract RegExpTree simplify(String flags); /** * True if the presence or absence of an {@code "i"} flag would change the * meaning of this regular expression. */ public abstract boolean isCaseSensitive(); /** * True if the regular expression contains an anchor : {@code ^} or {@code $}. */ public abstract boolean containsAnchor(); /** * True if the regular expression contains capturing groups. */ public final boolean hasCapturingGroup() { return numCapturingGroups() != 0; } /** * The number of capturing groups. */ public abstract int numCapturingGroups(); /** * The children of this node. */ public abstract List children(); /** * Appends this regular expression source to the given buffer. */ protected abstract void appendSourceCode(StringBuilder sb); protected abstract void appendDebugInfo(StringBuilder sb); @Override public final String toString() { StringBuilder sb = new StringBuilder(); sb.append('/'); appendSourceCode(sb); // Don't emit a regular expression that looks like a line comment start. if (sb.length() == 1) { sb.append("(?:)"); } sb.append('/'); return sb.toString(); } public final String toDebugString() { StringBuilder sb = new StringBuilder(); appendDebugString(sb); return sb.toString(); } private void appendDebugString(StringBuilder sb) { sb.append('(').append(getClass().getSimpleName()); int len = sb.length(); sb.append(' '); appendDebugInfo(sb); if (sb.length() == len + 1) { sb.setLength(len); } for (RegExpTree child : children()) { sb.append(' '); child.appendDebugString(sb); } sb.append(')'); } @Override public abstract boolean equals(Object o); @Override public abstract int hashCode(); /** * Parses a regular expression to an AST. * * @param pattern The {@code foo} From {@code /foo/i}. * @param flags The {@code i} From {@code /foo/i}. */ public static RegExpTree parseRegExp( final String pattern, final String flags) { /** A recursive descent parser that closes over pattern and flags above. */ class Parser { /** The number of characters in pattern consumed. */ int pos; /** The number of capturing groups seen so far. */ int numCapturingGroups = 0; /** The length of pattern. */ final int limit = pattern.length(); RegExpTree parseTopLevel() { this.pos = 0; RegExpTree out = parse(); if (pos < limit) { // Unmatched closed paren maybe. throw new IllegalArgumentException(pattern.substring(pos)); } return out; } RegExpTree parse() { // Collects ["foo", "bar", "baz"] for /foo|bar|baz/. ImmutableList.Builder alternatives = null; // The last item parsed within an alternation. RegExpTree preceder = null; topLoop: while (pos < limit) { char ch = pattern.charAt(pos); RegExpTree atom; switch (ch) { case '[': atom = parseCharset(); break; case '(': atom = parseParenthetical(); break; case ')': break topLoop; case '\\': atom = parseEscape(); break; case '^': case '$': atom = new Anchor(ch); ++pos; break; case '.': // We represent . as a character set to make it easy to simplify // things like /.|[\r\n]/. atom = DOT_CHARSET; ++pos; break; case '|': // An alternative may be empty as in /foo||bar/. // The '|' is consumed below. atom = Empty.INSTANCE; break; default: // Find a run of concatenated characters to avoid building a // tree node per literal character. int start = pos; int end = pos + 1; charsLoop: while (end < limit) { switch (pattern.charAt(end)) { case '[': case '(': case ')': case '\\': case '^': case '$': case '|': case '.': case '*': case '+': case '?': case '{': break charsLoop; default: // Repetition binds more tightly than concatenation. // Only consume up to "foo" in /foob*/ so that the suffix // operator parser below has the right precedence. if (end + 1 >= limit || !isRepetitionStart(pattern.charAt(end + 1))) { ++end; } else { break charsLoop; } } } atom = new Text(pattern.substring(start, end)); pos = end; break; } if (pos < limit && isRepetitionStart(pattern.charAt(pos))) { atom = parseRepetition(atom); } if (preceder == null) { preceder = atom; } else { preceder = new Concatenation(preceder, atom); } // If this is an alternative in a alternation, then add it to the // list of complete alternatives, and reset the parser state for the // next alternative. if (pos < limit && pattern.charAt(pos) == '|') { if (alternatives == null) { alternatives = ImmutableList.builder(); } alternatives.add(preceder); preceder = null; ++pos; } } // An alternative may have no parsed content blank as in /foo|/. if (preceder == null) { preceder = Empty.INSTANCE; } if (alternatives != null) { alternatives.add(preceder); return new Alternation(alternatives.build()); } else { return preceder; } } /** * Handles capturing groups {@code (...)}, * non-capturing groups {@code (?:...)}, and lookahead assertions * {@code (?=...)}. */ private RegExpTree parseParenthetical() { Preconditions.checkState(pattern.charAt(pos) == '('); int start = pos; ++pos; boolean capturing = true; int type = 0; if (pos < limit && pattern.charAt(pos) == '?') { if (pos + 1 < limit) { capturing = false; char ch = pattern.charAt(pos + 1); switch (ch) { case ':': // A (?:...) style non-capturing group. pos += 2; break; case '!': // A lookahead assertion case '=': pos += 2; type = ch; break; default: throw new IllegalArgumentException( "Malformed parenthetical: " + pattern.substring(start)); } } } RegExpTree body = parse(); if (pos < limit && pattern.charAt(pos) == ')') { ++pos; } else { throw new IllegalArgumentException( "Unclosed parenthetical group: " + pattern.substring(start)); } if (capturing) { ++numCapturingGroups; return new CapturingGroup(body); } else if (type != 0) { return new LookaheadAssertion(body, type == '='); } else { return body; } } /** * Parses a square bracketed character set. * Standalone character groups (@code /\d/} are handled by * {@link #parseEscape}. */ private RegExpTree parseCharset() { Preconditions.checkState(pattern.charAt(pos) == '['); ++pos; boolean isCaseInsensitive = flags.indexOf('i') >= 0; boolean inverse = pos < limit && pattern.charAt(pos) == '^'; if (inverse) { ++pos; } CharRanges ranges = CharRanges.EMPTY; CharRanges ieExplicits = CharRanges.EMPTY; while (pos < limit && pattern.charAt(pos) != ']') { char ch = pattern.charAt(pos); char start; if (ch == '\\') { ++pos; char possibleGroupName = pattern.charAt(pos); CharRanges group = NAMED_CHAR_GROUPS.get(possibleGroupName); if (group != null) { ++pos; ranges = ranges.union(group); continue; } start = parseEscapeChar(); } else { start = ch; ++pos; } char end = start; if (pos + 1 < limit && pattern.charAt(pos) == '-' && pattern.charAt(pos + 1) != ']') { ++pos; ch = pattern.charAt(pos); if (ch == '\\') { ++pos; end = parseEscapeChar(); } else { end = ch; ++pos; } } CharRanges range = CharRanges.inclusive(start, end); ranges = ranges.union(range); if (IE_SPEC_ERRORS.contains(start) && IE_SPEC_ERRORS.contains(end)) { ieExplicits = ieExplicits.union(range.intersection(IE_SPEC_ERRORS)); } if (isCaseInsensitive) { // If the flags contain the 'i' flag, then it is not correct to // say that [^a-z] contains the letter 'A', or that [a-z] does not // contain the letter 'A'. // We expand out letter groups here so that parse returns something // that is valid independent of flags. // Calls to simplify(flags) may later reintroduce flag assumptions. // but without this step, later steps might conflate // /[a-z]/i // and // /[^\0-`{-\uffff]/i // which matches nothing because the information about whether the // ^ is present has been lost during optimizations and charset // unionizing as in /[...]|[^...]/. ranges = CaseCanonicalize.expandToAllMatched(ranges); } } ++pos; // Consume ']' if (inverse) { ranges = CharRanges.ALL_CODE_UNITS.difference(ranges); } return new Charset(ranges, ieExplicits); } /** * Parses an escape to a code point. * Some of the characters parsed here have special meanings in various * contexts, so contexts must filter those instead. * E.g. '\b' means a different thing inside a charset than without. */ private char parseEscapeChar() { char ch = pattern.charAt(pos++); switch (ch) { case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; case 'u': return parseHex(4); case 'v': return '\u000b'; case 'x': return parseHex(2); default: if ('0' <= ch && ch <= '7') { char codeUnit = (char) (ch - '0'); // Allow octal literals in the range \0-\377. // \41 might be a group, but \041 is not a group. // We read, but do not emit octal literals since they // are deprecated in ES5. int octLimit = Math.min( limit, pos + (ch <= '3' ? 2 : 1) + (ch == '0' ? 1 : 0)); while (pos < octLimit) { ch = pattern.charAt(pos); if ('0' <= ch && ch <= '7') { codeUnit = (char) ((codeUnit << 3) + (ch - '0')); ++pos; } else { break; } } return codeUnit; } return ch; } } /** * Parses an escape that appears outside a charset. */ private RegExpTree parseEscape() { Preconditions.checkState(pattern.charAt(pos) == '\\'); ++pos; char ch = pattern.charAt(pos); if (ch == 'b' || ch == 'B') { ++pos; return new WordBoundary(ch); } else if ('1' <= ch && ch <= '9') { ++pos; int possibleGroupIndex = ch - '0'; if (numCapturingGroups >= possibleGroupIndex) { if (pos < limit) { char next = pattern.charAt(pos); if ('0' <= next && next <= '9') { int twoDigitGroupIndex = possibleGroupIndex * 10 + (next - '0'); if (numCapturingGroups >= twoDigitGroupIndex) { ++pos; possibleGroupIndex = twoDigitGroupIndex; } } } return new BackReference(possibleGroupIndex); } else { // \1 - \7 are octal escapes if there is no such group. // \8 and \9 are the literal characters '8' and '9' if there // is no such group. return new Text(Character.toString( possibleGroupIndex <= 7 ? (char) possibleGroupIndex : ch)); } } else { CharRanges charGroup = NAMED_CHAR_GROUPS.get(ch); if (charGroup != null) { // Handle \d, etc. ++pos; return new Charset(charGroup, CharRanges.EMPTY); } return new Text("" + parseEscapeChar()); } } /** * Parses n hex digits to a code-unit. */ private char parseHex(int n) { if (pos + n > limit) { throw new IllegalArgumentException( "Abbreviated hex escape " + pattern.substring(pos)); } int result = 0; while (--n >= 0) { char ch = pattern.charAt(pos); int digit; if ('0' <= ch && ch <= '9') { digit = ch - '0'; } else if ('a' <= ch && ch <= 'f') { digit = ch + (10 - 'a'); } else if ('A' <= ch && ch <= 'F') { digit = ch + (10 - 'A'); } else { throw new IllegalArgumentException(pattern.substring(pos)); } ++pos; result = (result << 4) | digit; } return (char) result; } private boolean isRepetitionStart(char ch) { switch (ch) { case '?': case '*': case '+': case '{': return true; default: return false; } } /** * Parse a repetition. {@code x?} is treated as a repetition -- * an optional production can be matched 0 or 1 time. */ private RegExpTree parseRepetition(RegExpTree body) { if (pos == limit) { return body; } int min, max; switch (pattern.charAt(pos)) { case '+': ++pos; min = 1; max = Integer.MAX_VALUE; break; case '*': ++pos; min = 0; max = Integer.MAX_VALUE; break; case '?': ++pos; min = 0; max = 1; break; case '{': ++pos; int start = pos; int end = pattern.indexOf('}', start); if (end < 0) { pos = start - 1; return body; } String counts = pattern.substring(start, end); pos = end + 1; int comma = counts.indexOf(','); try { min = Integer.parseInt( comma >= 0 ? counts.substring(0, comma) : counts); max = comma >= 0 ? comma + 1 != counts.length() ? Integer.parseInt(counts.substring(comma + 1)) : Integer.MAX_VALUE : min; } catch (NumberFormatException ex) { min = max = -1; } if (min < 0 || min > max) { // Treat the open curly bracket literally. pos = start - 1; return body; } break; default: return body; } boolean greedy = true; if (pos < limit && pattern.charAt(pos) == '?') { greedy = false; ++pos; } return new Repetition(body, min, max, greedy); } } return new Parser().parseTopLevel(); } /** * True if, but not necessarily always when the, given regular expression * must match the whole input or none of it. */ public static boolean matchesWholeInput(RegExpTree t, String flags) { if (flags.indexOf('m') >= 0) { return false; } if (!(t instanceof Concatenation)) { return false; } Concatenation c = (Concatenation) t; if (c.elements.isEmpty()) { return false; } RegExpTree first = c.elements.get(0), last = c.elements.get(c.elements.size() - 1); if (!(first instanceof Anchor && last instanceof Anchor)) { return false; } return ((Anchor) first).type == '^' && ((Anchor) last).type == '$'; } static abstract class RegExpTreeAtom extends RegExpTree { @Override public boolean isCaseSensitive() { return false; } @Override public boolean containsAnchor() { return false; } @Override public final int numCapturingGroups() { return 0; } @Override public final List children() { return ImmutableList.of(); } } static final class Empty extends RegExpTreeAtom { static final Empty INSTANCE = new Empty(); @Override public RegExpTree simplify(String flags) { return this; } @Override protected void appendSourceCode(StringBuilder sb) { // No output } @Override protected void appendDebugInfo(StringBuilder sb) { // No output } @Override public boolean equals(Object o) { return o instanceof Empty; } @Override public int hashCode() { return 0x7ee06141; } } static final class Anchor extends RegExpTreeAtom { final char type; Anchor(char type) { this.type = type; } @Override public RegExpTree simplify(String flags) { return this; } @Override public boolean containsAnchor() { return true; } @Override protected void appendSourceCode(StringBuilder sb) { sb.append(type); } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(type); } @Override public boolean equals(Object o) { return o instanceof Anchor && type == ((Anchor) o).type; } @Override public int hashCode() { return type ^ 0xe85317ff; } } static final class WordBoundary extends RegExpTreeAtom { final char type; WordBoundary(char type) { this.type = type; } @Override public RegExpTree simplify(String flags) { return this; } @Override protected void appendSourceCode(StringBuilder sb) { sb.append('\\').append(type); } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(type); } @Override public boolean equals(Object o) { return o instanceof WordBoundary && type == ((WordBoundary) o).type; } @Override public int hashCode() { return 0x5673aa29 ^ type; } } static final class BackReference extends RegExpTreeAtom { final int groupIndex; BackReference(int groupIndex) { Preconditions.checkArgument(groupIndex >= 0 && groupIndex <= 99); this.groupIndex = groupIndex; } @Override public RegExpTree simplify(String flags) { return this; } @Override protected void appendSourceCode(StringBuilder sb) { sb.append('\\').append(groupIndex); } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(groupIndex); } @Override public boolean equals(Object o) { return o instanceof BackReference && groupIndex == ((BackReference) o).groupIndex; } @Override public int hashCode() { return 0xff072663 ^ groupIndex; } } static final class Text extends RegExpTreeAtom { final String text; Text(String text) { this.text = text; } /** * @param ch The code-unit to escape. * @param next The next code-unit or -1 if indeterminable. */ private static void escapeRegularCharOnto( char ch, int next, StringBuilder sb) { switch (ch) { case '$': case '^': case '*': case '(': case ')': case '+': case '[': case '|': case '.': case '/': case '?': sb.append('\\').append(ch); break; case '{': // If possibly part of a repetition, then escape. // Concatenation is handled by the digitsMightBleed check. if ('0' <= next && next <= '9') { sb.append('\\'); } sb.append(ch); break; default: escapeCharOnto(ch, sb); } } @Override public RegExpTree simplify(String flags) { int n = text.length(); if (n == 0) { return Empty.INSTANCE; } if (flags.indexOf('i') >= 0) { String canonicalized = CaseCanonicalize.caseCanonicalize(text); if (text != canonicalized) { return new Text(canonicalized); } } return this; } @Override public boolean isCaseSensitive() { for (int i = 0, n = text.length(); i < n; ++i) { if (CaseCanonicalize.CASE_SENSITIVE.contains(text.charAt(i))) { return true; } } return false; } @Override protected void appendSourceCode(StringBuilder sb) { for (int i = 0, n = text.length(); i < n; ++i) { escapeRegularCharOnto(text.charAt(i), i + 1 < n ? text.charAt(i + 1) : -1, sb); } } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append('`').append(text).append('`'); } @Override public boolean equals(Object o) { return o instanceof Text && text.equals(((Text) o).text); } @Override public int hashCode() { return text.hashCode() ^ 0x617e310; } } static final class Repetition extends RegExpTree { final RegExpTree body; final int min, max; final boolean greedy; Repetition(RegExpTree body, int min, int max, boolean greedy) { this.body = body; this.min = min; this.max = max; this.greedy = greedy; } @Override public RegExpTree simplify(String flags) { RegExpTree body = this.body.simplify(flags); if (max == 0 && !body.hasCapturingGroup()) { return Empty.INSTANCE; } if (body instanceof Empty || NEVER_MATCHES.equals(body)) { return body; } int min = this.min; int max = this.max; if (body instanceof Repetition) { Repetition rbody = (Repetition) body; if (rbody.greedy == greedy) { long lmin = ((long) min) * rbody.min; long lmax = ((long) max) * rbody.max; if (lmin < Integer.MAX_VALUE) { body = rbody.body; min = (int) lmin; max = lmax >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) lmax; } } } if (min == 1 && max == 1) { return body; } boolean greedy = this.greedy || min == max; return body.equals(this.body) && min == this.min && max == this.max && greedy == this.greedy ? this : new Repetition(body, min, max, greedy).simplify(flags); } @Override public boolean isCaseSensitive() { return body.isCaseSensitive(); } @Override public boolean containsAnchor() { return body.containsAnchor(); } @Override public int numCapturingGroups() { return body.numCapturingGroups(); } @Override public List children() { return ImmutableList.of(body); } private void appendBodySourceCode(StringBuilder sb) { if (body instanceof Alternation || body instanceof Concatenation || body instanceof Repetition || (body instanceof Text && ((Text) body).text.length() > 1)) { sb.append("(?:"); body.appendSourceCode(sb); sb.append(')'); } else { body.appendSourceCode(sb); } } private static int suffixLen(int min, int max) { // This mirrors the branches that renders a suffix in appendSourceCode below. if (max == Integer.MAX_VALUE) { switch (min) { case 0: return 1; // * case 1: return 1; // + default: return 3 + numDecimalDigits(min); // {3,} } } if (min == 0 && max == 1) { return 1; // ? } if (min == max) { if (min == 1) { return 0; // No suffix needed for {1}. } return 2 + numDecimalDigits(min); // {4} } return 3 + numDecimalDigits(min) + numDecimalDigits(max); // {2,7} } private static int numDecimalDigits(int n) { if (n < 0) { // Negative values should not be passed in. throw new AssertionError(); // If changing this code to support negative values, // Integer.MIN_VALUE is a corner-case.. } int nDigits = 1; while (n >= 10) { ++nDigits; n /= 10; } return nDigits; } @Override protected void appendSourceCode(StringBuilder sb) { int bodyStart = sb.length(); appendBodySourceCode(sb); int bodyEnd = sb.length(); int bodyLen = bodyEnd - bodyStart; int min = this.min; int max = this.max; if (min >= 2 && max == Integer.MAX_VALUE || max - min <= 1) { int expanded = // If min == max then we want to try expanding to the limit and // attach the empty suffix, which is equivalent to min = max = 1, // i.e. /a/ vs /a{1}/. min == max // Give aa+ preference over aaa*. || max == Integer.MAX_VALUE ? min - 1 : min; int expandedMin = min - expanded; int expandedMax = max == Integer.MAX_VALUE ? max : max - expanded; int suffixLen = suffixLen(min, max); int expandedSuffixLen = suffixLen(expandedMin, expandedMax); if (bodyLen * expanded + expandedSuffixLen < suffixLen && !body.hasCapturingGroup()) { // a{2} -> aa // a{2,} -> aa+ // a{2,3} -> aaa? while (--expanded >= 0) { sb.append(sb, bodyStart, bodyEnd); } min = expandedMin; max = expandedMax; } } if (max == Integer.MAX_VALUE) { switch (min) { case 0: sb.append('*'); break; case 1: sb.append('+'); break; default: sb.append('{').append(min).append(",}"); } } else if (min == 0 && max == 1) { sb.append('?'); } else if (min == max) { if (min != 1) { sb.append('{').append(min).append('}'); } } else { sb.append('{').append(min).append(',').append(max).append('}'); } if (!greedy) { sb.append('?'); } } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(" min=").append(min).append(", max=").append(max); if (!greedy) { sb.append(" not_greedy"); } } @Override public boolean equals(Object o) { if (!(o instanceof Repetition)) { return false; } Repetition that = (Repetition) o; return this.body.equals(that.body) && this.min == that.min && this.max == that.max && this.greedy == that.greedy; } @Override public int hashCode() { return min + 31 * (max + 31 * ((greedy ? 1 : 0) + 31 * body.hashCode())); } } static final class Alternation extends RegExpTree { final ImmutableList alternatives; Alternation(List alternatives) { this.alternatives = ImmutableList.copyOf(alternatives); } @Override public RegExpTree simplify(String flags) { List alternatives = Lists.newArrayList(); for (RegExpTree alternative : this.alternatives) { alternative = alternative.simplify(flags); if (alternative instanceof Alternation) { alternatives.addAll(((Alternation) alternative).alternatives); } else { alternatives.add(alternative); } } // Remove duplicates RegExpTree last = null; for (Iterator it = alternatives.iterator(); it.hasNext();) { RegExpTree alternative = it.next(); if (alternative.equals(NEVER_MATCHES)) { continue; } if (alternative.equals(last) && !alternative.hasCapturingGroup()) { it.remove(); } else { last = alternative; } } // Collapse character alternatives into character sets. for (int i = 0, n = alternatives.size(); i < n; ++i) { RegExpTree alternative = alternatives.get(i); if ((alternative instanceof Text && ((Text) alternative).text.length() == 1) || alternative instanceof Charset) { int end = i; int nCharsets = 0; while (end < n) { RegExpTree follower = alternatives.get(end); if (follower instanceof Charset) { ++nCharsets; } else if (!(follower instanceof Text && ((Text) follower).text.length() == 1)) { break; } ++end; } if (end - i >= 3 || (nCharsets != 0 && end - i >= 2)) { int[] members = new int[end - i - nCharsets]; int memberIdx = 0; CharRanges chars = CharRanges.EMPTY; CharRanges ieExplicits = CharRanges.EMPTY; List charAlternatives = alternatives.subList(i, end); for (RegExpTree charAlternative : charAlternatives) { if (charAlternative instanceof Text) { char ch = ((Text) charAlternative).text.charAt(0); members[memberIdx++] = ch; if (IE_SPEC_ERRORS.contains(ch)) { ieExplicits = ieExplicits.union(CharRanges.inclusive(ch, ch)); } } else if (charAlternative instanceof Charset) { Charset cs = (Charset) charAlternative; chars = chars.union(cs.ranges); ieExplicits = ieExplicits.union(cs.ieExplicits); } } chars = chars.union(CharRanges.withMembers(members)); charAlternatives.clear(); charAlternatives.add( new Charset(chars, ieExplicits).simplify(flags)); n = alternatives.size(); } } } switch (alternatives.size()) { case 0: return Empty.INSTANCE; case 1: return alternatives.get(0); case 2: if (alternatives.get(1) instanceof Empty) { // (?:a|) -> a? return new Repetition(alternatives.get(0), 0, 1, true); } else if (alternatives.get(0) instanceof Empty) { return new Repetition(alternatives.get(1), 0, 1, false); } break; } // TODO: maybe pull out common prefix or suffix return alternatives.equals(this.alternatives) ? this : new Alternation(alternatives); } @Override public boolean isCaseSensitive() { for (RegExpTree alternative : alternatives) { if (alternative.isCaseSensitive()) { return true; } } return false; } @Override public boolean containsAnchor() { for (RegExpTree alternative : alternatives) { if (alternative.containsAnchor()) { return true; } } return false; } @Override public int numCapturingGroups() { int n = 0; for (RegExpTree alternative : alternatives) { n += alternative.numCapturingGroups(); } return n; } @Override public List children() { return alternatives; } @Override protected void appendSourceCode(StringBuilder sb) { for (int i = 0, n = alternatives.size(); i < n; ++i) { if (i != 0) { sb.append('|'); } alternatives.get(i).appendSourceCode(sb); } } @Override protected void appendDebugInfo(StringBuilder sb) { // Nothing besides children. } @Override public boolean equals(Object o) { return this == o || ( (o instanceof Alternation) && alternatives.equals(((Alternation) o).alternatives)); } @Override public int hashCode() { return 0x51b57cd1 ^ alternatives.hashCode(); } } private static final RegExpTree NEVER_MATCHES = new LookaheadAssertion( Empty.INSTANCE, false); static final class LookaheadAssertion extends RegExpTree { final RegExpTree body; final boolean positive; LookaheadAssertion(RegExpTree body, boolean positive) { this.body = body; this.positive = positive; } @Override public RegExpTree simplify(String flags) { RegExpTree simpleBody = body.simplify(flags); if (simpleBody instanceof Empty) { if (positive) { // Always true return simpleBody; } } return new LookaheadAssertion(simpleBody, positive); } @Override public boolean isCaseSensitive() { return body.isCaseSensitive(); } @Override public boolean containsAnchor() { return body.containsAnchor(); } @Override public int numCapturingGroups() { return body.numCapturingGroups(); } @Override public List children() { return ImmutableList.of(body); } @Override protected void appendSourceCode(StringBuilder sb) { sb.append(positive ? "(?=" : "(?!"); body.appendSourceCode(sb); sb.append(')'); } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(positive ? "positive" : "negative"); } @Override public boolean equals(Object o) { if (!(o instanceof LookaheadAssertion)) { return false; } LookaheadAssertion that = (LookaheadAssertion) o; return this.positive == that.positive && this.body.equals(that.body); } @Override public int hashCode() { return 0x723aba9 ^ body.hashCode(); } } static final class CapturingGroup extends RegExpTree { final RegExpTree body; CapturingGroup(RegExpTree body) { this.body = body; } @Override public RegExpTree simplify(String flags) { return new CapturingGroup(body.simplify(flags)); } @Override public boolean isCaseSensitive() { return body.isCaseSensitive(); } @Override public boolean containsAnchor() { return body.containsAnchor(); } @Override public int numCapturingGroups() { return 1; } @Override public List children() { return ImmutableList.of(body); } @Override protected void appendSourceCode(StringBuilder sb) { sb.append('('); body.appendSourceCode(sb); sb.append(')'); } @Override protected void appendDebugInfo(StringBuilder sb) { // Nothing besides children. } @Override public boolean equals(Object o) { return o instanceof CapturingGroup && body.equals(((CapturingGroup) o).body); } @Override public int hashCode() { return 0x55781738 ^ body.hashCode(); } } private static final CharRanges DIGITS = CharRanges.inclusive('0', '9'); private static final CharRanges UCASE_LETTERS = CharRanges.inclusive('A', 'Z'); private static final CharRanges LCASE_LETTERS = CharRanges.inclusive('a', 'z'); private static final CharRanges LETTERS = UCASE_LETTERS.union(LCASE_LETTERS); private static final CharRanges WORD_CHARS = DIGITS .union(LETTERS).union(CharRanges.withMembers('_')); private static final CharRanges INVERSE_WORD_CHARS = CharRanges.ALL_CODE_UNITS.difference(WORD_CHARS); private static final CharRanges SPACE_CHARS = CharRanges.withMembers( '\t', '\n', '\u000b', '\u000c', '\r', ' ', '\u00a0', // Unicode 3.0 Zs '\u1680', '\u180e', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009', '\u200a', // Line terminator chars '\u2028', '\u2029', // Unicode 3.0 Zs '\u202f', '\u205f', '\u3000', // Byte order marker is a space character in ES5 but not ES3. '\ufeff' ); /** IE is broken around \s. IE (6, 7, 8 at least), only recognize these. */ private static final CharRanges IE_SPACE_CHARS = CharRanges.withMembers( '\t', '\n', '\u000b', '\u000c', '\r', ' ' ); /** IE is broken around \s. IE (6, 7, 8 at least), only recognize these. */ private static final CharRanges IE_SPEC_ERRORS = SPACE_CHARS.difference( IE_SPACE_CHARS); private static final ImmutableMap NAMED_CHAR_GROUPS = ImmutableMap.builder() .put('d', DIGITS) .put('D', CharRanges.ALL_CODE_UNITS.difference(DIGITS)) .put('s', SPACE_CHARS) .put('S', CharRanges.ALL_CODE_UNITS.difference(SPACE_CHARS)) .put('w', WORD_CHARS) .put('W', INVERSE_WORD_CHARS) .build(); private static final Charset DOT_CHARSET = new Charset( CharRanges.ALL_CODE_UNITS.difference( CharRanges.withMembers('\n', '\r', '\u2028', '\u2029')), CharRanges.EMPTY); static final class Charset extends RegExpTreeAtom { final CharRanges ranges; /** * Code units that were mentioned explicitly and that might be matched by * a group according to ECMAScript 5 but would not because of specification * violations in IE. */ final CharRanges ieExplicits; Charset(CharRanges ranges, CharRanges ieExplicits) { this.ranges = ranges; this.ieExplicits = ieExplicits; } private static int complexityWordFolded(CharRanges ranges) { return Math.min( complexityWordFoldedHelper(ranges), 1 + complexityWordFoldedHelper( CharRanges.ALL_CODE_UNITS.difference(ranges))); } private static int complexityWordFoldedHelper(CharRanges ranges) { int complexity = DecomposedCharset.complexity(ranges); if (ranges.containsAll(WORD_CHARS)) { complexity = Math.min( complexity, 1 + DecomposedCharset.complexity(ranges.difference(WORD_CHARS))); } if (ranges.containsAll(INVERSE_WORD_CHARS)) { complexity = Math.min( complexity, 1 + DecomposedCharset.complexity( ranges.difference(INVERSE_WORD_CHARS))); } return complexity; } @Override public RegExpTree simplify(String flags) { if (ranges.isEmpty()) { return NEVER_MATCHES; } CharRanges best = ranges; if (flags.indexOf('i') >= 0) { Set options = Sets.newLinkedHashSet(); options.add(CaseCanonicalize.expandToAllMatched(ranges)); options.add(CaseCanonicalize.reduceToMinimum(ranges)); CharRanges lcaseLetters = ranges.intersection(LCASE_LETTERS); CharRanges ucaseLetters = ranges.intersection(UCASE_LETTERS); CharRanges lcaseLettersToUpper = lcaseLetters.shift(-32); CharRanges ucaseLettersToLower = ucaseLetters.shift(32); options.add(ranges.union(ucaseLettersToLower)); options.add(ranges.union(lcaseLettersToUpper)); options.add(ranges.union(lcaseLettersToUpper) .union(ucaseLettersToLower)); options.add(ranges.union(ucaseLettersToLower).difference(ucaseLetters)); options.add(ranges.union(lcaseLettersToUpper).difference(lcaseLetters)); int bestComplexity = complexityWordFolded(ranges); for (CharRanges option : options) { int complexity = complexityWordFolded(option); if (complexity < bestComplexity) { bestComplexity = complexity; best = option; } } } if (best.getNumRanges() == 1 && best.end(0) - best.start(0) == 1) { return new Text(Character.toString((char) best.start(0))); } if (!best.equals(ranges)) { return new Charset(best, ieExplicits); } return this; } @Override public boolean isCaseSensitive() { // We could test // !ranges.equals(CaseCanonicalize.expandToAllMatched(ranges)) // but we get better optimizations by leaving the 'i' flag on in most // cases. // Check whether skipping all the character groups that are known // case-insensitive leaves us with something that matches the above // definition. CharRanges withoutNamedGroups = decompose().ranges; return !withoutNamedGroups.equals( CaseCanonicalize.expandToAllMatched(withoutNamedGroups)); } private DecomposedCharset decompose(CharRanges ranges, boolean inverted) { StringBuilder namedGroups = new StringBuilder(); CharRanges rangesInterIeExplicits = ranges.intersection(ieExplicits); while (true) { char groupName = 0; CharRanges simplest = null; int minComplexity = DecomposedCharset.complexity(ranges); for (Map.Entry namedGroup : NAMED_CHAR_GROUPS.entrySet()) { CharRanges group = namedGroup.getValue(); if (ranges.containsAll(group)) { CharRanges withoutGroup = ranges.difference(group).union( rangesInterIeExplicits); int complexity = DecomposedCharset.complexity(withoutGroup); if (complexity < minComplexity) { simplest = withoutGroup; groupName = namedGroup.getKey().charValue(); minComplexity = complexity; } } } if (simplest != null) { namedGroups.append('\\').append(groupName); ranges = simplest; } else { break; } } return new DecomposedCharset(inverted, ranges, namedGroups.toString()); } @Override protected void appendSourceCode(StringBuilder sb) { if (DOT_CHARSET.ranges.equals(ranges)) { sb.append('.'); return; } decompose().appendSourceCode(sb); } DecomposedCharset decompose() { CharRanges negRanges = CharRanges.ALL_CODE_UNITS.difference(ranges); if (!ieExplicits.isEmpty()) { if (negRanges.intersection(ieExplicits).isEmpty()) { return decompose(ranges, false); } else if (ranges.intersection(ieExplicits).isEmpty()) { return decompose(negRanges, true); } } DecomposedCharset positive = decompose(ranges, false); DecomposedCharset negative = decompose(negRanges, true); return positive.complexity() <= negative.complexity() ? positive : negative; } @Override protected void appendDebugInfo(StringBuilder sb) { sb.append(ranges); } @Override public boolean equals(Object o) { return o instanceof Charset && ranges.equals(((Charset) o).ranges); } @Override public int hashCode() { return ranges.hashCode() ^ 0xdede2246; } } static final class DecomposedCharset { boolean inverted; final CharRanges ranges; final String namedGroups; DecomposedCharset( boolean inverted, CharRanges ranges, String namedGroups) { this.inverted = inverted; this.ranges = ranges; this.namedGroups = namedGroups; } int complexity() { return (inverted ? 1 : 0) + namedGroups.length() + complexity(ranges); } void appendSourceCode(StringBuilder sb) { if (ranges.isEmpty()) { if (!inverted && namedGroups.length() == 2) { sb.append(namedGroups); return; } else if (ranges.isEmpty() && namedGroups.length() == 0) { sb.append(inverted ? "[\\S\\s]" : "(?!)"); return; } } sb.append('['); if (inverted) { sb.append('^'); } sb.append(namedGroups); boolean rangesStartCharset = !inverted && namedGroups.length() == 0; boolean emitDashAtEnd = false; for (int i = 0, n = ranges.getNumRanges(); i < n; ++i) { char start = (char) ranges.start(i); char end = (char) (ranges.end(i) - 1); switch (end - start) { case 0: if (start == '-') { // Put it at the end where it doesn't need escaping. emitDashAtEnd = true; } else { escapeRangeCharOnto( start, rangesStartCharset, i == 0, i + 1 == n, sb); } break; case 1: escapeRangeCharOnto(start, rangesStartCharset, i == 0, false, sb); escapeRangeCharOnto( end, rangesStartCharset, false, i + 1 == n, sb); break; default: escapeRangeCharOnto(start, rangesStartCharset, i == 0, false, sb); sb.append('-'); escapeRangeCharOnto(end, rangesStartCharset, false, true, sb); break; } } if (emitDashAtEnd) { sb.append('-'); } sb.append(']'); } static void escapeRangeCharOnto( char ch, boolean startIsFlush, boolean atStart, boolean atEnd, StringBuilder sb) { switch (ch) { case '\b': sb.append("\\b"); break; case '^': sb.append(atStart && startIsFlush ? "\\^" : "^"); break; case '-': sb.append(atStart || atEnd ? "-" : "\\-"); break; case '\\': case ']': sb.append('\\').append(ch); break; default: escapeCharOnto(ch, sb); } } static int complexity(CharRanges ranges) { int complexity = 0; for (int i = 0, n = ranges.getNumRanges(); i < n; ++i) { int start = ranges.start(i); int end = ranges.end(i) - 1; if (start < 0x20 || start >= 0x7f) { complexity += start >= 0x100 ? 6 : 4; } else { ++complexity; } switch (end - start) { case 0: continue; case 1: break; default: complexity += 1; } if (end < 0x20 || end >= 0x7f) { complexity += end >= 0x100 ? 6 : 4; } else { ++complexity; } } return complexity; } @Override public boolean equals(Object o) { if (!(o instanceof DecomposedCharset)) { return false; } DecomposedCharset that = (DecomposedCharset) o; return this.inverted = that.inverted && this.ranges.equals(that.ranges) && this.namedGroups.equals(that.namedGroups); } @Override public int hashCode() { return ranges.hashCode() + 31 * (namedGroups.hashCode() + (inverted ? 1 : 0)); } } static final class Concatenation extends RegExpTree { final ImmutableList elements; Concatenation(RegExpTree a, RegExpTree b) { elements = ImmutableList.of(a, b); } Concatenation(List elements) { this.elements = ImmutableList.copyOf(elements); } @Override public RegExpTree simplify(final String flags) { class Simplifier { final List simplified = Lists.newArrayList(); void simplify(RegExpTree t) { if (t instanceof Concatenation) { for (RegExpTree child : ((Concatenation) t).elements) { simplify(child); } } else if (t instanceof Empty) { // Do nothing } else { int lastIndex = simplified.size() - 1; if (lastIndex >= 0) { RegExpTree pairwise = simplifyPairwise( simplified.get(lastIndex), t); if (pairwise != null) { simplified.set(lastIndex, pairwise); return; } } simplified.add(t); } } RegExpTree simplifyPairwise(RegExpTree before, RegExpTree after) { if (before instanceof Text && after instanceof Text) { return new Text( ((Text) before).text + ((Text) after).text).simplify(flags); } // Fold adjacent repetitions. int beforeMin = 1, beforeMax = 1; RegExpTree beforeBody = before; boolean beforeGreedy = false; if (before instanceof Repetition) { Repetition r = (Repetition) before; beforeMin = r.min; beforeMax = r.max; beforeBody = r.body; beforeGreedy = r.greedy; } int afterMin = 1, afterMax = 1; RegExpTree afterBody = after; boolean afterGreedy = false; if (after instanceof Repetition) { Repetition r = (Repetition) after; afterMin = r.min; afterMax = r.max; afterBody = r.body; afterGreedy = r.greedy; } if (beforeBody.equals(afterBody) && !beforeBody.hasCapturingGroup()) { long lmin = ((long) beforeMin) + afterMin; long lmax = ((long) beforeMax) + afterMax; if (lmin < Integer.MAX_VALUE) { int min = (int) lmin; int max = lmax >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) lmax; return new Repetition( beforeBody, min, max, beforeGreedy || afterGreedy || min == max); } } return null; } } Simplifier s = new Simplifier(); for (RegExpTree element : elements) { s.simplify(element.simplify(flags)); } switch (s.simplified.size()) { case 0: return Empty.INSTANCE; case 1: return s.simplified.get(0); default: return new Concatenation(s.simplified); } } @Override public boolean isCaseSensitive() { for (RegExpTree element : elements) { if (element.isCaseSensitive()) { return true; } } return false; } @Override public boolean containsAnchor() { for (RegExpTree element : elements) { if (element.containsAnchor()) { return true; } } return false; } @Override public int numCapturingGroups() { int n = 0; for (RegExpTree element : elements) { n += element.numCapturingGroups(); } return n; } @Override public List children() { return elements; } @Override protected void appendSourceCode(StringBuilder sb) { // True if the last content written might consume // decimal digits written subsequently. boolean digitsMightBleed = false; for (RegExpTree element : elements) { boolean parenthesize = false; if (element instanceof Alternation || element instanceof Concatenation) { parenthesize = true; } if (parenthesize) { sb.append("(?:"); element.appendSourceCode(sb); sb.append(')'); } else { int start = sb.length(); element.appendSourceCode(sb); if (digitsMightBleed && sb.length() > start) { char firstChar = sb.charAt(start); if ('0' <= firstChar && firstChar <= '9') { // Bleeding happened. // If the last character would be ambiguous // with a repetition, escape it. if (sb.charAt(start - 1) == '{') { // Concatenation from optimization of // /{(?:0,}/ -> /\{0,}/ sb.insert(start - 1, '\\'); } else { // Or parenthesize otherwise. // Concatenation from optimization of // /(.)\1(?:0)/ -> /(.)\1(?:0)/. sb.insert(start, "(?:").append(')'); } } } } digitsMightBleed = ( // \1(?:0) bleeds if there are 10 or more // capturing groups preceding. (element instanceof BackReference && ((BackReference) element).groupIndex < 10) // foo{(?:10}) bleeds. || (element instanceof Text && ((Text) element).text.endsWith("{"))); } } @Override protected void appendDebugInfo(StringBuilder sb) { // Nothing besides children. } @Override public boolean equals(Object o) { return o instanceof Concatenation && elements.equals(((Concatenation) o).elements); } @Override public int hashCode() { return 0x20997e3e ^ elements.hashCode(); } } static void escapeCharOnto(char ch, StringBuilder sb) { switch (ch) { case '\u0000': sb.append("\\0"); break; case '\f': sb.append("\\f"); break; case '\t': sb.append("\\t"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\\': sb.append("\\\\"); break; default: if (ch < 0x20 || ch >= 0x7f) { if (ch >= 0x100) { sb.append("\\u"); sb.append("0123456789abcdef".charAt((ch >> 12) & 0xf)); sb.append("0123456789abcdef".charAt((ch >> 8) & 0xf)); sb.append("0123456789abcdef".charAt((ch >> 4) & 0xf)); sb.append("0123456789abcdef".charAt((ch) & 0xf)); } else { sb.append("\\x"); sb.append("0123456789abcdef".charAt((ch >> 4) & 0xf)); sb.append("0123456789abcdef".charAt((ch) & 0xf)); } } else { sb.append(ch); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JSError.java0000644000175000017500000002044312115204405025030 0ustar apoapo/* * Copyright 2004 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.rhino.Node; import javax.annotation.Nullable; /** * Compile error description * */ public class JSError { /** A type of the error */ private final DiagnosticType type; /** Description of the error */ public final String description; /** Name of the source */ public final String sourceName; /** Node where the warning occurred. */ final Node node; /** Line number of the source */ public final int lineNumber; /** @deprecated Use #getDefaultLevel */ @Deprecated public final CheckLevel level; private final CheckLevel defaultLevel; // character number private final int charno; // // JSError.make - static factory methods for creating JSError objects // // The general form of the arguments is // // [source location] [level] DiagnosticType [argument ...] // // This order echos a typical command line diagnostic. Source location // arguments are arranged to be sources of information in the order // file-line-column. // // If the level is not given, it is taken from the level of the // DiagnosticType. /** * Creates a JSError with no source information * * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(DiagnosticType type, String... arguments) { return new JSError(null, null, -1, -1, type, null, arguments); } /** * Creates a JSError at a given source location * * @param sourceName The source file name * @param lineno Line number with source file, or -1 if unknown * @param charno Column number within line, or -1 for whole line. * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, int lineno, int charno, DiagnosticType type, String... arguments) { return new JSError(sourceName, null, lineno, charno, type, null, arguments); } /** * Creates a JSError at a given source location * * @param sourceName The source file name * @param lineno Line number with source file, or -1 if unknown * @param charno Column number within line, or -1 for whole line. * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, int lineno, int charno, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError( sourceName, null, lineno, charno, type, level, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, DiagnosticType type, String... arguments) { return new JSError(sourceName, n, type, arguments); } /** * Creates a JSError from a file and Node position. * * @param n Determines the line and char position and source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(Node n, DiagnosticType type, String... arguments) { return new JSError(n.getSourceFileName(), n, type, arguments); } /** * Creates a JSError from a file and Node position. * * @param sourceName The source file name * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public static JSError make(String sourceName, Node n, CheckLevel level, DiagnosticType type, String... arguments) { return new JSError(sourceName, n, n.getLineno(), n.getCharno(), type, level, arguments); } // // JSError constructors // /** * Creates a JSError at a CheckLevel for a source file location. * Private to avoid any entanglement with code outside of the compiler. */ private JSError( String sourceName, @Nullable Node node, int lineno, int charno, DiagnosticType type, CheckLevel level, String... arguments) { this.type = type; this.node = node; this.description = type.format.format(arguments); this.lineNumber = lineno; this.charno = charno; this.sourceName = sourceName; this.defaultLevel = level == null ? type.level : level; this.level = level == null ? type.level : level; } /** * Creates a JSError for a source file location. Private to avoid * any entanglement with code outside of the compiler. */ private JSError(String sourceName, @Nullable Node node, DiagnosticType type, String... arguments) { this(sourceName, node, (node != null) ? node.getLineno() : -1, (node != null) ? node.getCharno() : -1, type, null, arguments); } public DiagnosticType getType() { return type; } /** * Format a message at the given level. * * @return the formatted message or {@code null} */ public String format(CheckLevel level, MessageFormatter formatter) { switch (level) { case ERROR: return formatter.formatError(this); case WARNING: return formatter.formatWarning(this); default: return null; } } @Override public String toString() { // TODO(user): remove custom toString. return type.key + ". " + description + " at " + (sourceName != null && sourceName.length() > 0 ? sourceName : "(unknown source)") + " line " + (lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") + " : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)"); } /** * Get the character number. */ public int getCharno() { return charno; } /** * Get the line number. One-based. */ public int getLineNumber() { return lineNumber; } /** * @return the offset of the region the Error applies to, or -1 if the offset * is unknown. */ public int getNodeSourceOffset() { return node != null ? node.getSourceOffset() : -1; } /** * @return the length of the region the Error applies to, or 0 if the length * is unknown. */ public int getNodeLength() { return node != null ? node.getLength() : 0; } /** The default level, before any of the WarningsGuards are applied. */ public CheckLevel getDefaultLevel() { return defaultLevel; } @Override public boolean equals(Object o) { // Generated by Intellij IDEA if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JSError jsError = (JSError) o; if (charno != jsError.charno) { return false; } if (lineNumber != jsError.lineNumber) { return false; } if (!description.equals(jsError.description)) { return false; } if (defaultLevel != jsError.defaultLevel) { return false; } if (sourceName != null ? !sourceName.equals(jsError.sourceName) : jsError.sourceName != null) { return false; } if (!type.equals(jsError.type)) { return false; } return true; } @Override public int hashCode() { // Generated by Intellij IDEA int result = type.hashCode(); result = 31 * result + description.hashCode(); result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0); result = 31 * result + lineNumber; result = 31 * result + defaultLevel.hashCode(); result = 31 * result + charno; return result; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MakeDeclaredNamesUnique.java0000644000175000017500000004535612115204405030170 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Find all Functions, VARs, and Exception names and make them * unique. Specifically, it will not modify object properties. * @author johnlenz@google.com (John Lenz) * TODO(johnlenz): Try to merge this with the ScopeCreator. */ class MakeDeclaredNamesUnique implements NodeTraversal.ScopedCallback { // Arguments is special cased to handle cases where a local name shadows // the arguments declaration. public static final String ARGUMENTS = "arguments"; // The name stack is similar to how we model scopes but handles some // additional cases that are not handled by the current Scope object. // Specifically, a Scope currently has only two concepts of scope (global, // and function local). But there are in reality a couple of additional // case to worry about: // catch expressions // function expressions names // Both belong to a scope by themselves. private Deque nameStack = new ArrayDeque(); private final Renamer rootRenamer; MakeDeclaredNamesUnique() { this(new ContextualRenamer()); } MakeDeclaredNamesUnique(Renamer renamer) { this.rootRenamer = renamer; } static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) { return new ContextualRenameInverter(compiler); } @Override public void enterScope(NodeTraversal t) { Node declarationRoot = t.getScopeRoot(); Renamer renamer; if (nameStack.isEmpty()) { // If the contextual renamer is being used, the starting context can not // be a function. Preconditions.checkState( !declarationRoot.isFunction() || !(rootRenamer instanceof ContextualRenamer)); Preconditions.checkState(t.inGlobalScope()); renamer = rootRenamer; } else { renamer = nameStack.peek().forChildScope(); } if (!declarationRoot.isFunction()) { // Add the block declarations findDeclaredNames(declarationRoot, null, renamer); } nameStack.push(renamer); } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { nameStack.pop(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: { // Add recursive function name, if needed. // NOTE: "enterScope" is called after we need to pick up this name. Renamer renamer = nameStack.peek().forChildScope(); // If needed, add the function recursive name. String name = n.getFirstChild().getString(); if (name != null && !name.isEmpty() && parent != null && !NodeUtil.isFunctionDeclaration(n)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.PARAM_LIST: { Renamer renamer = nameStack.peek().forChildScope(); // Add the function parameters for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = n.getNext(); findDeclaredNames(functionBody, null, renamer); nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the Javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove the function body scope nameStack.pop(); // Remove function recursive name (if any). nameStack.pop(); break; case Token.PARAM_LIST: // Note: The parameters and function body variables live in the // same scope, we introduce the scope when in the "shouldTraverse" // visit of LP, but remove it when when we exit the function above. break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || !parent.isFunction() || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); } else if (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque> referenceStack = new ArrayDeque>(); // Name are globally unique initially, so we don't need a per-scope map. private Map> nameMap = Maps.newHashMap(); private ContextualRenameInverter(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, js, this); } public static String getOrginalName(String name) { int index = indexOfSeparator(name); return (index == -1) ? name : name.substring(0, index); } private static int indexOfSeparator(String name) { return name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR); } private boolean containsSeparator(String name) { return name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1; } /** * Prepare a set for the new scope. */ @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } referenceStack.push(referencedNames); referencedNames = Sets.newHashSet(); } /** * Rename vars for the current scope, and merge any referenced * names into the parent scope reference set. */ @Override public void exitScope(NodeTraversal t) { if (t.inGlobalScope()) { return; } for (Iterator it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); handleScopeVar(v); } // Merge any names that were referenced but not declared in the current // scope. Set current = referencedNames; referencedNames = referenceStack.pop(); // If there isn't anything left in the stack we will be going into the // global scope: don't try to build a set of referenced names for the // global scope. if (!referenceStack.isEmpty()) { referencedNames.addAll(current); } } /** * For the Var declared in the current scope determine if it is possible * to revert the name to its original form without conflicting with other * values. */ void handleScopeVar(Var v) { String name = v.getName(); if (containsSeparator(name) && !getOrginalName(name).isEmpty()) { String newName = findReplacementName(name); referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.isName()); n.setString(newName); } compiler.reportCodeChange(); nameMap.remove(name); } } /** * Find a name usable in the local scope. */ private String findReplacementName(String name) { String original = getOrginalName(name); String newName = original; int i = 0; while (!isValidName(newName)) { newName = original + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); } return newName; } /** * @return Whether the name is valid to use in the local scope. */ private boolean isValidName(String name) { if (TokenStream.isJSIdentifier(name) && !referencedNames.contains(name) && !name.equals(ARGUMENTS)) { return true; } return false; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (t.inGlobalScope()) { return; } if (NodeUtil.isReferenceName(node)) { String name = node.getString(); // Add all referenced names to the set so it is possible to check for // conflicts. referencedNames.add(name); // Store only references to candidate names in the node map. if (containsSeparator(name)) { addCandidateNameReference(name, node); } } } private void addCandidateNameReference(String name, Node n) { List nodes = nameMap.get(name); if (null == nodes) { nodes = Lists.newLinkedList(); nameMap.put(name, nodes); } nodes.add(n); } } /** * Rename every locally name to be unique, the first encountered declaration * (specifically global names) are left in their original form. Those that are * renamed are made unique by giving them a unique suffix based on * the number of declarations of the name. * * The root ContextualRenamer is assumed to be in GlobalScope. * * Used by the Normalize pass. * @see Normalize */ static class ContextualRenamer implements Renamer { private final Multiset nameUsage; private final Map declarations = Maps.newHashMap(); private final boolean global; final static String UNIQUE_ID_SEPARATOR = "$$"; ContextualRenamer() { this.global = true; nameUsage = HashMultiset.create(); } /** * Constructor for child scopes. */ private ContextualRenamer(Multiset nameUsage) { this.global = false; this.nameUsage = nameUsage; } /** * Create a ContextualRenamer */ @Override public Renamer forChildScope() { return new ContextualRenamer(nameUsage); } /** * Adds a name to the map of names declared in this scope. */ @Override public void addDeclaredName(String name) { if (!name.equals(ARGUMENTS)) { if (global) { reserveName(name); } else { // It hasn't been declared locally yet, so increment the count. if (!declarations.containsKey(name)) { int id = incrementNameCount(name); String newName = null; if (id != 0) { newName = getUniqueName(name, id); } declarations.put(name, newName); } } } } @Override public String getReplacementName(String oldName) { return declarations.get(oldName); } /** * Given a name and the associated id, create a new unique name. */ private String getUniqueName(String name, int id) { return name + UNIQUE_ID_SEPARATOR + id; } private void reserveName(String name) { nameUsage.setCount(name, 0, 1); } private int incrementNameCount(String name) { return nameUsage.add(name, 1); } @Override public boolean stripConstIfReplaced() { return false; } } /** * Rename every declared name to be unique. Typically this would be used * when injecting code to insure that names do not conflict with existing * names. * * Used by the FunctionInjector * @see FunctionInjector */ static class InlineRenamer implements Renamer { private final Map declarations = Maps.newHashMap(); private final Supplier uniqueIdSupplier; private final String idPrefix; private final boolean removeConstness; private final CodingConvention convention; InlineRenamer( CodingConvention convention, Supplier uniqueIdSupplier, String idPrefix, boolean removeConstness) { this.convention = convention; this.uniqueIdSupplier = uniqueIdSupplier; // To ensure that the id does not conflict with the id from the // ContextualRenamer some prefix is needed. Preconditions.checkArgument(!idPrefix.isEmpty()); this.idPrefix = idPrefix; this.removeConstness = removeConstness; } @Override public void addDeclaredName(String name) { Preconditions.checkState(!name.equals(ARGUMENTS)); if (!declarations.containsKey(name)) { declarations.put(name, getUniqueName(name)); } } private String getUniqueName(String name) { if (name.isEmpty()) { return name; } if (name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1) { name = name.substring( 0, name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR)); } if (convention.isExported(name)) { // The google internal coding convention includes a naming convention // to export names starting with "_". Simply strip "_" those to avoid // exporting names. name = "JSCompiler_" + name; } // By using the same separator the id will be stripped if it isn't // needed when variable renaming is turned off. return name + ContextualRenamer.UNIQUE_ID_SEPARATOR + idPrefix + uniqueIdSupplier.get(); } @Override public String getReplacementName(String oldName) { return declarations.get(oldName); } @Override public Renamer forChildScope() { return new InlineRenamer( convention, uniqueIdSupplier, idPrefix, removeConstness); } @Override public boolean stripConstIfReplaced() { return removeConstness; } } /** * For injecting boilerplate libraries. Leaves global names alone * and renames local names like InlineRenamer. */ static class BoilerplateRenamer extends ContextualRenamer { private final Supplier uniqueIdSupplier; private final String idPrefix; private final CodingConvention convention; BoilerplateRenamer( CodingConvention convention, Supplier uniqueIdSupplier, String idPrefix) { this.convention = convention; this.uniqueIdSupplier = uniqueIdSupplier; this.idPrefix = idPrefix; } @Override public Renamer forChildScope() { return new InlineRenamer(convention, uniqueIdSupplier, idPrefix, false); } } /** Only rename things that match the whitelist. Wraps another renamer. */ static class WhitelistedRenamer implements Renamer { private Renamer delegate; private Set whitelist; WhitelistedRenamer(Renamer delegate, Set whitelist) { this.delegate = delegate; this.whitelist = whitelist; } @Override public void addDeclaredName(String name) { if (whitelist.contains(name)) { delegate.addDeclaredName(name); } } @Override public String getReplacementName(String oldName) { return whitelist.contains(oldName) ? delegate.getReplacementName(oldName) : null; } @Override public boolean stripConstIfReplaced() { return delegate.stripConstIfReplaced(); } @Override public Renamer forChildScope() { return new WhitelistedRenamer(delegate.forChildScope(), whitelist); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GenerateExports.java0000644000175000017500000001304512115204405026621 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.FindExportableNodes.GenerateNodeContext; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Map; /** * Generates goog.exportSymbol/goog.exportProperty for the @export annotation. * */ class GenerateExports implements CompilerPass { private static final String PROTOTYPE_PROPERTY = "prototype"; private final AbstractCompiler compiler; private final String exportSymbolFunction; private final String exportPropertyFunction; /** * Creates a new generate exports compiler pass. * @param compiler JS compiler. * @param exportSymbolFunction function used for exporting symbols. * @param exportPropertyFunction function used for exporting property names. */ GenerateExports(AbstractCompiler compiler, String exportSymbolFunction, String exportPropertyFunction) { Preconditions.checkNotNull(compiler); Preconditions.checkNotNull(exportSymbolFunction); Preconditions.checkNotNull(exportPropertyFunction); this.compiler = compiler; this.exportSymbolFunction = exportSymbolFunction; this.exportPropertyFunction = exportPropertyFunction; } @Override public void process(Node externs, Node root) { FindExportableNodes findExportableNodes = new FindExportableNodes(compiler); NodeTraversal.traverse(compiler, root, findExportableNodes); Map exports = findExportableNodes .getExports(); CodingConvention convention = compiler.getCodingConvention(); for (Map.Entry entry : exports.entrySet()) { String export = entry.getKey(); GenerateNodeContext context = entry.getValue(); // Emit the proper CALL expression. // This is an optimization to avoid exporting everything as a symbol // because exporting a property is significantly simpler/faster. // Only export the property if the parent is being exported or // if the parent is "prototype" and the grandparent is being exported. String parent = null; String grandparent = null; Node node = context.getNode().getFirstChild(); if (node.isGetProp()) { parent = node.getFirstChild().getQualifiedName(); if (node.getFirstChild().isGetProp() && getPropertyName(node.getFirstChild()).equals(PROTOTYPE_PROPERTY)) { grandparent = node.getFirstChild().getFirstChild().getQualifiedName(); } } boolean useExportSymbol = true; if (grandparent != null && exports.containsKey(grandparent)) { useExportSymbol = false; } else if (parent != null && exports.containsKey(parent)) { useExportSymbol = false; } Node call; if (useExportSymbol) { // exportSymbol(publicPath, object); call = IR.call( NodeUtil.newQualifiedNameNode( convention, exportSymbolFunction, context.getNode(), export), IR.string(export), NodeUtil.newQualifiedNameNode( convention, export, context.getNode(), export)); } else { // exportProperty(object, publicName, symbol); String property = getPropertyName(node); call = IR.call( NodeUtil.newQualifiedNameNode( convention, exportPropertyFunction, context.getNode(), exportPropertyFunction), NodeUtil.newQualifiedNameNode( convention, parent, context.getNode(), exportPropertyFunction), IR.string(property), NodeUtil.newQualifiedNameNode( convention, export, context.getNode(), exportPropertyFunction)); } Node expression = IR.exprResult(call); annotate(expression); // It's important that any class-building calls (goog.inherits) // come right after the class definition, so move the export after that. Node insertionPoint = context.getContextNode().getNext(); while (insertionPoint != null && NodeUtil.isExprCall(insertionPoint) && convention.getClassesDefinedByCall( insertionPoint.getFirstChild()) != null) { insertionPoint = insertionPoint.getNext(); } if (insertionPoint == null) { context.getScriptNode().addChildToBack(expression); } else { context.getScriptNode().addChildBefore(expression, insertionPoint); } compiler.reportCodeChange(); } } private void annotate(Node node) { NodeTraversal.traverse( compiler, node, new PrepareAst.PrepareAnnotations()); } /** * Assumes the node type is correct and returns the property name * (not fully qualified). * @param node node * @return property name. */ private String getPropertyName(Node node) { Preconditions.checkArgument(node.isGetProp()); return node.getLastChild().getString(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameAnalyzer.java0000644000175000017500000017056612115204405026104 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.GatherSideEffectSubexpressionsCallback.GetReplacementSideEffectSubexpressions; import com.google.javascript.jscomp.GatherSideEffectSubexpressionsCallback.SideEffectAccumulator; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This pass identifies all global names, simple (e.g. a) or * qualified (e.g. a.b.c), and the dependencies between them, then * removes code associated with unreferenced names. It starts by assuming that * only externally accessible names (e.g. window) are referenced, * then iteratively marks additional names as referenced (e.g. Foo * in window['foo'] = new Foo();). This makes it possible to * eliminate code containing circular references. * *

Qualified names can be defined using dotted or object literal syntax * (a.b.c = x; or a.b = {c: x};, respectively). * *

Removal of prototype classes is currently all or nothing. In other words, * prototype properties and methods are never individually removed. * *

Optionally generates pretty HTML output of data so that it is easy to * analyze dependencies. * *

Only operates on names defined in the global scope, but it would be easy * to extend the pass to names defined in local scopes. * * TODO(nicksantos): In the initial implementation of this pass, it was * important to understand namespaced names (e.g., that a.b is distinct from * a.b.c). Now that this pass comes after CollapseProperties, this is no longer * necessary. For now, I've changed so that {@code referenceParentNames} * creates a two-way reference between a.b and a.b.c, so that they're * effectively the same name. When someone has the time, we should completely * rip out all the logic that understands namespaces. * */ final class NameAnalyzer implements CompilerPass { /** Reference to the JS compiler */ private final AbstractCompiler compiler; /** Map of all JS names found */ private final Map allNames = Maps.newTreeMap(); /** Reference dependency graph */ private DiGraph referenceGraph = LinkedDirectedGraph.createWithoutAnnotations(); /** * Map of name scopes - all children of the Node key have a dependency on the * name value. * * If scopes.get(node).equals(name) && node2 is a child of node, then node2 * will not get executed unless name is referenced via a get operation */ private final ListMultimap scopes = LinkedListMultimap.create(); /** Used to parse prototype names */ private static final String PROTOTYPE_SUBSTRING = ".prototype."; private static final int PROTOTYPE_SUBSTRING_LEN = PROTOTYPE_SUBSTRING.length(); private static final int PROTOTYPE_SUFFIX_LEN = ".prototype".length(); /** Window root */ private static final String WINDOW = "window"; /** Function class name */ private static final String FUNCTION = "Function"; /** All of these refer to global scope. These can be moved to config */ static final Set DEFAULT_GLOBAL_NAMES = ImmutableSet.of( "window", "goog.global"); /** Whether to remove unreferenced variables in main pass */ private final boolean removeUnreferenced; /** Names that refer to the global scope */ private final Set globalNames; /** Ast change helper */ private final AstChangeProxy changeProxy; /** Names that are externally defined */ private final Set externalNames = Sets.newHashSet(); /** Name declarations or assignments, in post-order traversal order */ private final List refNodes = Lists.newArrayList(); /** * When multiple names in the global scope point to the same object, we * call them aliases. Store a map from each alias name to the alias set. */ private final Map aliases = Maps.newHashMap(); /** * All the aliases in a program form a graph, where each global name is * a node in the graph, and two names are connected if one directly aliases * the other. * * An {@code AliasSet} represents a connected component in that graph. We do * not explicitly track the graph--we just track the connected components. */ private static class AliasSet { Set names = Sets.newHashSet(); // Every alias set starts with exactly 2 names. AliasSet(String name1, String name2) { names.add(name1); names.add(name2); } } /** * Relationship between the two names. * Currently only two different reference types exists: * goog.inherits class relations and all other references. */ private static enum RefType { REGULAR, INHERITANCE, } /** * Callback that propagates reference information. */ private static class ReferencePropagationCallback implements EdgeCallback { @Override public boolean traverseEdge(JsName from, RefType callSite, JsName to) { if (from.referenced && !to.referenced) { to.referenced = true; return true; } else { return false; } } } /** * Class to hold information that can be determined from a node tree about a * given name */ private static class NameInformation { /** Fully qualified name */ String name; /** Whether the name is guaranteed to be externally referenceable */ boolean isExternallyReferenceable = false; /** Whether this name is a prototype function */ boolean isPrototype = false; /** Name of the prototype class, i.e. "a" if name is "a.prototype.b" */ String prototypeClass = null; /** Local name of prototype property i.e. "b" if name is "a.prototype.b" */ String prototypeProperty = null; /** Name of the super class of name */ String superclass = null; /** Whether this is a call that only affects the class definition */ boolean onlyAffectsClassDef = false; } /** * Struct to hold information about a fully qualified JS name */ private static class JsName implements Comparable { /** Fully qualified name */ String name; /** Name of prototype functions attached to this name */ List prototypeNames = Lists.newArrayList(); /** Whether this is an externally defined name */ boolean externallyDefined = false; /** Whether this node is referenced */ boolean referenced = false; /** Whether the name has descendants that are written to. */ boolean hasWrittenDescendants = false; /** Whether the name is used in a instanceof check */ boolean hasInstanceOfReference = false; /** * Output the node as a string * * @return Node as a string */ @Override public String toString() { StringBuilder out = new StringBuilder(); out.append(name); if (prototypeNames.size() > 0) { out.append(" (CLASS)\n"); out.append(" - FUNCTIONS: "); Iterator pIter = prototypeNames.iterator(); while (pIter.hasNext()) { out.append(pIter.next()); if (pIter.hasNext()) { out.append(", "); } } } return out.toString(); } @Override public int compareTo(JsName rhs) { return this.name.compareTo(rhs.name); } } /** * Interface to get information about and remove unreferenced names. */ interface RefNode { JsName name(); void remove(); } /** * Class for nodes that reference a fully-qualified JS name. Fully qualified * names are of form A or A.B (A.B.C, etc.). References can get the value or * set the value of the JS name. */ private class JsNameRefNode implements RefNode { /** JsName node for this reference */ JsName name; /** * Top GETPROP or NAME or STRING [objlit key] node defining the name of * this node */ @SuppressWarnings("unused") Node node; /** * Parent node of the name access * (ASSIGN, VAR, FUNCTION, OBJECTLIT, or CALL) */ Node parent; /** * Create a node that refers to a name * * @param name The name * @param node The top node representing the name (GETPROP, NAME, STRING) */ JsNameRefNode(JsName name, Node node) { this.name = name; this.node = node; this.parent = node.getParent(); } @Override public JsName name() { return name; } @Override public void remove() { // Setters have VAR, FUNCTION, or ASSIGN parent nodes. CALL parent // nodes are global refs, and are handled later in this function. Node containingNode = parent.getParent(); switch (parent.getType()) { case Token.VAR: Preconditions.checkState(parent.hasOneChild()); replaceWithRhs(containingNode, parent); break; case Token.FUNCTION: replaceWithRhs(containingNode, parent); break; case Token.ASSIGN: if (containingNode.isExprResult()) { replaceWithRhs(containingNode.getParent(), containingNode); } else { replaceWithRhs(containingNode, parent); } break; case Token.OBJECTLIT: // TODO(nicksantos): Come up with a way to remove this. // If we remove object lit keys, then we will need to also // create dependency scopes for them. break; } } } /** * Class for nodes that set prototype properties or methods. */ private class PrototypeSetNode extends JsNameRefNode { /** * Create a set node from the name & setter node * * @param name The name * @param parent Parent node that assigns the expression (an ASSIGN) */ PrototypeSetNode(JsName name, Node parent) { super(name, parent.getFirstChild()); Preconditions.checkState(parent.isAssign()); } @Override public void remove() { Node gramps = parent.getParent(); if (gramps.isExprResult()) { // name.prototype.foo = function() { ... }; changeProxy.removeChild(gramps.getParent(), gramps); } else { // ... name.prototype.foo = function() { ... } ... changeProxy.replaceWith(gramps, parent, parent.getLastChild().detachFromParent()); } } } /** * Base class for special reference nodes. */ private abstract class SpecialReferenceNode implements RefNode { /** JsName node for the function */ JsName name; /** The CALL node */ Node node; /** * Create a special reference node. * * @param name The name * @param node The CALL node */ SpecialReferenceNode(JsName name, Node node) { this.name = name; this.node = node; } @Override public JsName name() { return name; } Node getParent() { return node.getParent(); } Node getGramps() { return node.getParent() == null ? null : node.getParent().getParent(); } } /** * Class for nodes that are function calls that may change a function's * prototype */ private class ClassDefiningFunctionNode extends SpecialReferenceNode { /** * Create a class defining function node from the name & setter node * * @param name The name * @param node The CALL node */ ClassDefiningFunctionNode(JsName name, Node node) { super(name, node); Preconditions.checkState(node.isCall()); } @Override public void remove() { Preconditions.checkState(node.isCall()); Node parent = getParent(); if (parent.isExprResult()) { changeProxy.removeChild(getGramps(), parent); } else { changeProxy.replaceWith(parent, node, IR.voidNode(IR.number(0))); } } } /** * Class for nodes that check instanceof */ private class InstanceOfCheckNode extends SpecialReferenceNode { /** * Create an instanceof node from the name and parent node * * @param name The name * @param node The qualified name node */ InstanceOfCheckNode(JsName name, Node node) { super(name, node); Preconditions.checkState(node.isQualifiedName()); Preconditions.checkState(getParent().isInstanceOf()); } @Override public void remove() { changeProxy.replaceWith(getGramps(), getParent(), IR.falseNode()); } } /** * Walk through externs and mark nodes as externally declared if declared */ private class ProcessExternals extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { NameInformation ns = null; if (NodeUtil.isVarDeclaration(n)) { ns = createNameInformation(t, n); } else if (NodeUtil.isFunctionDeclaration(n)) { ns = createNameInformation(t, n.getFirstChild()); } if (ns != null) { JsName jsName = getName(ns.name, true); jsName.externallyDefined = true; externalNames.add(ns.name); } } } /** *

Identifies all dependency scopes. * *

A dependency scope is a relationship between a node tree and a name that * implies that the node tree will not execute (and thus can be eliminated) if * the name is never referenced. * *

The entire parse tree is ultimately in a dependency scope relationship * with window (or an equivalent name for the global scope), but * the goal here is to find finer-grained relationships. This callback creates * dependency scopes for every assignment statement, variable declaration, and * function call in the global scope. * *

Note that dependency scope node trees aren't necessarily disjoint. * In the following code snippet, for example, the function definition * forms a dependency scope with the name f and the assignment * inside the function forms a dependency scope with the name x. *

   * var x; function f() { x = 1; }
   * 
*/ private class FindDependencyScopes extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!t.inGlobalScope()) { return; } if (n.isAssign()) { recordAssignment(t, n, n); if (!NodeUtil.isImmutableResult(n.getLastChild())) { recordConsumers(t, n, n); } } else if (NodeUtil.isVarDeclaration(n)) { NameInformation ns = createNameInformation(t, n); recordDepScope(n, ns); } else if (NodeUtil.isFunctionDeclaration(n)) { NameInformation ns = createNameInformation(t, n.getFirstChild()); recordDepScope(n, ns); } else if (NodeUtil.isExprCall(n)) { Node callNode = n.getFirstChild(); Node nameNode = callNode.getFirstChild(); NameInformation ns = createNameInformation(t, nameNode); if (ns != null && ns.onlyAffectsClassDef) { recordDepScope(n, ns); } } } private void recordConsumers(NodeTraversal t, Node n, Node recordNode) { Node parent = n.getParent(); switch (parent.getType()) { case Token.ASSIGN: if (n == parent.getLastChild()) { recordAssignment(t, parent, recordNode); } recordConsumers(t, parent, recordNode); break; case Token.NAME: NameInformation ns = createNameInformation(t, parent); recordDepScope(recordNode, ns); break; case Token.OR: recordConsumers(t, parent, recordNode); break; case Token.AND: // In "a && b" only "b" can be meaningfully aliased. // "a" must be falsy, which it must be an immutable, non-Object case Token.COMMA: case Token.HOOK: if (n != parent.getFirstChild()) { recordConsumers(t, parent, recordNode); } break; } } private void recordAssignment(NodeTraversal t, Node n, Node recordNode) { Node nameNode = n.getFirstChild(); Node parent = n.getParent(); NameInformation ns = createNameInformation(t, nameNode); if (ns != null) { if (parent.isFor() && !NodeUtil.isForIn(parent)) { // Patch for assignments that appear in the init, // condition or iteration part of a FOR loop. Without // this change, all 3 of those parts try to claim the for // loop as their dependency scope. The last assignment in // those three fields wins, which can result in incorrect // reference edges between referenced and assigned variables. // // TODO(user) revisit the dependency scope calculation // logic. if (parent.getFirstChild().getNext() != n) { recordDepScope(recordNode, ns); } else { recordDepScope(nameNode, ns); } } else { recordDepScope(recordNode, ns); } } } /** * Defines a dependency scope. */ private void recordDepScope(Node node, NameInformation name) { Preconditions.checkNotNull(name); scopes.put(node, name); } } /** * Create JsName objects for variable and function declarations in * the global scope before computing name references. In JavaScript * it is legal to refer to variable and function names before the * actual declaration. */ private class HoistVariableAndFunctionDeclarations extends NodeTraversal.AbstractShallowCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isVarDeclaration(n)) { NameInformation ns = createNameInformation(t, n); Preconditions.checkNotNull(ns, "NameInformation is null"); createName(ns.name); } else if (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); NameInformation ns = createNameInformation(t, nameNode); Preconditions.checkNotNull(ns, "NameInformation is null"); createName(nameNode.getString()); } } } /** * Identifies all declarations of global names and setter statements * affecting global symbols (assignments to global names). * * All declarations and setters must be gathered in a single * traversal and stored in traversal order so "removeUnreferenced" * can perform modifications in traversal order. */ private class FindDeclarationsAndSetters extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // Record global variable and function declarations if (t.inGlobalScope()) { if (NodeUtil.isVarDeclaration(n)) { NameInformation ns = createNameInformation(t, n); Preconditions.checkNotNull(ns); recordSet(ns.name, n); } else if (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); NameInformation ns = createNameInformation(t, nameNode); if (ns != null) { JsName nameInfo = getName(nameNode.getString(), true); recordSet(nameInfo.name, nameNode); } } else if (NodeUtil.isObjectLitKey(n)) { NameInformation ns = createNameInformation(t, n); if (ns != null) { recordSet(ns.name, n); } } } // Record assignments and call sites if (n.isAssign()) { Node nameNode = n.getFirstChild(); NameInformation ns = createNameInformation(t, nameNode); if (ns != null) { if (ns.isPrototype) { recordPrototypeSet(ns.prototypeClass, ns.prototypeProperty, n); } else { recordSet(ns.name, nameNode); } } } else if (n.isCall()) { Node nameNode = n.getFirstChild(); NameInformation ns = createNameInformation(t, nameNode); if (ns != null && ns.onlyAffectsClassDef) { JsName name = getName(ns.name, true); refNodes.add(new ClassDefiningFunctionNode(name, n)); } } } /** * Records the assignment of a value to a global name. * * @param name Fully qualified name * @param node The top node representing the name (GETPROP, NAME, or STRING * [objlit key]) */ private void recordSet(String name, Node node) { JsName jsn = getName(name, true); JsNameRefNode nameRefNode = new JsNameRefNode(jsn, node); refNodes.add(nameRefNode); // Now, look at all parent names and record that their properties have // been written to. if (node.isGetElem()) { recordWriteOnProperties(name); } else if (name.indexOf('.') != -1) { recordWriteOnProperties(name.substring(0, name.lastIndexOf('.'))); } } /** * Records the assignment to a prototype property of a global name, * if possible. * * @param className The name of the class. * @param prototypeProperty The name of the prototype property. * @param node The top node representing the name (GETPROP) */ private void recordPrototypeSet(String className, String prototypeProperty, Node node) { JsName name = getName(className, true); name.prototypeNames.add(prototypeProperty); refNodes.add(new PrototypeSetNode(name, node)); recordWriteOnProperties(className); } /** * Record that the properties of this name have been written to. */ private void recordWriteOnProperties(String parentName) { do { JsName parent = getName(parentName, true); if (parent.hasWrittenDescendants) { // If we already recorded this name, then all its parents must // also be recorded. short-circuit this loop. return; } else { parent.hasWrittenDescendants = true; } if (parentName.indexOf('.') == -1) { return; } parentName = parentName.substring(0, parentName.lastIndexOf('.')); } while(true); } } private static final Predicate NON_LOCAL_RESULT_PREDICATE = new Predicate() { @Override public boolean apply(Node input) { if (input.isCall()) { return false; } // TODO(johnlenz): handle NEW calls that record their 'this' // in global scope and effectively return an alias. // Other non-local references are handled by this pass. return true; } }; /** *

Identifies all references between global names. * *

A reference from a name f to a name g means * that if the name f must be defined, then the name * g must also be defined. This would be the case if, for * example, f were a function that called g. */ private class FindReferences implements Callback { Set nodesToKeep; FindReferences() { nodesToKeep = Sets.newHashSet(); } private void addAllChildren(Node n) { nodesToKeep.add(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { addAllChildren(child); } } private void addSimplifiedChildren(Node n) { NodeTraversal.traverse( compiler, n, new GatherSideEffectSubexpressionsCallback( compiler, new NodeAccumulator())); } private void addSimplifiedExpression(Node n, Node parent) { if (parent.isVar()) { Node value = n.getFirstChild(); if (value != null) { addSimplifiedChildren(value); } } else if (n.isAssign() && (parent.isExprResult() || parent.isFor() || parent.isReturn())) { for (Node child : n.children()) { addSimplifiedChildren(child); } } else if (n.isCall() && parent.isExprResult()) { addSimplifiedChildren(n); } else { addAllChildren(n); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (parent == null) { return true; } // Gather the list of nodes that either have side effects, are // arguments to function calls with side effects or are used in // control structure predicates. These names are always // referenced when the enclosing function is called. if (n.isFor()) { if (!NodeUtil.isForIn(n)) { Node decl = n.getFirstChild(); Node pred = decl.getNext(); Node step = pred.getNext(); addSimplifiedExpression(decl, n); addSimplifiedExpression(pred, n); addSimplifiedExpression(step, n); } else { // n.getChildCount() == 3 Node decl = n.getFirstChild(); Node iter = decl.getNext(); addAllChildren(decl); addAllChildren(iter); } } if (parent.isVar() || parent.isExprResult() || parent.isReturn() || parent.isThrow()) { addSimplifiedExpression(n, parent); } if ((parent.isIf() || parent.isWhile() || parent.isWith() || parent.isSwitch() || parent.isCase()) && parent.getFirstChild() == n) { addAllChildren(n); } if (parent.isDo() && parent.getLastChild() == n) { addAllChildren(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!(n.isName() || NodeUtil.isGet(n) && !parent.isGetProp())) { // This is not a simple or qualified name. return; } NameInformation nameInfo = createNameInformation(t, n); if (nameInfo == null) { // The name is not a global name return; } if (nameInfo.onlyAffectsClassDef) { if (nameInfo.superclass != null) { recordReference( nameInfo.name, nameInfo.superclass, RefType.INHERITANCE); } // Make sure that we record a reference to the function that does // the inheritance, so that the inherits() function itself does // not get stripped. String nodeName = n.getQualifiedName(); if (nodeName != null) { recordReference( nameInfo.name, nodeName, RefType.REGULAR); } return; } if (parent.isInstanceOf() && parent.getLastChild() == n && // Don't cover GETELEMs with a global root node. n.isQualifiedName()) { JsName checkedClass = getName(nameInfo.name, true); refNodes.add(new InstanceOfCheckNode(checkedClass, n)); checkedClass.hasInstanceOfReference = true; return; } // Determine which name might be potentially referring to this one by // looking up the nearest enclosing dependency scope. It's unnecessary to // determine all enclosing dependency scopes because this callback should // create a chain of references between them. List referers = getDependencyScope(n); if (referers.isEmpty()) { maybeRecordReferenceOrAlias(t, n, parent, nameInfo, null); } else { for (NameInformation referring : referers) { maybeRecordReferenceOrAlias(t, n, parent, nameInfo, referring); } recordAliases(referers); } } private void maybeRecordReferenceOrAlias( NodeTraversal t, Node n, Node parent, NameInformation nameInfo, NameInformation referring) { String referringName = ""; if (referring != null) { referringName = referring.isPrototype ? referring.prototypeClass : referring.name; } String name = nameInfo.name; // A value whose result is the return value of a function call // can be an alias to global object. // Here we add a alias to the general "global" object // to act as a placeholder for the actual (unnamed) value. if (maybeHiddenAlias(n)) { recordAlias(name, WINDOW); } // An externally referenceable name must always be defined, so we add a // reference to it from the global scope (a.k.a. window). if (nameInfo.isExternallyReferenceable) { recordReference(WINDOW, name, RefType.REGULAR); maybeRecordAlias(name, parent, referring, referringName); return; } // An assignment implies a reference from the enclosing dependency scope. // For example, foo references bar in: function foo() {bar=5}. if (NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { if (referring != null) { recordReference(referringName, name, RefType.REGULAR); } return; } if (nodesToKeep.contains(n)) { List functionScopes = getEnclosingFunctionDependencyScope(t); if (!functionScopes.isEmpty()) { for (NameInformation functionScope : functionScopes) { recordReference(functionScope.name, name, RefType.REGULAR); } } else { recordReference(WINDOW, name, RefType.REGULAR); if (referring != null) { maybeRecordAlias(name, parent, referring, referringName); } } } else if (referring != null) { if (!maybeRecordAlias(name, parent, referring, referringName)) { RefType depType = referring.onlyAffectsClassDef ? RefType.INHERITANCE : RefType.REGULAR; recordReference(referringName, name, depType); } } else { // No named dependency scope found. Unfortunately that might // mean that the expression is a child of an function expression // or assignment with a complex lhs. In those cases, // protect this node by creating a reference to WINDOW. for (Node ancestor : n.getAncestors()) { if (NodeUtil.isAssignmentOp(ancestor) || ancestor.isFunction()) { recordReference(WINDOW, name, RefType.REGULAR); break; } } } } private void recordAliases(List referers) { int size = referers.size(); for (int i = 0; i < size; i++) { for (int j = i + 1; j < size; j++) { recordAlias(referers.get(i).name, referers.get(j).name); recordAlias(referers.get(j).name, referers.get(i).name); } } } /** * A value whose result is the return value of a function call * can be an alias to global object. The dependency on the call target will * prevent the removal of the function and its dependent values, but won't * prevent the alias' removal. */ private boolean maybeHiddenAlias(Node n) { Node parent = n.getParent(); if (NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { Node rhs = (parent.isVar()) ? n.getFirstChild() : parent.getLastChild(); return (rhs != null && !NodeUtil.evaluatesToLocalValue( rhs, NON_LOCAL_RESULT_PREDICATE)); } return false; } /** * @return Whether the alias was recorded. */ private boolean maybeRecordAlias( String name, Node parent, NameInformation referring, String referringName) { // A common type of reference is // function F() {} // F.prototype.bar = goog.nullFunction; // // In this specific case, we do not want a reference to goog.nullFunction // to preserve F. // // In the general case, the user could do something like // function F() {} // F.prototype.bar = goog.nullFunction; // F.prototype.bar.baz = 3; // where it would not be safe to remove F. // // So we do not treat this alias as a backdoor for people to mutate the // original object. We think that this heuristic will always be // OK in real code. boolean isPrototypePropAssignment = parent.isAssign() && NodeUtil.isPrototypeProperty(parent.getFirstChild()); if ((parent.isName() || parent.isAssign()) && !isPrototypePropAssignment && referring != null && scopes.get(parent).contains(referring)) { recordAlias(referringName, name); return true; } return false; } /** * Helper class that gathers the list of nodes that would be left * behind after simplification. */ private class NodeAccumulator implements SideEffectAccumulator { @Override public boolean classDefiningCallsHaveSideEffects() { return false; } @Override public void keepSubTree(Node original) { addAllChildren(original); } @Override public void keepSimplifiedShortCircuitExpression(Node original) { Node condition = original.getFirstChild(); Node thenBranch = condition.getNext(); addAllChildren(condition); addSimplifiedChildren(thenBranch); } @Override public void keepSimplifiedHookExpression(Node hook, boolean thenHasSideEffects, boolean elseHasSideEffects) { Node condition = hook.getFirstChild(); Node thenBranch = condition.getNext(); Node elseBranch = thenBranch.getNext(); addAllChildren(condition); if (thenHasSideEffects) { addSimplifiedChildren(thenBranch); } if (elseHasSideEffects) { addSimplifiedChildren(elseBranch); } } } } private class RemoveListener implements AstChangeProxy.ChangeListener { @Override public void nodeRemoved(Node n) { compiler.reportCodeChange(); } } /** * Creates a name analyzer, with option to remove unreferenced variables when * calling process(). * * The analyzer make a best guess at whether functions affect global scope * based on usage (no assignment of return value means that a function has * side effects). * * @param compiler The AbstractCompiler * @param removeUnreferenced If true, remove unreferenced variables during * process() */ NameAnalyzer(AbstractCompiler compiler, boolean removeUnreferenced) { this.compiler = compiler; this.removeUnreferenced = removeUnreferenced; this.globalNames = DEFAULT_GLOBAL_NAMES; this.changeProxy = new AstChangeProxy(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, new ProcessExternals()); NodeTraversal.traverse(compiler, root, new FindDependencyScopes()); NodeTraversal.traverse( compiler, root, new HoistVariableAndFunctionDeclarations()); NodeTraversal.traverse(compiler, root, new FindDeclarationsAndSetters()); NodeTraversal.traverse(compiler, root, new FindReferences()); // Create bi-directional references between parent names and their // descendants. This may create new names. referenceParentNames(); // If we modify the property of an alias, make sure that modification // gets reflected in the original object. referenceAliases(); calculateReferences(); if (removeUnreferenced) { removeUnreferenced(); } } /** * Records an alias of one name to another name. */ private void recordAlias(String fromName, String toName) { recordReference(fromName, toName, RefType.REGULAR); // We need to add an edge to the alias graph. The alias graph is expressed // implicitly as a set of connected components, called AliasSets. // // There are three possibilities: // 1) Neither name is part of a connected component. Create a new one. // 2) Exactly one name is part of a connected component. Merge the new // name into the component. // 3) The two names are already part of connected components. Merge // those components together. AliasSet toNameAliasSet = aliases.get(toName); AliasSet fromNameAliasSet = aliases.get(fromName); AliasSet resultSet = null; if (toNameAliasSet == null && fromNameAliasSet == null) { resultSet = new AliasSet(toName, fromName); } else if (toNameAliasSet != null && fromNameAliasSet != null) { resultSet = toNameAliasSet; resultSet.names.addAll(fromNameAliasSet.names); for (String name : fromNameAliasSet.names) { aliases.put(name, resultSet); } } else if (toNameAliasSet != null) { resultSet = toNameAliasSet; resultSet.names.add(fromName); } else { resultSet = fromNameAliasSet; resultSet.names.add(toName); } aliases.put(fromName, resultSet); aliases.put(toName, resultSet); } /** * Records a reference from one name to another name. */ private void recordReference(String fromName, String toName, RefType depType) { if (fromName.equals(toName)) { // Don't bother recording self-references. return; } JsName from = getName(fromName, true); JsName to = getName(toName, true); referenceGraph.createNode(from); referenceGraph.createNode(to); if (!referenceGraph.isConnectedInDirection(from, depType, to)) { referenceGraph.connect(from, depType, to); } } /** * Removes all unreferenced variables. */ void removeUnreferenced() { RemoveListener listener = new RemoveListener(); changeProxy.registerListener(listener); for (RefNode refNode : refNodes) { JsName name = refNode.name(); if (!name.referenced && !name.externallyDefined) { refNode.remove(); } } changeProxy.unregisterListener(listener); } /** * Generates an HTML report * * @return The report */ String getHtmlReport() { StringBuilder sb = new StringBuilder(); sb.append(""); sb.append("OVERALL STATS

    "); appendListItem(sb, "Total Names: " + countOf(TriState.BOTH, TriState.BOTH)); appendListItem(sb, "Total Classes: " + countOf(TriState.TRUE, TriState.BOTH)); appendListItem(sb, "Total Static Functions: " + countOf(TriState.FALSE, TriState.BOTH)); appendListItem(sb, "Referenced Names: " + countOf(TriState.BOTH, TriState.TRUE)); appendListItem(sb, "Referenced Classes: " + countOf(TriState.TRUE, TriState.TRUE)); appendListItem(sb, "Referenced Functions: " + countOf(TriState.FALSE, TriState.TRUE)); sb.append("
"); sb.append("ALL NAMES
    \n"); for (JsName node : allNames.values()) { sb.append("
  • " + nameAnchor(node.name) + "
      "); if (node.prototypeNames.size() > 0) { sb.append("
    • PROTOTYPES: "); Iterator protoIter = node.prototypeNames.iterator(); while (protoIter.hasNext()) { sb.append(protoIter.next()); if (protoIter.hasNext()) { sb.append(", "); } } } if (referenceGraph.hasNode(node)) { List> refersTo = referenceGraph.getOutEdges(node); if (refersTo.size() > 0) { sb.append("
    • REFERS TO: "); Iterator> toIter = refersTo.iterator(); while (toIter.hasNext()) { sb.append(nameLink(toIter.next().getDestination().getValue().name)); if (toIter.hasNext()) { sb.append(", "); } } } List> referencedBy = referenceGraph.getInEdges(node); if (referencedBy.size() > 0) { sb.append("
    • REFERENCED BY: "); Iterator> fromIter = refersTo.iterator(); while (fromIter.hasNext()) { sb.append( nameLink(fromIter.next().getDestination().getValue().name)); if (fromIter.hasNext()) { sb.append(", "); } } } } sb.append("
    • "); sb.append("
  • "); } sb.append("
"); sb.append(""); return sb.toString(); } private void appendListItem(StringBuilder sb, String text) { sb.append("
  • " + text + "
  • \n"); } private String nameLink(String name) { return "" + name + ""; } private String nameAnchor(String name) { return "" + name + ""; } /** * Looks up a {@link JsName} by name, optionally creating one if it doesn't * already exist. * * @param name A fully qualified name * @param canCreate Whether to create the object if necessary * @return The {@code JsName} object, or null if one can't be found and * can't be created. */ private JsName getName(String name, boolean canCreate) { if (canCreate) { createName(name); } return allNames.get(name); } /** * Creates a {@link JsName} for the given name if it doesn't already * exist. * * @param name A fully qualified name */ private void createName(String name) { JsName jsn = allNames.get(name); if (jsn == null) { jsn = new JsName(); jsn.name = name; allNames.put(name, jsn); } } /** * The NameAnalyzer algorithm works best when all objects have a canonical * name in the global scope. When multiple names in the global scope * point to the same object, things start to break down. * * For example, if we have * * var a = {}; * var b = a; * a.foo = 3; * alert(b.foo); * * then a.foo and b.foo are the same name, even though NameAnalyzer doesn't * represent them as such. * * To handle this case, we look at all the aliases in the program. * If descendant properties of that alias are assigned, then we create a * directional reference from the original name to the alias. For example, * in this case, the assign to {@code a.foo} triggers a reference from * {@code b} to {@code a}, but NOT from a to b. * * Similarly, "instanceof" checks do not prevent the removal * of a unaliased name but an instanceof check on an alias can only be removed * if the other aliases are also removed, so we add a connection here. */ private void referenceAliases() { for (Map.Entry entry : aliases.entrySet()) { JsName name = getName(entry.getKey(), false); if (name.hasWrittenDescendants || name.hasInstanceOfReference) { for (String alias : entry.getValue().names) { recordReference(alias, entry.getKey(), RefType.REGULAR); } } } } /** * Adds mutual references between all known global names and their parent * names. (e.g. between a.b.c and a.b). */ private void referenceParentNames() { // Duplicate set of nodes to process so we don't modify set we are // currently iterating over Set allNamesCopy = Sets.newHashSet(allNames.values()); for (JsName name : allNamesCopy) { String curName = name.name; JsName curJsName = name; while (curName.indexOf('.') != -1) { String parentName = curName.substring(0, curName.lastIndexOf('.')); if (!globalNames.contains(parentName)) { JsName parentJsName = getName(parentName, true); recordReference(curJsName.name, parentJsName.name, RefType.REGULAR); recordReference(parentJsName.name, curJsName.name, RefType.REGULAR); curJsName = parentJsName; } curName = parentName; } } } /** * Creates name information for the current node during a traversal. * * @param t The node traversal * @param n The current node * @return The name information, or null if the name is irrelevant to this * pass */ private NameInformation createNameInformation(NodeTraversal t, Node n) { Node parent = n.getParent(); // Build the full name and find its root node by iterating down through all // GETPROP/GETELEM nodes. String name = ""; Node rootNameNode = n; boolean bNameWasShortened = false; while (true) { if (NodeUtil.isGet(rootNameNode)) { Node prop = rootNameNode.getLastChild(); if (rootNameNode.isGetProp()) { name = "." + prop.getString() + name; } else { // We consider the name to be "a.b" in a.b['c'] or a.b[x].d. bNameWasShortened = true; name = ""; } rootNameNode = rootNameNode.getFirstChild(); } else if (NodeUtil.isObjectLitKey(rootNameNode)) { name = "." + rootNameNode.getString() + name; // Check if this is an object literal assigned to something. Node objLit = rootNameNode.getParent(); Node objLitParent = objLit.getParent(); if (objLitParent.isAssign()) { // This must be the right side of the assign. rootNameNode = objLitParent.getFirstChild(); } else if (objLitParent.isName()) { // This must be a VAR initialization. rootNameNode = objLitParent; } else if (objLitParent.isStringKey()) { // This must be a object literal key initialization. rootNameNode = objLitParent; } else { return null; } } else { break; } } // Check whether this is a class-defining call. Classes may only be defined // in the global scope. if (parent.isCall() && t.inGlobalScope()) { CodingConvention convention = compiler.getCodingConvention(); SubclassRelationship classes = convention.getClassesDefinedByCall(parent); if (classes != null) { NameInformation nameInfo = new NameInformation(); nameInfo.name = classes.subclassName; nameInfo.onlyAffectsClassDef = true; nameInfo.superclass = classes.superclassName; return nameInfo; } String singletonGetterClass = convention.getSingletonGetterClassName(parent); if (singletonGetterClass != null) { NameInformation nameInfo = new NameInformation(); nameInfo.name = singletonGetterClass; nameInfo.onlyAffectsClassDef = true; return nameInfo; } } switch (rootNameNode.getType()) { case Token.NAME: // Check whether this is an assignment to a prototype property // of an object defined in the global scope. if (!bNameWasShortened && n.isGetProp() && parent.isAssign() && "prototype".equals(n.getLastChild().getString())) { if (createNameInformation(t, n.getFirstChild()) != null) { name = rootNameNode.getString() + name; name = name.substring(0, name.length() - PROTOTYPE_SUFFIX_LEN); NameInformation nameInfo = new NameInformation(); nameInfo.name = name; return nameInfo; } else { return null; } } return createNameInformation( rootNameNode.getString() + name, t.getScope(), rootNameNode); case Token.THIS: if (t.inGlobalScope()) { NameInformation nameInfo = new NameInformation(); if (name.indexOf('.') == 0) { nameInfo.name = name.substring(1); // strip leading "." } else { nameInfo.name = name; } nameInfo.isExternallyReferenceable = true; return nameInfo; } return null; default: return null; } } /** * Creates name information for a particular qualified name that occurs in a * particular scope. * * @param name A qualified name (e.g. "x" or "a.b.c") * @param scope The scope in which {@code name} occurs * @param rootNameNode The NAME node for the first token of {@code name} * @return The name information, or null if the name is irrelevant to this * pass */ private NameInformation createNameInformation( String name, Scope scope, Node rootNameNode) { // Check the scope. Currently we're only looking at globally scoped vars. String rootName = rootNameNode.getString(); Var v = scope.getVar(rootName); boolean isExtern = (v == null && externalNames.contains(rootName)); boolean isGlobalRef = (v != null && v.isGlobal()) || isExtern || rootName.equals(WINDOW); if (!isGlobalRef) { return null; } NameInformation nameInfo = new NameInformation(); // If a prototype property or method, fill in prototype information. int idx = name.indexOf(PROTOTYPE_SUBSTRING); if (idx != -1) { nameInfo.isPrototype = true; nameInfo.prototypeClass = name.substring(0, idx); nameInfo.prototypeProperty = name.substring( idx + PROTOTYPE_SUBSTRING_LEN); } nameInfo.name = name; nameInfo.isExternallyReferenceable = isExtern || isExternallyReferenceable(scope, name); return nameInfo; } /** * Checks whether a name can be referenced outside of the compiled code. * These names will be the root of dependency trees. * * @param scope The current variable scope * @param name The name * @return True if can be referenced outside */ private boolean isExternallyReferenceable(Scope scope, String name) { if (compiler.getCodingConvention().isExported(name)) { return true; } if (scope.isLocal()) { return false; } for (String s : globalNames) { if (name.startsWith(s)) { return true; } } return false; } /** * Gets the nearest enclosing dependency scope, or null if there isn't one. */ private List getDependencyScope(Node n) { for (Node node : n.getAncestors()) { List refs = scopes.get(node); if (!refs.isEmpty()) { return refs; } } return Collections.emptyList(); } /** * Get dependency scope defined by the enclosing function, or null. * If enclosing function is a function expression, determine scope based on * its parent if the parent node is a variable declaration or * assignment. */ private List getEnclosingFunctionDependencyScope( NodeTraversal t) { Node function = t.getEnclosingFunction(); if (function == null) { return Collections.emptyList(); } List refs = scopes.get(function); if (!refs.isEmpty()) { return refs; } // Function expression. try to get a name from the parent var // declaration or assignment. Node parent = function.getParent(); if (parent != null) { // Account for functions defined in the form: // var a = cond ? function a() {} : function b() {}; while (parent.isHook()) { parent = parent.getParent(); } if (parent.isName()) { return scopes.get(parent); } if (parent.isAssign()) { return scopes.get(parent); } } return Collections.emptyList(); } /** * Propagate "referenced" property down the graph. */ private void calculateReferences() { JsName window = getName(WINDOW, true); window.referenced = true; JsName function = getName(FUNCTION, true); function.referenced = true; // Propagate "referenced" property to a fixed point. FixedPointGraphTraversal.newTraversal(new ReferencePropagationCallback()) .computeFixedPoint(referenceGraph); } /** * Enum for saying a value can be true, false, or either (cleaner than using a * Boolean with null) */ private enum TriState { /** If value is true */ TRUE, /** If value is false */ FALSE, /** If value can be true or false */ BOTH } /** * Gets the count of nodes matching the criteria * * @param isClass Whether the node is a class * @param referenced Whether the node is referenced * @return Number of matches */ private int countOf(TriState isClass, TriState referenced) { int count = 0; for (JsName name : allNames.values()) { boolean nodeIsClass = name.prototypeNames.size() > 0; boolean classMatch = isClass == TriState.BOTH || (nodeIsClass && isClass == TriState.TRUE) || (!nodeIsClass && isClass == TriState.FALSE); boolean referenceMatch = referenced == TriState.BOTH || (name.referenced && referenced == TriState.TRUE) || (!name.referenced && referenced == TriState.FALSE); if (classMatch && referenceMatch && !name.externallyDefined) { count++; } } return count; } /** * Extract a list of replacement nodes to use. */ private List getSideEffectNodes(Node n) { List subexpressions = Lists.newArrayList(); NodeTraversal.traverse( compiler, n, new GatherSideEffectSubexpressionsCallback( compiler, new GetReplacementSideEffectSubexpressions( compiler, subexpressions))); List replacements = Lists.newArrayListWithExpectedSize(subexpressions.size()); for (Node subexpression : subexpressions) { replacements.add(NodeUtil.newExpr(subexpression)); } return replacements; } /** * Replace n with a simpler expression, while preserving program * behavior. * * If the n's value is used, replace it with its RHS; otherwise * replace it with the subexpressions that have side effects. */ private void replaceWithRhs(Node parent, Node n) { if (valueConsumedByParent(n, parent)) { // parent reads from n directly; replace it with n's rhs + lhs // subexpressions with side effects. List replacements = getRhsSubexpressions(n); List newReplacements = Lists.newArrayList(); for (int i = 0; i < replacements.size() - 1; i++) { newReplacements.addAll(getSideEffectNodes(replacements.get(i))); } Node valueExpr = replacements.get(replacements.size() - 1); valueExpr.detachFromParent(); newReplacements.add(valueExpr); changeProxy.replaceWith( parent, n, collapseReplacements(newReplacements)); } else if (n.isAssign() && !parent.isFor()) { // assignment appears in a RHS expression. we have already // considered names in the assignment's RHS as being referenced; // replace the assignment with its RHS. // TODO(user) make the pass smarter about these cases and/or run // this pass and RemoveConstantExpressions together in a loop. Node replacement = n.getLastChild(); replacement.detachFromParent(); changeProxy.replaceWith(parent, n, replacement); } else { replaceTopLevelExpressionWithRhs(parent, n); } } /** * Simplify a toplevel expression, while preserving program * behavior. */ private void replaceTopLevelExpressionWithRhs(Node parent, Node n) { // validate inputs switch (parent.getType()) { case Token.BLOCK: case Token.SCRIPT: case Token.FOR: case Token.LABEL: break; default: throw new IllegalArgumentException( "Unsupported parent node type in replaceWithRhs " + Token.name(parent.getType())); } switch (n.getType()) { case Token.EXPR_RESULT: case Token.FUNCTION: case Token.VAR: break; case Token.ASSIGN: Preconditions.checkArgument(parent.isFor(), "Unsupported assignment in replaceWithRhs. parent: %s", Token.name(parent.getType())); break; default: throw new IllegalArgumentException( "Unsupported node type in replaceWithRhs " + Token.name(n.getType())); } // gather replacements List replacements = Lists.newArrayList(); for (Node rhs : getRhsSubexpressions(n)) { replacements.addAll(getSideEffectNodes(rhs)); } if (parent.isFor()) { // tweak replacements array s.t. it is a single expression node. if (replacements.isEmpty()) { replacements.add(IR.empty()); } else { Node expr = collapseReplacements(replacements); replacements.clear(); replacements.add(expr); } } changeProxy.replaceWith(parent, n, replacements); } /** * Determine if the parent reads the value of a child expression * directly. This is true children used in predicates, RETURN * statements and, RHS of variable declarations and assignments. * * In the case of: * if (a) b else c * * This method returns true for "a", and false for "b" and "c": the * IF expression does something special based on "a"'s value. "b" * and "c" are effectively outputs. Same logic applies to FOR, * WHILE and DO loop predicates. AND/OR/HOOK expressions are * syntactic sugar for IF statements; therefore this method returns * true for the predicate and false otherwise. */ private boolean valueConsumedByParent(Node n, Node parent) { if (NodeUtil.isAssignmentOp(parent)) { return parent.getLastChild() == n; } switch (parent.getType()) { case Token.NAME: case Token.RETURN: return true; case Token.AND: case Token.OR: case Token.HOOK: return parent.getFirstChild() == n; case Token.FOR: return parent.getFirstChild().getNext() == n; case Token.IF: case Token.WHILE: return parent.getFirstChild() == n; case Token.DO: return parent.getLastChild() == n; default: return false; } } /** * Merge a list of nodes into a single expression. The value of the * new expression is determined by the last expression in the list. */ private Node collapseReplacements(List replacements) { Node expr = null; for (Node rep : replacements) { if (rep.isExprResult()) { rep = rep.getFirstChild(); rep.detachFromParent(); } if (expr == null) { expr = rep; } else { expr = IR.comma(expr, rep); } } return expr; } /** * Extract a list of subexpressions that act as right hand sides. */ private List getRhsSubexpressions(Node n) { switch (n.getType()) { case Token.EXPR_RESULT: // process body return getRhsSubexpressions(n.getFirstChild()); case Token.FUNCTION: // function nodes have no RHS return Collections.emptyList(); case Token.NAME: { // parent is a var node. RHS is the first child Node rhs = n.getFirstChild(); if (rhs != null) { return Lists.newArrayList(rhs); } else { return Collections.emptyList(); } } case Token.ASSIGN: { // add LHS and RHS expressions - LHS may be a complex expression Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); return Lists.newArrayList(lhs, rhs); } case Token.VAR: { // recurse on all children List nodes = Lists.newArrayList(); for (Node child : n.children()) { nodes.addAll(getRhsSubexpressions(child)); } return nodes; } default: throw new IllegalArgumentException("AstChangeProxy::getRhs " + n); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameAnonymousFunctions.java0000644000175000017500000000601112115204405030157 0ustar apoapo/* * Copyright 2004 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.rhino.Node; import java.util.logging.*; /** * Gives anonymous function names. This makes it way easier to debug because * debuggers and stack traces use the function names. So if you have * * goog.string.htmlEscape = function(str) { * } * * It will become * * goog.string.htmlEscape = function $goog$string$htmlEscape$(str) { * } * */ class NameAnonymousFunctions implements CompilerPass { private static final Logger logger = Logger.getLogger( NameAnonymousFunctions.class.getName()); static final char DELIMITER = '$'; private final AbstractCompiler compiler; private int namedCount = 0; private int bytesUsed = 0; NameAnonymousFunctions(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { AnonymousFunctionNamingCallback namingCallback = new AnonymousFunctionNamingCallback(new AnonymousFunctionNamer()); NodeTraversal.traverse(compiler, root, namingCallback); logger.fine("Named " + namedCount + " anon functions using " + bytesUsed + " bytes"); } /** * Names anonymous functions. The function names don't have to be globally * unique or even locally unique. We make them somewhat unique because of a * bug in IE (and there may be other bugs we haven't found). See unit test for * more info. */ private class AnonymousFunctionNamer implements AnonymousFunctionNamingCallback.FunctionNamer { private NodeNameExtractor nameExtractor; AnonymousFunctionNamer() { this.nameExtractor = new NodeNameExtractor(DELIMITER); } /** * Returns a likely not conflicting name to make IE happy. See unit test * for more info. */ private String getLikelyNonConflictingName(String name) { return DELIMITER + name + DELIMITER; } @Override public final String getName(Node node) { return nameExtractor.getName(node); } @Override public final void setFunctionName(String name, Node fnNode) { Node fnNameNode = fnNode.getFirstChild(); String uniqueName = getLikelyNonConflictingName(name); fnNameNode.setString(uniqueName); compiler.reportCodeChange(); namedCount++; bytesUsed += uniqueName.length(); } @Override public final String getCombinedName(String lhs, String rhs) { return lhs + DELIMITER + rhs; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/XtbMessageBundle.java0000644000175000017500000001614112115204405026676 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import java.io.*; import java.util.*; import javax.annotation.Nullable; import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** * A MessageBundle that parses messages from an XML Translation Bundle (XTB) * file. * */ @SuppressWarnings("sunapi") public class XtbMessageBundle implements MessageBundle { private static final SecureEntityResolver NOOP_RESOLVER = new SecureEntityResolver(); private final Map messages; private final JsMessage.IdGenerator idGenerator; public XtbMessageBundle( InputStream xtb, @Nullable String projectId, @SuppressWarnings("unused") boolean unused) { this(xtb, projectId); } /** * Creates an instance and initializes it with the messages in an XTB file. * * @param xtb the XTB file as a byte stream * @param projectId the translation console project id (i.e. name) */ public XtbMessageBundle(InputStream xtb, @Nullable String projectId) { Preconditions.checkState(!"".equals(projectId)); this.messages = Maps.newHashMap(); this.idGenerator = new GoogleJsMessageIdGenerator(projectId); try { // Use a SAX parser for speed and less memory usage. SAXParser parser = createSAXParser(); XMLReader reader = parser.getXMLReader(); Handler contentHandler = new Handler(); reader.setContentHandler(contentHandler); reader.parse(new InputSource(xtb)); } catch (ParserConfigurationException e) { throw new RuntimeException(e); } catch (SAXException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } // Inlined from guava-internal. private SAXParser createSAXParser() throws ParserConfigurationException, SAXException { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(false); factory.setXIncludeAware(false); factory.setFeature( "http://xml.org/sax/features/external-general-entities", false); factory.setFeature( "http://xml.org/sax/features/external-parameter-entities",false); factory.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); SAXParser parser = factory.newSAXParser(); XMLReader xmlReader = parser.getXMLReader(); xmlReader.setEntityResolver(NOOP_RESOLVER); return parser; } @Override public JsMessage getMessage(String id) { return messages.get(id); } @Override public JsMessage.IdGenerator idGenerator() { return idGenerator; } @Override public Iterable getAllMessages() { return Iterables.unmodifiableIterable(messages.values()); } /** * A {@link ContentHandler} that creates a {@link JsMessage} for each message * parsed from an XML Translation Bundle (XTB) file. */ private class Handler implements ContentHandler { private static final String BUNDLE_ELEM_NAME = "translationbundle"; private static final String LANG_ATT_NAME = "lang"; private static final String TRANSLATION_ELEM_NAME = "translation"; private static final String MESSAGE_ID_ATT_NAME = "id"; private static final String PLACEHOLDER_ELEM_NAME = "ph"; private static final String PLACEHOLDER_NAME_ATT_NAME = "name"; String lang; JsMessage.Builder msgBuilder; @Override public void setDocumentLocator(Locator locator) {} @Override public void startDocument() {} @Override public void endDocument() {} @Override public void startPrefixMapping(String prefix, String uri) {} @Override public void endPrefixMapping(String prefix) {} @Override public void startElement(String uri, String localName, String qName, Attributes atts) { if (BUNDLE_ELEM_NAME.equals(qName)) { Preconditions.checkState(lang == null); lang = atts.getValue(LANG_ATT_NAME); Preconditions.checkState(lang != null && !lang.isEmpty()); } else if (TRANSLATION_ELEM_NAME.equals(qName)) { Preconditions.checkState(msgBuilder == null); String id = atts.getValue(MESSAGE_ID_ATT_NAME); Preconditions.checkState(id != null && !id.isEmpty()); msgBuilder = new JsMessage.Builder(id); } else if (PLACEHOLDER_ELEM_NAME.equals(qName)) { Preconditions.checkState(msgBuilder != null); String phRef = atts.getValue(PLACEHOLDER_NAME_ATT_NAME); phRef = JsMessageVisitor.toLowerCamelCaseWithNumericSuffixes(phRef); msgBuilder.appendPlaceholderReference(phRef); } } @Override public void endElement(String uri, String localName, String qName) { if (TRANSLATION_ELEM_NAME.equals(qName)) { Preconditions.checkState(msgBuilder != null); if (!msgBuilder.hasParts()) { msgBuilder.appendStringPart(""); } String key = msgBuilder.getKey(); messages.put(key, msgBuilder.build()); msgBuilder = null; } } @Override public void characters(char ch[], int start, int length) { if (msgBuilder != null) { // Append a string literal to the message. msgBuilder.appendStringPart(String.valueOf(ch, start, length)); } } @Override public void ignorableWhitespace(char ch[], int start, int length) { if (msgBuilder != null) { // Preserve whitespace in messages. msgBuilder.appendStringPart(String.valueOf(ch, start, length)); } } @Override public void processingInstruction(String target, String data) {} @Override public void skippedEntity(String name) {} } /** * A secure EntityResolver that returns an empty string in response to * any attempt to resolve an external entity. The class is used by our * secure version of the internal saxon SAX parser. */ private static final class SecureEntityResolver implements EntityResolver { @Override public InputSource resolveEntity(String publicId, String systemId) { return new InputSource(new StringReader("")); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java0000644000175000017500000001525012115204405030644 0ustar apoapo/* * 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.base.CaseFormat; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.JsMessage.IdGenerator; import com.google.javascript.jscomp.JsMessage.PlaceholderReference; import java.util.List; /** * An {@link IdGenerator} designed to play nicely with Google's Translation * systems. Each message is scoped to a project id, so that it does * not conflict with other messages at Google. *

    * Just as reminder what key type used in different formats: *

      *
    1. XMB - id. We export using this format. *
    2. XTB - id. Internal, result of translation. *
    3. XLB - name. External, use it if we need to share translation with third * part. *
    4. PROPERTIES - name. *
    * * @see xmb */ public class GoogleJsMessageIdGenerator implements IdGenerator { private final String projectId; /** * Creates an instance. * * @param projectId A TC project name (e.g. "MyProject") */ public GoogleJsMessageIdGenerator(String projectId) { this.projectId = projectId; } @Override public String generateId(String meaning, List messageParts) { Preconditions.checkState(meaning != null); StringBuilder sb = new StringBuilder(); for (CharSequence part : messageParts) { if (part instanceof PlaceholderReference) { sb.append(CaseFormat.LOWER_CAMEL.to( CaseFormat.UPPER_UNDERSCORE, ((PlaceholderReference) part).getName())); } else { sb.append(part); } } String tcValue = sb.toString(); String projectScopedMeaning = (projectId != null ? (projectId + ": ") : "") + meaning; return String.valueOf( MessageId.GenerateId(tcValue, projectScopedMeaning)); } /** * 64-bit fingerprint support. * * Forked from the guava-internal library. */ private static final class FP { private FP() {} /** Generate fingerprint of "byte[start,limit-1]". */ private static long fingerprint(byte[] str, int start, int limit) { int hi = hash32(str, start, limit, 0); int lo = hash32(str, start, limit, 102072); if ((hi == 0) && (lo == 0 || lo == 1)) { // Turn 0/1 into another fingerprint hi ^= 0x130f9bef; lo ^= 0x94a0a928; } return (((long) hi) << 32) | (lo & 0xffffffffl); } /** * Generate fingerprint of "str". Equivalent to UTF-encoding "str" into * bytes and then fingerprinting those bytes. */ private static long fingerprint(String str) { byte[] tmp = str.getBytes(Charsets.UTF_8); return FP.fingerprint(tmp, 0, tmp.length); } @SuppressWarnings("fallthrough") private static int hash32(byte[] str, int start, int limit, int c) { int a = 0x9e3779b9; int b = 0x9e3779b9; int i; for (i = start; i + 12 <= limit; i += 12) { a += (((str[i + 0] & 0xff) << 0) | ((str[i + 1] & 0xff) << 8) | ((str[i + 2] & 0xff) << 16) | ((str[i + 3] & 0xff) << 24)); b += (((str[i + 4] & 0xff) << 0) | ((str[i + 5] & 0xff) << 8) | ((str[i + 6] & 0xff) << 16) | ((str[i + 7] & 0xff) << 24)); c += (((str[i + 8] & 0xff) << 0) | ((str[i + 9] & 0xff) << 8) | ((str[i + 10] & 0xff) << 16) | ((str[i + 11] & 0xff) << 24)); // Mix a -= b; a -= c; a ^= (c >>> 13); b -= c; b -= a; b ^= (a << 8); c -= a; c -= b; c ^= (b >>> 13); a -= b; a -= c; a ^= (c >>> 12); b -= c; b -= a; b ^= (a << 16); c -= a; c -= b; c ^= (b >>> 5); a -= b; a -= c; a ^= (c >>> 3); b -= c; b -= a; b ^= (a << 10); c -= a; c -= b; c ^= (b >>> 15); } c += limit - start; switch (limit - i) { // deal with rest. Cases fall through case 11: c += (str[i + 10] & 0xff) << 24; case 10: c += (str[i + 9] & 0xff) << 16; case 9: c += (str[i + 8] & 0xff) << 8; // the first byte of c is reserved for the length case 8: b += (str[i + 7] & 0xff) << 24; case 7: b += (str[i + 6] & 0xff) << 16; case 6: b += (str[i + 5] & 0xff) << 8; case 5: b += (str[i + 4] & 0xff); case 4: a += (str[i + 3] & 0xff) << 24; case 3: a += (str[i + 2] & 0xff) << 16; case 2: a += (str[i + 1] & 0xff) << 8; case 1: a += (str[i + 0] & 0xff); // case 0 : nothing left to add } // Mix a -= b; a -= c; a ^= (c >>> 13); b -= c; b -= a; b ^= (a << 8); c -= a; c -= b; c ^= (b >>> 13); a -= b; a -= c; a ^= (c >>> 12); b -= c; b -= a; b ^= (a << 16); c -= a; c -= b; c ^= (b >>> 5); a -= b; a -= c; a ^= (c >>> 3); b -= c; b -= a; b ^= (a << 10); c -= a; c -= b; c ^= (b >>> 15); return c; } } /** * Generates fingerprint for an English message using the FP package. * This supersedes the message id generation using C fingerprint * functions and JNI. This is slower than the C implementation ( * we're talking about microseconds here) but it avoids using JNI and * shared libraries.

    * * Forked from the i18n library. */ private static class MessageId { private final static long GenerateId(String message, String meaning) { long fp = FP.fingerprint(message); if (null != meaning && meaning.length() > 0) { // combine the fingerprints of message and meaning long fp2 = FP.fingerprint(meaning); fp = fp2 + (fp << 1) + (fp < 0 ? 1 : 0); } // To avoid negative ids we strip the high-order bit return fp & 0x7fffffffffffffffL; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ErrorManager.java0000644000175000017500000000344712115204405026073 0ustar apoapo/* * Copyright 2007 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.CheckLevel; /** * The error manager is in charge of storing, organizing and displaying * errors and warnings generated by the compiler. * */ public interface ErrorManager extends ErrorHandler { /** * Reports an error. The errors will be displayed by the * {@link #generateReport()} at the discretion of the implementation. * * @param level the reporting level * @param error the error to report */ @Override void report(CheckLevel level, JSError error); /** * Writes a report to an implementation-specific medium. The compiler calls * this method after any and all {@link #report} calls. */ void generateReport(); /** * Gets the number of reported errors. */ int getErrorCount(); /** * Gets the number of reported warnings. */ int getWarningCount(); /** * Gets all the errors. */ JSError[] getErrors(); /** * Gets all the warnings. */ JSError[] getWarnings(); /** * Sets the percentage of typed expressions. */ void setTypedPercent(double typedPercent); /** * Gets the percentage of typed expressions. */ double getTypedPercent(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CollapseAnonymousFunctions.java0000644000175000017500000000714312115204405031050 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; /** * Collapses anonymous function expressions into named function declarations, * i.e. the following: * *

     * var f = function()
     * 
     *
     * becomes:
     *
     * 
    function f()
    * * This reduces the generated code size but changes the semantics because f * will be defined before its definition is reached. * */ class CollapseAnonymousFunctions implements CompilerPass { private final AbstractCompiler compiler; public CollapseAnonymousFunctions(AbstractCompiler compiler) { Preconditions.checkArgument(compiler.getLifeCycleStage().isNormalized()); this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new Callback()); } private class Callback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isVar()) { return; } // It is only safe to collapse anonymous functions that appear // at top-level blocks. In other cases the difference between // variable and function declarations can lead to problems or // expose subtle bugs in browser implementation as function // definitions are added to scopes before the start of execution. Node grandparent = parent.getParent(); if (!(parent.isScript() || grandparent != null && grandparent.isFunction() && parent.isBlock())) { return; } // Need to store the next name in case the current name is removed from // the linked list. Preconditions.checkState(n.hasOneChild()); Node name = n.getFirstChild(); Node value = name.getFirstChild(); if (value != null && value.isFunction() && !isRecursiveFunction(value)) { Node fnName = value.getFirstChild(); fnName.setString(name.getString()); NodeUtil.copyNameAnnotations(name, fnName); name.removeChild(value); parent.replaceChild(n, value); // Renormalize the code. if (!t.inGlobalScope() && NodeUtil.isHoistedFunctionDeclaration(value)) { parent.addChildToFront(value.detachFromParent()); } compiler.reportCodeChange(); } } private boolean isRecursiveFunction(Node function) { Node name = function.getFirstChild(); if (name.getString().isEmpty()) { return false; } Node args = name.getNext(); Node body = args.getNext(); return containsName(body, name.getString()); } private boolean containsName(Node n, String name) { if (n.isName() && n.getString().equals(name)) { return true; } for (Node child : n.children()) { if (containsName(child, name)) { return true; } } return false; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DataFlowAnalysis.java0000644000175000017500000004530312115204405026711 0ustar apoapo/* * 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.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; /** * A framework to help writing static program analysis. A subclass of * this framework should specify how a single node changes the state * of a program. This class finds a safe estimate (a fixed-point) for * the whole program. The proven facts about the program will be * annotated with * {@link com.google.javascript.jscomp.graph.GraphNode#setAnnotation} to the * given control flow graph's nodes in form of {@link LatticeElement} * after calling {@link #analyze()}. * *

    As a guideline, the following is a list of behaviors that any analysis * can take: *

      *
    1. Flow Direction: Is the analysis a forward or backward analysis? *
    2. Lattice Elements: How does the analysis represent the state of the * program at any given point? *
    3. JOIN Operation: Given two incoming paths and a lattice state value, what * can the compiler conclude at the join point? *
    4. Flow Equations: How does an instruction modify the state of program in * terms of lattice values? *
    5. Initial Entry Value: What can the compiler assume at the beginning of the * program? *
    6. Initial Estimate: What can the compiler assume at each point of the * program? (What is the BOTTOM value of the lattice) By definition this lattice * JOIN {@code x} for any {@code x} must also be {@code x}. *
    * To make these behaviors known to the framework, the following steps must be * taken. *
      *
    1. Flow Direction: Implement {@link #isForward()}. *
    2. Lattice Elements: Implement {@link LatticeElement}. *
    3. JOIN Operation: Implement * {@link JoinOp#apply}. *
    4. Flow Equations: Implement * {@link #flowThrough(Object, LatticeElement)}. *
    5. Initial Entry Value: Implement {@link #createEntryLattice()}. *
    6. Initial Estimate: Implement {@link #createInitialEstimateLattice()}. *
    * *

    Upon execution of the {@link #analyze()} method, nodes of the input * control flow graph will be annotated with a {@link FlowState} object that * represents maximum fixed point solution. Any previous annotations at the * nodes of the control flow graph will be lost. * * * @param The control flow graph's node value type. * @param Lattice element type. */ abstract class DataFlowAnalysis { private final ControlFlowGraph cfg; final JoinOp joinOp; protected final Set> orderedWorkSet; /* * Feel free to increase this to a reasonable number if you are finding that * more and more passes need more than 200000 steps before finding a * fixed-point. If you just have a special case, consider calling * {@link #analyse(int)} instead. */ public static final int MAX_STEPS = 200000; /** * Constructs a data flow analysis. * *

    Typical usage *

       * DataFlowAnalysis dfa = ...
       * dfa.analyze();
       * 
    * * {@link #analyze()} annotates the result to the control flow graph by * means of {@link DiGraphNode#setAnnotation} without any * modification of the graph itself. Additional calls to {@link #analyze()} * recomputes the analysis which can be useful if the control flow graph * has been modified. * * @param targetCfg The control flow graph object that this object performs * on. Modification of the graph requires a separate call to * {@link #analyze()}. * * @see #analyze() */ DataFlowAnalysis(ControlFlowGraph targetCfg, JoinOp joinOp) { this.cfg = targetCfg; this.joinOp = joinOp; Comparator> nodeComparator = cfg.getOptionalNodeComparator(isForward()); if (nodeComparator != null) { this.orderedWorkSet = Sets.newTreeSet(nodeComparator); } else { this.orderedWorkSet = Sets.newLinkedHashSet(); } } /** * Returns the control flow graph that this analysis was performed on. * Modifications can be done on this graph, however, the only time that the * annotations are correct is after {@link #analyze()} is called and before * the graph has been modified. */ final ControlFlowGraph getCfg() { return cfg; } /** * Returns the lattice element at the exit point. */ L getExitLatticeElement() { DiGraphNode node = getCfg().getImplicitReturn(); FlowState state = node.getAnnotation(); return state.getIn(); } @SuppressWarnings("unchecked") protected L join(L latticeA, L latticeB) { return joinOp.apply(Lists.newArrayList(latticeA, latticeB)); } /** * Checks whether the analysis is a forward flow analysis or backward flow * analysis. * * @return {@code true} if it is a forward analysis. */ abstract boolean isForward(); /** * Computes the output state for a given node and input state. * * @param node The node. * @param input Input lattice that should be read-only. * @return Output lattice. */ abstract L flowThrough(N node, L input); /** * Finds a fixed-point solution using at most {@link #MAX_STEPS} * iterations. * * @see #analyze(int) */ final void analyze() { analyze(MAX_STEPS); } /** * Finds a fixed-point solution. The function has the side effect of replacing * the existing node annotations with the computed solutions using {@link * com.google.javascript.jscomp.graph.GraphNode#setAnnotation(Annotation)}. * *

    Initially, each node's input and output flow state contains the value * given by {@link #createInitialEstimateLattice()} (with the exception of the * entry node of the graph which takes on the {@link #createEntryLattice()} * value. Each node will use the output state of its predecessor and compute a * output state according to the instruction. At that time, any nodes that * depends on the node's newly modified output value will need to recompute * their output state again. Each step will perform a computation at one node * until no extra computation will modify any existing output state anymore. * * @param maxSteps Max number of iterations before the method stops and throw * a {@link MaxIterationsExceededException}. This will prevent the * analysis from going into a infinite loop. */ final void analyze(int maxSteps) { initialize(); int step = 0; while (!orderedWorkSet.isEmpty()) { if (step > maxSteps) { throw new MaxIterationsExceededException( "Analysis did not terminate after " + maxSteps + " iterations"); } DiGraphNode curNode = orderedWorkSet.iterator().next(); orderedWorkSet.remove(curNode); joinInputs(curNode); if (flow(curNode)) { // If there is a change in the current node, we want to grab the list // of nodes that this node affects. List> nextNodes = isForward() ? cfg.getDirectedSuccNodes(curNode) : cfg.getDirectedPredNodes(curNode); for (DiGraphNode nextNode : nextNodes) { if (nextNode != cfg.getImplicitReturn()) { orderedWorkSet.add(nextNode); } } } step++; } if (isForward()) { joinInputs(getCfg().getImplicitReturn()); } } /** * Gets the state of the initial estimation at each node. * * @return Initial state. */ abstract L createInitialEstimateLattice(); /** * Gets the incoming state of the entry node. * * @return Entry state. */ abstract L createEntryLattice(); /** * Initializes the work list and the control flow graph. */ protected void initialize() { // TODO(user): Calling clear doesn't deallocate the memory in a // LinkedHashSet. Consider creating a new work set if we plan to repeatedly // call analyze. orderedWorkSet.clear(); for (DiGraphNode node : cfg.getDirectedGraphNodes()) { node.setAnnotation(new FlowState(createInitialEstimateLattice(), createInitialEstimateLattice())); if (node != cfg.getImplicitReturn()) { orderedWorkSet.add(node); } } } /** * Performs a single flow through a node. * * @return {@code true} if the flow state differs from the previous state. */ protected boolean flow(DiGraphNode node) { FlowState state = node.getAnnotation(); if (isForward()) { L outBefore = state.out; state.out = flowThrough(node.getValue(), state.in); return !outBefore.equals(state.out); } else { L inBefore = state.in; state.in = flowThrough(node.getValue(), state.out); return !inBefore.equals(state.in); } } /** * Computes the new flow state at a given node's entry by merging the * output (input) lattice of the node's predecessor (successor). * * @param node Node to compute new join. */ protected void joinInputs(DiGraphNode node) { FlowState state = node.getAnnotation(); if (isForward()) { if (cfg.getEntry() == node) { state.setIn(createEntryLattice()); } else { List> inNodes = cfg.getDirectedPredNodes(node); if (inNodes.size() == 1) { FlowState inNodeState = inNodes.get(0).getAnnotation(); state.setIn(inNodeState.getOut()); } else if (inNodes.size() > 1) { List values = new ArrayList(inNodes.size()); for (DiGraphNode currentNode : inNodes) { FlowState currentNodeState = currentNode.getAnnotation(); values.add(currentNodeState.getOut()); } state.setIn(joinOp.apply(values)); } } } else { List> inNodes = cfg.getDirectedSuccNodes(node); if (inNodes.size() == 1) { DiGraphNode inNode = inNodes.get(0); if (inNode == cfg.getImplicitReturn()) { state.setOut(createEntryLattice()); } else { FlowState inNodeState = inNode.getAnnotation(); state.setOut(inNodeState.getIn()); } } else if (inNodes.size() > 1) { List values = new ArrayList(inNodes.size()); for (DiGraphNode currentNode : inNodes) { FlowState currentNodeState = currentNode.getAnnotation(); values.add(currentNodeState.getIn()); } state.setOut(joinOp.apply(values)); } } } /** * The in and out states of a node. * * @param Input and output lattice element type. */ static class FlowState implements Annotation { private L in; private L out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private FlowState(L inState, L outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) { Preconditions.checkNotNull(in); this.in = in; } L getOut() { return out; } void setOut(L out) { Preconditions.checkNotNull(out); this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * The exception to be thrown if the analysis has been running for a long * number of iterations. Chances are the analysis is not monotonic, a * fixed-point cannot be found and it is currently stuck in an infinite loop. */ static class MaxIterationsExceededException extends RuntimeException { private static final long serialVersionUID = 1L; MaxIterationsExceededException(String msg) { super(msg); } } abstract static class BranchedForwardDataFlowAnalysis extends DataFlowAnalysis { @Override protected void initialize() { orderedWorkSet.clear(); for (DiGraphNode node : getCfg().getDirectedGraphNodes()) { int outEdgeCount = getCfg().getOutEdges(node.getValue()).size(); List outLattices = Lists.newArrayList(); for (int i = 0; i < outEdgeCount; i++) { outLattices.add(createInitialEstimateLattice()); } node.setAnnotation(new BranchedFlowState( createInitialEstimateLattice(), outLattices)); if (node != getCfg().getImplicitReturn()) { orderedWorkSet.add(node); } } } BranchedForwardDataFlowAnalysis(ControlFlowGraph targetCfg, JoinOp joinOp) { super(targetCfg, joinOp); } /** * Returns the lattice element at the exit point. Needs to be overridden * because we use a BranchedFlowState instead of a FlowState; ugh. */ @Override L getExitLatticeElement() { DiGraphNode node = getCfg().getImplicitReturn(); BranchedFlowState state = node.getAnnotation(); return state.getIn(); } @Override final boolean isForward() { return true; } /** * The branched flow function maps a single lattice to a list of output * lattices. * *

    Each outgoing edge of a node will have a corresponding output lattice * in the ordered returned by * {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)} * in the returned list. * * @return A list of output values depending on the edge's branch type. */ abstract List branchedFlowThrough(N node, L input); @Override protected final boolean flow(DiGraphNode node) { BranchedFlowState state = node.getAnnotation(); List outBefore = state.out; state.out = branchedFlowThrough(node.getValue(), state.in); Preconditions.checkState(outBefore.size() == state.out.size()); for (int i = 0; i < outBefore.size(); i++) { if (!outBefore.get(i).equals(state.out.get(i))) { return true; } } return false; } @Override protected void joinInputs(DiGraphNode node) { BranchedFlowState state = node.getAnnotation(); List> predNodes = getCfg().getDirectedPredNodes(node); List values = new ArrayList(predNodes.size()); for (DiGraphNode predNode : predNodes) { BranchedFlowState predNodeState = predNode.getAnnotation(); L in = predNodeState.out.get( getCfg().getDirectedSuccNodes(predNode).indexOf(node)); values.add(in); } if (getCfg().getEntry() == node) { state.setIn(createEntryLattice()); } else if (!values.isEmpty()) { state.setIn(joinOp.apply(values)); } } } /** * The in and out states of a node. * * @param Input and output lattice element type. */ static class BranchedFlowState implements Annotation { private L in; private List out; /** * Private constructor. No other classes should create new states. * * @param inState Input. * @param outState Output. */ private BranchedFlowState(L inState, List outState) { Preconditions.checkNotNull(inState); Preconditions.checkNotNull(outState); this.in = inState; this.out = outState; } L getIn() { return in; } void setIn(L in) { Preconditions.checkNotNull(in); this.in = in; } List getOut() { return out; } void setOut(List out) { Preconditions.checkNotNull(out); for (L item : out) { Preconditions.checkNotNull(item); } this.out = out; } @Override public String toString() { return String.format("IN: %s OUT: %s", in, out); } @Override public int hashCode() { return Objects.hashCode(in, out); } } /** * Compute set of escaped variables. When a variable is escaped in a * dataflow analysis, it can be reference outside of the code that we are * analyzing. A variable is escaped if any of the following is true: * *

      *
    1. It is defined as the exception name in CATCH clause so it became a * variable local not to our definition of scope.
    2. *
    3. Exported variables as they can be needed after the script terminates. *
    4. *
    5. Names of named functions because in JavaScript, function foo(){} * does not kill foo in the dataflow.
    6. */ static void computeEscaped(final Scope jsScope, final Set escaped, AbstractCompiler compiler) { // TODO(user): Very good place to store this information somewhere. AbstractPostOrderCallback finder = new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (jsScope == t.getScope() || !n.isName() || parent.isFunction()) { return; } String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null && var.scope == jsScope) { escaped.add(jsScope.getVar(name)); } } }; NodeTraversal t = new NodeTraversal(compiler, finder); t.traverseAtScope(jsScope); // 1: Remove the exception name in CATCH which technically isn't local to // begin with. for (Iterator i = jsScope.getVars(); i.hasNext();) { Var var = i.next(); if (var.getParentNode().isCatch() || compiler.getCodingConvention().isExported(var.getName())) { escaped.add(var); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeOptimizationsPass.java0000644000175000017500000001320412115204405030661 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import java.util.ArrayList; /** * A compiler pass to run various peephole optimizations (e.g. constant folding, * some useless code removal, some minimizations). * * @author dcc@google.com (Devin Coughlin) * @author acleung@google.com (Alan Leung)( */ class PeepholeOptimizationsPass implements CompilerPass { private AbstractCompiler compiler; // Use an array here for faster iteration compared to ImmutableSet private final AbstractPeepholeOptimization[] peepholeOptimizations; // Track whether the a scope has been modified so that it can be revisited // immediately. private StateStack traversalState = new StateStack(); private boolean retraverseOnChange = true; private static class ScopeState { boolean changed; boolean traverseChildScopes; ScopeState() { reset(); } void reset() { changed = false; traverseChildScopes = true; } } private static class StateStack { private ArrayList states = Lists.newArrayList(); private int currentDepth = 0; StateStack() { states.add(new ScopeState()); } ScopeState peek() { return states.get(currentDepth); } void push() { currentDepth++; if (states.size() <= currentDepth) { states.add(new ScopeState()); } else { states.get(currentDepth).reset(); } } void pop() { currentDepth--; } } private class PeepholeChangeHandler extends CodeChangeHandler { @Override public void reportChange() { traversalState.peek().changed = true; } } /** * Creates a peephole optimization pass that runs the given * optimizations. */ PeepholeOptimizationsPass(AbstractCompiler compiler, AbstractPeepholeOptimization... optimizations) { this.compiler = compiler; this.peepholeOptimizations = optimizations; } PeepholeOptimizationsPass setRetraverseOnChange(boolean retraverse) { this.retraverseOnChange = retraverse; return this; } public AbstractCompiler getCompiler() { return compiler; } @Override public void process(Node externs, Node root) { PeepholeChangeHandler handler = new PeepholeChangeHandler(); compiler.addChangeHandler(handler); beginTraversal(); traverse(root); endTraversal(); compiler.removeChangeHandler(handler); } private void traverse(Node node) { // The goal here is to avoid retraversing // the entire AST to catch newly created opportunities. // So we track whether a "unit of code" has changed, // and revisit immediately. if (!shouldVisit(node)) { return; } int visits = 0; do { Node c = node.getFirstChild(); while (c != null) { Node next = c.getNext(); traverse(c); c = next; } visit(node); visits++; Preconditions.checkState(visits < 10000, "too many interations"); } while (shouldRetraverse(node)); exitNode(node); } private boolean shouldRetraverse(Node node) { if (retraverseOnChange && node.getParent() != null && (node.isFunction() || node.isScript())) { ScopeState state = traversalState.peek(); if (state.changed) { // prepare to re-visit the scope: // when revisiting, only visit the immediate scope // this reduces the cost of getting to a fixed // point in global scope. state.changed = false; state.traverseChildScopes = false; return true; } } return false; } private boolean shouldVisit(Node node) { if (node.isFunction() || node.isScript()) { ScopeState previous = traversalState.peek(); if (!previous.traverseChildScopes) { return false; } traversalState.push(); } return true; } private void exitNode(Node node) { if (node.isFunction() || node.isScript()) { traversalState.pop(); } } public void visit(Node n) { Node currentVersionOfNode = n; boolean somethingChanged = false; do { somethingChanged = false; for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { Node newVersionOfNode = optimization.optimizeSubtree(currentVersionOfNode); if (newVersionOfNode != currentVersionOfNode) { somethingChanged = true; currentVersionOfNode = newVersionOfNode; } if (currentVersionOfNode == null) { return; } } } while(somethingChanged); } /** * Make sure that all the optimizations have the current traversal so they * can report errors. */ private void beginTraversal() { for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { optimization.beginTraversal(compiler); } } private void endTraversal() { for (AbstractPeepholeOptimization optimization : peepholeOptimizations) { optimization.endTraversal(compiler); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JsMessageDefinition.java0000644000175000017500000000421312115204405027371 0ustar apoapo/* * 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.javascript.rhino.Node; /** * Container class that holds information about JS message source. * * This class is specific to our JsMessage syntax. Allows you to use the * new-style or the old-style messages. * * Old-style: * * var MSG_LEOPARD = 'Leopard'; * var MSG_LEOPARD_HELP = 'The Leopard operating system'; * * * New-style: * * /** @desc The leopard operating system * / * var MSG_LEOPARD = goog.getMsg('Leopard'); * * * @author anatol@google.com (Anatol Pomazau) */ class JsMessageDefinition { private final Node messageNode; private final Node messageParentNode; private final Node visitingNode; /** * Constructs JS message definition. * * @param visitingNode Node that is visited by * {@link JsMessageVisitor}. Take into * account that visiting node could differ from the node the message * was found. * @param messageNode A node that contains the message. It could be node with * goog.getMsg() call or string/function for old-style messages. * @param messageParentNode The parent of the message node. */ JsMessageDefinition(Node visitingNode, Node messageNode, Node messageParentNode) { this.messageNode = messageNode; this.visitingNode = visitingNode; this.messageParentNode = messageParentNode; } Node getMessageNode() { return messageNode; } Node getVisitingNode() { return visitingNode; } Node getMessageParentNode() { return messageParentNode; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CollapseVariableDeclarations.java0000644000175000017500000001550512115204405031246 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * Collapses multiple variable declarations into a single one. i.e the * following: * *
       * var a;
       * var b = 1;
       * var c = 2;
       * 
      * * becomes: * *
      var a, b = 1, c = 2;
      * * This reduces the generated code size. More optimizations are possible: *
    7. Group all variable declarations inside a function into one such variable. * declaration block.
    8. *
    9. Re-use variables instead of declaring a new one if they are used for * only part of a function.
    10. * * Similarly, also collapses assigns like: * *
       * a = true;
       * b = true;
       * var c = true;
       * 
      * * becomes: * *
      var c = b = a = true;
      * */ class CollapseVariableDeclarations implements CompilerPass { /** Reference to JS Compiler */ private final AbstractCompiler compiler; /** Encapsulation of information about a variable declaration collapse */ private static class Collapse { /** * Variable declaration that any following var nodes should be * collapsed into */ final Node startNode; /** * Last node (non-inclusive) of the chain of nodes to collapse. */ final Node endNode; /** Parent of the nodes to the collapse */ final Node parent; Collapse(Node startNode, Node endNode, Node parent) { this.startNode = startNode; this.endNode = endNode; this.parent = parent; } } /** * Collapses to do in this pass. */ private final List collapses = Lists.newArrayList(); /** * Nodes we've already looked at for collapsing, so that we don't look at them * again (we look ahead when examining what nodes can be collapsed, and the * node traversal may give them to us again) */ private final Set nodesToCollapse = Sets.newHashSet(); CollapseVariableDeclarations(AbstractCompiler compiler) { Preconditions.checkState(!compiler.getLifeCycleStage().isNormalized()); this.compiler = compiler; } @Override public void process(Node externs, Node root) { collapses.clear(); nodesToCollapse.clear(); NodeTraversal.traverse(compiler, root, new GatherCollapses()); if (!collapses.isEmpty()) { applyCollapses(); compiler.reportCodeChange(); } } /** * Gathers all of the variable declarations / assignments that should be * collapsed into one. * * We do not do the collapsing as we go since node traversal would be affected * by the changes we are making to the parse tree. */ private class GatherCollapses extends AbstractPostOrderCallback { // If a VAR is declared like // var x; // then we should not create new VAR nodes for it later in the tree. // This is a workaround for a bug in Firefox. private final Set blacklistedVars = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isVar()) { blacklistStubVars(t, n); } // Only care about var nodes if (!n.isVar() && !canBeRedeclared(n, t.getScope())) return; // If we've already looked at this node, skip it if (nodesToCollapse.contains(n)) return; // Adjacent VAR children of an IF node are the if and else parts and can't // be collapsed if (parent.isIf()) return; Node varNode = n; boolean hasVar = n.isVar(); // Find variable declarations that follow this one (if any) n = n.getNext(); boolean hasNodesToCollapse = false; while (n != null && (n.isVar() || canBeRedeclared(n, t.getScope()))) { if (n.isVar()) { blacklistStubVars(t, n); hasVar = true; } nodesToCollapse.add(n); hasNodesToCollapse = true; n = n.getNext(); } if (hasNodesToCollapse && hasVar) { nodesToCollapse.add(varNode); collapses.add(new Collapse(varNode, n, parent)); } } private void blacklistStubVars(NodeTraversal t, Node varNode) { for (Node child = varNode.getFirstChild(); child != null; child = child.getNext()) { if (child.getFirstChild() == null) { blacklistedVars.add(t.getScope().getVar(child.getString())); } } } private boolean canBeRedeclared(Node n, Scope s) { if (!NodeUtil.isExprAssign(n)) { return false; } Node assign = n.getFirstChild(); Node lhs = assign.getFirstChild(); if (!lhs.isName()) { return false; } Var var = s.getVar(lhs.getString()); return var != null && var.getScope() == s && !isNamedParameter(var) && !blacklistedVars.contains(var); } } private boolean isNamedParameter(Var v) { return v.getParentNode().isParamList(); } private void applyCollapses() { for (Collapse collapse : collapses) { Node var = new Node(Token.VAR); var.copyInformationFrom(collapse.startNode); collapse.parent.addChildBefore(var, collapse.startNode); boolean redeclaration = false; for (Node n = collapse.startNode; n != collapse.endNode;) { Node next = n.getNext(); Preconditions.checkState(var.getNext() == n); collapse.parent.removeChildAfter(var); if (n.isVar()) { while(n.hasChildren()) { var.addChildToBack(n.removeFirstChild()); } } else { Node assign = n.getFirstChild(); Node lhs = assign.getFirstChild(); Preconditions.checkState(lhs.isName()); Node rhs = assign.getLastChild(); lhs.addChildToBack(rhs.detachFromParent()); var.addChildToBack(lhs.detachFromParent()); redeclaration = true; } n = next; } if (redeclaration) { JSDocInfo info = new JSDocInfo(); info.addSuppression("duplicate"); var.setJSDocInfo(info); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodingConvention.java0000644000175000017500000003216512115204405026754 0ustar apoapo/* * Copyright 2007 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.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; /** * CodingConvention defines a set of hooks to customize the behavior of the * Compiler for a specific team/company. * */ public interface CodingConvention extends Serializable { /** * This checks whether a given variable name, such as a name in all-caps * should be treated as if it had the @const annotation. * * @param variableName potentially constant variable name * @return {@code true} if the name should be treated as a constant. */ public boolean isConstant(String variableName); /** * This checks whether a given key of an object literal, such as a * name in all-caps should be treated as if it had the @const * annotation. */ public boolean isConstantKey(String keyName); /** * This checks that a given {@code key} may be used as a key for an enum. * * @param key the potential key to an enum * @return {@code true} if the {@code key} may be used as an enum key, * {@code false} otherwise */ public boolean isValidEnumKey(String key); /** * This checks whether a given parameter name should be treated as an * optional parameter as far as type checking or function call arg count * checking is concerned. Note that an optional function parameter may be * declared as a simple type and is automatically converted to a union of the * declared type and Undefined. * * @param parameter The parameter's node. * @return {@code true} if the parameter should be treated as an optional * parameter. */ public boolean isOptionalParameter(Node parameter); /** * This checks whether a given parameter should be treated as a marker * for a variable argument list function. A VarArgs parameter must be the * last parameter in a function declaration. * * @param parameter The parameter's node. * @return {@code true} if the parameter should be treated as a variable * length parameter. */ public boolean isVarArgsParameter(Node parameter); /** * Checks whether a global variable or function name should be treated as * exported, or externally referenceable. * * @param name A global variable or function name. * @param local {@code true} if the name is a local variable. * @return {@code true} if the name should be considered exported. */ public boolean isExported(String name, boolean local); /** * Should be isExported(name, true) || isExported(name, false); */ public boolean isExported(String name); /** * Checks whether a name should be considered private. Private global * variables and functions can only be referenced within the source file in * which they are declared. Private properties and methods should only be * accessed by the class that defines them. * * @param name The name of a global variable or function, or a method or * property. * @return {@code true} if the name should be considered private. */ public boolean isPrivate(String name); /** * Checks if the given method defines a subclass relationship, * and if it does, returns information on that relationship. By default, * always returns null. Meant to be overridden by subclasses. * * @param callNode A CALL node. */ public SubclassRelationship getClassesDefinedByCall(Node callNode); /** * Returns true if passed a string referring to the superclass. The string * will usually be from the string node at the right of a GETPROP, e.g. * this.superClass_. */ public boolean isSuperClassReference(String propertyName); /** * Convenience method for determining provided dependencies amongst different * JS scripts. */ public String extractClassNameIfProvide(Node node, Node parent); /** * Convenience method for determining required dependencies amongst different * JS scripts. */ public String extractClassNameIfRequire(Node node, Node parent); /** * Function name used when exporting properties. * Signature: fn(object, publicName, symbol). * @return function name. */ public String getExportPropertyFunction(); /** * Function name used when exporting symbols. * Signature: fn(publicPath, object). * @return function name. */ public String getExportSymbolFunction(); /** * Checks if the given CALL node is forward-declaring any types, * and returns the name of the types if it is. */ public List identifyTypeDeclarationCall(Node n); /** * In many JS libraries, the function that produces inheritance also * adds properties to the superclass and/or subclass. */ public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type); /** * Function name for abstract methods. An abstract method can be assigned to * an interface method instead of an function expression in order to avoid * linter warnings produced by assigning a function without a return value * where a return value is expected. * @return function name. */ public String getAbstractMethodName(); /** * Checks if the given method defines a singleton getter, and if it does, * returns the name of the class with the singleton getter. By default, always * returns null. Meant to be overridden by subclasses. * * addSingletonGetter needs a coding convention because in the general case, * it can't be inlined. The function inliner sees that it creates an alias * to the given class in an inner closure, and bails out. * * @param callNode A CALL node. */ public String getSingletonGetterClassName(Node callNode); /** * In many JS libraries, the function that adds a singleton getter to a class * adds properties to the class. */ public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType); /** * @return Whether the function is inlinable by convention. */ public boolean isInlinableFunction(Node n); /** * @return the delegate relationship created by the call or null. */ public DelegateRelationship getDelegateRelationship(Node callNode); /** * In many JS libraries, the function that creates a delegate relationship * also adds properties to the delegator and delegate base. */ public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate); /** * @return the name of the delegate superclass. */ public String getDelegateSuperclassName(); /** * Checks for function calls that set the calling conventions on delegate * methods. */ public void checkForCallingConventionDefiningCalls( Node n, Map delegateCallingConventions); /** * Defines the delegate proxy prototype properties. Their types depend on * properties of the delegate base methods. * * @param delegateProxyPrototypes List of delegate proxy prototypes. */ public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope scope, List delegateProxyPrototypes, Map delegateCallingConventions); /** * Gets the name of the global object. */ public String getGlobalObject(); /** * A Bind instance or null. */ public Bind describeFunctionBind(Node n); /** * A Bind instance or null. * @param useTypeInfo If we believe type information is reliable enough * to use to figure out what the bind function is. */ public Bind describeFunctionBind(Node n, boolean useTypeInfo); public static class Bind { // The target of the bind action final Node target; // The node representing the "this" value, maybe null final Node thisValue; // The head of a Node list representing the parameters final Node parameters; public Bind(Node target, Node thisValue, Node parameters) { this.target = target; this.thisValue = thisValue; this.parameters = parameters; } /** * The number of parameters bound (not including the 'this' value). */ int getBoundParameterCount() { if (parameters == null) { return 0; } Node paramParent = parameters.getParent(); return paramParent.getChildCount() - paramParent.getIndexOfChild(parameters); } } /** * Whether this CALL function is testing for the existence of a property. */ public boolean isPropertyTestFunction(Node call); /** * Whether this GETPROP node is an alias for an object prototype. */ public boolean isPrototypeAlias(Node getProp); /** * Checks if the given method performs a object literal cast, and if it does, * returns information on the cast. By default, always returns null. Meant * to be overridden by subclasses. * * @param callNode A CALL node. */ public ObjectLiteralCast getObjectLiteralCast(Node callNode); /** * Gets a collection of all properties that are defined indirectly on global * objects. (For example, Closure defines superClass_ in the goog.inherits * call). */ public Collection getIndirectlyDeclaredProperties(); /** * Returns the set of AssertionFunction. */ public Collection getAssertionFunctions(); static enum SubclassType { INHERITS, MIXIN } static class SubclassRelationship { final SubclassType type; final String subclassName; final String superclassName; public SubclassRelationship(SubclassType type, Node subclassNode, Node superclassNode) { this.type = type; this.subclassName = subclassNode.getQualifiedName(); this.superclassName = superclassNode.getQualifiedName(); } } /** * Delegates provides a mechanism and structure for identifying where classes * can call out to optional code to augment their functionality. The optional * code is isolated from the base code through the use of a subclass in the * optional code derived from the delegate class in the base code. */ static class DelegateRelationship { /** The subclass in the base code. */ final String delegateBase; /** The class in the base code. */ final String delegator; DelegateRelationship(String delegateBase, String delegator) { this.delegateBase = delegateBase; this.delegator = delegator; } } /** * An object literal cast provides a mechanism to cast object literals to * other types without a warning. */ static class ObjectLiteralCast { /** Type to cast to. */ final String typeName; /** Object to cast. */ final Node objectNode; /** Error message */ final DiagnosticType diagnosticType; ObjectLiteralCast(String typeName, Node objectNode, DiagnosticType diagnosticType) { this.typeName = typeName; this.objectNode = objectNode; this.diagnosticType = diagnosticType; } } /** * A function that will throw an exception when either: * -One or more of its parameters evaluate to false. * -One or more of its parameters are not of a certain type. */ public class AssertionFunctionSpec { protected final String functionName; protected final JSTypeNative assertedType; public AssertionFunctionSpec(String functionName) { this(functionName, null); } public AssertionFunctionSpec(String functionName, JSTypeNative assertedType) { this.functionName = functionName; this.assertedType = assertedType; } /** Returns the name of the function. */ public String getFunctionName() { return functionName; } /** * Returns the parameter of the assertion function that is being checked. * @param firstParam The first parameter of the function call. */ public Node getAssertedParam(Node firstParam) { return firstParam; } /** * Returns the type for a type assertion, or null if the function asserts * that the node must not be null or undefined. * @param call The asserting call */ public JSType getAssertedType(Node call, JSTypeRegistry registry) { return assertedType != null ? registry.getNativeType(assertedType) : null; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AngularPass.java0000644000175000017500000002117112115204405025721 0ustar apoapo/* * Copyright 2012 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayList; import java.util.List; /** * Compiler pass for AngularJS-specific needs. Generates {@code $inject} \ * properties for functions (class constructors, wrappers, etc) annotated with * @ngInject. * *

      For example, the following code:

      *
      {@code
       *
       * /** @ngInject * /
       * function Controller(dependency1, dependency2) {
       *   // do something
       * }
       *
       * }
      * *

      will be transformed into: *

      {@code
       *
       * function Controller(dependency1, dependency2) {
       *   // do something
       * }
       * Controller.$inject = ['dependency1', 'dependency2'];
       *
       * }
      * *

      This pass also supports assignments of function expressions to variables * like: *

      {@code
       *
       * /** @ngInject * /
       * var filter = function(a, b) {};
       *
       * var ns = {};
       * /** @ngInject * /
       * ns.method = function(a,b,c) {};
       *
       * /** @ngInject * /
       * var shorthand = ns.method2 = function(a,b,c,) {}
       *
       * }
      */ class AngularPass extends AbstractPostOrderCallback implements CompilerPass { final AbstractCompiler compiler; /** Nodes annotated with @ngInject */ private List injectables = new ArrayList(); public AngularPass(AbstractCompiler compiler) { this.compiler = compiler; } public static final String INJECT_PROPERTY_NAME = "$inject"; static final DiagnosticType INJECT_IN_NON_GLOBAL_OR_BLOCK_ERROR = DiagnosticType.error("JSC_INJECT_IN_NON_GLOBAL_OR_BLOCK_ERROR", "@ngInject only applies to functions defined in blocks or " + "global scope."); static final DiagnosticType INJECT_NON_FUNCTION_ERROR = DiagnosticType.error("JSC_INJECT_NON_FUNCTION_ERROR", "@ngInject can only be used when defining a function or " + "assigning a function expression."); static final DiagnosticType FUNCTION_NAME_ERROR = DiagnosticType.error("JSC_FUNCTION_NAME_ERROR", "Unable to determine target function name for @ngInject."); @Override public void process(Node externs, Node root) { // Traverses AST looking for nodes annotated with @ngInject. NodeTraversal.traverse(compiler, root, this); CodingConvention convention = compiler.getCodingConvention(); boolean codeChanged = false; // iterates through annotated nodes adding $inject property to elements. for (NodeContext entry : injectables) { String name = entry.getName(); Node fn = entry.getFunctionNode(); List dependencies = createDependenciesList(fn); // skips entry if it does have any dependencies. if (dependencies.size() == 0) { continue; } Node dependenciesArray = IR.arraylit(dependencies.toArray( new Node[dependencies.size()])); // creates `something.$inject = ['param1', 'param2']` node. Node statement = IR.exprResult( IR.assign( IR.getelem( NodeUtil.newQualifiedNameNode(convention, name), IR.string(INJECT_PROPERTY_NAME)), dependenciesArray ) ); // adds `something.$inject = [...]` node after the annotated node. Node target = entry.getTarget(); target.getParent().addChildAfter(statement, target); codeChanged = true; } if (codeChanged) { compiler.reportCodeChange(); } } /** * Given a FUNCTION node returns array of STRING nodes representing function * parameters. * @param n the FUNCTION node. * @return STRING nodes. */ private List createDependenciesList(Node n) { Preconditions.checkArgument(n.isFunction()); Node params = NodeUtil.getFunctionParameters(n); if (params != null) { return createStringsFromParamList(params); } return Lists.newArrayList(); } /** * Given a PARAM_LIST node creates an array of corresponding STRING nodes. * @param params PARAM_LIST node. * @return array of STRING nodes. */ private List createStringsFromParamList(Node params) { Node param = params.getFirstChild(); ArrayList names = Lists.newArrayList(); while (param != null && param.isName()) { names.add(IR.string(param.getString()).srcref(param)); param = param.getNext(); } return names; } @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo docInfo = n.getJSDocInfo(); if (docInfo != null && docInfo.isNgInject()) { addNode(n, t); } } /** * Add node to the list of injectables. * @param n node to add. * @param t node traversal instance. */ private void addNode(Node n, NodeTraversal t) { Node target = null; Node fn = null; String name = null; switch (n.getType()) { // handles assignment cases like: // a = function() {} // a = b = c = function() {} case Token.ASSIGN: name = n.getFirstChild().getQualifiedName(); // last node of chained assignment. fn = n; while (fn.isAssign()) { fn = fn.getLastChild(); } target = n.getParent(); break; // handles function case: // function fnName() {} case Token.FUNCTION: name = NodeUtil.getFunctionName(n); fn = n; target = n; break; // handles var declaration cases like: // var a = function() {} // var a = b = function() {} case Token.VAR: name = n.getFirstChild().getString(); // looks for a function node. fn = getDeclarationRValue(n); target = n; break; } // checks that the declaration took place in a block or in a global scope. if (!target.getParent().isScript() && !target.getParent().isBlock()) { compiler.report(t.makeError(n, INJECT_IN_NON_GLOBAL_OR_BLOCK_ERROR)); return; } // checks that it is a function declaration. if (fn == null || !fn.isFunction()) { compiler.report(t.makeError(n, INJECT_NON_FUNCTION_ERROR)); return; } // checks that name is present, which must always be the case unless the // compiler allowed a syntax error or a dangling anonymous function // expression. Preconditions.checkNotNull(name); // registers the node. injectables.add(new NodeContext(name, n, fn, target)); } /** * Given a VAR node (variable declaration) returns the node of initial value. * *
      {@code
         * var x;  // null
         * var y = "value"; // STRING "value" node
         * var z = x = y = function() {}; // FUNCTION node
         * }
      * @param n VAR node. * @return the assigned intial value, or the rightmost rvalue of an assignment * chain, or null. */ private Node getDeclarationRValue(Node n) { Preconditions.checkNotNull(n); Preconditions.checkArgument(n.isVar()); n = n.getFirstChild().getFirstChild(); if (n == null) { return null; } while (n.isAssign()) { n = n.getLastChild(); } return n; } class NodeContext { /** Name of the function/object. */ private String name; /** Node jsDoc is attached to. */ private Node node; /** Function node */ private Node functionNode; /** Node after which to inject the new code */ private Node target; public NodeContext(String name, Node node, Node functionNode, Node target) { this.name = name; this.node = node; this.functionNode = functionNode; this.target = target; } /** * @return the name. */ public String getName() { return name; } /** * @return the node. */ public Node getNode() { return node; } /** * @return the context. */ public Node getFunctionNode() { return functionNode; } /** * @return the context. */ public Node getTarget() { return target; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CreateSyntheticBlocks.java0000644000175000017500000001501012115204405027730 0ustar apoapo/* * 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.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import javax.annotation.Nullable; /** * Creates synthetic blocks to optimizations from moving code * past markers in the source. * */ class CreateSyntheticBlocks implements CompilerPass { static final DiagnosticType UNMATCHED_START_MARKER = DiagnosticType.warning( "JSC_UNMATCHED_START_MARKER", "Unmatched {0}"); static final DiagnosticType UNMATCHED_END_MARKER = DiagnosticType.warning( "JSC_UNMATCHED_END_MARKER", "Unmatched {1} - {0} not in the same block"); static final DiagnosticType INVALID_MARKER_USAGE = DiagnosticType.warning( "JSC_INVALID_MARKER_USAGE", "Marker {0} can only be used in a simple " + "call expression"); private final AbstractCompiler compiler; /** Name of the start marker. */ private final String startMarkerName; /** Name of the end marker. */ private final String endMarkerName; /** * Markers can be nested. */ private final Deque markerStack = new ArrayDeque(); private final List validMarkers = Lists.newArrayList(); private class Marker { final Node startMarker; final Node endMarker; public Marker(Node startMarker, Node endMarker) { this.startMarker = startMarker; this.endMarker = endMarker; } } public CreateSyntheticBlocks(AbstractCompiler compiler, String startMarkerName, String endMarkerName) { this.compiler = compiler; this.startMarkerName = startMarkerName; this.endMarkerName = endMarkerName; } @Override public void process(Node externs, Node root) { // Find and validate the markers. NodeTraversal.traverse(compiler, root, new Callback()); // Complain about any unmatched markers. for (Node node : markerStack) { compiler.report( JSError.make(NodeUtil.getSourceName(node), node, UNMATCHED_START_MARKER, startMarkerName)); } // Add the block for the valid marker sets. for (Marker marker : validMarkers) { addBlocks(marker); } } /** * @param marker The marker to add synthetic blocks for. */ private void addBlocks(Marker marker) { // Add block around the template section so that it looks like this: // BLOCK (synthetic) // START // BLOCK (synthetic) // BODY // END // This prevents the start or end markers from mingling with the code // in the block body. Node originalParent = marker.endMarker.getParent(); Node outerBlock = IR.block(); outerBlock.setIsSyntheticBlock(true); originalParent.addChildBefore(outerBlock, marker.startMarker); Node innerBlock = IR.block(); innerBlock.setIsSyntheticBlock(true); // Move everything after the start Node up to the end Node into the inner // block. moveSiblingExclusive(originalParent, innerBlock, marker.startMarker, marker.endMarker); // Add the start node. outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); // Add the inner block outerBlock.addChildToBack(innerBlock); // and finally the end node. outerBlock.addChildToBack(originalParent.removeChildAfter(outerBlock)); compiler.reportCodeChange(); } /** * Move the Nodes between start and end from the source block to the * destination block. If start is null, move the first child of the block. * If end is null, move the last child of the block. */ private void moveSiblingExclusive( Node src, Node dest, @Nullable Node start, @Nullable Node end) { while (childAfter(src, start) != end) { Node child = removeChildAfter(src, start); dest.addChildToBack(child); } } /** * Like Node.getNext, that null is used to signal the child before the * block. */ private Node childAfter(Node parent, @Nullable Node siblingBefore) { if (siblingBefore == null) { return parent.getFirstChild(); } else { return siblingBefore.getNext(); } } /** * Like removeChildAfter, the firstChild is removed */ private Node removeChildAfter(Node parent, @Nullable Node siblingBefore) { if (siblingBefore == null) { return parent.removeFirstChild(); } else { return parent.removeChildAfter(siblingBefore); } } private class Callback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall() || !n.getFirstChild().isName()) { return; } Node callTarget = n.getFirstChild(); String callName = callTarget.getString(); if (startMarkerName.equals(callName)) { if (!parent.isExprResult()) { compiler.report( t.makeError(n, INVALID_MARKER_USAGE, startMarkerName)); return; } markerStack.push(parent); return; } if (!endMarkerName.equals(callName)) { return; } Node endMarkerNode = parent; if (!endMarkerNode.isExprResult()) { compiler.report( t.makeError(n, INVALID_MARKER_USAGE, endMarkerName)); return; } if (markerStack.isEmpty()) { compiler.report(t.makeError(n, UNMATCHED_END_MARKER, startMarkerName, endMarkerName)); return; } Node startMarkerNode = markerStack.pop(); if (endMarkerNode.getParent() != startMarkerNode.getParent()) { // The end marker isn't in the same block as the start marker. compiler.report(t.makeError(n, UNMATCHED_END_MARKER, startMarkerName, endMarkerName)); return; } // This is a valid marker set add it to the list of markers to process. validMarkers.add(new Marker(startMarkerNode, endMarkerNode)); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AstChangeProxy.java0000644000175000017500000000631712115204405026405 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; /** * Proxy that provides a high level interface that compiler passes can * use to replace or remove sections of the AST. * */ class AstChangeProxy { /** * Interface used to notify client code about changes done by * AstChangeProxy. */ interface ChangeListener { /** * Notifies clients about node removals. */ void nodeRemoved(Node node); } private final List listeners; AstChangeProxy() { listeners = Lists.newArrayList(); } /** * Registers a change listener. */ final void registerListener(ChangeListener listener) { listeners.add(listener); } /** * Unregisters a change listener. */ final void unregisterListener(ChangeListener listener) { listeners.remove(listener); } /** * Notifies listeners about a removal. */ private void notifyOfRemoval(Node node) { for (ChangeListener listener : listeners) { listener.nodeRemoved(node); } } /** * Removes a node from the parent's child list. */ final void removeChild(Node parent, Node node) { parent.removeChild(node); notifyOfRemoval(node); } /** * Replaces a node from the parent's child list. */ final void replaceWith(Node parent, Node node, Node replacement) { replaceWith(parent, node, Lists.newArrayList(replacement)); } /** * Replaces a node with the provided list. */ final void replaceWith(Node parent, Node node, List replacements) { Preconditions.checkNotNull(replacements, "\"replacements\" is null."); int size = replacements.size(); if ((size == 1) && node.isEquivalentTo(replacements.get(0))) { // trees are equal... don't replace return; } int parentType = parent.getType(); Preconditions.checkState(size == 1 || parentType == Token.BLOCK || parentType == Token.SCRIPT || parentType == Token.LABEL); if (parentType == Token.LABEL && size != 1) { Node block = IR.block(); for (Node newChild : replacements) { newChild.copyInformationFrom(node); block.addChildToBack(newChild); } parent.replaceChild(node, block); } else { for (Node newChild : replacements) { newChild.copyInformationFrom(node); parent.addChildBefore(newChild, node); } parent.removeChild(node); } notifyOfRemoval(node); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/OptimizeCalls.java0000644000175000017500000000345012115204405026260 0ustar apoapo/* * 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.common.collect.Lists; import com.google.javascript.rhino.Node; import java.util.List; /** * A root pass that container for other passes that should run on * with a single call graph (currently a SimpleDefinitionFinder). * Expected passes include: * - optimize parameters * - optimize returns * - devirtualize prototype methods * * @author johnlenz@google.com (John Lenz) */ class OptimizeCalls implements CompilerPass { List passes = Lists.newArrayList(); private AbstractCompiler compiler; OptimizeCalls(AbstractCompiler compiler) { this.compiler = compiler; } OptimizeCalls addPass(CallGraphCompilerPass pass) { passes.add(pass); return this; } interface CallGraphCompilerPass { void process(Node externs, Node root, SimpleDefinitionFinder definitions); } @Override public void process(Node externs, Node root) { if (passes.size() > 0) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); for (CallGraphCompilerPass pass : passes) { pass.process(externs, root, defFinder); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DiagnosticGroups.java0000644000175000017500000002376012115204405026773 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.util.Map; /** * Named groups of DiagnosticTypes exposed by Compiler. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroups { static final DiagnosticType UNUSED = DiagnosticType.warning("JSC_UNUSED", "{0}"); public DiagnosticGroups() {} private static final Map groupsByName = Maps.newHashMap(); static DiagnosticGroup registerDeprecatedGroup(String name) { return registerGroup(name, new DiagnosticGroup(name, UNUSED)); } static DiagnosticGroup registerGroup(String name, DiagnosticGroup group) { groupsByName.put(name, group); return group; } static DiagnosticGroup registerGroup(String name, DiagnosticType ... types) { DiagnosticGroup group = new DiagnosticGroup(name, types); groupsByName.put(name, group); return group; } static DiagnosticGroup registerGroup(String name, DiagnosticGroup ... groups) { DiagnosticGroup group = new DiagnosticGroup(name, groups); groupsByName.put(name, group); return group; } /** Get the registered diagnostic groups, indexed by name. */ protected Map getRegisteredGroups() { return ImmutableMap.copyOf(groupsByName); } /** Find the diagnostic group registered under the given name. */ public DiagnosticGroup forName(String name) { return groupsByName.get(name); } // A bit of a hack to display the available groups on the command-line. // New groups should be added to this list if they are public and should // be listed on the command-line as an available option. // // If a group is suppressible on a per-file basis, it should be added // to parser/ParserConfig.properties static final String DIAGNOSTIC_GROUP_NAMES = "accessControls, ambiguousFunctionDecl, checkRegExp, " + "checkTypes, checkVars, const, constantProperty, deprecated, " + "duplicateMessage, " + "es5Strict, externsValidation, fileoverviewTags, globalThis, " + "internetExplorerChecks, invalidCasts, misplacedTypeAnnotation, " + "missingProperties, " + "nonStandardJsDocs, suspiciousCode, strictModuleDepCheck, " + "typeInvalidation, " + "undefinedNames, undefinedVars, unknownDefines, uselessCode, " + "visibility"; public static final DiagnosticGroup GLOBAL_THIS = DiagnosticGroups.registerGroup("globalThis", CheckGlobalThis.GLOBAL_THIS); public static final DiagnosticGroup DEPRECATED = DiagnosticGroups.registerGroup("deprecated", CheckAccessControls.DEPRECATED_NAME, CheckAccessControls.DEPRECATED_NAME_REASON, CheckAccessControls.DEPRECATED_PROP, CheckAccessControls.DEPRECATED_PROP_REASON, CheckAccessControls.DEPRECATED_CLASS, CheckAccessControls.DEPRECATED_CLASS_REASON); public static final DiagnosticGroup VISIBILITY = DiagnosticGroups.registerGroup("visibility", CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS, CheckAccessControls.BAD_PRIVATE_PROPERTY_ACCESS, CheckAccessControls.BAD_PROTECTED_PROPERTY_ACCESS, CheckAccessControls.PRIVATE_OVERRIDE, CheckAccessControls.VISIBILITY_MISMATCH); public static final DiagnosticGroup CONSTANT_PROPERTY = DiagnosticGroups.registerGroup("constantProperty", CheckAccessControls.CONST_PROPERTY_DELETED, CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE); public static final DiagnosticGroup NON_STANDARD_JSDOC = DiagnosticGroups.registerGroup("nonStandardJsDocs", RhinoErrorReporter.BAD_JSDOC_ANNOTATION); public static final DiagnosticGroup ACCESS_CONTROLS = DiagnosticGroups.registerGroup("accessControls", DEPRECATED, VISIBILITY); public static final DiagnosticGroup INVALID_CASTS = DiagnosticGroups.registerGroup("invalidCasts", TypeValidator.INVALID_CAST); public static final DiagnosticGroup FILEOVERVIEW_JSDOC = DiagnosticGroups.registerDeprecatedGroup("fileoverviewTags"); public static final DiagnosticGroup STRICT_MODULE_DEP_CHECK = DiagnosticGroups.registerGroup("strictModuleDepCheck", VarCheck.STRICT_MODULE_DEP_ERROR, CheckGlobalNames.STRICT_MODULE_DEP_QNAME); public static final DiagnosticGroup VIOLATED_MODULE_DEP = DiagnosticGroups.registerGroup("violatedModuleDep", VarCheck.VIOLATED_MODULE_DEP_ERROR); public static final DiagnosticGroup EXTERNS_VALIDATION = DiagnosticGroups.registerGroup("externsValidation", VarCheck.NAME_REFERENCE_IN_EXTERNS_ERROR, VarCheck.UNDEFINED_EXTERN_VAR_ERROR); public static final DiagnosticGroup AMBIGUOUS_FUNCTION_DECL = DiagnosticGroups.registerGroup("ambiguousFunctionDecl", VariableReferenceCheck.AMBIGUOUS_FUNCTION_DECL, StrictModeCheck.BAD_FUNCTION_DECLARATION); public static final DiagnosticGroup UNKNOWN_DEFINES = DiagnosticGroups.registerGroup("unknownDefines", ProcessDefines.UNKNOWN_DEFINE_WARNING); public static final DiagnosticGroup TWEAKS = DiagnosticGroups.registerGroup("tweakValidation", ProcessTweaks.INVALID_TWEAK_DEFAULT_VALUE_WARNING, ProcessTweaks.TWEAK_WRONG_GETTER_TYPE_WARNING, ProcessTweaks.UNKNOWN_TWEAK_WARNING); public static final DiagnosticGroup MISSING_PROPERTIES = DiagnosticGroups.registerGroup("missingProperties", TypeCheck.INEXISTENT_PROPERTY); public static final DiagnosticGroup INTERNET_EXPLORER_CHECKS = DiagnosticGroups.registerGroup("internetExplorerChecks", RhinoErrorReporter.TRAILING_COMMA); public static final DiagnosticGroup UNDEFINED_VARIABLES = DiagnosticGroups.registerGroup("undefinedVars", VarCheck.UNDEFINED_VAR_ERROR); public static final DiagnosticGroup UNDEFINED_NAMES = DiagnosticGroups.registerGroup("undefinedNames", CheckGlobalNames.UNDEFINED_NAME_WARNING); public static final DiagnosticGroup DEBUGGER_STATEMENT_PRESENT = DiagnosticGroups.registerGroup("checkDebuggerStatement", CheckDebuggerStatement.DEBUGGER_STATEMENT_PRESENT); public static final DiagnosticGroup CHECK_REGEXP = DiagnosticGroups.registerGroup("checkRegExp", CheckRegExp.REGEXP_REFERENCE, CheckRegExp.MALFORMED_REGEXP); public static final DiagnosticGroup CHECK_TYPES = DiagnosticGroups.registerGroup("checkTypes", TypeValidator.ALL_DIAGNOSTICS, TypeCheck.ALL_DIAGNOSTICS); public static final DiagnosticGroup CHECK_STRUCT_DICT_INHERITENCE = DiagnosticGroups.registerGroup("checkStructDictInheritence", TypeCheck.CONFLICTING_SHAPE_TYPE); public static final DiagnosticGroup CHECK_VARIABLES = DiagnosticGroups.registerGroup("checkVars", VarCheck.UNDEFINED_VAR_ERROR, SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR); public static final DiagnosticGroup CHECK_USELESS_CODE = DiagnosticGroups.registerGroup("uselessCode", CheckSideEffects.USELESS_CODE_ERROR, CheckUnreachableCode.UNREACHABLE_CODE); public static final DiagnosticGroup CONST = DiagnosticGroups.registerGroup("const", CheckAccessControls.CONST_PROPERTY_DELETED, CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE, ConstCheck.CONST_REASSIGNED_VALUE_ERROR); public static final DiagnosticGroup TYPE_INVALIDATION = DiagnosticGroups.registerGroup("typeInvalidation", DisambiguateProperties.Warnings.INVALIDATION, DisambiguateProperties.Warnings.INVALIDATION_ON_TYPE); public static final DiagnosticGroup DUPLICATE_VARS = DiagnosticGroups.registerGroup("duplicate", SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR, TypeValidator.DUP_VAR_DECLARATION); public static final DiagnosticGroup ES5_STRICT = DiagnosticGroups.registerGroup("es5Strict", ControlStructureCheck.USE_OF_WITH, StrictModeCheck.UNKNOWN_VARIABLE, StrictModeCheck.EVAL_DECLARATION, StrictModeCheck.EVAL_ASSIGNMENT, StrictModeCheck.ARGUMENTS_DECLARATION, StrictModeCheck.ARGUMENTS_ASSIGNMENT, StrictModeCheck.DELETE_VARIABLE, StrictModeCheck.DUPLICATE_OBJECT_KEY, StrictModeCheck.BAD_FUNCTION_DECLARATION); public static final DiagnosticGroup CHECK_PROVIDES = DiagnosticGroups.registerGroup("checkProvides", CheckProvides.MISSING_PROVIDE_WARNING); public static final DiagnosticGroup DUPLICATE_MESSAGE = DiagnosticGroups.registerGroup("duplicateMessage", JsMessageVisitor.MESSAGE_DUPLICATE_KEY); public static final DiagnosticGroup MISPLACED_TYPE_ANNOTATION = DiagnosticGroups.registerGroup("misplacedTypeAnnotation", RhinoErrorReporter.MISPLACED_TYPE_ANNOTATION); public static final DiagnosticGroup SUSPICIOUS_CODE = DiagnosticGroups.registerGroup("suspiciousCode", CheckSuspiciousCode.SUSPICIOUS_SEMICOLON, CheckSuspiciousCode.SUSPICIOUS_COMPARISON_WITH_NAN); /** * Adds warning levels by name. */ void setWarningLevel(CompilerOptions options, String name, CheckLevel level) { DiagnosticGroup group = forName(name); Preconditions.checkNotNull(group, "No warning class for name: %s", name); options.setWarningLevel(group, level); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RenameVars.java0000644000175000017500000004323712115204405025553 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.Nullable; /** * RenameVars renames all the variables names into short names, to reduce code * size and also to obfuscate the code. * */ final class RenameVars implements CompilerPass { private final AbstractCompiler compiler; /** List of global NAME nodes */ private final ArrayList globalNameNodes = new ArrayList(); /** List of local NAME nodes */ private final ArrayList localNameNodes = new ArrayList(); /** * Maps a name node to its pseudo name, null if we are not generating so * there will be no overhead unless we are debugging. */ private final Map pseudoNameMap; /** Set of extern variable names */ private final Set externNames = new HashSet(); /** Set of reserved variable names */ private final Set reservedNames; /** The renaming map */ private final Map renameMap = new HashMap(); /** The previously used rename map. */ private final VariableMap prevUsedRenameMap; /** The global name prefix */ private final String prefix; /** Counter for each assignment */ private int assignmentCount = 0; /** Logs all name assignments */ private StringBuilder assignmentLog; // Logic for bleeding functions, where the name leaks into the outer // scope on IE but not on other browsers. private Set localBleedingFunctions = Sets.newHashSet(); private ArrayListMultimap localBleedingFunctionsPerScope = ArrayListMultimap.create(); class Assignment { final String oldName; final int orderOfOccurrence; String newName; int count; // Number of times this is referenced Assignment(String name) { this.oldName = name; this.newName = null; this.count = 0; // Represents the order at which a symbol appears in the source. this.orderOfOccurrence = assignmentCount++; } /** * Assigns the new name. */ void setNewName(String newName) { Preconditions.checkState(this.newName == null); this.newName = newName; } } /** Maps an old name to a new name assignment */ private final Map assignments = new HashMap(); /** Whether renaming should apply to local variables only. */ private final boolean localRenamingOnly; /** * Whether function expression names should be preserved. Typically, for * debugging purposes. * * @see NameAnonymousFunctions */ private boolean preserveFunctionExpressionNames; private final boolean shouldShadow; /** Characters that shouldn't be used in variable names. */ private final char[] reservedCharacters; /** A prefix to distinguish temporary local names from global names */ // TODO(user): No longer needs to be public when shadowing doesn't use it. public static final String LOCAL_VAR_PREFIX = "L "; RenameVars(AbstractCompiler compiler, String prefix, boolean localRenamingOnly, boolean preserveFunctionExpressionNames, boolean generatePseudoNames, boolean shouldShadow, VariableMap prevUsedRenameMap, @Nullable char[] reservedCharacters, @Nullable Set reservedNames) { this.compiler = compiler; this.prefix = prefix == null ? "" : prefix; this.localRenamingOnly = localRenamingOnly; this.preserveFunctionExpressionNames = preserveFunctionExpressionNames; if (generatePseudoNames) { this.pseudoNameMap = Maps.newHashMap(); } else { this.pseudoNameMap = null; } this.prevUsedRenameMap = prevUsedRenameMap; this.reservedCharacters = reservedCharacters; this.shouldShadow = shouldShadow; if (reservedNames == null) { this.reservedNames = Sets.newHashSet(); } else { this.reservedNames = Sets.newHashSet(reservedNames); } } /** * Iterate through the nodes, collect all the NAME nodes that need to be * renamed, and count how many times each variable name is referenced. * * There are 2 passes: * - externs: keep track of the global vars in the externNames_ map. * - source: keep track of all name references in globalNameNodes_, and * localNameNodes_. * * To get shorter local variable renaming, we rename local variables to a * temporary name "LOCAL_VAR_PREFIX + index" where index is the index of the * variable declared in the local scope stack. * e.g. * Foo(fa, fb) { * var c = function(d, e) { return fa; } * } * The indexes are: fa:0, fb:1, c:2, d:3, e:4 * * In that way, local variable names are reused in each global function. * e.g. the final code might look like * function x(a,b) { ... } * function y(a,b,c) { ... } */ class ProcessVars extends AbstractPostOrderCallback implements ScopedCallback { private final boolean isExternsPass_; ProcessVars(boolean isExterns) { isExternsPass_ = isExterns; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) return; Iterator it = t.getScope().getVars(); while (it.hasNext()) { Var current = it.next(); if (current.isBleedingFunction()) { localBleedingFunctions.add(current); localBleedingFunctionsPerScope.put( t.getScope().getParent(), current); } } } @Override public void exitScope(NodeTraversal t) {} @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String name = n.getString(); // Ignore anonymous functions if (name.length() == 0) { return; } // Is this local or Global? // Bleeding functions should be treated as part of their outer // scope, because IE has bugs in how it handles bleeding // functions. Scope.Var var = t.getScope().getVar(name); boolean local = (var != null) && var.isLocal() && (!var.scope.getParent().isGlobal() || !var.isBleedingFunction()); // Are we renaming global variables? if (!local && localRenamingOnly) { reservedNames.add(name); return; } // Are we renaming function expression names? if (preserveFunctionExpressionNames && var != null && NodeUtil.isFunctionExpression(var.getParentNode())) { reservedNames.add(name); return; } // Check if we can rename this. if (!okToRenameVar(name, local)) { if (local) { // Blindly de-uniquify for the Prototype library for issue 103. String newName = MakeDeclaredNamesUnique.ContextualRenameInverter .getOrginalName(name); if (!newName.equals(name)) { n.setString(newName); } } return; } if (isExternsPass_) { // Keep track of extern globals. if (!local) { externNames.add(name); } return; } if (pseudoNameMap != null) { recordPseudoName(n); } if (local) { // Local var: assign a new name String tempName = LOCAL_VAR_PREFIX + getLocalVarIndex(var); incCount(tempName); localNameNodes.add(n); n.setString(tempName); } else if (var != null) { // Not an extern // If it's global, increment global count incCount(name); globalNameNodes.add(n); } } // Increment count of an assignment void incCount(String name) { Assignment s = assignments.get(name); if (s == null) { s = new Assignment(name); assignments.put(name, s); } s.count++; } } /** * Sorts Assignment objects by their count, breaking ties by their order of * occurrence in the source to ensure a deterministic total ordering. */ private static final Comparator FREQUENCY_COMPARATOR = new Comparator() { @Override public int compare(Assignment a1, Assignment a2) { if (a1.count != a2.count) { return a2.count - a1.count; } // Break a tie using the order in which the variable first appears in // the source. return ORDER_OF_OCCURRENCE_COMPARATOR.compare(a1, a2); } }; /** * Sorts Assignment objects by the order the variable name first appears in * the source. */ private static final Comparator ORDER_OF_OCCURRENCE_COMPARATOR = new Comparator() { @Override public int compare(Assignment a1, Assignment a2) { return a1.orderOfOccurrence - a2.orderOfOccurrence; } }; @Override public void process(Node externs, Node root) { assignmentLog = new StringBuilder(); // Do variable reference counting. NodeTraversal.traverse(compiler, externs, new ProcessVars(true)); NodeTraversal.traverse(compiler, root, new ProcessVars(false)); // Make sure that new names don't overlap with extern names. reservedNames.addAll(externNames); // Rename vars, sorted by frequency of occurrence to minimize code size. SortedSet varsByFrequency = new TreeSet(FREQUENCY_COMPARATOR); varsByFrequency.addAll(assignments.values()); if (shouldShadow) { new ShadowVariables( compiler, assignments, varsByFrequency, pseudoNameMap).process( externs, root); } // First try to reuse names from an earlier compilation. if (prevUsedRenameMap != null) { reusePreviouslyUsedVariableMap(); } // Assign names, sorted by descending frequency to minimize code size. assignNames(varsByFrequency); boolean changed = false; // Rename the globals! for (Node n : globalNameNodes) { String newName = getNewGlobalName(n); // Note: if newName is null, then oldName is an extern. if (newName != null) { n.setString(newName); changed = true; } } // Rename the locals! for (Node n : localNameNodes) { String newName = getNewLocalName(n); if (newName != null) { n.setString(newName); changed = true; } } if (changed) { compiler.reportCodeChange(); } // Lastly, write the name assignments to the debug log. compiler.addToDebugLog("JS var assignments:\n" + assignmentLog); assignmentLog = null; } private String getNewGlobalName(Node n) { String oldName = n.getString(); Assignment a = assignments.get(oldName); if (a.newName != null && !a.newName.equals(oldName)) { if (pseudoNameMap != null) { return pseudoNameMap.get(n); } return a.newName; } else { return null; } } private String getNewLocalName(Node n) { String oldTempName = n.getString(); Assignment a = assignments.get(oldTempName); if (!a.newName.equals(oldTempName)) { if (pseudoNameMap != null) { return pseudoNameMap.get(n); } return a.newName; } return null; } private void recordPseudoName(Node n) { // Variable names should be in a different name space than // property pseudo names. pseudoNameMap.put(n, '$' + n.getString() + "$$" ); } /** * Runs through the assignments and reuses as many names as possible from the * previously used variable map. Updates reservedNames with the set of names * that were reused. */ private void reusePreviouslyUsedVariableMap() { // If prevUsedRenameMap had duplicate values then this pass would be // non-deterministic. // In such a case, the following will throw an IllegalArgumentException. Preconditions.checkState( prevUsedRenameMap.getNewNameToOriginalNameMap() instanceof Map); for (Assignment a : assignments.values()) { String prevNewName = prevUsedRenameMap.lookupNewName(a.oldName); if (prevNewName == null || reservedNames.contains(prevNewName)) { continue; } if (a.oldName.startsWith(LOCAL_VAR_PREFIX) || (!externNames.contains(a.oldName) && prevNewName.startsWith(prefix))) { reservedNames.add(prevNewName); finalizeNameAssignment(a, prevNewName); } } } /** * Determines which new names to substitute for the original names. */ private void assignNames(SortedSet varsToRename) { NameGenerator globalNameGenerator = new NameGenerator(reservedNames, prefix, reservedCharacters); // Local variables never need a prefix. NameGenerator localNameGenerator = prefix.isEmpty() ? globalNameGenerator : new NameGenerator( reservedNames, "", reservedCharacters); // Generated names and the assignments for non-local vars. List pendingAssignments = new ArrayList(); List generatedNamesForAssignments = new ArrayList(); for (Assignment a : varsToRename) { if (a.newName != null) { continue; } if (externNames.contains(a.oldName)) { continue; } String newName; if (a.oldName.startsWith(LOCAL_VAR_PREFIX)) { // For local variable, we make the assignment right away. newName = localNameGenerator.generateNextName(); finalizeNameAssignment(a, newName); } else { // For non-local variable, delay finalizing the name assignment // until we know how many new names we'll have of length 2, 3, etc. newName = globalNameGenerator.generateNextName(); pendingAssignments.add(a); generatedNamesForAssignments.add(newName); } reservedNames.add(newName); } // Now that we have a list of generated names, and a list of variable // Assignment objects, we assign the generated names to the vars as // follows: // 1) The most frequent vars get the shorter names. // 2) If N number of vars are going to be assigned names of the same // length, we assign the N names based on the order at which the vars // first appear in the source. This makes the output somewhat less // random, because symbols declared close together are assigned names // that are quite similar. With this heuristic, the output is more // compressible. // For instance, the output may look like: // var da = "..", ea = ".."; // function fa() { .. } function ga() { .. } int numPendingAssignments = generatedNamesForAssignments.size(); for (int i = 0; i < numPendingAssignments;) { SortedSet varsByOrderOfOccurrence = new TreeSet(ORDER_OF_OCCURRENCE_COMPARATOR); // Add k number of Assignment to the set, where k is the number of // generated names of the same length. int len = generatedNamesForAssignments.get(i).length(); for (int j = i; j < numPendingAssignments && generatedNamesForAssignments.get(j).length() == len; j++) { varsByOrderOfOccurrence.add(pendingAssignments.get(j)); } // Now, make the assignments for (Assignment a : varsByOrderOfOccurrence) { finalizeNameAssignment(a, generatedNamesForAssignments.get(i)); ++i; } } } /** * Makes a final name assignment. */ private void finalizeNameAssignment(Assignment a, String newName) { a.setNewName(newName); // Keep track of the mapping renameMap.put(a.oldName, newName); // Log the mapping assignmentLog.append(a.oldName).append(" => ").append(newName).append('\n'); } /** * Gets the variable map. */ VariableMap getVariableMap() { return new VariableMap(ImmutableMap.copyOf(renameMap)); } /** * Determines whether a variable name is okay to rename. */ private boolean okToRenameVar(String name, boolean isLocal) { return !compiler.getCodingConvention().isExported(name, isLocal); } /** * Returns the index within the scope stack. * e.g. function Foo(a) { var b; function c(d) { } } * a = 0, b = 1, c = 2, d = 3 */ private int getLocalVarIndex(Var v) { int num = v.index; Scope s = v.scope.getParent(); if (s == null) { throw new IllegalArgumentException("Var is not local"); } boolean isBleedingIntoScope = s.getParent() != null && localBleedingFunctions.contains(v); while (s.getParent() != null) { if (isBleedingIntoScope) { num += localBleedingFunctionsPerScope.get(s).indexOf(v) + 1; isBleedingIntoScope = false; } else { num += localBleedingFunctionsPerScope.get(s).size(); } num += s.getVarCount(); s = s.getParent(); } return num; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AstParallelizer.java0000644000175000017500000001620712115204405026603 0ustar apoapo/* * 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.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.List; /** * Breaks up the AST at different levels for parallel analysis and * optimizations. In all cases, the subtrees are detached from the original * source tree and are replaced by place-holders for the reverse process. * Although this class breaks the AST into independent subtrees and make tree * transformation safe, it is still up to individual passes to preserve proper * semantics when analyzing the subtrees. * */ class AstParallelizer { public static final String TEMP_NAME = "JSC_TMP_PLACE_HOLDER"; private final Predicate shouldSplit; private final Supplier placeHolderProvider; private final List forest; private final Node root; private final boolean includeRoot; // Maps to place holder to the original function. private final List detachPointList; /** * Constructor. * * @param shouldSplit Specify at which node it should split the subtree. * @param shouldTraverse Specify when to stop looking for subtree to split. * This is very important for performance as we do not want to * traverse too much just looking for subtree. * @param placeHolderProvider Specify what type of node should be place as * a temporary place holder for where the subtree is detached. * @param root The AST itself. * @param includeRoot Should we include the root inside the forest returned * by {{@link #split()}. */ public AstParallelizer( Predicate shouldSplit, Predicate shouldTraverse, Supplier placeHolderProvider, Node root, boolean includeRoot) { this.shouldSplit = shouldSplit; this.placeHolderProvider = placeHolderProvider; this.root = root; this.includeRoot = includeRoot; this.forest = Lists.newLinkedList(); this.detachPointList = Lists.newLinkedList(); } public static AstParallelizer createNewFunctionLevelAstParallelizer( Node root, boolean globalPass) { // Split at function level. Predicate shouldSplit = new Predicate() { @Override public boolean apply(Node input) { return input.isFunction(); } }; // Always traverse until it finds a split point. Predicate shouldTraverse = new Predicate() { @Override public boolean apply(Node ignored) { return true; } }; // Use a function declaration of the same name. Supplier placeHolders = new Supplier() { @Override public Node get() { return IR.function(IR.name(TEMP_NAME), IR.paramList(), IR.block()); } }; return new AstParallelizer( shouldSplit, shouldTraverse, placeHolders, root, globalPass); } public static AstParallelizer createNewFileLevelAstParallelizer(Node root) { // Split at every node that has a file name prop. Predicate shouldSplit = new Predicate() { @Override public boolean apply(Node input) { return input.getSourceFileName() != null; } }; // Use a string as place holder. Supplier placeHolders = new Supplier() { @Override public Node get() { return NodeUtil.newExpr(IR.string(TEMP_NAME)); } }; // Only traverse blocks. Predicate shouldTraverse = new Predicate() { @Override public boolean apply(Node n) { return n.isBlock(); } }; return new AstParallelizer( shouldSplit, shouldTraverse, placeHolders, root, false); } /** * Remembers the split point for use in {@link #join()}. */ private void recordSplitPoint(Node placeHolder, Node before, Node original) { detachPointList.add(new DetachPoint(placeHolder, before, original)); } /** * Splits the AST into subtree at different levels. The subtrees itself are * usually not valid JavaScript but they are all subtrees of some valid * JavaScript. */ public List split() { if (includeRoot) { forest.add(root); } split(root); return forest; } private void split(Node n) { Node c = n.getFirstChild(); Node before = null; while (c != null) { Node next = c.getNext(); if (shouldSplit.apply(c)) { Node placeHolder = placeHolderProvider.get(); if (before == null) { forest.add(n.removeFirstChild()); n.addChildToFront(placeHolder); } else { n.addChildAfter(placeHolder, c); n.removeChildAfter(before); forest.add(c); } recordSplitPoint(placeHolder, before, c); before = placeHolder; } else { split(c); before = c; } c = next; } } /** * Reverse the splitting done by {@link #split()}. */ public void join() { // Revert in a reverse order to undo the detachment. while (!detachPointList.isEmpty()) { DetachPoint entry = detachPointList.remove(detachPointList.size() - 1); entry.reattach(); } } /** * A class to map the place holder to the original subtree for re-attachment. * Normally a Map from Node -> Node is sufficient, however, if we also * remember the node before the place holder, we can avoid using * {@link Node#replaceChild(Node, Node)} which requires a linear search of * the before node. Maybe someday we should get a prev pointer for this * purpose. */ private static class DetachPoint { // The place holder to remember where the original node was. private Node placeHolder; // The node before the place holder and the original, null if private Node before; // The root of the subtree to be temporary detached. private Node original; private DetachPoint(Node placeHolder, Node before, Node original) { this.placeHolder = placeHolder; this.before = before; this.original = original; } public void reattach() { // If the place-holder no longer has a parent, this implies the function // has been removed from the AST. if (placeHolder.getParent() != null) { if (before == null) { placeHolder.getParent().addChildrenToFront(original); placeHolder.getParent().removeChildAfter(original); } else { placeHolder.getParent().addChildAfter(original, before); placeHolder.getParent().removeChildAfter(original); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SyntacticScopeCreator.java0000644000175000017500000002104012115204405027747 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** *

      The syntactic scope creator scans the parse tree to create a Scope object * containing all the variable declarations in that scope.

      * *

      This implementation is not thread-safe.

      * */ class SyntacticScopeCreator implements ScopeCreator { private final AbstractCompiler compiler; private Scope scope; private InputId inputId; private final RedeclarationHandler redeclarationHandler; // The arguments variable is special, in that it's declared in every local // scope, but not explicitly declared. private static final String ARGUMENTS = "arguments"; public static final DiagnosticType VAR_MULTIPLY_DECLARED_ERROR = DiagnosticType.error( "JSC_VAR_MULTIPLY_DECLARED_ERROR", "Variable {0} first declared in {1}"); public static final DiagnosticType VAR_ARGUMENTS_SHADOWED_ERROR = DiagnosticType.error( "JSC_VAR_ARGUMENTS_SHADOWED_ERROR", "Shadowing \"arguments\" is not allowed"); /** * Creates a ScopeCreator. */ SyntacticScopeCreator(AbstractCompiler compiler) { this.compiler = compiler; this.redeclarationHandler = new DefaultRedeclarationHandler(); } SyntacticScopeCreator( AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } @Override public Scope createScope(Node n, Scope parent) { inputId = null; if (parent == null) { scope = Scope.createGlobalScope(n); } else { scope = new Scope(parent, n); } scanRoot(n); inputId = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n) { if (n.isFunction()) { if (inputId == null) { inputId = NodeUtil.getInputId(n); // TODO(johnlenz): inputId maybe null if the FUNCTION node is detached // from the AST. // Is it meaningful to build a scope for detached FUNCTION node? } final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnNameNode); } // Args: Declare function variables Preconditions.checkState(args.isParamList()); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.isName()); declareVar(a); } // Body scanVars(body); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); declareVar(child); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top-level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if (!allowDupe) { compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(NodeUtil.getSourceName(n), n, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * @param n The name node to check. * @param origVar The associated Var. * @return Whether duplicated declarations warnings should be suppressed * for the given node. */ static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); Node origParent = origVar.getParentNode(); JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } if (info != null && info.getSuppressions().contains("duplicate")) { return true; } info = origVar.nameNode.getJSDocInfo(); if (info == null) { info = origParent.getJSDocInfo(); } return (info != null && info.getSuppressions().contains("duplicate")); } /** * Generates an untyped global scope from the root of AST of compiler (which * includes externs). * * @param compiler The compiler for which the scope is generated. * @return The new untyped global scope generated as a result of this call. */ static Scope generateUntypedTopScope(AbstractCompiler compiler) { return new SyntacticScopeCreator(compiler).createScope(compiler.getRoot(), null); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/0000755000175000017500000000000012115204405024772 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/common/0000755000175000017500000000000012115204405026262 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/common/Protocol.java0000644000175000017500000002120512115204405030726 0ustar apoapo/* * 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.webservice.common; import com.google.common.collect.Sets; import java.util.Set; /** * All the strings used by the webservice protocol. * */ public class Protocol { private Protocol() {} /** * All enums that need to be shared between the Java and JS code should * implement this interface. */ public static interface ProtocolEnum { /** * @return A string representing the key or value specified by the * protocol. */ public String getValue(); } /** * All the keys that can be part of the http request. */ public static enum RequestKey implements ProtocolEnum { CODE_URL("code_url"), JS_CODE("js_code"), EXCLUDE_DEFAULT_EXTERNS("exclude_default_externs"), EXTERNS_URL("externs_url"), EXTERNS_CODE("js_externs"), COMPILATION_LEVEL("compilation_level"), OUTPUT_FORMAT("output_format"), OUTPUT_INFO("output_info"), OUTPUT_FILE_NAME("output_file_name"), OUTPUT_WRAPPER("output_wrapper"), API_KEY("api_key"), FORMATTING("formatting"), WARNING_LEVEL("warning_level"), USER_ID("uid"), USE_CLOSURE("use_closure_library"), BUILD_DEBUG("debug"), CHARSET("charset"), LANGUAGE("language"), USE_TYPES_FOR_OPTIMIZATIONS("use_types_for_optimization"), // Old ROBOCOMP urls. RAWJS("rawjs"), BASE("base"), MODE("mode"), SCRIPT("script"), NOCACHE("nocache") // Ignored. ; private static final Set permittedKeys = getPermittedKeys(); private static Set getPermittedKeys() { Set keys = Sets.newHashSet(); for (RequestKey key : RequestKey.values()) { keys.add(key.asGetParameter()); } return keys; } private final String asGetParameter; private RequestKey(String asGetParameter) { this.asGetParameter = asGetParameter; } public String asGetParameter() { return asGetParameter; } @Override public String toString() { return asGetParameter; } public static boolean isKeyValid(String key) { return permittedKeys.contains(key.toLowerCase()); } @Override public String getValue() { return asGetParameter; } } /** * All the possible values for the OUTPUT_INFO key. */ public static enum OutputInfoKey implements ProtocolEnum { VARIABLE_MAP("variable_map"), COMPILED_CODE("compiled_code"), WARNINGS("warnings"), ERRORS("errors"), STATISTICS("statistics"), ; private final String value; private OutputInfoKey(String value) { this.value = value; } @Override public String getValue() { return value; } } /** * All the possible values for the FORMATTING key. */ public static enum FormattingKey implements ProtocolEnum { PRETTY_PRINT("pretty_print"), PRINT_INPUT_DELIMITER("print_input_delimiter"), ; private final String value; private FormattingKey(String value) { this.value = value; } @Override public String getValue() { return value; } } public static enum CompilationLevelKey implements ProtocolEnum { WHITESPACE_ONLY("whitespace_only"), SIMPLE_OPTIMIZATIONS("simple_optimizations"), ADVANCED_OPTIMIZATIONS("advanced_optimizations"), ; private final String value; CompilationLevelKey(String value) { this.value = value; } @Override public String getValue() { return value; } } public static enum WarningLevelKey implements ProtocolEnum { QUIET("quiet"), DEFAULT("default"), VERBOSE("verbose"), ; private final String value; private WarningLevelKey(String value) { this.value = value; } @Override public String getValue() { return value; } } public static enum OutputFormatKey implements ProtocolEnum { TEXT("text"), XML("xml"), JSON("json"), ; private final String value; private OutputFormatKey(String value) { this.value = value; } @Override public String getValue() { return value; } @Override public String toString() { return getValue(); } } /** * Fields in the JSON response from the ApiKeyGenerationServlet. */ public static enum ApiKeyResponse implements ProtocolEnum { API_KEY("api_key"), ; private final String responseParam; ApiKeyResponse(String responseParam) { this.responseParam = responseParam; } /** * Name of the key as it appears in the JSON. */ public String getResponseParam() { return responseParam; } @Override public String toString() { return getResponseParam(); } @Override public String getValue() { return getResponseParam(); } } /** * All the xml/json tags that can be returned by the backend if xml or json is * selected as the output mode. */ public static enum ResponseTag implements ProtocolEnum { ROOT_TAG("compilationResult"), COMPILED_CODE_TAG("compiledCode"), WARNINGS_TAG("warnings"), WARNING_TAG("warning"), ERRORS_TAG("errors"), ERROR_TAG("error"), ERROR_LINE_NO_ATTR("lineno"), ERROR_LINE_ATTR("line"), // Charno is negative if error occurred outside range of columns that // JSCompiler records. Change the sign of the value to find the // maximum column represented. // Note that JSCompiler uses -1 as an "I don't know" state, and it can // also turn up occasionally. ERROR_CHAR_ATTR("charno"), ERROR_FILE_ATTR("file"), ERROR_TYPE_ATTR("type"), STATS_TAG("statistics"), ORIGINAL_SIZE_TAG("originalSize"), ORIGINAL_GZIP_SIZE_TAG("originalGzipSize"), COMPRESSED_SIZE_TAG("compressedSize"), COMPRESSED_GZIP_SIZE_TAG("compressedGzipSize"), COMPILE_TIME_TAG("compileTime"), SERVER_ERRORS_TAG("serverErrors"), SERVER_ERROR_TAG("error"), SERVER_ERROR_CODE_ATTR("code"), VARIABLE_MAP("variableMap"), VARIABLE_MAP_ENTRY("variableMapEntry"), ORIGINAL_NAME_ATTR("originalName"), NEW_NAME_ATTR("newName"), OUTPUT_FILE_PATH("outputFilePath"), ; private final String responseTag; private ResponseTag(String responseTag) { this.responseTag = responseTag; } public String getResponseTag() { return responseTag; } @Override public String toString() { return getResponseTag(); } @Override public String getValue() { return getResponseTag(); } } /** * Properties key for getting the maximum input file size that may be * compiled by the service. This is parameterized so we can have different * values for inside and outside Google. * The value should be a string representation of an integer representing * the maximum input size in bytes. */ public static final String MAX_INPUT_SIZE_KEY = "com.google.javascript.jscomp.webservice.maximumInputSize"; /** * Fallback value in case no setting is provided. */ public static final int FALLBACK_MAX_INPUT_SIZE = 500 * 1024; /** * Hard limit on input size set at execution time from the MAX_INPUT_SIZE_KEY * property. */ private static int maxInputSize; /** * Initialize maxInputSize to the value from the MAX_INPUT_SIZE_KEY property * at startup. */ static { resetMaximumInputSize(); } /** * Find the maximum input size that this configuration of the web service * allows. * @return maximum input size permitted (in bytes) */ public static final int maximumInputSize() { // Limit the number of files downloaded if they are too big to compile. return maxInputSize; } /** * Reset the maximum input size so that the property key is rechecked. * This is needed for testing code because we are caching the maximum * input size value. */ public static final void resetMaximumInputSize() { String maxInputSizeStr = System.getProperty(Protocol.MAX_INPUT_SIZE_KEY); if (maxInputSizeStr == null) { maxInputSize = Protocol.FALLBACK_MAX_INPUT_SIZE; } else { maxInputSize = Integer.parseInt(maxInputSizeStr); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/common/ErrorCode.java0000644000175000017500000000266612115204405031023 0ustar apoapo/* * 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.webservice.common; /** * Enum of all the possible error described in the Web Service protocol. * */ public enum ErrorCode { UNKNOWN_OUTPUT_MODE(2), UNKNOWN_API_KEY(3), UNKNOWN_COMPILATION_LEVEL(4), UNKNOWN_CHARSET(5), POST_DATA_TOO_LARGE(8), FILE_TOO_LARGE(9), UNREACHABLE_URL(10), MALFORMED_URL(12), NO_OUTPUT_INFO(13), UNKNOWN_OUTPUT_INFO(14), MISSING_API_KEY(15), UNKNOWN_WARNING_LEVEL(16), UNKNOWN_FORMATTING_OPTION(17), UNKNOWN_PARAMETER(18), ILLEGAL_OUTPUT_FILE_NAME(19), HASH_MISMATCH(20), NO_CODE_FOUND_IN_CACHE(21), ACCOUNT_OVER_QUOTA(22), COMPILER_EXCEPTION(23), UNSUPPORTED_INPUT_RESOURCE_TYPE(24), DOWNLOAD_OVER_QUOTA(25), ; private final int code; ErrorCode(int code) { this.code = code; } public int getCode() { return code; } } ././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/common/AbstractWebServiceException.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/webservice/common/AbstractWebServic0000644000175000017500000000205212115204405031561 0ustar apoapo/* * 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.webservice.common; /** * All the exceptions that can be returned as error to the client of the API. * */ public abstract class AbstractWebServiceException extends Exception { public abstract ErrorCode getErrorCode(); public abstract String getFormattedError(); @Override public String toString() { return String.format("Error(%d): %s", getErrorCode().getCode(), getFormattedError()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExploitAssigns.java0000644000175000017500000001725612115204405026466 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Tries to chain assignments together. * * @author nicksantos@google.com (Nick Santos) * @author acleung@google.com (Alan Leung) * */ class ExploitAssigns extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node subtree) { for (Node child = subtree.getFirstChild(); child != null;) { Node next = child.getNext(); if (NodeUtil.isExprAssign(child)) { collapseAssign(child.getFirstChild(), child, subtree); } child = next; } return subtree; } /** * Try to collapse the given assign into subsequent expressions. */ private void collapseAssign(Node assign, Node expr, Node exprParent) { Node leftValue = assign.getFirstChild(); Node rightValue = leftValue.getNext(); if (isCollapsibleValue(leftValue, true) && collapseAssignEqualTo(expr, exprParent, leftValue)) { reportCodeChange(); } else if (isCollapsibleValue(rightValue, false) && collapseAssignEqualTo(expr, exprParent, rightValue)) { reportCodeChange(); } else if (rightValue.isAssign()) { // Recursively deal with nested assigns. collapseAssign(rightValue, expr, exprParent); } } /** * Determines whether we know enough about the given value to be able * to collapse it into subsequent expressions. * * For example, we can collapse booleans and variable names: * * x = 3; y = x; // y = x = 3; * a = true; b = true; // b = a = true; * * But we won't try to collapse complex expressions. * * @param value The value node. * @param isLValue Whether it's on the left-hand side of an expr. */ private boolean isCollapsibleValue(Node value, boolean isLValue) { switch (value.getType()) { case Token.GETPROP: // Do not collapse GETPROPs on arbitrary objects, because // they may be implemented setter functions, and oftentimes // setter functions fail on native objects. This is OK for "THIS" // objects, because we assume that they are non-native. return !isLValue || value.getFirstChild().isThis(); case Token.NAME: return true; default: return NodeUtil.isImmutableValue(value); } } /** * Collapse the given assign expression into the expression directly * following it, if possible. * * @param expr The expression that may be moved. * @param exprParent The parent of {@code expr}. * @param value The value of this expression, expressed as a node. Each * expression may have multiple values, so this function may be called * multiple times for the same expression. For example, * * a = true; * * is equal to the name "a" and the boolean "true". * @return Whether the expression was collapsed successfully. */ private boolean collapseAssignEqualTo(Node expr, Node exprParent, Node value) { Node assign = expr.getFirstChild(); Node parent = exprParent; Node next = expr.getNext(); while (next != null) { switch (next.getType()) { case Token.AND: case Token.OR: case Token.HOOK: case Token.IF: case Token.RETURN: case Token.EXPR_RESULT: // Dive down the left side parent = next; next = next.getFirstChild(); break; case Token.VAR: if (next.getFirstChild().hasChildren()) { parent = next.getFirstChild(); next = parent.getFirstChild(); break; } return false; case Token.GETPROP: case Token.NAME: if (next.isQualifiedName()) { String nextName = next.getQualifiedName(); if (value.isQualifiedName() && nextName.equals(value.getQualifiedName())) { // If the previous expression evaluates to value of a // qualified name, and that qualified name is used again // shortly, then we can exploit the assign here. // Verify the assignment doesn't change its own value. if (!isSafeReplacement(next, assign)) { return false; } exprParent.removeChild(expr); expr.removeChild(assign); parent.replaceChild(next, assign); return true; } } return false; case Token.ASSIGN: // Assigns are really tricky. In lots of cases, we want to inline // into the right side of the assign. But the left side of the // assign is evaluated first, and it may have convoluted logic: // a = null; // (a = b).c = null; // We don't want to exploit the first assign. Similarly: // a.b = null; // a.b.c = null; // We don't want to exploit the first assign either. // // To protect against this, we simply only inline when the left side // is guaranteed to evaluate to the same L-value no matter what. Node leftSide = next.getFirstChild(); if (leftSide.isName() || leftSide.isGetProp() && leftSide.getFirstChild().isThis()) { // Dive down the right side of the assign. parent = next; next = leftSide.getNext(); break; } else { return false; } default: if (NodeUtil.isImmutableValue(next) && next.isEquivalentTo(value)) { // If the r-value of the expr assign is an immutable value, // and the value is used again shortly, then we can exploit // the assign here. exprParent.removeChild(expr); expr.removeChild(assign); parent.replaceChild(next, assign); return true; } // Return without inlining a thing return false; } } return false; } /** * Checks name referenced in node to determine if it might have * changed. * @return Whether the replacement can be made. */ private boolean isSafeReplacement(Node node, Node replacement) { // No checks are needed for simple names. if (node.isName()) { return true; } Preconditions.checkArgument(node.isGetProp()); Node name = node.getFirstChild(); if (name.isName() && isNameAssignedTo(name.getString(), replacement)) { return false; } return true; } /** * @return Whether name is assigned in the expression rooted at node. */ private boolean isNameAssignedTo(String name, Node node) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (isNameAssignedTo(name, c)) { return true; } } if (node.isName()) { Node parent = node.getParent(); if (parent.isAssign() && parent.getFirstChild() == node) { if (name.equals(node.getString())) { return true; } } } return false; } }closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CrossModuleCodeMotion.java0000644000175000017500000003212312115204405027720 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; /** * A {@link Compiler} pass for moving code to a deeper module if possible. * - currently it only moves functions + variables * */ class CrossModuleCodeMotion extends AbstractPostOrderCallback implements CompilerPass { private static final Logger logger = Logger.getLogger(CrossModuleCodeMotion.class.getName()); private final AbstractCompiler compiler; private final JSModuleGraph graph; /** * Map from module to the node in that module that should parent any string * variable declarations that have to be moved into that module */ private final Map moduleVarParentMap = new HashMap(); /* * NOTE - I made this a LinkedHashMap to make testing easier. With a regular * HashMap, the variables may not output in a consistent order */ private final Map namedInfo = new LinkedHashMap(); /** * Creates an instance. * * @param compiler The compiler */ CrossModuleCodeMotion(AbstractCompiler compiler, JSModuleGraph graph) { this.compiler = compiler; this.graph = graph; } @Override public void process(Node externs, Node root) { logger.fine("Moving functions + variable into deeper modules"); // If there are <2 modules, then we will never move anything, so we're done if (graph != null && graph.getModuleCount() > 1) { // Traverse the tree and find the modules where a var is declared + used NodeTraversal.traverse(compiler, root, this); // Move the functions + variables to a deeper module [if possible] moveCode(); } } /** move the code accordingly */ private void moveCode() { for (NamedInfo info : namedInfo.values()) { JSModule deepestDependency = info.deepestModule; // Only move if all are true: // a) allowMove is true // b) it was used + declared somewhere [if not, then it will be removed // as dead or invalid code elsewhere] // c) the new dependency depends on the declModule if (info.allowMove && deepestDependency != null) { Iterator it = info.declarationIterator(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); while (it.hasNext()) { Declaration decl = it.next(); if (decl.module != null && moduleGraph.dependsOn(deepestDependency, decl.module)) { // Find the appropriate spot to move it to Node destParent = moduleVarParentMap.get(deepestDependency); if (destParent == null) { destParent = compiler.getNodeForCodeInsertion(deepestDependency); moduleVarParentMap.put(deepestDependency, destParent); } // VAR Nodes are normalized to have only one child. Node declParent = decl.node.getParent(); Preconditions.checkState( !declParent.isVar() || declParent.hasOneChild(), "AST not normalized."); // Remove it declParent.detachFromParent(); // Add it to the new spot destParent.addChildToFront(declParent); compiler.reportCodeChange(); } } } } } /** useful information for each variable candidate */ private class NamedInfo { boolean allowMove = true; // The deepest module where the variable is used. Starts at null private JSModule deepestModule = null; // The module where declarations appear private JSModule declModule = null; // information on the spot where the item was declared private final Deque declarations = new ArrayDeque(); // Add a Module where it is used void addUsedModule(JSModule m) { // If we are not allowed to move it, all bets are off if (!allowMove) { return; } // If we have no deepest module yet, set this one if (deepestModule == null) { deepestModule = m; } else { // Find the deepest common dependency deepestModule = graph.getDeepestCommonDependencyInclusive(m, deepestModule); } } /** * Add a declaration for this name. * @return Whether this is a valid declaration. If this returns false, * this should be added as a reference. */ boolean addDeclaration(Declaration d) { // all declarations must appear in the same module. if (declModule != null && d.module != declModule) { return false; } declarations.push(d); declModule = d.module; return true; } /** * Returns an iterator over the declarations, in the order that they were * declared. */ Iterator declarationIterator() { return declarations.iterator(); } } private class Declaration { final JSModule module; final Node node; Declaration(JSModule module, Node node) { this.module = module; this.node = node; } } /** * return true if the node has any form of conditional in its ancestry * TODO(nicksantos) keep track of the conditionals in the ancestry, so * that we don't have to recrawl it. */ private boolean hasConditionalAncestor(Node n) { for (Node ancestor : n.getAncestors()) { switch (ancestor.getType()) { case Token.DO: case Token.FOR: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: case Token.FUNCTION: return true; } } return false; } /** * get the information on a variable */ private NamedInfo getNamedInfo(Var v) { NamedInfo info = namedInfo.get(v); if (info == null) { info = new NamedInfo(); namedInfo.put(v, info); } return info; } /** * Process the references to named variables */ private void processReference(NodeTraversal t, NamedInfo info, String name) { // A name is recursively defined if: // 1: It is calling itself. // 2: One of its property calls itself. // Recursive definition should not block movement. boolean recursive = false; Node rootNode = t.getScope().getRootNode(); if (rootNode.isFunction()) { // CASE #1: String scopeFuncName = rootNode.getFirstChild().getString(); Node scopeFuncParent = rootNode.getParent(); if (scopeFuncName.equals(name)) { recursive = true; } else if (scopeFuncParent.isName() && scopeFuncParent.getString().equals(name)) { recursive = true; } else { // CASE #2: // Suppose name is Foo, we keep look up the scope stack to look for // a scope with "Foo.prototype.bar = function() { ..... " for (Scope s = t.getScope(); s.getParent() != null; s = s.getParent()) { Node curRoot = s.getRootNode(); if (curRoot.getParent().isAssign()) { Node owner = curRoot.getParent().getFirstChild(); while (owner.isGetProp()) { owner = owner.getFirstChild(); } if (owner.isName() && owner.getString().equals(name)) { recursive = true; break; } } } } } if (!recursive) { info.addUsedModule(t.getModule()); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } // Skip empty and exported names String name = n.getString(); if (name.isEmpty() || compiler.getCodingConvention().isExported(name)) { return; } // If the JSCompiler can't find a Var for this string, then all // bets are off. This sometimes occurs with closures. Alternately, we skip // non-global variables Var v = t.getScope().getVar(name); if (v == null || !v.isGlobal()) { return; } NamedInfo info = getNamedInfo(v); if (info.allowMove) { if (maybeProcessDeclaration(t, n, parent, info)) { // Check to see if the declaration is conditional starting at the // grandparent of the name node. Since a function declaration // is considered conditional (the function might not be called) // we would need to skip the parent in this check as the name could // just be a function itself. if (hasConditionalAncestor(parent.getParent())) { info.allowMove = false; } } else { // Otherwise, it's a reference processReference(t, info, name); } } } /** * Determines whether the given NAME node belongs to a declaration that * can be moved across modules. If it is, registers it properly. * * There are four types of movable declarations: * 1) var NAME = [movable object]; * 2) function NAME() {} * 3) NAME = [movable object]; * NAME.prop = [movable object]; * NAME.prop.prop2 = [movable object]; * etc. * 4) Class-defining function calls, like "inherits" and "mixin". * NAME.inherits([some other name]); * where "movable object" is a literal or a function. */ private boolean maybeProcessDeclaration(NodeTraversal t, Node name, Node parent, NamedInfo info) { Node gramps = parent.getParent(); switch (parent.getType()) { case Token.VAR: if (canMoveValue(name.getFirstChild())) { return info.addDeclaration( new Declaration(t.getModule(), name)); } return false; case Token.FUNCTION: if (NodeUtil.isFunctionDeclaration(parent)) { return info.addDeclaration( new Declaration(t.getModule(), name)); } return false; case Token.ASSIGN: case Token.GETPROP: Node child = name; // Look for assignment expressions where the name is the root // of a qualified name on the left hand side of the assignment. for (Node current : name.getAncestors()) { if (current.isGetProp()) { // fallthrough } else if (current.isAssign() && current.getFirstChild() == child) { Node currentParent = current.getParent(); if (currentParent.isExprResult() && canMoveValue(current.getLastChild())) { return info.addDeclaration( new Declaration(t.getModule(), current)); } } else { return false; } child = current; } return false; case Token.CALL: if (NodeUtil.isExprCall(gramps)) { SubclassRelationship relationship = compiler.getCodingConvention().getClassesDefinedByCall(parent); if (relationship != null && name.getString().equals(relationship.subclassName)) { return info.addDeclaration( new Declaration(t.getModule(), parent)); } } return false; default: return false; } } /** * Determines whether the given value is eligible to be moved across modules. */ private boolean canMoveValue(Node n) { // the value is only movable if it's // a) nothing, // b) a constant literal, // c) a function, or // d) an array/object literal of movable values. // e) a function stub generated by CrossModuleMethodMotion. if (n == null || NodeUtil.isLiteralValue(n, true) || n.isFunction()) { return true; } else if (n.isCall()) { Node functionName = n.getFirstChild(); return functionName.isName() && (functionName.getString().equals( CrossModuleMethodMotion.STUB_METHOD_NAME) || functionName.getString().equals( CrossModuleMethodMotion.UNSTUB_METHOD_NAME)); } else if (n.isArrayLit() || n.isObjectLit()) { boolean isObjectLit = n.isObjectLit(); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!canMoveValue(isObjectLit ? child.getFirstChild() : child)) { return false; } } return true; } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExpressionDecomposer.java0000644000175000017500000007604312115204405027671 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Supplier; import com.google.javascript.jscomp.MakeDeclaredNamesUnique.ContextualRenamer; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Methods necessary for partially or full decomposing an expression. Initially * this is intended to expanded the locations were inlining can occur, but has * other uses as well. * * For example: * var x = y() + z(); * * Becomes: * var a = y(); * var b = z(); * x = a + b; * * @author johnlenz@google.com (John Lenz) */ class ExpressionDecomposer { /** * @see #canExposeExpression */ enum DecompositionType { UNDECOMPOSABLE, MOVABLE, DECOMPOSABLE } private final AbstractCompiler compiler; private final Supplier safeNameIdSupplier; private final Set knownConstants; public ExpressionDecomposer( AbstractCompiler compiler, Supplier safeNameIdSupplier, Set constNames) { Preconditions.checkNotNull(compiler); Preconditions.checkNotNull(safeNameIdSupplier); Preconditions.checkNotNull(constNames); this.compiler = compiler; this.safeNameIdSupplier = safeNameIdSupplier; this.knownConstants = constNames; } // An arbitrary limit to prevent catch infinite recursion. private static final int MAX_INTERATIONS = 100; /** * If required, rewrite the statement containing the expression. * @param expression The expression to be exposed. * @see #canExposeExpression */ void maybeExposeExpression(Node expression) { // If the expression needs to exposed. int i = 0; while (DecompositionType.DECOMPOSABLE == canExposeExpression(expression)) { exposeExpression(expression); i++; if (i > MAX_INTERATIONS) { throw new IllegalStateException( "DecomposeExpression depth exceeded on :\n" + expression.toStringTree()); } } } /** * Perform any rewriting necessary so that the specified expression * is movable. This is a partial expression decomposition. * @see #canExposeExpression */ void exposeExpression(Node expression) { Node expressionRoot = findExpressionRoot(expression); Preconditions.checkState(expressionRoot != null); exposeExpression(expressionRoot, expression); compiler.reportCodeChange(); } // TODO(johnlenz): This is not currently used by the function inliner, // as moving the call out of the expression before the actual function // results in additional variables being introduced. As the variable // inliner is improved, this might be a viable option. /** * Extract the specified expression from its parent expression. * @see #canExposeExpression */ void moveExpression(Node expression) { String resultName = getResultValueName(); Node injectionPoint = findInjectionPoint(expression); Preconditions.checkNotNull(injectionPoint); Node injectionPointParent = injectionPoint.getParent(); Preconditions.checkNotNull(injectionPointParent); Preconditions.checkState(NodeUtil.isStatementBlock(injectionPointParent)); // Replace the expression with a reference to the new name. Node expressionParent = expression.getParent(); expressionParent.replaceChild( expression, IR.name(resultName)); // Re-add the expression at the appropriate place. Node newExpressionRoot = NodeUtil.newVarNode(resultName, expression); injectionPointParent.addChildBefore(newExpressionRoot, injectionPoint); compiler.reportCodeChange(); } /** * Rewrite the expression such that the sub-expression is in a movable * expression statement while maintaining evaluation order. * * Two types of subexpressions are extracted from the source expression: * 1) subexpressions with side-effects. * 2) conditional expressions, that contain the call, which are transformed * into IF statements. * * The following terms are used: * expressionRoot: The top-level node before which the any extracted * expressions should be placed before. * nonconditionalExpr: The node that will be extracted either expres. * */ private void exposeExpression(Node expressionRoot, Node subExpression) { Node nonconditionalExpr = findNonconditionalParent( subExpression, expressionRoot); // Before extraction, record whether there are side-effect boolean hasFollowingSideEffects = NodeUtil.mayHaveSideEffects( nonconditionalExpr, compiler); Node exprInjectionPoint = findInjectionPoint(nonconditionalExpr); DecompositionState state = new DecompositionState(); state.sideEffects = hasFollowingSideEffects; state.extractBeforeStatement = exprInjectionPoint; // Extract expressions in the reverse order of their evaluation. for (Node grandchild = null, child = nonconditionalExpr, parent = child.getParent(); parent != expressionRoot; grandchild = child, child = parent, parent = child.getParent()) { int parentType = parent.getType(); Preconditions.checkState( !isConditionalOp(parent) || child == parent.getFirstChild()); if (parentType == Token.ASSIGN) { if (isSafeAssign(parent, state.sideEffects)) { // It is always safe to inline "foo()" for expressions such as // "a = b = c = foo();" // As the assignment is unaffected by side effect of "foo()" // and the names assigned-to can not influence the state before // the call to foo. // // This is not true of more complex LHS values, such as // a.x = foo(); // next().x = foo(); // in these cases the checks below are necessary. } else { // Alias "next()" in "next().foo" Node left = parent.getFirstChild(); int type = left.getType(); if (left != child) { Preconditions.checkState(NodeUtil.isGet(left)); if (type == Token.GETELEM) { decomposeSubExpressions(left.getLastChild(), null, state); } decomposeSubExpressions(left.getFirstChild(), null, state); } } } else if (parentType == Token.CALL && NodeUtil.isGet(parent.getFirstChild())) { Node functionExpression = parent.getFirstChild(); decomposeSubExpressions(functionExpression.getNext(), child, state); // Now handle the call expression if (isExpressionTreeUnsafe(functionExpression, state.sideEffects) && functionExpression.getFirstChild() != grandchild) { // TODO(johnlenz): In Internet Explorer, non-JavaScript objects such // as DOM objects can not be decomposed. Preconditions.checkState(allowObjectCallDecomposing(), "Object method calls can not be decomposed."); // Either there were preexisting side-effects, or this node has // side-effects. state.sideEffects = true; // Rewrite the call so "this" is preserved. Node replacement = rewriteCallExpression(parent, state); // Continue from here. parent = replacement; } } else if (parentType == Token.OBJECTLIT) { decomposeObjectLiteralKeys(parent.getFirstChild(), child, state); } else { decomposeSubExpressions(parent.getFirstChild(), child, state); } } // Now extract the expression that the decomposition is being performed to // to allow to be moved. All expressions that need to be evaluated before // this have been extracted, so add the expression statement after the // other extracted expressions and the original statement (or replace // the original statement. if (nonconditionalExpr == subExpression) { // Don't extract the call, as that introduces an extra constant VAR // that will simply need to be inlined back. It will be handled as // an EXPRESSION call site type. // Node extractedCall = extractExpression(decomposition, expressionRoot); } else { Node parent = nonconditionalExpr.getParent(); boolean needResult = !parent.isExprResult(); extractConditional(nonconditionalExpr, exprInjectionPoint, needResult); } } private static boolean allowObjectCallDecomposing() { return false; } /** * @return Whether the node may represent an external method. */ private boolean maybeExternMethod(Node node) { // TODO(johnlenz): Provide some mechanism for determining this. return true; } /** * @return "expression" or the node closest to "expression", that does not * have a conditional ancestor. */ private static Node findNonconditionalParent( Node subExpression, Node expressionRoot) { Node result = subExpression; for (Node child = subExpression, parent = child.getParent(); parent != expressionRoot; child = parent, parent = child.getParent()) { if (isConditionalOp(parent)) { // Only the first child is always executed, if the function may never // be called, don't inline it. if (child != parent.getFirstChild()) { result = parent; } } } return result; } /** * A simple class to track two things: * - whether side effects have been seen. * - the last statement inserted */ private static class DecompositionState { boolean sideEffects; Node extractBeforeStatement; } /** * Decompose an object literal. * @param key The object literal key. * @param stopNode A node after which to stop iterating. */ private void decomposeObjectLiteralKeys( Node key, Node stopNode, DecompositionState state) { if (key == null || key == stopNode) { return; } decomposeObjectLiteralKeys(key.getNext(), stopNode, state); decomposeSubExpressions(key.getFirstChild(), stopNode, state); } /** * @param n The node with which to start iterating. * @param stopNode A node after which to stop iterating. */ private void decomposeSubExpressions( Node n, Node stopNode, DecompositionState state) { if (n == null || n == stopNode) { return; } // Never try to decompose an object literal key. Preconditions.checkState(!NodeUtil.isObjectLitKey(n)); // Decompose the children in reverse evaluation order. This simplifies // determining if the any of the children following have side-effects. // If they do we need to be more aggressive about removing values // from the expression. decomposeSubExpressions( n.getNext(), stopNode, state); // Now this node. // TODO(johnlenz): Move "safety" code to a shared class. if (isExpressionTreeUnsafe(n, state.sideEffects)) { // Either there were preexisting side-effects, or this node has // side-effects. state.sideEffects = true; state.extractBeforeStatement = extractExpression( n, state.extractBeforeStatement); } } /** * * @param expr The conditional expression to extract. * @param injectionPoint The before which extracted expression, would be * injected. * @param needResult Whether the result of the expression is required. * @return The node that contains the logic of the expression after * extraction. */ private Node extractConditional( Node expr, Node injectionPoint, boolean needResult) { Node parent = expr.getParent(); String tempName = getTempValueName(); // Break down the conditional. Node first = expr.getFirstChild(); Node second = first.getNext(); Node last = expr.getLastChild(); // Isolate the children nodes. expr.detachChildren(); // Transform the conditional to an IF statement. Node cond = null; Node trueExpr = IR.block().srcref(expr); Node falseExpr = IR.block().srcref(expr); switch (expr.getType()) { case Token.HOOK: // a = x?y:z --> if (x) {a=y} else {a=z} cond = first; trueExpr.addChildToFront(NodeUtil.newExpr( buildResultExpression(second, needResult, tempName))); falseExpr.addChildToFront(NodeUtil.newExpr( buildResultExpression(last, needResult, tempName))); break; case Token.AND: // a = x&&y --> if (a=x) {a=y} else {} cond = buildResultExpression(first, needResult, tempName); trueExpr.addChildToFront(NodeUtil.newExpr( buildResultExpression(last, needResult, tempName))); break; case Token.OR: // a = x||y --> if (a=x) {} else {a=y} cond = buildResultExpression(first, needResult, tempName); falseExpr.addChildToFront(NodeUtil.newExpr( buildResultExpression(last, needResult, tempName))); break; default: // With a valid tree we should never get here. throw new IllegalStateException("Unexpected."); } Node ifNode; if (falseExpr.hasChildren()) { ifNode = IR.ifNode(cond, trueExpr, falseExpr); } else { ifNode = IR.ifNode(cond, trueExpr); } ifNode.copyInformationFrom(expr); if (needResult) { Node tempVarNode = NodeUtil.newVarNode(tempName, null) .copyInformationFromForTree(expr); Node injectionPointParent = injectionPoint.getParent(); injectionPointParent.addChildBefore(tempVarNode, injectionPoint); injectionPointParent.addChildAfter(ifNode, tempVarNode); // Replace the expression with the temporary name. Node replacementValueNode = IR.name(tempName); parent.replaceChild(expr, replacementValueNode); } else { // Only conditionals that are the direct child of an expression statement // don't need results, for those simply replace the expression statement. Preconditions.checkArgument(parent.isExprResult()); Node gramps = parent.getParent(); gramps.replaceChild(parent, ifNode); } return ifNode; } /** * Create an expression tree for an expression. * If the result of the expression is needed, then: * ASSIGN * tempName * expr * otherwise, simply: * expr */ private static Node buildResultExpression( Node expr, boolean needResult, String tempName) { if (needResult) { return IR.assign( IR.name(tempName), expr).srcrefTree(expr); } else { return expr; } } private boolean isConstantName(Node n, Set knownConstants) { // Non-constant names values may have been changed. return n.isName() && (NodeUtil.isConstantName(n) || knownConstants.contains(n.getString())); } /** * @param expr The expression to extract. * @param injectionPoint The node before which to added the extracted * expression. * @return The extract statement node. */ private Node extractExpression(Node expr, Node injectionPoint) { Node parent = expr.getParent(); boolean isLhsOfAssignOp = NodeUtil.isAssignmentOp(parent) && !parent.isAssign() && parent.getFirstChild() == expr; Node firstExtractedNode = null; // Expressions on the LHS of an assignment-op must have any possible // side-effects extracted as the value must be duplicated: // next().foo += 2; // becomes: // var t1 = next(); // t1.foo = t1.foo + 2; if (isLhsOfAssignOp && NodeUtil.isGet(expr)) { for (Node n : expr.children()) { if (!n.isString() && !isConstantName(n, knownConstants)) { Node extractedNode = extractExpression(n, injectionPoint); if (firstExtractedNode == null) { firstExtractedNode = extractedNode; } } } } // The temp is known to be constant. String tempName = getTempConstantValueName(); Node replacementValueNode = IR.name(tempName).srcref(expr); Node tempNameValue; // If it is ASSIGN_XXX, keep the assignment in place and extract the // original value of the LHS operand. if (isLhsOfAssignOp) { Preconditions.checkState(expr.isName() || NodeUtil.isGet(expr)); // Transform "x += 2" into "x = temp + 2" Node opNode = new Node(NodeUtil.getOpFromAssignmentOp(parent)) .copyInformationFrom(parent); Node rightOperand = parent.getLastChild(); parent.setType(Token.ASSIGN); parent.replaceChild(rightOperand, opNode); opNode.addChildToFront(replacementValueNode); opNode.addChildToBack(rightOperand); // The original expression is still being used, so make a clone. tempNameValue = expr.cloneTree(); } else { // Replace the expression with the temporary name. parent.replaceChild(expr, replacementValueNode); // Keep the original node so that CALL expressions can still be found // and inlined properly. tempNameValue = expr; } // Re-add the expression in the declaration of the temporary name. Node tempVarNode = NodeUtil.newVarNode(tempName, tempNameValue); Node injectionPointParent = injectionPoint.getParent(); injectionPointParent.addChildBefore(tempVarNode, injectionPoint); if (firstExtractedNode == null) { firstExtractedNode = tempVarNode; } return firstExtractedNode; } /** * Rewrite the call so "this" is preserved. * a.b(c); * becomes: * var temp1 = a; * var temp0 = temp1.b; * temp0.call(temp1,c); * * @return The replacement node. */ private Node rewriteCallExpression(Node call, DecompositionState state) { Preconditions.checkArgument(call.isCall()); Node first = call.getFirstChild(); Preconditions.checkArgument(NodeUtil.isGet(first)); // Extracts the expression representing the function to call. For example: // "a['b'].c" from "a['b'].c()" Node getVarNode = extractExpression( first, state.extractBeforeStatement); state.extractBeforeStatement = getVarNode; // Extracts the object reference to be used as "this". For example: // "a['b']" from "a['b'].c" Node getExprNode = getVarNode.getFirstChild().getFirstChild(); Preconditions.checkArgument(NodeUtil.isGet(getExprNode)); Node thisVarNode = extractExpression( getExprNode.getFirstChild(), state.extractBeforeStatement); state.extractBeforeStatement = thisVarNode; // Rewrite the CALL expression. Node thisNameNode = thisVarNode.getFirstChild(); Node functionNameNode = getVarNode.getFirstChild(); // CALL // GETPROP // functionName // "call" // thisName // original-parameter1 // original-parameter2 // ... Node newCall = IR.call( IR.getprop( functionNameNode.cloneNode(), IR.string("call")), thisNameNode.cloneNode()).srcref(call); // Throw away the call name call.removeFirstChild(); if (call.hasChildren()) { // Add the call parameters to the new call. newCall.addChildrenToBack(call.removeChildren()); } // Replace the call. Node callParent = call.getParent(); callParent.replaceChild(call, newCall); return newCall; } private String tempNamePrefix = "JSCompiler_temp"; private String resultNamePrefix = "JSCompiler_inline_result"; /** * Allow the temp name to be overridden to make tests more readable. */ @VisibleForTesting public void setTempNamePrefix(String prefix) { this.tempNamePrefix = prefix; } /** * Create a unique temp name. */ private String getTempValueName(){ return tempNamePrefix + ContextualRenamer.UNIQUE_ID_SEPARATOR + safeNameIdSupplier.get(); } /** * Allow the temp name to be overridden to make tests more readable. */ @VisibleForTesting public void setResultNamePrefix(String prefix) { this.resultNamePrefix = prefix; } /** * Create a unique name for call results. */ private String getResultValueName() { return resultNamePrefix + ContextualRenamer.UNIQUE_ID_SEPARATOR + safeNameIdSupplier.get(); } /** * Create a constant unique temp name. */ private String getTempConstantValueName(){ String name = tempNamePrefix + "_const" + ContextualRenamer.UNIQUE_ID_SEPARATOR + safeNameIdSupplier.get(); this.knownConstants.add(name); return name; } /** * @return For the subExpression, find the nearest statement Node before which * it can be inlined. Null if no such location can be found. */ static Node findInjectionPoint(Node subExpression) { Node expressionRoot = findExpressionRoot(subExpression); Preconditions.checkNotNull(expressionRoot); Node injectionPoint = expressionRoot; Node parent = injectionPoint.getParent(); while (parent.isLabel()) { injectionPoint = parent; parent = injectionPoint.getParent(); } Preconditions.checkState( NodeUtil.isStatementBlock(injectionPoint.getParent())); return injectionPoint; } /** * @return Whether the node is a conditional op. */ private static boolean isConditionalOp(Node n) { switch(n.getType()) { case Token.HOOK: case Token.AND: case Token.OR: return true; default: return false; } } /** * @return The statement containing the expression. null if subExpression * is not contain by in by a Node where inlining is known to be possible. * For example, a WHILE node condition expression. */ static Node findExpressionRoot(Node subExpression) { Node child = subExpression; for (Node parent : child.getAncestors()) { int parentType = parent.getType(); switch (parentType) { // Supported expression roots: // SWITCH and IF can have multiple children, but the CASE, DEFAULT, // or BLOCK will be encountered first for any of the children other // than the condition. case Token.EXPR_RESULT: case Token.IF: case Token.SWITCH: case Token.RETURN: case Token.VAR: Preconditions.checkState(child == parent.getFirstChild()); return parent; // Any of these indicate an unsupported expression: case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: case Token.CASE: case Token.DEFAULT_CASE: return null; } child = parent; } throw new IllegalStateException("Unexpected AST structure."); } /** * Determine whether a expression is movable, or can be be made movable be * decomposing the containing expression. * * An subExpression is MOVABLE if it can be replaced with a temporary holding * its results and moved to immediately before the root of the expression. * There are three conditions that must be met for this to occur: * 1) There must be a location to inject a statement for the expression. For * example, this condition can not be met if the expression is a loop * condition or CASE condition. * 2) If the expression can be affect by side-effects, there can not be a * side-effect between original location and the expression root. * 3) If the expression has side-effects, there can not be any other * expression that can be effected between the original location and the * expression root. * * An expression is DECOMPOSABLE if it can be rewritten so that an * subExpression is MOVABLE. * * An expression is decomposed by moving any other sub-expressions that * preventing an subExpression from being MOVABLE. * * @return Whether This is a call that can be moved to an new point in the * AST to allow it to be inlined. */ DecompositionType canExposeExpression(Node subExpression) { Node expressionRoot = findExpressionRoot(subExpression); if (expressionRoot != null) { return isSubexpressionMovable(expressionRoot, subExpression); } return DecompositionType.UNDECOMPOSABLE; } /** * Walk the AST from the call site to the expression root and verify that * the portions of the expression that are evaluated before the call are: * 1) Unaffected by the the side-effects, if any, of the call. * 2) That there are no side-effects, that may influence the call. * * For example, if x has side-effects: * a = 1 + x(); * the call to x can be moved because "a" final value of a can not be * influenced by x(), but in: * a = b + x(); * the call to x can not be moved because the value of b may be modified * by the call to x. * * If x is without side-effects in: * a = b + x(); * the call to x can be moved, but in: * a = (b.foo = c) + x(); * the call to x can not be moved because the value of b.foo may be referenced * by x(). Note: this is true even if b is a local variable; the object that * b refers to may have a global alias. * * @return UNDECOMPOSABLE if the expression can not be moved, DECOMPOSABLE if * decomposition is required before the expression can be moved, otherwise * MOVABLE. */ private DecompositionType isSubexpressionMovable( Node expressionRoot, Node subExpression) { boolean requiresDecomposition = false; boolean seenSideEffects = NodeUtil.mayHaveSideEffects( subExpression, compiler); Node child = subExpression; for (Node parent : child.getAncestors()) { if (parent == expressionRoot) { // Done. The walk back to the root of the expression is complete, and // nothing was encountered that blocks the call from being moved. return requiresDecomposition ? DecompositionType.DECOMPOSABLE : DecompositionType.MOVABLE; } if (isConditionalOp(parent)) { // Only the first child is always executed, otherwise it must be // decomposed. if (child != parent.getFirstChild()) { requiresDecomposition = true; } } else { // Only inline the call if none of the preceding siblings in the // expression have side-effects, and are unaffected by the side-effects, // if any, of the call in question. // NOTE: This depends on the siblings being in the same order as they // are evaluated. // SPECIAL CASE: Assignment to a simple name if (isSafeAssign(parent, seenSideEffects)) { // It is always safe to inline "foo()" for expressions such as // "a = b = c = foo();" // As the assignment is unaffected by side effect of "foo()" // and the names assigned-to can not influence the state before // the call to foo. // // This is not true of more complex LHS values, such as // a.x = foo(); // next().x = foo(); // in these cases the checks below are necessary. } else { // Everything else. for (Node n : parent.children()) { if (n == child) { // None of the preceding siblings have side-effects. // This is OK. break; } if (isExpressionTreeUnsafe( n, seenSideEffects)) { seenSideEffects = true; requiresDecomposition = true; } } // In Internet Explorer, DOM objects and other external objects // methods can not be called indirectly, as is required when the // object or its property can be side-effected. For example, // when exposing expression f() (with side-effects) in: x.m(f()) // either the value of x or its property m might have changed, so // both the 'this' value ('x') and the function to be called ('x.m') // need to be preserved. Like so: // var t1 = x, t2 = x.m, t3 = f(); // t2.call(t1, t3); // As IE doesn't support the call to these non-JavaScript objects // methods in this way. We can't do this. // We don't currently distinguish between these types of objects // in the extern definitions and if we did we would need accurate // type information. // Node first = parent.getFirstChild(); if (requiresDecomposition && parent.isCall() && NodeUtil.isGet(first)) { if (maybeExternMethod(first)) { return DecompositionType.UNDECOMPOSABLE; } else { return DecompositionType.DECOMPOSABLE; } } } } // Continue looking up the expression tree. child = parent; } // With a valid tree we should never get here. throw new IllegalStateException("Unexpected."); } /** * It is always safe to inline "foo()" for expressions such as * "a = b = c = foo();" * As the assignment is unaffected by side effect of "foo()" * and the names assigned-to can not influence the state before * the call to foo. * * It is also safe in cases like where the object is constant: * CONST_NAME.a = foo() * CONST_NAME[CONST_VALUE] = foo(); * * This is not true of more complex LHS values, such as * a.x = foo(); * next().x = foo(); * in these cases the checks below are necessary. * * @param seenSideEffects If true, check to see if node-tree maybe affected by * side-effects, otherwise if the tree has side-effects. @see * isExpressionTreeUnsafe * @return Whether the assignment is safe from side-effects. */ private boolean isSafeAssign(Node n, boolean seenSideEffects) { if (n.isAssign()) { Node lhs = n.getFirstChild(); switch (lhs.getType()) { case Token.NAME: return true; case Token.GETPROP: return !isExpressionTreeUnsafe(lhs.getFirstChild(), seenSideEffects); case Token.GETELEM: return !isExpressionTreeUnsafe(lhs.getFirstChild(), seenSideEffects) && !isExpressionTreeUnsafe(lhs.getLastChild(), seenSideEffects); } } return false; } /** * @return Whether anything in the expression tree prevents a call from * being moved. */ private boolean isExpressionTreeUnsafe( Node n, boolean followingSideEffectsExist) { if (followingSideEffectsExist) { // If the call to be inlined has side-effects, check to see if this // expression tree can be affected by any side-effects. // This is a superset of "NodeUtil.mayHaveSideEffects". return NodeUtil.canBeSideEffected(n, this.knownConstants); } else { // The function called doesn't have side-effects but check to see if there // are side-effects that that may affect it. return NodeUtil.mayHaveSideEffects(n, compiler); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckSuspiciousCode.java0000644000175000017500000000617612115204405027410 0ustar apoapo/* * Copyright 2012 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Checks for common errors, such as misplaced semicolons: *
       * if (x); act_now();
       * 
      * or comparison against NaN: *
       * if (x === NaN) act();
       * 
      * and generates warnings. * * @author johnlenz@google.com (John Lenz) */ final class CheckSuspiciousCode extends AbstractPostOrderCallback { static final DiagnosticType SUSPICIOUS_SEMICOLON = DiagnosticType.warning( "JSC_SUSPICIOUS_SEMICOLON", "If this if/for/while really shouldn't have a body, use {}"); static final DiagnosticType SUSPICIOUS_COMPARISON_WITH_NAN = DiagnosticType.warning( "JSC_SUSPICIOUS_NAN", "Comparison again NaN is always false. Did you mean isNaN()?"); CheckSuspiciousCode() { } @Override public void visit(NodeTraversal t, Node n, Node parent) { checkMissingSemicolon(t, n); checkNaN(t, n); } private void checkMissingSemicolon(NodeTraversal t, Node n) { switch (n.getType()) { case Token.IF: Node trueCase = n.getFirstChild().getNext(); reportIfWasEmpty(t, trueCase); Node elseCase = trueCase.getNext(); if (elseCase != null) { reportIfWasEmpty(t, elseCase); } break; case Token.WHILE: case Token.FOR: reportIfWasEmpty(t, NodeUtil.getLoopCodeBlock(n)); break; } } private void reportIfWasEmpty(NodeTraversal t, Node block) { Preconditions.checkState(block.isBlock()); // A semicolon is distinguished from a block without children by // annotating it with EMPTY_BLOCK. Blocks without children are // usually intentional, especially with loops. if (!block.hasChildren() && block.wasEmptyNode()) { t.getCompiler().report( t.makeError(block, SUSPICIOUS_SEMICOLON)); } } private void checkNaN(NodeTraversal t, Node n) { switch (n.getType()) { case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LT: case Token.NE: case Token.SHEQ: case Token.SHNE: reportIfNaN(t, n.getFirstChild()); reportIfNaN(t, n.getLastChild()); } } private void reportIfNaN(NodeTraversal t, Node n) { if (NodeUtil.isNaN(n)) { t.getCompiler().report( t.makeError(n.getParent(), SUSPICIOUS_COMPARISON_WITH_NAN)); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SimpleDefinitionFinder.java0000644000175000017500000003721012115204405030074 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.DefinitionsRemover.ExternalNameOnlyDefinition; import com.google.javascript.jscomp.DefinitionsRemover.UnknownDefinition; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.List; import java.util.Map; /** * Simple name-based definition gatherer that implements * {@link DefinitionProvider}. * * It treats all variable writes as happening in the global scope and * treats all objects as capable of having the same set of properties. * The current implementation only handles definitions whose right * hand side is an immutable value or function expression. All * complex definitions are treated as unknowns. * */ class SimpleDefinitionFinder implements CompilerPass, DefinitionProvider { private final AbstractCompiler compiler; private final Map definitionSiteMap; private final Multimap nameDefinitionMultimap; private final Multimap nameUseSiteMultimap; public SimpleDefinitionFinder(AbstractCompiler compiler) { this.compiler = compiler; this.definitionSiteMap = Maps.newLinkedHashMap(); this.nameDefinitionMultimap = LinkedHashMultimap.create(); this.nameUseSiteMultimap = LinkedHashMultimap.create(); } /** * Returns the collection of definition sites found during traversal. * * @return definition site collection. */ public Collection getDefinitionSites() { return definitionSiteMap.values(); } private DefinitionSite getDefinitionAt(Node node) { return definitionSiteMap.get(node); } DefinitionSite getDefinitionForFunction(Node function) { Preconditions.checkState(function.isFunction()); return getDefinitionAt(getNameNodeFromFunctionNode(function)); } @Override public Collection getDefinitionsReferencedAt(Node useSite) { if (definitionSiteMap.containsKey(useSite)) { return null; } if (useSite.isGetProp()) { String propName = useSite.getLastChild().getString(); if (propName.equals("apply") || propName.equals("call")) { useSite = useSite.getFirstChild(); } } String name = getSimplifiedName(useSite); if (name != null) { Collection defs = nameDefinitionMultimap.get(name); if (!defs.isEmpty()) { return defs; } else { return null; } } else { return null; } } @Override public void process(Node externs, Node source) { NodeTraversal.traverse( compiler, externs, new DefinitionGatheringCallback(true)); NodeTraversal.traverse( compiler, source, new DefinitionGatheringCallback(false)); NodeTraversal.traverse( compiler, source, new UseSiteGatheringCallback()); } /** * Returns a collection of use sites that may refer to provided * definition. Returns an empty collection if the definition is not * used anywhere. * * @param definition Definition of interest. * @return use site collection. */ Collection getUseSites(Definition definition) { String name = getSimplifiedName(definition.getLValue()); return nameUseSiteMultimap.get(name); } /** * Extract a name from a node. In the case of GETPROP nodes, * replace the namespace or object expression with "this" for * simplicity and correctness at the expense of inefficiencies due * to higher chances of name collisions. * * TODO(user) revisit. it would be helpful to at least use fully * qualified names in the case of namespaces. Might not matter as * much if this pass runs after "collapsing properties". */ private static String getSimplifiedName(Node node) { if (node.isName()) { String name = node.getString(); if (name != null && !name.isEmpty()) { return name; } else { return null; } } else if (node.isGetProp()) { return "this." + node.getLastChild().getString(); } return null; } private class DefinitionGatheringCallback extends AbstractPostOrderCallback { private boolean inExterns; DefinitionGatheringCallback(boolean inExterns) { this.inExterns = inExterns; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { // Arguments of external functions should not count as name // definitions. They are placeholder names for documentation // purposes only which are not reachable from anywhere. if (inExterns && node.isName() && parent.isParamList()) { return; } Definition def = DefinitionsRemover.getDefinition(node, inExterns); if (def != null) { String name = getSimplifiedName(def.getLValue()); if (name != null) { Node rValue = def.getRValue(); if ((rValue != null) && !NodeUtil.isImmutableValue(rValue) && !rValue.isFunction()) { // Unhandled complex expression Definition unknownDef = new UnknownDefinition(def.getLValue(), inExterns); def = unknownDef; } // TODO(johnlenz) : remove this stub dropping code if it becomes // illegal to have untyped stubs in the externs definitions. if (inExterns) { // We need special handling of untyped externs stubs here: // the stub should be dropped if the name is provided elsewhere. List stubsToRemove = Lists.newArrayList(); String qualifiedName = node.getQualifiedName(); // If there is no qualified name for this, then there will be // no stubs to remove. This will happen if node is an object // literal key. if (qualifiedName != null) { for (Definition prevDef : nameDefinitionMultimap.get(name)) { if (prevDef instanceof ExternalNameOnlyDefinition && !jsdocContainsDeclarations(node)) { String prevName = prevDef.getLValue().getQualifiedName(); if (qualifiedName.equals(prevName)) { // Drop this stub, there is a real definition. stubsToRemove.add(prevDef); } } } for (Definition prevDef : stubsToRemove) { nameDefinitionMultimap.remove(name, prevDef); } } } nameDefinitionMultimap.put(name, def); definitionSiteMap.put(node, new DefinitionSite(node, def, traversal.getModule(), traversal.inGlobalScope(), inExterns)); } } if (inExterns && (parent != null) && parent.isExprResult()) { String name = getSimplifiedName(node); if (name != null) { // TODO(johnlenz) : remove this code if it becomes illegal to have // stubs in the externs definitions. // We need special handling of untyped externs stubs here: // the stub should be dropped if the name is provided elsewhere. // We can't just drop the stub now as it needs to be used as the // externs definition if no other definition is provided. boolean dropStub = false; if (!jsdocContainsDeclarations(node)) { String qualifiedName = node.getQualifiedName(); if (qualifiedName != null) { for (Definition prevDef : nameDefinitionMultimap.get(name)) { String prevName = prevDef.getLValue().getQualifiedName(); if (qualifiedName.equals(prevName)) { dropStub = true; break; } } } } if (!dropStub) { // Incomplete definition Definition definition = new ExternalNameOnlyDefinition(node); nameDefinitionMultimap.put(name, definition); definitionSiteMap.put(node, new DefinitionSite(node, definition, traversal.getModule(), traversal.inGlobalScope(), inExterns)); } } } } /** * @return Whether the node has a JSDoc that actually declares something. */ private boolean jsdocContainsDeclarations(Node node) { JSDocInfo info = node.getJSDocInfo(); return (info != null && info.containsDeclaration()); } } private class UseSiteGatheringCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal traversal, Node node, Node parent) { Collection defs = getDefinitionsReferencedAt(node); if (defs == null) { return; } Definition first = defs.iterator().next(); String name = getSimplifiedName(first.getLValue()); Preconditions.checkNotNull(name); nameUseSiteMultimap.put( name, new UseSite(node, traversal.getScope(), traversal.getModule())); } } /** * @param use A use site to check. * @return Whether the use is a call or new. */ static boolean isCallOrNewSite(UseSite use) { Node call = use.node.getParent(); if (call == null) { // The node has been removed from the AST. return false; } // We need to make sure we're dealing with a call to the function we're // optimizing. If the the first child of the parent is not the site, this // is a nested call and it's a call to another function. return NodeUtil.isCallOrNew(call) && call.getFirstChild() == use.node; } boolean canModifyDefinition(Definition definition) { if (isExported(definition)) { return false; } // Don't modify unused definitions for two reasons: // 1) It causes unnecessary churn // 2) Other definitions might be used to reflect on this one using // goog.reflect.object (the check for definitions with uses is below). Collection useSites = getUseSites(definition); if (useSites.isEmpty()) { return false; } for (UseSite site : useSites) { // This catches the case where an object literal in goog.reflect.object // and a prototype method have the same property name. // NOTE(nicksantos): Maps and trogedit both do this by different // mechanisms. Node nameNode = site.node; Collection singleSiteDefinitions = getDefinitionsReferencedAt(nameNode); if (singleSiteDefinitions.size() > 1) { return false; } Preconditions.checkState(!singleSiteDefinitions.isEmpty()); Preconditions.checkState(singleSiteDefinitions.contains(definition)); } return true; } /** * @return Whether the definition is directly exported. */ private boolean isExported(Definition definition) { // Assume an exported method result is used. Node lValue = definition.getLValue(); if (lValue == null) { return true; } String partialName; if (lValue.isGetProp()) { partialName = lValue.getLastChild().getString(); } else if (lValue.isName()) { partialName = lValue.getString(); } else { // GETELEM is assumed to be an export or other expression are unknown // uses. return true; } CodingConvention codingConvention = compiler.getCodingConvention(); if (codingConvention.isExported(partialName)) { return true; } return false; } /** * @return Whether the function is defined in a non-aliasing expression. */ static boolean isSimpleFunctionDeclaration(Node fn) { Node parent = fn.getParent(); Node gramps = parent.getParent(); // Simple definition finder doesn't provide useful results in some // cases, specifically: // - functions with recursive definitions // - functions defined in object literals // - functions defined in array literals // Here we defined a set of known function declaration that are 'ok'. // Some projects seem to actually define "JSCompiler_renameProperty" // rather than simply having an extern definition. Don't mess with it. Node nameNode = SimpleDefinitionFinder.getNameNodeFromFunctionNode(fn); if (nameNode != null && nameNode.isName()) { String name = nameNode.getString(); if (name.equals(NodeUtil.JSC_PROPERTY_NAME_FN) || name.equals( ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) { return false; } } // example: function a(){}; if (NodeUtil.isFunctionDeclaration(fn)) { return true; } // example: a = function(){}; // example: var a = function(){}; if (fn.getFirstChild().getString().isEmpty() && (NodeUtil.isExprAssign(gramps) || parent.isName())) { return true; } return false; } /** * @return the node defining the name for this function (if any). */ static Node getNameNodeFromFunctionNode(Node function) { Preconditions.checkState(function.isFunction()); if (NodeUtil.isFunctionDeclaration(function)) { return function.getFirstChild(); } else { Node parent = function.getParent(); if (NodeUtil.isVarDeclaration(parent)) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild(); } else if (NodeUtil.isObjectLitKey(parent)) { return parent; } } return null; } /** * Traverse a node and its children and remove any references to from * the structures. */ void removeReferences(Node node) { if (DefinitionsRemover.isDefinitionNode(node)) { DefinitionSite defSite = definitionSiteMap.get(node); if (defSite != null) { Definition def = defSite.definition; String name = getSimplifiedName(def.getLValue()); if (name != null) { this.definitionSiteMap.remove(node); this.nameDefinitionMultimap.remove(name, node); } } } else { Node useSite = node; if (useSite.isGetProp()) { String propName = useSite.getLastChild().getString(); if (propName.equals("apply") || propName.equals("call")) { useSite = useSite.getFirstChild(); } } String name = getSimplifiedName(useSite); if (name != null) { this.nameUseSiteMultimap.remove(name, new UseSite(useSite, null, null)); } } for (Node child : node.children()) { removeReferences(child); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExpandJqueryAliases.java0000644000175000017500000005011712115204405027424 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Logger; /** * Replace known jQuery aliases and methods with standard * conventions so that the compiler recognizes them. Expected * replacements include: * - jQuery.fn -> jQuery.prototype * - jQuery.extend -> expanded into direct object assignments * - jQuery.expandedEach -> expand into direct assignments * * @author chadkillingsworth@missouristate.edu (Chad Killingsworth) */ class ExpandJqueryAliases extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final CodingConvention convention; private static final Logger logger = Logger.getLogger(ExpandJqueryAliases.class.getName()); static final DiagnosticType JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR = DiagnosticType.warning("JSC_JQUERY_UNABLE_TO_EXPAND_INVALID_LIT", "jQuery.expandedEach call cannot be expanded because the first " + "argument must be an object literal or an array of strings " + "literal."); static final DiagnosticType JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR = DiagnosticType.error("JSC_JQUERY_UNABLE_TO_EXPAND_INVALID_NAME", "jQuery.expandedEach expansion would result in the invalid " + "property name \"{0}\"."); static final DiagnosticType JQUERY_USELESS_EACH_EXPANSION = DiagnosticType.warning("JSC_JQUERY_USELESS_EACH_EXPANSION", "jQuery.expandedEach was not expanded as no valid property " + "assignments were encountered. Consider using jQuery.each instead."); private static final Set JQUERY_EXTEND_NAMES = ImmutableSet.of( "jQuery.extend", "jQuery.fn.extend", "jQuery.prototype.extend"); private static final String JQUERY_EXPANDED_EACH_NAME = "jQuery.expandedEach"; private final PeepholeOptimizationsPass peepholePasses; ExpandJqueryAliases(AbstractCompiler compiler) { this.compiler = compiler; this.convention = compiler.getCodingConvention(); // All of the "early" peephole optimizations. // These passes should make the code easier to analyze. // Passes, such as StatementFusion, are omitted for this reason. final boolean late = false; this.peepholePasses = new PeepholeOptimizationsPass(compiler, new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(late), new PeepholeRemoveDeadCode(), new PeepholeFoldConstants(late), new PeepholeCollectPropertyAssignments()); } /** * Check that Node n is a call to one of the jQuery.extend methods that we * can expand. Valid calls are single argument calls where the first argument * is an object literal or two argument calls where the first argument * is a name and the second argument is an object literal. */ public static boolean isJqueryExtendCall(Node n, String qname, AbstractCompiler compiler) { if (JQUERY_EXTEND_NAMES.contains(qname)) { Node firstArgument = n.getNext(); if (firstArgument == null) { return false; } Node secondArgument = firstArgument.getNext(); if ((firstArgument.isObjectLit() && secondArgument == null) || (firstArgument.isName() || NodeUtil.isGet(firstArgument) && !NodeUtil.mayHaveSideEffects(firstArgument, compiler) && secondArgument != null && secondArgument.isObjectLit() && secondArgument.getNext() == null)) { return true; } } return false; } public boolean isJqueryExpandedEachCall(Node call, String qName) { Preconditions.checkArgument(call.isCall()); if (call.getFirstChild() != null && JQUERY_EXPANDED_EACH_NAME.equals(qName)) { return true; } return false; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp() && convention.isPrototypeAlias(n)) { maybeReplaceJqueryPrototypeAlias(n); } else if (n.isCall()) { Node callTarget = n.getFirstChild(); String qName = callTarget.getQualifiedName(); if (isJqueryExtendCall(callTarget, qName, this.compiler)) { maybeExpandJqueryExtendCall(n); } else if (isJqueryExpandedEachCall(n, qName)) { maybeExpandJqueryEachCall(t, n); } } } @Override public void process(Node externs, Node root) { logger.fine("Expanding Jquery Aliases"); NodeTraversal.traverse(compiler, root, this); } private void maybeReplaceJqueryPrototypeAlias(Node n) { // Check to see if this is the assignment of the original alias. // If so, leave it intact. if(NodeUtil.isLValue(n)) { Node maybeAssign = n.getParent(); while (!NodeUtil.isStatement(maybeAssign) && !maybeAssign.isAssign()) { maybeAssign = maybeAssign.getParent(); } if (maybeAssign.isAssign()) { maybeAssign = maybeAssign.getParent(); if (maybeAssign.isBlock() || maybeAssign.isScript() || NodeUtil.isStatement(maybeAssign)) { return; } } } Node fn = n.getLastChild(); if (fn != null) { n.replaceChild(fn, IR.string("prototype")); compiler.reportCodeChange(); } } /** * Expand jQuery.extend (and derivative) calls into direct object assignments * Example: jQuery.extend(obj1, {prop1: val1, prop2: val2}) -> * obj1.prop1 = val1; * obj1.prop2 = val2; */ private void maybeExpandJqueryExtendCall(Node n) { Node callTarget = n.getFirstChild(); Node objectToExtend = callTarget.getNext(); // first argument Node extendArg = objectToExtend.getNext(); // second argument boolean ensureObjectDefined = true; if (extendArg == null) { // Only one argument was specified, so extend jQuery namespace extendArg = objectToExtend; objectToExtend = callTarget.getFirstChild(); ensureObjectDefined = false; } else if (objectToExtend.isGetProp() && (objectToExtend.getLastChild().getString().equals("prototype") || convention.isPrototypeAlias(objectToExtend))) { ensureObjectDefined = false; } // Check for an empty object literal if (!extendArg.hasChildren()) { return; } // Since we are expanding jQuery.extend calls into multiple statements, // encapsulate the new statements in a new block. Node fncBlock = IR.block().srcref(n); if (ensureObjectDefined) { Node assignVal = IR.or(objectToExtend.cloneTree(), IR.objectlit().srcref(n)).srcref(n); Node assign = IR.assign(objectToExtend.cloneTree(), assignVal).srcref(n); fncBlock.addChildrenToFront(IR.exprResult(assign).srcref(n)); } while (extendArg.hasChildren()) { Node currentProp = extendArg.removeFirstChild(); currentProp.setType(Token.STRING); Node propValue = currentProp.removeFirstChild(); Node newProp; if(currentProp.isQuotedString()) { newProp = IR.getelem(objectToExtend.cloneTree(), currentProp).srcref(currentProp); } else { newProp = IR.getprop(objectToExtend.cloneTree(), currentProp).srcref(currentProp); } Node assignNode = IR.assign(newProp, propValue).srcref(currentProp); fncBlock.addChildToBack(IR.exprResult(assignNode).srcref(currentProp)); } // Check to see if the return value is used. If not, replace the original // call with new block. Otherwise, wrap the statements in an // immediately-called anonymous function. if (n.getParent().isExprResult()) { Node parent = n.getParent(); parent.getParent().replaceChild(parent, fncBlock); } else { Node targetVal; if ("jQuery.prototype".equals(objectToExtend.getQualifiedName())) { // When extending the jQuery prototype, return the jQuery namespace. // This is not commonly used. targetVal = objectToExtend.removeFirstChild(); } else { targetVal = objectToExtend.detachFromParent(); } fncBlock.addChildToBack(IR.returnNode(targetVal).srcref(targetVal)); Node fnc = IR.function(IR.name("").srcref(n), IR.paramList().srcref(n), fncBlock); n.replaceChild(callTarget, fnc); n.putBooleanProp(Node.FREE_CALL, true); // remove any other pre-existing call arguments while(fnc.getNext() != null) { n.removeChildAfter(fnc); } } compiler.reportCodeChange(); } /** * Expand a jQuery.expandedEach call * * Expanded jQuery.expandedEach calls will replace the GETELEM nodes of a * property assignment with GETPROP nodes to allow for renaming. */ private void maybeExpandJqueryEachCall(NodeTraversal t, Node n) { Node objectToLoopOver = n.getChildAtIndex(1); if (objectToLoopOver == null) { return; } Node callbackFunction = objectToLoopOver.getNext(); if (callbackFunction == null || !callbackFunction.isFunction()) { return; } // Run the peephole optimizations on the first argument to handle // cases like ("a " + "b").split(" ") peepholePasses.process(null, n.getChildAtIndex(1)); // Create a reference tree Node nClone = n.cloneTree(); objectToLoopOver = nClone.getChildAtIndex(1); // Check to see if the first argument is something we recognize and can // expand. if (!objectToLoopOver.isObjectLit() && !(objectToLoopOver.isArrayLit() && isArrayLitValidForExpansion(objectToLoopOver))) { t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_LIT_ERROR, (String)null); return; } // Find all references to the callback function arguments List keyNodeReferences = Lists.newArrayList(); List valueNodeReferences = Lists.newArrayList(); NodeTraversal.traverse(compiler, NodeUtil.getFunctionBody(callbackFunction), new FindCallbackArgumentReferences(callbackFunction, keyNodeReferences, valueNodeReferences, objectToLoopOver.isArrayLit())); if(keyNodeReferences.size() == 0) { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String)null); return; } Node fncBlock = tryExpandJqueryEachCall(t, nClone, callbackFunction, keyNodeReferences, valueNodeReferences); if (fncBlock != null && fncBlock.hasChildren()) { replaceOriginalJqueryEachCall(n, fncBlock); } else { // We didn't do anything useful ... t.report(n, JQUERY_USELESS_EACH_EXPANSION, (String)null); } } private Node tryExpandJqueryEachCall(NodeTraversal t, Node n, Node callbackFunction, List keyNodes, List valueNodes) { Node callTarget = n.getFirstChild(); Node objectToLoopOver = callTarget.getNext(); // New block to contain the expanded statements Node fncBlock = IR.block().srcref(callTarget); boolean isValidExpansion = true; // Expand the jQuery.expandedEach call Node key = objectToLoopOver.getFirstChild(), val = null; for(int i = 0; key != null; key = key.getNext(), i++) { if (key != null) { if (objectToLoopOver.isArrayLit()) { // Arrays have a value of their index number val = IR.number(i).srcref(key); } else { val = key.getFirstChild(); } } // Keep track of the replaced nodes so we can reset the tree List newKeys = Lists.newArrayList(); List newValues = Lists.newArrayList(); List origGetElems = Lists.newArrayList(); List newGetProps = Lists.newArrayList(); // Replace all of the key nodes with the prop name for (int j = 0; j < keyNodes.size(); j++) { Node origNode = keyNodes.get(j); Node ancestor = origNode.getParent(); Node newNode = IR.string(key.getString()).srcref(key); newKeys.add(newNode); ancestor.replaceChild(origNode, newNode); // Walk up the tree to see if the key is used in a GETELEM // assignment while (ancestor != null && !NodeUtil.isStatement(ancestor) && !ancestor.isGetElem()) { ancestor = ancestor.getParent(); } // Convert GETELEM nodes to GETPROP nodes so that they can be // renamed or removed. if (ancestor != null && ancestor.isGetElem()) { Node propObject = ancestor; while (propObject.isGetProp() || propObject.isGetElem()) { propObject = propObject.getFirstChild(); } Node ancestorClone = ancestor.cloneTree(); // Run the peephole passes to handle cases such as // obj['lit' + key] = val; peepholePasses.process(null, ancestorClone.getChildAtIndex(1)); Node prop = ancestorClone.getChildAtIndex(1); if (prop.isString() && NodeUtil.isValidPropertyName(prop.getString())) { Node target = ancestorClone.getFirstChild(); Node newGetProp = IR.getprop(target.detachFromParent(), prop.detachFromParent()); newGetProps.add(newGetProp); origGetElems.add(ancestor); ancestor.getParent().replaceChild(ancestor, newGetProp); } else { if (prop.isString() && !NodeUtil.isValidPropertyName(prop.getString())) { t.report(n, JQUERY_UNABLE_TO_EXPAND_INVALID_NAME_ERROR, prop.getString()); } isValidExpansion = false; } } } if (isValidExpansion) { // Replace all of the value nodes with the prop value for (int j = 0; val != null && j < valueNodes.size(); j++) { Node origNode = valueNodes.get(j); Node newNode = val.cloneTree(); newValues.add(newNode); origNode.getParent().replaceChild(origNode, newNode); } // Wrap the new tree in an anonymous function call Node fnc = IR.function(IR.name("").srcref(key), IR.paramList().srcref(key), callbackFunction.getChildAtIndex(2).cloneTree()).srcref(key); Node call = IR.call(fnc).srcref(key); call.putBooleanProp(Node.FREE_CALL, true); fncBlock.addChildToBack(IR.exprResult(call).srcref(call)); } // Reset the source tree for (int j = 0; j < newGetProps.size(); j++) { newGetProps.get(j).getParent().replaceChild(newGetProps.get(j), origGetElems.get(j)); } for (int j = 0; j < newKeys.size(); j++) { newKeys.get(j).getParent().replaceChild(newKeys.get(j), keyNodes.get(j)); } for (int j = 0; j < newValues.size(); j++) { newValues.get(j).getParent().replaceChild(newValues.get(j), valueNodes.get(j)); } if (!isValidExpansion) { return null; } } return fncBlock; } private void replaceOriginalJqueryEachCall(Node n, Node expandedBlock) { // Check to see if the return value of the original jQuery.expandedEach // call is used. If so, we need to wrap each loop expansion in an anonymous // function and return the original objectToLoopOver. if (n.getParent().isExprResult()) { Node parent = n.getParent(); Node grandparent = parent.getParent(); Node insertAfter = parent; while (expandedBlock.hasChildren()) { Node child = expandedBlock.getFirstChild().detachFromParent(); grandparent.addChildAfter(child, insertAfter); insertAfter = child; } grandparent.removeChild(parent); } else { // Return the original object Node callTarget = n.getFirstChild(); Node objectToLoopOver = callTarget.getNext(); objectToLoopOver.detachFromParent(); Node ret = IR.returnNode(objectToLoopOver).srcref(callTarget); expandedBlock.addChildToBack(ret); // Wrap all of the expanded loop calls in a new anonymous function Node fnc = IR.function(IR.name("").srcref(callTarget), IR.paramList().srcref(callTarget), expandedBlock); n.replaceChild(callTarget, fnc); n.putBooleanProp(Node.FREE_CALL, true); // remove any other pre-existing call arguments while(fnc.getNext() != null) { n.removeChildAfter(fnc); } } compiler.reportCodeChange(); } private boolean isArrayLitValidForExpansion(Node n) { Iterator iter = n.children().iterator(); while (iter.hasNext()) { Node child = iter.next(); if (!child.isString()) { return false; } } return true; } /** * Given a jQuery.expandedEach callback function, traverse it and collect any * references to its parameter names. */ class FindCallbackArgumentReferences extends AbstractPostOrderCallback implements ScopedCallback { private final String keyName; private final String valueName; private Scope startingScope; private List keyReferences; private List valueReferences; FindCallbackArgumentReferences(Node functionRoot, List keyReferences, List valueReferences, boolean useArrayMode) { Preconditions.checkState(functionRoot.isFunction()); String keyString = null, valueString = null; Node callbackParams = NodeUtil.getFunctionParameters(functionRoot); Node param = callbackParams.getFirstChild(); if (param != null) { Preconditions.checkState(param.isName()); keyString = param.getString(); param = param.getNext(); if (param != null) { Preconditions.checkState(param.isName()); valueString = param.getString(); } } this.keyName = keyString; this.valueName = valueString; // For arrays, the keyString is the index number of the element. // We're interested in the value of the element instead if (useArrayMode) { this.keyReferences = valueReferences; this.valueReferences = keyReferences; } else { this.keyReferences = keyReferences; this.valueReferences = valueReferences; } this.startingScope = null; } private boolean isShadowed(String name, Scope scope) { Var nameVar = scope.getVar(name); return nameVar != null && nameVar.getScope() != this.startingScope; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // In the top scope, "this" is a reference to "value" boolean isThis = false; if (t.getScope() == this.startingScope) { isThis = n.isThis(); } if (isThis || n.isName() && !isShadowed(n.getString(), t.getScope())) { String nodeValue = isThis ? null : n.getString(); if (!isThis && keyName != null && nodeValue.equals(keyName)) { keyReferences.add(n); } else if (isThis || (valueName != null && nodeValue.equals(valueName))) { valueReferences.add(n); } } } /** * As we enter each scope, make sure that the scope doesn't define * a local variable with the same name as our original callback method * parameter names. */ @Override public void enterScope(NodeTraversal t) { if (this.startingScope == null) { this.startingScope = t.getScope(); } } @Override public void exitScope(NodeTraversal t) { } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckLevel.java0000644000175000017500000000201712115204405025504 0ustar apoapo/* * Copyright 2004 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; /** * Controls checking levels of certain options. For all checks going * forward, this should be used instead of booleans, so teams and * individuals can control which checks are off, which produce only warnings, * and which produce errors, without everyone having to agree. */ public enum CheckLevel { ERROR, WARNING, OFF; boolean isOn() { return this != OFF; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JqueryCodingConvention.java0000644000175000017500000000362112115204405030147 0ustar apoapo/* * Copyright 2011 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 java.util.Set; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.javascript.rhino.Node; /** * This describes the jQuery specific JavaScript coding conventions. */ public class JqueryCodingConvention extends CodingConventions.Proxy { private static final long serialVersionUID = 1L; public JqueryCodingConvention() { this(CodingConventions.getDefault()); } public JqueryCodingConvention(CodingConvention wrapped) { super(wrapped); } @Override public String getGlobalObject() { return "window"; } private final static Set propertyTestFunctions = ImmutableSet.of( "jQuery.isPlainObject", "jQuery.isFunction", "jQuery.isNumeric", "jQuery.isEmptyObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()); } private final static Set prototypeAliases = ImmutableSet.of( "jQuery.fn", "jQuerySub.fn"); @Override public boolean isPrototypeAlias(Node getProp) { Preconditions.checkArgument(getProp.isGetProp()); return prototypeAliases.contains(getProp.getQualifiedName()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MethodCompilerPass.java0000644000175000017500000002106012115204405027240 0ustar apoapo/* * Copyright 2007 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.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * Finds all method declarations and pulls them into data structures * for use during cleanups such as arity checks or inlining. * */ abstract class MethodCompilerPass implements CompilerPass { /** List of methods defined in externs */ final Set externMethods = Sets.newHashSet(); /** List of extern methods without signatures that we can't warn about */ final Set externMethodsWithoutSignatures = Sets.newHashSet(); /** List of property names that may not be methods */ final Set nonMethodProperties = Sets.newHashSet(); // Use a linked map here to keep the output deterministic. Otherwise, // the choice of method bodies is random when multiple identical definitions // are found which causes problems in the source maps. final Multimap methodDefinitions = LinkedHashMultimap.create(); final AbstractCompiler compiler; /** * The signature storage is provided by the implementing class. */ interface SignatureStore { public void reset(); public void addSignature( String functionName, Node functionNode, String sourceFile); public void removeSignature(String functionName); } MethodCompilerPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { externMethods.clear(); externMethodsWithoutSignatures.clear(); getSignatureStore().reset(); methodDefinitions.clear(); if (externs != null) { NodeTraversal.traverse(compiler, externs, new GetExternMethods()); } List externsAndJs = Lists.newArrayList(externs, root); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), new GatherSignatures()); NodeTraversal.traverseRoots( compiler, externsAndJs, getActingCallback()); } /** * Subclasses should return a callback that does the actual work they * want to perform given the computed list of method signatures */ abstract Callback getActingCallback(); /** * Subclasses should return a SignatureStore for storing discovered * signatures. */ abstract SignatureStore getSignatureStore(); /** * Adds a node that may represent a function signature (if it's a function * itself or the name of a function). */ private void addPossibleSignature(String name, Node node, NodeTraversal t) { if (node.isFunction()) { // The node we're looking at is a function, so we can add it directly addSignature(name, node, t.getSourceName()); } else { nonMethodProperties.add(name); } } private void addSignature(String name, Node function, String fnSourceName) { if (externMethodsWithoutSignatures.contains(name)) { return; } getSignatureStore().addSignature(name, function, fnSourceName); methodDefinitions.put(name, function); } /** * Gathers methods from the externs file. Methods that are listed there but * do not have a signature are flagged to be ignored when doing arity checks. * Methods that do include signatures will be checked. */ private class GetExternMethods extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: case Token.GETELEM: { Node dest = n.getFirstChild().getNext(); if (!dest.isString()) { return; } String name = dest.getString(); // We have a signature. Parse tree of the form: // assign <- parent // getprop <- n // name methods // string setTimeout // function if (parent.isAssign() && parent.getFirstChild() == n && n.getNext().isFunction()) { addSignature(name, n.getNext(), t.getSourceName()); } else { getSignatureStore().removeSignature(name); externMethodsWithoutSignatures.add(name); } externMethods.add(name); } break; case Token.OBJECTLIT: { for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { Node value = key.getFirstChild(); String name = key.getString(); if (key.isStringKey() && value.isFunction()) { addSignature(name, value, t.getSourceName()); } else { getSignatureStore().removeSignature(name); externMethodsWithoutSignatures.add(name); } externMethods.add(name); } } break; } } } /** * Gather signatures from the source to be compiled. */ private class GatherSignatures extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); if (dest.isString()) { if (dest.getString().equals("prototype")) { processPrototypeParent(t, parent); } else { // Static methods of the form Foo.bar = function() {} or // Static methods of the form Foo.bar = baz (where baz is a // function name). Parse tree looks like: // assign <- parent // getprop <- n // name Foo // string bar // function or name <- n.getNext() if (parent.isAssign() && parent.getFirstChild() == n) { addPossibleSignature(dest.getString(), n.getNext(), t); } } } break; case Token.OBJECTLIT: for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { switch(key.getType()) { case Token.STRING_KEY: addPossibleSignature(key.getString(), key.getFirstChild(), t); break; case Token.SETTER_DEF: case Token.GETTER_DEF: nonMethodProperties.add(key.getString()); break; default: throw new IllegalStateException( "unexpect OBJECTLIT key: " + key); } } break; } } /** * Processes the parent of a GETPROP prototype, which can either be * another GETPROP (in the case of Foo.prototype.bar), or can be * an assignment (in the case of Foo.prototype = ...). */ private void processPrototypeParent(NodeTraversal t, Node n) { switch (n.getType()) { // Foo.prototype.getBar = function() { ... } or // Foo.prototype.getBar = getBaz (where getBaz is a function) // parse tree looks like: // assign <- parent // getprop <- n // getprop // name Foo // string prototype // string getBar // function or name <- assignee case Token.GETPROP: case Token.GETELEM: Node dest = n.getFirstChild().getNext(); Node parent = n.getParent().getParent(); if (dest.isString() && parent.isAssign()) { Node assignee = parent.getFirstChild().getNext(); addPossibleSignature(dest.getString(), assignee, t); } break; } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DefinitionSite.java0000644000175000017500000000265112115204405026420 0ustar apoapo/* * 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.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.rhino.Node; /** * Information about the context in which a Definition appears. * Includes the definition node, and context in which the definition * occurs - including the definition module. * */ class DefinitionSite { final Node node; final Definition definition; final JSModule module; final boolean inGlobalScope; final boolean inExterns; DefinitionSite(Node node, Definition definition, JSModule module, boolean inGlobalScope, boolean inExterns) { this.node = node; this.definition = definition; this.module = module; this.inGlobalScope = inGlobalScope; this.inExterns = inExterns; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VerboseMessageFormatter.java0000644000175000017500000000362312115204405030301 0ustar apoapo/* * Copyright 2007 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.base.Strings; import com.google.javascript.jscomp.CheckLevel; /** * Verbose message formatter. This formatter generates very loud and long * messages with multi-line source excerpts. * */ class VerboseMessageFormatter extends AbstractMessageFormatter { VerboseMessageFormatter(SourceExcerptProvider source) { super(source); } @Override public String formatError(JSError error) { return getLevelName(CheckLevel.ERROR) + ": " + format(error); } @Override public String formatWarning(JSError warning) { return getLevelName(CheckLevel.WARNING) + ": " + format(warning); } private String format(JSError message) { String description = message.description; String sourceName = message.sourceName; int lineNumber = message.lineNumber; Region sourceRegion = getSource().getSourceRegion(sourceName, lineNumber); String lineSource = null; if (sourceRegion != null) { lineSource = sourceRegion.getSourceExcerpt(); } return String.format("%s at %s line %s %s", description, (Strings.isNullOrEmpty(sourceName) ? "(unknown source)" : sourceName), ((lineNumber < 0) ? String.valueOf(lineNumber) : "(unknown line)"), ((lineSource != null) ? ":\n\n" + lineSource : ".")); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodingConventions.java0000644000175000017500000003154612115204405027141 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Helper classes for dealing with coding conventions. */ public class CodingConventions { private CodingConventions() {} /** Gets the default coding convention. */ public static CodingConvention getDefault() { return new DefaultCodingConvention(); } /** * A convention that wraps another. * * When you want to support a new library, you should subclass this * delegate, and override the methods that you want to customize. * * This way, a person using jQuery and Closure Library can create a new * coding convention by creating a jQueryCodingConvention that delegates * to a ClosureCodingConvention that delegates to a DefaultCodingConvention. */ public static class Proxy implements CodingConvention { protected final CodingConvention nextConvention; protected Proxy(CodingConvention convention) { this.nextConvention = convention; } @Override public boolean isConstant(String variableName) { return nextConvention.isConstant(variableName); } @Override public boolean isConstantKey(String keyName) { return nextConvention.isConstantKey(keyName); } @Override public boolean isValidEnumKey(String key) { return nextConvention.isValidEnumKey(key); } @Override public boolean isOptionalParameter(Node parameter) { return nextConvention.isOptionalParameter(parameter); } @Override public boolean isVarArgsParameter(Node parameter) { return nextConvention.isVarArgsParameter(parameter); } @Override public boolean isExported(String name, boolean local) { return nextConvention.isExported(name, local); } @Override public final boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return nextConvention.isPrivate(name); } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return nextConvention.getClassesDefinedByCall(callNode); } @Override public boolean isSuperClassReference(String propertyName) { return nextConvention.isSuperClassReference(propertyName); } @Override public String extractClassNameIfProvide(Node node, Node parent) { return nextConvention.extractClassNameIfProvide(node, parent); } @Override public String extractClassNameIfRequire(Node node, Node parent) { return nextConvention.extractClassNameIfRequire(node, parent); } @Override public String getExportPropertyFunction() { return nextConvention.getExportPropertyFunction(); } @Override public String getExportSymbolFunction() { return nextConvention.getExportSymbolFunction(); } @Override public List identifyTypeDeclarationCall(Node n) { return nextConvention.identifyTypeDeclarationCall(n); } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { nextConvention.applySubclassRelationship( parentCtor, childCtor, type); } @Override public String getAbstractMethodName() { return nextConvention.getAbstractMethodName(); } @Override public String getSingletonGetterClassName(Node callNode) { return nextConvention.getSingletonGetterClassName(callNode); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { nextConvention.applySingletonGetter( functionType, getterType, objectType); } @Override public boolean isInlinableFunction(Node n) { return nextConvention.isInlinableFunction(n); } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return nextConvention.getDelegateRelationship(callNode); } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { nextConvention.applyDelegateRelationship( delegateSuperclass, delegateBase, delegator, delegateProxy, findDelegate); } @Override public String getDelegateSuperclassName() { return nextConvention.getDelegateSuperclassName(); } @Override public void checkForCallingConventionDefiningCalls( Node n, Map delegateCallingConventions) { nextConvention.checkForCallingConventionDefiningCalls( n, delegateCallingConventions); } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope scope, List delegateProxyPrototypes, Map delegateCallingConventions) { nextConvention.defineDelegateProxyPrototypeProperties( registry, scope, delegateProxyPrototypes, delegateCallingConventions); } @Override public String getGlobalObject() { return nextConvention.getGlobalObject(); } @Override public Collection getAssertionFunctions() { return nextConvention.getAssertionFunctions(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { return nextConvention.describeFunctionBind(n, useTypeInfo); } @Override public boolean isPropertyTestFunction(Node call) { return nextConvention.isPropertyTestFunction(call); } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return nextConvention.getObjectLiteralCast(callNode); } @Override public Collection getIndirectlyDeclaredProperties() { return nextConvention.getIndirectlyDeclaredProperties(); } } /** * The default coding convention. * Should be at the bottom of all proxy chains. */ private static class DefaultCodingConvention implements CodingConvention { private static final long serialVersionUID = 1L; @Override public boolean isConstant(String variableName) { return false; } @Override public boolean isConstantKey(String variableName) { return false; } @Override public boolean isValidEnumKey(String key) { return key != null && key.length() > 0; } @Override public boolean isOptionalParameter(Node parameter) { // be as lax as possible, but this must be mutually exclusive from // var_args parameters. return false; } @Override public boolean isVarArgsParameter(Node parameter) { // be as lax as possible return false; } @Override public boolean isExported(String name, boolean local) { return local && name.startsWith("$super"); } @Override public boolean isExported(String name) { return isExported(name, false) || isExported(name, true); } @Override public boolean isPrivate(String name) { return false; } @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { return null; } @Override public boolean isSuperClassReference(String propertyName) { return false; } @Override public String extractClassNameIfProvide(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String extractClassNameIfRequire(Node node, Node parent) { String message = "only implemented in GoogleCodingConvention"; throw new UnsupportedOperationException(message); } @Override public String getExportPropertyFunction() { return null; } @Override public String getExportSymbolFunction() { return null; } @Override public List identifyTypeDeclarationCall(Node n) { return null; } @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { // do nothing } @Override public String getAbstractMethodName() { return null; } @Override public String getSingletonGetterClassName(Node callNode) { return null; } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { // do nothing. } @Override public boolean isInlinableFunction(Node n) { Preconditions.checkState(n.isFunction()); return true; } @Override public DelegateRelationship getDelegateRelationship(Node callNode) { return null; } @Override public void applyDelegateRelationship( ObjectType delegateSuperclass, ObjectType delegateBase, ObjectType delegator, FunctionType delegateProxy, FunctionType findDelegate) { // do nothing. } @Override public String getDelegateSuperclassName() { return null; } @Override public void checkForCallingConventionDefiningCalls(Node n, Map delegateCallingConventions) { // do nothing. } @Override public void defineDelegateProxyPrototypeProperties( JSTypeRegistry registry, StaticScope scope, List delegateProxyPrototypes, Map delegateCallingConventions) { // do nothing. } @Override public String getGlobalObject() { return "window"; } @Override public boolean isPropertyTestFunction(Node call) { return false; } @Override public boolean isPrototypeAlias(Node getProp) { return false; } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { return null; } @Override public Collection getAssertionFunctions() { return Collections.emptySet(); } @Override public Bind describeFunctionBind(Node n) { return describeFunctionBind(n, false); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("Function.prototype.bind.call")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } } if (callTarget.isGetProp() && callTarget.getLastChild().getString().equals("bind")) { Node maybeFn = callTarget.getFirstChild(); JSType maybeFnType = maybeFn.getJSType(); FunctionType fnType = null; if (useTypeInfo && maybeFnType != null) { fnType = maybeFnType.restrictByNotNullOrUndefined() .toMaybeFunctionType(); } if (fnType != null || maybeFn.isFunction()) { // (function(){}).bind(self, args...); Node thisValue = callTarget.getNext(); Node parameters = safeNext(thisValue); return new Bind(maybeFn, thisValue, parameters); } } return null; } @Override public Collection getIndirectlyDeclaredProperties() { return ImmutableList.of(); } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionInjector.java0000644000175000017500000007772212115204405027001 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ExpressionDecomposer.DecompositionType; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.Map; import java.util.Set; /** * A set of utility functions that replaces CALL with a specified * FUNCTION body, replacing and aliasing function parameters as * necessary. * * @author johnlenz@google.com (John Lenz) */ class FunctionInjector { private final AbstractCompiler compiler; private final Supplier safeNameIdSupplier; private final boolean allowDecomposition; private Set knownConstants = Sets.newHashSet(); private final boolean assumeStrictThis; private final boolean assumeMinimumCapture; /** * @param allowDecomposition Whether an effort should be made to break down * expressions into simpler expressions to allow functions to be injected * where they would otherwise be disallowed. */ public FunctionInjector( AbstractCompiler compiler, Supplier safeNameIdSupplier, boolean allowDecomposition, boolean assumeStrictThis, boolean assumeMinimumCapture) { Preconditions.checkNotNull(compiler); Preconditions.checkNotNull(safeNameIdSupplier); this.compiler = compiler; this.safeNameIdSupplier = safeNameIdSupplier; this.allowDecomposition = allowDecomposition; this.assumeStrictThis = assumeStrictThis; this.assumeMinimumCapture = assumeMinimumCapture; } /** The type of inlining to perform. */ enum InliningMode { /** * Directly replace the call expression. Only functions of meeting * strict preconditions can be inlined. */ DIRECT, /** * Replaces the call expression with a block of statements. Conditions * on the function are looser in mode, but stricter on the call site. */ BLOCK } /** Holds a reference to the call node of a function call */ static class Reference { final Node callNode; final JSModule module; final InliningMode mode; Reference(Node callNode, JSModule module, InliningMode mode){ this.callNode = callNode; this.module = module; this.mode = mode; } } /** * In order to estimate the cost of lining, we make the assumption that * Identifiers are reduced 2 characters. For the call arguments, the important * thing is that the cost is assumed to be the same in the call and the * function, so the actual length doesn't matter in most cases. */ private static final int NAME_COST_ESTIMATE = InlineCostEstimator.ESTIMATED_IDENTIFIER_COST; /** The cost of a argument separator (a comma). */ private static final int COMMA_COST = 1; /** The cost of the parentheses needed to make a call.*/ private static final int PAREN_COST = 2; /** * @param fnName The name of this function. This either the name of the * variable to which the function is assigned or the name from the FUNCTION * node. * @param fnNode The FUNCTION node of the function to inspect. * @return Whether the function node meets the minimum requirements for * inlining. */ boolean doesFunctionMeetMinimumRequirements( final String fnName, Node fnNode) { Node block = NodeUtil.getFunctionBody(fnNode); // Basic restrictions on functions that can be inlined: // 0) The function is inlinable by convention // 1) It contains a reference to itself. // 2) It uses its parameters indirectly using "arguments" (it isn't // handled yet. // 3) It references "eval". Inline a function containing eval can have // large performance implications. if (!compiler.getCodingConvention().isInlinableFunction(fnNode)) { return false; } final String fnRecursionName = fnNode.getFirstChild().getString(); Preconditions.checkState(fnRecursionName != null); // If the function references "arguments" directly in the function boolean referencesArguments = NodeUtil.isNameReferenced( block, "arguments", NodeUtil.MATCH_NOT_FUNCTION); // or it references "eval" or one of its names anywhere. Predicate p = new Predicate(){ @Override public boolean apply(Node n) { if (n.isName()) { return n.getString().equals("eval") || (!fnName.isEmpty() && n.getString().equals(fnName)) || (!fnRecursionName.isEmpty() && n.getString().equals(fnRecursionName)); } return false; } }; return !referencesArguments && !NodeUtil.has(block, p, Predicates.alwaysTrue()); } /** * @param t The traversal use to reach the call site. * @param callNode The CALL node. * @param fnNode The function to evaluate for inlining. * @param needAliases A set of function parameter names that can not be * used without aliasing. Returned by getUnsafeParameterNames(). * @param mode Inlining mode to be used. * @param referencesThis Whether fnNode contains references to its this * object. * @param containsFunctions Whether fnNode contains inner functions. * @return Whether the inlining can occur. */ CanInlineResult canInlineReferenceToFunction(NodeTraversal t, Node callNode, Node fnNode, Set needAliases, InliningMode mode, boolean referencesThis, boolean containsFunctions) { // TODO(johnlenz): This function takes too many parameter, without // context. Modify the API to take a structure describing the function. // Allow direct function calls or "fn.call" style calls. if (!isSupportedCallType(callNode)) { return CanInlineResult.NO; } // Limit where functions that contain functions can be inline. Introducing // an inner function into another function can capture a variable and cause // a memory leak. This isn't a problem in the global scope as those values // last until explicitly cleared. if (containsFunctions) { if (!assumeMinimumCapture && !t.inGlobalScope()) { // TODO(johnlenz): Allow inlining into any scope without local names or // inner functions. return CanInlineResult.NO; } else if (NodeUtil.isWithinLoop(callNode)) { // An inner closure maybe relying on a local value holding a value for a // single iteration through a loop. return CanInlineResult.NO; } } // TODO(johnlenz): Add support for 'apply' if (referencesThis && !NodeUtil.isFunctionObjectCall(callNode)) { // TODO(johnlenz): Allow 'this' references to be replaced with a // global 'this' object. return CanInlineResult.NO; } if (mode == InliningMode.DIRECT) { return canInlineReferenceDirectly(callNode, fnNode); } else { return canInlineReferenceAsStatementBlock( t, callNode, fnNode, needAliases); } } /** * Only ".call" calls and direct calls to functions are supported. * @param callNode The call evaluate. * @return Whether the call is of a type that is supported. */ private boolean isSupportedCallType(Node callNode) { if (!callNode.getFirstChild().isName()) { if (NodeUtil.isFunctionObjectCall(callNode)) { if (!assumeStrictThis) { Node thisValue = callNode.getFirstChild().getNext(); if (thisValue == null || !thisValue.isThis()) { return false; } } } else if (NodeUtil.isFunctionObjectApply(callNode)) { return false; } } return true; } /** * Inline a function into the call site. */ Node inline( Node callNode, String fnName, Node fnNode, InliningMode mode) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); if (mode == InliningMode.DIRECT) { return inlineReturnValue(callNode, fnNode); } else { return inlineFunction(callNode, fnNode, fnName); } } /** * Inline a function that fulfills the requirements of * canInlineReferenceDirectly into the call site, replacing only the CALL * node. */ private Node inlineReturnValue(Node callNode, Node fnNode) { Node block = fnNode.getLastChild(); Node callParentNode = callNode.getParent(); // NOTE: As the normalize pass guarantees globals aren't being // shadowed and an expression can't introduce new names, there is // no need to check for conflicts. // Create an argName -> expression map, checking for side effects. Map argMap = FunctionArgumentInjector.getFunctionCallParameterMap( fnNode, callNode, this.safeNameIdSupplier); Node newExpression; if (!block.hasChildren()) { Node srcLocation = block; newExpression = NodeUtil.newUndefinedNode(srcLocation); } else { Node returnNode = block.getFirstChild(); Preconditions.checkArgument(returnNode.isReturn()); // Clone the return node first. Node safeReturnNode = returnNode.cloneTree(); Node inlineResult = FunctionArgumentInjector.inject( null, safeReturnNode, null, argMap); Preconditions.checkArgument(safeReturnNode == inlineResult); newExpression = safeReturnNode.removeFirstChild(); } callParentNode.replaceChild(callNode, newExpression); return newExpression; } /** * Supported call site types. */ private enum CallSiteType { /** * Used for a call site for which there does not exist a method * to inline it. */ UNSUPPORTED() { @Override public void prepare(FunctionInjector injector, Node callNode) { throw new IllegalStateException("unexpected"); } }, /** * A call as a statement. For example: "foo();". * EXPR_RESULT * CALL */ SIMPLE_CALL() { @Override public void prepare(FunctionInjector injector, Node callNode) { // Nothing to do. } }, /** * An assignment, where the result of the call is assigned to a simple * name. For example: "a = foo();". * EXPR_RESULT * NAME A * CALL * FOO */ SIMPLE_ASSIGNMENT() { @Override public void prepare(FunctionInjector injector, Node callNode) { // Nothing to do. } }, /** * An var declaration and initialization, where the result of the call is * assigned to the declared name * name. For example: "a = foo();". * VAR * NAME A * CALL * FOO */ VAR_DECL_SIMPLE_ASSIGNMENT() { @Override public void prepare(FunctionInjector injector, Node callNode) { // Nothing to do. } }, /** * An arbitrary expression, the root of which is a EXPR_RESULT, IF, * RETURN, SWITCH or VAR. The call must be the first side-effect in * the expression. * * Examples include: * "if (foo()) {..." * "return foo();" * "var a = 1 + foo();" * "a = 1 + foo()" * "foo() ? 1:0" * "foo() && x" */ EXPRESSION() { @Override public void prepare(FunctionInjector injector, Node callNode) { injector.getDecomposer().moveExpression(callNode); // Reclassify after move CallSiteType callSiteType = injector.classifyCallSite(callNode); Preconditions.checkState(this != callSiteType); callSiteType.prepare(injector, callNode); } }, /** * An arbitrary expression, the root of which is a EXPR_RESULT, IF, * RETURN, SWITCH or VAR. Where the call is not the first side-effect in * the expression. */ DECOMPOSABLE_EXPRESSION() { @Override public void prepare(FunctionInjector injector, Node callNode) { injector.getDecomposer().maybeExposeExpression(callNode); // Reclassify after decomposition CallSiteType callSiteType = injector.classifyCallSite(callNode); Preconditions.checkState(this != callSiteType); callSiteType.prepare(injector, callNode); } }; public abstract void prepare(FunctionInjector injector, Node callNode); } /** * Determine which, if any, of the supported types the call site is. */ private CallSiteType classifyCallSite(Node callNode) { Node parent = callNode.getParent(); Node grandParent = parent.getParent(); // Verify the call site: if (NodeUtil.isExprCall(parent)) { // This is a simple call? Example: "foo();". return CallSiteType.SIMPLE_CALL; } else if (NodeUtil.isExprAssign(grandParent) && !NodeUtil.isVarOrSimpleAssignLhs(callNode, parent) && parent.getFirstChild().isName() && !NodeUtil.isConstantName(parent.getFirstChild())) { // This is a simple assignment. Example: "x = foo();" return CallSiteType.SIMPLE_ASSIGNMENT; } else if (parent.isName() && !NodeUtil.isConstantName(parent) && grandParent.isVar() && grandParent.hasOneChild()) { // This is a var declaration. Example: "var x = foo();" // TODO(johnlenz): Should we be checking for constants on the // left-hand-side of the assignments and handling them as EXPRESSION? return CallSiteType.VAR_DECL_SIMPLE_ASSIGNMENT; } else { Node expressionRoot = ExpressionDecomposer.findExpressionRoot(callNode); if (expressionRoot != null) { ExpressionDecomposer decomposer = new ExpressionDecomposer( compiler, safeNameIdSupplier, knownConstants); DecompositionType type = decomposer.canExposeExpression( callNode); if (type == DecompositionType.MOVABLE) { return CallSiteType.EXPRESSION; } else if (type == DecompositionType.DECOMPOSABLE) { return CallSiteType.DECOMPOSABLE_EXPRESSION; } else { Preconditions.checkState(type == DecompositionType.UNDECOMPOSABLE); } } } return CallSiteType.UNSUPPORTED; } private ExpressionDecomposer getDecomposer() { return new ExpressionDecomposer( compiler, safeNameIdSupplier, knownConstants); } /** * If required, rewrite the statement containing the call expression. * @see ExpressionDecomposer#canExposeExpression */ void maybePrepareCall(Node callNode) { CallSiteType callSiteType = classifyCallSite(callNode); callSiteType.prepare(this, callNode); } /** * Inline a function which fulfills the requirements of * canInlineReferenceAsStatementBlock into the call site, replacing the * parent expression. */ private Node inlineFunction( Node callNode, Node fnNode, String fnName) { Node parent = callNode.getParent(); Node grandParent = parent.getParent(); // TODO(johnlenz): Consider storing the callSite classification in the // reference object and passing it in here. CallSiteType callSiteType = classifyCallSite(callNode); Preconditions.checkArgument(callSiteType != CallSiteType.UNSUPPORTED); boolean isCallInLoop = NodeUtil.isWithinLoop(callNode); // Store the name for the result. This will be used to // replace "return expr" statements with "resultName = expr" // to replace String resultName = null; boolean needsDefaultReturnResult = true; switch (callSiteType) { case SIMPLE_ASSIGNMENT: resultName = parent.getFirstChild().getString(); break; case VAR_DECL_SIMPLE_ASSIGNMENT: resultName = parent.getString(); break; case SIMPLE_CALL: resultName = null; // "foo()" doesn't need a result. needsDefaultReturnResult = false; break; case EXPRESSION: throw new IllegalStateException( "Movable expressions must be moved before inlining."); case DECOMPOSABLE_EXPRESSION: throw new IllegalStateException( "Decomposable expressions must be decomposed before inlining."); default: throw new IllegalStateException("Unexpected call site type."); } FunctionToBlockMutator mutator = new FunctionToBlockMutator( compiler, this.safeNameIdSupplier); Node newBlock = mutator.mutate( fnName, fnNode, callNode, resultName, needsDefaultReturnResult, isCallInLoop); // TODO(nicksantos): Create a common mutation function that // can replace either a VAR name assignment, assignment expression or // a EXPR_RESULT. Node greatGrandParent = grandParent.getParent(); switch (callSiteType) { case VAR_DECL_SIMPLE_ASSIGNMENT: // Remove the call from the name node. parent.removeChild(parent.getFirstChild()); Preconditions.checkState(parent.getFirstChild() == null); // Add the call, after the VAR. greatGrandParent.addChildAfter(newBlock, grandParent); break; case SIMPLE_ASSIGNMENT: // The assignment is now part of the inline function so // replace it completely. Preconditions.checkState(grandParent.isExprResult()); greatGrandParent.replaceChild(grandParent, newBlock); break; case SIMPLE_CALL: // If nothing is looking at the result just replace the call. Preconditions.checkState(parent.isExprResult()); grandParent.replaceChild(parent, newBlock); break; default: throw new IllegalStateException("Unexpected call site type."); } return newBlock; } /** * Checks if the given function matches the criteria for an inlinable * function, and if so, adds it to our set of inlinable functions. */ boolean isDirectCallNodeReplacementPossible(Node fnNode) { // Only inline single-statement functions Node block = NodeUtil.getFunctionBody(fnNode); // Check if this function is suitable for direct replacement of a CALL node: // a function that consists of single return that returns an expression. if (!block.hasChildren()) { // special case empty functions. return true; } else if (block.hasOneChild()) { // Only inline functions that return something. if (block.getFirstChild().isReturn() && block.getFirstChild().getFirstChild() != null) { return true; } } return false; } enum CanInlineResult { YES, AFTER_PREPARATION, NO } /** * Determines whether a function can be inlined at a particular call site. * There are several criteria that the function and reference must hold in * order for the functions to be inlined: * - It must be a simple call, or assignment, or var initialization. *
         *    f();
         *    a = foo();
         *    var a = foo();
         * 
      */ private CanInlineResult canInlineReferenceAsStatementBlock( NodeTraversal t, Node callNode, Node fnNode, Set namesToAlias) { CallSiteType callSiteType = classifyCallSite(callNode); if (callSiteType == CallSiteType.UNSUPPORTED) { return CanInlineResult.NO; } if (!allowDecomposition && (callSiteType == CallSiteType.DECOMPOSABLE_EXPRESSION || callSiteType == CallSiteType.EXPRESSION)) { return CanInlineResult.NO; } if (!callMeetsBlockInliningRequirements( t, callNode, fnNode, namesToAlias)) { return CanInlineResult.NO; } if (callSiteType == CallSiteType.DECOMPOSABLE_EXPRESSION || callSiteType == CallSiteType.EXPRESSION) { return CanInlineResult.AFTER_PREPARATION; } else { return CanInlineResult.YES; } } /** * Determines whether a function can be inlined at a particular call site. * - Don't inline if the calling function contains an inner function and * inlining would introduce new globals. */ private boolean callMeetsBlockInliningRequirements( NodeTraversal t, Node callNode, final Node fnNode, Set namesToAlias) { final boolean assumeMinimumCapture = this.assumeMinimumCapture; // Note: functions that contain function definitions are filtered out // in isCandidateFunction. // TODO(johnlenz): Determining if the called function contains VARs // or if the caller contains inner functions accounts for 20% of the // run-time cost of this pass. // Don't inline functions with var declarations into a scope with inner // functions as the new vars would leak into the inner function and // cause memory leaks. boolean fnContainsVars = NodeUtil.has( NodeUtil.getFunctionBody(fnNode), new NodeUtil.MatchDeclaration(), new NodeUtil.MatchShallowStatement()); boolean forbidTemps = false; if (!t.inGlobalScope()) { Node fnCaller = t.getScopeRoot(); Node fnCallerBody = fnCaller.getLastChild(); // Don't allow any new vars into a scope that contains eval or one // that contains functions (excluding the function being inlined). Predicate match = new Predicate(){ @Override public boolean apply(Node n) { if (n.isName()) { return n.getString().equals("eval"); } if (!assumeMinimumCapture && n.isFunction()) { return n != fnNode; } return false; } }; forbidTemps = NodeUtil.has(fnCallerBody, match, NodeUtil.MATCH_NOT_FUNCTION); } if (fnContainsVars && forbidTemps) { return false; } // If the caller contains functions or evals, verify we aren't adding any // additional VAR declarations because aliasing is needed. if (forbidTemps) { Map args = FunctionArgumentInjector.getFunctionCallParameterMap( fnNode, callNode, this.safeNameIdSupplier); boolean hasArgs = !args.isEmpty(); if (hasArgs) { // Limit the inlining Set allNamesToAlias = Sets.newHashSet(namesToAlias); FunctionArgumentInjector.maybeAddTempsForCallArguments( fnNode, args, allNamesToAlias, compiler.getCodingConvention()); if (!allNamesToAlias.isEmpty()) { return false; } } } return true; } /** * Determines whether a function can be inlined at a particular call site. * There are several criteria that the function and reference must hold in * order for the functions to be inlined: * 1) If a call's arguments have side effects, * the corresponding argument in the function must only be referenced once. * For instance, this will not be inlined: *
         *     function foo(a) { return a + a }
         *     x = foo(i++);
         * 
      */ private CanInlineResult canInlineReferenceDirectly( Node callNode, Node fnNode) { if (!isDirectCallNodeReplacementPossible(fnNode)) { return CanInlineResult.NO; } Node block = fnNode.getLastChild(); // CALL NODE: [ NAME, ARG1, ARG2, ... ] Node cArg = callNode.getFirstChild().getNext(); // Functions called via 'call' and 'apply' have a this-object as // the first parameter, but this is not part of the called function's // parameter list. if (!callNode.getFirstChild().isName()) { if (NodeUtil.isFunctionObjectCall(callNode)) { // TODO(johnlenz): Support replace this with a value. if (cArg == null || !cArg.isThis()) { return CanInlineResult.NO; } cArg = cArg.getNext(); } else { // ".apply" call should be filtered before this. Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode)); } } // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ] Node fnParam = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); while (cArg != null || fnParam != null) { // For each named parameter check if a mutable argument use more than one. if (fnParam != null) { if (cArg != null) { // Check for arguments that are evaluated more than once. // Note: Unlike block inlining, there it is not possible that a // parameter reference will be in a loop. if (NodeUtil.mayEffectMutableState(cArg, compiler) && NodeUtil.getNameReferenceCount( block, fnParam.getString()) > 1) { return CanInlineResult.NO; } } // Move to the next name. fnParam = fnParam.getNext(); } // For every call argument check for side-effects, even if there // isn't a named parameter to match. if (cArg != null) { if (NodeUtil.mayHaveSideEffects(cArg, compiler)) { return CanInlineResult.NO; } cArg = cArg.getNext(); } } return CanInlineResult.YES; } /** * Determine if inlining the function is likely to reduce the code size. * @param namesToAlias */ boolean inliningLowersCost( JSModule fnModule, Node fnNode, Collection refs, Set namesToAlias, boolean isRemovable, boolean referencesThis) { int referenceCount = refs.size(); if (referenceCount == 0) { return true; } int referencesUsingBlockInlining = 0; boolean checkModules = isRemovable && fnModule != null; JSModuleGraph moduleGraph = compiler.getModuleGraph(); for (Reference ref : refs) { if (ref.mode == InliningMode.BLOCK) { referencesUsingBlockInlining++; } // Check if any of the references cross the module boundaries. if (checkModules && ref.module != null) { if (ref.module != fnModule && !moduleGraph.dependsOn(ref.module, fnModule)) { // Calculate the cost as if the function were non-removable, // if it still lowers the cost inline it. isRemovable = false; checkModules = false; // no need to check additional modules. } } } int referencesUsingDirectInlining = referenceCount - referencesUsingBlockInlining; // Don't bother calculating the cost of function for simple functions where // possible. // However, when inlining a complex function, even a single reference may be // larger than the original function if there are many returns (resulting // in additional assignments) or many parameters that need to be aliased // so use the cost estimating. if (referenceCount == 1 && isRemovable && referencesUsingDirectInlining == 1) { return true; } int callCost = estimateCallCost(fnNode, referencesThis); int overallCallCost = callCost * referenceCount; int costDeltaDirect = inlineCostDelta( fnNode, namesToAlias, InliningMode.DIRECT); int costDeltaBlock = inlineCostDelta( fnNode, namesToAlias, InliningMode.BLOCK); return doesLowerCost(fnNode, overallCallCost, referencesUsingDirectInlining, costDeltaDirect, referencesUsingBlockInlining, costDeltaBlock, isRemovable); } /** * @return Whether inlining will lower cost. */ private boolean doesLowerCost( Node fnNode, int callCost, int directInlines, int costDeltaDirect, int blockInlines, int costDeltaBlock, boolean removable) { // Determine the threshold value for this inequality: // inline_cost < call_cost // But solve it for the function declaration size so the size of it // is only calculated once and terminated early if possible. int fnInstanceCount = directInlines + blockInlines - (removable ? 1 : 0); // Prevent division by zero. if (fnInstanceCount == 0) { // Special case single reference function that are being block inlined: // If the cost of the inline is greater than the function definition size, // don't inline. if (blockInlines > 0 && costDeltaBlock > 0) { return false; } return true; } int costDelta = (directInlines * costDeltaDirect) + (blockInlines * costDeltaBlock); int threshold = (callCost - costDelta) / fnInstanceCount; return InlineCostEstimator.getCost(fnNode, threshold + 1) <= threshold; } /** * Gets an estimate of the cost in characters of making the function call: * the sum of the identifiers and the separators. * @param referencesThis */ private static int estimateCallCost(Node fnNode, boolean referencesThis) { Node argsNode = NodeUtil.getFunctionParameters(fnNode); int numArgs = argsNode.getChildCount(); int callCost = NAME_COST_ESTIMATE + PAREN_COST; if (numArgs > 0) { callCost += (numArgs * NAME_COST_ESTIMATE) + ((numArgs - 1) * COMMA_COST); } if (referencesThis) { // TODO(johnlenz): Update this if we start supporting inlining // other functions that reference this. // The only functions that reference this that are currently inlined // are those that are called via ".call" with an explicit "this". callCost += 5 + 5; // ".call" + "this," } return callCost; } /** * @return The difference between the function definition cost and * inline cost. */ private static int inlineCostDelta( Node fnNode, Set namesToAlias, InliningMode mode) { // The part of the function that is never inlined: // "function xx(xx,xx){}" (15 + (param count * 3) -1; int paramCount = NodeUtil.getFunctionParameters(fnNode).getChildCount(); int commaCount = (paramCount > 1) ? paramCount - 1 : 0; int costDeltaFunctionOverhead = 15 + commaCount + (paramCount * InlineCostEstimator.ESTIMATED_IDENTIFIER_COST); Node block = fnNode.getLastChild(); if (!block.hasChildren()) { // Assume the inline cost is zero for empty functions. return -costDeltaFunctionOverhead; } if (mode == InliningMode.DIRECT) { // The part of the function that is inlined using direct inlining: // "return " (7) return -(costDeltaFunctionOverhead + 7); } else { int aliasCount = namesToAlias.size(); // Originally, we estimated purely base on the function code size, relying // on later optimizations. But that did not produce good results, so here // we try to estimate the something closer to the actual inlined coded. // NOTE 1: Result overhead is only if there is an assignment, but // getting that information would require some refactoring. // NOTE 2: The aliasing overhead is currently an under-estimate, // as some parameters are aliased because of the parameters used. // Perhaps we should just assume all parameters will be aliased? final int INLINE_BLOCK_OVERHEAD = 4; // "X:{}" final int PER_RETURN_OVERHEAD = 2; // "return" --> "break X" final int PER_RETURN_RESULT_OVERHEAD = 3; // "XX=" final int PER_ALIAS_OVERHEAD = 3; // "XX=" // TODO(johnlenz): Counting the number of returns is relatively expensive // this information should be determined during the traversal and // cached. int returnCount = NodeUtil.getNodeTypeReferenceCount( block, Token.RETURN, new NodeUtil.MatchShallowStatement()); int resultCount = (returnCount > 0) ? returnCount - 1 : 0; int baseOverhead = (returnCount > 0) ? INLINE_BLOCK_OVERHEAD : 0; int overhead = baseOverhead + returnCount * PER_RETURN_OVERHEAD + resultCount * PER_RETURN_RESULT_OVERHEAD + aliasCount * PER_ALIAS_OVERHEAD; return (overhead - costDeltaFunctionOverhead); } } /** * Store the names of known constants to be used when classifying call-sites * in expressions. */ public void setKnownConstants(Set knownConstants) { // This is only expected to be set once. The same set should be used // when evaluating call-sites and inlining calls. Preconditions.checkState(this.knownConstants.isEmpty()); this.knownConstants = knownConstants; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NodeTraversal.java0000644000175000017500000004600312115204405026253 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * NodeTraversal allows an iteration through the nodes in the parse tree, * and facilitates the optimizations on the parse tree. * */ public class NodeTraversal { // Package protected for tests private final AbstractCompiler compiler; private final Callback callback; /** Contains the current node*/ private Node curNode; public static final DiagnosticType NODE_TRAVERSAL_ERROR = DiagnosticType.error("JSC_NODE_TRAVERSAL_ERROR", "{0}"); /** * Stack containing the Scopes that have been created. The Scope objects * are lazily created; so the {@code scopeRoots} stack contains the * Nodes for all Scopes that have not been created yet. */ private final Deque scopes = new ArrayDeque(); /** * A stack of scope roots. All scopes that have not been created * are represented in this Deque. */ private final Deque scopeRoots = new ArrayDeque(); /** * Stack of control flow graphs (CFG). There is one CFG per scope. CFGs * are lazily populated: elements are {@code null} until requested by * {@link #getControlFlowGraph()}. Note that {@link ArrayDeque} does not allow * {@code null} elements, so {@link LinkedList} is used instead. */ Deque> cfgs = new LinkedList>(); /** The current source file name */ private String sourceName; /** The current input */ private InputId inputId; /** The scope creator */ private ScopeCreator scopeCreator; /** Possible callback for scope entry and exist **/ private ScopedCallback scopeCallback; /** * Callback */ public interface Callback { /** *

      Visits a node in pre order (before visiting its children) and decides * whether this node's children should be traversed. If children are * traversed, they will be visited by * {@link #visit(NodeTraversal, Node, Node)} in post order.

      *

      Implementations can have side effects (e.g. modifying the parse * tree).

      * @return whether the children of this node should be visited */ boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent); /** *

      Visits a node in post order (after its children have been visited). * A node is visited only if all its parents should be traversed * ({@link #shouldTraverse(NodeTraversal, Node, Node)}).

      *

      Implementations can have side effects (e.g. modifying the parse * tree).

      */ void visit(NodeTraversal t, Node n, Node parent); } /** * Callback that also knows about scope changes */ public interface ScopedCallback extends Callback { /** * Called immediately after entering a new scope. The new scope can * be accessed through t.getScope() */ void enterScope(NodeTraversal t); /** * Called immediately before exiting a scope. The ending scope can * be accessed through t.getScope() */ void exitScope(NodeTraversal t); } /** * Abstract callback to visit all nodes in post order. */ public abstract static class AbstractPostOrderCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } } /** * Abstract scoped callback to visit all nodes in post order. */ public abstract static class AbstractScopedCallback implements ScopedCallback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return true; } @Override public void enterScope(NodeTraversal t) {} @Override public void exitScope(NodeTraversal t) {} } /** * Abstract callback to visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || !parent.isFunction() || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { @Override public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb; if (cb instanceof ScopedCallback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.inputId = null; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (inputId != null) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false, false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { inputId = NodeUtil.getInputId(root); sourceName = ""; curNode = root; pushScope(root); traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); inputId = NodeUtil.getInputId(scopeRoot); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { String sourceFileName = getBestSourceFileName(n); if (sourceFileName == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceFileName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceFileName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); inputId = null; sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule() { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } public static void traverseRoots( AbstractCompiler compiler, Callback cb, Node ... roots) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock(), body); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1; } int getScopeDepth() { return scopes.size() + scopeRoots.size(); } public boolean hasScope() { return !(scopes.isEmpty() && scopeRoots.isEmpty()); } /** Reports a diagnostic (error or warning) */ public void report(Node n, DiagnosticType diagnosticType, String... arguments) { JSError error = JSError.make(getBestSourceFileName(n), n, diagnosticType, arguments); compiler.report(error); } private static String getSourceName(Node n) { String name = n.getSourceFileName(); return name == null ? "" : name; } InputId getInputId() { return inputId; } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, CheckLevel level, DiagnosticType type, String... arguments) { return JSError.make(getBestSourceFileName(n), n, level, type, arguments); } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, DiagnosticType type, String... arguments) { return JSError.make(getBestSourceFileName(n), n, type, arguments); } private String getBestSourceFileName(Node n) { return n == null ? sourceName : n.getSourceFileName(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveUnusedNames.java0000644000175000017500000000452112115204405027106 0ustar apoapo/* * 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.javascript.jscomp.AnalyzeNameReferences.NameInfo; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NameReferenceGraph.Reference; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.rhino.Node; import java.util.logging.Logger; /** * Removes unused names. * */ class RemoveUnusedNames implements CompilerPass { private static final Logger logger = Logger.getLogger(RemoveUnusedNames.class.getName()); private final AbstractCompiler compiler; private final boolean canModifyExterns; /** * Creates a new pass for removing unused prototype properties, based * on the uniqueness of property names. * @param compiler The compiler. */ RemoveUnusedNames(AbstractCompiler compiler, boolean canModifyExterns) { this.compiler = compiler; this.canModifyExterns = canModifyExterns; } @Override public void process(Node externRoot, Node root) { AnalyzeNameReferences analyzer = new AnalyzeNameReferences(compiler); analyzer.process(externRoot, root); removeUnusedProperties(analyzer.getGraph()); } /** * Remove all properties under a given name if the property name is * never referenced. */ private void removeUnusedProperties(NameReferenceGraph graph) { for (GraphNode node : graph.getNodes()) { Name name = node.getValue(); NameInfo nameInfo = node.getAnnotation(); if (nameInfo == null || !nameInfo.isReferenced()) { if (canModifyExterns || !name.isExtern()) { name.remove(); compiler.reportCodeChange(); logger.fine("Removed unused name" + name); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/LineNumberCheck.java0000644000175000017500000000501612115204405026477 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; /** * A simple pass to ensure that all AST nodes have line numbers, * an that the line numbers are monotonically increasing. * * @author nicksantos@google.com (Nick Santos) */ class LineNumberCheck implements Callback, CompilerPass { static final DiagnosticType MISSING_LINE_INFO = DiagnosticType.error( "JSC_MISSING_LINE_INFO", "No source location information associated with {0}.\n" + "Most likely a Node has been created with settings the source file " + "and line/column location. Usually this is done using " + "Node.copyInformationFrom and supplying a Node from the source AST."); private final AbstractCompiler compiler; private boolean requiresLineNumbers = false; LineNumberCheck(AbstractCompiler compiler) { this.compiler = compiler; } public void setCheckSubTree(Node root) { requiresLineNumbers = true; NodeTraversal.traverse(compiler, root, this); } @Override public void process(Node externs, Node root) { requiresLineNumbers = false; NodeTraversal.traverse(compiler, root, this); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Each JavaScript file is rooted in a script node, so we'll only // have line number information inside the script node. if (n.isScript()) { requiresLineNumbers = true; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isScript()) { requiresLineNumbers = false; } else if (requiresLineNumbers) { if (n.getLineno() == -1) { // The tree version of the node is really the best diagnostic // info we have to offer here. compiler.report( t.makeError(n, MISSING_LINE_INFO, n.toStringTree())); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/testing/0000755000175000017500000000000012115204405024311 5ustar apoapo././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/testing/SimpleSourceExcerptProvider0000644000175000017500000000260112115204405031713 0ustar apoapo/* * 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.testing; import com.google.javascript.jscomp.Region; import com.google.javascript.jscomp.SourceExcerptProvider; import com.google.javascript.jscomp.SourceFile; /** * A simple source excerpt provider for testing. * @author nicksantos@google.com (Nick Santos) */ public class SimpleSourceExcerptProvider implements SourceExcerptProvider { private final SourceFile sourceFile; public SimpleSourceExcerptProvider(String source) { sourceFile = SourceFile.fromCode("input", source); } @Override public String getSourceLine(String sourceName, int lineNumber) { return sourceFile.getLine(lineNumber); } @Override public Region getSourceRegion(String sourceName, int lineNumber) { return sourceFile.getRegion(lineNumber); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/testing/TestErrorReporter.java0000644000175000017500000000524712115204405030640 0ustar apoapo/* * Copyright 2007 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.testing; import com.google.javascript.rhino.head.ErrorReporter; import com.google.javascript.rhino.head.EvaluatorException; import junit.framework.Assert; /** *

      An error reporter for testing that verifies that messages reported to the * reporter are expected.

      * *

      Sample use

      *
       * TestErrorReporter e =
       *   new TestErrorReporter(null, new String[] { "first warning" });
       * ...
       * assertTrue(e.hasEncounteredAllWarnings());
       * 
      * */ public final class TestErrorReporter extends Assert implements ErrorReporter { private final String[] errors; private final String[] warnings; private int errorsIndex = 0; private int warningsIndex = 0; public TestErrorReporter(String[] errors, String[] warnings) { this.errors = errors; this.warnings = warnings; } @Override public void error(String message, String sourceName, int line, String lineSource, int lineOffset) { if (errors != null && errorsIndex < errors.length) { assertEquals(errors[errorsIndex++], message); } else { fail("extra error: " + message); } } @Override public void warning(String message, String sourceName, int line, String lineSource, int lineOffset) { if (warnings != null && warningsIndex < warnings.length) { assertEquals(warnings[warningsIndex++], message); } else { fail("extra warning: " + message); } } @Override public EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { return new EvaluatorException("JSCompiler test code: " + message); } /** * Returns whether all warnings were reported to this reporter. */ public boolean hasEncounteredAllWarnings() { return (warnings == null) ? warningsIndex == 0 : warnings.length == warningsIndex; } /** * Returns whether all errors were reported to this reporter. */ public boolean hasEncounteredAllErrors() { return (errors == null) ? errorsIndex == 0 : errors.length == errorsIndex; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JsMessage.java0000644000175000017500000004643012115204405025367 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; /** * A representation of a translatable message in JavaScript source code. * *

      Instances are created using a {@link JsMessage.Builder}, * like this: *

       * JsMessage m = new JsMessage.Builder(key)
       *     .appendPart("Hi ")
       *     .appendPlaceholderReference("firstName")
       *     .appendPart("!")
       *     .setDesc("A welcome message")
       *     .build();
       * 
      * */ public class JsMessage { /** * Message style that could be used for JS code parsing. * The enum order is from most relaxed to most restricted. */ public enum Style { LEGACY, // All legacy code is completely OK RELAX, // You allowed to use legacy code but it would be reported as warn CLOSURE; // Any legacy code is prohibited /** * Calculates current messages {@link Style} based on the given arguments. * * @param useClosure if true then use closure style, otherwise not * @param allowLegacyMessages if true then allow legacy messages otherwise * not * @return the message style based on the given arguments */ static Style getFromParams(boolean useClosure, boolean allowLegacyMessages) { if (useClosure) { return allowLegacyMessages ? RELAX : CLOSURE; } else { return LEGACY; } } } private static final String MESSAGE_REPRESENTATION_FORMAT = "{$%s}"; private final String key; private final String id; private final List parts; private final Set placeholders; private final String desc; private final boolean hidden; private final String meaning; private final String sourceName; private final boolean isAnonymous; private final boolean isExternal; /** * Creates an instance. Client code should use a {@link JsMessage.Builder}. * * @param key a key that should identify this message in sources; typically * it is the message's name (e.g. {@code "MSG_HELLO"}). * @param id an id that *uniquely* identifies the message in the bundle. * It could be either the message name or id generated from the message * content. * @param meaning The user-specified meaning of the message. May be null if * the user did not specify an explicit meaning. */ private JsMessage(String sourceName, String key, boolean isAnonymous, boolean isExternal, String id, List parts, Set placeholders, String desc, boolean hidden, String meaning) { Preconditions.checkState(key != null); Preconditions.checkState(id != null); this.key = key; this.id = id; this.parts = Collections.unmodifiableList(parts); this.placeholders = Collections.unmodifiableSet(placeholders); this.desc = desc; this.hidden = hidden; this.meaning = meaning; this.sourceName = sourceName; this.isAnonymous = isAnonymous; this.isExternal = isExternal; } /** * Gets the message's sourceName. */ public String getSourceName() { return sourceName; } /** * Gets the message's key, or name (e.g. {@code "MSG_HELLO"}). */ public String getKey() { return key; } public boolean isAnonymous() { return isAnonymous; } public boolean isExternal() { return isExternal; } /** * Gets the message's id, or name (e.g. {@code "92430284230902938293"}). */ public String getId() { return id; } /** * Gets the description associated with this message, intended to help * translators, or null if this message has no description. */ public String getDesc() { return desc; } /** * Gets the meaning annotated to the message, intended to force different * translations. */ String getMeaning() { return meaning; } /** * Gets whether this message should be hidden from volunteer translators (to * reduce the chances of a new feature leak). */ public boolean isHidden() { return hidden; } /** * Gets a read-only list of the parts of this message. Each part is either a * {@link String} or a {@link PlaceholderReference}. */ public List parts() { return parts; } /** Gets a read-only set of the registered placeholders in this message. */ public Set placeholders() { return placeholders; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (CharSequence p : parts) { sb.append(p.toString()); } return sb.toString(); } /** @return false iff the message is represented by empty string. */ public boolean isEmpty() { for (CharSequence part : parts) { if (part.length() > 0) { return false; } } return true; } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof JsMessage)) return false; JsMessage m = (JsMessage) o; return id.equals(m.id) && key.equals(m.key) && isAnonymous == m.isAnonymous && parts.equals(m.parts) && (meaning == null ? m.meaning == null : meaning.equals(m.meaning)) && placeholders.equals(m.placeholders) && (desc == null ? m.desc == null : desc.equals(m.desc)) && (sourceName == null ? m.sourceName == null : sourceName.equals(m.sourceName)) && hidden == m.hidden; } @Override public int hashCode() { int hash = key.hashCode(); hash = 31 * hash + (isAnonymous ? 1 : 0); hash = 31 * hash + id.hashCode(); hash = 31 * hash + parts.hashCode(); hash = 31 * hash + (desc != null ? desc.hashCode() : 0); hash = 31 * hash + (hidden ? 1 : 0); hash = 31 * hash + (sourceName != null ? sourceName.hashCode() : 0); return hash; } /** A reference to a placeholder in a translatable message. */ public static class PlaceholderReference implements CharSequence { private final String name; PlaceholderReference(String name) { this.name = name; } @Override public int length() { return name.length(); } @Override public char charAt(int index) { return name.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return name.subSequence(start, end); } public String getName() { return name; } @Override public String toString() { return String.format(MESSAGE_REPRESENTATION_FORMAT, name); } @Override public boolean equals(Object o) { return o == this || o instanceof PlaceholderReference && name.equals(((PlaceholderReference) o).name); } @Override public int hashCode() { return 31 * name.hashCode(); } } /** * Contains functionality for creating JS messages. Generates authoritative * keys and fingerprints for a message that must stay constant over time. * * This implementation correctly processes unnamed messages and creates a key * for them that looks like MSG_. */ public static class Builder { private static final Pattern MSG_EXTERNAL_PATTERN = Pattern.compile("MSG_EXTERNAL_(\\d+)"); /** * @return an external message id or null if this is not an * external message identifier */ private static String getExternalMessageId(String identifier) { Matcher m = MSG_EXTERNAL_PATTERN.matcher(identifier); return m.matches() ? m.group(1) : null; } private String key; private String meaning; private String desc; private boolean hidden; private List parts = Lists.newLinkedList(); private Set placeholders = Sets.newHashSet(); private String sourceName; public Builder() { this(null); } /** Creates an instance. */ public Builder(String key) { this.key = key; } /** Gets the message's key (e.g. {@code "MSG_HELLO"}). */ public String getKey() { return key; } /** * @param key a key that should uniquely identify this message; typically * it is the message's name (e.g. {@code "MSG_HELLO"}). */ public Builder setKey(String key) { this.key = key; return this; } /** * @param sourceName The message's sourceName. */ public Builder setSourceName(String sourceName) { this.sourceName = sourceName; return this; } /** * Appends a placeholder reference to the message */ public Builder appendPlaceholderReference(String name) { Preconditions.checkNotNull(name, "Placeholder name could not be null"); parts.add(new PlaceholderReference(name)); placeholders.add(name); return this; } /** Appends a translatable string literal to the message. */ public Builder appendStringPart(String part) { Preconditions.checkNotNull(part, "String part of the message could not be null"); parts.add(part); return this; } /** Returns the message registered placeholders */ public Set getPlaceholders() { return placeholders; } /** Sets the description of the message, which helps translators. */ public Builder setDesc(String desc) { this.desc = desc; return this; } /** * Sets the programmer-specified meaning of this message, which * forces this message to translate differently. */ public Builder setMeaning(String meaning) { this.meaning = meaning; return this; } /** Sets whether the message should be hidden from volunteer translators. */ public Builder setIsHidden(boolean hidden) { this.hidden = hidden; return this; } /** Gets whether at least one part has been appended. */ public boolean hasParts() { return !parts.isEmpty(); } public List getParts() { return parts; } public JsMessage build() { return build(null); } public JsMessage build(IdGenerator idGenerator) { boolean isAnonymous = false; boolean isExternal = false; String id = null; if (getKey() == null) { // Before constructing a message we need to change unnamed messages name // to the unique one. key = JsMessageVisitor.MSG_PREFIX + fingerprint(getParts()); isAnonymous = true; } if (!isAnonymous) { String externalId = getExternalMessageId(key); if (externalId != null) { isExternal = true; id = externalId; } } if (!isExternal) { String defactoMeaning = meaning != null ? meaning : key; id = idGenerator == null ? defactoMeaning : idGenerator.generateId(defactoMeaning, parts); } return new JsMessage(sourceName, key, isAnonymous, isExternal, id, parts, placeholders, desc, hidden, meaning); } /** * Generates a compact uppercase alphanumeric text representation of a * 63-bit fingerprint of the content parts of a message. */ private static String fingerprint(List messageParts) { StringBuilder sb = new StringBuilder(); for (CharSequence part : messageParts) { if (part instanceof JsMessage.PlaceholderReference) { sb.append(part.toString()); } else { sb.append(part); } } long nonnegativeHash = Long.MAX_VALUE & Hash.hash64(sb.toString()); return Long.toString(nonnegativeHash, 36).toUpperCase(); } } /** * This class contains routines for hashing. * *

      The hash takes a byte array representing arbitrary data (a * number, String, or Object) and turns it into a small, hopefully * unique, number. There are additional convenience functions which * hash int, long, and String types. * *

      Note: this hash has weaknesses in the two * most-significant key bits and in the three least-significant seed * bits. The weaknesses are small and practically speaking, will not * affect the distribution of hash values. Still, it would be good * practice not to choose seeds 0, 1, 2, 3, ..., n to yield n, * independent hash functions. Use pseudo-random seeds instead. * *

      This code is based on the work of Craig Silverstein and Sanjay * Ghemawat in, then forked from com.google.common. * *

      The original code for the hash function is courtesy * Bob Jenkins. * *

      TODO: Add stream hashing functionality. */ final static class Hash { private Hash() {} /** Default hash seed (64 bit) */ private static final long SEED64 = 0x2b992ddfa23249d6L; // part of pi, arbitrary /** Hash constant (64 bit) */ private static final long CONSTANT64 = 0xe08c1d668b756f82L; // part of golden ratio, arbitrary /****************** * STRING HASHING * ******************/ /** * Hash a string to a 64 bit value. The digits of pi are used for * the hash seed. * * @param value the string to hash * @return 64 bit hash value */ static long hash64(@Nullable String value) { return hash64(value, SEED64); } /** * Hash a string to a 64 bit value using the supplied seed. * * @param value the string to hash * @param seed the seed * @return 64 bit hash value */ private static long hash64(@Nullable String value, long seed) { if (value == null) { return hash64(null, 0, 0, seed); } return hash64(value.getBytes(), seed); } /** * Hash byte array to a 64 bit value using the supplied seed. * * @param value the bytes to hash * @param seed the seed * @return 64 bit hash value */ private static long hash64(byte[] value, long seed) { return hash64(value, 0, value == null ? 0 : value.length, seed); } /** * Hash byte array to a 64 bit value using the supplied seed. * * @param value the bytes to hash * @param offset the starting position of value where bytes are * used for the hash computation * @param length number of bytes of value that are used for the * hash computation * @param seed the seed * @return 64 bit hash value */ @SuppressWarnings("fallthrough") private static long hash64( byte[] value, int offset, int length, long seed) { long a = CONSTANT64; long b = a; long c = seed; int keylen; for (keylen = length; keylen >= 24; keylen -= 24, offset += 24) { a += word64At(value, offset); b += word64At(value, offset + 8); c += word64At(value, offset + 16); // Mix a -= b; a -= c; a ^= c >>> 43; b -= c; b -= a; b ^= a << 9; c -= a; c -= b; c ^= b >>> 8; a -= b; a -= c; a ^= c >>> 38; b -= c; b -= a; b ^= a << 23; c -= a; c -= b; c ^= b >>> 5; a -= b; a -= c; a ^= c >>> 35; b -= c; b -= a; b ^= a << 49; c -= a; c -= b; c ^= b >>> 11; a -= b; a -= c; a ^= c >>> 12; b -= c; b -= a; b ^= a << 18; c -= a; c -= b; c ^= b >>> 22; } c += length; switch (keylen) { // deal with rest. Cases fall through case 23: c += ((long) value[offset + 22]) << 56; case 22: c += (value[offset + 21] & 0xffL) << 48; case 21: c += (value[offset + 20] & 0xffL) << 40; case 20: c += (value[offset + 19] & 0xffL) << 32; case 19: c += (value[offset + 18] & 0xffL) << 24; case 18: c += (value[offset + 17] & 0xffL) << 16; case 17: c += (value[offset + 16] & 0xffL) << 8; // the first byte of c is reserved for the length case 16: b += word64At(value, offset + 8); a += word64At(value, offset); break; case 15: b += (value[offset + 14] & 0xffL) << 48; case 14: b += (value[offset + 13] & 0xffL) << 40; case 13: b += (value[offset + 12] & 0xffL) << 32; case 12: b += (value[offset + 11] & 0xffL) << 24; case 11: b += (value[offset + 10] & 0xffL) << 16; case 10: b += (value[offset + 9] & 0xffL) << 8; case 9: b += (value[offset + 8] & 0xffL); case 8: a += word64At(value, offset); break; case 7: a += (value[offset + 6] & 0xffL) << 48; case 6: a += (value[offset + 5] & 0xffL) << 40; case 5: a += (value[offset + 4] & 0xffL) << 32; case 4: a += (value[offset + 3] & 0xffL) << 24; case 3: a += (value[offset + 2] & 0xffL) << 16; case 2: a += (value[offset + 1] & 0xffL) << 8; case 1: a += (value[offset + 0] & 0xffL); // case 0: nothing left to add } return mix64(a, b, c); } private static long word64At(byte[] bytes, int offset) { return (bytes[offset + 0] & 0xffL) + ((bytes[offset + 1] & 0xffL) << 8) + ((bytes[offset + 2] & 0xffL) << 16) + ((bytes[offset + 3] & 0xffL) << 24) + ((bytes[offset + 4] & 0xffL) << 32) + ((bytes[offset + 5] & 0xffL) << 40) + ((bytes[offset + 6] & 0xffL) << 48) + ((bytes[offset + 7] & 0xffL) << 56); } /** * Mixes longs a, b, and c, and returns the final value of c. */ private static long mix64(long a, long b, long c) { a -= b; a -= c; a ^= c >>> 43; b -= c; b -= a; b ^= a << 9; c -= a; c -= b; c ^= b >>> 8; a -= b; a -= c; a ^= c >>> 38; b -= c; b -= a; b ^= a << 23; c -= a; c -= b; c ^= b >>> 5; a -= b; a -= c; a ^= c >>> 35; b -= c; b -= a; b ^= a << 49; c -= a; c -= b; c ^= b >>> 11; a -= b; a -= c; a ^= c >>> 12; b -= c; b -= a; b ^= a << 18; c -= a; c -= b; c ^= b >>> 22; return c; } } public interface IdGenerator { /** * Generate the ID for the message. Messages with the same messageParts * and meaning will get the same id. Messages with the same id * will get the same translation. * * @param meaning The programmer-specified meaning. If no {@code @meaning} * annotation appears, we will use the name of the variable it's * assigned to. If the variable is unnamed, then we will just * use a fingerprint of the message. * @param messageParts The parts of the message, including the main * message text. */ String generateId(String meaning, List messageParts); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/0000755000175000017500000000000012115204405023735 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/StandardUnionFind.java0000644000175000017500000001436512115204405030163 0ustar apoapo/* * 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.graph; import javax.annotation.Nullable; import com.google.common.base.Objects; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import static com.google.common.collect.Iterators.filter; import com.google.common.collect.Maps; import java.io.Serializable; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * A Union-Find implementation. * *

      This class implements Union-Find algorithm with rank and path * compression. * *

      See * algorithmist for more detail. * * @param element type */ public class StandardUnionFind implements Serializable, UnionFind { private static final long serialVersionUID = -1L; /** All values with the same root node are in the same equivalence set. */ private final Map> elmap = Maps.newLinkedHashMap(); /** Creates an empty UnionFind structure. */ public StandardUnionFind() { } /** * Creates an UnionFind structure being a copy of other structure. * The created structure is optimal in a sense that the paths to * the root from any element will have a length of at most 1. * * @param other structure to be copied */ public StandardUnionFind(UnionFind other) { for (E elem : other.elements()) { union(elem, other.find(elem)); } } @Override public void add(E e) { union(e, e); } @Override public E union(E a, E b) { Node nodeA = findRootOrCreateNode(a); Node nodeB = findRootOrCreateNode(b); if (nodeA == nodeB) { return nodeA.element; } if (nodeA.rank > nodeB.rank) { nodeB.parent = nodeA; nodeA.size += nodeB.size; return nodeA.element; } nodeA.parent = nodeB; if (nodeA.rank == nodeB.rank) { nodeB.rank++; } nodeB.size += nodeA.size; return nodeB.element; } @Override public E find(E e) { checkArgument(elmap.containsKey(e), "Element does not exist: %s", e); return findRoot(elmap.get(e)).element; } @Override public boolean areEquivalent(E a, E b) { E aRep = find(a); E bRep = find(b); return aRep == bRep; } @Override public Set elements() { return Collections.unmodifiableSet(elmap.keySet()); } @Override public Collection> allEquivalenceClasses() { Map, ImmutableSet.Builder> groupsTmp = Maps.newHashMap(); for (Node elem : elmap.values()) { Node root = findRoot(elem); ImmutableSet.Builder builder = groupsTmp.get(root); if (builder == null) { builder = ImmutableSet.builder(); groupsTmp.put(root, builder); } builder.add(elem.element); } ImmutableList.Builder> result = ImmutableList.builder(); for (ImmutableSet.Builder group : groupsTmp.values()) { result.add(group.build()); } return result.build(); } /** * If e is already in a non-trivial equivalence class, that is, a class with * more than two elements, then return the {@link Node} corresponding to the * representative element. Otherwise, if e sits in an equivalence class by * itself, then create a {@link Node}, put it into elmap and return it. */ private Node findRootOrCreateNode(E e) { Node node = elmap.get(e); if (node != null) { return findRoot(node); } node = new Node(e); elmap.put(e, node); return node; } /** * Given a {@link Node}, walk the parent field as far as possible, until * reaching the root, which is the {@link Node} for the current * representative of this equivalence class. To achieve low runtime * complexity, also compress the path, by making each node a direct child of * the root. */ private Node findRoot(Node node) { if (node.parent != node) { node.parent = findRoot(node.parent); } return node.parent; } @Override public Set findAll(final E value) { checkArgument(elmap.containsKey(value), "Element does not exist: " + value); final Predicate isSameRoot = new Predicate() { /** some node that's close to the root, or null */ Node nodeForValue = elmap.get(value); @Override public boolean apply(@Nullable Object b) { if (Objects.equal(value, b)) { return true; } Node nodeForB = elmap.get(b); if (nodeForB == null) { return false; } nodeForValue = findRoot(nodeForValue); return findRoot(nodeForB) == nodeForValue; } }; return new AbstractSet() { @Override public boolean contains(Object o) { return isSameRoot.apply(o); } @Override public Iterator iterator() { return filter(elmap.keySet().iterator(), isSameRoot); } @Override public int size() { return findRoot(elmap.get(value)).size; } }; } /** The internal node representation. */ private static class Node { /** The parent node of this element. */ Node parent; /** The element represented by this node. */ final E element; /** A bound on the depth of the subtree rooted to this node. */ int rank = 0; /** * If this node is the root of a tree, this is the number of elements in the * tree. Otherwise, it's undefined. */ int size = 1; Node(E element) { this.parent = this; this.element = element; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/LatticeElement.java0000644000175000017500000000133112115204405027475 0ustar apoapo/* * 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.graph; /** * A lattice element. * */ public interface LatticeElement { } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/UnionFind.java0000644000175000017500000000542012115204405026472 0ustar apoapo/* * 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.graph; import java.util.Collection; import java.util.Set; /** * Union-Find is a classical algorithm used to find connected components in * graph theory. * *

      Each equivalence class has a representative element that is chosen * arbitrarily and is used to determine if two elements are members of the same * class. * *

      See * algorithmist for more detail. * * @param element type */ public interface UnionFind { /** * Adds the given element to a new set if it is not already in a set. * * @throws UnsupportedOperationException if the add operation is not * supported by this union-find. */ public void add(E e); /** * Unions the equivalence classes of {@code a} and {@code b} and returns the * representative of the resulting equivalence class. The elements will be * added if they are not already present. * * @throws UnsupportedOperationException if the add operation is not * supported by this union-find. */ public E union(E a, E b); /** Returns the representative of the equivalence class of {@code e}. */ public E find(E e); /** * Returns true if {@code a} and {@code b} belong to the same equivalence * class. * * @throws IllegalArgumentException if any argument is not an element of this * structure. */ public boolean areEquivalent(E a, E b); /** Returns an unmodifiable set of all elements added to the UnionFind. */ public Set elements(); /** * Returns an immutable collection containing all equivalence classes. The * returned collection represents a snapshot of the current state and will not * reflect changes made to the data structure. */ public Collection> allEquivalenceClasses(); /** * Returns the elements in the same equivalence class as {@code value}. * * @return an unmodifiable view. As equivalence classes are merged, this set * will reflect those changes. * @throws IllegalArgumentException if a requested element does not belong * to the structure. */ public Set findAll(final E value); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/GraphvizGraph.java0000644000175000017500000000505312115204405027357 0ustar apoapo/* * 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.graph; import java.util.List; /** * A graph that can be dumped to a Graphviz DOT file. *

      * An object which can be visualized as a graph should implement this interface. * The DotFormatter.toDot function can be used to get a * visualization of the object for debugging purpose. * */ public interface GraphvizGraph { /** * Name of the graph. * * @return Name of the graph. */ String getName(); /** * Graph type. * * @return True if the graph is a directed graph. */ boolean isDirected(); /** * Retrieve a list of nodes in the graph. * * @return A list of nodes in the graph. */ List getGraphvizNodes(); /** * Retrieve a list of edges in the graph. * * @return A list of edges in the graph. */ List getGraphvizEdges(); /** * A Graphviz node. */ interface GraphvizNode { /** * Retrieves the unique ID. * * @return A the unique ID of the node. */ String getId(); /** * Retrieves color of the node. * * @return The color of the node. */ String getColor(); /** * Retrieves the label of the node. * * @return Label of the node. */ String getLabel(); } /** * A Graphviz edge. */ interface GraphvizEdge { /** * Get the first node in the edge. In a directed node, this will be the * source node. * * @return First node in the edge. */ String getNode1Id(); /** * Get the second node in the edge. In a directed node, this will be the * destination node. * * @return First node in the edge. */ String getNode2Id(); /** * Retrieves color of the edge. * * @return The color of the edge. */ String getColor(); /** * Retrieves the label of the edge. * * @return Label of the edge. */ String getLabel(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/AdjacencyGraph.java0000644000175000017500000000340012115204405027440 0ustar apoapo/* * 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.graph; import java.util.Collection; /** * A minimal graph interface. Provided is add nodes to the graph, adjacency * calculation between a SubGraph and a GraphNode, and adding node annotations. * *

      For a more extensive interface, see {@link Graph}. * * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. * @see Graph */ public interface AdjacencyGraph { /** Gets an immutable list of all nodes. */ Collection> getNodes(); /** * Gets a node from the graph given a value. Values equality are compared * using Object.equals. * * @param value The node's value. * @return The corresponding node in the graph, null if there value has no * corresponding node. */ GraphNode getNode(N value); /** Returns an empty SubGraph for this Graph. */ SubGraph newSubGraph(); /** Makes each node's annotation null. */ void clearNodeAnnotations(); /** * Returns a weight for the given value to be used in ordering nodes, e.g. * in {@link GraphColoring}. */ int getWeight(N value); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/FixedPointGraphTraversal.java0000644000175000017500000001153712115204405031526 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * A utility class for doing fixed-point computations. We traverse * the edges over the given directed graph until the graph reaches * a steady state. * * @author nicksantos@google.com (Nick Santos) * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public final class FixedPointGraphTraversal { // TODO(nicksantos): Generalize the algorithm for undirected graphs, if we // need it. private final EdgeCallback callback; public static final String NON_HALTING_ERROR_MSG = "Fixed point computation not halting"; /** * Create a new traversal. * @param callback A callback for updating the state of the graph each * time an edge is traversed. */ public FixedPointGraphTraversal(EdgeCallback callback) { this.callback = callback; } /** * Helper method for creating new traversals. */ public static FixedPointGraphTraversal newTraversal( EdgeCallback callback) { return new FixedPointGraphTraversal(callback); } /** * Compute a fixed point for the given graph. * @param graph The graph to traverse. */ public void computeFixedPoint(DiGraph graph) { Set nodes = Sets.newHashSet(); for (DiGraphNode node : graph.getDirectedGraphNodes()) { nodes.add(node.getValue()); } computeFixedPoint(graph, nodes); } /** * Compute a fixed point for the given graph, entering from the given node. * @param graph The graph to traverse. * @param entry The node to begin traversing from. */ public void computeFixedPoint(DiGraph graph, N entry) { Set entrySet = Sets.newHashSet(); entrySet.add(entry); computeFixedPoint(graph, entrySet); } /** * Compute a fixed point for the given graph, entering from the given nodes. * @param graph The graph to traverse. * @param entrySet The nodes to begin traversing from. */ public void computeFixedPoint(DiGraph graph, Set entrySet) { int cycleCount = 0; long nodeCount = graph.getNodes().size(); // Choose a bail-out heuristically in case the computation // doesn't converge. long maxIterations = Math.max(nodeCount * nodeCount * nodeCount, 100); // Use a LinkedHashSet, so that the traversal is deterministic. LinkedHashSet> workSet = Sets.newLinkedHashSet(); for (N n : entrySet) { workSet.add(graph.getDirectedGraphNode(n)); } for (; !workSet.isEmpty() && cycleCount < maxIterations; cycleCount++) { // For every out edge in the workSet, traverse that edge. If that // edge updates the state of the graph, then add the destination // node to the resultSet, so that we can update all of its out edges // on the next iteration. DiGraphNode source = workSet.iterator().next(); N sourceValue = source.getValue(); workSet.remove(source); List> outEdges = source.getOutEdges(); for (DiGraphEdge edge : outEdges) { N destNode = edge.getDestination().getValue(); if (callback.traverseEdge(sourceValue, edge.getValue(), destNode)) { workSet.add(edge.getDestination()); } } } Preconditions.checkState(cycleCount != maxIterations, NON_HALTING_ERROR_MSG); } public static interface EdgeCallback { /** * Update the state of the destination node when the given edge * is traversed. For the fixed-point computation to work, only the * destination node may be modified. The source node and the edge must * not be modified. * * @param source The start node. * @param e The edge. * @param destination The end node. * @return Whether the state of the destination node changed. */ boolean traverseEdge(Node source, Edge e, Node destination); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/DiGraph.java0000644000175000017500000000726312115204405026126 0ustar apoapo/* * 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.graph; import java.util.List; /** * A generic directed graph. * * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class DiGraph extends Graph { /** * Gets an immutable iterable over all the nodes in the graph. */ public abstract Iterable> getDirectedGraphNodes(); /** * Gets an immutable list of out edges of the given node. */ public abstract List> getOutEdges(N nodeValue); /** * Gets an immutable list of in edges of the given node. */ public abstract List> getInEdges(N nodeValue); public abstract List> getDirectedPredNodes( DiGraphNode n); public abstract List> getDirectedSuccNodes( DiGraphNode n); public abstract List> getDirectedPredNodes(N nodeValue); public abstract List> getDirectedSuccNodes(N nodeValue); public abstract DiGraphNode createDirectedGraphNode(N nodeValue); public abstract DiGraphNode getDirectedGraphNode(N nodeValue); public abstract List> getDirectedGraphEdges(N n1, N n2); /** * Disconnects all edges from n1 to n2. * * @param n1 Source node. * @param n2 Destination node. */ public abstract void disconnectInDirection(N n1, N n2); /** * Checks whether two nodes in the graph are connected via a directed edge. * * @param n1 Node 1. * @param n2 Node 2. * @return true if the graph contains edge from n1 to n2. */ public abstract boolean isConnectedInDirection(N n1, N n2); /** * Checks whether two nodes in the graph are connected via a directed edge * with the given value. * * @param n1 Node 1. * @param edgeValue edge value tag * @param n2 Node 2. * @return true if the edge exists. */ public abstract boolean isConnectedInDirection(N n1, E edgeValue, N n2); @Override public boolean isConnected(N n1, N n2) { return isConnectedInDirection(n1, n2) || isConnectedInDirection(n2, n1); } @Override public boolean isConnected(N n1, E e, N n2) { return isConnectedInDirection(n1, e, n2) || isConnectedInDirection(n2, e, n1); } /** * A generic directed graph node. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public static interface DiGraphNode extends GraphNode { public List> getOutEdges(); public List> getInEdges(); } /** * A generic directed graph edge. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public static interface DiGraphEdge extends GraphEdge { public DiGraphNode getSource(); public DiGraphNode getDestination(); public void setSource(DiGraphNode node); public void setDestination(DiGraphNode node); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/SubGraph.java0000644000175000017500000000207512115204405026317 0ustar apoapo/* * 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.graph; /** * An interface representing a subgraph that provides adjacency calculation to * a node. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public interface SubGraph { /** Returns true if the node is a neighbor of any node in this SubGraph. */ boolean isIndependentOf(N node); /** Adds the node into this subgraph. */ void addNode(N value); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/GraphNode.java0000644000175000017500000000165712115204405026460 0ustar apoapo/* * 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.graph; /** * A generic node. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public interface GraphNode extends Annotatable { /** * Retrieves the node's value. * * @return The value. */ N getValue(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/GraphReachability.java0000644000175000017500000000630712115204405030170 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; /** * Computes all the reachable nodes. Upon execution of {@link #compute(Object)}, * the graph nodes will be annotated with {@link #REACHABLE} if it is reachable * from the specified entry node. * * @see GraphNode#getAnnotation() */ public class GraphReachability implements EdgeCallback { // TODO(user): This should work for undirected graphs when // FixedPointGraphTraversal accepts them. private final DiGraph graph; private final Predicate> edgePredicate; public GraphReachability(DiGraph graph) { this(graph, null); } /** * @param graph The graph. * @param edgePredicate Given the predecessor P of the a node S and the edge * coming from P to S, this predicate should return true if S is * reachable from P using the edge. */ public GraphReachability(DiGraph graph, Predicate> edgePredicate) { this.graph = graph; this.edgePredicate = edgePredicate; } public void compute(N entry) { graph.clearNodeAnnotations(); graph.getNode(entry).setAnnotation(REACHABLE); FixedPointGraphTraversal.newTraversal(this) .computeFixedPoint(graph, entry); } public void recompute(N reachableNode) { GraphNode newReachable = graph.getNode(reachableNode); Preconditions.checkState(newReachable.getAnnotation() != REACHABLE); newReachable.setAnnotation(REACHABLE); FixedPointGraphTraversal.newTraversal(this) .computeFixedPoint(graph, reachableNode); } @Override public boolean traverseEdge(N source, E e, N destination) { if (graph.getNode(source).getAnnotation() == REACHABLE && (edgePredicate == null || edgePredicate.apply(new EdgeTuple(source, e, destination)))) { GraphNode destNode = graph.getNode(destination); if (destNode.getAnnotation() != REACHABLE) { destNode.setAnnotation(REACHABLE); return true; } } return false; } public static final Annotation REACHABLE = new Annotation() {}; /** * Represents Source Node, Edge and Destination Node. */ public static final class EdgeTuple { public final N sourceNode; public final E edge; public final N destNode; public EdgeTuple(N sourceNode, E edge, N destNode) { this.sourceNode = sourceNode; this.edge = edge; this.destNode = destNode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/Annotation.java0000644000175000017500000000142712115204405026716 0ustar apoapo/* * 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.graph; /** * Information that can be annotated to a {@link GraphNode} or * {@link Graph.GraphEdge}. */ public interface Annotation { } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/LinkedDirectedGraph.java0000644000175000017500000003706312115204405030445 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; /** * A directed graph using linked list within nodes to store edge information. *

      * This implementation favors directed graph operations inherited from * DirectedGraph. * Operations from Graph would tends to be slower. * * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public class LinkedDirectedGraph extends DiGraph implements GraphvizGraph { protected final Map> nodes = Maps.newHashMap(); @Override public SubGraph newSubGraph() { return new SimpleSubGraph(this); } public static LinkedDirectedGraph createWithoutAnnotations() { return new LinkedDirectedGraph(false, false); } public static LinkedDirectedGraph createWithNodeAnnotations() { return new LinkedDirectedGraph(true, false); } public static LinkedDirectedGraph createWithEdgeAnnotations() { return new LinkedDirectedGraph(false, true); } public static LinkedDirectedGraph create() { return new LinkedDirectedGraph(true, true); } private final boolean useNodeAnnotations; private final boolean useEdgeAnnotations; protected LinkedDirectedGraph( boolean useNodeAnnotations, boolean useEdgeAnnotations) { this.useNodeAnnotations = useNodeAnnotations; this.useEdgeAnnotations = useEdgeAnnotations; } @Override public void connect(N srcValue, E edgeValue, N destValue) { LinkedDirectedGraphNode src = getNodeOrFail(srcValue); LinkedDirectedGraphNode dest = getNodeOrFail(destValue); LinkedDirectedGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedDirectedGraphEdge(src, edgeValue, dest) : new LinkedDirectedGraphEdge(src, edgeValue, dest); src.getOutEdges().add(edge); dest.getInEdges().add(edge); } @Override public void disconnect(N n1, N n2) { disconnectInDirection(n1, n2); disconnectInDirection(n2, n1); } @Override public void disconnectInDirection(N srcValue, N destValue) { LinkedDirectedGraphNode src = getNodeOrFail(srcValue); LinkedDirectedGraphNode dest = getNodeOrFail(destValue); for (DiGraphEdge edge : getDirectedGraphEdges(srcValue, destValue)) { src.getOutEdges().remove(edge); dest.getInEdges().remove(edge); } } @Override public Iterable> getDirectedGraphNodes() { return Collections.>unmodifiableCollection( nodes.values()); } @Override public DiGraphNode getDirectedGraphNode(N nodeValue) { return nodes.get(nodeValue); } @Override public GraphNode getNode(N nodeValue) { return getDirectedGraphNode(nodeValue); } @Override public List> getInEdges(N nodeValue) { LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); return Collections.>unmodifiableList(node.getInEdges()); } @Override public List> getOutEdges(N nodeValue) { LinkedDirectedGraphNode node = getNodeOrFail(nodeValue); return Collections.>unmodifiableList(node.getOutEdges()); } @Override public DiGraphNode createDirectedGraphNode(N nodeValue) { LinkedDirectedGraphNode node = nodes.get(nodeValue); if (node == null) { node = useNodeAnnotations ? new AnnotatedLinkedDirectedGraphNode(nodeValue) : new LinkedDirectedGraphNode(nodeValue); nodes.put(nodeValue, node); } return node; } @Override public List> getEdges(N n1, N n2) { // Since this is a method from a generic graph, edges from both // directions must be added to the returning list. List> forwardEdges = getDirectedGraphEdges(n1, n2); List> backwardEdges = getDirectedGraphEdges(n2, n1); int totalSize = forwardEdges.size() + backwardEdges.size(); List> edges = Lists.newArrayListWithCapacity(totalSize); edges.addAll(forwardEdges); edges.addAll(backwardEdges); return edges; } @Override public GraphEdge getFirstEdge(N n1, N n2) { DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode dNode2 = getNodeOrFail(n2); for (DiGraphEdge outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { return outEdge; } } for (DiGraphEdge outEdge : dNode2.getOutEdges()) { if (outEdge.getDestination() == dNode1) { return outEdge; } } return null; } @Override public GraphNode createNode(N value) { return createDirectedGraphNode(value); } @Override public List> getDirectedGraphEdges(N n1, N n2) { DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode dNode2 = getNodeOrFail(n2); List> edges = Lists.newArrayList(); for (DiGraphEdge outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2) { edges.add(outEdge); } } return edges; } @Override public boolean isConnectedInDirection(N n1, N n2) { return isConnectedInDirection(n1, Predicates.alwaysTrue(), n2); } @Override public boolean isConnectedInDirection(N n1, E edgeValue, N n2) { return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2); } private boolean isConnectedInDirection(N n1, Predicate edgeMatcher, N n2) { // Verify the nodes. DiGraphNode dNode1 = getNodeOrFail(n1); DiGraphNode dNode2 = getNodeOrFail(n2); for (DiGraphEdge outEdge : dNode1.getOutEdges()) { if (outEdge.getDestination() == dNode2 && edgeMatcher.apply(outEdge.getValue())) { return true; } } return false; } @Override public List> getDirectedPredNodes(N nodeValue) { return getDirectedPredNodes(nodes.get(nodeValue)); } @Override public List> getDirectedSuccNodes(N nodeValue) { return getDirectedSuccNodes(nodes.get(nodeValue)); } @Override public List> getDirectedPredNodes( DiGraphNode dNode) { if (dNode == null) { throw new IllegalArgumentException(dNode + " is null"); } List> nodeList = Lists.newArrayList(); for (DiGraphEdge edge : dNode.getInEdges()) { nodeList.add(edge.getSource()); } return nodeList; } @Override public List> getDirectedSuccNodes( DiGraphNode dNode) { if (dNode == null) { throw new IllegalArgumentException(dNode + " is null"); } List> nodeList = Lists.newArrayList(); for (DiGraphEdge edge : dNode.getOutEdges()) { nodeList.add(edge.getDestination()); } return nodeList; } @Override public List getGraphvizEdges() { List edgeList = Lists.newArrayList(); for (LinkedDirectedGraphNode node : nodes.values()) { for (DiGraphEdge edge : node.getOutEdges()) { edgeList.add((LinkedDirectedGraphEdge) edge); } } return edgeList; } @Override public List getGraphvizNodes() { List nodeList = Lists.newArrayListWithCapacity(nodes.size()); for (LinkedDirectedGraphNode node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public String getName() { return "LinkedGraph"; } @Override public boolean isDirected() { return true; } @Override public Collection> getNodes() { return Collections.>unmodifiableCollection(nodes.values()); } @Override public List> getNeighborNodes(N value) { DiGraphNode node = getDirectedGraphNode(value); return getNeighborNodes(node); } public List> getNeighborNodes(DiGraphNode node) { List> result = Lists.newArrayList(); for (Iterator> i = ((LinkedDirectedGraphNode) node).neighborIterator();i.hasNext();) { result.add(i.next()); } return result; } @Override public Iterator> getNeighborNodesIterator(N value) { LinkedDirectedGraphNode node = nodes.get(value); Preconditions.checkNotNull(node); return node.neighborIterator(); } @Override public List> getEdges() { List> result = Lists.newArrayList(); for (DiGraphNode node : nodes.values()) { for (DiGraphEdge edge : node.getOutEdges()) { result.add(edge); } } return Collections.unmodifiableList(result); } @Override public int getNodeDegree(N value) { DiGraphNode node = getNodeOrFail(value); return node.getInEdges().size() + node.getOutEdges().size(); } /** * A directed graph node that stores outgoing edges and incoming edges as an * list within the node itself. */ static class LinkedDirectedGraphNode implements DiGraphNode, GraphvizNode { List> inEdgeList = Lists.newArrayList(); List> outEdgeList = Lists.newArrayList(); protected final N value; /** * Constructor * * @param nodeValue Node's value. */ LinkedDirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public N getValue() { return value; } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String toString() { return getLabel(); } @Override public List> getInEdges() { return inEdgeList; } @Override public List> getOutEdges() { return outEdgeList; } private Iterator> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator> { private final Iterator> in = inEdgeList.iterator(); private final Iterator> out = outEdgeList.iterator(); @Override public boolean hasNext() { return in.hasNext() || out.hasNext(); } @Override public GraphNode next() { boolean isOut = !in.hasNext(); Iterator> curIterator = isOut ? out : in; DiGraphEdge s = curIterator.next(); return isOut ? s.getDestination() : s.getSource(); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * A directed graph node with annotations. */ static class AnnotatedLinkedDirectedGraphNode extends LinkedDirectedGraphNode { protected Annotation annotation; /** * @param nodeValue Node's value. */ AnnotatedLinkedDirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class LinkedDirectedGraphEdge implements DiGraphEdge, GraphvizEdge { private DiGraphNode sourceNode; private DiGraphNode destNode; protected final E value; /** * Constructor. * * @param edgeValue Edge Value. */ LinkedDirectedGraphEdge(DiGraphNode sourceNode, E edgeValue, DiGraphNode destNode) { this.value = edgeValue; this.sourceNode = sourceNode; this.destNode = destNode; } @Override public DiGraphNode getSource() { return sourceNode; } @Override public DiGraphNode getDestination() { return destNode; } @Override public void setDestination(DiGraphNode node) { destNode = node; } @Override public void setSource(DiGraphNode node) { sourceNode = node; } @Override public E getValue() { return value; } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String getNode1Id() { return ((LinkedDirectedGraphNode) sourceNode).getId(); } @Override public String getNode2Id() { return ((LinkedDirectedGraphNode) destNode).getId(); } @Override public String toString() { return sourceNode.toString() + " -> " + destNode.toString(); } @Override public GraphNode getNodeA() { return sourceNode; } @Override public GraphNode getNodeB() { return destNode; } } /** * A directed graph edge that stores the source and destination nodes at each * edge. */ static class AnnotatedLinkedDirectedGraphEdge extends LinkedDirectedGraphEdge { protected Annotation annotation; /** * Constructor. * * @param edgeValue Edge Value. */ AnnotatedLinkedDirectedGraphEdge(DiGraphNode sourceNode, E edgeValue, DiGraphNode destNode) { super(sourceNode, edgeValue, destNode); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/Graph.java0000644000175000017500000002405312115204405025645 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.List; /** * The base generic class for graph-like data structure and algorithms in * the compiler. *

      * Nodes and edges in the graph can store a piece of data that this graph is * used to represent. For example, a variable interference graph might store a * variable in the node. This piece of data can be accessed with * {@link GraphNode#getValue} and {@link GraphEdge#getValue}. *

      * Algorithms and analysis can annotate information on the nodes and edges * using {@link GraphNode#getValue} and {@link GraphEdge#getValue}. For example, * a graph coloring algorithm can store the color as an annotation. If multiple * analyses are required, it is up to the user of the analysis to save the * annotated solution between passes. *

      * We implemented our own graph data structure (as opposed to using * com.google.common.graph) for two reasons. First, aside from * the node's label value, we would like to annotate information on the nodes * and edges. Using a map to annotate would introduce too much overhead during * fix point analysis. Also, com.google.common.graph does not * support labeling of edges. Secondly, not using another external package would * limit our dependencies. *

      * TODO(user): All functionality for removing nodes and edges. * * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class Graph implements AdjacencyGraph { /** * Pseudo typedef for a pair of annotations. Record of an object's * annotation at some state. */ private static final class AnnotationState { private final Annotatable first; private final Annotation second; public AnnotationState(Annotatable annotatable, Annotation annotation) { this.first = annotatable; this.second = annotation; } } /** * Pseudo typedef for ArrayList. Record of a collection of * objects' annotations at some state. */ private static class GraphAnnotationState extends ArrayList { private static final long serialVersionUID = 1L; public GraphAnnotationState(int size) { super(size); } } /** * Used by {@link #pushNodeAnnotations()} and {@link #popNodeAnnotations()}. */ private Deque nodeAnnotationStack; /** * Used by {@link #pushEdgeAnnotations()} and {@link #popEdgeAnnotations()}. */ private Deque edgeAnnotationStack; /** * Connects two nodes in the graph with an edge. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public abstract void connect(N n1, E edge, N n2); /** * Disconnects two nodes in the graph by removing all edges between them. * * @param n1 First node. * @param n2 Second node. */ public abstract void disconnect(N n1, N n2); /** * Connects two nodes in the graph with an edge if such edge does not already * exists between the nodes. * * @param n1 First node. * @param edge The edge. * @param n2 Second node. */ public final void connectIfNotFound(N n1, E edge, N n2) { if (!isConnected(n1, edge, n2)) { connect(n1, edge, n2); } } /** * Gets a node from the graph given a value. New nodes are created if that * value has not been assigned a graph node. Values equality are compared * using Object.equals. * * @param value The node's value. * @return The corresponding node in the graph. */ public abstract GraphNode createNode(N value); /** Gets an immutable list of all nodes. */ @Override public abstract Collection> getNodes(); /** Gets an immutable list of all edges. */ public abstract List> getEdges(); /** * Gets the degree of a node. * * @param value The node's value. * @return The degree of the node. */ public abstract int getNodeDegree(N value); @Override public int getWeight(N value) { return getNodeDegree(value); } /** * Gets the neighboring nodes. * * @param value The node's value. * @return A list of neighboring nodes. */ public abstract List> getNeighborNodes(N value); public abstract Iterator> getNeighborNodesIterator(N value); /** * Retrieves an edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The list of edges between those two values in the graph. */ public abstract List> getEdges(N n1, N n2); /** * Retrieves any edge from the graph. * * @param n1 Node one. * @param n2 Node two. * @return The first edges between those two values in the graph. null if * there are none. */ public abstract GraphEdge getFirstEdge(N n1, N n2); /** * Checks whether the node exists in the graph ({@link #createNode(Object)} * has been called with that value). * * @param n Node. * @return true if it exist. */ public final boolean hasNode(N n) { return getNode(n) != null; } /** * Checks whether two nodes in the graph are connected. * * @param n1 Node 1. * @param n2 Node 2. * @return true if the two nodes are connected. */ public abstract boolean isConnected(N n1, N n2); /** * Checks whether two nodes in the graph are connected by the given * edge type. * * @param n1 Node 1. * @param e The edge type. * @param n2 Node 2. */ public abstract boolean isConnected(N n1, E e, N n2); /** * Gets the node of the specified type, or throws an * IllegalArgumentException. */ @SuppressWarnings("unchecked") > T getNodeOrFail(N val) { T node = (T) getNode(val); if (node == null) { throw new IllegalArgumentException(val + " does not exist in graph"); } return node; } @Override public final void clearNodeAnnotations() { for (GraphNode n : getNodes()) { n.setAnnotation(null); } } /** Makes each edge's annotation null. */ public final void clearEdgeAnnotations() { for (GraphEdge e : getEdges()) { e.setAnnotation(null); } } /** * Pushes nodes' annotation values. Restored with * {@link #popNodeAnnotations()}. Nodes' annotation values are cleared. */ public final void pushNodeAnnotations() { if (nodeAnnotationStack == null) { nodeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(nodeAnnotationStack, getNodes()); } /** * Restores nodes' annotation values to state before last * {@link #pushNodeAnnotations()}. */ public final void popNodeAnnotations() { Preconditions.checkNotNull(nodeAnnotationStack, "Popping node annotations without pushing."); popAnnotations(nodeAnnotationStack); } /** * Pushes edges' annotation values. Restored with * {@link #popEdgeAnnotations()}. Edges' annotation values are cleared. */ public final void pushEdgeAnnotations() { if (edgeAnnotationStack == null) { edgeAnnotationStack = Lists.newLinkedList(); } pushAnnotations(edgeAnnotationStack, getEdges()); } /** * Restores edges' annotation values to state before last * {@link #pushEdgeAnnotations()}. */ public final void popEdgeAnnotations() { Preconditions.checkNotNull(edgeAnnotationStack, "Popping edge annotations without pushing."); popAnnotations(edgeAnnotationStack); } /** * A generic edge. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public interface GraphEdge extends Annotatable { /** * Retrieves the edge's value. * * @return The value. */ E getValue(); GraphNode getNodeA(); GraphNode getNodeB(); } /** * A simple implementation of SubGraph that calculates adjacency by iterating * over a node's neighbors. */ class SimpleSubGraph implements SubGraph { private Graph graph; private List> nodes = Lists.newArrayList(); SimpleSubGraph(Graph graph) { this.graph = graph; } @Override public boolean isIndependentOf(N value) { GraphNode node = graph.getNode(value); for (GraphNode n : nodes) { if (graph.getNeighborNodes(n.getValue()).contains(node)) { return false; } } return true; } @Override public void addNode(N value) { nodes.add(graph.getNodeOrFail(value)); } } /** * Pushes a new list on stack and stores nodes annotations in the new list. * Clears objects' annotations as well. */ private static void pushAnnotations( Deque stack, Collection haveAnnotations) { stack.push(new GraphAnnotationState(haveAnnotations.size())); for (Annotatable h : haveAnnotations) { stack.peek().add(new AnnotationState(h, h.getAnnotation())); h.setAnnotation(null); } } /** * Restores the node annotations on the top of stack and pops stack. */ private static void popAnnotations(Deque stack) { for (AnnotationState as : stack.pop()) { as.first.setAnnotation(as.second); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/Annotatable.java0000644000175000017500000000212712115204405027032 0ustar apoapo/* * 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.graph; /** * Object that has an annotation. */ public interface Annotatable { /** * Annotates a piece of information to the object. * * @param data Information to be annotated. */ void setAnnotation(Annotation data); /** * Retrieves a piece of information that has been annotated. * * @return The annotation or null if the object has not * been annotated. */ A getAnnotation(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/GraphPruner.java0000644000175000017500000000661012115204405027040 0ustar apoapo/* * Copyright 2011 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.graph; import com.google.common.base.Predicate; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; /** * Prunes a graph, creating a new graph with nodes removed. * * If a node is removed from the graph, any paths through that node * will be replaced with edges. In other words, if A and B are nodes * in the original graph and the pruned graph, then there exists a path * from A -> B in the original graph iff there's a path from A -> B * in the pruned graph. * * We do not make any guarantees about what edges are in the pruned graph. * * @author nicksantos@google.com (Nick Santos) */ public class GraphPruner { private final DiGraph graph; public GraphPruner(DiGraph graph) { this.graph = graph; } public LinkedDirectedGraph prune(Predicate keep) { LinkedDirectedGraph workGraph = cloneGraph(graph); // Create a work graph where all nodes with a path between them have // an edge. for (DiGraphNode node : workGraph.getDirectedGraphNodes()) { for (DiGraphEdge inEdge : node.getInEdges()) { for (DiGraphEdge outEdge : node.getOutEdges()) { N source = inEdge.getSource().getValue(); N dest = outEdge.getDestination().getValue(); if (!workGraph.isConnectedInDirection(source, dest)) { workGraph.connect(source, outEdge.getValue(), dest); } } } } // Build a complete subgraph of workGraph. LinkedDirectedGraph resultGraph = LinkedDirectedGraph.create(); for (DiGraphNode node : workGraph.getDirectedGraphNodes()) { if (keep.apply(node.getValue())) { resultGraph.createNode(node.getValue()); for (DiGraphEdge outEdge : node.getOutEdges()) { N source = node.getValue(); N dest = outEdge.getDestination().getValue(); if (keep.apply(dest)) { resultGraph.createNode(dest); if (source != dest && !resultGraph.isConnectedInDirection(source, dest)) { resultGraph.connect(source, outEdge.getValue(), dest); } } } } } return resultGraph; } private static LinkedDirectedGraph cloneGraph( DiGraph graph) { LinkedDirectedGraph newGraph = LinkedDirectedGraph.create(); for (DiGraphNode node : graph.getDirectedGraphNodes()) { newGraph.createNode(node.getValue()); for (DiGraphEdge outEdge : node.getOutEdges()) { N dest = outEdge.getDestination().getValue(); newGraph.createNode(dest); newGraph.connect(node.getValue(), outEdge.getValue(), dest); } } return newGraph; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/LinkedUndirectedGraph.java0000644000175000017500000003174412115204405031010 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; /** * An undirected graph using linked list within nodes to store edge * information. * * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public class LinkedUndirectedGraph extends UndiGraph implements GraphvizGraph { protected final Map> nodes = Maps.newHashMap(); @Override public SubGraph newSubGraph() { return new SimpleSubGraph(this); } public static LinkedUndirectedGraph createWithoutAnnotations() { return new LinkedUndirectedGraph(false, false); } public static LinkedUndirectedGraph createWithNodeAnnotations() { return new LinkedUndirectedGraph(true, false); } public static LinkedUndirectedGraph createWithEdgeAnnotations() { return new LinkedUndirectedGraph(false, true); } public static LinkedUndirectedGraph create() { return new LinkedUndirectedGraph(true, true); } private final boolean useNodeAnnotations; private final boolean useEdgeAnnotations; protected LinkedUndirectedGraph( boolean useNodeAnnotations, boolean useEdgeAnnotations) { this.useNodeAnnotations = useNodeAnnotations; this.useEdgeAnnotations = useEdgeAnnotations; } @Override public void connect(N srcValue, E edgeValue, N destValue) { LinkedUndirectedGraphNode src = getNodeOrFail(srcValue); LinkedUndirectedGraphNode dest = getNodeOrFail(destValue); LinkedUndirectedGraphEdge edge = useEdgeAnnotations ? new AnnotatedLinkedUndirectedGraphEdge(src, edgeValue, dest) : new LinkedUndirectedGraphEdge(src, edgeValue, dest); src.getNeighborEdges().add(edge); dest.getNeighborEdges().add(edge); } @Override public void disconnect(N srcValue, N destValue) { LinkedUndirectedGraphNode src = getNodeOrFail(srcValue); LinkedUndirectedGraphNode dest = getNodeOrFail(destValue); for (UndiGraphEdge edge : getUndirectedGraphEdges(srcValue, destValue)) { src.getNeighborEdges().remove(edge); dest.getNeighborEdges().remove(edge); } } @Override public UndiGraphNode createUndirectedGraphNode( N nodeValue) { LinkedUndirectedGraphNode node = nodes.get(nodeValue); if (node == null) { node = useNodeAnnotations ? new AnnotatedLinkedUndirectedGraphNode(nodeValue) : new LinkedUndirectedGraphNode(nodeValue); nodes.put(nodeValue, node); } return node; } @Override public List> getNeighborNodes(N value) { List> nodeList = Lists.newArrayList(); for (Iterator> i = getNeighborNodesIterator(value); i.hasNext();) { nodeList.add(i.next()); } return nodeList; } @Override public Iterator> getNeighborNodesIterator(N value) { UndiGraphNode uNode = getUndirectedGraphNode(value); Preconditions.checkNotNull(uNode, "%s should be in the graph.", value); return ((LinkedUndirectedGraphNode) uNode).neighborIterator(); } @SuppressWarnings("unchecked") @Override public List> getUndirectedGraphEdges(N n1, N n2) { UndiGraphNode dNode1 = nodes.get(n1); if (dNode1 == null) { return null; } UndiGraphNode dNode2 = nodes.get(n2); if (dNode2 == null) { return null; } List> edges = Lists.newArrayList(); for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { edges.add(outEdge); } } return edges; } @Override public UndiGraphNode getUndirectedGraphNode(N nodeValue) { return nodes.get(nodeValue); } @Override public Collection> getUndirectedGraphNodes() { return Collections.>unmodifiableCollection( nodes.values()); } @Override public GraphNode createNode(N value) { return createUndirectedGraphNode(value); } @Override public List> getEdges(N n1, N n2) { return Collections.>unmodifiableList( getUndirectedGraphEdges(n1, n2)); } @Override public GraphEdge getFirstEdge(N n1, N n2) { UndiGraphNode dNode1 = getNodeOrFail(n1); UndiGraphNode dNode2 = getNodeOrFail(n2); for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { if (outEdge.getNodeA() == dNode2 || outEdge.getNodeB() == dNode2) { return outEdge; } } return null; } @Override public GraphNode getNode(N value) { return getUndirectedGraphNode(value); } @Override public boolean isConnected(N n1, N n2) { return isConnected(n1, Predicates.alwaysTrue(), n2); } @Override public boolean isConnected(N n1, E e, N n2) { return isConnected(n1, Predicates.equalTo(e), n2); } private boolean isConnected(N n1, Predicate edgePredicate, N n2) { UndiGraphNode dNode1 = nodes.get(n1); if (dNode1 == null) { return false; } UndiGraphNode dNode2 = nodes.get(n2); if (dNode2 == null) { return false; } for (UndiGraphEdge outEdge : dNode1.getNeighborEdges()) { if ((outEdge.getNodeA() == dNode1 && outEdge.getNodeB() == dNode2) || (outEdge.getNodeA() == dNode2 && outEdge.getNodeB() == dNode1)) { if (edgePredicate.apply(outEdge.getValue())) { return true; } } } return false; } @Override public List getGraphvizEdges() { List edgeList = Lists.newArrayList(); for (LinkedUndirectedGraphNode node : nodes.values()) { for (UndiGraphEdge edge : node.getNeighborEdges()) { if (edge.getNodeA() == node) { edgeList.add((GraphvizEdge) edge); } } } return edgeList; } @Override public String getName() { return "LinkedUndirectedGraph"; } @Override public List getGraphvizNodes() { List nodeList = Lists.newArrayListWithCapacity(nodes.size()); for (LinkedUndirectedGraphNode node : nodes.values()) { nodeList.add(node); } return nodeList; } @Override public boolean isDirected() { return false; } @Override public Collection> getNodes() { return Collections.> unmodifiableCollection(nodes.values()); } @SuppressWarnings("unchecked") @Override public List> getEdges() { List> result = Lists.newArrayList(); for (LinkedUndirectedGraphNode node : nodes.values()) { for (UndiGraphEdge edge : node.getNeighborEdges()) { if (edge.getNodeA() == node) { result.add(edge); } } } return result; } @Override public int getNodeDegree(N value) { UndiGraphNode uNode = getUndirectedGraphNode(value); if (uNode == null) { throw new IllegalArgumentException(value + " not found in graph"); } return uNode.getNeighborEdges().size(); } /** * An undirected graph node that stores outgoing edges and incoming edges as * an list within the node itself. */ static class LinkedUndirectedGraphNode implements UndiGraphNode, GraphvizNode { private List> neighborList = Lists.newArrayList(); private final N value; LinkedUndirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public List> getNeighborEdges() { return neighborList; } @Override public Iterator> getNeighborEdgesIterator() { return neighborList.iterator(); } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public N getValue() { return value; } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } public Iterator> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator> { private final Iterator> edgeIterator = neighborList.iterator(); @Override public boolean hasNext() { return edgeIterator.hasNext(); } @Override public GraphNode next() { UndiGraphEdge edge = edgeIterator.next(); if (edge.getNodeA() == LinkedUndirectedGraphNode.this) { return edge.getNodeB(); } else { return edge.getNodeA(); } } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * An undirected graph node with annotations. */ static class AnnotatedLinkedUndirectedGraphNode extends LinkedUndirectedGraphNode { protected Annotation annotation; AnnotatedLinkedUndirectedGraphNode(N nodeValue) { super(nodeValue); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /** * An undirected graph edge that stores two nodes at each edge. */ static class LinkedUndirectedGraphEdge implements UndiGraphEdge, GraphvizEdge { private UndiGraphNode nodeA; private UndiGraphNode nodeB; protected final E value; LinkedUndirectedGraphEdge(UndiGraphNode nodeA, E edgeValue, UndiGraphNode nodeB) { this.value = edgeValue; this.nodeA = nodeA; this.nodeB = nodeB; } @Override public E getValue() { return value; } @Override public GraphNode getNodeA() { return nodeA; } @Override public GraphNode getNodeB() { return nodeB; } @Override public A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with edge annotations turned off"); } @Override public String getColor() { return "black"; } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @SuppressWarnings("unchecked") @Override public String getNode1Id() { return ((LinkedUndirectedGraphNode) nodeA).getId(); } @SuppressWarnings("unchecked") @Override public String getNode2Id() { return ((LinkedUndirectedGraphNode) nodeB).getId(); } @Override public String toString() { return nodeA.toString() + " -- " + nodeB.toString(); } } /** * An annotated undirected graph edge.. */ static class AnnotatedLinkedUndirectedGraphEdge extends LinkedUndirectedGraphEdge { protected Annotation annotation; AnnotatedLinkedUndirectedGraphEdge( UndiGraphNode nodeA, E edgeValue, UndiGraphNode nodeB) { super(nodeA, edgeValue, nodeB); } @SuppressWarnings("unchecked") @Override public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/package.html0000644000175000017500000000032112115204405026212 0ustar apoapo Provides graph data structures and algorithms for coloring and fixed-point computations. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/GraphColoring.java0000644000175000017500000001263212115204405027342 0ustar apoapo/* * 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.graph; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.SubGraph; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; /** * Annotates the graph with a color in a way that no connected node will have * the same color. Nodes of the same color cab then be partitioned together and * be represented by a super node. This class will merely annotate the nodes * with a color using {@link GraphNode#setAnnotation(Annotation)} and provide * a node to super node mapping with {@link #getPartitionSuperNode(Object)}. The * give graph itself will not be modified. * *

      This algorithm is NOT deterministic by default. Passes that * requires deterministic output should provide a {@code Comparator} in the * constructor as a tie-breaker. This tie-break will be used when deciding * which node should be colored first when multiple nodes have the same degree. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. * */ public abstract class GraphColoring { // Maps a color (represented by an integer) to a variable. If, for example, // the color 5 is mapped to "foo". Then any other variables colored with the // color 5 will now use the name "foo". protected N[] colorToNodeMap; protected final AdjacencyGraph graph; public GraphColoring(AdjacencyGraph graph) { this.graph = graph; } /** * Annotates the graph with {@link Color} objects using * {@link GraphNode#setAnnotation(Annotation)}. * * @return The number of unique colors need. */ public abstract int color(); /** * Using the coloring as partitions, finds the node that represents that * partition as the super node. The first to retrieve its partition will * become the super node. */ public N getPartitionSuperNode(N node) { Preconditions.checkNotNull(colorToNodeMap, "No coloring founded. color() should be called first."); Color color = graph.getNode(node).getAnnotation(); N headNode = colorToNodeMap[color.value]; if (headNode == null) { colorToNodeMap[color.value] = node; return node; } else { return headNode; } } public AdjacencyGraph getGraph() { return graph; } public static class Color implements Annotation { int value = 0; Color(int value) { this.value = value; } @Override public boolean equals(Object other) { if (!(other instanceof Color)) { return false; } else { return value == ((Color) other).value; } } @Override public int hashCode() { return value; } } /** * Greedily assign nodes with high degree unique colors. */ public static class GreedyGraphColoring extends GraphColoring { private final Comparator tieBreaker; public GreedyGraphColoring(AdjacencyGraph graph) { this(graph, null); } /** * @param tieBreaker In case of a tie between two nodes of the same degree, * this comparator will determine which node should be colored first. */ public GreedyGraphColoring( AdjacencyGraph graph, Comparator tieBreaker) { super(graph); this.tieBreaker = tieBreaker; } @Override public int color() { graph.clearNodeAnnotations(); List> worklist = Lists.newArrayList(graph.getNodes()); // Sort nodes by degree. Collections.sort(worklist, new Comparator>() { @Override public int compare(GraphNode o1, GraphNode o2) { int result = graph.getWeight(o2.getValue()) - graph.getWeight(o1.getValue()); return result == 0 && tieBreaker != null ? tieBreaker.compare(o1.getValue(), o2.getValue()) : result; } }); // Idea: From the highest to lowest degree, assign any uncolored node with // a unique color if none of its neighbor has been assigned that color. int count = 0; do { Color color = new Color(count); SubGraph subgraph = graph.newSubGraph(); for (Iterator> i = worklist.iterator(); i.hasNext();) { GraphNode node = i.next(); if (subgraph.isIndependentOf(node.getValue())) { subgraph.addNode(node.getValue()); node.setAnnotation(color); i.remove(); } } count++; } while (!worklist.isEmpty()); @SuppressWarnings("unchecked") N[] map = (N[]) new Object[count]; colorToNodeMap = map; return count; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/graph/UndiGraph.java0000644000175000017500000000361312115204405026464 0ustar apoapo/* * 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.graph; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * A generic undirected graph. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public abstract class UndiGraph extends Graph { /** * Gets an immutable collection of all the nodes in this graph. */ abstract Collection> getUndirectedGraphNodes(); abstract UndiGraphNode createUndirectedGraphNode(N nodeValue); public abstract UndiGraphNode getUndirectedGraphNode(N nodeValue); abstract List> getUndirectedGraphEdges(N n1, N n2); /** * A generic undirected graph node. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public static interface UndiGraphNode extends GraphNode { public List> getNeighborEdges(); public Iterator> getNeighborEdgesIterator(); } /** * A generic undirected graph edge. * * @param Value type that the graph node stores. * @param Value type that the graph edge stores. */ public static interface UndiGraphEdge extends GraphEdge { } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PrintStreamErrorManager.java0000644000175000017500000000511012115204405030251 0ustar apoapo/* * Copyright 2007 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.CheckLevel; import java.io.PrintStream; /** *

      An error manager that prints errors and warnings to the print stream * provided in addition to the functionality of the * {@link BasicErrorManager}.

      * *

      It collaborates with a {@link SourceExcerptProvider} via a * {@link MessageFormatter} to display error messages with source context.

      * */ public class PrintStreamErrorManager extends BasicErrorManager { private final MessageFormatter formatter; private final PrintStream stream; private int summaryDetailLevel = 1; /** * Creates an error manager. * @param formatter the message formatter used to format the messages * @param stream the stream on which the errors and warnings should be * printed. This class does not close the stream */ public PrintStreamErrorManager(MessageFormatter formatter, PrintStream stream) { this.formatter = formatter; this.stream = stream; } /** * Creates an instance with a source-less error formatter. */ public PrintStreamErrorManager(PrintStream stream) { this(ErrorFormat.SOURCELESS.toFormatter(null, false), stream); } @Override public void println(CheckLevel level, JSError error) { stream.println(error.format(level, formatter)); } public void setSummaryDetailLevel(int summaryDetailLevel) { this.summaryDetailLevel = summaryDetailLevel; } @Override public void printSummary() { if (summaryDetailLevel >= 3 || (summaryDetailLevel >= 1 && getErrorCount() + getWarningCount() > 0) || (summaryDetailLevel >= 2 && getTypedPercent() > 0.0)) { if (getTypedPercent() > 0.0) { stream.format("%d error(s), %d warning(s), %.1f%% typed%n", getErrorCount(), getWarningCount(), getTypedPercent()); } else { stream.format("%d error(s), %d warning(s)%n", getErrorCount(), getWarningCount()); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VariableRenamingPolicy.java0000644000175000017500000000202012115204405030057 0ustar apoapo/* * 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; /** * Policies to determine which variables should be renamed. */ public enum VariableRenamingPolicy { /** * Rename no variables. */ OFF, /** * Rename local variables only. */ LOCAL, /** * Rename all variables and functions unless they are exported or externed. */ ALL, // for transitioning off old flags. not for public consumption. UNSPECIFIED } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReplaceIdGenerators.java0000644000175000017500000003026412115204405027366 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.Base64; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.Collections; import java.util.Map; import java.util.Set; /** * Replaces calls to id generators with ids. * * Use this to get unique and short ids. * */ class ReplaceIdGenerators implements CompilerPass { static final DiagnosticType NON_GLOBAL_ID_GENERATOR_CALL = DiagnosticType.error( "JSC_NON_GLOBAL_ID_GENERATOR_CALL", "Id generator call must be in the global scope"); static final DiagnosticType CONDITIONAL_ID_GENERATOR_CALL = DiagnosticType.error( "JSC_CONDITIONAL_ID_GENERATOR_CALL", "Id generator call must be unconditional"); static final DiagnosticType CONFLICTING_GENERATOR_TYPE = DiagnosticType.error( "JSC_CONFLICTING_ID_GENERATOR_TYPE", "Id generator can only be one of consistent, inconsistent, or stable."); static final DiagnosticType INVALID_GENERATOR_ID_MAPPING = DiagnosticType.error( "JSC_INVALID_GENERATOR_ID_MAPPING", "Invalid generator id mapping. {0}"); private final AbstractCompiler compiler; private final Map nameGenerators; private final Map> consistNameMap; private final Map> idGeneratorMaps; private final Map> previousMap; private final boolean generatePseudoNames; public ReplaceIdGenerators( AbstractCompiler compiler, Set idGens, boolean generatePseudoNames, String previousMapSerialized) { this.compiler = compiler; this.generatePseudoNames = generatePseudoNames; nameGenerators = Maps.newLinkedHashMap(); idGeneratorMaps = Maps.newLinkedHashMap(); consistNameMap = Maps.newLinkedHashMap(); Map> previousMap; previousMap = parsePreviousResults(previousMapSerialized); this.previousMap = previousMap; if (idGens != null) { for (String gen : idGens) { nameGenerators.put( gen, createNameSupplier(RenameStrategy.INCONSISTENT, previousMap.get(gen))); idGeneratorMaps.put(gen, Maps.newLinkedHashMap()); } } } private enum RenameStrategy { CONSISTENT, INCONSISTENT, STABLE } private static interface NameSupplier { String getName(String id, String name); RenameStrategy getRenameStrategy(); } private static class ObfuscatedNameSuppier implements NameSupplier { private final NameGenerator generator; private final Map previousMappings; private RenameStrategy renameStrategy; public ObfuscatedNameSuppier( RenameStrategy renameStrategy, BiMap previousMappings) { this.previousMappings = previousMappings.inverse(); this.generator = new NameGenerator(previousMappings.keySet(), "", null); this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { String newName = previousMappings.get(id); if (newName == null) { newName = generator.generateNextName(); } return newName; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class PseudoNameSuppier implements NameSupplier { private int counter = 0; private RenameStrategy renameStrategy; public PseudoNameSuppier(RenameStrategy renameStrategy) { this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { if (renameStrategy == RenameStrategy.INCONSISTENT) { return name + "$" + counter++; } return name + "$0"; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class StableNameSupplier implements NameSupplier { @Override public String getName(String id, String name) { return Base64.base64EncodeInt(name.hashCode()); } @Override public RenameStrategy getRenameStrategy() { return RenameStrategy.STABLE; } } private NameSupplier createNameSupplier( RenameStrategy renameStrategy, BiMap previousMappings) { previousMappings = previousMappings != null ? previousMappings : ImmutableBiMap.of(); if (renameStrategy == RenameStrategy.STABLE) { return new StableNameSupplier(); } else if (generatePseudoNames) { return new PseudoNameSuppier(renameStrategy); } else { return new ObfuscatedNameSuppier(renameStrategy, previousMappings); } } private class GatherGenerators extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { JSDocInfo doc = n.getJSDocInfo(); if (doc == null) { return; } int numGeneratorAnnotations = (doc.isConsistentIdGenerator() ? 1 : 0) + (doc.isIdGenerator() ? 1 : 0) + (doc.isStableIdGenerator() ? 1 : 0); if (numGeneratorAnnotations == 0) { return; } else if (numGeneratorAnnotations > 1) { compiler.report(t.makeError(n, CONFLICTING_GENERATOR_TYPE)); } String name = null; if (n.isAssign()) { name = n.getFirstChild().getQualifiedName(); } else if (n.isVar()) { name = n.getFirstChild().getString(); } else if (n.isFunction()){ name = n.getFirstChild().getString(); if (name.isEmpty()) { return; } } if (doc.isConsistentIdGenerator()) { consistNameMap.put(name, Maps.newLinkedHashMap()); nameGenerators.put( name, createNameSupplier(RenameStrategy.CONSISTENT, previousMap.get(name))); } else if (doc.isStableIdGenerator()) { nameGenerators.put( name, createNameSupplier(RenameStrategy.STABLE, previousMap.get(name))); } else { nameGenerators.put( name, createNameSupplier(RenameStrategy.INCONSISTENT, previousMap.get(name))); } idGeneratorMaps.put(name, Maps.newLinkedHashMap()); } } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new GatherGenerators()); if (!nameGenerators.isEmpty()) { NodeTraversal.traverse(compiler, root, new ReplaceGenerators()); } } private class ReplaceGenerators extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } String callName = n.getFirstChild().getQualifiedName(); NameSupplier nameGenerator = nameGenerators.get(callName); if (nameGenerator == null) { return; } if (!t.inGlobalScope() && nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { // Warn about calls not in the global scope. compiler.report(t.makeError(n, NON_GLOBAL_ID_GENERATOR_CALL)); return; } if (nameGenerator.getRenameStrategy() == RenameStrategy.INCONSISTENT) { for (Node ancestor : n.getAncestors()) { if (NodeUtil.isControlStructure(ancestor)) { // Warn about conditional calls. compiler.report(t.makeError(n, CONDITIONAL_ID_GENERATOR_CALL)); return; } } } Node id = n.getFirstChild().getNext(); // TODO(user): Error on id not a string literal. if (!id.isString()) { return; } Map idGeneratorMap = idGeneratorMaps.get(callName); String rename = null; String name = id.getString(); String instanceId = getIdForGeneratorNode( nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT, id); if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) { Map entry = consistNameMap.get(callName); rename = entry.get(instanceId); if (rename == null) { rename = nameGenerator.getName(instanceId, name); entry.put(instanceId, rename); } } else { rename = nameGenerator.getName(instanceId, name); } parent.replaceChild(n, IR.string(rename)); idGeneratorMap.put(rename, instanceId); compiler.reportCodeChange(); } } /** * @return The serialize map of generators and their ids and their * replacements. */ public String getSerializedIdMappings() { StringBuilder sb = new StringBuilder(); for (Map.Entry> replacements : idGeneratorMaps.entrySet()) { if (!replacements.getValue().isEmpty()) { sb.append("["); sb.append(replacements.getKey()); sb.append("]\n\n"); for (Map.Entry replacement : replacements.getValue().entrySet()) { sb.append(replacement.getKey()); sb.append(':'); sb.append(replacement.getValue()); sb.append("\n"); } sb.append("\n"); } } return sb.toString(); } private Map> parsePreviousResults( String serializedMap) { // // The expected format looks like this: // // [generatorName] // someId:someFile:theLine:theColumn // // if (serializedMap == null || serializedMap.isEmpty()) { return Collections.emptyMap(); } Map> resultMap = Maps.newHashMap(); BufferedReader reader = new BufferedReader(new StringReader(serializedMap)); BiMap currentSectionMap = null; String line; int lineIndex = 0; try { while ((line = reader.readLine()) != null) { lineIndex++; if (line.isEmpty()) { continue; } if (line.charAt(0) == '[') { String currentSection = line.substring(1, line.length() - 1); currentSectionMap = resultMap.get(currentSection); if (currentSectionMap == null) { currentSectionMap = HashBiMap.create(); resultMap.put(currentSection, currentSectionMap); } else { reportInvalidLine(line, lineIndex); return Collections.emptyMap(); } } else { int split = line.indexOf(':'); if (split != -1) { String name = line.substring(0, split); String location = line.substring(split + 1, line.length()); currentSectionMap.put(name, location); } else { reportInvalidLine(line, lineIndex); return Collections.emptyMap(); } } } } catch (IOException e) { JSError.make(INVALID_GENERATOR_ID_MAPPING, e.getMessage()); } return resultMap; } private void reportInvalidLine(String line, int lineIndex) { JSError.make(INVALID_GENERATOR_ID_MAPPING, "line(" + line + "): " + lineIndex); } String getIdForGeneratorNode(boolean consistent, Node n) { Preconditions.checkState(n.isString()); if (consistent) { return n.getString(); } else { return n.getSourceFileName() + ':' + n.getLineno() + ":" + n.getCharno(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReorderConstantExpression.java0000644000175000017500000000376312115204405030704 0ustar apoapo/* * Copyright 2011 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.rhino.Node; /** * Reorder constant expression hoping for a better compression. * ex. x === 0 -> 0 === x * After reordering, expressions like 0 === x and 0 === y may have higher * compression together than their original counterparts. * */ class ReorderConstantExpression extends AbstractPeepholeOptimization { // TODO(user): Rename this pass to PeepholeReorderConstantExpression // to follow our naming convention. @Override Node optimizeSubtree(Node subtree) { // if the operator is symmetric if (NodeUtil.isSymmetricOperation(subtree) || NodeUtil.isRelationalOperation(subtree)) { // right value is immutable and left is not if (NodeUtil.isImmutableValue(subtree.getLastChild()) && !NodeUtil.isImmutableValue(subtree.getFirstChild())) { // if relational, get the inverse operator. if (NodeUtil.isRelationalOperation(subtree)){ int inverseOperator = NodeUtil.getInverseOperator(subtree.getType()); subtree.setType(inverseOperator); } // swap them Node firstNode = subtree.getFirstChild().detachFromParent(); Node lastNode = subtree.getLastChild().detachFromParent(); subtree.addChildrenToFront(lastNode); subtree.addChildrenToBack(firstNode); reportCodeChange(); } } return subtree; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/WarningLevel.java0000644000175000017500000001162312115204405026077 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.CompilerOptions; /** * Convert the warnings level to an Options object. * */ public enum WarningLevel { QUIET, DEFAULT, VERBOSE; public void setOptionsForWarningLevel(CompilerOptions options) { switch (this) { case QUIET: silenceAllWarnings(options); break; case DEFAULT: addDefaultWarnings(options); break; case VERBOSE: addVerboseWarnings(options); break; default: throw new RuntimeException("Unknown warning level."); } } /** * Silence all non-essential warnings. */ private static void silenceAllWarnings(CompilerOptions options) { // Just use a ShowByPath warnings guard, so that we don't have // to maintain a separate class of warnings guards for silencing warnings. options.addWarningsGuard( new ShowByPathWarningsGuard( "the_longest_path_that_cannot_be_expressed_as_a_string")); // Allow passes that aren't going to report anything to be skipped. options.checkRequires = CheckLevel.OFF; options.checkProvides = CheckLevel.OFF; options.checkMissingGetCssNameLevel = CheckLevel.OFF; options.aggressiveVarCheck = CheckLevel.OFF; options.checkTypes = false; options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.OFF); options.checkUnreachableCode = CheckLevel.OFF; options.checkMissingReturn = CheckLevel.OFF; options.setWarningLevel(DiagnosticGroups.ACCESS_CONTROLS, CheckLevel.OFF); options.setWarningLevel(DiagnosticGroups.CONST, CheckLevel.OFF); options.setWarningLevel(DiagnosticGroups.CONSTANT_PROPERTY, CheckLevel.OFF); options.checkGlobalNamesLevel = CheckLevel.OFF; options.checkSuspiciousCode = false; options.checkGlobalThisLevel = CheckLevel.OFF; options.setWarningLevel(DiagnosticGroups.GLOBAL_THIS, CheckLevel.OFF); options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF); options.checkCaja = false; // Allows annotations that are not standard. options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, CheckLevel.OFF); } /** * Add the default checking pass to the compilation options. * @param options The CompilerOptions object to set the options on. */ private static void addDefaultWarnings(CompilerOptions options) { options.checkSuspiciousCode = true; options.checkUnreachableCode = CheckLevel.WARNING; options.checkControlStructures = true; // Allows annotations that are not standard. options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, CheckLevel.OFF); } /** * Add all the check pass that are possibly relevant to a non-googler. * @param options The CompilerOptions object to set the options on. */ private static void addVerboseWarnings(CompilerOptions options) { addDefaultWarnings(options); // checkSuspiciousCode needs to be enabled for CheckGlobalThis to get run. options.checkSuspiciousCode = true; options.checkGlobalThisLevel = CheckLevel.WARNING; options.checkSymbols = true; options.checkMissingReturn = CheckLevel.WARNING; // checkTypes has the side-effect of asserting that the // correct number of arguments are passed to a function. // Because the CodingConvention used with the web service does not provide a // way for optional arguments to be specified, these warnings may result in // false positives. options.checkTypes = true; options.checkGlobalNamesLevel = CheckLevel.WARNING; options.aggressiveVarCheck = CheckLevel.WARNING; options.setWarningLevel( DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.DEPRECATED, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.VISIBILITY, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.CONST, CheckLevel.WARNING); options.setWarningLevel( DiagnosticGroups.CHECK_REGEXP, CheckLevel.WARNING); // Kindly tell the user that they have JsDocs that we don't understand. options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, CheckLevel.WARNING); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/function_info.proto0000644000175000017500000000112712115204405026562 0ustar apoapo// Copyright 2008 Google Inc. // Author: Mark Goodman syntax = "proto2"; package jscomp; option java_package = "com.google.javascript.jscomp"; option java_multiple_files = true; message FunctionInformationMap { repeated group Entry = 1 { required int32 id = 2; required string source_name = 3; required int32 line_number = 4; required string module_name = 5; required int32 size = 6; required string name = 7; required string compiled_source = 8; } repeated group Module = 101 { required string name = 102; required string compiled_source = 103; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ant/0000755000175000017500000000000012115204405023416 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ant/CompileTask.java0000644000175000017500000004515212115204405026503 0ustar apoapo/* * 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.ant; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.CommandLineRunner; import com.google.javascript.jscomp.CompilationLevel; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.DiagnosticGroup; import com.google.javascript.jscomp.DiagnosticGroups; import com.google.javascript.jscomp.MessageFormatter; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.jscomp.SourceMap; import com.google.javascript.jscomp.SourceMap.Format; import com.google.javascript.jscomp.WarningLevel; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileList; import org.apache.tools.ant.types.Parameter; import org.apache.tools.ant.types.Path; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.logging.Level; /** * This class implements a simple Ant task to do almost the same as * CommandLineRunner. * * Most of the public methods of this class are entry points for the * Ant code to hook into. * */ public final class CompileTask extends Task { private CompilerOptions.LanguageMode languageIn; private WarningLevel warningLevel; private boolean debugOptions; private String encoding = "UTF-8"; private String outputEncoding = "UTF-8"; private CompilationLevel compilationLevel; private boolean customExternsOnly; private boolean manageDependencies; private boolean prettyPrint; private boolean printInputDelimiter; private boolean generateExports; private boolean replaceProperties; private boolean forceRecompile; private String replacePropertiesPrefix; private File outputFile; private final List defineParams; private final List externFileLists; private final List sourceFileLists; private final List sourcePaths; private final List warnings; private String sourceMapFormat; private File sourceMapOutputFile; public CompileTask() { this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT3; this.warningLevel = WarningLevel.DEFAULT; this.debugOptions = false; this.compilationLevel = CompilationLevel.SIMPLE_OPTIMIZATIONS; this.customExternsOnly = false; this.manageDependencies = false; this.prettyPrint = false; this.printInputDelimiter = false; this.generateExports = false; this.replaceProperties = false; this.forceRecompile = false; this.replacePropertiesPrefix = "closure.define."; this.defineParams = Lists.newLinkedList(); this.externFileLists = Lists.newLinkedList(); this.sourceFileLists = Lists.newLinkedList(); this.sourcePaths = Lists.newLinkedList(); this.warnings = Lists.newLinkedList(); } /** * Set the language to which input sources conform. * @param value The name of the language. * (ECMASCRIPT3, ECMASCRIPT5, ECMASCRIPT5_STRICT). */ public void setLanguageIn(String value) { if (value.equals("ECMASCRIPT5_STRICT") || value.equals("ES5_STRICT")) { this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT5_STRICT; } else if (value.equals("ECMASCRIPT5") || value.equals("ES5")) { this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT5; } else if (value.equals("ECMASCRIPT3") || value.equals("ES3")) { this.languageIn = CompilerOptions.LanguageMode.ECMASCRIPT3; } else { throw new BuildException( "Unrecognized 'languageIn' option value (" + value + ")"); } } /** * Set the warning level. * @param value The warning level by string name. (default, quiet, verbose). */ public void setWarning(String value) { if ("default".equalsIgnoreCase(value)) { this.warningLevel = WarningLevel.DEFAULT; } else if ("quiet".equalsIgnoreCase(value)) { this.warningLevel = WarningLevel.QUIET; } else if ("verbose".equalsIgnoreCase(value)) { this.warningLevel = WarningLevel.VERBOSE; } else { throw new BuildException( "Unrecognized 'warning' option value (" + value + ")"); } } /** * Enable debugging options. * @param value True if debug mode is enabled. */ public void setDebug(boolean value) { this.debugOptions = value; } /** * Set the compilation level. * @param value The optimization level by string name. * (whitespace, simple, advanced). */ public void setCompilationLevel(String value) { if ("simple".equalsIgnoreCase(value)) { this.compilationLevel = CompilationLevel.SIMPLE_OPTIMIZATIONS; } else if ("advanced".equalsIgnoreCase(value)) { this.compilationLevel = CompilationLevel.ADVANCED_OPTIMIZATIONS; } else if ("whitespace".equalsIgnoreCase(value)) { this.compilationLevel = CompilationLevel.WHITESPACE_ONLY; } else { throw new BuildException( "Unrecognized 'compilation' option value (" + value + ")"); } } public void setManageDependencies(boolean value) { this.manageDependencies = value; } /** * Use only custom externs. */ public void setCustomExternsOnly(boolean value) { this.customExternsOnly = value; } /** * Set output file. */ public void setOutput(File value) { this.outputFile = value; } /** * Set the replacement property prefix. */ public void setReplacePropertiesPrefix(String value) { this.replacePropertiesPrefix = value; } /** * Whether to replace {@code @define} lines with properties */ public void setReplaceProperties(boolean value) { this.replaceProperties = value; } /** * Set input file encoding */ public void setEncoding(String encoding) { this.encoding = encoding; } /** * Set output file encoding */ public void setOutputEncoding(String outputEncoding) { this.outputEncoding = outputEncoding; } /** * Set pretty print formatting option */ public void setPrettyPrint(boolean pretty) { this.prettyPrint = pretty; } /** * Set print input delimiter formatting option */ public void setPrintInputDelimiter(boolean print) { this.printInputDelimiter = print; } /** * Set force recompile option */ public void setForceRecompile(boolean forceRecompile) { this.forceRecompile = forceRecompile; } /** * Set generateExports option */ public void setGenerateExports(boolean generateExports) { this.generateExports = generateExports; } /** * Sets the externs file. */ public void addExterns(FileList list) { this.externFileLists.add(list); } /** * Adds a entry * * Each warning entry must have two attributes, group and level. Group must * contain one of the constants from DiagnosticGroups (e.g., * "ACCESS_CONTROLS"), while level must contain one of the CheckLevel * constants ("ERROR", "WARNING" or "OFF"). */ public void addWarning(Warning warning) { this.warnings.add(warning); } /** * Sets the source files. */ public void addSources(FileList list) { this.sourceFileLists.add(list); } /** * Adds a entry. */ public void addPath(Path list) { this.sourcePaths.add(list); } @Override public void execute() { if (this.outputFile == null) { throw new BuildException("outputFile attribute must be set"); } Compiler.setLoggingLevel(Level.OFF); CompilerOptions options = createCompilerOptions(); Compiler compiler = createCompiler(options); List externs = findExternFiles(); List sources = findSourceFiles(); if (isStale() || forceRecompile) { log("Compiling " + sources.size() + " file(s) with " + externs.size() + " extern(s)"); Result result = compiler.compile(externs, sources, options); if (result.success) { StringBuilder source = new StringBuilder(compiler.toSource()); if (result.sourceMap != null) { flushSourceMap(result.sourceMap); source.append(System.getProperty("line.separator")); source.append("//@ sourceMappingURL=" + sourceMapOutputFile.getName()); } writeResult(source.toString()); } else { throw new BuildException("Compilation failed."); } } else { log("None of the files changed. Compilation skipped."); } } private void flushSourceMap(SourceMap sourceMap) { try { FileWriter out = new FileWriter(sourceMapOutputFile); sourceMap.appendTo(out, sourceMapOutputFile.getName()); out.close(); } catch (IOException e) { throw new BuildException("Cannot write sourcemap to file.", e); } } private CompilerOptions createCompilerOptions() { CompilerOptions options = new CompilerOptions(); this.compilationLevel.setOptionsForCompilationLevel(options); if (this.debugOptions) { this.compilationLevel.setDebugOptionsForCompilationLevel(options); } options.prettyPrint = this.prettyPrint; options.printInputDelimiter = this.printInputDelimiter; options.generateExports = this.generateExports; options.setLanguageIn(this.languageIn); this.warningLevel.setOptionsForWarningLevel(options); options.setManageClosureDependencies(manageDependencies); if (replaceProperties) { convertPropertiesMap(options); } convertDefineParameters(options); for (Warning warning : warnings) { CheckLevel level = warning.getLevel(); String groupName = warning.getGroup(); DiagnosticGroup group = new DiagnosticGroups().forName(groupName); if (group == null) { throw new BuildException( "Unrecognized 'warning' option value (" + groupName + ")"); } options.setWarningLevel(group, level); } if (!Strings.isNullOrEmpty(sourceMapFormat)) { options.sourceMapFormat = Format.valueOf(sourceMapFormat); } if (sourceMapOutputFile != null) { File parentFile = sourceMapOutputFile.getParentFile(); if (parentFile.mkdirs()) { log("Created missing parent directory " + parentFile, Project.MSG_DEBUG); } options.sourceMapOutputPath = parentFile.getAbsolutePath(); } return options; } /** * Creates a new {@code } nested element. Supports name and value * attributes. */ public Parameter createDefine() { Parameter param = new Parameter(); defineParams.add(param); return param; } /** * Converts {@code } nested elements into Compiler {@code @define} * replacements. Note: unlike project properties, {@code } elements * do not need to be named starting with the replacement prefix. */ private void convertDefineParameters(CompilerOptions options) { for (Parameter p : defineParams) { String key = p.getName(); Object value = p.getValue(); if (!setDefine(options, key, value)) { log("Unexpected @define value for name=" + key + "; value=" + value); } } } /** * Converts project properties beginning with the replacement prefix * into Compiler {@code @define} replacements. * * @param options */ private void convertPropertiesMap(CompilerOptions options) { @SuppressWarnings("unchecked") Map props = getProject().getProperties(); for (Map.Entry entry : props.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (key.startsWith(replacePropertiesPrefix)) { key = key.substring(replacePropertiesPrefix.length()); if (!setDefine(options, key, value)) { log("Unexpected property value for key=" + key + "; value=" + value); } } } } /** * Maps Ant-style values (e.g., from Properties) into expected * Closure {@code @define} literals * * @return True if the {@code @define} replacement succeeded, false if * the variable's value could not be mapped properly. */ private boolean setDefine(CompilerOptions options, String key, Object value) { boolean success = false; if (value instanceof String) { final boolean isTrue = "true".equals(value); final boolean isFalse = "false".equals(value); if (isTrue || isFalse) { options.setDefineToBooleanLiteral(key, isTrue); } else { try { double dblTemp = Double.parseDouble((String) value); options.setDefineToDoubleLiteral(key, dblTemp); } catch (NumberFormatException nfe) { // Not a number, assume string options.setDefineToStringLiteral(key, (String) value); } } success = true; } else if (value instanceof Boolean) { options.setDefineToBooleanLiteral(key, (Boolean) value); success = true; } else if (value instanceof Integer) { options.setDefineToNumberLiteral(key, (Integer) value); success = true; } else if (value instanceof Double) { options.setDefineToDoubleLiteral(key, (Double) value); success = true; } return success; } private Compiler createCompiler(CompilerOptions options) { Compiler compiler = new Compiler(); MessageFormatter formatter = options.errorFormat.toFormatter(compiler, false); AntErrorManager errorManager = new AntErrorManager(formatter, this); compiler.setErrorManager(errorManager); return compiler; } private List findExternFiles() { List files = Lists.newLinkedList(); if (!this.customExternsOnly) { files.addAll(getDefaultExterns()); } for (FileList list : this.externFileLists) { files.addAll(findJavaScriptFiles(list)); } return files; } private List findSourceFiles() { List files = Lists.newLinkedList(); for (FileList list : this.sourceFileLists) { files.addAll(findJavaScriptFiles(list)); } for (Path list : this.sourcePaths) { files.addAll(findJavaScriptFiles(list)); } return files; } /** * Translates an Ant file list into the file format that the compiler * expects. */ private List findJavaScriptFiles(FileList fileList) { List files = Lists.newLinkedList(); File baseDir = fileList.getDir(getProject()); for (String included : fileList.getFiles(getProject())) { files.add(SourceFile.fromFile(new File(baseDir, included), Charset.forName(encoding))); } return files; } /** * Translates an Ant Path into the file list format that the compiler * expects. */ private List findJavaScriptFiles(Path path) { List files = Lists.newArrayList(); for (String included : path.list()) { files.add(SourceFile.fromFile(new File(included), Charset.forName(encoding))); } return files; } /** * Gets the default externs set. * * Adapted from {@link CommandLineRunner}. */ private List getDefaultExterns() { try { return CommandLineRunner.getDefaultExterns(); } catch (IOException e) { throw new BuildException(e); } } private void writeResult(String source) { if (this.outputFile.getParentFile().mkdirs()) { log("Created missing parent directory " + this.outputFile.getParentFile(), Project.MSG_DEBUG); } try { OutputStreamWriter out = new OutputStreamWriter( new FileOutputStream(this.outputFile), outputEncoding); out.append(source); out.flush(); out.close(); } catch (IOException e) { throw new BuildException(e); } log("Compiled JavaScript written to " + this.outputFile.getAbsolutePath(), Project.MSG_DEBUG); } /** * Determine if compilation must actually happen, i.e. if any input file * (extern or source) has changed after the outputFile was last modified. * * @return true if compilation should happen */ private boolean isStale() { long lastRun = outputFile.lastModified(); long sourcesLastModified = Math.max( getLastModifiedTime(this.sourceFileLists), getLastModifiedTime(this.sourcePaths)); long externsLastModified = getLastModifiedTime(this.externFileLists); return lastRun <= sourcesLastModified || lastRun <= externsLastModified; } /** * Returns the most recent modified timestamp of the file collection. * * Note: this must be combined into one method to account for both * Path and FileList erasure types. * * @param fileLists Collection of FileList or Path * @return Most recent modified timestamp */ private long getLastModifiedTime(List fileLists) { long lastModified = 0; for (Object entry : fileLists) { if (entry instanceof FileList) { FileList list = (FileList) entry; for (String fileName : list.getFiles(this.getProject())) { File path = list.getDir(this.getProject()); File file = new File(path, fileName); lastModified = Math.max(getLastModifiedTime(file), lastModified); } } else if (entry instanceof Path) { Path path = (Path) entry; for (String src : path.list()) { File file = new File(src); lastModified = Math.max(getLastModifiedTime(file), lastModified); } } } return lastModified; } /** * Returns the last modified timestamp of the given File. */ private long getLastModifiedTime(File file) { long fileLastModified = file.lastModified(); // If the file is absent, we don't know if it changed (maybe was deleted), // so assume it has just changed. if (fileLastModified == 0) { fileLastModified = new Date().getTime(); } return fileLastModified; } public void setSourceMapFormat(String format) { this.sourceMapFormat = format; } public void setSourceMapOutputFile(File sourceMapOutputFile) { this.sourceMapOutputFile = sourceMapOutputFile; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ant/AntErrorManager.java0000644000175000017500000000377412115204405027323 0ustar apoapo/* * 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.ant; import com.google.javascript.jscomp.BasicErrorManager; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.JSError; import com.google.javascript.jscomp.MessageFormatter; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; /** * An error manager that pipes warnings and errors properly into the Ant * task infrastructure. */ public final class AntErrorManager extends BasicErrorManager { private final MessageFormatter formatter; private final Task task; public AntErrorManager(MessageFormatter formatter, Task task) { this.formatter = formatter; this.task = task; } @Override public void println(CheckLevel level, JSError error) { switch (level) { case ERROR: this.task.log(error.format(level, this.formatter), Project.MSG_ERR); break; case WARNING: this.task.log(error.format(level, this.formatter), Project.MSG_WARN); break; case OFF: break; } } @Override protected void printSummary() { String message = getErrorCount() + " error(s), " + getWarningCount() + " warning(s)"; if (getTypedPercent() > 0.0) { message += ", " + getTypedPercent() + " typed"; } int level = (getErrorCount() + getWarningCount() == 0) ? Project.MSG_INFO : Project.MSG_WARN; this.task.log(message, level); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ant/Warning.java0000644000175000017500000000207512115204405025672 0ustar apoapo/* * Copyright 2012 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.ant; import com.google.javascript.jscomp.CheckLevel; /** Simple representation of a warning flag in Ant */ public class Warning { private String group; private CheckLevel level; public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public CheckLevel getLevel() { return level; } public void setLevel(CheckLevel level) { this.level = level; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveTryCatch.java0000644000175000017500000000710712115204405026403 0ustar apoapo/* * Copyright 2006 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.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.HashSet; import java.util.Set; /** * Removes try catch finally blocks from a parse tree for easier debugging * (these statements impact both debugging in IE and sometimes even in FF). * */ class RemoveTryCatch implements CompilerPass { private final AbstractCompiler compiler; private final Set tryNodesContainingReturnStatements; RemoveTryCatch(AbstractCompiler compiler) { this.compiler = compiler; this.tryNodesContainingReturnStatements = new HashSet(); } /** * Do all processing on the root node. */ @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new RemoveTryCatchCode()); } private class RemoveTryCatchCode extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.TRY: // Ignore the try statement if it has the @preserveTry annotation // (for expected exceptions). JSDocInfo info = n.getJSDocInfo(); if (info != null && info.shouldPreserveTry()) { return; } Node tryBlock = n.getFirstChild(); Node catchBlock = tryBlock.getNext(); // may be null or empty Node finallyBlock = catchBlock != null ? catchBlock.getNext() : null; // Ignore the try statement if it has a finally part and the try // block contains an early return. if (finallyBlock != null && tryNodesContainingReturnStatements.contains(n)) { return; } // Redeclare vars declared in the catch node to be removed. if (catchBlock.hasOneChild()) { NodeUtil.redeclareVarsInsideBranch(catchBlock); } // Disconnect the try/catch/finally nodes from the parent // and each other. n.detachChildren(); // try node Node block; if (!NodeUtil.isStatementBlock(parent)) { block = IR.block(); parent.replaceChild(n, block); block.addChildToFront(tryBlock); } else { parent.replaceChild(n, tryBlock); block = parent; } // finally node if (finallyBlock != null) { block.addChildAfter(finallyBlock, tryBlock); } compiler.reportCodeChange(); break; case Token.RETURN: for (Node anc = parent; anc != null && !anc.isFunction(); anc = anc.getParent()) { if (anc.isTry()) { tryNodesContainingReturnStatements.add(anc); break; } } break; } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TightenTypes.java0000644000175000017500000014375512115204405026145 0ustar apoapo/* * 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 static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ConcreteType.ConcreteFunctionType; import com.google.javascript.jscomp.ConcreteType.ConcreteInstanceType; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Computes the set of possible concrete types for every variable, property, * function argument, and function return value in the program. Unlike a normal * reference type annotation, a concrete type of A indicates that an instance of * A -- not a subclass of A -- is a possible value. * * Also unlike normal type checking, this pass does not assume that all defined * functions are actually called. Instead, it assumes only that the top-level * code is executed plus any implicit calls detected, such as calls to functions * exported via goog.exportSymbol or Element.addEventListener. Hence, this pass * also performs a very strict form of dead code detection. Elimination of dead * code will occur because the disambiguation pass can rename all uncalled * functions to have distinct names, which will then appear to be uncalled to * the normal unused property remover. * * Since concrete types are all reference types, we only care about the limited * set of actions that apply to them: assignments to variables/properties, * method calls, and return statements. To speed up and simplify the * implementation, the first time a scope is processed, we make one pass through * it {@link CreateScope} to translate it into a list of Actions. Each Action * can translate itself into a list of assignments: method calls are just * assignments to the parameter variables, while return statements are * assignments to a special $return slot. Each time a scope is (re-)processed, * we iterate over the assignments produced by the actions and update the types * of the target slots. Once we complete a pass through all scopes with no * changes, we are done. * */ class TightenTypes implements CompilerPass, ConcreteType.Factory { public static final String NON_HALTING_ERROR_MSG = "TightenTypes pass appears to be stuck in an infinite loop."; /** The compiler that invoked this pass. */ private final AbstractCompiler compiler; /** * Map of function type information to their concrete wrappers. These must be * reused so that each declaration has only a single concrete type, which will * hold all the known types that flow to its arguments and return value. */ private final Map functionFromDeclaration = Maps.newHashMap(); /** * Secondary index of concrete functions by JSType. This is necessary for * retrieving the concrete type of a superclass, where the actual declaration * is not at hand. Note that we must use an identity hash map here because * functions are compared using the signature only. */ private final Map functionFromJSType = Maps.newIdentityHashMap(); /** * Map of instance type information to their concrete wrappers. These must be * reused so that each property has only one variable, which will store all * known types that flow to that variable. */ private final Map instanceFromJSType = Maps.newHashMap(); /** * Memoized results of "createTypeIntersection" calls. */ private final Map typeIntersectionMemos = Maps.newHashMap(); /** Scope storing the top-level variables and functions. */ private ConcreteScope topScope; TightenTypes(AbstractCompiler compiler) { this.compiler = compiler; } /** Returns the top scope computed during the pass. */ ConcreteScope getTopScope() { return topScope; } /** Convenience method to get the type registry of the compiler. */ @Override public JSTypeRegistry getTypeRegistry() { return compiler.getTypeRegistry(); } /** All concrete instance types encountered during flow analysis. */ private Set allInstantiatedTypes = Sets.newHashSet(); @Override public void process(Node externRoot, Node jsRoot) { // Create the scope of top-level variables and functions. topScope = new ConcreteScope(null); topScope.initForExternRoot(externRoot); topScope.initForScopeRoot(jsRoot); // Process the assignments in each scope in the working set until no more // changes are detected. Each time a new scope is discovered (starting with // the top-level scope), it is added to the working set to be processed. // Since changes in almost any scope can affect another, we iterate over all // discovered scopes until no further changes occur. long maxIterations = 1000; long iterations = 0; Set workSet = Sets.newHashSet(topScope); List workList = Lists.newArrayList(topScope); boolean changed; do { changed = false; for (int i = 0; i < workList.size(); ++i) { ConcreteScope scope = workList.get(i); for (Action action : scope.getActions()) { for (Assignment assign : action.getAssignments(scope)) { if (assign.slot.addConcreteType(assign.type)) { changed = true; ConcreteScope varScope = assign.slot.getScope(); if ((varScope != scope) && !workSet.contains(varScope)) { workSet.add(varScope); workList.add(varScope); } } } } } Preconditions.checkState(++iterations != maxIterations, NON_HALTING_ERROR_MSG); } while (changed); } /** * Represents a scope in which a set of slots are declared. The scope also * includes code, which is normalized to a set of actions (which may affect * slots in other scopes as well). */ class ConcreteScope implements StaticScope { private final ConcreteScope parent; private final Map slots; private final List actions; ConcreteScope(ConcreteScope parent) { this.parent = parent; this.slots = Maps.newHashMap(); this.actions = Lists.newArrayList(); } @Override public Node getRootNode() { return null; } @Override public StaticScope getParentScope() { return parent; } @Override public StaticSlot getOwnSlot(String name) { return slots.get(name); } @Override public StaticSlot getSlot(String name) { StaticSlot var = getOwnSlot(name); if (var != null) { return var; } else if (parent != null) { return parent.getSlot(name); } else { return null; } } /** Returns all the slots in this scope. */ Collection getSlots() { return slots.values(); } @Override public ConcreteType getTypeOfThis() { // Since the slot doesn't have a reference to its ConcreteType, we can't // reference the ConcreteFunctionType directly to get the typeOfThis. ConcreteSlot thisVar = slots.get(ConcreteFunctionType.THIS_SLOT_NAME); return (thisVar != null) ? thisVar.getType() : ConcreteType.NONE; } /** Add a declaration for the given variable. */ void declareSlot(String name, Node declaration) { slots.put(name, new ConcreteSlot(this, name)); } /** Add a declaration for the given variable with the given type. */ void declareSlot(String name, Node declaration, ConcreteType type) { ConcreteSlot var = new ConcreteSlot(this, name); var.addConcreteType(type); slots.put(name, var); } /** Returns all the actions performed in the code of this scope. */ List getActions() { return actions; } /** Finds assignments and variables from the function body. */ void initForScopeRoot(Node decl) { Preconditions.checkNotNull(decl); if (decl.isFunction()) { decl = decl.getLastChild(); } Preconditions.checkArgument(decl.isBlock()); NodeTraversal.traverse(compiler, decl, new CreateScope(this, false)); } /** Finds assignments and variables from the given externs. */ void initForExternRoot(Node decl) { Preconditions.checkNotNull(decl); Preconditions.checkArgument(decl.isBlock()); NodeTraversal.traverse(compiler, decl, new CreateScope(this, true)); } /** Adds the given action to the list for the code in this scope. */ void addAction(Action action) { actions.add(action); } @Override public String toString() { return getTypeOfThis().toString() + " " + getSlots(); } } /** Represents a variable or function declared in a scope. */ static class ConcreteSlot implements StaticSlot { private final ConcreteScope scope; private final String name; private ConcreteType type; ConcreteSlot(ConcreteScope scope, String name) { this.scope = scope; this.name = name; this.type = ConcreteType.NONE; } /** Returns the scope in which this slot exists. */ ConcreteScope getScope() { return scope; } /** Returns the name of this slot in its scope. */ @Override public String getName() { return name; } @Override public ConcreteType getType() { return type; } /** Whether this type was inferred rather than declared (always true). */ @Override public boolean isTypeInferred() { return true; } @Override public StaticReference getDeclaration() { return null; } @Override public JSDocInfo getJSDocInfo() { return null; } /** * Adds the given type to the possible concrete types for this slot. * Returns whether the added type was not already known. */ boolean addConcreteType(ConcreteType type) { ConcreteType origType = this.type; this.type = origType.unionWith(type); return !this.type.equals(origType); } @Override public String toString() { return getName() + ": " + getType(); } } /** * Represents a type of action performed in the body of scope that may affect * the concrete types of slot. Example actions are a function call, a * variable assignment, and a property assignment. The function call will * create assignments for each of the function parameters, for the "this" * slot, and for the "call" slot. Property and variable assignment actions * create assignments for the property or variable they represent. */ private static interface Action { /** Returns all assignments that may occur by this action. */ Collection getAssignments(ConcreteScope scope); } /** Represents an assignment to a variable of a set of possible types. */ private static class Assignment { private final ConcreteSlot slot; private final ConcreteType type; Assignment(ConcreteSlot slot, ConcreteType type) { this.slot = slot; this.type = type; Preconditions.checkNotNull(slot); Preconditions.checkNotNull(type); } } /** Records an assignment of an expression to a variable. */ private class VariableAssignAction implements Action { private final ConcreteSlot slot; private final Node expression; VariableAssignAction(ConcreteSlot slot, Node expr) { this.slot = slot; this.expression = expr; Preconditions.checkNotNull(slot); Preconditions.checkNotNull(expr); } @Override public Collection getAssignments(ConcreteScope scope) { return Lists.newArrayList( new Assignment(slot, inferConcreteType(scope, expression))); } } /** Records an assignment of an expression to a property of an object. */ private class PropertyAssignAction implements Action { private final Node receiver; private final String propName; private final Node expression; PropertyAssignAction(Node receiver, Node expr) { this.receiver = receiver; this.propName = receiver.getNext().getString(); this.expression = expr; Preconditions.checkNotNull(receiver); Preconditions.checkNotNull(propName); Preconditions.checkNotNull(expr); } /** * Returns all assignments that could occur as a result of this property * assign action. Each type in the receiver is checked for a property * {@code propName}, and if that property exists, it is assigned the type * of {@code expression}. */ @Override public Collection getAssignments(ConcreteScope scope) { ConcreteType recvType = inferConcreteType(scope, receiver); ConcreteType exprType = inferConcreteType(scope, expression); List assigns = Lists.newArrayList(); for (StaticSlot prop : recvType.getPropertySlots(propName)) { assigns.add(new Assignment((ConcreteSlot) prop, exprType)); } return assigns; } } /** Helper class to build a FunctionCall object. */ private class FunctionCallBuilder { private boolean isNewCall = false; private boolean isCallFunction = false; private final Node receiver; private final Node firstArgument; private String propName = null; FunctionCallBuilder(Node receiver, Node firstArgument) { this.receiver = receiver; this.firstArgument = firstArgument; } FunctionCallBuilder setPropName(String propName) { this.propName = propName; return this; } /** Should be called iff this is a new call, e.g. new Object(); */ FunctionCallBuilder setIsNewCall(boolean isNew) { Preconditions.checkState(!(isCallFunction && isNew), "A function call cannot be of the form: new Object.call()"); isNewCall = isNew; return this; } /** * Should be called iff this is a {@code call()} function call, * e.g. Array.prototype.slice.call(arguments, 0); */ FunctionCallBuilder setIsCallFunction() { Preconditions.checkState(!isNewCall, "A function call cannot be of the form: new Object.call()"); isCallFunction = true; return this; } Action build() { if (isCallFunction) { return new NativeCallFunctionCall(receiver, propName, firstArgument); } else { return new FunctionCall(isNewCall, receiver, propName, firstArgument); } } } /** * Returns a list of assignments that will result from a function call with * the given concrete types. */ private List getFunctionCallAssignments(ConcreteType recvType, ConcreteType thisType, List argTypes) { List assigns = Lists.newArrayList(); for (ConcreteFunctionType fType : recvType.getFunctions()) { assigns.add(new Assignment((ConcreteSlot) fType.getCallSlot(), fType)); assigns.add(new Assignment((ConcreteSlot) fType.getThisSlot(), thisType)); for (int i = 0; i < argTypes.size(); ++i) { ConcreteSlot variable = (ConcreteSlot) fType.getParameterSlot(i); // TODO(johnlenz): Support "arguments" references in function bodies. // For now, ignore anonymous arguments. if (variable != null) { assigns.add(new Assignment(variable, argTypes.get(i))); } } } return assigns; } /** * Records a call to a function with a given set of concrete types. This is * used for function calls that originate outside the scope of the user code. * E.g. callbacks from an extern function. */ private class ExternFunctionCall implements Action { private Node receiver; private ConcreteType thisType; private List argTypes; ExternFunctionCall(Node receiver, ConcreteType thisType, List argTypes) { this.receiver = receiver; this.thisType = thisType; this.argTypes = argTypes; } @Override public Collection getAssignments(ConcreteScope scope) { return getFunctionCallAssignments(inferConcreteType(scope, receiver), thisType, argTypes); } } /** Records a call to a function with a given set of arguments. */ private class FunctionCall implements Action { private final boolean isNewCall; private final Node receiver; private final String propName; private final Node firstArgument; /** * The function called is {@code receiver} or, if {@code propName} is * non-null, the {@propName} field of {@code receiver}. */ FunctionCall(boolean isNewCall, Node receiver, String propName, Node firstArgument) { this.isNewCall = isNewCall; this.receiver = receiver; this.propName = propName; this.firstArgument = firstArgument; Preconditions.checkNotNull(receiver); } @Override public Collection getAssignments(ConcreteScope scope) { ConcreteType thisType = ConcreteType.NONE; ConcreteType recvType = inferConcreteType(scope, receiver); // If a property name was specified, then the receiver is actually the // type of this and the actual receiver is the type of that property. if (propName != null) { thisType = recvType; recvType = thisType.getPropertyType(propName); } if (recvType.isAll()) { // TODO(user): ensure that this will trigger for code like // functions[3](); throw new AssertionError( "Found call on all type, which makes tighten types useless."); } // If this is a call to new, then a new instance of the receiver is // created and passed in as the value of this. if (isNewCall) { thisType = ConcreteType.NONE; for (ConcreteInstanceType instType : recvType.getFunctionInstanceTypes()) { thisType = thisType.unionWith(instType); } boolean added = allInstantiatedTypes.add(thisType); if (added) { // A new type instance invalidates the cached type intersections. typeIntersectionMemos.clear(); } } List argTypes = Lists.newArrayList(); for (Node arg = firstArgument; arg != null; arg = arg.getNext()) { argTypes.add(inferConcreteType(scope, arg)); } return getFunctionCallAssignments(recvType, thisType, argTypes); } } /** Records a call to the native call() function. */ private class NativeCallFunctionCall implements Action { private final Node receiver; private final String propName; private final Node firstArgument; NativeCallFunctionCall(Node receiver, String propName, Node firstArgument) { this.receiver = receiver; this.propName = propName; this.firstArgument = firstArgument; Preconditions.checkNotNull(receiver); } @Override public Collection getAssignments(ConcreteScope scope) { ConcreteType thisType = (firstArgument != null) ? inferConcreteType(scope, firstArgument) : getTopScope().getTypeOfThis(); ConcreteType recvType = inferConcreteType(scope, receiver); if (recvType instanceof ConcreteInstanceType && ((ConcreteInstanceType) recvType).isFunctionPrototype()) { recvType = thisType.getPropertyType(propName); } List argTypes = Lists.newArrayList(); // Skip the first argument for call() as it is the 'this' object. for (Node arg = firstArgument.getNext(); arg != null; arg = arg.getNext()) { argTypes.add(inferConcreteType(scope, arg)); } return getFunctionCallAssignments(recvType, thisType, argTypes); } } /** Adds all the variables and assignments to a given scope from the code. */ private class CreateScope extends AbstractShallowCallback { private final ConcreteScope scope; private final boolean inExterns; CreateScope(ConcreteScope scope, boolean inExterns) { this.scope = scope; this.inExterns = inExterns; } // TODO(user): handle object literals like { a: new Foo }; @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Variable declaration, e.g. var a = b; Node name; for (name = n.getFirstChild(); name != null; name = name.getNext()) { if (inExterns) { // In externs, we have to trust the type information because there // are not necessarily assignments to the variables, calls to the // functions, etc. scope.declareSlot(name.getString(), n, createType(name, scope)); } else { scope.declareSlot(name.getString(), n); if (name.getFirstChild() != null) { addActions(createAssignmentActions( name, name.getFirstChild(), n)); } } } break; case Token.GETPROP: // Property access, e.g. a.b = c; if (inExterns) { ConcreteType type = inferConcreteType(getTopScope(), n); // We only need to set a type if one hasn't been assigned by // something else, e.g. an ASSIGN node. if (type.isNone()) { ConcreteScope scope = (ConcreteScope) inferConcreteType(getTopScope(), n.getFirstChild()).getScope(); if (scope != null) { type = createType(n.getJSType()); if (type.isNone() || type.isAll()) { break; } type = createUnionWithSubTypes(type); Node nameNode = n.getLastChild(); scope.declareSlot(nameNode.getString(), n, type); } } } break; case Token.FUNCTION: // Function declaration, e.g. function Foo() {}; if (NodeUtil.isFunctionDeclaration(n)) { if (!n.getJSType().isNoObjectType()) { ConcreteFunctionType type = createConcreteFunction(n, scope); scope.declareSlot(n.getFirstChild().getString(), n, type); if (inExterns && type.getInstanceType() != null) { // We must assume all extern types are instantiated since they // can be created by the browser itself. allInstantiatedTypes.add(type.getInstanceType()); } } } break; case Token.ASSIGN: // Variable assignment, e.g. a = b; Node lhs = n.getFirstChild(); if (inExterns) { // Again, we have to trust the externs. ConcreteScope scope; if (lhs.isGetProp()) { ConcreteType type = inferConcreteType(getTopScope(), lhs.getFirstChild()); scope = (ConcreteScope) type.getScope(); } else { scope = getTopScope(); } if (scope == null) break; ConcreteType type = inferConcreteType(getTopScope(), n); if (type.isNone() || type.isAll()) { break; } if (type.isFunction()) { JSType lhsType = lhs.getJSType(); if (lhsType == null) { break; } FunctionType funType = lhsType.restrictByNotNullOrUndefined().toMaybeFunctionType(); if (funType == null) { break; } ConcreteType retType = createType(funType.getReturnType()); retType = createUnionWithSubTypes(retType); ConcreteType newret = type.toFunction().getReturnSlot() .getType().unionWith(retType); ((ConcreteScope) type.getScope()).declareSlot( ConcreteFunctionType.RETURN_SLOT_NAME, n, newret); } scope.declareSlot(lhs.getLastChild().getString(), n, type); } else { addActions(createAssignmentActions(lhs, n.getLastChild(), n)); } break; case Token.NEW: case Token.CALL: Node receiver = n.getFirstChild(); if (receiver.isGetProp()) { Node first = receiver.getFirstChild(); // Special case the call() function. if ("call".equals(first.getNext().getString())) { if (first.isGetProp()) { // foo.bar.call() addAction(new FunctionCallBuilder(first, receiver.getNext()) .setPropName(first.getFirstChild().getNext().getString()) .setIsCallFunction() .build()); } else { // bar.call() addAction(new FunctionCallBuilder( first, receiver.getNext()).setIsCallFunction() .build()); } } else { // foo.bar() addAction(new FunctionCallBuilder(first, receiver.getNext()) .setPropName(first.getNext().getString()) .build()); } } else { // foo() or new Foo() addAction(new FunctionCallBuilder(receiver, receiver.getNext()) .setIsNewCall(n.isNew()) .build()); } break; case Token.NAME: if (parent.isCatch() && parent.getFirstChild() == n) { // The variable in a catch statement gets defined in the scope of // the catch block. We approximate that, as does the normal type // system, by declaring a variable for it in the scope in which the // catch is declared. scope.declareSlot(n.getString(), n, createUnionWithSubTypes( createType(getTypeRegistry().getType("Error")).toInstance())); } break; case Token.RETURN: if (n.getFirstChild() != null) { addAction(new VariableAssignAction( (ConcreteSlot) scope.getOwnSlot( ConcreteFunctionType.RETURN_SLOT_NAME), n.getFirstChild())); } break; } Collection actions = getImplicitActions(n); if (actions != null) { for (Action action : actions) { addAction(action); } } } /** Adds the given action to the scope (in non-externs only). */ private void addAction(Action action) { Preconditions.checkState(!inExterns, "Unexpected action in externs."); scope.addAction(action); } /** Adds the given action to the scope (in non-externs only). */ private void addActions(List actions) { Preconditions.checkState(!inExterns, "Unexpected action in externs."); for (Action action : actions) { scope.addAction(action); } } /** * Returns an action for assigning the right-hand-side to the left or null * if this assignment should be ignored. */ private List createAssignmentActions( Node lhs, Node rhs, Node parent) { switch (lhs.getType()) { case Token.NAME: ConcreteSlot var = (ConcreteSlot) scope.getSlot(lhs.getString()); Preconditions.checkState(var != null, "Type tightener could not find variable with name %s", lhs.getString()); return Lists.newArrayList( new VariableAssignAction(var, rhs)); case Token.GETPROP: Node receiver = lhs.getFirstChild(); return Lists.newArrayList( new PropertyAssignAction(receiver, rhs)); case Token.GETELEM: return Lists.newArrayList(); default: throw new AssertionError( "Bad LHS for assignment: " + parent.toStringTree()); } } private ExternFunctionCall createExternFunctionCall( Node receiver, JSType jsThisType, FunctionType fun) { List argTypes = Lists.newArrayList(); ConcreteType thisType; if (fun != null) { thisType = createType(jsThisType); for (Node arg : fun.getParameters()) { argTypes.add(createType(arg, scope)); } } else { thisType = ConcreteType.NONE; } return new ExternFunctionCall(receiver, thisType, argTypes); } private JSType getJSType(Node n) { if (n.getJSType() != null) { return n.getJSType(); } else { return getTypeRegistry().getNativeType(UNKNOWN_TYPE); } } /** * Returns any actions that are implicit in the given code. This can return * null instead of an empty collection if none are found. */ private Collection getImplicitActions(Node n) { switch (n.getType()) { case Token.CALL: // Functions passed to externs functions are considered called. // E.g. window.setTimeout(callback, 100); // TODO(user): support global extern function calls if necessary // TODO(user): handle addEventListener for the case of an object // implementing the EventListener interface. Node receiver = n.getFirstChild(); if (!inExterns && receiver.isGetProp()) { return getImplicitActionsFromCall(n, receiver.getJSType()); } break; case Token.ASSIGN: Node lhs = n.getFirstChild(); // Functions assigned to externs properties are considered called. // E.g. element.onclick = function handle(evt) {}; if (!inExterns && lhs.isGetProp()) { return getImplicitActionsFromProp(lhs.getFirstChild().getJSType(), lhs.getLastChild().getString(), n.getLastChild()); } break; } return null; } private Collection getImplicitActionsFromCall( Node n, JSType recvType) { Node receiver = n.getFirstChild(); if (recvType.isUnionType()) { List actions = Lists.newArrayList(); for (JSType alt : recvType.toMaybeUnionType().getAlternates()) { actions.addAll(getImplicitActionsFromCall(n, alt)); } return actions; } else if (!(recvType.isFunctionType())) { return Lists.newArrayList(); } ObjectType objType = ObjectType.cast( getJSType(receiver.getFirstChild()) .restrictByNotNullOrUndefined()); String prop = receiver.getLastChild().getString(); if (objType != null && (objType.isPropertyInExterns(prop)) && (recvType.toMaybeFunctionType()).getParameters() != null) { List actions = Lists.newArrayList(); // Look for a function type in the argument list. Iterator paramIter = (recvType.toMaybeFunctionType()).getParameters().iterator(); Iterator argumentIter = n.children().iterator(); argumentIter.next(); // Skip the function name. while (paramIter.hasNext() && argumentIter.hasNext()) { Node arg = argumentIter.next(); Node param = paramIter.next(); if (arg.getJSType() != null && arg.getJSType().isFunctionType()) { actions.addAll(getImplicitActionsFromArgument( arg, arg.getJSType().toMaybeFunctionType().getTypeOfThis() .toObjectType(), param.getJSType())); } } return actions; } return Lists.newArrayList(); } private Collection getImplicitActionsFromArgument( Node arg, ObjectType thisType, JSType paramType) { if (paramType.isUnionType()) { List actions = Lists.newArrayList(); for (JSType paramAlt : paramType.toMaybeUnionType().getAlternates()) { actions.addAll( getImplicitActionsFromArgument(arg, thisType, paramAlt)); } return actions; } else if (paramType.isFunctionType()) { return Lists.newArrayList(createExternFunctionCall( arg, thisType, paramType.toMaybeFunctionType())); } else { return Lists.newArrayList(createExternFunctionCall( arg, thisType, null)); } } private Collection getImplicitActionsFromProp( JSType jsType, String prop, Node fnNode) { List actions = Lists.newArrayList(); if (jsType.isUnionType()) { boolean found = false; for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { ObjectType altObj = ObjectType.cast(alt); if (altObj != null) { actions.addAll(getImplicitActionsFromPropNonUnion( altObj, prop, fnNode)); if (altObj.hasProperty(prop)) { found = true; } } } if (found) { return actions; } } else { ObjectType objType = ObjectType.cast(jsType); if (objType != null && !objType.isUnknownType() && objType.hasProperty(prop)) { return getImplicitActionsFromPropNonUnion(objType, prop, fnNode); } } // If we didn't find a type that has the property, then check if there // exists a property with this name anywhere in the externs. for (ObjectType type : getTypeRegistry().getEachReferenceTypeWithProperty(prop)) { actions.addAll( getImplicitActionsFromPropNonUnion( type, prop, fnNode)); } return actions; } private Collection getImplicitActionsFromPropNonUnion( ObjectType jsType, String prop, Node fnNode) { JSType propType = jsType.getPropertyType(prop) .restrictByNotNullOrUndefined(); if (jsType.isPropertyInExterns(prop) && propType.isFunctionType()) { ObjectType thisType = jsType; if (jsType.isFunctionPrototypeType()) { thisType = thisType.getOwnerFunction().getInstanceType(); } FunctionType callType = propType.toMaybeFunctionType(); Action action = createExternFunctionCall( fnNode, thisType, callType); return Lists.newArrayList(action); } return Lists.newArrayList(); } } /** Returns a concrete type from the JSType of the given variable. */ private ConcreteType createType(Node name, ConcreteScope scope) { Preconditions.checkNotNull(name); Preconditions.checkArgument(name.isName()); if (name.getJSType() == null) { return ConcreteType.ALL; } if ((name.getFirstChild() != null) && (name.getFirstChild().isFunction())) { return createConcreteFunction(name.getFirstChild(), scope); } return createType(name.getJSType()); } /** Returns a concrete type from the given JSType. */ private ConcreteType createType(JSType jsType) { if (jsType.isUnknownType() || jsType.isEmptyType()) { return ConcreteType.ALL; } if (jsType.isUnionType()) { ConcreteType type = ConcreteType.NONE; for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { type = type.unionWith(createType(alt)); } return type; } if (jsType.isFunctionType()) { if (getConcreteFunction(jsType.toMaybeFunctionType()) != null) { return getConcreteFunction(jsType.toMaybeFunctionType()); } // Since we don't have a declaration, it's not concrete. return ConcreteType.ALL; } if (jsType.isObject()) { return createConcreteInstance(jsType.toObjectType()); } return ConcreteType.NONE; // Not a reference type. } /** * Returns a concrete type from the given JSType that includes the concrete * types for subtypes and implementing types for any interfaces. */ private ConcreteType createTypeWithSubTypes(JSType jsType) { ConcreteType ret = ConcreteType.NONE; if (jsType.isUnionType()) { for (JSType alt : jsType.toMaybeUnionType().getAlternates()) { ret = ret.unionWith(createTypeWithSubTypes(alt)); } } else { ObjectType instType = ObjectType.cast(jsType); if (instType != null && instType.getConstructor() != null && instType.getConstructor().isInterface()) { Collection implementors = getTypeRegistry().getDirectImplementors(instType); for (FunctionType implementor : implementors) { ret = ret.unionWith(createTypeWithSubTypes( implementor.getInstanceType())); } } else { ret = ret.unionWith(createUnionWithSubTypes(createType(jsType))); } } return ret; } /** Computes the concrete types that can result from the given expression. */ ConcreteType inferConcreteType(ConcreteScope scope, Node expr) { Preconditions.checkNotNull(scope); Preconditions.checkNotNull(expr); ConcreteType ret; switch (expr.getType()) { case Token.NAME: StaticSlot slot = scope.getSlot(expr.getString()); if (slot != null) { ret = slot.getType(); } else { // This should occur only for extern variables, which we are assuming // do not ever get assigned instances of user types. ret = ConcreteType.ALL; } break; case Token.THIS: ret = scope.getTypeOfThis(); break; case Token.ASSIGN: // Using the right-hand side is more specific since the left hand side // is a variable of some sort that can be assigned elsewhere. ret = inferConcreteType(scope, expr.getLastChild()); break; case Token.COMMA: ret = inferConcreteType(scope, expr.getLastChild()); break; case Token.AND: // Since a reference type is always true, only the right hand side could // actually be returned. ret = inferConcreteType(scope, expr.getLastChild()); break; case Token.OR: ret = inferConcreteType(scope, expr.getFirstChild()).unionWith( inferConcreteType(scope, expr.getLastChild())); break; case Token.HOOK: ret = inferConcreteType(scope, expr.getFirstChild().getNext()).unionWith( inferConcreteType(scope, expr.getLastChild())); break; case Token.GETPROP: ConcreteType recvType = inferConcreteType(scope, expr.getFirstChild()); if (recvType.isAll()) { ret = recvType; break; } Node prop = expr.getLastChild(); String propName = prop.getString(); ConcreteType type = recvType.getPropertyType(propName); if ("prototype".equals(propName)) { for (ConcreteFunctionType funType : recvType.getFunctions()) { type = type.unionWith(funType.getPrototypeType()); } } else if (compiler.getCodingConvention() .isSuperClassReference(propName)) { for (ConcreteFunctionType superType : recvType.getSuperclassTypes()) { type = type.unionWith(superType.getPrototypeType()); } } else if ("call".equals(propName)) { type = recvType; } ret = type; break; case Token.GETELEM: ret = ConcreteType.ALL; break; case Token.CALL: // TODO(user): Support apply on functions. // TODO(user): Create goog.bind that curries some arguments. ConcreteType targetType = inferConcreteType(scope, expr.getFirstChild()); if (targetType.isAll()) { ret = targetType; break; } ret = ConcreteType.NONE; for (ConcreteFunctionType funType : targetType.getFunctions()) { ret = ret.unionWith(funType.getReturnSlot().getType()); } break; case Token.NEW: ConcreteType constructorType = inferConcreteType(scope, expr.getFirstChild()); if (constructorType.isAll()) { throw new AssertionError("Attempted new call on all type!"); } ret = ConcreteType.NONE; for (ConcreteInstanceType instType : constructorType.getFunctionInstanceTypes()) { ret = ret.unionWith(instType); } allInstantiatedTypes.add(ret); break; case Token.FUNCTION: ret = createConcreteFunction(expr, scope); break; case Token.OBJECTLIT: if ((expr.getJSType() != null) && !expr.getJSType().isUnknownType()) { JSType exprType = expr.getJSType().restrictByNotNullOrUndefined(); ConcreteType inst = createConcreteInstance(exprType.toObjectType()); allInstantiatedTypes.add(inst); ret = inst; } else { ret = ConcreteType.ALL; } break; case Token.ARRAYLIT: ObjectType arrayType = (ObjectType) getTypeRegistry() .getNativeType(JSTypeNative.ARRAY_TYPE); ConcreteInstanceType inst = createConcreteInstance(arrayType); allInstantiatedTypes.add(inst); ret = inst; break; default: ret = ConcreteType.NONE; } return createTypeIntersection(ret, expr.getJSType()); } private ConcreteType createTypeIntersection( ConcreteType concreteType, JSType jsType) { // TODO(johnlenz): Even with memoizing all the time of this pass is still // spent in this function (due to invalidation caused by changes to // allInstantiatedTypes), specifically calls to ConcreteUnionType.unionWith ConcreteJSTypePair key = new ConcreteJSTypePair(concreteType, jsType); ConcreteType ret = typeIntersectionMemos.get(key); if (ret != null) { return ret; } if (jsType == null || jsType.isUnknownType() || concreteType.isNone()) { ret = concreteType; } else if (concreteType.isUnion() || concreteType.isSingleton()) { ret = concreteType.intersectWith(createTypeWithSubTypes(jsType)); } else { Preconditions.checkState(concreteType.isAll()); ret = createTypeWithSubTypes(jsType); } ret = ret.intersectWith(ConcreteType.createForTypes(allInstantiatedTypes)); // Keep all function types, as restricting to instantiated types will only // keep instance types. // TODO(user): only keep functions that match the JS type. for (ConcreteFunctionType functionType : concreteType.getFunctions()) { ret = ret.unionWith(functionType); } // The prototype type is special as it should only appear from a direct // reference to Foo.prototype, and not via a type cast, thus, do not filter // them out. We do not include them in the list of instantiated types. for (ConcreteInstanceType prototype : concreteType.getPrototypeTypes()) { ret = ret.unionWith(prototype); } // Anonymous object types and enums will get removed in the createForTypes // call, so add them back in as well. for (ConcreteInstanceType instance : concreteType.getInstances()) { if (!instance.instanceType.isInstanceType() && !instance.isFunctionPrototype()) { ret = ret.unionWith(instance); } } typeIntersectionMemos.put(key, ret); return ret; } @Override public ConcreteFunctionType createConcreteFunction( Node decl, StaticScope parent) { ConcreteFunctionType funType = functionFromDeclaration.get(decl); if (funType == null) { functionFromDeclaration.put(decl, funType = new ConcreteFunctionType(this, decl, parent)); if (decl.getJSType() != null) { functionFromJSType.put(decl.getJSType().toMaybeFunctionType(), funType); } } return funType; } @Override public ConcreteInstanceType createConcreteInstance(ObjectType instanceType) { // This should be an instance or function prototype object, not a function. Preconditions.checkArgument( !instanceType.isFunctionType() || instanceType == getTypeRegistry().getNativeType(U2U_CONSTRUCTOR_TYPE)); ConcreteInstanceType instType = instanceFromJSType.get(instanceType); if (instType == null) { instanceFromJSType.put(instanceType, instType = new ConcreteInstanceType(this, instanceType)); } return instType; } /** Returns the (already created) function with the given declaration. */ ConcreteFunctionType getConcreteFunction(Node decl) { return functionFromDeclaration.get(decl); } /** Returns the function (if any) for the given node. */ @Override public ConcreteFunctionType getConcreteFunction(FunctionType functionType) { return functionFromJSType.get(functionType); } /** Returns the function (if any) for the given node. */ @Override public ConcreteInstanceType getConcreteInstance(ObjectType instanceType) { return instanceFromJSType.get(instanceType); } @Override public StaticScope createFunctionScope( Node decl, StaticScope parent) { ConcreteScope scope = new ConcreteScope((ConcreteScope) parent); scope.declareSlot(ConcreteFunctionType.CALL_SLOT_NAME, decl); scope.declareSlot(ConcreteFunctionType.THIS_SLOT_NAME, decl); scope.declareSlot(ConcreteFunctionType.RETURN_SLOT_NAME, decl); for (Node n = decl.getFirstChild().getNext().getFirstChild(); n != null; n = n.getNext()) { scope.declareSlot(n.getString(), n); } // TODO(user): Create an 'arguments' variable that returns the union // of the concrete types of all parameters. scope.initForScopeRoot(decl.getLastChild()); return scope; } @Override public StaticScope createInstanceScope( ObjectType instanceType) { ConcreteScope parentScope = null; ObjectType implicitProto = instanceType.getImplicitPrototype(); if (implicitProto != null && !implicitProto.isUnknownType()) { ConcreteInstanceType prototype = createConcreteInstance(implicitProto); parentScope = (ConcreteScope) prototype.getScope(); } ConcreteScope scope = new ConcreteScope(parentScope); for (String propName : instanceType.getOwnPropertyNames()) { scope.declareSlot(propName, null); } return scope; } /** * Returns a ConcreteType that is the union of the given type and all of its * subtypes. This assumes that the passed in type is an instance type, * otherwise an empty set is returned. The returned set will be instance * types. */ ConcreteType createUnionWithSubTypes(ConcreteType type) { Set set = null; if (type.isInstance()) { set = getSubTypes(type.toInstance()); } return ConcreteType.createForTypes(set).unionWith(type); } /** Returns the set of subtypes of the given type. */ private Set getSubTypes(ConcreteInstanceType type) { if (type.getConstructorType() == null) { return null; } Set set = Sets.newHashSet(); getSubTypes(type.getConstructorType().getJSType(), set); return set; } /** * Adds all subtypes of the given type to the provided set. * @return false if the all type was encountered, else true. */ private boolean getSubTypes(FunctionType type, Set set) { if (type.getSubTypes() != null) { for (FunctionType sub : type.getSubTypes()) { ConcreteType concrete = createType(sub); if (concrete.isFunction() && concrete.toFunction().getInstanceType() != null) { concrete = concrete.toFunction().getInstanceType(); if (!set.contains(concrete)) { set.add(concrete); if (!getSubTypes(sub, set)) { return false; } } } else { // The only time we should find a subtype that doesn't have an // instance type is for the odd case of ActiveXObject, which is // of the NoObject type and will be returned as a subtype of Object. set.clear(); set.add(ConcreteType.ALL); return false; } } } return true; } /** * A simple class used to pair a concrete type and a JS type. Used to * memoize the results of a "createTypeIntersection" call. */ static class ConcreteJSTypePair { final ConcreteType concrete; final JSType jstype; final int hashcode; ConcreteJSTypePair(ConcreteType concrete, JSType jstype) { this.concrete = concrete; this.jstype = jstype; this.hashcode = concrete.hashCode() + getJSTypeHashCode(); } private int getJSTypeHashCode() { return jstype != null ? jstype.hashCode() : 0; } private boolean equalsJSType(JSType jsType) { if (jsType == null || jstype == null) { return jstype == jsType; } else { return jsType.equals(this.jstype); } } @Override public boolean equals(Object o) { if (o instanceof ConcreteJSTypePair) { ConcreteJSTypePair pair = (ConcreteJSTypePair) o; if ((pair.concrete.equals(this.concrete) && equalsJSType(pair.jstype))) { return true; } } return false; } @Override public int hashCode() { return hashcode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineSimpleMethods.java0000644000175000017500000002137112115204405027417 0ustar apoapo/* * Copyright 2007 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.NodeTraversal.Callback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.List; import java.util.logging.Logger; /** * Inlines methods that take no arguments and have only a return statement * returning a property. Because it works on method names rather than type * inference, a method with multiple definitions will be inlined if each * definition is identical. * *
       * A.prototype.foo = function() { return this.b; }
       * B.prototype.foo = function() { return this.b; }
       * 
      * * will inline foo, but * *
       * A.prototype.foo = function() { return this.b; }
       * B.prototype.foo = function() { return this.c; }
       * 
      * * will not. * * Declarations are not removed because we do not find all possible * call sites. For examples, calls of the form foo["bar"] are not * detected. * */ class InlineSimpleMethods extends MethodCompilerPass { private static final Logger logger = Logger.getLogger(InlineSimpleMethods.class.getName()); InlineSimpleMethods(AbstractCompiler compiler) { super(compiler); } /** * For each method call, see if it is a candidate for inlining. * TODO(kushal): Cache the results of the checks */ private class InlineTrivialAccessors extends InvocationsCallback { @Override void visit(NodeTraversal t, Node callNode, Node parent, String callName) { if (externMethods.contains(callName) || nonMethodProperties.contains(callName)) { return; } Collection definitions = methodDefinitions.get(callName); if (definitions == null || definitions.size() == 0) { return; } // Do check of arity, complexity, and consistency in what we think is // the order from least to most complex Node firstDefinition = definitions.iterator().next(); // Check any multiple definitions if (definitions.size() == 1 || allDefinitionsEquivalent(definitions)) { if (!argsMayHaveSideEffects(callNode)) { // Verify this is a trivial return Node returned = returnedExpression(firstDefinition); if (returned != null) { if (isPropertyTree(returned)) { logger.fine("Inlining property accessor: " + callName); inlinePropertyReturn(parent, callNode, returned); } else if (NodeUtil.isLiteralValue(returned, false) && !NodeUtil.mayHaveSideEffects( callNode.getFirstChild(), compiler)) { logger.fine("Inlining constant accessor: " + callName); inlineConstReturn(parent, callNode, returned); } } else if (isEmptyMethod(firstDefinition) && !NodeUtil.mayHaveSideEffects( callNode.getFirstChild(), compiler)) { logger.fine("Inlining empty method: " + callName); inlineEmptyMethod(parent, callNode); } } } else { logger.fine("Method '" + callName + "' has conflicting definitions."); } } } @Override Callback getActingCallback() { return new InlineTrivialAccessors(); } /** * Returns true if the provided node is a getprop for * which the left child is this or a valid property tree * and for which the right side is a string. */ private static boolean isPropertyTree(Node expectedGetprop) { if (!expectedGetprop.isGetProp()) { return false; } Node leftChild = expectedGetprop.getFirstChild(); if (!leftChild.isThis() && !isPropertyTree(leftChild)) { return false; } Node retVal = leftChild.getNext(); if (NodeUtil.getStringValue(retVal) == null) { return false; } return true; } /** * Finds the occurrence of "this" in the provided property tree and replaces * it with replacement */ private static void replaceThis(Node expectedGetprop, Node replacement) { Node leftChild = expectedGetprop.getFirstChild(); if (leftChild.isThis()) { expectedGetprop.replaceChild(leftChild, replacement); } else { replaceThis(leftChild, replacement); } } /** * Return the node that represents the expression returned * by the method, given a FUNCTION node. */ private static Node returnedExpression(Node fn) { Node expectedBlock = getMethodBlock(fn); if (!expectedBlock.hasOneChild()) { return null; } Node expectedReturn = expectedBlock.getFirstChild(); if (!expectedReturn.isReturn()) { return null; } if (!expectedReturn.hasOneChild()) { return null; } return expectedReturn.getLastChild(); } /** * Return whether the given FUNCTION node is an empty method definition. * * Must be private, or moved to NodeUtil. */ private static boolean isEmptyMethod(Node fn) { Node expectedBlock = getMethodBlock(fn); return expectedBlock == null ? false : NodeUtil.isEmptyBlock(expectedBlock); } /** * Return a BLOCK node if the given FUNCTION node is a valid method * definition, null otherwise. * * Must be private, or moved to NodeUtil. */ private static Node getMethodBlock(Node fn) { if (fn.getChildCount() != 3) { return null; } Node expectedBlock = fn.getLastChild(); return expectedBlock.isBlock() ? expectedBlock : null; } /** * Given a set of method definitions, verify they are the same. */ private boolean allDefinitionsEquivalent( Collection definitions) { List list = Lists.newArrayList(); list.addAll(definitions); Node node0 = list.get(0); for (int i = 1; i < list.size(); i++) { if (!compiler.areNodesEqualForInlining(list.get(i), node0)) { return false; } } return true; } /** * Replace the provided method call with the tree specified in returnedValue * * Parse tree of a call is * name * call * getprop * obj * string */ private void inlinePropertyReturn(Node parent, Node call, Node returnedValue) { Node getProp = returnedValue.cloneTree(); replaceThis(getProp, call.getFirstChild().removeFirstChild()); parent.replaceChild(call, getProp); compiler.reportCodeChange(); } /** * Replace the provided object and its method call with the tree specified * in returnedValue. Should be called only if the object reference has * no side effects. */ private void inlineConstReturn(Node parent, Node call, Node returnedValue) { Node retValue = returnedValue.cloneTree(); parent.replaceChild(call, retValue); compiler.reportCodeChange(); } /** * Remove the provided object and its method call. */ private void inlineEmptyMethod(Node parent, Node call) { // If the return value of the method call is read, // replace it with "void 0". Otherwise, remove the call entirely. if (NodeUtil.isExprCall(parent)) { parent.getParent().replaceChild(parent, IR.empty()); } else { Node srcLocation = call; parent.replaceChild(call, NodeUtil.newUndefinedNode(srcLocation)); } compiler.reportCodeChange(); } /** * Check whether the given method call's arguments have side effects. * @param call The call node of a method invocation. */ private boolean argsMayHaveSideEffects(Node call) { for (Node currentChild = call.getFirstChild().getNext(); currentChild != null; currentChild = currentChild.getNext()) { if (NodeUtil.mayHaveSideEffects(currentChild, compiler)) { return true; } } return false; } /** * A do-nothing signature store. */ static final MethodCompilerPass.SignatureStore DUMMY_SIGNATURE_STORE = new MethodCompilerPass.SignatureStore() { @Override public void addSignature( String functionName, Node functionNode, String sourceFile) { } @Override public void removeSignature(String functionName) { } @Override public void reset() { } }; @Override SignatureStore getSignatureStore() { return DUMMY_SIGNATURE_STORE; } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveUnusedPrototypeProperties.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RemoveUnusedPrototypeProperties.jav0000644000175000017500000001156612115204405031773 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.javascript.jscomp.AnalyzePrototypeProperties.AssignmentProperty; import com.google.javascript.jscomp.AnalyzePrototypeProperties.GlobalFunction; import com.google.javascript.jscomp.AnalyzePrototypeProperties.LiteralProperty; import com.google.javascript.jscomp.AnalyzePrototypeProperties.NameInfo; import com.google.javascript.jscomp.AnalyzePrototypeProperties.Symbol; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.logging.Logger; /** * Removes unused properties from prototypes. * */ class RemoveUnusedPrototypeProperties implements SpecializationAwareCompilerPass { private static final Logger logger = Logger.getLogger(RemoveUnusedPrototypeProperties.class.getName()); private final AbstractCompiler compiler; private final boolean canModifyExterns; private final boolean anchorUnusedVars; private SpecializeModule.SpecializationState specializationState; /** * Creates a new pass for removing unused prototype properties, based * on the uniqueness of property names. * @param compiler The compiler. * @param canModifyExterns If true, then we can remove prototype * properties that are declared in the externs file. * @param anchorUnusedVars If true, then we must keep unused variables * and the prototype properties they reference, even if they are * never used. */ RemoveUnusedPrototypeProperties(AbstractCompiler compiler, boolean canModifyExterns, boolean anchorUnusedVars) { this.compiler = compiler; this.canModifyExterns = canModifyExterns; this.anchorUnusedVars = anchorUnusedVars; } @Override public void enableSpecialization(SpecializeModule.SpecializationState state) { this.specializationState = state; } @Override public void process(Node externRoot, Node root) { AnalyzePrototypeProperties analyzer = new AnalyzePrototypeProperties(compiler, null /* no module graph */, canModifyExterns, anchorUnusedVars); analyzer.process(externRoot, root); removeUnusedSymbols(analyzer.getAllNameInfo()); } /** * Remove all properties under a given name if the property name is * never referenced. */ private void removeUnusedSymbols(Collection allNameInfo) { boolean changed = false; for (NameInfo nameInfo : allNameInfo) { if (!nameInfo.isReferenced()) { for (Symbol declaration : nameInfo.getDeclarations()) { boolean canRemove = false; if (specializationState == null) { canRemove = true; } else { Node specializableFunction = getSpecializableFunctionFromSymbol(declaration); if (specializableFunction != null) { specializationState.reportRemovedFunction( specializableFunction, null); canRemove = true; } } if (canRemove) { declaration.remove(); changed = true; } } logger.fine("Removed unused prototype property: " + nameInfo.name); } } if (changed) { compiler.reportCodeChange(); } } /** * Attempts to find a specializable function from the Symbol. */ private Node getSpecializableFunctionFromSymbol(Symbol symbol) { Preconditions.checkNotNull(specializationState); Node specializableFunction = null; if (symbol instanceof GlobalFunction) { specializableFunction = ((GlobalFunction) symbol).getFunctionNode(); } else if (symbol instanceof AssignmentProperty) { Node propertyValue = ((AssignmentProperty) symbol).getValue(); if (propertyValue.isFunction()) { specializableFunction = propertyValue; } } else if (symbol instanceof LiteralProperty) { // Module specialization doesn't know how to handle these // because the "name" of the function isn't the name // it needs to add an unspecialized version of. return null; } else { Preconditions.checkState(false, "Should be unreachable."); } if (specializableFunction != null && specializationState.canFixupFunction(specializableFunction)) { return specializableFunction; } else { return null; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineObjectLiterals.java0000644000175000017500000004063412115204405027553 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Using the infrastructure provided by {@link ReferenceCollectingCallback}, * identify variables that are only ever assigned to object literals * and that are never used in entirety, and expand the objects into * individual variables. * * Based on the InlineVariables pass * */ class InlineObjectLiterals implements CompilerPass { public static final String VAR_PREFIX = "JSCompiler_object_inline_"; private final AbstractCompiler compiler; private final Supplier safeNameIdSupplier; InlineObjectLiterals( AbstractCompiler compiler, Supplier safeNameIdSupplier) { this.compiler = compiler; this.safeNameIdSupplier = safeNameIdSupplier; } @Override public void process(Node externs, Node root) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new InliningBehavior()); callback.process(externs, root); } /** * Builds up information about nodes in each scope. When exiting the * scope, inspects all variables in that scope, and inlines any * that we can. */ private class InliningBehavior implements Behavior { /** * A list of variables that should not be inlined, because their * reference information is out of sync with the state of the AST. */ private final Set staleVars = Sets.newHashSet(); @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { for (Iterator it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); if (isVarInlineForbidden(v)) { continue; } ReferenceCollection referenceInfo = referenceMap.getReferences(v); if (isInlinableObject(referenceInfo.references)) { // Blacklist the object itself, as well as any other values // that it refers to, since they will have been moved around. staleVars.add(v); Reference init = referenceInfo.getInitializingReference(); // Split up the object into individual variables if the object // is never referenced directly in full. splitObject(v, init, referenceInfo); } } } /** * If there are any variable references in the given node tree, * blacklist them to prevent the pass from trying to inline the * variable. Any code modifications will have potentially made the * ReferenceCollection invalid. */ private void blacklistVarReferencesInTree(Node root, final Scope scope) { NodeUtil.visitPreOrder(root, new NodeUtil.Visitor() { @Override public void visit(Node node) { if (node.isName()) { staleVars.add(scope.getVar(node.getString())); } } }, NodeUtil.MATCH_NOT_FUNCTION); } /** * Whether the given variable is forbidden from being inlined. */ private boolean isVarInlineForbidden(Var var) { // A variable may not be inlined if: // 1) The variable is defined in the externs // 2) The variable is exported, // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME // 4) A reference to the variable has been inlined. We're downstream // of the mechanism that creates variable references, so we don't // have a good way to update the reference. Just punt on it. // Additionally, exclude global variables for now. return var.isGlobal() || var.isExtern() || compiler.getCodingConvention().isExported(var.name) || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name) || staleVars.contains(var); } /** * Counts the number of direct (full) references to an object. * Specifically, we check for references of the following type: *
           *   x;
           *   x.fn();
           * 
      */ private boolean isInlinableObject(List refs) { boolean ret = false; Set validProperties = Sets.newHashSet(); for (Reference ref : refs) { Node name = ref.getNode(); Node parent = ref.getParent(); Node gramps = ref.getGrandparent(); // Ignore most indirect references, like x.y (but not x.y(), // since the function referenced by y might reference 'this'). // if (parent.isGetProp()) { Preconditions.checkState(parent.getFirstChild() == name); // A call target may be using the object as a 'this' value. if (gramps.isCall() && gramps.getFirstChild() == parent) { return false; } // Deleting a property has different semantics from deleting // a variable, so deleted properties should not be inlined. if (gramps.isDelProp()) { return false; } // NOTE(nicksantos): This pass's object-splitting algorithm has // a blind spot. It assumes that if a property isn't defined on an // object, then the value is undefined. This is not true, because // Object.prototype can have arbitrary properties on it. // // We short-circuit this problem by bailing out if we see a reference // to a property that isn't defined on the object literal. This // isn't a perfect algorithm, but it should catch most cases. String propName = parent.getLastChild().getString(); if (!validProperties.contains(propName)) { if (NodeUtil.isVarOrSimpleAssignLhs(parent, gramps)) { validProperties.add(propName); } else { return false; } } continue; } // Only rewrite VAR declarations or simple assignment statements if (!isVarOrAssignExprLhs(name)) { return false; } Node val = ref.getAssignedValue(); if (val == null) { // A var with no assignment. continue; } // We're looking for object literal assignments only. if (!val.isObjectLit()) { return false; } // Make sure that the value is not self-referential. IOW, // disallow things like x = {b: x.a}. // // TODO: Only exclude unorderable self-referential // assignments. i.e. x = {a: x.b, b: x.a} is not orderable, // but x = {a: 1, b: x.a} is. // // Also, ES5 getters/setters aren't handled by this pass. for (Node child = val.getFirstChild(); child != null; child = child.getNext()) { if (child.isGetterDef() || child.isSetterDef()) { // ES5 get/set not supported. return false; } validProperties.add(child.getString()); Node childVal = child.getFirstChild(); // Check if childVal is the parent of any of the passed in // references, as that is how self-referential assignments // will happen. for (Reference t : refs) { Node refNode = t.getParent(); while (!NodeUtil.isStatementBlock(refNode)) { if (refNode == childVal) { // There's a self-referential assignment return false; } refNode = refNode.getParent(); } } } // We have found an acceptable object literal assignment. As // long as there are no other assignments that mess things up, // we can inline. ret = true; } return ret; } private boolean isVarOrAssignExprLhs(Node n) { Node parent = n.getParent(); return parent.isVar() || (parent.isAssign() && parent.getFirstChild() == n && parent.getParent().isExprResult()); } /** * Computes a list of ever-referenced keys in the object being * inlined, and returns a mapping of key name -> generated * variable name. */ private Map computeVarList( ReferenceCollection referenceInfo) { Map varmap = Maps.newLinkedHashMap(); for (Reference ref : referenceInfo.references) { if (ref.isLvalue() || ref.isInitializingDeclaration()) { Node val = ref.getAssignedValue(); if (val != null) { Preconditions.checkState(val.isObjectLit()); for (Node child = val.getFirstChild(); child != null; child = child.getNext()) { String varname = child.getString(); if (varmap.containsKey(varname)) { continue; } String var = VAR_PREFIX + varname + "_" + safeNameIdSupplier.get(); varmap.put(varname, var); } } } else if (ref.getParent().isVar()) { // This is the var. There is no value. } else { Node getprop = ref.getParent(); Preconditions.checkState(getprop.isGetProp()); // The key being looked up in the original map. String varname = getprop.getLastChild().getString(); if (varmap.containsKey(varname)) { continue; } String var = VAR_PREFIX + varname + "_" + safeNameIdSupplier.get(); varmap.put(varname, var); } } return varmap; } /** * Populates a map of key names -> initial assigned values. The * object literal these are being pulled from is invalidated as * a result. */ private void fillInitialValues(Reference init, Map initvals) { Node object = init.getAssignedValue(); Preconditions.checkState(object.isObjectLit()); for (Node key = object.getFirstChild(); key != null; key = key.getNext()) { initvals.put(key.getString(), key.removeFirstChild()); } } /** * Replaces an assignment like x = {...} with t1=a,t2=b,t3=c,true. * Note that the resulting expression will always evaluate to * true, as would the x = {...} expression. */ private void replaceAssignmentExpression(Var v, Reference ref, Map varmap) { // Compute all of the assignments necessary List nodes = Lists.newArrayList(); Node val = ref.getAssignedValue(); blacklistVarReferencesInTree(val, v.scope); Preconditions.checkState(val.isObjectLit()); Set all = Sets.newLinkedHashSet(varmap.keySet()); for (Node key = val.getFirstChild(); key != null; key = key.getNext()) { String var = key.getString(); Node value = key.removeFirstChild(); // TODO(user): Copy type information. nodes.add( IR.assign( IR.name(varmap.get(var)), value)); all.remove(var); } // TODO(user): Better source information. for (String var : all) { nodes.add( IR.assign( IR.name(varmap.get(var)), NodeUtil.newUndefinedNode(null))); } Node replacement; if (nodes.isEmpty()) { replacement = IR.trueNode(); } else { // All assignments evaluate to true, so make sure that the // expr statement evaluates to true in case it matters. nodes.add(IR.trueNode()); // Join these using COMMA. A COMMA node must have 2 children, so we // create a tree. In the tree the first child be the COMMA to match // the parser, otherwise tree equality tests fail. nodes = Lists.reverse(nodes); replacement = new Node(Token.COMMA); Node cur = replacement; int i; for (i = 0; i < nodes.size() - 2; i++) { cur.addChildToFront(nodes.get(i)); Node t = new Node(Token.COMMA); cur.addChildToFront(t); cur = t; } cur.addChildToFront(nodes.get(i)); cur.addChildToFront(nodes.get(i + 1)); } Node replace = ref.getParent(); replacement.copyInformationFromForTree(replace); if (replace.isVar()) { replace.getParent().replaceChild( replace, NodeUtil.newExpr(replacement)); } else { replace.getParent().replaceChild(replace, replacement); } } /** * Splits up the object literal into individual variables, and * updates all uses. */ private void splitObject(Var v, Reference init, ReferenceCollection referenceInfo) { // First figure out the FULL set of possible keys, so that they // can all be properly set as necessary. Map varmap = computeVarList(referenceInfo); Map initvals = Maps.newHashMap(); // Figure out the top-level of the var assign node. If it's a plain // ASSIGN, then there's an EXPR_STATEMENT above it, if it's a // VAR then it should be directly replaced. Node vnode; boolean defined = referenceInfo.isWellDefined() && init.getParent().isVar(); if (defined) { vnode = init.getParent(); fillInitialValues(init, initvals); } else { // TODO(user): More test / rewrite this part. // Find the beginning of the function / script. vnode = v.getScope().getRootNode().getLastChild().getFirstChild(); } for (Map.Entry entry : varmap.entrySet()) { Node val = initvals.get(entry.getKey()); Node varnode = NodeUtil.newVarNode(entry.getValue(), val); if (val == null) { // is this right? varnode.copyInformationFromForTree(vnode); } else { blacklistVarReferencesInTree(val, v.scope); } vnode.getParent().addChildBefore(varnode, vnode); } if (defined) { vnode.getParent().removeChild(vnode); } for (Reference ref : referenceInfo.references) { // The init/decl have already been converted. if (defined && ref == init) continue; if (ref.isLvalue()) { // Assignments have to be handled specially, since they // expand out into multiple assignments. replaceAssignmentExpression(v, ref, varmap); } else if (ref.getParent().isVar()) { // The old variable declaration. It didn't have a // value. Remove it entirely as it should now be unused. ref.getGrandparent().removeChild(ref.getParent()); } else { // Make sure that the reference is a GETPROP as we expect it to be. Node getprop = ref.getParent(); Preconditions.checkState(getprop.isGetProp()); // The key being looked up in the original map. String var = getprop.getChildAtIndex(1).getString(); // If the variable hasn't already been declared, add an empty // declaration near all the other declarations. Preconditions.checkState(varmap.containsKey(var)); // Replace the GETPROP node with a NAME. Node replacement = IR.name(varmap.get(var)); replacement.copyInformationFrom(getprop); ref.getGrandparent().replaceChild(ref.getParent(), replacement); } } compiler.reportCodeChange(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PropertyRenamingPolicy.java0000644000175000017500000000246112115204405030167 0ustar apoapo/* * 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; /** * Policies to determine how properties should be renamed. */ public enum PropertyRenamingPolicy { /** * Rename no properties. */ OFF, /** * Rename properties heuristically. * @see RenamePrototypes */ HEURISTIC, /** * Rename properties more heuristically. * @see RenamePrototypes */ AGGRESSIVE_HEURISTIC, /** * Rename all properties that aren't explicitly quoted and aren't * externally defined (i.e. declared in an externs file). This policy * achieves better compaction than the others. * @see RenameProperties */ ALL_UNQUOTED, // for transitioning off old flags. not for public consumption. UNSPECIFIED } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodeConsumer.java0000644000175000017500000001650212115204405026071 0ustar apoapo/* * Copyright 2004 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.rhino.Node; /** * Abstracted consumer of the CodeGenerator output. * * @see CodeGenerator * @see CodePrinter * @see InlineCostEstimator */ abstract class CodeConsumer { boolean statementNeedsEnded = false; boolean statementStarted = false; boolean sawFunction = false; /** * Starts the source mapping for the given * node at the current position. */ void startSourceMapping(Node node) { } /** * Finishes the source mapping for the given * node at the current position. */ void endSourceMapping(Node node) { } /** * Provides a means of interrupting the CodeGenerator. Derived classes should * return false to stop further processing. */ boolean continueProcessing() { return true; } /** * Retrieve the last character of the last string sent to append. */ abstract char getLastChar(); void addIdentifier(String identifier) { add(identifier); } /** * Appends a string to the code, keeping track of the current line length. * * NOTE: the string must be a complete token--partial strings or * partial regexes will run the risk of being split across lines. * * Do not directly append newlines with this method. Instead use * {@link #startNewLine}. */ abstract void append(String str); void appendBlockStart() { append("{"); } void appendBlockEnd() { append("}"); } void startNewLine() { } void maybeLineBreak() { maybeCutLine(); } void maybeCutLine() { } void endLine() { } void notePreferredLineBreak() { } void beginBlock() { if (statementNeedsEnded) { append(";"); maybeLineBreak(); } appendBlockStart(); endLine(); statementNeedsEnded = false; } void endBlock() { endBlock(false); } void endBlock(boolean shouldEndLine) { appendBlockEnd(); if (shouldEndLine) { endLine(); } statementNeedsEnded = false; } void listSeparator() { add(","); maybeLineBreak(); } /** * Indicates the end of a statement and a ';' may need to be added. * But we don't add it now, in case we're at the end of a block (in which * case we don't have to add the ';'). * See maybeEndStatement() */ void endStatement() { endStatement(false); } void endStatement(boolean needSemiColon) { if (needSemiColon) { append(";"); maybeLineBreak(); statementNeedsEnded = false; } else if (statementStarted) { statementNeedsEnded = true; } } /** * This is to be called when we're in a statement. If the prev statement * needs to be ended, add a ';'. */ void maybeEndStatement() { // Add a ';' if we need to. if (statementNeedsEnded) { append(";"); maybeLineBreak(); endLine(); statementNeedsEnded = false; } statementStarted = true; } void endFunction() { endFunction(false); } void endFunction(boolean statementContext) { sawFunction = true; if (statementContext) { endLine(); } } void beginCaseBody() { append(":"); } void endCaseBody() { } void add(String newcode) { maybeEndStatement(); if (newcode.length() == 0) { return; } char c = newcode.charAt(0); if ((isWordChar(c) || c == '\\') && isWordChar(getLastChar())) { // need space to separate. This is not pretty printing. // For example: "return foo;" append(" "); } else if (c == '/' && getLastChar() == '/') { // Do not allow a forward slash to appear after a DIV. // For example, // REGEXP DIV REGEXP // is valid and should print like // / // / / append(" "); } append(newcode); } void appendOp(String op, boolean binOp) { append(op); } void addOp(String op, boolean binOp) { maybeEndStatement(); char first = op.charAt(0); char prev = getLastChar(); if ((first == '+' || first == '-') && prev == first) { // This is not pretty printing. This is to prevent misparsing of // things like "x + ++y" or "x++ + ++y" append(" "); } else if (Character.isLetter(first) && isWordChar(prev)) { // Make sure there is a space after e.g. instanceof , typeof append(" "); } else if (prev == '-' && first == '>') { // Make sure that we don't emit --> append(" "); } // Allow formatting around the operator. appendOp(op, binOp); // Line breaking after an operator is always safe. Line breaking before an // operator on the other hand is not. We only line break after a bin op // because it looks strange. if (binOp) { maybeCutLine(); } } void addNumber(double x) { // This is not pretty printing. This is to prevent misparsing of x- -4 as // x--4 (which is a syntax error). char prev = getLastChar(); boolean negativeZero = isNegativeZero(x); if ((x < 0 || negativeZero) && prev == '-') { add(" "); } if (negativeZero) { addConstant("-0"); } else if ((long) x == x) { long value = (long) x; long mantissa = value; int exp = 0; if (Math.abs(x) >= 100) { while (mantissa / 10 * Math.pow(10, exp + 1) == value) { mantissa /= 10; exp++; } } if (exp > 2) { addConstant(Long.toString(mantissa) + "E" + Integer.toString(exp)); } else { long valueAbs = Math.abs(value); if (Long.toHexString(valueAbs).length() + 2 < Long.toString(valueAbs).length()) { addConstant((value < 0 ? "-" : "") + "0x" + Long.toHexString(valueAbs)); } else { addConstant(Long.toString(value)); } } } else { addConstant(String.valueOf(x).replace(".0E", "E")); } } void addConstant(String newcode) { add(newcode); } static boolean isNegativeZero(double x) { return x == 0.0 && Math.copySign(1, x) == -1.0; } static boolean isWordChar(char ch) { return (ch == '_' || ch == '$' || Character.isLetterOrDigit(ch)); } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? Doing so can * help when pretty-printing the code, and permits putting a debugging * breakpoint on the statement inside the condition. * * @return {@boolean true} if such expressions should be wrapped */ boolean shouldPreserveExtraBlocks() { return false; } /** * @return Whether the a line break can be added after the specified BLOCK. */ boolean breakAfterBlockFor(Node n, boolean statementContext) { return statementContext; } /** Called when we're at the end of a file. */ void endFile() {} } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DefinitionsRemover.java0000644000175000017500000002570612115204405027324 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Models an assignment that defines a variable and the removal of it. * */ class DefinitionsRemover { /** * @return an {@link Definition} object if the node contains a definition or * {@code null} otherwise. */ static Definition getDefinition(Node n, boolean isExtern) { // TODO(user): Since we have parent pointers handy. A lot of constructors // can be simplified. // This logic must match #isDefinitionNode Node parent = n.getParent(); if (parent == null) { return null; } if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { return new VarDefinition(n, isExtern); } else if (parent.isFunction() && parent.getFirstChild() == n) { if (!NodeUtil.isFunctionExpression(parent)) { return new NamedFunctionDefinition(parent, isExtern); } else if (!n.getString().equals("")) { return new FunctionExpressionDefinition(parent, isExtern); } } else if (parent.isAssign() && parent.getFirstChild() == n) { return new AssignmentDefinition(parent, isExtern); } else if (NodeUtil.isObjectLitKey(n)) { return new ObjectLiteralPropertyDefinition(parent, n, n.getFirstChild(), isExtern); } else if (parent.isParamList()) { Node function = parent.getParent(); return new FunctionArgumentDefinition(function, n, isExtern); } return null; } /** * @return Whether a definition object can be created. */ static boolean isDefinitionNode(Node n) { // This logic must match #getDefinition Node parent = n.getParent(); if (parent == null) { return false; } if (NodeUtil.isVarDeclaration(n) && n.hasChildren()) { return true; } else if (parent.isFunction() && parent.getFirstChild() == n) { if (!NodeUtil.isFunctionExpression(parent)) { return true; } else if (!n.getString().equals("")) { return true; } } else if (parent.isAssign() && parent.getFirstChild() == n) { return true; } else if (NodeUtil.isObjectLitKey(n)) { return true; } else if (parent.isParamList()) { return true; } return false; } static abstract class Definition { private final boolean isExtern; Definition(boolean isExtern) { this.isExtern = isExtern; } /** * Removes this definition from the AST if it is not an extern. * * This method should not be called on a definition for which isExtern() * is true. */ public void remove() { if (!isExtern) { performRemove(); } else { throw new IllegalStateException("Attempt to remove() an extern" + " definition."); } } /** * Subclasses should override to remove the definition from the AST. */ protected abstract void performRemove(); /** * Variable or property name represented by this definition. * For example, in the case of assignments this method would * return the NAME, GETPROP or GETELEM expression that acts as the * assignment left hand side. * * @return the L-Value associated with this definition. * The node's type is always NAME, GETPROP or GETELEM. */ public abstract Node getLValue(); /** * Value expression that acts as the right hand side of the * definition statement. */ public abstract Node getRValue(); /** * Returns true if the definition is an extern. */ public boolean isExtern() { return isExtern; } } /** * Represents an name-only external definition. The definition's * RHS is missing. */ abstract static class IncompleteDefinition extends Definition { private static final Set ALLOWED_TYPES = ImmutableSet.of(Token.NAME, Token.GETPROP, Token.GETELEM); private final Node lValue; IncompleteDefinition(Node lValue, boolean inExterns) { super(inExterns); Preconditions.checkNotNull(lValue); Preconditions.checkArgument(ALLOWED_TYPES.contains(lValue.getType()), "Unexpected lValue type %s", Token.name(lValue.getType())); this.lValue = lValue; } @Override public Node getLValue() { return lValue; } @Override public Node getRValue() { return null; } } /** * Represents an unknown definition. */ static final class UnknownDefinition extends IncompleteDefinition { UnknownDefinition(Node lValue, boolean inExterns) { super(lValue, inExterns); } @Override public void performRemove() { throw new IllegalArgumentException("Can't remove an UnknownDefinition"); } } /** * Represents an name-only external definition. The definition's * RHS is missing. */ static final class ExternalNameOnlyDefinition extends IncompleteDefinition { ExternalNameOnlyDefinition(Node lValue) { super(lValue, true); } @Override public void performRemove() { throw new IllegalArgumentException( "Can't remove external name-only definition"); } } /** * Represents a function formal parameter. The definition's RHS is missing. */ static final class FunctionArgumentDefinition extends IncompleteDefinition { FunctionArgumentDefinition(Node function, Node argumentName, boolean inExterns) { super(argumentName, inExterns); Preconditions.checkArgument(function.isFunction()); Preconditions.checkArgument(argumentName.isName()); } @Override public void performRemove() { throw new IllegalArgumentException( "Can't remove a FunctionArgumentDefinition"); } } /** * Represents a function declaration or function expression. */ abstract static class FunctionDefinition extends Definition { protected final Node function; FunctionDefinition(Node node, boolean inExterns) { super(inExterns); Preconditions.checkArgument(node.isFunction()); function = node; } @Override public Node getLValue() { return function.getFirstChild(); } @Override public Node getRValue() { return function; } } /** * Represents a function declaration without assignment node such as * {@code function foo()}. */ static final class NamedFunctionDefinition extends FunctionDefinition { NamedFunctionDefinition(Node node, boolean inExterns) { super(node, inExterns); } @Override public void performRemove() { function.detachFromParent(); } } /** * Represents a function expression that acts as a RHS. The defined * name is only reachable from within the function. */ static final class FunctionExpressionDefinition extends FunctionDefinition { FunctionExpressionDefinition(Node node, boolean inExterns) { super(node, inExterns); Preconditions.checkArgument( NodeUtil.isFunctionExpression(node)); } @Override public void performRemove() { // replace internal name with "" function.replaceChild(function.getFirstChild(), IR.name("")); } } /** * Represents a declaration within an assignment. */ static final class AssignmentDefinition extends Definition { private final Node assignment; AssignmentDefinition(Node node, boolean inExterns) { super(inExterns); Preconditions.checkArgument(node.isAssign()); assignment = node; } @Override public void performRemove() { // A simple assignment. foo = bar() -> bar(); Node parent = assignment.getParent(); Node last = assignment.getLastChild(); assignment.removeChild(last); parent.replaceChild(assignment, last); } @Override public Node getLValue() { return assignment.getFirstChild(); } @Override public Node getRValue() { return assignment.getLastChild(); } } /** * Represents member declarations using a object literal. * Example: var x = { e : function() { } }; */ static final class ObjectLiteralPropertyDefinition extends Definition { private final Node literal; private final Node name; private final Node value; ObjectLiteralPropertyDefinition(Node lit, Node name, Node value, boolean isExtern) { super(isExtern); this.literal = lit; this.name = name; this.value = value; } @Override public void performRemove() { literal.removeChild(name); } @Override public Node getLValue() { // TODO(user) revisit: object literal definitions are an example // of definitions whose LHS doesn't correspond to a node that // exists in the AST. We will have to change the return type of // getLValue sooner or later in order to provide this added // flexibility. switch (name.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING_KEY: // TODO(johnlenz): return a GETELEM for quoted strings. return IR.getprop( IR.objectlit(), IR.string(name.getString())); default: throw new IllegalStateException("unexpected"); } } @Override public Node getRValue() { return value; } } /** * Represents a VAR declaration with an assignment. */ static final class VarDefinition extends Definition { private final Node name; VarDefinition(Node node, boolean inExterns) { super(inExterns); Preconditions.checkArgument(NodeUtil.isVarDeclaration(node)); Preconditions.checkArgument(node.hasChildren(), "VAR Declaration of %sshould be assigned a value.", node.getString()); name = node; } @Override public void performRemove() { Node var = name.getParent(); Preconditions.checkState(var.getFirstChild() == var.getLastChild(), "AST should be normalized first"); Node parent = var.getParent(); Node rValue = name.removeFirstChild(); Preconditions.checkState(!parent.isFor()); parent.replaceChild(var, NodeUtil.newExpr(rValue)); } @Override public Node getLValue() { return name; } @Override public Node getRValue() { return name.getFirstChild(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MemoizedScopeCreator.java0000644000175000017500000000614112115204405027564 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; /** * Memoize a scope creator. * * This allows you to make multiple passes, without worrying about * the expense of generating Scope objects over and over again. * * On the other hand, you also have to be more aware of what your passes * are doing. Scopes are memoized stupidly, so if the underlying tree * changes, the scope may be out of sync. * * @author nicksantos@google.com (Nick Santos) */ class MemoizedScopeCreator implements ScopeCreator, StaticSymbolTable { private final Map scopes = Maps.newHashMap(); private final ScopeCreator delegate; /** * @param delegate The real source of Scope objects. */ MemoizedScopeCreator(ScopeCreator delegate) { this.delegate = delegate; } @Override public Iterable getReferences(Var var) { return ImmutableList.of(var); } @Override public Scope getScope(Var var) { return var.scope; } @Override public Iterable getAllSymbols() { List vars = Lists.newArrayList(); for (Scope s : scopes.values()) { Iterables.addAll(vars, s.getAllSymbols()); } return vars; } @Override public Scope createScope(Node n, Scope parent) { Scope scope = scopes.get(n); if (scope == null) { scope = delegate.createScope(n, parent); scopes.put(n, scope); } else { Preconditions.checkState(parent == scope.getParent()); } return scope; } Collection getAllMemoizedScopes() { return Collections.unmodifiableCollection(scopes.values()); } Scope getScopeIfMemoized(Node n) { return scopes.get(n); } /** * Removes all scopes with root nodes from a given script file. * * @param scriptName the name of the script file to remove nodes for. */ void removeScopesForScript(String scriptName) { for (Node scopeRoot : ImmutableSet.copyOf(scopes.keySet())) { if (scriptName.equals(scopeRoot.getSourceFileName())) { scopes.remove(scopeRoot); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AnalyzePrototypeProperties.java0000644000175000017500000006373412115204405031122 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.jscomp.graph.FixedPointGraphTraversal.EdgeCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; /** * Analyzes properties on prototypes. * * Uses a reference graph to analyze prototype properties. Each unique property * name is represented by a node in this graph. An edge from property A to * property B means that there's a GETPROP access of a property B on some * object inside of a method named A. * * Global functions are also represented by nodes in this graph, with * similar semantics. * */ class AnalyzePrototypeProperties implements CompilerPass { // Constants for symbol types, for easier readability. private final SymbolType PROPERTY = SymbolType.PROPERTY; private final SymbolType VAR = SymbolType.VAR; private final AbstractCompiler compiler; private final boolean canModifyExterns; private final boolean anchorUnusedVars; private final JSModuleGraph moduleGraph; private final JSModule firstModule; // Properties that are implicitly used as part of the JS language. private static final Set IMPLICITLY_USED_PROPERTIES = ImmutableSet.of("length", "toString", "valueOf"); // A graph where the nodes are property names or variable names, // and the edges signify the modules where the property is referenced. // For example, if we had the code: // // Foo.prototype.bar = function(x) { x.baz(); }; // in module 2.; // // then this would be represented in the graph by a node representing // "bar", a node representing "baz", and an edge between them representing // module #2. // // Similarly, if we had: // // var scotch = function(f) { return f.age(); }; // // then there would be a node for "scotch", a node for "age", and an edge // from scotch to age. private final LinkedDirectedGraph symbolGraph = LinkedDirectedGraph.createWithoutAnnotations(); // A dummy node for representing global references. private final NameInfo globalNode = new NameInfo("[global]"); // A dummy node for representing extern references. private final NameInfo externNode = new NameInfo("[extern]"); // A dummy node for representing all anonymous functions with no names. private final NameInfo anonymousNode = new NameInfo("[anonymous]"); // All the real NameInfo for prototype properties, hashed by the name // of the property that they represent. private final Map propertyNameInfo = Maps.newHashMap(); // All the NameInfo for global functions, hashed by the name of the // global variable that it's assigned to. private final Map varNameInfo = Maps.newHashMap(); /** * Creates a new pass for analyzing prototype properties. * @param compiler The compiler. * @param moduleGraph The graph for resolving module dependencies. May be * null if we don't care about module dependencies. * @param canModifyExterns If true, then we can move prototype * properties that are declared in the externs file. * @param anchorUnusedVars If true, then we must mark all vars as referenced, * even if they are never used. */ AnalyzePrototypeProperties(AbstractCompiler compiler, JSModuleGraph moduleGraph, boolean canModifyExterns, boolean anchorUnusedVars) { this.compiler = compiler; this.moduleGraph = moduleGraph; this.canModifyExterns = canModifyExterns; this.anchorUnusedVars = anchorUnusedVars; if (moduleGraph != null) { firstModule = moduleGraph.getRootModule(); } else { firstModule = null; } globalNode.markReference(null); externNode.markReference(null); symbolGraph.createNode(globalNode); symbolGraph.createNode(externNode); for (String property : IMPLICITLY_USED_PROPERTIES) { NameInfo nameInfo = getNameInfoForName(property, PROPERTY); if (moduleGraph == null) { symbolGraph.connect(externNode, null, nameInfo); } else { for (JSModule module : moduleGraph.getAllModules()) { symbolGraph.connect(externNode, module, nameInfo); } } } } @Override public void process(Node externRoot, Node root) { if (!canModifyExterns) { NodeTraversal.traverse(compiler, externRoot, new ProcessExternProperties()); } NodeTraversal.traverse(compiler, root, new ProcessProperties()); FixedPointGraphTraversal t = FixedPointGraphTraversal.newTraversal(new PropagateReferences()); t.computeFixedPoint(symbolGraph, Sets.newHashSet(externNode, globalNode)); } /** * Returns information on all prototype properties. */ public Collection getAllNameInfo() { List result = Lists.newArrayList(propertyNameInfo.values()); result.addAll(varNameInfo.values()); return result; } /** * Gets the name info for the property or variable of a given name, * and creates a new one if necessary. * * @param name The name of the symbol. * @param type The type of symbol. */ private NameInfo getNameInfoForName(String name, SymbolType type) { Map map = type == PROPERTY ? propertyNameInfo : varNameInfo; if (map.containsKey(name)) { return map.get(name); } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack symbolStack = new Stack(); @Override public void enterScope(NodeTraversal t) { Node n = t.getCurrentNode(); if (n.isFunction()) { String propName = getPrototypePropertyNameFromRValue(n); if (propName != null) { symbolStack.push( new NameContext( getNameInfoForName(propName, PROPERTY), t.getScope())); } else if (isGlobalFunctionDeclaration(t, n)) { Node parent = n.getParent(); String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push( new NameContext(getNameInfoForName(name, VAR), t.getScope())); } else { // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. symbolStack.push(new NameContext(anonymousNode, t.getScope())); } } else { Preconditions.checkState(t.inGlobalScope()); symbolStack.push(new NameContext(globalNode, t.getScope())); } } @Override public void exitScope(NodeTraversal t) { symbolStack.pop(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. String propName = processNonFunctionPrototypeAssign(n, parent); if (propName != null) { symbolStack.push( new NameContext( getNameInfoForName(propName, PROPERTY), null)); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { String propName = n.getFirstChild().getNext().getString(); if (n.isQualifiedName()) { if (propName.equals("prototype")) { if (processPrototypeRef(t, n)) { return; } } else if (compiler.getCodingConvention().isExported(propName)) { addGlobalUseOfSymbol(propName, t.getModule(), PROPERTY); return; } else { // Do not mark prototype prop assigns as a 'use' in the global scope. if (n.getParent().isAssign() && n.getNext() != null) { String rValueName = getPrototypePropertyNameFromRValue(n); if (rValueName != null) { return; } } } } addSymbolUse(propName, t.getModule(), PROPERTY); } else if (n.isObjectLit()) { // Make sure that we're not handling object literals being // assigned to a prototype, as in: // Foo.prototype = {bar: 3, baz: 5}; String lValueName = NodeUtil.getBestLValueName( NodeUtil.getBestLValue(n)); if (lValueName != null && lValueName.endsWith(".prototype")) { return; } // var x = {a: 1, b: 2} // should count as a use of property a and b. for (Node propNameNode = n.getFirstChild(); propNameNode != null; propNameNode = propNameNode.getNext()) { // May be STRING, GET, or SET, but NUMBER isn't interesting. if (!propNameNode.isQuotedString()) { addSymbolUse(propNameNode.getString(), t.getModule(), PROPERTY); } } } else if (n.isName()) { String name = n.getString(); Var var = t.getScope().getVar(name); if (var != null) { // Only process global functions. if (var.isGlobal()) { if (var.getInitialValue() != null && var.getInitialValue().isFunction()) { if (t.inGlobalScope()) { if (!processGlobalFunctionDeclaration(t, n, var)) { addGlobalUseOfSymbol(name, t.getModule(), VAR); } } else { addSymbolUse(name, t.getModule(), VAR); } } // If it is not a global, it might be accessing a local of the outer // scope. If that's the case the functions between the variable's // declaring scope and the variable reference scope cannot be moved. } else if (var.getScope() != t.getScope()){ for (int i = symbolStack.size() - 1; i >= 0; i--) { NameContext context = symbolStack.get(i); if (context.scope == var.getScope()) { break; } context.name.readClosureVariables = true; } } } } // Process prototype assignments to non-functions. if (processNonFunctionPrototypeAssign(n, parent) != null) { symbolStack.pop(); } } private void addSymbolUse(String name, JSModule module, SymbolType type) { NameInfo info = getNameInfoForName(name, type); NameInfo def = null; // Skip all anonymous nodes. We care only about symbols with names. for (int i = symbolStack.size() - 1; i >= 0; i--) { def = symbolStack.get(i).name; if (def != anonymousNode) { break; } } if (!def.equals(info)) { symbolGraph.connect(def, module, info); } } /** * If this is a non-function prototype assign, return the prop name. * Otherwise, return null. */ private String processNonFunctionPrototypeAssign(Node n, Node parent) { if (isAssignRValue(n, parent) && !n.isFunction()) { return getPrototypePropertyNameFromRValue(n); } return null; } /** * Determines whether {@code n} is the FUNCTION node in a global function * declaration. */ private boolean isGlobalFunctionDeclaration(NodeTraversal t, Node n) { // Make sure we're either in the global scope, or the function // we're looking at is the root of the current local scope. Scope s = t.getScope(); if (!(s.isGlobal() || s.getDepth() == 1 && s.getRootNode() == n)) { return false; } return NodeUtil.isFunctionDeclaration(n) || n.isFunction() && n.getParent().isName(); } /** * Returns true if this is the r-value of an assignment. */ private boolean isAssignRValue(Node n, Node parent) { return parent != null && parent.isAssign() && parent.getFirstChild() != n; } /** * Returns the name of a prototype property being assigned to this r-value. * * Returns null if this is not the R-value of a prototype property, or if * the R-value is used in multiple expressions (i.e., if there's * a prototype property assignment in a more complex expression). */ private String getPrototypePropertyNameFromRValue(Node rValue) { Node lValue = NodeUtil.getBestLValue(rValue); if (lValue == null || lValue.getParent() == null || lValue.getParent().getParent() == null || !(NodeUtil.isObjectLitKey(lValue) || NodeUtil.isExprAssign(lValue.getParent().getParent()))) { return null; } String lValueName = NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue)); if (lValueName == null) { return null; } int lastDot = lValueName.lastIndexOf('.'); if (lastDot == -1) { return null; } String firstPart = lValueName.substring(0, lastDot); if (!firstPart.endsWith(".prototype")) { return null; } return lValueName.substring(lastDot + 1); } /** * Processes a NAME node to see if it's a global function declaration. * If it is, record it and return true. Otherwise, return false. */ private boolean processGlobalFunctionDeclaration(NodeTraversal t, Node nameNode, Var v) { Node firstChild = nameNode.getFirstChild(); Node parent = nameNode.getParent(); if (// Check for a named FUNCTION. isGlobalFunctionDeclaration(t, parent) || // Check for a VAR declaration. firstChild != null && isGlobalFunctionDeclaration(t, firstChild)) { String name = nameNode.getString(); getNameInfoForName(name, VAR).getDeclarations().add( new GlobalFunction(nameNode, v, t.getModule())); // If the function name is exported, we should create an edge here // so that it's never removed. if (compiler.getCodingConvention().isExported(name) || anchorUnusedVars) { addGlobalUseOfSymbol(name, t.getModule(), VAR); } return true; } return false; } /** * Processes the GETPROP of prototype, which can either be under * another GETPROP (in the case of Foo.prototype.bar), or can be * under an assignment (in the case of Foo.prototype = ...). * @return True if a declaration was added. */ private boolean processPrototypeRef(NodeTraversal t, Node ref) { Node root = NodeUtil.getRootOfQualifiedName(ref); Node n = ref.getParent(); switch (n.getType()) { // Foo.prototype.getBar = function() { ... } case Token.GETPROP: Node dest = n.getFirstChild().getNext(); Node parent = n.getParent(); Node grandParent = parent.getParent(); if (dest.isString() && NodeUtil.isExprAssign(grandParent) && NodeUtil.isVarOrSimpleAssignLhs(n, parent)) { String name = dest.getString(); Property prop = new AssignmentProperty( grandParent, maybeGetVar(t, root), t.getModule()); getNameInfoForName(name, PROPERTY).getDeclarations().add(prop); return true; } break; // Foo.prototype = { "getBar" : function() { ... } } case Token.ASSIGN: Node map = n.getFirstChild().getNext(); if (map.isObjectLit()) { for (Node key = map.getFirstChild(); key != null; key = key.getNext()) { // May be STRING, GETTER_DEF, or SETTER_DEF, String name = key.getString(); Property prop = new LiteralProperty( key, key.getFirstChild(), map, n, maybeGetVar(t, root), t.getModule()); getNameInfoForName(name, PROPERTY).getDeclarations().add(prop); } return true; } break; } return false; } private Var maybeGetVar(NodeTraversal t, Node maybeName) { return maybeName.isName() ? t.getScope().getVar(maybeName.getString()) : null; } private void addGlobalUseOfSymbol(String name, JSModule module, SymbolType type) { symbolGraph.connect(globalNode, module, getNameInfoForName(name, type)); } } private class ProcessExternProperties extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { symbolGraph.connect(externNode, firstModule, getNameInfoForName(n.getLastChild().getString(), PROPERTY)); } } } private class PropagateReferences implements EdgeCallback { @Override public boolean traverseEdge(NameInfo start, JSModule edge, NameInfo dest) { if (start.isReferenced()) { JSModule startModule = start.getDeepestCommonModuleRef(); if (startModule != null && moduleGraph.dependsOn(startModule, edge)) { return dest.markReference(startModule); } else { return dest.markReference(edge); } } return false; } } // TODO(user): We can use DefinitionsRemover and UseSite here. Then all // we need to do is call getDefinition() and we'll magically know everything // about the definition. /** * The declaration of an abstract symbol. */ interface Symbol { /** * Remove the declaration from the AST. */ void remove(); /** * The variable for the root of this symbol. */ Var getRootVar(); /** * Returns the module where this appears. */ JSModule getModule(); } private enum SymbolType { PROPERTY, VAR; } /** * A function initialized as a VAR statement or a function declaration. */ class GlobalFunction implements Symbol { private final Node nameNode; private final Var var; private final JSModule module; GlobalFunction(Node nameNode, Var var, JSModule module) { Node parent = nameNode.getParent(); Preconditions.checkState( parent.isVar() || NodeUtil.isFunctionDeclaration(parent)); this.nameNode = nameNode; this.var = var; this.module = module; } @Override public Var getRootVar() { return var; } @Override public void remove() { Node parent = nameNode.getParent(); if (parent.isFunction() || parent.hasOneChild()) { NodeUtil.removeChild(parent.getParent(), parent); } else { Preconditions.checkState(parent.isVar()); parent.removeChild(nameNode); } } @Override public JSModule getModule() { return module; } public Node getFunctionNode() { Node parent = nameNode.getParent(); if (parent.isFunction()) { return parent; } else { // we are the name of a var node, so the function is name's second child return nameNode.getChildAtIndex(1); } } } /** * Since there are two ways of assigning properties to prototypes, we hide * then behind this interface so they can both be removed regardless of type. */ interface Property extends Symbol { /** Returns the GETPROP node that refers to the prototype. */ Node getPrototype(); /** Returns the value of this property. */ Node getValue(); } /** * Properties created via EXPR assignment: * *
      function Foo() { ... };
         * Foo.prototype.bar = function() { ... };
      */ static class AssignmentProperty implements Property { private final Node exprNode; private final Var rootVar; private final JSModule module; /** * @param node An EXPR node. */ AssignmentProperty(Node node, Var rootVar, JSModule module) { this.exprNode = node; this.rootVar = rootVar; this.module = module; } @Override public Var getRootVar() { return rootVar; } @Override public void remove() { NodeUtil.removeChild(exprNode.getParent(), exprNode); } @Override public Node getPrototype() { return getAssignNode().getFirstChild().getFirstChild(); } @Override public Node getValue() { return getAssignNode().getLastChild(); } private Node getAssignNode() { return exprNode.getFirstChild(); } @Override public JSModule getModule() { return module; } } /** * Properties created via object literals: * *
      function Foo() { ... };
         * Foo.prototype = {bar: function() { ... };
      */ static class LiteralProperty implements Property { private final Node key; private final Node value; private final Node map; private final Node assign; private final Var rootVar; private final JSModule module; LiteralProperty(Node key, Node value, Node map, Node assign, Var rootVar, JSModule module) { this.key = key; this.value = value; this.map = map; this.assign = assign; this.rootVar = rootVar; this.module = module; } @Override public Var getRootVar() { return rootVar; } @Override public void remove() { map.removeChild(key); } @Override public Node getPrototype() { return assign.getFirstChild(); } @Override public Node getValue() { return value; } @Override public JSModule getModule() { return module; } } /** * The context of the current name. This includes the NameInfo and the scope * if it is a scope defining name (function). */ private class NameContext { final NameInfo name; // If this is a function context, then scope will be the scope of the // corresponding function. Otherwise, it will be null. final Scope scope; NameContext(NameInfo name, Scope scope) { this.name = name; this.scope = scope; } } /** * Information on all properties or global variables of a given name. */ class NameInfo { final String name; private boolean referenced = false; private final Deque declarations = new ArrayDeque(); private JSModule deepestCommonModuleRef = null; // True if this property is a function that reads a variable from an // outer scope which isn't the global scope. private boolean readClosureVariables = false; /** * Constructs a new NameInfo. * @param name The name of the property that this represents. May be null * to signify dummy nodes in the property graph. */ NameInfo(String name) { this.name = name; } @Override public String toString() { return name; } /** Determines whether we've marked a reference to this property name. */ boolean isReferenced() { return referenced; } /** Determines whether it reads a closure variable. */ boolean readsClosureVariables() { return readClosureVariables; } /** * Mark a reference in a given module to this property name, and record * the deepest common module reference. * @param module The module where it was referenced. * @return Whether the name info has changed. */ boolean markReference(JSModule module) { boolean hasChanged = false; if (!referenced) { referenced = true; hasChanged = true; } if (moduleGraph != null) { JSModule originalDeepestCommon = deepestCommonModuleRef; if (deepestCommonModuleRef == null) { deepestCommonModuleRef = module; } else { deepestCommonModuleRef = moduleGraph.getDeepestCommonDependencyInclusive( deepestCommonModuleRef, module); } if (originalDeepestCommon != deepestCommonModuleRef) { hasChanged = true; } } return hasChanged; } /** * Returns the deepest common module of all the references to this * property. */ JSModule getDeepestCommonModuleRef() { return deepestCommonModuleRef; } /** * Returns a mutable collection of all the prototype property declarations * of this property name. */ Deque getDeclarations() { return declarations; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ShadowVariables.java0000644000175000017500000002321712115204405026562 0ustar apoapo/* * Copyright 2011 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.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.RenameVars.Assignment; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.SortedSet; /** * Tries to compute a list of variables that can shadow a variable in the * outer scope. * * For example: * * * var a = function() { * var b = getB(); * b(); * return function(y) {}; * }; * * * Normally, b would be mapped to variable L0, y would be L1. * * Instead we are going to make y shadows L0 in hope of using less variables * and reusing frequently used local names. * */ class ShadowVariables implements CompilerPass { // Keep a map of Upward Referencing name nodes of each scope. // A name is upward referencing name of a scope if: // // 1) It refers to (or defines) a name that is defined in the current // scope or any scope above the current scope that isn't the // global scope. // // 2) It is a upward referencing name of a child scope of this scope. // // Example: // var x; var y; function foo(a) { function bar(b) { x, a } } // The upward referencing names in scope 'foo' is bar, b, x and a; // The key to this map is the root node of the scope. // // We can see that for any variable x in the current scope, we can shadow // a variable y in an outer scope given that y is not a upward referencing // name of the current scope. // TODO(user): Maps scope to string instead of Node to string. // Make sure of scope memorization to minimize scope creation cost. private final Multimap scopeUpRefMap = HashMultimap.create(); // Maps all local Scope.Var to all of its referencing NAME node // in any scope. private final Multimap varToNameUsage = HashMultimap.create(); private final AbstractCompiler compiler; // All the information used for renaming. private final SortedSet varsByFrequency; private final Map assignments; private final Map oldPseudoNameMap; private final Map deltaPseudoNameMap; /** * @param assignments Map of old variable names to its assignment Objects. * @param varsByFrequency Sorted variable assignments by Frequency. * @param pseudoNameMap The current pseudo name map so this pass can update * it accordingly. */ ShadowVariables( AbstractCompiler compiler, Map assignments, SortedSet varsByFrequency, Map pseudoNameMap) { this.compiler = compiler; this.assignments = assignments; this.varsByFrequency = varsByFrequency; this.oldPseudoNameMap = pseudoNameMap; this.deltaPseudoNameMap = Maps.newLinkedHashMap(); } @Override public void process(Node externs, Node root) { // The algorithm is divided into two stages: // // 1. Information gathering (variable usage, upward referencing) // // 2. Tries to find shadows for each variables, updates the // variable usage frequency map. // // 3. Updates the pseudo naming map if needed. NodeTraversal.traverse(compiler, root, new GatherReferenceInfo()); NodeTraversal.traverse(compiler, root, new DoShadowVariables()); if (oldPseudoNameMap != null) { oldPseudoNameMap.putAll(deltaPseudoNameMap); } } private class GatherReferenceInfo extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // Skipping over non-name nodes and empty function names. if (!NodeUtil.isReferenceName(n)) { return; } // We focus on shadowing local variables as their name occurs much more // than global names. // TODO(user): Alternatively, we could experiment with using a local // name to shadow a global variable. if (t.inGlobalScope()) { return; } Var var = t.getScope().getVar(n.getString()); if (var == null) { // extern name or undefined name. return; } if (var.getScope().isGlobal()) { // We will not shadow a global variable name. return; } // Using the definition of upward referencing, fill in the map. if (var.getScope() != t.getScope()) { for (Scope s = t.getScope(); s != var.getScope() && s.isLocal(); s = s.getParent()) { scopeUpRefMap.put(s.getRootNode(), var.name); } } if (var.getScope() == t.getScope()) { scopeUpRefMap.put(t.getScopeRoot(), var.name); } // Find in the usage map that tracks a var and all of its usage. varToNameUsage.put(var, n); } } private class DoShadowVariables extends AbstractPostOrderCallback implements ScopedCallback { @Override public void enterScope(NodeTraversal t) { Scope s = t.getScope(); if (!s.isLocal()) { return; } // Since we don't shadow global, there is nothing to be done in the // first immediate local scope as well. if (s.getParent().isGlobal()) { return; } for (Iterator vars = s.getVars(); vars.hasNext();) { Var var = vars.next(); // Don't shadow variables that is bleed-out to fix an IE bug. if (var.isBleedingFunction()) { continue; } // Don't shadow an exported local. if (compiler.getCodingConvention().isExported(var.name, s.isLocal())) { continue; } // Try to look for the best shadow for the current candidate. Assignment bestShadow = findBestShadow(s); if (bestShadow == null) { continue; } // The name assignment being shadowed. Assignment localAssignment = assignments.get(var.getName()); // Only shadow if this increases the number of occurrences of the // shadowed variable. if (bestShadow.count < localAssignment.count) { continue; // Hope the next local variable would have a smaller count. } doShadow(localAssignment, bestShadow, var); if (oldPseudoNameMap != null) { String targetPseudoName = oldPseudoNameMap.get(s.getVar(bestShadow.oldName).nameNode); for (Node use : varToNameUsage.get(var)) { deltaPseudoNameMap.put(use, targetPseudoName); } } } } @Override public void exitScope(NodeTraversal t) {} @Override public void visit(NodeTraversal t, Node n, Node parent) {} /** * @returns An assignment that can be used as a shadow for a local variable * in the scope defined by curScopeRoot. */ private Assignment findBestShadow(Scope curScope) { // Search for the candidate starting from the most used local. for (Assignment assignment : varsByFrequency) { if (assignment.oldName.startsWith(RenameVars.LOCAL_VAR_PREFIX)) { if (!scopeUpRefMap.get(curScope.getRootNode()).contains( assignment.oldName)) { if (curScope.isDeclared(assignment.oldName, true)) { return assignment; } } } } return null; } private void doShadow(Assignment original, Assignment toShadow, Var var) { Scope s = var.getScope(); // We are now shadowing 'bestShadow' with localAssignment. // All of the reference NAME node of this variable. Collection references = varToNameUsage.get(var); // First remove both assignments from the sorted list since they need // to be re-sorted. varsByFrequency.remove(original); varsByFrequency.remove(toShadow); // Adjust the count offset by the inner scope variable. original.count -= references.size(); toShadow.count += references.size(); // Add it back to the sorted list after re-adjustment. varsByFrequency.add(original); varsByFrequency.add(toShadow); // This is an important step. If variable L7 is going to be renamed to // L1, by definition of upward referencing, The name L1 is now in the // set of upward referencing names of the current scope up to the // declaring scope of the best shadow variable. Var shadowed = s.getVar(toShadow.oldName); if (shadowed != null) { for (Scope curScope = s; curScope != shadowed.scope; curScope = curScope.getParent()) { scopeUpRefMap.put(curScope.getRootNode(), toShadow.oldName); } } // Mark all the references as shadowed. for (Node n : references) { n.setString(toShadow.oldName); Node cur = n; while(cur != s.getRootNode()) { cur = cur.getParent(); if (cur.isFunction()) { scopeUpRefMap.put(cur, toShadow.oldName); } } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SymbolTable.java0000644000175000017500000015020012115204405025712 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Marker; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.SourcePosition; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.SimpleReference; import com.google.javascript.rhino.jstype.SimpleSlot; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.StaticSymbolTable; import com.google.javascript.rhino.jstype.UnionType; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.logging.Logger; import javax.annotation.Nullable; /** * A symbol table for people that want to use Closure Compiler as an indexer. * * Contains an index of all the symbols in the code within a compilation * job. The API is designed for people who want to visit all the symbols, rather * than people who want to lookup a specific symbol by a certain key. * * We can use this to combine different types of symbol tables. For example, * one class might have a {@code StaticSymbolTable} of all variable references, * and another class might have a {@code StaticSymbolTable} of all type names * in JSDoc comments. This class allows you to combine them into a unified * index. * * Most passes build their own "partial" symbol table that implements the same * interface (StaticSymbolTable, StaticSlot, and friends). Individual compiler * passes usually need more or less metadata about the certainty of symbol * information. Building a complete symbol table with all the necessary metadata * for all passes would be too slow. However, as long as these "partial" symbol * tables implement the proper interfaces, we should be able to add them to this * symbol table to make it more complete. * * If clients want fast lookup, they should build their own wrapper around * this symbol table that indexes symbols or references by the desired lookup * key. * * By design, when this symbol table creates symbols for types, it tries * to mimic the symbol table you would get in an OO language. For example, * the "type Foo" and "the constructor that creates objects of type Foo" * are the same symbol. The types of "Foo.prototype" and "new Foo()" also * have the same symbol. Although JSCompiler internally treats these as * distinct symbols, we assume that most clients will not care about * the distinction. * * @see #addSymbolsFrom For more information on how to write plugins for this * symbol table. * * @author nicksantos@google.com (Nick Santos) */ public final class SymbolTable implements StaticSymbolTable { private static final Logger logger = Logger.getLogger(SymbolTable.class.getName()); /** * The name we use for the JavaScript built-in Global object. It's * anonymous in JavaScript, so we have to give it an invalid identifier * to avoid conflicts with user-defined property names. */ public static final String GLOBAL_THIS = "*global*"; /** * All symbols in the program, uniquely identified by the node where * they're declared and their name. */ private final Table symbols = HashBasedTable.create(); /** * All syntactic scopes in the program, uniquely identified by the node where * they're declared. */ private final Map scopes = Maps.newLinkedHashMap(); /** * All JSDocInfo in the program. */ private final List docInfos = Lists.newArrayList(); private SymbolScope globalScope = null; private final JSTypeRegistry registry; /** * Clients should get a symbol table by asking the compiler at the end * of a compilation job. */ SymbolTable(JSTypeRegistry registry) { this.registry = registry; } @Override public Iterable getReferences(Symbol symbol) { return Collections.unmodifiableCollection(symbol.references.values()); } public List getReferenceList(Symbol symbol) { return ImmutableList.copyOf(symbol.references.values()); } @Override public Iterable getAllSymbols() { return Collections.unmodifiableCollection(symbols.values()); } /** * Get the symbols in their natural ordering. * Always returns a mutable list. */ public List getAllSymbolsSorted() { List sortedSymbols = Lists.newArrayList(symbols.values()); Collections.sort(sortedSymbols, getNaturalSymbolOrdering()); return sortedSymbols; } /** * Gets the 'natural' ordering of symbols. * * Right now, we only guarantee that symbols in the global scope will come * before symbols in local scopes. After that, the order is deterministic but * undefined. */ public Ordering getNaturalSymbolOrdering() { return SYMBOL_ORDERING; } @Override public SymbolScope getScope(Symbol slot) { return slot.scope; } public Collection getAllJSDocInfo() { return Collections.unmodifiableList(docInfos); } /** * Declare a symbol after the main symbol table was constructed. * Throws an exception if you try to declare a symbol twice. */ public Symbol declareInferredSymbol( SymbolScope scope, String name, Node declNode) { return declareSymbol(name, null, true, scope, declNode, null); } /** * Gets the scope that contains the given node. * If {@code n} is a function name, we return the scope that contains the * function, not the function itself. */ public SymbolScope getEnclosingScope(Node n) { Node current = n.getParent(); if (n.isName() && n.getParent().isFunction()) { current = current.getParent(); } for (; current != null; current = current.getParent()) { if (scopes.containsKey(current)) { return scopes.get(current); } } return null; } /** * If {@code sym} is a function, try to find a Symbol for * a parameter with the given name. * * Returns null if we couldn't find one. * * Notice that this just makes a best effort, and may not be able * to find parameters for non-conventional function definitions. * For example, we would not be able to find "y" in this code: * * var x = x() ? function(y) {} : function(y) {}; * */ public Symbol getParameterInFunction(Symbol sym, String paramName) { SymbolScope scope = getScopeInFunction(sym); if (scope != null) { Symbol param = scope.getSlot(paramName); if (param != null && param.scope == scope) { return param; } } return null; } private SymbolScope getScopeInFunction(Symbol sym) { FunctionType type = sym.getFunctionType(); if (type == null) { return null; } Node functionNode = type.getSource(); if (functionNode == null) { return null; } return scopes.get(functionNode); } /** * All local scopes are associated with a function, and some functions * are associated with a symbol. Returns the symbol associated with the given * scope. */ public Symbol getSymbolForScope(SymbolScope scope) { if (scope.getSymbolForScope() == null) { scope.setSymbolForScope(findSymbolForScope(scope)); } return scope.getSymbolForScope(); } /** * Find the symbol associated with the given scope. * Notice that we won't always be able to figure out this association * dynamically, so sometimes we'll just create the association when we * create the scope. */ private Symbol findSymbolForScope(SymbolScope scope) { Node rootNode = scope.getRootNode(); if (rootNode.getParent() == null) { return globalScope.getSlot(GLOBAL_THIS); } if (!rootNode.isFunction()) { return null; } String name = NodeUtil.getBestLValueName( NodeUtil.getBestLValue(rootNode)); return name == null ? null : scope.getParentScope().getQualifiedSlot(name); } /** * Get all symbols associated with the type of the given symbol. * * For example, given a variable x declared as * /* @type {Array|Date} / * var x = f(); * this will return the constructors for Array and Date. */ public Iterable getAllSymbolsForTypeOf(Symbol sym) { return getAllSymbolsForType(sym.getType()); } /** * Returns the global scope. */ public SymbolScope getGlobalScope() { return globalScope; } /** * Gets the symbol for the given constructor or interface. */ public Symbol getSymbolDeclaredBy(FunctionType fn) { Preconditions.checkState(fn.isConstructor() || fn.isInterface()); ObjectType instanceType = fn.getInstanceType(); return getSymbolForName(fn.getSource(), instanceType.getReferenceName()); } /** * Gets the symbol for the given enum. */ public Symbol getSymbolDeclaredBy(EnumType enumType) { return getSymbolForName(null, enumType.getElementsType().getReferenceName()); } /** * Gets the symbol for the prototype if this is the symbol for a constructor * or interface. */ public Symbol getSymbolForInstancesOf(Symbol sym) { FunctionType fn = sym.getFunctionType(); if (fn != null && fn.isNominalConstructor()) { return getSymbolForInstancesOf(fn); } return null; } /** * Gets the symbol for the prototype of the given constructor or interface. */ public Symbol getSymbolForInstancesOf(FunctionType fn) { Preconditions.checkState(fn.isConstructor() || fn.isInterface()); ObjectType pType = fn.getPrototype(); return getSymbolForName(fn.getSource(), pType.getReferenceName()); } private Symbol getSymbolForName(Node source, String name) { if (name == null || globalScope == null) { return null; } SymbolScope scope = source == null ? globalScope : getEnclosingScope(source); // scope will sometimes be null if one of the type-stripping passes // was run, and the symbol isn't in the AST anymore. return scope == null ? null : scope.getQualifiedSlot(name); } /** * Gets all symbols associated with the given type. * For union types, this may be multiple symbols. * For instance types, this will return the constructor of * that instance. */ public List getAllSymbolsForType(JSType type) { if (type == null) { return ImmutableList.of(); } UnionType unionType = type.toMaybeUnionType(); if (unionType != null) { List result = Lists.newArrayListWithExpectedSize(2); for (JSType alt : unionType.getAlternates()) { // Our type system never has nested unions. Symbol altSym = getSymbolForTypeHelper(alt, true); if (altSym != null) { result.add(altSym); } } return result; } Symbol result = getSymbolForTypeHelper(type, true); return result == null ? ImmutableList.of() : ImmutableList.of(result); } /** * Gets all symbols associated with the given type. * If there is more that one symbol associated with the given type, * return null. * @param type The type. * @param linkToCtor If true, we should link instance types back * to their constructor function. If false, we should link * instance types back to their prototype. See the comments * at the top of this file for more information on how * our internal type system is more granular than Symbols. */ private Symbol getSymbolForTypeHelper(JSType type, boolean linkToCtor) { if (type == null) { return null; } if (type.isGlobalThisType()) { return globalScope.getSlot(GLOBAL_THIS); } else if (type.isNominalConstructor()) { return linkToCtor ? globalScope.getSlot("Function") : getSymbolDeclaredBy(type.toMaybeFunctionType()); } else if (type.isFunctionPrototypeType()) { FunctionType ownerFn = ((ObjectType) type).getOwnerFunction(); if (!ownerFn.isConstructor() && !ownerFn.isInterface()) { return null; } return linkToCtor ? getSymbolDeclaredBy(ownerFn) : getSymbolForInstancesOf(ownerFn); } else if (type.isInstanceType()) { FunctionType ownerFn = ((ObjectType) type).getConstructor(); return linkToCtor ? getSymbolDeclaredBy(ownerFn) : getSymbolForInstancesOf(ownerFn); } else if (type.isFunctionType()) { return linkToCtor ? globalScope.getSlot("Function") : globalScope.getQualifiedSlot("Function.prototype"); } else if (type.autoboxesTo() != null) { return getSymbolForTypeHelper(type.autoboxesTo(), linkToCtor); } else { return null; } } public String toDebugString() { StringBuilder builder = new StringBuilder(); for (Symbol symbol : getAllSymbols()) { toDebugString(builder, symbol); } return builder.toString(); } private void toDebugString(StringBuilder builder, Symbol symbol) { SymbolScope scope = symbol.scope; if (scope.isGlobalScope()) { builder.append( String.format("'%s' : in global scope:\n", symbol.getName())); } else if (scope.getRootNode() != null) { builder.append( String.format("'%s' : in scope %s:%d\n", symbol.getName(), scope.getRootNode().getSourceFileName(), scope.getRootNode().getLineno())); } else if (scope.getSymbolForScope() != null) { builder.append( String.format("'%s' : in scope %s\n", symbol.getName(), scope.getSymbolForScope().getName())); } else { builder.append( String.format("'%s' : in unknown scope\n", symbol.getName())); } int refCount = 0; for (Reference ref : getReferences(symbol)) { builder.append( String.format(" Ref %d: %s:%d\n", refCount, ref.getNode().getSourceFileName(), ref.getNode().getLineno())); refCount++; } } /** * Make sure all the given scopes in {@code otherSymbolTable} * are in this symbol table. */ > void addScopes(Collection scopes) { for (S scope : scopes) { createScopeFrom(scope); } } /** Finds all the scopes and adds them to this symbol table. */ void findScopes(AbstractCompiler compiler, Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), new NodeTraversal.AbstractScopedCallback() { @Override public void enterScope(NodeTraversal t) { createScopeFrom(t.getScope()); } @Override public void visit(NodeTraversal t, Node n, Node p) {} }); } /** Gets all the scopes in this symbol table. */ public Collection getAllScopes() { return Collections.unmodifiableCollection(scopes.values()); } /** * Finds anonymous functions in local scopes, and gives them names * and symbols. They will show up as local variables with names * "function%0", "function%1", etc. */ public void addAnonymousFunctions() { TreeSet scopes = Sets.newTreeSet(LEXICAL_SCOPE_ORDERING); for (SymbolScope scope : getAllScopes()) { if (scope.isLexicalScope()) { scopes.add(scope); } } for (SymbolScope scope : scopes) { addAnonymousFunctionsInScope(scope); } } private void addAnonymousFunctionsInScope(SymbolScope scope) { Symbol sym = getSymbolForScope(scope); if (sym == null) { // JSCompiler has no symbol for this scope. Check to see if it's a // local function. If it is, give it a name. if (scope.isLexicalScope() && !scope.isGlobalScope() && scope.getRootNode() != null && !scope.getRootNode().isFromExterns() && scope.getParentScope() != null) { SymbolScope parent = scope.getParentScope(); int count = parent.innerAnonFunctionsWithNames++; String innerName = "function%" + count; scope.setSymbolForScope( declareInferredSymbol( parent, innerName, scope.getRootNode())); } } } /** * Make sure all the symbols and references in {@code otherSymbolTable} * are in this symbol table. * * Uniqueness of symbols and references is determined by the associated * node. * * If multiple symbol tables are mixed in, we do not check for consistency * between symbol tables. The first symbol we see dictates the type * information for that symbol. */ , R extends StaticReference> void addSymbolsFrom(StaticSymbolTable otherSymbolTable) { for (S otherSymbol : otherSymbolTable.getAllSymbols()) { String name = otherSymbol.getName(); SymbolScope myScope = createScopeFrom( otherSymbolTable.getScope(otherSymbol)); StaticReference decl = findBestDeclToAdd(otherSymbolTable, otherSymbol); Symbol mySymbol = null; if (decl != null) { Node declNode = decl.getNode(); // If we have a declaration node, we can ensure the symbol is declared. mySymbol = isAnySymbolDeclared(name, declNode, myScope); if (mySymbol == null) { mySymbol = copySymbolTo(otherSymbol, declNode, myScope); } } else { // If we don't have a declaration node, we won't be able to declare // a symbol in this symbol table. But we may be able to salvage the // references if we already have a symbol. mySymbol = myScope.getOwnSlot(name); } if (mySymbol != null) { for (R otherRef : otherSymbolTable.getReferences(otherSymbol)) { if (isGoodRefToAdd(otherRef)) { mySymbol.defineReferenceAt(otherRef.getNode()); } } } } } /** * Checks if any symbol is already declared at the given node and scope * for the given name. If so, returns it. */ private Symbol isAnySymbolDeclared( String name, Node declNode, SymbolScope scope) { Symbol sym = symbols.get(declNode, name); if (sym == null) { // Sometimes, our symbol tables will disagree on where the // declaration node should be. In the rare case where this happens, // trust the existing symbol. // See SymbolTableTest#testDeclarationDisagreement. return scope.ownSymbols.get(name); } return sym; } /** Helper for addSymbolsFrom, to determine the best declaration spot. */ private , R extends StaticReference> StaticReference findBestDeclToAdd( StaticSymbolTable otherSymbolTable, S slot) { StaticReference decl = slot.getDeclaration(); if (isGoodRefToAdd(decl)) { return decl; } for (R ref : otherSymbolTable.getReferences(slot)) { if (isGoodRefToAdd(ref)) { return ref; } } return null; } /** * Helper for addSymbolsFrom, to determine whether a reference is * acceptable. A reference must be in the normal source tree. */ private boolean isGoodRefToAdd(@Nullable StaticReference ref) { return ref != null && ref.getNode() != null && ref.getNode().getStaticSourceFile() != null && !Compiler.SYNTHETIC_EXTERNS.equals( ref.getNode().getStaticSourceFile().getName()); } private Symbol copySymbolTo(StaticSlot sym, SymbolScope scope) { return copySymbolTo(sym, sym.getDeclaration().getNode(), scope); } private Symbol copySymbolTo( StaticSlot sym, Node declNode, SymbolScope scope) { // All symbols must have declaration nodes. Preconditions.checkNotNull(declNode); return declareSymbol( sym.getName(), sym.getType(), sym.isTypeInferred(), scope, declNode, sym.getJSDocInfo()); } private Symbol addSymbol( String name, JSType type, boolean inferred, SymbolScope scope, Node declNode) { Symbol symbol = new Symbol(name, type, inferred, scope); Symbol replacedSymbol = symbols.put(declNode, name, symbol); Preconditions.checkState( replacedSymbol == null, "Found duplicate symbol %s in global index. Type %s", name, type); replacedSymbol = scope.ownSymbols.put(name, symbol); Preconditions.checkState( replacedSymbol == null, "Found duplicate symbol %s in its scope. Type %s", name, type); return symbol; } private Symbol declareSymbol( String name, JSType type, boolean inferred, SymbolScope scope, Node declNode, JSDocInfo info) { Symbol symbol = addSymbol(name, type, inferred, scope, declNode); symbol.setJSDocInfo(info); symbol.setDeclaration(symbol.defineReferenceAt(declNode)); return symbol; } private void removeSymbol(Symbol s) { SymbolScope scope = getScope(s); if (scope.ownSymbols.remove(s.getName()) != s) { throw new IllegalStateException("Symbol not found in scope " + s); } if (symbols.remove(s.getDeclaration().getNode(), s.getName()) != s) { throw new IllegalStateException("Symbol not found in table " + s); } } /** * Not all symbol tables record references to "namespace" objects. * For example, if you have: * goog.dom.DomHelper = function() {}; * The symbol table may not record that as a reference to "goog.dom", * because that would be redundant. */ void fillNamespaceReferences() { for (Symbol symbol : getAllSymbolsSorted()) { String qName = symbol.getName(); int rootIndex = qName.indexOf('.'); if (rootIndex == -1) { continue; } Symbol root = symbol.scope.getQualifiedSlot( qName.substring(0, rootIndex)); if (root == null) { // In theory, this should never happen, but we fail quietly anyway // just to be safe. continue; } for (Reference ref : getReferences(symbol)) { Node currentNode = ref.getNode(); if (!currentNode.isQualifiedName()) { continue; } while (currentNode.isGetProp()) { currentNode = currentNode.getFirstChild(); String name = currentNode.getQualifiedName(); if (name != null) { Symbol namespace = isAnySymbolDeclared(name, currentNode, root.scope); if (namespace == null) { namespace = root.scope.getQualifiedSlot(name); } if (namespace == null && root.scope.isGlobalScope()) { namespace = declareSymbol(name, registry.getNativeType(JSTypeNative.UNKNOWN_TYPE), true, root.scope, currentNode, null /* JsDoc info */); } if (namespace != null) { namespace.defineReferenceAt(currentNode); } } } } } } void fillPropertyScopes() { // Collect all object symbols. List types = Lists.newArrayList(); // Create a property scope for each named type and each anonymous object, // and populate it with that object's properties. // // We notably don't want to create a property scope for 'x' in // var x = new Foo(); // where x is just an instance of another type. for (Symbol sym : getAllSymbols()) { if (needsPropertyScope(sym)) { types.add(sym); } } // The order of operations here is significant. // // When we add properties to Foo, we'll remove Foo.prototype from // the symbol table and replace it with a fresh symbol in Foo's // property scope. So the symbol for Foo.prototype in // {@code instances} will be stale. // // To prevent this, we sort the list by the reverse of the // default symbol order, which will do the right thing. Collections.sort(types, Collections.reverseOrder(getNaturalSymbolOrdering())); for (Symbol s : types) { createPropertyScopeFor(s); } pruneOrphanedNames(); } private boolean needsPropertyScope(Symbol sym) { ObjectType type = ObjectType.cast(sym.getType()); if (type == null) { return false; } // Anonymous objects if (type.getReferenceName() == null) { return true; } // Constructors/prototypes // Should this check for // (type.isNominalConstructor() || type.isFunctionPrototypeType()) // ? if (sym.getName().equals(type.getReferenceName())) { return true; } // Enums if (type.isEnumType() && sym.getName().equals( type.toMaybeEnumType().getElementsType().getReferenceName())) { return true; } return false; } /** * Removes symbols where the namespace they're on has been removed. * * After filling property scopes, we may have two symbols represented * in different ways. For example, "A.superClass_.foo" and B.prototype.foo". * * This resolves that ambiguity by pruning the duplicates. * If we have a lexical symbol with a constructor in its property * chain, then we assume there's also a property path to this symbol. * In other words, we can remove "A.superClass_.foo" because it's rooted * at "A", and we built a property scope for "A" above. */ void pruneOrphanedNames() { nextSymbol: for (Symbol s : getAllSymbolsSorted()) { if (s.isProperty()) { continue; } String currentName = s.getName(); int dot = -1; while (-1 != (dot = currentName.lastIndexOf('.'))) { currentName = currentName.substring(0, dot); Symbol owner = s.scope.getQualifiedSlot(currentName); if (owner != null && owner.getType() != null && (owner.getType().isNominalConstructor() || owner.getType().isFunctionPrototypeType() || owner.getType().isEnumType())) { removeSymbol(s); continue nextSymbol; } } } } /** * Create symbols and references for all properties of types in * this symbol table. * * This gets a little bit tricky, because of the way this symbol table * conflates "type Foo" and "the constructor of type Foo". So if you * have: * * * SymbolTable symbolTable = for("var x = new Foo();"); * Symbol x = symbolTable.getGlobalScope().getSlot("x"); * Symbol type = symbolTable.getAllSymbolsForType(x.getType()).get(0); * * * Then type.getPropertyScope() will have the properties of the * constructor "Foo". To get the properties of instances of "Foo", * you will need to call: * * * Symbol instance = symbolTable.getSymbolForInstancesOf(type); * * * As described at the top of this file, notice that "new Foo()" and * "Foo.prototype" are represented by the same symbol. */ void fillPropertySymbols( AbstractCompiler compiler, Node externs, Node root) { (new PropertyRefCollector(compiler)).process(externs, root); } /** Index JSDocInfo. */ void fillJSDocInfo( AbstractCompiler compiler, Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), new JSDocInfoCollector(compiler.getTypeRegistry())); // Create references to parameters in the JSDoc. for (Symbol sym : getAllSymbolsSorted()) { JSDocInfo info = sym.getJSDocInfo(); if (info == null) { continue; } for (Marker marker : info.getMarkers()) { SourcePosition pos = marker.getNameNode(); if (pos == null) { continue; } Node paramNode = pos.getItem(); String name = paramNode.getString(); Symbol param = getParameterInFunction(sym, name); if (param == null) { // There is no reference to this parameter in the actual JavaScript // code, so we'll try to create a special JsDoc-only symbol in // a JsDoc-only scope. SourcePosition typePos = marker.getType(); JSType type = null; if (typePos != null) { type = typePos.getItem().getJSType(); } if (sym.docScope == null) { sym.docScope = new SymbolScope(null /* root */, null /* parent scope */, null /* type of this */, sym); } // Check to make sure there's no existing symbol. In theory, this // should never happen, but we check anyway and fail silently // if our assumptions are wrong. (We do not want to put the symbol // table into an invalid state). Symbol existingSymbol = isAnySymbolDeclared(name, paramNode, sym.docScope); if (existingSymbol == null) { declareSymbol(name, type, type == null, sym.docScope, paramNode, null /* info */); } } else { param.defineReferenceAt(paramNode); } } } } /** * Build a property scope for the given symbol. Any properties of the symbol * will be added to the property scope. * * It is important that property scopes are created in order from the leaves * up to the root, so this should only be called from #fillPropertyScopes. * If you try to create a property scope for a parent before its leaf, * then the leaf will get cut and re-added to the parent property scope, * and weird things will happen. */ private void createPropertyScopeFor(Symbol s) { // In order to build a property scope for s, we will need to build // a property scope for all its implicit prototypes first. This means // that sometimes we will already have built its property scope // for a previous symbol. if (s.propertyScope != null) { return; } SymbolScope parentPropertyScope = null; ObjectType type = s.getType() == null ? null : s.getType().toObjectType(); if (type == null) { return; } ObjectType proto = type.getParentScope(); if (proto != null && proto != type && proto.getConstructor() != null) { Symbol parentSymbol = getSymbolForInstancesOf(proto.getConstructor()); if (parentSymbol != null) { createPropertyScopeFor(parentSymbol); parentPropertyScope = parentSymbol.getPropertyScope(); } } ObjectType instanceType = type; Iterable propNames = type.getOwnPropertyNames(); if (instanceType.isFunctionPrototypeType()) { // Guard against modifying foo.prototype when foo is a regular (non-constructor) function. if (instanceType.getOwnerFunction().hasInstanceType()) { // Merge the properties of "Foo.prototype" and "new Foo()" together. instanceType = instanceType.getOwnerFunction().getInstanceType(); Set set = Sets.newHashSet(propNames); Iterables.addAll(set, instanceType.getOwnPropertyNames()); propNames = set; } } s.setPropertyScope(new SymbolScope(null, parentPropertyScope, type, s)); for (String propName : propNames) { StaticSlot newProp = instanceType.getSlot(propName); if (newProp.getDeclaration() == null) { // Skip properties without declarations. We won't know how to index // them, because we index things by node. continue; } // We have symbol tables that do not do type analysis. They just try // to build a complete index of all objects in the program. So we might // already have symbols for things like "Foo.bar". If this happens, // throw out the old symbol and use the type-based symbol. Symbol oldProp = symbols.get(newProp.getDeclaration().getNode(), s.getName() + "." + propName); if (oldProp != null) { removeSymbol(oldProp); } // If we've already have an entry in the table for this symbol, // then skip it. This should only happen if we screwed up, // and declared multiple distinct properties with the same name // at the same node. We bail out here to be safe. if (symbols.get(newProp.getDeclaration().getNode(), newProp.getName()) != null) { logger.warning("Found duplicate symbol " + newProp); continue; } Symbol newSym = copySymbolTo(newProp, s.propertyScope); if (oldProp != null) { if (newSym.getJSDocInfo() == null) { newSym.setJSDocInfo(oldProp.getJSDocInfo()); } newSym.setPropertyScope(oldProp.propertyScope); for (Reference ref : oldProp.references.values()) { newSym.defineReferenceAt(ref.getNode()); } } } } /** * Fill in references to "this" variables. */ void fillThisReferences( AbstractCompiler compiler, Node externs, Node root) { (new ThisRefCollector(compiler)).process(externs, root); } /** * Given a scope from another symbol table, returns the {@code SymbolScope} * rooted at the same node. Creates one if it doesn't exist yet. */ private SymbolScope createScopeFrom(StaticScope otherScope) { Node otherScopeRoot = otherScope.getRootNode(); SymbolScope myScope = scopes.get(otherScopeRoot); if (myScope == null) { StaticScope otherScopeParent = otherScope.getParentScope(); // If otherScope is a global scope, and we already have a global scope, // then something has gone seriously wrong. // // Not all symbol tables are rooted at the same global node, and // we do not want to mix and match symbol tables that are rooted // differently. if (otherScopeParent == null) { // The global scope must be created before any local scopes. Preconditions.checkState( globalScope == null, "Global scopes found at different roots"); } myScope = new SymbolScope( otherScopeRoot, otherScopeParent == null ? null : createScopeFrom(otherScopeParent), otherScope.getTypeOfThis(), null); scopes.put(otherScopeRoot, myScope); if (myScope.isGlobalScope()) { globalScope = myScope; } } return myScope; } public static final class Symbol extends SimpleSlot { // Use a linked hash map, so that the results are deterministic // (and so the declaration always comes first). private final Map references = Maps.newLinkedHashMap(); private final SymbolScope scope; private SymbolScope propertyScope = null; private Reference declaration = null; private JSDocInfo docInfo = null; // A scope for symbols that are only documented in JSDoc. private SymbolScope docScope = null; Symbol(String name, JSType type, boolean inferred, SymbolScope scope) { super(name, type, inferred); this.scope = scope; } @Override public Reference getDeclaration() { return declaration; } public FunctionType getFunctionType() { return JSType.toMaybeFunctionType(getType()); } public Reference defineReferenceAt(Node n) { Reference result = references.get(n); if (result == null) { result = new Reference(this, n); references.put(n, result); } return result; } /** Sets the declaration node. May only be called once. */ void setDeclaration(Reference ref) { Preconditions.checkState(this.declaration == null); this.declaration = ref; } public boolean inGlobalScope() { return scope.isGlobalScope(); } public boolean inExterns() { Node n = getDeclarationNode(); return n == null ? false : n.isFromExterns(); } public Node getDeclarationNode() { return declaration == null ? null : declaration.getNode(); } public String getSourceFileName() { Node n = getDeclarationNode(); return n == null ? null : n.getSourceFileName(); } public SymbolScope getPropertyScope() { return propertyScope; } void setPropertyScope(SymbolScope scope) { this.propertyScope = scope; if (scope != null) { this.propertyScope.setSymbolForScope(this); } } @Override public JSDocInfo getJSDocInfo() { return docInfo; } void setJSDocInfo(JSDocInfo info) { this.docInfo = info; } /** Whether this is a property of another variable. */ public boolean isProperty() { return scope.isPropertyScope(); } /** Whether this is a variable in a lexical scope. */ public boolean isLexicalVariable() { return scope.isLexicalScope(); } /** Whether this is a variable that's only in JSDoc. */ public boolean isDocOnlyParameter() { return scope.isDocScope(); } @Override public String toString() { Node n = getDeclarationNode(); int lineNo = n == null ? -1 : n.getLineno(); return getName() + "@" + getSourceFileName() + ":" + lineNo; } } public static final class Reference extends SimpleReference { Reference(Symbol symbol, Node node) { super(symbol, node); } } public static final class SymbolScope implements StaticScope { private final Node rootNode; private final SymbolScope parent; private final JSType typeOfThis; private final Map ownSymbols = Maps.newLinkedHashMap(); private final int scopeDepth; // The number of inner anonymous functions that we've given names to. private int innerAnonFunctionsWithNames = 0; // The symbol associated with a property scope or doc scope. private Symbol mySymbol; SymbolScope( Node rootNode, @Nullable SymbolScope parent, JSType typeOfThis, Symbol mySymbol) { this.rootNode = rootNode; this.parent = parent; this.typeOfThis = typeOfThis; this.scopeDepth = parent == null ? 0 : (parent.getScopeDepth() + 1); this.mySymbol = mySymbol; } Symbol getSymbolForScope() { return mySymbol; } void setSymbolForScope(Symbol sym) { this.mySymbol = sym; } /** Gets a unique index for the symbol in this scope. */ public int getIndexOfSymbol(Symbol sym) { return Iterables.indexOf( ownSymbols.values(), Predicates.equalTo(sym)); } @Override public Node getRootNode() { return rootNode; } @Override public SymbolScope getParentScope() { return parent; } /** * Get the slot for a fully-qualified name (e.g., "a.b.c") by trying * to find property scopes at each part of the path. */ public Symbol getQualifiedSlot(String name) { Symbol fullyNamedSym = getSlot(name); if (fullyNamedSym != null) { return fullyNamedSym; } int dot = name.lastIndexOf("."); if (dot != -1) { Symbol owner = getQualifiedSlot(name.substring(0, dot)); if (owner != null && owner.getPropertyScope() != null) { return owner.getPropertyScope().getSlot(name.substring(dot + 1)); } } return null; } @Override public Symbol getSlot(String name) { Symbol own = getOwnSlot(name); if (own != null) { return own; } Symbol ancestor = parent == null ? null : parent.getSlot(name); if (ancestor != null) { return ancestor; } return null; } @Override public Symbol getOwnSlot(String name) { return ownSymbols.get(name); } @Override public JSType getTypeOfThis() { return typeOfThis; } public boolean isGlobalScope() { return getParentScope() == null && getRootNode() != null; } /** * Returns whether this is a doc scope. A doc scope is a table for symbols * that are documented solely within a JSDoc comment. */ public boolean isDocScope() { return getRootNode() == null && mySymbol != null && mySymbol.docScope == this; } public boolean isPropertyScope() { return getRootNode() == null && !isDocScope(); } public boolean isLexicalScope() { return getRootNode() != null; } public int getScopeDepth() { return scopeDepth; } @Override public String toString() { Node n = getRootNode(); if (n != null) { return "Scope@" + n.getSourceFileName() + ":" + n.getLineno(); } else { return "PropertyScope@" + getSymbolForScope(); } } } private class PropertyRefCollector extends NodeTraversal.AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; PropertyRefCollector(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private boolean maybeDefineReference( Node n, String propName, Symbol ownerSymbol) { // getPropertyScope() will be null in some rare cases where there // are no extern declarations for built-in types (like Function). if (ownerSymbol != null && ownerSymbol.getPropertyScope() != null) { Symbol prop = ownerSymbol.getPropertyScope().getSlot(propName); if (prop != null) { prop.defineReferenceAt(n); return true; } } return false; } // Try to find the symbol by its fully qualified name. private boolean tryDefineLexicalQualifiedNameRef(String name, Node n) { if (name != null) { Symbol lexicalSym = getEnclosingScope(n).getQualifiedSlot(name); if (lexicalSym != null) { lexicalSym.defineReferenceAt(n); return true; } } return false; } // Try to remove a reference by its fully qualified name. // If the symbol has no references left, remove it completely. private void tryRemoveLexicalQualifiedNameRef(String name, Node n) { if (name != null) { Symbol lexicalSym = getEnclosingScope(n).getQualifiedSlot(name); if (lexicalSym != null && lexicalSym.isLexicalVariable() && lexicalSym.getDeclaration().getNode() == n) { removeSymbol(lexicalSym); } } } private boolean maybeDefineTypedReference( Node n, String propName, JSType owner) { if (owner.isGlobalThisType()) { Symbol sym = globalScope.getSlot(propName); if (sym != null) { sym.defineReferenceAt(n); return true; } } else if (owner.isNominalConstructor()) { return maybeDefineReference( n, propName, getSymbolDeclaredBy(owner.toMaybeFunctionType())); } else if (owner.isEnumType()) { return maybeDefineReference( n, propName, getSymbolDeclaredBy(owner.toMaybeEnumType())); } else { boolean defined = false; for (Symbol ctor : getAllSymbolsForType(owner)) { if (maybeDefineReference( n, propName, getSymbolForInstancesOf(ctor))) { defined = true; } } return defined; } return false; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // There are two ways to define a property reference: // 1) As a fully qualified lexical symbol (e.g., x.y) // 2) As a property of another object (e.g., x's y) // Property definitions should take precedence over lexical // definitions. e.g., for "a.b", it's more useful to record // this as "property b of the type of a", than as "symbol a.b". if (n.isGetProp()) { JSType owner = n.getFirstChild().getJSType(); if (owner != null) { boolean defined = maybeDefineTypedReference( n, n.getLastChild().getString(), owner); if (defined) { tryRemoveLexicalQualifiedNameRef(n.getQualifiedName(), n); return; } } tryDefineLexicalQualifiedNameRef(n.getQualifiedName(), n); } else if (n.isStringKey()) { JSType owner = parent.getJSType(); if (owner != null) { boolean defined = maybeDefineTypedReference(n, n.getString(), owner); if (defined) { tryRemoveLexicalQualifiedNameRef( NodeUtil.getBestLValueName(n), n); return; } } tryDefineLexicalQualifiedNameRef( NodeUtil.getBestLValueName(n), n); } } } private class ThisRefCollector extends NodeTraversal.AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; // The 'this' symbols in the current scope chain. // // If we don't know how to declare 'this' in a scope chain, // then null should be on the stack. But this should be a rare // occurrence. We should strive to always be able to come up // with some symbol for 'this'. private final List thisStack = Lists.newArrayList(); ThisRefCollector(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } @Override public void enterScope(NodeTraversal t) { Symbol symbol = null; if (t.inGlobalScope()) { // Declare the global this at the first input root. // This is a bizarre place to put it, but we need some // location with a real file path (because all symbols // must have a path). // Note that root.lastChild.firstChild is the first non-extern input. Node firstInputRoot = t.getScopeRoot().getLastChild().getFirstChild(); if (firstInputRoot != null) { symbol = addSymbol( GLOBAL_THIS, registry.getNativeType(JSTypeNative.GLOBAL_THIS), false /* declared */, globalScope, firstInputRoot); symbol.setDeclaration(new Reference(symbol, firstInputRoot)); } } else { // Otherwise, declare a "this" property when possible. SymbolScope scope = scopes.get(t.getScopeRoot()); Preconditions.checkNotNull(scope); Symbol scopeSymbol = getSymbolForScope(scope); if (scopeSymbol != null) { SymbolScope propScope = scopeSymbol.getPropertyScope(); if (propScope != null) { // If a function is assigned multiple times, we only want // one addressable "this" symbol. symbol = propScope.getOwnSlot("this"); if (symbol == null) { JSType rootType = t.getScopeRoot().getJSType(); FunctionType fnType = rootType == null ? null : rootType.toMaybeFunctionType(); JSType type = fnType == null ? null : fnType.getTypeOfThis(); symbol = addSymbol( "this", type, false /* declared */, scope, t.getScopeRoot()); } // TODO(nicksantos): It's non-obvious where the declaration of // the 'this' symbol should be. Figure this out later. } } } thisStack.add(symbol); } @Override public void exitScope(NodeTraversal t) { thisStack.remove(thisStack.size() - 1); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isThis()) { return; } Symbol symbol = thisStack.get(thisStack.size() - 1); if (symbol != null) { Reference ref = symbol.defineReferenceAt(n); if (symbol.getDeclaration() == null) { symbol.setDeclaration(ref); } } } } /** Collects references to types in JSDocInfo. */ private class JSDocInfoCollector extends NodeTraversal.AbstractPostOrderCallback { private final JSTypeRegistry typeRegistry; private JSDocInfoCollector(JSTypeRegistry registry) { this.typeRegistry = registry; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getJSDocInfo() != null) { // Find references in the JSDocInfo. JSDocInfo info = n.getJSDocInfo(); docInfos.add(info); for (Node typeAst : info.getTypeNodes()) { SymbolScope scope = scopes.get(t.getScopeRoot()); visitTypeNode(scope == null ? globalScope : scope, typeAst); } } } public void visitTypeNode(SymbolScope scope, Node n) { if (n.isString()) { Symbol symbol = scope.getSlot(n.getString()); if (symbol == null) { // If we can't find this type, it might be a reference to a // primitive type (like {string}). Autobox it to check. JSType type = typeRegistry.getType(n.getString()); JSType autobox = type == null ? null : type.autoboxesTo(); symbol = autobox == null ? null : getSymbolForTypeHelper(autobox, true); } if (symbol != null) { symbol.defineReferenceAt(n); } } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitTypeNode(scope, child); } } } // Comparators private final Ordering SOURCE_NAME_ORDERING = Ordering.natural().nullsFirst(); private final Ordering NODE_ORDERING = new Ordering() { @Override public int compare(Node a, Node b) { int result = SOURCE_NAME_ORDERING.compare( a.getSourceFileName(), b.getSourceFileName()); if (result != 0) { return result; } // Source position is a bit mask of line in the top 4 bits, so this // is a quick way to compare order without computing absolute position. return a.getSourcePosition() - b.getSourcePosition(); } }; private final Ordering LEXICAL_SCOPE_ORDERING = new Ordering() { @Override public int compare(SymbolScope a, SymbolScope b) { Preconditions.checkState(a.isLexicalScope() && b.isLexicalScope(), "We can only sort lexical scopes"); return NODE_ORDERING.compare(a.getRootNode(), b.getRootNode()); } }; private final Ordering SYMBOL_ORDERING = new Ordering() { @Override public int compare(Symbol a, Symbol b) { SymbolScope scopeA = getScope(a); SymbolScope scopeB = getScope(b); // More deeply nested symbols should go later. int result = getLexicalScopeDepth(scopeA) - getLexicalScopeDepth(scopeB); if (result != 0) { return result; } // After than, just use lexicographic ordering. // This ensures "a.b" comes before "a.b.c". return a.getName().compareTo(b.getName()); } }; /** * For a lexical scope, just returns the normal scope depth. * * For a property scope, returns the number of scopes we have to search * to find the nearest lexical scope, plus that lexical scope's depth. * * For a doc info scope, returns 0. */ private int getLexicalScopeDepth(SymbolScope scope) { if (scope.isLexicalScope() || scope.isDocScope()) { return scope.getScopeDepth(); } else { Preconditions.checkState(scope.isPropertyScope()); Symbol sym = scope.getSymbolForScope(); Preconditions.checkNotNull(sym); return getLexicalScopeDepth(getScope(sym)) + 1; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AbstractCompiler.java0000644000175000017500000002657312115204405026752 0ustar apoapo/* * 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.base.Supplier; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.parsing.Config; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.head.ErrorReporter; import com.google.javascript.rhino.head.ast.AstRoot; import com.google.javascript.rhino.jstype.JSTypeRegistry; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** * An abstract compiler, to help remove the circular dependency of * passes on JSCompiler. * * This is an abstract class, so that we can make the methods package-private. * * @author nicksantos@google.com (Nick Santos) */ public abstract class AbstractCompiler implements SourceExcerptProvider { static final DiagnosticType READ_ERROR = DiagnosticType.error( "JSC_READ_ERROR", "Cannot read: {0}"); private LifeCycleStage stage = LifeCycleStage.RAW; // TODO(nicksantos): Decide if all of these are really necessary. // Many of them are just accessors that should be passed to the // CompilerPass's constructor. /** * Looks up an input (possibly an externs input) by input id. * May return null. */ public abstract CompilerInput getInput(InputId inputId); /** * Looks up a source file by name. May return null. */ abstract SourceFile getSourceFileByName(String sourceName); /** * Creates a new externs file. * @param name A name for the new externs file. * @throws IllegalArgumentException If the name of the externs file conflicts * with a pre-existing externs file. */ abstract CompilerInput newExternInput(String name); /** * Gets the module graph. May return null if there aren't at least two * modules. */ abstract JSModuleGraph getModuleGraph(); /** * Gets the inputs in the order in which they are being processed. * Only for use by {@code AbstractCompilerRunner}. */ abstract List getInputsInOrder(); /** * Gets a central registry of type information from the compiled JS. */ public abstract JSTypeRegistry getTypeRegistry(); /** * Gets a memoized scope creator with type information. */ abstract ScopeCreator getTypedScopeCreator(); /** * Gets the top scope. */ public abstract Scope getTopScope(); /** * Report an error or warning. */ public abstract void report(JSError error); /** * Report an internal error. */ abstract void throwInternalError(String msg, Exception cause); /** * Gets the current coding convention. */ public abstract CodingConvention getCodingConvention(); /** * Report code changes. */ public abstract void reportCodeChange(); /** * Logs a message under a central logger. */ abstract void addToDebugLog(String message); /** * Sets the CssRenamingMap. */ abstract void setCssRenamingMap(CssRenamingMap map); /** * Gets the CssRenamingMap. */ abstract CssRenamingMap getCssRenamingMap(); /** * Gets a suitable SCRIPT node to serve as a parent for code insertion. If * {@code module} contains any inputs, the returned node will be the SCRIPT * node corresponding to its first input. If {@code module} is empty, on the * other hand, then the returned node will be the first SCRIPT node in a * non-empty module that {@code module} depends on (the deepest one possible). * * @param module A module. If null, will return the first SCRIPT node of all * modules. * @return A SCRIPT node (never null). */ abstract Node getNodeForCodeInsertion(JSModule module); /** * Gets the central registry of type violations. */ abstract TypeValidator getTypeValidator(); /** * Parses code for injecting. */ abstract Node parseSyntheticCode(String code); /** * Parses code for injecting, and associate it with a given source file. */ abstract Node parseSyntheticCode(String filename, String code); /** * Parses code for testing. */ abstract Node parseTestCode(String code); /** * Prints a node to source code. */ abstract String toSource(Node root); /** * Gets a default error reporter for injecting into Rhino. */ abstract ErrorReporter getDefaultErrorReporter(); /** * Get an interpreter for type analysis. */ public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter(); /** * @return The current life-cycle stage of the AST we're working on. */ LifeCycleStage getLifeCycleStage() { return stage; } /** * Generates unique ids. */ abstract Supplier getUniqueNameIdSupplier(); /** * @return Whether any errors have been encountered that * should stop the compilation process. */ abstract boolean hasHaltingErrors(); /** * Register a listener for code change events. */ abstract void addChangeHandler(CodeChangeHandler handler); /** * Remove a listener for code change events. */ abstract void removeChangeHandler(CodeChangeHandler handler); /** * Returns true if compiling in IDE mode. */ abstract boolean isIdeMode(); /** * @return Whether the compiler is in ES5Mode. */ abstract boolean acceptEcmaScript5(); /** * @return Whether the compiler accepts `const' keyword. */ abstract boolean acceptConstKeyword(); /** * Returns the parser configuration. */ abstract Config getParserConfig(); /** * Returns true if type checking is enabled. */ abstract boolean isTypeCheckingEnabled(); /** * Normalizes the types of AST nodes in the given tree, and * annotates any nodes to which the coding convention applies so that passes * can read the annotations instead of using the coding convention. */ abstract void prepareAst(Node root); /** * Gets the error manager. */ abstract public ErrorManager getErrorManager(); /** * Set the current life-cycle state. */ void setLifeCycleStage(LifeCycleStage stage) { this.stage = stage; } /** * Are the nodes equal for the purpose of inlining? * If type aware optimizations are on, type equality is checked. */ abstract boolean areNodesEqualForInlining(Node n1, Node n2); /** * Set if RegExp global properties are used. * @param references Whether there are references to the RegExp global object * properties. */ abstract void setHasRegExpGlobalReferences(boolean references); /** * @return Whether the AST contains references to the RegExp global object * properties. */ abstract boolean hasRegExpGlobalReferences(); /** * @return The error level the given error object will be reported at. */ abstract CheckLevel getErrorLevel(JSError error); static enum LifeCycleStage { RAW, // See constraints put on the tree by Normalize.java NORMALIZED, // The normalize pass has put constraints on the tree, // but variables and properties have been renamed so // coding conventions no longer apply. NORMALIZED_OBFUSCATED; boolean isNormalized() { return this == NORMALIZED || this == NORMALIZED_OBFUSCATED; } boolean isNormalizedUnobfuscated() { return this == NORMALIZED; } boolean isNormalizedObfuscated() { return this == NORMALIZED_OBFUSCATED; } } /** * Runs a given compiler-pass by calling its {@code process()} method. * @param pass The pass to be run. */ abstract void process(CompilerPass pass); /** * Returns the root node of the AST, which includes both externs and source. */ abstract Node getRoot(); // TODO(bashir) It would be good to extract a single dumb data object with // only getters and setters that keeps all global information we keep for a // compiler instance. Then move some of the functions of this class there. /** * Updates the list of references for variables in global scope. * * @param refMapPatch Maps each variable to all of its references; may contain * references collected from the whole AST or only a SCRIPT sub-tree. * @param collectionRoot The root of sub-tree in which reference collection * has been done. This should either be a SCRIPT node (if collection is * done on a single file) or it is assumed that collection is on full AST. */ abstract void updateGlobalVarReferences(Map refMapPatch, Node collectionRoot); /** * This can be used to get the list of all references to all global variables * based on all previous calls to {@code updateGlobalVarReferences}. * * @return The reference collection map associated to global scope variable. */ abstract GlobalVarReferenceMap getGlobalVarReferences(); /** * @return a CompilerInput that can be modified to add addition extern * definitions; */ abstract CompilerInput getSynthesizedExternsInput(); /** * @return a number in [0,1] range indicating an approximate progress of the * last compile. Note this should only be used as a hint and no assumptions * should be made on accuracy, even a completed compile may choose not to set * this to 1.0 at the end. */ public abstract double getProgress(); /** * Gets the last pass name set by setProgress. */ abstract String getLastPassName(); /** * Sets the progress percentage as well as the name of the last pass that * ran (if available). * @param progress A precentage expressed as a double in the range [0, 1]. * Use -1 if you just want to set the last pass name. */ abstract void setProgress(double progress, @Nullable String lastPassName); /** * The subdir js/ contains libraries of code that we inject * at compile-time only if requested by this function. * * Notice that these libraries will almost always create global symbols. * * @param resourceName The name of the library. For example, if "base" is * is specified, then we load js/base.js * @return If new code was injected, returns the last expression node of the * library. If the caller needs to add additional code, they should add * it as the next sibling of this node. If new code was not injected, * returns null. */ abstract Node ensureLibraryInjected(String resourceName); /** * Stores the "new" Rhino parse tree for a given source file. * @param sourceName The source file name. * @param astRoot The "new" Rhino parse tree. */ abstract void setOldParseTree(String sourceName, AstRoot astRoot); /** * Gets an old format parse tree for a given source file. * @param sourceName The source file name to get the tree for. * @return The "new" Rhino parse tree for the given source file. */ abstract AstRoot getOldParseTreeByName(String sourceName); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Result.java0000644000175000017500000000507712115204405024766 0ustar apoapo/* * 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 java.util.Map; /** * Compilation results */ public class Result { public final boolean success; public final JSError[] errors; public final JSError[] warnings; public final String debugLog; public final VariableMap variableMap; public final VariableMap propertyMap; public final VariableMap namedAnonFunctionMap; public final VariableMap stringMap; public final FunctionInformationMap functionInformationMap; public final SourceMap sourceMap; public final Map cssNames; public final String externExport; public final String idGeneratorMap; Result(JSError[] errors, JSError[] warnings, String debugLog, VariableMap variableMap, VariableMap propertyMap, VariableMap namedAnonFunctionMap, VariableMap stringMap, FunctionInformationMap functionInformationMap, SourceMap sourceMap, String externExport, Map cssNames, String idGeneratorMap) { this.success = errors.length == 0; this.errors = errors; this.warnings = warnings; this.debugLog = debugLog; this.variableMap = variableMap; this.propertyMap = propertyMap; this.namedAnonFunctionMap = namedAnonFunctionMap; this.stringMap = stringMap; this.functionInformationMap = functionInformationMap; this.sourceMap = sourceMap; this.externExport = externExport; this.cssNames = cssNames; this.idGeneratorMap = idGeneratorMap; } // Visible for testing only. public Result(JSError[] errors, JSError[] warnings, String debugLog, VariableMap variableMap, VariableMap propertyMap, VariableMap namedAnonFunctionMap, FunctionInformationMap functionInformationMap, SourceMap sourceMap, String externExport) { this(errors, warnings, debugLog, variableMap, propertyMap, namedAnonFunctionMap, null, functionInformationMap, sourceMap, externExport, null, null); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineFunctions.java0000644000175000017500000010365112115204405026614 0ustar apoapo/* * Copyright 2005 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.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.FunctionInjector.CanInlineResult; import com.google.javascript.jscomp.FunctionInjector.InliningMode; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * Inlines functions that are divided into two types: "direct call node * replacement" (aka "direct") and as a block of statements (aka block). * Function that can be inlined "directly" functions consist of a single * return statement, everything else is must be inlined as a "block". These * functions must meet these general requirements: * - it is not recursive * - the function does not contain another function -- these may be * intentional to to limit the scope of closures. * - function is called only once OR the size of the inline function is smaller * than the call itself. * - the function name is not referenced in any other manner * * "directly" inlined functions must meet these additional requirements: * - consists of a single return statement * */ class InlineFunctions implements SpecializationAwareCompilerPass { // TODO(nicksantos): This needs to be completely rewritten to use scopes // to do variable lookups. Right now, it assumes that all functions are // uniquely named variables. There's currently a stopgap scope-check // to ensure that this doesn't produce invalid code. But in the long run, // this needs a major refactor. private final Map fns = Maps.newHashMap(); private final Map anonFns = Maps.newHashMap(); private final AbstractCompiler compiler; private final FunctionInjector injector; private final boolean blockFunctionInliningEnabled; private final boolean inlineGlobalFunctions; private final boolean inlineLocalFunctions; private final boolean assumeMinimumCapture; private SpecializeModule.SpecializationState specializationState; InlineFunctions(AbstractCompiler compiler, Supplier safeNameIdSupplier, boolean inlineGlobalFunctions, boolean inlineLocalFunctions, boolean blockFunctionInliningEnabled, boolean assumeStrictThis, boolean assumeMinimumCapture) { Preconditions.checkArgument(compiler != null); Preconditions.checkArgument(safeNameIdSupplier != null); this.compiler = compiler; this.inlineGlobalFunctions = inlineGlobalFunctions; this.inlineLocalFunctions = inlineLocalFunctions; this.blockFunctionInliningEnabled = blockFunctionInliningEnabled; this.assumeMinimumCapture = assumeMinimumCapture; this.injector = new FunctionInjector( compiler, safeNameIdSupplier, true, assumeStrictThis, assumeMinimumCapture); } FunctionState getOrCreateFunctionState(String fnName) { FunctionState fs = fns.get(fnName); if (fs == null) { fs = new FunctionState(); fns.put(fnName, fs); } return fs; } @Override public void enableSpecialization(SpecializeModule.SpecializationState specializationState) { this.specializationState = specializationState; } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, new FindCandidateFunctions()); if (fns.isEmpty()) { return; // Nothing left to do. } NodeTraversal.traverse(compiler, root, new FindCandidatesReferences(fns, anonFns)); trimCanidatesNotMeetingMinimumRequirements(); if (fns.isEmpty()) { return; // Nothing left to do. } // Store the set of function names eligible for inlining and use this to // prevent function names from being moved into temporaries during // expression decomposition. If this movement were allowed it would prevent // the Inline callback from finding the function calls. // // This pass already assumes these are constants, so this is safe for anyone // using function inlining. // Set fnNames = Sets.newHashSet(fns.keySet()); injector.setKnownConstants(fnNames); trimCanidatesUsingOnCost(); if (fns.isEmpty()) { return; // Nothing left to do. } resolveInlineConflicts(); decomposeExpressions(); NodeTraversal.traverse(compiler, root, new CallVisitor( fns, anonFns, new Inline(injector, specializationState))); removeInlinedFunctions(); } /** * Find functions that might be inlined. */ private class FindCandidateFunctions implements Callback { private int callsSeen = 0; @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { // Don't traverse into function bodies // if we aren't inlining local functions. return inlineLocalFunctions || nodeTraversal.inGlobalScope(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((t.inGlobalScope() && inlineGlobalFunctions) || (!t.inGlobalScope() && inlineLocalFunctions)) { findNamedFunctions(t, n, parent); findFunctionExpressions(t, n); } } public void findNamedFunctions(NodeTraversal t, Node n, Node parent) { if (!NodeUtil.isStatement(n)) { // There aren't any interesting functions here. return; } switch (n.getType()) { // Functions expressions in the form of: // var fooFn = function(x) { return ... } case Token.VAR: Preconditions.checkState(n.hasOneChild()); Node nameNode = n.getFirstChild(); if (nameNode.isName() && nameNode.hasChildren() && nameNode.getFirstChild().isFunction()) { maybeAddFunction(new FunctionVar(n), t.getModule()); } break; // Named functions // function Foo(x) { return ... } case Token.FUNCTION: Preconditions.checkState(NodeUtil.isStatementBlock(parent) || parent.isLabel()); if (!NodeUtil.isFunctionExpression(n)) { Function fn = new NamedFunction(n); maybeAddFunction(fn, t.getModule()); } break; } } /** * Find function expressions that are called directly in the form of * (function(a,b,...){...})(a,b,...) * or * (function(a,b,...){...}).call(this,a,b, ...) */ public void findFunctionExpressions(NodeTraversal t, Node n) { switch (n.getType()) { // Functions expressions in the form of: // (function(){})(); case Token.CALL: Node fnNode = null; if (n.getFirstChild().isFunction()) { fnNode = n.getFirstChild(); } else if (NodeUtil.isFunctionObjectCall(n)) { Node fnIdentifingNode = n.getFirstChild().getFirstChild(); if (fnIdentifingNode.isFunction()) { fnNode = fnIdentifingNode; } } // If a interesting function was discovered, add it. if (fnNode != null) { Function fn = new FunctionExpression(fnNode, callsSeen++); maybeAddFunction(fn, t.getModule()); anonFns.put(fnNode, fn.getName()); } break; } } } /** * Updates the FunctionState object for the given function. Checks if the * given function matches the criteria for an inlinable function. */ private void maybeAddFunction(Function fn, JSModule module) { String name = fn.getName(); FunctionState fs = getOrCreateFunctionState(name); // TODO(johnlenz): Maybe "smarten" FunctionState by adding this logic // to it? // If the function has multiple definitions, don't inline it. if (fs.hasExistingFunctionDefinition()) { fs.setInline(false); } else { // verify the function hasn't already been marked as "don't inline" if (fs.canInline()) { // store it for use when inlining. fs.setFn(fn); if (injector.isDirectCallNodeReplacementPossible( fn.getFunctionNode())) { fs.inlineDirectly(true); } // verify the function meets all the requirements. // TODO(johnlenz): Minimum requirement checks are about 5% of the // run-time cost of this pass. if (!isCandidateFunction(fn)) { // It doesn't meet the requirements. fs.setInline(false); } // Set the module and gather names that need temporaries. if (fs.canInline()) { fs.setModule(module); Node fnNode = fn.getFunctionNode(); Set namesToAlias = FunctionArgumentInjector.findModifiedParameters(fnNode); if (!namesToAlias.isEmpty()) { fs.inlineDirectly(false); fs.setNamesToAlias(namesToAlias); } Node block = NodeUtil.getFunctionBody(fnNode); if (NodeUtil.referencesThis(block)) { fs.setReferencesThis(true); } if (NodeUtil.containsFunction(block)) { fs.setHasInnerFunctions(true); // If there are inner functions, we can inline into global scope // if there are no local vars or named functions. // TODO(johnlenz): this can be improved by looking at the possible // values for locals. If there are simple values, or constants // we could still inline. if (!assumeMinimumCapture && hasLocalNames(fnNode)) { fs.setInline(false); } } } // Check if block inlining is allowed. if (fs.canInline() && !fs.canInlineDirectly()) { if (!blockFunctionInliningEnabled) { fs.setInline(false); } } } } } /** * @param fnNode The function to inspect. * @return Whether the function has parameters, var, or function declarations. */ private boolean hasLocalNames(Node fnNode) { Node block = NodeUtil.getFunctionBody(fnNode); return NodeUtil.getFunctionParameters(fnNode).hasChildren() || NodeUtil.has( block, new NodeUtil.MatchDeclaration(), new NodeUtil.MatchShallowStatement()); } /** * Returns the function the traversal is currently traversing, or null * if in the global scope. */ private static Node getContainingFunction(NodeTraversal t) { return (t.inGlobalScope()) ? null : t.getScopeRoot(); } /** * Checks if the given function matches the criteria for an inlinable * function. */ private boolean isCandidateFunction(Function fn) { // Don't inline exported functions. String fnName = fn.getName(); if (compiler.getCodingConvention().isExported(fnName)) { // TODO(johnlenz): Should we allow internal references to be inlined? // An exported name can be replaced externally, any inlined instance // would not reflect this change. // To allow inlining we need to be able to distinguish between exports // that are used in a read-only fashion and those that can be replaced // by external definitions. return false; } // Don't inline this special function if (RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(fnName)) { return false; } // Don't inline if we are specializing and the function can't be fixed up if (specializationState != null && !specializationState.canFixupFunction(fn.getFunctionNode())) { return false; } Node fnNode = fn.getFunctionNode(); return injector.doesFunctionMeetMinimumRequirements(fnName, fnNode); } /** * @see CallVisitor */ private interface CallVisitorCallback { public void visitCallSite( NodeTraversal t, Node callNode, Node parent, FunctionState fs); } /** * Visit call sites for functions in functionMap. */ private static class CallVisitor extends AbstractPostOrderCallback { protected CallVisitorCallback callback; private Map functionMap; private Map anonFunctionMap; CallVisitor(Map fns, Map anonFns, CallVisitorCallback callback) { this.functionMap = fns; this.anonFunctionMap = anonFns; this.callback = callback; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { // Function calls case Token.CALL: Node child = n.getFirstChild(); String name = null; // NOTE: The normalization pass insures that local names do not // collide with global names. if (child.isName()) { name = child.getString(); } else if (child.isFunction()) { name = anonFunctionMap.get(child); } else if (NodeUtil.isFunctionObjectCall(n)) { Preconditions.checkState(NodeUtil.isGet(child)); Node fnIdentifingNode = child.getFirstChild(); if (fnIdentifingNode.isName()) { name = fnIdentifingNode.getString(); } else if (fnIdentifingNode.isFunction()) { name = anonFunctionMap.get(fnIdentifingNode); } } if (name != null) { FunctionState fs = functionMap.get(name); // Only visit call-sites for functions that can be inlined. if (fs != null) { callback.visitCallSite(t, n, parent, fs); } } break; } } } /** * @return Whether the name is used in a way that might be a candidate * for inlining. */ static boolean isCandidateUsage(Node name) { Node parent = name.getParent(); Preconditions.checkState(name.isName()); if (parent.isVar() || parent.isFunction()) { // This is a declaration. Duplicate declarations are handle during // function candidate gathering. return true; } if (parent.isCall() && parent.getFirstChild() == name) { // This is a normal reference to the function. return true; } // Check for a ".call" to the named function: // CALL // GETPROP/GETELEM // NAME // STRING == "call" // This-Value // Function-parameter-1 // ... if (NodeUtil.isGet(parent) && name == parent.getFirstChild() && name.getNext().isString() && name.getNext().getString().equals("call")) { Node gramps = name.getAncestor(2); if (gramps.isCall() && gramps.getFirstChild() == parent) { // Yep, a ".call". return true; } } return false; } /** * Find references to functions that are inlinable. */ private class FindCandidatesReferences extends CallVisitor implements CallVisitorCallback { FindCandidatesReferences( Map fns, Map anonFns) { super(fns, anonFns, null); this.callback = this; } @Override public void visit(NodeTraversal t, Node n, Node parent) { super.visit(t, n, parent); if (n.isName()) { checkNameUsage(n, parent); } } @Override public void visitCallSite( NodeTraversal t, Node callNode, Node parent, FunctionState fs) { maybeAddReference(t, fs, callNode, t.getModule()); } void maybeAddReference(NodeTraversal t, FunctionState fs, Node callNode, JSModule module) { if (!fs.canInline()) { return; } boolean referenceAdded = false; InliningMode mode = fs.canInlineDirectly() ? InliningMode.DIRECT : InliningMode.BLOCK; referenceAdded = maybeAddReferenceUsingMode( t, fs, callNode, module, mode); if (!referenceAdded && mode == InliningMode.DIRECT && blockFunctionInliningEnabled) { // This reference can not be directly inlined, see if // block replacement inlining is possible. mode = InliningMode.BLOCK; referenceAdded = maybeAddReferenceUsingMode( t, fs, callNode, module, mode); } if (!referenceAdded) { // Don't try to remove a function if we can't inline all // the references. fs.setRemove(false); } } private boolean maybeAddReferenceUsingMode( NodeTraversal t, FunctionState fs, Node callNode, JSModule module, InliningMode mode) { if (specializationState != null) { // If we're specializing, make sure we can fixup // the containing function before inlining Node containingFunction = getContainingFunction(t); if (containingFunction != null && !specializationState.canFixupFunction( containingFunction)) { return false; } } CanInlineResult result = injector.canInlineReferenceToFunction( t, callNode, fs.getFn().getFunctionNode(), fs.getNamesToAlias(), mode, fs.getReferencesThis(), fs.hasInnerFunctions()); if (result != CanInlineResult.NO) { // Yeah! boolean decompose = (result == CanInlineResult.AFTER_PREPARATION); fs.addReference(new Reference(callNode, module, mode, decompose)); return true; } return false; } /** * Find functions that can be inlined. */ private void checkNameUsage(Node n, Node parent) { Preconditions.checkState(n.isName()); if (isCandidateUsage(n)) { return; } // Other refs to a function name remove its candidacy for inlining String name = n.getString(); FunctionState fs = fns.get(name); if (fs == null) { return; } // Unlike normal call/new parameters, references passed to // JSCompiler_ObjectPropertyString are not aliases of a value, but // a reference to the name itself, as such the value of the name is // unknown and can not be inlined. if (parent.isNew()) { Node target = parent.getFirstChild(); if (target.isName() && target.getString().equals( ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING)) { // This method is going to be replaced so don't inline it anywhere. fs.setInline(false); } } // If the name is being assigned to it can not be inlined. if (parent.isAssign() && parent.getFirstChild() == n) { // e.g. bar = something; <== we can't inline "bar" // so mark the function as uninlinable. // TODO(johnlenz): Should we just remove it from fns here? fs.setInline(false); } else { // e.g. var fn = bar; <== we can't inline "bar" // As this reference can't be inlined mark the function as // unremovable. fs.setRemove(false); } } } /** * Inline functions at the call sites. */ private static class Inline implements CallVisitorCallback { private final FunctionInjector injector; private final SpecializeModule.SpecializationState specializationState; Inline(FunctionInjector injector, SpecializeModule.SpecializationState specializationState) { this.injector = injector; this.specializationState = specializationState; } @Override public void visitCallSite( NodeTraversal t, Node callNode, Node parent, FunctionState fs) { Preconditions.checkState(fs.hasExistingFunctionDefinition()); if (fs.canInline()) { Reference ref = fs.getReference(callNode); // There are two cases ref can be null: if the call site was introduce // because it was part of a function that was inlined during this pass // or if the call site was trimmed from the list of references because // the function couldn't be inlined at this location. if (ref != null) { if (specializationState != null) { Node containingFunction = getContainingFunction(t); if (containingFunction != null) { // Report that the function was specialized so that // {@link SpecializeModule} can fix it up. specializationState.reportSpecializedFunction(containingFunction); } } inlineFunction(t, callNode, fs, ref.mode); // Keep track of references that have been inlined so that // we can verify that none have been missed. ref.inlined = true; } } } /** * Inline a function into the call site. */ private void inlineFunction( NodeTraversal t, Node callNode, FunctionState fs, InliningMode mode) { Function fn = fs.getFn(); String fnName = fn.getName(); Node fnNode = fs.getSafeFnNode(); injector.inline(callNode, fnName, fnNode, mode); t.getCompiler().reportCodeChange(); t.getCompiler().addToDebugLog("Inlined function: " + fn.getName()); } } /** * Remove entries that aren't a valid inline candidates, from the list of * encountered names. */ private void trimCanidatesNotMeetingMinimumRequirements() { Iterator> i; for (i = fns.entrySet().iterator(); i.hasNext();) { FunctionState fs = i.next().getValue(); if (!fs.hasExistingFunctionDefinition() || !fs.canInline()) { i.remove(); } } } /** * Remove entries from the list of candidates that can't be inlined. */ void trimCanidatesUsingOnCost() { Iterator> i; for (i = fns.entrySet().iterator(); i.hasNext();) { FunctionState fs = i.next().getValue(); if (fs.hasReferences()) { // Only inline function if it decreases the code size. boolean lowersCost = mimimizeCost(fs); if (!lowersCost) { // It shouldn't be inlined; remove it from the list. i.remove(); } } else if (!fs.canRemove()) { // Don't bother tracking functions without references that can't be // removed. i.remove(); } } } /** * Determines if the function is worth inlining and potentially * trims references that increase the cost. * @return Whether inlining the references lowers the overall cost. */ private boolean mimimizeCost(FunctionState fs) { if (!inliningLowersCost(fs)) { // Try again without Block inlining references if (fs.hasBlockInliningReferences()) { fs.setRemove(false); fs.removeBlockInliningReferences(); if (!fs.hasReferences() || !inliningLowersCost(fs)) { return false; } } else { return false; } } return true; } /** * @return Whether inlining the function reduces code size. */ private boolean inliningLowersCost(FunctionState fs) { return injector.inliningLowersCost( fs.getModule(), fs.getFn().getFunctionNode(), fs.getReferences(), fs.getNamesToAlias(), fs.canRemove(), fs.getReferencesThis()); } /** * Size base inlining calculations are thrown off when a function that is * being inlined also contains calls to functions that are slated for * inlining. * * Specifically, a clone of the FUNCTION node tree is used when the function * is inlined. Calls in this new tree are not included in the list of function * references so they won't be inlined (which is what we want). Here we mark * those functions as non-removable (as they will have new references in the * cloned node trees). * * This prevents a function that would only be inlined because it is * referenced once from being inlined into multiple call sites because * the calling function has been inlined in multiple locations or the * function being removed while there are still references. */ private void resolveInlineConflicts() { for (FunctionState fs : fns.values()) { resolveInlineConflictsForFunction(fs); } } /** * @see #resolveInlineConflicts */ private void resolveInlineConflictsForFunction(FunctionState fs) { // Functions that aren't referenced don't cause conflicts. if (!fs.hasReferences() || !fs.canInline()) { return; } Node fnNode = fs.getFn().getFunctionNode(); Set names = findCalledFunctions(fnNode); if (!names.isEmpty()) { // Prevent the removal of the referenced functions. for (String name : names) { FunctionState fsCalled = fns.get(name); if (fsCalled != null && fsCalled.canRemove()) { fsCalled.setRemove(false); // For functions that can no longer be removed, check if they should // still be inlined. if (!mimimizeCost(fsCalled)) { // It can't be inlined remove it from the list. fsCalled.setInline(false); } } } // Make a copy of the Node, so it isn't changed by other inlines. fs.setSafeFnNode(fs.getFn().getFunctionNode().cloneTree()); } } /** * This functions that may be called directly. */ private Set findCalledFunctions(Node node) { Set changed = Sets.newHashSet(); findCalledFunctions(NodeUtil.getFunctionBody(node), changed); return changed; } /** * @see #findCalledFunctions(Node) */ private void findCalledFunctions( Node node, Set changed) { Preconditions.checkArgument(changed != null); // For each referenced function, add a new reference if (node.isName()) { if (isCandidateUsage(node)) { changed.add(node.getString()); } } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { findCalledFunctions(c, changed); } } /** * For any call-site that needs it, prepare the call-site for inlining * by rewriting the containing expression. */ private void decomposeExpressions() { for (FunctionState fs : fns.values()) { if (fs.canInline()) { for (Reference ref : fs.getReferences()) { if (ref.requiresDecomposition) { injector.maybePrepareCall(ref.callNode); } } } } } /** * Removed inlined functions that no longer have any references. */ void removeInlinedFunctions() { for (FunctionState fs : fns.values()) { if (fs.canRemove()) { Function fn = fs.getFn(); Preconditions.checkState(fs.canInline()); Preconditions.checkState(fn != null); verifyAllReferencesInlined(fs); if (specializationState != null) { specializationState.reportRemovedFunction( fn.getFunctionNode(), fn.getDeclaringBlock()); } fn.remove(); compiler.reportCodeChange(); } } } /** * Sanity check to verify, that expression rewriting didn't * make a call inaccessible. */ void verifyAllReferencesInlined(FunctionState fs) { for (Reference ref : fs.getReferences()) { if (!ref.inlined) { throw new IllegalStateException("Call site missed.\n call: " + ref.callNode.toStringTree() + "\n parent: " + ref.callNode.getParent().toStringTree()); } } } /** * Use to track the decisions that have been make about a function. */ private static class FunctionState { private Function fn = null; private Node safeFnNode = null; private boolean inline = true; private boolean remove = true; private boolean inlineDirectly = false; private boolean referencesThis = false; private boolean hasInnerFunctions = false; private Map references = null; private JSModule module = null; private Set namesToAlias = null; boolean hasExistingFunctionDefinition() { return (fn != null); } public void setReferencesThis(boolean referencesThis) { this.referencesThis = referencesThis; } public boolean getReferencesThis() { return this.referencesThis; } public void setHasInnerFunctions(boolean hasInnerFunctions) { this.hasInnerFunctions = hasInnerFunctions; } public boolean hasInnerFunctions() { return hasInnerFunctions; } void removeBlockInliningReferences() { Iterator> i; for (i = getReferencesInternal().entrySet().iterator(); i.hasNext();) { Entry entry = i.next(); if (entry.getValue().mode == InliningMode.BLOCK) { i.remove(); } } } public boolean hasBlockInliningReferences() { for (Reference r : getReferencesInternal().values()) { if (r.mode == InliningMode.BLOCK) { return true; } } return false; } public Function getFn() { return fn; } public void setFn(Function fn) { Preconditions.checkState(this.fn == null); this.fn = fn; } public Node getSafeFnNode() { return (safeFnNode != null) ? safeFnNode : fn.getFunctionNode(); } public void setSafeFnNode(Node safeFnNode) { this.safeFnNode = safeFnNode; } public boolean canInline() { return inline; } public void setInline(boolean inline) { this.inline = inline; if (inline == false) { // No need to keep references to function that can't be inlined. references = null; // Don't remove functions that we aren't inlining. remove = false; } } public boolean canRemove() { return remove; } public void setRemove(boolean remove) { this.remove = remove; } public boolean canInlineDirectly() { return inlineDirectly; } public void inlineDirectly(boolean directReplacement) { this.inlineDirectly = directReplacement; } public boolean hasReferences() { return (references != null && !references.isEmpty()); } private Map getReferencesInternal() { if (references == null) { return Collections.emptyMap(); } return references; } public void addReference(Reference ref) { if (references == null) { references = Maps.newLinkedHashMap(); } references.put(ref.callNode, ref); } public Collection getReferences() { return getReferencesInternal().values(); } public Reference getReference(Node n) { return getReferencesInternal().get(n); } public Set getNamesToAlias() { if (namesToAlias == null) { return Collections.emptySet(); } return Collections.unmodifiableSet(namesToAlias); } public void setNamesToAlias(Set names) { namesToAlias = names; } public void setModule(JSModule module) { this.module = module; } public JSModule getModule() { return module; } } /** * Interface for dealing with function declarations and function * expressions equally */ private static interface Function { /** Gets the name of the function */ public String getName(); /** Gets the function node */ public Node getFunctionNode(); /** Removes itself from the JavaScript */ public void remove(); public Node getDeclaringBlock(); } /** NamedFunction implementation of the Function interface */ private static class NamedFunction implements Function { private final Node fn; public NamedFunction(Node fn) { this.fn = fn; } @Override public String getName() { return fn.getFirstChild().getString(); } @Override public Node getFunctionNode() { return fn; } @Override public void remove() { NodeUtil.removeChild(fn.getParent(), fn); } @Override public Node getDeclaringBlock() { return fn.getParent(); } } /** FunctionVar implementation of the Function interface */ private static class FunctionVar implements Function { private final Node var; public FunctionVar(Node var) { this.var = var; } @Override public String getName() { return var.getFirstChild().getString(); } @Override public Node getFunctionNode() { return var.getFirstChild().getFirstChild(); } @Override public void remove() { NodeUtil.removeChild(var.getParent(), var); } @Override public Node getDeclaringBlock() { return var.getParent(); } } /** FunctionExpression implementation of the Function interface */ private static class FunctionExpression implements Function { private final Node fn; private final String fakeName; public FunctionExpression(Node fn, int index) { this.fn = fn; // A number is not a valid function JavaScript identifier // so we don't need to worry about collisions. this.fakeName = String.valueOf(index); } @Override public String getName() { return fakeName; } @Override public Node getFunctionNode() { return fn; } @Override public void remove() { // Nothing to do. The function is removed with the call. } @Override public Node getDeclaringBlock() { return null; } } class Reference extends FunctionInjector.Reference { final boolean requiresDecomposition; boolean inlined = false; Reference( Node callNode, JSModule module, InliningMode mode, boolean decompose) { super(callNode, module, mode); this.requiresDecomposition = decompose; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypedCodeGenerator.java0000644000175000017500000001650512115204405027235 0ustar apoapo/* * 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Set; /** * A code generator that outputs type annotations for functions and * constructors. */ class TypedCodeGenerator extends CodeGenerator { private final JSTypeRegistry registry; TypedCodeGenerator( CodeConsumer consumer, CompilerOptions options, JSTypeRegistry registry) { super(consumer, options); Preconditions.checkNotNull(registry); this.registry = registry; } @Override void add(Node n, Context context) { Node parent = n.getParent(); if (parent != null && (parent.isBlock() || parent.isScript())) { if (n.isFunction()) { add(getFunctionAnnotation(n)); } else if (n.isExprResult() && n.getFirstChild().isAssign()) { Node rhs = n.getFirstChild().getLastChild(); add(getTypeAnnotation(rhs)); } else if (n.isVar() && n.getFirstChild().getFirstChild() != null) { add(getTypeAnnotation(n.getFirstChild().getFirstChild())); } } super.add(n, context); } private String getTypeAnnotation(Node node) { // Only add annotations for things with JSDoc, or function literals. JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node); if (jsdoc == null && !node.isFunction()) { return ""; } JSType type = node.getJSType(); if (type == null) { return ""; } else if (type.isFunctionType()) { return getFunctionAnnotation(node); } else if (type.isEnumType()) { return "/** @enum {" + type.toMaybeEnumType().getElementsType().toAnnotationString() + "} */\n"; } else if (!type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.isFunction()); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = type.toMaybeFunctionType(); if (JSType.isEquivalent( type, registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE))) { return "/** @type {!Function} */\n"; } StringBuilder sb = new StringBuilder("/**\n"); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * "); appendAnnotation(sb, "param", getParameterNodeJSDocType(n)); sb.append(" ") .append(paramNode.getString()) .append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isEmptyType() && // There is no annotation for the empty type. !funType.isInterface()) { // Interfaces never return a value. sb.append(" * "); appendAnnotation(sb, "return", retType.toAnnotationString()); sb.append("\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { FunctionType superConstructor = funType.getSuperClassConstructor(); if (superConstructor != null) { ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType(); if (!superInstance.toString().equals("Object")) { sb.append(" * "); appendAnnotation(sb, "extends", superInstance.toAnnotationString()); sb.append("\n"); } } if (funType.isInterface()) { for (ObjectType interfaceType : funType.getExtendedInterfaces()) { sb.append(" * "); appendAnnotation(sb, "extends", interfaceType.toAnnotationString()); sb.append("\n"); } } // Avoid duplicates, add implemented type to a set first Set interfaces = Sets.newTreeSet(); for (ObjectType interfaze : funType.getImplementedInterfaces()) { interfaces.add(interfaze.toAnnotationString()); } for (String interfaze : interfaces) { sb.append(" * "); appendAnnotation(sb, "implements", interfaze); sb.append("\n"); } if (funType.isConstructor()) { sb.append(" * @constructor\n"); } else if (funType.isInterface()) { sb.append(" * @interface\n"); } } if (!funType.getTemplateTypeMap().getTemplateKeys().isEmpty()) { sb.append(" * @template "); sb.append(Joiner.on(",").join( funType.getTemplateTypeMap().getTemplateKeys())); sb.append("\n"); } if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) { sb.append(" * @javadispatch\n"); } sb.append(" */\n"); return sb.toString(); } private void appendAnnotation(StringBuilder sb, String name, String type) { sb.append("@").append(name).append(" {").append(type).append("}"); } /** * Creates a JSDoc-suitable String representation the type of a parameter. * * @param parameterNode The parameter node. */ private String getParameterNodeJSDocType(Node parameterNode) { JSType parameterType = parameterNode.getJSType(); String typeString; if (parameterNode.isOptionalArg()) { typeString = restrictByUndefined(parameterType).toAnnotationString() + "="; } else if (parameterNode.isVarArgs()) { typeString = "..." + restrictByUndefined(parameterType).toAnnotationString(); } else { typeString = parameterType.toAnnotationString(); } return typeString; } private JSType restrictByUndefined(JSType type) { if (type.isUnionType()) { return type.toMaybeUnionType().getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } return type; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NodeUtil.java0000644000175000017500000027422612115204405025237 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticSourceFile; import com.google.javascript.rhino.jstype.TernaryValue; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * NodeUtil contains generally useful AST utilities. * */ public final class NodeUtil { static final long MAX_POSITIVE_INTEGER_NUMBER = (long) Math.pow(2, 53); static final String JSC_PROPERTY_NAME_FN = "JSCompiler_renameProperty"; static final char LARGEST_BASIC_LATIN = 0x7f; /** the set of builtin constructors that don't have side effects. */ private static final Set CONSTRUCTORS_WITHOUT_SIDE_EFFECTS = new HashSet(Arrays.asList( "Array", "Date", "Error", "Object", "RegExp", "XMLHttpRequest")); // Utility class; do not instantiate. private NodeUtil() {} /** * Gets the boolean value of a node that represents a expression. This method * effectively emulates the Boolean() JavaScript cast function. * Note: unlike getBooleanValue this function does not return UNKNOWN * for expressions with side-effects. */ static TernaryValue getImpureBooleanValue(Node n) { switch (n.getType()) { case Token.ASSIGN: case Token.COMMA: // For ASSIGN and COMMA the value is the value of the RHS. return getImpureBooleanValue(n.getLastChild()); case Token.NOT: TernaryValue value = getImpureBooleanValue(n.getLastChild()); return value.not(); case Token.AND: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.and(rhs); } case Token.OR: { TernaryValue lhs = getImpureBooleanValue(n.getFirstChild()); TernaryValue rhs = getImpureBooleanValue(n.getLastChild()); return lhs.or(rhs); } case Token.HOOK: { TernaryValue trueValue = getImpureBooleanValue( n.getFirstChild().getNext()); TernaryValue falseValue = getImpureBooleanValue(n.getLastChild()); if (trueValue.equals(falseValue)) { return trueValue; } else { return TernaryValue.UNKNOWN; } } case Token.ARRAYLIT: case Token.OBJECTLIT: // ignoring side-effects return TernaryValue.TRUE; case Token.VOID: return TernaryValue.FALSE; default: return getPureBooleanValue(n); } } /** * Gets the boolean value of a node that represents a literal. This method * effectively emulates the Boolean() JavaScript cast function * except it return UNKNOWN for known values with side-effects, use * getImpureBooleanValue if you don't care about side-effects. */ static TernaryValue getPureBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return TernaryValue.forBoolean(n.getString().length() > 0); case Token.NUMBER: return TernaryValue.forBoolean(n.getDouble() != 0); case Token.NOT: return getPureBooleanValue(n.getLastChild()).not(); case Token.NULL: case Token.FALSE: return TernaryValue.FALSE; case Token.VOID: if (!mayHaveSideEffects(n.getFirstChild())) { return TernaryValue.FALSE; } break; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return TernaryValue.FALSE; } else if ("Infinity".equals(name)) { return TernaryValue.TRUE; } break; case Token.TRUE: case Token.REGEXP: return TernaryValue.TRUE; case Token.ARRAYLIT: case Token.OBJECTLIT: if (!mayHaveSideEffects(n)) { return TernaryValue.TRUE; } break; } return TernaryValue.UNKNOWN; } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * String() JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): regex literals as well. switch (n.getType()) { case Token.STRING: case Token.STRING_KEY: return n.getString(); case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name)) { return name; } break; case Token.NUMBER: return getStringValue(n.getDouble()); case Token.FALSE: return "false"; case Token.TRUE: return "true"; case Token.NULL: return "null"; case Token.VOID: return "undefined"; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? "false" : "true"; // reversed. } break; case Token.ARRAYLIT: return arrayToString(n); case Token.OBJECTLIT: return "[object Object]"; } return null; } static String getStringValue(double value) { long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(value); } } /** * When converting arrays to string using Array.prototype.toString or * Array.prototype.join, the rules for conversion to String are different * than converting each element individually. Specifically, "null" and * "undefined" are converted to an empty string. * @param n A node that is a member of an Array. * @return The string representation. */ static String getArrayElementStringValue(Node n) { return (NodeUtil.isNullOrUndefined(n) || n.isEmpty()) ? "" : getStringValue(n); } static String arrayToString(Node literal) { Node first = literal.getFirstChild(); StringBuilder result = new StringBuilder(); for (Node n = first; n != null; n = n.getNext()) { String childValue = getArrayElementStringValue(n); if (childValue == null) { return null; } if (n != first) { result.append(','); } result.append(childValue); } return result.toString(); } /** * Gets the value of a node as a Number, or null if it cannot be converted. * When it returns a non-null Double, this method effectively emulates the * Number() JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER: return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) { return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 16)); } catch (NumberFormatException e) { return Double.NaN; } } if (s.length() > 3 && (s.charAt(0) == '-' || s.charAt(0) == '+') && s.charAt(1) == '0' && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { // hex numbers with explicit signs vary between browsers. return null; } // Firefox and IE treat the "Infinity" differently. Firefox is case // insensitive, but IE treats "infinity" as NaN. So leave it alone. if (s.equals("infinity") || s.equals("-infinity") || s.equals("+infinity")) { return null; } try { return Double.parseDouble(s); } catch (NumberFormatException e) { return Double.NaN; } } static String trimJsWhiteSpace(String s) { int start = 0; int end = s.length(); while (end > 0 && isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) { end--; } while (start < end && isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) { start++; } return s.substring(start, end); } /** * Copied from Rhino's ScriptRuntime */ public static TernaryValue isStrWhiteSpaceChar(int c) { switch (c) { case '\u000B': // return TernaryValue.UNKNOWN; // IE says "no", ECMAScript says "yes" case ' ': // case '\n': // case '\r': // case '\t': // case '\u00A0': // case '\u000C': // case '\u2028': // case '\u2029': // case '\uFEFF': // return TernaryValue.TRUE; default: return (Character.getType(c) == Character.SPACE_SEPARATOR) ? TernaryValue.TRUE : TernaryValue.FALSE; } } /** * Gets the function's name. This method recognizes five forms: *
        *
      • {@code function name() ...}
      • *
      • {@code var name = function() ...}
      • *
      • {@code qualified.name = function() ...}
      • *
      • {@code var name2 = function name1() ...}
      • *
      • {@code qualified.name2 = function name1() ...}
      • *
      * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Preconditions.checkState(n.isFunction()); Node parent = n.getParent(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getQualifiedName(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... String name = n.getFirstChild().getQualifiedName(); return name; } } /** * Gets the function's name. This method recognizes the forms: *
        *
      • {@code {'name': function() ...}}
      • *
      • {@code {name: function() ...}}
      • *
      • {@code function name() ...}
      • *
      • {@code var name = function() ...}
      • *
      • {@code qualified.name = function() ...}
      • *
      • {@code var name2 = function name1() ...}
      • *
      • {@code qualified.name2 = function name1() ...}
      • *
      * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ public static String getNearestFunctionName(Node n) { if (!n.isFunction()) { return null; } String name = getFunctionName(n); if (name != null) { return name; } // Check for the form { 'x' : function() { } } Node parent = n.getParent(); switch (parent.getType()) { case Token.SETTER_DEF: case Token.GETTER_DEF: case Token.STRING_KEY: // Return the name of the literal's key. return parent.getString(); case Token.NUMBER: return getStringValue(parent); } return null; } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: return true; case Token.CAST: case Token.NOT: return isImmutableValue(n.getFirstChild()); case Token.VOID: case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if the operator on this node is symmetric */ static boolean isSymmetricOperation(Node n) { switch (n.getType()) { case Token.EQ: // equal case Token.NE: // not equal case Token.SHEQ: // exactly equal case Token.SHNE: // exactly not equal case Token.MUL: // multiply, unlike add it only works on numbers // or results NaN if any of the operators is not a number return true; } return false; } /** * Returns true if the operator on this node is relational. * the returned set does not include the equalities. */ static boolean isRelationalOperation(Node n) { switch (n.getType()) { case Token.GT: // equal case Token.GE: // not equal case Token.LT: // exactly equal case Token.LE: // exactly not equal return true; } return false; } /** * Returns the inverse of an operator if it is invertible. * ex. '>' ==> '<' */ static int getInverseOperator(int type) { switch (type) { case Token.GT: return Token.LT; case Token.LT: return Token.GT; case Token.GE: return Token.LE; case Token.LE: return Token.GE; } return Token.ERROR; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * the name a is not. * * Function literals do not meet this definition, because they * lexically capture variables. For example, if you have * * function() { return a; } * * If it is evaluated in a different scope, then it * captures a different variable. Even if the function did not read * any captured variables directly, it would still fail this definition, * because it affects the lifecycle of variables in the enclosing scope. * * However, a function literal with respect to a particular scope is * a literal. * * @param includeFunctions If true, all function expressions will be * treated as literals. */ static boolean isLiteralValue(Node n, boolean includeFunctions) { switch (n.getType()) { case Token.CAST: return isLiteralValue(n.getFirstChild(), includeFunctions); case Token.ARRAYLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Unary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (!block.isBlock()) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (!n.isEmpty()) { return false; } } return true; } static boolean isSimpleOperator(Node n) { return isSimpleOperatorType(n.getType()); } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GETELEM: case Token.GETPROP: case Token.GT: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.NOT: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.TYPEOF: case Token.VOID: case Token.POS: case Token.NEG: case Token.URSH: return true; default: return false; } } /** * Creates an EXPR_RESULT. * * @param child The expression itself. * @return Newly created EXPR node with the child as subexpression. */ static Node newExpr(Node child) { return IR.exprResult(child).srcref(child); } /** * Returns true if the node may create new mutable state, or change existing * state. * * @see
      XKCD Cartoon */ static boolean mayEffectMutableState(Node n) { return mayEffectMutableState(n, null); } static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, true, compiler); } /** * Returns true if the node which may have side effects when executed. */ static boolean mayHaveSideEffects(Node n) { return mayHaveSideEffects(n, null); } static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) { return checkForStateChangeHelper(n, false, compiler); } /** * Returns true if some node in n's subtree changes application state. * If {@code checkForNewObjects} is true, we assume that newly created * mutable objects (like object literals) change state. Otherwise, we assume * that they have no side effects. */ private static boolean checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.CAST: case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.PARAM_LIST: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.STRING_KEY: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: if (checkForNewObjects) { return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper( c.getFirstChild(), checkForNewObjects, compiler)) { return true; } } return false; case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) { return true; } break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return checkForNewObjects || !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperator(n)) { break; } if (isAssignmentOp(n)) { Node assignTarget = n.getFirstChild(); if (assignTarget.isName()) { return true; } // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } if (isGet(assignTarget)) { // If the object being assigned to is a local object, don't // consider this a side-effect as it can't be referenced // elsewhere. Don't do this recursively as the property might // be an alias of another object, unlike a literal below. Node current = assignTarget.getFirstChild(); if (evaluatesToLocalValue(current)) { return false; } // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of // other objects. // If the root object is a literal don't consider this a // side-effect. while (isGet(current)) { current = current.getFirstChild(); } return !isLiteralValue(current, true); } else { // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior. return !isLiteralValue(assignTarget, true); } } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - constructor call node */ static boolean constructorCallHasSideEffects(Node callNode) { return constructorCallHasSideEffects(callNode, null); } static boolean constructorCallHasSideEffects( Node callNode, AbstractCompiler compiler) { if (!callNode.isNew()) { throw new IllegalStateException( "Expected NEW node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.isName() && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } // A list of built-in object creation or primitive type cast functions that // can also be called as constructors but lack side-effects. // TODO(johnlenz): consider adding an extern annotation for this. private static final Set BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS = ImmutableSet.of( "Object", "Array", "String", "Number", "Boolean", "RegExp", "Error"); private static final Set OBJECT_METHODS_WITHOUT_SIDEEFFECTS = ImmutableSet.of("toString", "valueOf"); private static final Set REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { if (!callNode.isCall()) { throw new IllegalStateException( "Expected CALL node, got " + Token.name(callNode.getType())); } if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.isName()) { String name = nameNode.getString(); if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) { return false; } } else if (nameNode.isGetProp()) { if (callNode.hasOneChild() && OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains( nameNode.getLastChild().getString())) { return false; } if (callNode.isOnlyModifiesThisCall() && evaluatesToLocalValue(nameNode.getFirstChild())) { return false; } // Math.floor has no side-effects. // TODO(nicksantos): This is a terrible terrible hack, until // I create a definitionProvider that understands namespacing. if (nameNode.getFirstChild().isName()) { if ("Math.floor".equals(nameNode.getQualifiedName())) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().isRegExp() && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().isString() && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.isString() || param.isRegExp())) { return false; } } } } return true; } /** * @return Whether the call has a local result. */ static boolean callHasLocalResult(Node n) { Preconditions.checkState(n.isCall()); return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0; } /** * @return Whether the new has a local result. */ static boolean newHasLocalResult(Node n) { Preconditions.checkState(n.isNew()); return n.isOnlyModifiesThisCall(); } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { return nodeTypeMayHaveSideEffects(n, null); } static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.DELPROP: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.CALL: return NodeUtil.functionCallHasSideEffects(n, compiler); case Token.NEW: return NodeUtil.constructorCallHasSideEffects(n, compiler); case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return 3; case Token.AND: return 4; case Token.BITOR: return 5; case Token.BITXOR: return 6; case Token.BITAND: return 7; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: return 8; case Token.LT: case Token.GT: case Token.LE: case Token.GE: case Token.INSTANCEOF: case Token.IN: return 9; case Token.LSH: case Token.RSH: case Token.URSH: return 10; case Token.SUB: case Token.ADD: return 11; case Token.MUL: case Token.MOD: case Token.DIV: return 12; case Token.INC: case Token.DEC: case Token.NEW: case Token.DELPROP: case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: return 13; case Token.CALL: case Token.GETELEM: case Token.GETPROP: // Data values case Token.ARRAYLIT: case Token.EMPTY: // TODO(johnlenz): remove this. case Token.FALSE: case Token.FUNCTION: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.STRING: case Token.STRING_KEY: case Token.THIS: case Token.TRUE: return 15; case Token.CAST: return 16; default: throw new Error("Unknown precedence for " + Token.name(type) + " (type " + type + ")"); } } static boolean isUndefined(Node n) { switch (n.getType()) { case Token.VOID: return true; case Token.NAME: return n.getString().equals("undefined"); } return false; } static boolean isNullOrUndefined(Node n) { return n.isNull() || isUndefined(n); } static final Predicate IMMUTABLE_PREDICATE = new Predicate() { @Override public boolean apply(Node n) { return isImmutableValue(n); } }; static boolean isImmutableResult(Node n) { return allResultsMatch(n, IMMUTABLE_PREDICATE); } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean allResultsMatch(Node n, Predicate p) { switch (n.getType()) { case Token.CAST: return allResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return allResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return allResultsMatch(n.getFirstChild(), p) && allResultsMatch(n.getLastChild(), p); case Token.HOOK: return allResultsMatch(n.getFirstChild().getNext(), p) && allResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } /** * Apply the supplied predicate against * all possible result Nodes of the expression. */ static boolean anyResultsMatch(Node n, Predicate p) { switch (n.getType()) { case Token.CAST: return anyResultsMatch(n.getFirstChild(), p); case Token.ASSIGN: case Token.COMMA: return anyResultsMatch(n.getLastChild(), p); case Token.AND: case Token.OR: return anyResultsMatch(n.getFirstChild(), p) || anyResultsMatch(n.getLastChild(), p); case Token.HOOK: return anyResultsMatch(n.getFirstChild().getNext(), p) || anyResultsMatch(n.getLastChild(), p); default: return p.apply(n); } } static class NumbericResultPredicate implements Predicate { @Override public boolean apply(Node n) { return isNumericResultHelper(n); } } static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE = new NumbericResultPredicate(); /** * Returns true if the result of node evaluation is always a number */ static boolean isNumericResult(Node n) { return allResultsMatch(n, NUMBERIC_RESULT_PREDICATE); } static boolean isNumericResultHelper(Node n) { switch (n.getType()) { case Token.ADD: return !mayBeString(n.getFirstChild()) && !mayBeString(n.getLastChild()); case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.LSH: case Token.RSH: case Token.URSH: case Token.SUB: case Token.MUL: case Token.MOD: case Token.DIV: case Token.INC: case Token.DEC: case Token.POS: case Token.NEG: case Token.NUMBER: return true; case Token.NAME: String name = n.getString(); if (name.equals("NaN")) { return true; } if (name.equals("Infinity")) { return true; } return false; default: return false; } } static class BooleanResultPredicate implements Predicate { @Override public boolean apply(Node n) { return isBooleanResultHelper(n); } } static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE = new BooleanResultPredicate(); /** * @return Whether the result of node evaluation is always a boolean */ static boolean isBooleanResult(Node n) { return allResultsMatch(n, BOOLEAN_RESULT_PREDICATE); } static boolean isBooleanResultHelper(Node n) { switch (n.getType()) { // Primitives case Token.TRUE: case Token.FALSE: // Comparisons case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.LT: case Token.GT: case Token.LE: case Token.GE: // Queries case Token.IN: case Token.INSTANCEOF: // Inversion case Token.NOT: // delete operator returns a boolean. case Token.DELPROP: return true; default: return false; } } static class MayBeStringResultPredicate implements Predicate { @Override public boolean apply(Node n) { return mayBeStringHelper(n); } } static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE = new MayBeStringResultPredicate(); /** * @returns Whether the results is possibly a string. */ static boolean mayBeString(Node n) { return mayBeString(n, true); } static boolean mayBeString(Node n, boolean recurse) { if (recurse) { return anyResultsMatch(n, MAY_BE_STRING_PREDICATE); } else { return mayBeStringHelper(n); } } static boolean mayBeStringHelper(Node n) { return !isNumericResult(n) && !isBooleanResult(n) && !isUndefined(n) && !n.isNull(); } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } /** * Returns true if the operator is commutative. * e.g. (a * b) * c = c * (b * a) * Note 1: "+" is not commutative because it is also the concatenation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 * Note 2: only operations on literals and pure functions are commutative. */ static boolean isCommutative(int type) { switch (type) { case Token.MUL: case Token.BITOR: case Token.BITXOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assignment op:" + n); } /** * Determines if the given node contains a function statement or function * expression. */ static boolean containsFunction(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the shallow scope contains references to 'this' keyword */ static boolean referencesThis(Node n) { Node start = (n.isFunction()) ? n.getLastChild() : n; return containsType(start, Token.THIS, MATCH_NOT_FUNCTION); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.isGetProp() || n.isGetElem(); } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.isName() && n.getParent().isVar(); } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(n.isName()); Node parent = n.getParent(); if (parent.isVar()) { return n.getFirstChild(); } else if (parent.isAssign() && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.isExprResult() && n.getFirstChild().isAssign(); } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.isExprResult() && n.getFirstChild().isCall(); } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.isFor() && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild(); case Token.DO: return n.getFirstChild(); default: return null; } } /** * @return Whether the specified node has a loop parent that * is within the current scope. */ static boolean isWithinLoop(Node n) { for (Node parent : n.getAncestors()) { if (NodeUtil.isLoopStructure(parent)) { return true; } if (parent.isFunction()) { break; } } return false; } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT_CASE: return true; default: return false; } } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT_CASE: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.isScript() || n.isBlock(); } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { return isStatementParent(n.getParent()); } static boolean isStatementParent(Node parent) { // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.isCase() || n.isDefaultCase(); } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return n.isName() && !n.getString().isEmpty(); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.isTry() && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Whether the node is a CATCH container BLOCK. */ static boolean isTryCatchNodeContainer(Node n) { Node parent = n.getParent(); return parent.isTry() && parent.getFirstChild().getNext() == n; } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { if (isTryFinallyNode(parent, node)) { if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) { // A finally can only be removed if there is a catch. parent.removeChild(node); } else { // Otherwise, only its children can be removed. node.detachChildren(); } } else if (node.isCatch()) { // The CATCH can can only be removed if there is a finally clause. Node tryNode = node.getParent().getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren(); } else if (node.isBlock()) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (isStatementBlock(parent) || isSwitchCase(node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.isVar()) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (parent.isLabel() && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.isFor() && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, IR.empty()); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of " + parent.toString()); } } /** * Add a finally block if one does not exist. */ static void maybeAddFinally(Node tryNode) { Preconditions.checkState(tryNode.isTry()); if (!NodeUtil.hasFinally(tryNode)) { tryNode.addChildrenToBack(IR.block().srcref(tryNode)); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.isBlock()); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else { return false; } } /** * @param node A node * @return Whether the call is a NEW or CALL node. */ static boolean isCallOrNew(Node node) { return node.isCall() || node.isNew(); } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(fn.isFunction()); return fn.getLastChild(); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. * *

      Some examples of function expressions: *

         * (function () {})
         * (function f() {})()
         * [ function f() {} ]
         * var f = function f() {};
         * for (function f() {};;) {}
         * 
      * *

      Some examples of functions that are not expressions: *

         * function f() {}
         * if (x); else function f() {}
         * for (;;) { function f() {} }
         * 
      * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.isFunction() && !isStatement(n); } /** * Returns whether this is a bleeding function (an anonymous named function * that bleeds into the inner scope). */ static boolean isBleedingFunctionName(Node n) { return n.isName() && !n.getString().isEmpty() && isFunctionExpression(n.getParent()); } /** * Determines if a node is a function expression that has an empty body. * * @param node a node * @return whether the given node is a function expression that is empty */ static boolean isEmptyFunctionExpression(Node node) { return isFunctionExpression(node) && isEmptyBlock(node.getLastChild()); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { // TODO(johnlenz): rename this function Preconditions.checkArgument(function.isFunction()); return isNameReferenced( function.getLastChild(), "arguments", MATCH_NOT_FUNCTION); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.isCall()) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.isString()) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isVarOrSimpleAssignLhs(Node n, Node parent) { return (parent.isAssign() && parent.getFirstChild() == n) || parent.isVar(); } /** * Determines whether this node is used as an L-value. Notice that sometimes * names are used as both L-values and R-values. * * We treat "var x;" as a pseudo-L-value, which kind of makes sense if you * treat it as "assignment to 'undefined' at the top of the scope". But if * we're honest with ourselves, it doesn't make sense, and we only do this * because it makes sense to treat this as syntactically similar to * "var x = 0;". * * @param n The node * @return True if n is an L-value. */ public static boolean isLValue(Node n) { Preconditions.checkArgument(n.isName() || n.isGetProp() || n.isGetElem()); Node parent = n.getParent(); if (parent == null) { return false; } return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) || parent.isVar() || (parent.isFunction() && parent.getFirstChild() == n) || parent.isDec() || parent.isInc() || parent.isParamList() || parent.isCatch(); } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node */ static boolean isObjectLitKey(Node node) { switch (node.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); valueType = fntype.getReturnType(); } else { return null; } break; case Token.SETTER_DEF: if (valueType.isFunctionType()) { // SET must always return a function type. FunctionType fntype = valueType.toMaybeFunctionType(); Node param = fntype.getParametersNode().getFirstChild(); // SET function must always have one parameter. valueType = param.getJSType(); } else { return null; } break; } } return valueType; } /** * Determines whether a node represents an object literal get or set key * (e.g. key1 in {get key1() {}, set key2(a){}). * * @param node A node */ static boolean isGetOrSetKey(Node node) { switch (node.getType()) { case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|"; case Token.OR: return "||"; case Token.BITXOR: return "^"; case Token.AND: return "&&"; case Token.BITAND: return "&"; case Token.SHEQ: return "==="; case Token.EQ: return "=="; case Token.NOT: return "!"; case Token.NE: return "!="; case Token.SHNE: return "!=="; case Token.LSH: return "<<"; case Token.IN: return "in"; case Token.LE: return "<="; case Token.LT: return "<"; case Token.URSH: return ">>>"; case Token.RSH: return ">>"; case Token.GE: return ">="; case Token.GT: return ">"; case Token.MUL: return "*"; case Token.DIV: return "/"; case Token.MOD: return "%"; case Token.BITNOT: return "~"; case Token.ADD: return "+"; case Token.SUB: return "-"; case Token.POS: return "+"; case Token.NEG: return "-"; case Token.ASSIGN: return "="; case Token.ASSIGN_BITOR: return "|="; case Token.ASSIGN_BITXOR: return "^="; case Token.ASSIGN_BITAND: return "&="; case Token.ASSIGN_LSH: return "<<="; case Token.ASSIGN_RSH: return ">>="; case Token.ASSIGN_URSH: return ">>>="; case Token.ASSIGN_ADD: return "+="; case Token.ASSIGN_SUB: return "-="; case Token.ASSIGN_MUL: return "*="; case Token.ASSIGN_DIV: return "/="; case Token.ASSIGN_MOD: return "%="; case Token.VOID: return "void"; case Token.TYPEOF: return "typeof"; case Token.INSTANCEOF: return "instanceof"; default: return null; } } /** * Converts an operator's token value (see {@link Token}) to a string * representation or fails. * * @param operator the operator's token value to convert * @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript()); return addingRoot; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode( CodingConvention convention, String name) { int endPos = name.indexOf('.'); if (endPos == -1) { return newName(convention, name); } Node node; String nodeName = name.substring(0, endPos); if ("this".equals(nodeName)) { node = IR.thisNode(); } else { node = newName(convention, nodeName); } int startPos; do { startPos = endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); Node propNode = IR.string(part); if (convention.isConstantKey(part)) { propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } node = IR.getprop(node, propNode); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode( CodingConvention convention, String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(convention, name); setDebugInformation(node, basisNode, originalName); return node; } /** * Gets the root node of a qualified name. Must be either NAME or THIS. */ static Node getRootOfQualifiedName(Node qName) { for (Node current = qName; true; current = current.getFirstChild()) { if (current.isName() || current.isThis()) { return current; } Preconditions.checkState(current.isGetProp()); } } /** * Sets the debug information (source file info and original name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copyInformationFromForTree(basisNode); node.putProp(Node.ORIGINALNAME_PROP, originalName); } private static Node newName( CodingConvention convention, String name) { Node nameNode = IR.name(name); if (convention.isConstant(name)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } return nameNode; } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node. * * @param name The name for the new NAME node. * @param srcref The node that represents the name as currently found in * the AST. * * @return The node created. */ static Node newName(CodingConvention convention, String name, Node srcref) { return newName(convention, name).srcref(srcref); } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return The node created. */ static Node newName( CodingConvention convention, String name, Node basisNode, String originalName) { Node nameNode = newName(convention, name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name is a valid variable name. */ static boolean isValidSimpleName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, Unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } /** * Determines whether the given name is a valid qualified name. */ // TODO(nicksantos): This should be moved into a "Language" API, // so that the results are different for es5 and es3. public static boolean isValidQualifiedName(String name) { if (name.endsWith(".") || name.startsWith(".")) { return false; } String[] parts = name.split("\\."); for (String part : parts) { if (!isValidSimpleName(part)) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return isValidSimpleName(name); } private static class VarCollector implements Visitor { final Map vars = Maps.newLinkedHashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ static Collection getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { return isExprAssign(n) && isPrototypeProperty(n.getFirstChild().getFirstChild()); } /** * @return Whether the node represents a qualified prototype property. */ static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); return lhsString != null && lhsString.contains(".prototype."); } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = IR.voidNode(IR.number(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = IR.name(name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.srcref(value); } Node var = IR.var(nodeName).srcref(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate{ final String name; MatchNameNode(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */ private static class MatchNotFunction implements Predicate{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate traverseChildrenPred) { return getCount(node, new MatchNodeType(type), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name, Predicate traverseChildrenPred) { return has(node, new MatchNameNode(name), traverseChildrenPred); } /** * Whether a simple name is referenced within the node tree. */ static boolean isNameReferenced(Node node, String name) { return isNameReferenced(node, name, Predicates.alwaysTrue()); } /** * Finds the number of times a simple name is referenced within the node tree. */ static int getNameReferenceCount(Node node, String name) { return getCount( node, new MatchNameNode(name), Predicates.alwaysTrue()); } /** * @return Whether the predicate is true for the node or any of its children. */ static boolean has(Node node, Predicate pred, Predicate traverseChildrenPred) { if (pred.apply(node)) { return true; } if (!traverseChildrenPred.apply(node)) { return false; } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (has(c, pred, traverseChildrenPred)) { return true; } } return false; } /** * @return The number of times the the predicate is true for the node * or any of its children. */ static int getCount( Node n, Predicate pred, Predicate traverseChildrenPred) { int total = 0; if (pred.apply(n)) { total++; } if (traverseChildrenPred.apply(n)) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { total += getCount(c, pred, traverseChildrenPred); } } return total; } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor visitor, Predicate traverseChildrenPred) { visitor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, visitor, traverseChildrenPred); } } } /** * A post-order traversal, calling Visitor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor visitor, Predicate traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, visitor, traverseChildrenPred); } } visitor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.isTry()); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.isTry()); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.isBlock()); return n.hasChildren() && n.getFirstChild().isCatch(); } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ public static Node getFunctionParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.isFunction()); return fnNode.getFirstChild().getNext(); } /** *

      Determines whether a variable is constant: *

        *
      1. In Normalize, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. *
      * * @param node A NAME or STRING node * @return True if a name node represents a constant variable */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(node.getString()); } else if (isObjectLitKey(node)) { return convention.isConstantKey(node.getString()); } else if (node.isName()) { return convention.isConstant(node.getString()); } return false; } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = n.getSourceFileName(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static StaticSourceFile getSourceFile(Node n) { StaticSourceFile sourceName = null; while (sourceName == null && n != null) { sourceName = n.getStaticSourceFile(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The InputId property on the node or its ancestors. */ public static InputId getInputId(Node n) { while (n != null && !n.isScript()) { n = n.getParent(); } return (n != null && n.isScript()) ? n.getInputId() : null; } /** * A new CALL node with the "FREE_CALL" set based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = !isGet(callTarget); Node call = IR.call(callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate locals) { switch (value.getType()) { case Token.CAST: return evaluatesToLocalValue(value.getFirstChild(), locals); case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.INC: case Token.DEC: if (value.getBooleanProp(Node.INCRDECR_PROP)) { return evaluatesToLocalValue(value.getFirstChild(), locals); } else { return true; } case Token.THIS: return locals.apply(value); case Token.NAME: return isImmutableValue(value) || locals.apply(value); case Token.GETELEM: case Token.GETPROP: // There is no information about the locality of object properties. return locals.apply(value); case Token.CALL: return callHasLocalResult(value) || isToStringMethodCall(value) || locals.apply(value); case Token.NEW: return newHasLocalResult(value) || locals.apply(value); case Token.FUNCTION: case Token.REGEXP: case Token.ARRAYLIT: case Token.OBJECTLIT: // Literals objects with non-literal children are allowed. return true; case Token.DELPROP: case Token.IN: // TODO(johnlenz): should IN operator be included in #isSimpleOperator? return true; default: // Other op force a local value: // x = '' + g (x is now an local string) // x -= g (x is now an local number) if (isAssignmentOp(value) || isSimpleOperator(value) || isImmutableValue(value)) { return true; } throw new IllegalStateException( "Unexpected expression node" + value + "\n parent:" + value.getParent()); } } /** * Given the first sibling, this returns the nth * sibling or null if no such sibling exists. * This is like "getChildAtIndex" but returns null for non-existent indexes. */ private static Node getNthSibling(Node first, int index) { Node sibling = first; while (index != 0 && sibling != null) { sibling = sibling.getNext(); index--; } return sibling; } /** * Given the function, this returns the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } /** * Returns whether this is a target of a call or new. */ static boolean isCallOrNewTarget(Node target) { Node parent = target.getParent(); return parent != null && NodeUtil.isCallOrNew(parent) && parent.getFirstChild() == target; } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { Node parent = n.getParent(); if (parent == null) { return null; } if (parent.isName()) { return getBestJSDocInfo(parent); } else if (parent.isAssign()) { return getBestJSDocInfo(parent); } else if (isObjectLitKey(parent)) { return parent.getJSDocInfo(); } else if (parent.isFunction()) { return parent.getJSDocInfo(); } else if (parent.isVar() && parent.hasOneChild()) { return parent.getJSDocInfo(); } else if ((parent.isHook() && parent.getFirstChild() != n) || parent.isOr() || parent.isAnd() || (parent.isComma() && parent.getFirstChild() != n)) { return getBestJSDocInfo(parent); } else if (parent.isCast()) { return parent.getJSDocInfo(); } } return info; } /** Find the l-value that the given r-value is being assigned to. */ static Node getBestLValue(Node n) { Node parent = n.getParent(); boolean isFunctionDeclaration = isFunctionDeclaration(n); if (isFunctionDeclaration) { return n.getFirstChild(); } else if (parent.isName()) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild(); } else if (isObjectLitKey(parent)) { return parent; } else if ( (parent.isHook() && parent.getFirstChild() != n) || parent.isOr() || parent.isAnd() || (parent.isComma() && parent.getFirstChild() != n)) { return getBestLValue(parent); } else if (parent.isCast()) { return getBestLValue(parent); } return null; } /** Gets the r-value of a node returned by getBestLValue. */ static Node getRValueOfLValue(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.ASSIGN: return n.getNext(); case Token.VAR: return n.getFirstChild(); case Token.FUNCTION: return parent; } return null; } /** Get the owner of the given l-value node. */ static Node getBestLValueOwner(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue)) { return getBestLValue(lValue.getParent()); } else if (isGet(lValue)) { return lValue.getFirstChild(); } return null; } /** Get the name of the given l-value node. */ static String getBestLValueName(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue)) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner); if (ownerName != null) { return ownerName + "." + getObjectLitKeyName(lValue); } } return null; } return lValue.getQualifiedName(); } /** * @returns false iff the result of the expression is not consumed. */ static boolean isExpressionResultUsed(Node expr) { // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. Node parent = expr.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.EXPR_RESULT: return false; case Token.CAST: return isExpressionResultUsed(parent); case Token.HOOK: case Token.AND: case Token.OR: return (expr == parent.getFirstChild()) ? true : isExpressionResultUsed(parent); case Token.COMMA: Node gramps = parent.getParent(); if (gramps.isCall() && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See ECMA-262 S15.1.2.1. So it's OK for the first // expression to a comma to be a no-op if it's used to indirect // an eval. This we pretend that this is "used". if (expr == parent.getFirstChild() && parent.getChildCount() == 2 && expr.getNext().isName() && "eval".equals(expr.getNext().getString())) { return true; } } return (expr == parent.getFirstChild()) ? false : isExpressionResultUsed(parent); case Token.FOR: if (!NodeUtil.isForIn(parent)) { // Only an expression whose result is in the condition part of the // expression is used. return (parent.getChildAtIndex(1) == expr); } break; } return true; } /** * @param n The expression to check. * @return Whether the expression is unconditionally executed only once in the * containing execution scope. */ static boolean isExecutedExactlyOnce(Node n) { inspect: do { Node parent = n.getParent(); switch (parent.getType()) { case Token.IF: case Token.HOOK: case Token.AND: case Token.OR: if (parent.getFirstChild() != n) { return false; } // other ancestors may be conditional continue inspect; case Token.FOR: if (NodeUtil.isForIn(parent)) { if (parent.getChildAtIndex(1) != n) { return false; } } else { if (parent.getFirstChild() != n) { return false; } } // other ancestors may be conditional continue inspect; case Token.WHILE: case Token.DO: return false; case Token.TRY: // Consider all code under a try/catch to be conditionally executed. if (!hasFinally(parent) || parent.getLastChild() != n) { return false; } continue inspect; case Token.CASE: case Token.DEFAULT_CASE: return false; case Token.SCRIPT: case Token.FUNCTION: // Done, we've reached the scope root. break inspect; } } while ((n = n.getParent()) != null); return true; } /** * @return An appropriate AST node for the boolean value. */ static Node booleanNode(boolean value) { return value ? IR.trueNode() : IR.falseNode(); } /** * @return An appropriate AST node for the double value. */ static Node numberNode(double value, Node srcref) { Node result; if (Double.isNaN(value)) { result = IR.name("NaN"); } else if (value == Double.POSITIVE_INFINITY) { result = IR.name("Infinity"); } else if (value == Double.NEGATIVE_INFINITY) { result = IR.neg(IR.name("Infinity")); } else { result = IR.number(value); } if (srcref != null) { result.srcrefTree(srcref); } return result; } static boolean isNaN(Node n) { if ((n.isName() && n.getString().equals("NaN")) || (n.getType() == Token.DIV && n.getFirstChild().isNumber() && n.getFirstChild().getDouble() == 0 && n.getLastChild().isNumber() && n.getLastChild().getDouble() == 0)) { return true; } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineProperties.java0000644000175000017500000002446412115204405027004 0ustar apoapo/* * Copyright 2012 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.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.TypeValidator.TypeMismatch; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Map; import java.util.Set; /** * InlineProperties attempts to find references to properties that are known to * be constants and inline the known value. * * This pass relies on type information to find these property references and * properties are assumed to be constant if either: * - the property is assigned unconditionally in the instance constructor * - the property is assigned unconditionally to the type's prototype * * The current implementation only inlines immutable values (as defined by * NodeUtil.isImmutableValue). * * @author johnlenz@google.com (John Lenz) */ public class InlineProperties implements CompilerPass { private final AbstractCompiler compiler; static class PropertyInfo { PropertyInfo(JSType type, Node value) { this.type = type; this.value = value; } final JSType type; final Node value; } private static final PropertyInfo INVALIDATED = new PropertyInfo( null, null); private final Map props = Maps.newHashMap(); private Set invalidatingTypes; InlineProperties(AbstractCompiler compiler) { this.compiler = compiler; buildInvalidatingTypeSet(); } // TODO(johnlenz): this is a direct copy of the invalidation code // from AmbiguateProperties, if in the end we don't need to modify it // we should move it to a common location. private void buildInvalidatingTypeSet() { JSTypeRegistry registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.NULL_TYPE), registry.getNativeType(JSTypeNative.VOID_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.GLOBAL_THIS), registry.getNativeType(JSTypeNative.OBJECT_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA); addInvalidatingType(mis.typeB); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt); } } invalidatingTypes.add(type); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.isInstanceType()) { invalidatingTypes.add(objType.getImplicitPrototype()); } } /** Returns true if properties on this type should not be renamed. */ private boolean isInvalidatingType(JSType type) { if (type.isUnionType()) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { if (isInvalidatingType(alt)) { return true; } } return false; } } ObjectType objType = ObjectType.cast(type); return objType == null || invalidatingTypes.contains(objType) || !objType.hasReferenceName() || objType.isUnknownType() || objType.isEmptyType() /* unresolved types */ || objType.isEnumType() || objType.autoboxesTo() != null; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { return compiler.getTypeRegistry().getNativeType( JSTypeNative.UNKNOWN_TYPE); } else { return jsType; } } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, new GatherCandidates(), externs, root); NodeTraversal.traverseRoots( compiler, new ReplaceCandidates(), externs, root); } class GatherCandidates extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { boolean invalidatingPropRef = false; String propName = null; if (n.isGetProp()) { propName = n.getLastChild().getString(); if (t.getInput().isExtern()) { // Any extern reference invalidates invalidatingPropRef = true; } else if (parent.isAssign()) { invalidatingPropRef = !maybeCandidateDefinition(t, n, parent); } else if (NodeUtil.isLValue(n)) { // Other LValue references invalidate invalidatingPropRef = true; } else if (parent.isDelProp()) { // Deletes invalidate invalidatingPropRef = true; } else { // A property read doesn't invalidate invalidatingPropRef = false; } } else if (n.isStringKey()) { propName = n.getString(); if (t.getInput().isExtern()) { // Any extern reference invalidates invalidatingPropRef = true; } else { // For now, any object literal key invalidates // TODO(johnlenz): support prototype properties like: // foo.prototype = { a: 1, b: 2 }; invalidatingPropRef = true; } } if (invalidatingPropRef) { Preconditions.checkNotNull(propName); invalidateProperty(propName); } } /** * @return Whether this is a valid definition for a candidate property. */ private boolean maybeCandidateDefinition( NodeTraversal t, Node n, Node parent) { Preconditions.checkState(n.isGetProp() && parent.isAssign()); boolean isCandidate = false; Node src = n.getFirstChild(); String propName = n.getLastChild().getString(); Node value = parent.getLastChild(); if (src.isThis()) { // This is a simple assignment like: // this.foo = 1; if (inContructor(t)) { // This maybe a valid assignment. isCandidate = maybeStoreCandidateValue( getJSType(src), propName, value); } } else if (t.inGlobalScope() && src.isGetProp() && src.getLastChild().getString().equals("prototype")) { // This is a prototype assignment like: // x.prototype.foo = 1; JSType instanceType = maybeGetInstanceTypeFromPrototypeRef(src); if (instanceType != null) { isCandidate = maybeStoreCandidateValue( instanceType, propName, value); } } return isCandidate; } private JSType maybeGetInstanceTypeFromPrototypeRef(Node src) { JSType ownerType = getJSType(src.getFirstChild()); if (ownerType.isFunctionType() && ownerType.isConstructor()) { FunctionType functionType = ((FunctionType) ownerType); return functionType.getInstanceType(); } return null; } private void invalidateProperty(String propName) { props.put(propName, INVALIDATED); } private boolean maybeStoreCandidateValue( JSType type, String propName, Node value) { Preconditions.checkNotNull(value); if (!props.containsKey(propName) && !isInvalidatingType(type) && NodeUtil.isImmutableValue(value) && NodeUtil.isExecutedExactlyOnce(value)) { props.put(propName, new PropertyInfo(type, value)); return true; } return false; } private boolean inContructor(NodeTraversal t) { Node root = t.getScopeRoot(); JSDocInfo info = NodeUtil.getBestJSDocInfo(root); return info != null && info.isConstructor(); } } class ReplaceCandidates extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp() && !NodeUtil.isLValue(n)) { Node target = n.getFirstChild(); String propName = n.getLastChild().getString(); PropertyInfo info = props.get(propName); if (info != null && info != INVALIDATED && isMatchingType(target, info.type)) { Node replacement = info.value.cloneTree(); if (NodeUtil.mayHaveSideEffects(n.getFirstChild(), compiler)) { replacement = IR.comma(n.removeFirstChild(), replacement).srcref(n); } parent.replaceChild(n, replacement); compiler.reportCodeChange(); } } } private boolean isMatchingType(Node n, JSType src) { src = src.restrictByNotNullOrUndefined(); JSType dest = getJSType(n).restrictByNotNullOrUndefined(); if (!isInvalidatingType(dest) && dest.isSubtype(src)) { return true; } return false; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ControlFlowAnalysis.java0000644000175000017500000010431212115204405027454 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayDeque; import java.util.Comparator; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.PriorityQueue; /** * This is a compiler pass that computes a control flow graph. * */ final class ControlFlowAnalysis implements Callback, CompilerPass { /** * Based roughly on the first few pages of * * "Declarative Intraprocedural Flow Analysis of Java Source Code by * Nilsson-Nyman, Hedin, Magnusson & Ekman", * * this pass computes the control flow graph from the AST. However, a full * attribute grammar is not necessary. We will compute the flow edges with a * single post order traversal. The "follow()" of a given node will be * computed recursively in a demand driven fashion. * * As of this moment, we are not performing any inter-procedural analysis * within our framework. */ private final AbstractCompiler compiler; private ControlFlowGraph cfg; private Map astPosition; // TODO(nicksantos): should these be node annotations? private Map, Integer> nodePriorities; // We order CFG nodes by by looking at the AST positions. // CFG nodes that come first lexically should be visited first, because // they will often be executed first in the source program. private final Comparator> priorityComparator = new Comparator>() { @Override public int compare( DiGraphNode a, DiGraphNode b) { return astPosition.get(a.getValue()) - astPosition.get(b.getValue()); } }; private int astPositionCounter; private int priorityCounter; private final boolean shouldTraverseFunctions; private final boolean edgeAnnotations; // We need to store where we started, in case we aren't doing a flow analysis // for the whole scope. This happens, for example, when running type inference // on only the externs. private Node root; /* * This stack captures the structure of nested TRY blocks. The top of the * stack is the inner most TRY block. A FUNCTION node in this stack implies * that the handler is determined by the caller of the function at runtime. */ private final Deque exceptionHandler = new ArrayDeque(); /* * This map is used to handle the follow of FINALLY. For example: * * while(x) { * try { * try { * break; * } catch (a) { * } finally { * foo(); * } * fooFollow(); * } catch (b) { * } finally { * bar(); * } * barFollow(); * } * END(); * * In this case finallyMap will contain a map from: * first FINALLY -> bar() * second FINALLY -> END() * * When we are connecting foo() and bar() to to their respective follow, we * must also look up this map and connect: * foo() -> bar() * bar() -> END */ private final Multimap finallyMap = HashMultimap.create(); /** * Constructor. * * @param compiler Compiler instance. * @param shouldTraverseFunctions Whether functions should be traversed (true * by default). * @param edgeAnnotations Whether to allow edge annotations. By default, * only node annotations are allowed. */ ControlFlowAnalysis(AbstractCompiler compiler, boolean shouldTraverseFunctions, boolean edgeAnnotations) { this.compiler = compiler; this.shouldTraverseFunctions = shouldTraverseFunctions; this.edgeAnnotations = edgeAnnotations; } ControlFlowGraph getCfg() { return cfg; } @Override public void process(Node externs, Node root) { this.root = root; astPositionCounter = 0; astPosition = Maps.newHashMap(); nodePriorities = Maps.newHashMap(); cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities, edgeAnnotations); NodeTraversal.traverse(compiler, root, this); astPosition.put(null, ++astPositionCounter); // the implicit return is last. // Now, generate the priority of nodes by doing a depth-first // search on the CFG. priorityCounter = 0; DiGraphNode entry = cfg.getEntry(); prioritizeFromEntryNode(entry); if (shouldTraverseFunctions) { // If we're traversing inner functions, we need to rank the // priority of them too. for (DiGraphNode candidate : cfg.getDirectedGraphNodes()) { Node value = candidate.getValue(); if (value != null && value.isFunction()) { Preconditions.checkState( !nodePriorities.containsKey(candidate) || candidate == entry); prioritizeFromEntryNode(candidate); } } } // At this point, all reachable nodes have been given a priority, but // unreachable nodes have not been given a priority. Put them last. // Presumably, it doesn't really matter what priority they get, since // this shouldn't happen in real code. for (DiGraphNode candidate : cfg.getDirectedGraphNodes()) { if (!nodePriorities.containsKey(candidate)) { nodePriorities.put(candidate, ++priorityCounter); } } // Again, the implicit return node is always last. nodePriorities.put(cfg.getImplicitReturn(), ++priorityCounter); } /** * Given an entry node, find all the nodes reachable from that node * and prioritize them. */ private void prioritizeFromEntryNode(DiGraphNode entry) { PriorityQueue> worklist = new PriorityQueue>(10, priorityComparator); worklist.add(entry); while (!worklist.isEmpty()) { DiGraphNode current = worklist.remove(); if (nodePriorities.containsKey(current)) { continue; } nodePriorities.put(current, ++priorityCounter); List> successors = cfg.getDirectedSuccNodes(current); for (DiGraphNode candidate : successors) { worklist.add(candidate); } } } @Override public boolean shouldTraverse( NodeTraversal nodeTraversal, Node n, Node parent) { astPosition.put(n, astPositionCounter++); switch (n.getType()) { case Token.FUNCTION: if (shouldTraverseFunctions || n == cfg.getEntry().getValue()) { exceptionHandler.push(n); return true; } return false; case Token.TRY: exceptionHandler.push(n); return true; } /* * We are going to stop the traversal depending on what the node's parent * is. * * We are only interested in adding edges between nodes that change control * flow. The most obvious ones are loops and IF-ELSE's. A statement * transfers control to its next sibling. * * In case of an expression tree, there is no control flow within the tree * even when there are short circuited operators and conditionals. When we * are doing data flow analysis, we will simply synthesize lattices up the * expression tree by finding the meet at each expression node. * * For example: within a Token.SWITCH, the expression in question does not * change the control flow and need not to be considered. */ if (parent != null) { switch (parent.getType()) { case Token.FOR: // Only traverse the body of the for loop. return n == parent.getLastChild(); // Skip the conditions. case Token.IF: case Token.WHILE: case Token.WITH: return n != parent.getFirstChild(); case Token.DO: return n != parent.getFirstChild().getNext(); // Only traverse the body of the cases case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.LABEL: return n != parent.getFirstChild(); case Token.FUNCTION: return n == parent.getFirstChild().getNext().getNext(); case Token.CONTINUE: case Token.BREAK: case Token.EXPR_RESULT: case Token.VAR: case Token.RETURN: case Token.THROW: return false; case Token.TRY: /* Just before we are about to visit the second child of the TRY node, * we know that we will be visiting either the CATCH or the FINALLY. * In other words, we know that the post order traversal of the TRY * block has been finished, no more exceptions can be caught by the * handler at this TRY block and should be taken out of the stack. */ if (n == parent.getFirstChild().getNext()) { Preconditions.checkState(exceptionHandler.peek() == parent); exceptionHandler.pop(); } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.IF: handleIf(n); return; case Token.WHILE: handleWhile(n); return; case Token.DO: handleDo(n); return; case Token.FOR: handleFor(n); return; case Token.SWITCH: handleSwitch(n); return; case Token.CASE: handleCase(n); return; case Token.DEFAULT_CASE: handleDefault(n); return; case Token.BLOCK: case Token.SCRIPT: handleStmtList(n); return; case Token.FUNCTION: handleFunction(n); return; case Token.EXPR_RESULT: handleExpr(n); return; case Token.THROW: handleThrow(n); return; case Token.TRY: handleTry(n); return; case Token.CATCH: handleCatch(n); return; case Token.BREAK: handleBreak(n); return; case Token.CONTINUE: handleContinue(n); return; case Token.RETURN: handleReturn(n); return; case Token.WITH: handleWith(n); return; case Token.LABEL: return; default: handleStmt(n); return; } } private void handleIf(Node node) { Node thenBlock = node.getFirstChild().getNext(); Node elseBlock = thenBlock.getNext(); createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock)); if (elseBlock == null) { createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); // not taken branch } else { createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock)); } connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleWhile(Node node) { // Control goes to the first statement if the condition evaluates to true. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild().getNext())); // Control goes to the follow() if the condition evaluates to false. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleDo(Node node) { // The first edge can be the initial iteration as well as the iterations // after. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild())); // The edge that leaves the do loop if the condition fails. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleFor(Node forNode) { if (forNode.getChildCount() == 4) { // We have for (init; cond; iter) { body } Node init = forNode.getFirstChild(); Node cond = init.getNext(); Node iter = cond.getNext(); Node body = iter.getNext(); // After initialization, we transfer to the FOR which is in charge of // checking the condition (for the first time). createEdge(init, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode, this)); // The end of the body will have a unconditional branch to our iter // (handled by calling computeFollowNode of the last instruction of the // body. Our iter will jump to the forNode again to another condition // check. createEdge(iter, Branch.UNCOND, forNode); connectToPossibleExceptionHandler(init, init); connectToPossibleExceptionHandler(forNode, cond); connectToPossibleExceptionHandler(iter, iter); } else { // We have for (item in collection) { body } Node item = forNode.getFirstChild(); Node collection = item.getNext(); Node body = collection.getNext(); // The collection behaves like init. createEdge(collection, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode, this)); connectToPossibleExceptionHandler(forNode, collection); } } private void handleSwitch(Node node) { // Transfer to the first non-DEFAULT CASE. if there are none, transfer // to the DEFAULT or the EMPTY node. Node next = getNextSiblingOfType( node.getFirstChild().getNext(), Token.CASE, Token.EMPTY); if (next != null) { // Has at least one CASE or EMPTY createEdge(node, Branch.UNCOND, next); } else { // Has no CASE but possibly a DEFAULT if (node.getFirstChild().getNext() != null) { createEdge(node, Branch.UNCOND, node.getFirstChild().getNext()); } else { // No CASE, no DEFAULT createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleCase(Node node) { // Case is a bit tricky....First it goes into the body if condition is true. createEdge(node, Branch.ON_TRUE, node.getFirstChild().getNext()); // Look for the next CASE, skipping over DEFAULT. Node next = getNextSiblingOfType(node.getNext(), Token.CASE); if (next != null) { // Found a CASE Preconditions.checkState(next.isCase()); createEdge(node, Branch.ON_FALSE, next); } else { // No more CASE found, go back and search for a DEFAULT. Node parent = node.getParent(); Node deflt = getNextSiblingOfType( parent.getFirstChild().getNext(), Token.DEFAULT_CASE); if (deflt != null) { // Has a DEFAULT createEdge(node, Branch.ON_FALSE, deflt); } else { // No DEFAULT found, go to the follow of the SWITCH. createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleDefault(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleWith(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getLastChild()); connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleStmtList(Node node) { Node parent = node.getParent(); // Special case, don't add a block of empty CATCH block to the graph. if (node.isBlock() && parent != null && parent.isTry() && NodeUtil.getCatchBlock(parent) == node && !NodeUtil.hasCatchHandler(node)) { return; } // A block transfer control to its first child if it is not empty. Node child = node.getFirstChild(); // Function declarations are skipped since control doesn't go into that // function (unless it is called) while (child != null && child.isFunction()) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT_CASE: case Token.CASE: case Token.TRY: break; default: if (node.isBlock() && node.isSyntheticBlock()) { createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this)); } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node previous = null; Node lastJump; Node parent = node.getParent(); /* * Continuously look up the ancestor tree for the BREAK target or the target * with the corresponding label and connect to it. If along the path we * discover a FINALLY, we will connect the BREAK to that FINALLY. From then * on, we will just record the control flow changes in the finallyMap. This * is due to the fact that we need to connect any node that leaves its own * FINALLY block to the outer FINALLY or the BREAK's target but those nodes * are not known yet due to the way we traverse the nodes. */ for (cur = node, lastJump = node; !isBreakTarget(cur, label); cur = parent, parent = parent.getParent()) { if (cur.isTry() && NodeUtil.hasFinally(cur) && cur.getLastChild() != previous) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFallThrough( cur.getLastChild())); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } if (parent == null) { if (compiler.isIdeMode()) { // In IDE mode, we expect that the data flow graph may // not be well-formed. return; } else { throw new IllegalStateException("Cannot find break target."); } } previous = cur; } if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur, this)); } else { finallyMap.put(lastJump, computeFollowNode(cur, this)); } } private void handleContinue(Node node) { String label = null; if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node previous = null; Node lastJump; // Similar to handBreak's logic with a few minor variation. Node parent = node.getParent(); for (cur = node, lastJump = node; !isContinueTarget(cur, parent, label); cur = parent, parent = parent.getParent()) { if (cur.isTry() && NodeUtil.hasFinally(cur) && cur.getLastChild() != previous) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, cur.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find continue target."); previous = cur; } Node iter = cur; if (cur.getChildCount() == 4) { iter = cur.getFirstChild().getNext().getNext(); } if (lastJump == node) { createEdge(node, Branch.UNCOND, iter); } else { finallyMap.put(lastJump, iter); } } private void handleReturn(Node node) { Node lastJump = null; for (Iterator iter = exceptionHandler.iterator(); iter.hasNext();) { Node curHandler = iter.next(); if (curHandler.isFunction()) { break; } if (NodeUtil.hasFinally(curHandler)) { if (lastJump == null) { createEdge(node, Branch.UNCOND, curHandler.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild())); } lastJump = curHandler; } } if (node.hasChildren()) { connectToPossibleExceptionHandler(node, node.getFirstChild()); } if (lastJump == null) { createEdge(node, Branch.UNCOND, null); } else { finallyMap.put(lastJump, null); } } private void handleStmt(Node node) { // Simply transfer to the next line. createEdge(node, Branch.UNCOND, computeFollowNode(node, this)); connectToPossibleExceptionHandler(node, node); } static Node computeFollowNode(Node node, ControlFlowAnalysis cfa) { return computeFollowNode(node, node, cfa); } static Node computeFollowNode(Node node) { return computeFollowNode(node, node, null); } /** * Computes the follow() node of a given node and its parent. There is a side * effect when calling this function. If this function computed an edge that * exists a FINALLY, it'll attempt to connect the fromNode to the outer * FINALLY according to the finallyMap. * * @param fromNode The original source node since {@code node} is changed * during recursion. * @param node The node that follow() should compute. */ private static Node computeFollowNode( Node fromNode, Node node, ControlFlowAnalysis cfa) { /* * This is the case where: * * 1. Parent is null implies that we are transferring control to the end of * the script. * * 2. Parent is a function implies that we are transferring control back to * the caller of the function. * * 3. If the node is a return statement, we should also transfer control * back to the caller of the function. * * 4. If the node is root then we have reached the end of what we have been * asked to traverse. * * In all cases we should transfer control to a "symbolic return" node. * This will make life easier for DFAs. */ Node parent = node.getParent(); if (parent == null || parent.isFunction() || (cfa != null && node == cfa.root)) { return null; } // If we are just before a IF/WHILE/DO/FOR: switch (parent.getType()) { // The follow() of any of the path from IF would be what follows IF. case Token.IF: return computeFollowNode(fromNode, parent, cfa); case Token.CASE: case Token.DEFAULT_CASE: // After the body of a CASE, the control goes to the body of the next // case, without having to go to the case condition. if (parent.getNext() != null) { if (parent.getNext().isCase()) { return parent.getNext().getFirstChild().getNext(); } else if (parent.getNext().isDefaultCase()) { return parent.getNext().getFirstChild(); } else { Preconditions.checkState(false, "Not reachable"); } } else { return computeFollowNode(fromNode, parent, cfa); } break; case Token.FOR: if (NodeUtil.isForIn(parent)) { return parent; } else { return parent.getFirstChild().getNext().getNext(); } case Token.WHILE: case Token.DO: return parent; case Token.TRY: // If we are coming out of the TRY block... if (parent.getFirstChild() == node) { if (NodeUtil.hasFinally(parent)) { // and have FINALLY block. return computeFallThrough(parent.getLastChild()); } else { // and have no FINALLY. return computeFollowNode(fromNode, parent, cfa); } // CATCH block. } else if (NodeUtil.getCatchBlock(parent) == node){ if (NodeUtil.hasFinally(parent)) { // and have FINALLY block. return computeFallThrough(node.getNext()); } else { return computeFollowNode(fromNode, parent, cfa); } // If we are coming out of the FINALLY block... } else if (parent.getLastChild() == node){ if (cfa != null) { for (Node finallyNode : cfa.finallyMap.get(parent)) { cfa.createEdge(fromNode, Branch.ON_EX, finallyNode); } } return computeFollowNode(fromNode, parent, cfa); } } // Now that we are done with the special cases follow should be its // immediate sibling, unless its sibling is a function Node nextSibling = node.getNext(); // Skip function declarations because control doesn't get pass into it. while (nextSibling != null && nextSibling.isFunction()) { nextSibling = nextSibling.getNext(); } if (nextSibling != null) { return computeFallThrough(nextSibling); } else { // If there are no more siblings, control is transferred up the AST. return computeFollowNode(fromNode, parent, cfa); } } /** * Computes the destination node of n when we want to fallthrough into the * subtree of n. We don't always create a CFG edge into n itself because of * DOs and FORs. */ static Node computeFallThrough(Node n) { switch (n.getType()) { case Token.DO: return computeFallThrough(n.getFirstChild()); case Token.FOR: if (NodeUtil.isForIn(n)) { return n.getFirstChild().getNext(); } return computeFallThrough(n.getFirstChild()); case Token.LABEL: return computeFallThrough(n.getLastChild()); default: return n; } } /** * Connects the two nodes in the control flow graph. * * @param fromNode Source. * @param toNode Destination. */ private void createEdge(Node fromNode, ControlFlowGraph.Branch branch, Node toNode) { cfg.createNode(fromNode); cfg.createNode(toNode); cfg.connectIfNotFound(fromNode, branch, toNode); } /** * Connects cfgNode to the proper CATCH block if target subtree might throw * an exception. If there are FINALLY blocks reached before a CATCH, it will * make the corresponding entry in finallyMap. */ private void connectToPossibleExceptionHandler(Node cfgNode, Node target) { if (mayThrowException(target) && !exceptionHandler.isEmpty()) { Node lastJump = cfgNode; for (Node handler : exceptionHandler) { if (handler.isFunction()) { return; } Preconditions.checkState(handler.isTry()); Node catchBlock = NodeUtil.getCatchBlock(handler); if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, handler.getLastChild()); } else { finallyMap.put(lastJump, handler.getLastChild()); } } else { // Has a catch. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, catchBlock); return; } else { finallyMap.put(lastJump, catchBlock); } } lastJump = handler; } } } /** * Get the next sibling (including itself) of one of the given types. */ private static Node getNextSiblingOfType(Node first, int ... types) { for (Node c = first; c != null; c = c.getNext()) { for (int type : types) { if (c.getType() == type) { return c; } } } return null; } /** * Checks if target is actually the break target of labeled continue. The * label can be null if it is an unlabeled break. */ public static boolean isBreakTarget(Node target, String label) { return isBreakStructure(target, label != null) && matchLabel(target.getParent(), label); } /** * Checks if target is actually the continue target of labeled continue. The * label can be null if it is an unlabeled continue. */ private static boolean isContinueTarget( Node target, Node parent, String label) { return isContinueStructure(target) && matchLabel(parent, label); } /** * Check if label is actually referencing the target control structure. If * label is null, it always returns true. */ private static boolean matchLabel(Node target, String label) { if (label == null) { return true; } while (target.isLabel()) { if (target.getFirstChild().getString().equals(label)) { return true; } target = target.getParent(); } return false; } /** * Determines if the subtree might throw an exception. */ public static boolean mayThrowException(Node n) { switch (n.getType()) { case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.THROW: case Token.NEW: case Token.ASSIGN: case Token.INC: case Token.DEC: case Token.INSTANCEOF: return true; case Token.FUNCTION: return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { return true; } } return false; } /** * Determines whether the given node can be terminated with a BREAK node. */ static boolean isBreakStructure(Node n, boolean labeled) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.SWITCH: return true; case Token.BLOCK: case Token.IF: case Token.TRY: return labeled; default: return false; } } /** * Determines whether the given node can be advanced with a CONTINUE node. */ static boolean isContinueStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * Get the TRY block with a CATCH that would be run if n throws an exception. * @return The CATCH node or null if it there isn't a CATCH before the * the function terminates. */ static Node getExceptionHandler(Node n) { for (Node cur = n; !cur.isScript() && !cur.isFunction(); cur = cur.getParent()) { Node catchNode = getCatchHandlerForBlock(cur); if (catchNode != null) { return catchNode; } } return null; } /** * Locate the catch BLOCK given the first block in a TRY. * @return The CATCH node or null there is no catch handler. */ static Node getCatchHandlerForBlock(Node block) { if (block.isBlock() && block.getParent().isTry() && block.getParent().getFirstChild() == block) { for (Node s = block.getNext(); s != null; s = s.getNext()) { if (NodeUtil.hasCatchHandler(s)) { return s.getFirstChild(); } } } return null; } /** * A {@link ControlFlowGraph} which provides a node comparator based on the * pre-order traversal of the AST. */ private static class AstControlFlowGraph extends ControlFlowGraph { private final Map, Integer> priorities; /** * Constructor. * @param entry The entry node. * @param priorities The map from nodes to position in the AST (to be * filled by the {@link ControlFlowAnalysis#shouldTraverse}). */ private AstControlFlowGraph(Node entry, Map, Integer> priorities, boolean edgeAnnotations) { super(entry, true /* node annotations */, edgeAnnotations); this.priorities = priorities; } @Override /** * Returns a node comparator based on the pre-order traversal of the AST. * @param isForward x 'before' y in the pre-order traversal implies * x 'less than' y (if true) and x 'greater than' y (if false). */ public Comparator> getOptionalNodeComparator( boolean isForward) { if (isForward) { return new Comparator>() { @Override public int compare( DiGraphNode n1, DiGraphNode n2) { return getPosition(n1) - getPosition(n2); } }; } else { return new Comparator>() { @Override public int compare( DiGraphNode n1, DiGraphNode n2) { return getPosition(n2) - getPosition(n1); } }; } } /** * Gets the pre-order traversal position of the given node. * @return An arbitrary counter used for comparing positions. */ private int getPosition(DiGraphNode n) { Integer priority = priorities.get(n); Preconditions.checkNotNull(priority); return priority; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SanityCheck.java0000644000175000017500000001073612115204405025713 0ustar apoapo/* * 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.javascript.rhino.Node; /** * A compiler pass that verifies the structure of the AST conforms * to a number of invariants. Because this can add a lot of overhead, * we only run this in development mode. * */ class SanityCheck implements CompilerPass { static final DiagnosticType CANNOT_PARSE_GENERATED_CODE = DiagnosticType.error("JSC_CANNOT_PARSE_GENERATED_CODE", "Internal compiler error. Cannot parse generated code: {0}"); static final DiagnosticType GENERATED_BAD_CODE = DiagnosticType.error( "JSC_GENERATED_BAD_CODE", "Internal compiler error. Generated bad code." + "----------------------------------------\n" + "Expected:\n{0}\n" + "----------------------------------------\n" + "Actual:\n{1}"); private final AbstractCompiler compiler; private final AstValidator astValidator = new AstValidator(); SanityCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { sanityCheckAst(externs, root); sanityCheckNormalization(externs, root); sanityCheckCodeGeneration(root); sanityCheckVars(externs, root); } /** * Sanity check the AST is structurally accurate. */ private void sanityCheckAst(Node externs, Node root) { astValidator.validateCodeRoot(externs); astValidator.validateCodeRoot(root); } private void sanityCheckVars(Node externs, Node root) { if (compiler.getLifeCycleStage().isNormalized()) { (new VarCheck(compiler, true)).process(externs, root); } } /** * Sanity checks code generation by performing it once, parsing the result, * then generating code from the second parse tree to verify that it matches * the code generated from the first parse tree. * * @return The regenerated parse tree. Null on error. */ private Node sanityCheckCodeGeneration(Node root) { if (compiler.hasHaltingErrors()) { // Don't even bother checking code generation if we already know the // the code is bad. return null; } String source = compiler.toSource(root); Node root2 = compiler.parseSyntheticCode(source); if (compiler.hasHaltingErrors()) { compiler.report(JSError.make(CANNOT_PARSE_GENERATED_CODE, Strings.truncateAtMaxLength(source, 100, true))); // Throw an exception, so that the infrastructure will tell us // which pass violated the sanity check. throw new IllegalStateException("Sanity Check failed"); } String source2 = compiler.toSource(root2); if (!source.equals(source2)) { compiler.report(JSError.make(GENERATED_BAD_CODE, source, source2)); // Throw an exception, so that the infrastructure will tell us // which pass violated the sanity check. throw new IllegalStateException("Sanity Check failed"); } return root2; } /** * Sanity checks the AST. This is by verifying the normalization passes do * nothing. */ private void sanityCheckNormalization(Node externs, Node root) { // Verify nothing has inappropriately denormalize the AST. CodeChangeHandler handler = new ForbiddenChange(); compiler.addChangeHandler(handler); // TODO(johnlenz): Change these normalization checks Preconditions and // Exceptions into Errors so that it is easier to find the root cause // when there are cascading issues. new PrepareAst(compiler, true).process(null, root); if (compiler.getLifeCycleStage().isNormalized()) { (new Normalize(compiler, true)).process(externs, root); if (compiler.getLifeCycleStage().isNormalizedUnobfuscated()) { boolean checkUserDeclarations = true; CompilerPass pass = new Normalize.VerifyConstants( compiler, checkUserDeclarations); pass.process(externs, root); } } compiler.removeChangeHandler(handler); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InstrumentFunctions.java0000644000175000017500000002643212115204405027547 0ustar apoapo/* * 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.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.protobuf.TextFormat; import java.io.IOException; import java.util.List; /** * Instruments functions for when functions first get called and defined. * * This pass be used to instrument code to: * 1. Gather statistics from real users in the wild. * 2. Incorporate utilization statistics into Selenium tests * 3. Access utilization statistics from an app's debug UI * * By parametrizing the whole instrumentation process we expect to be * able to support a wide variety of use cases and minimize the cost * of developing new instrumentation schemes. * * TODO(user): This pass currently runs just before the variable and * property renaming near the end of the optimization pass. I think * Mark put it there to minimize the difference between the code * generated with/without instrumentation; instrumentation makes * several optimization passes do less, for example inline functions. * * My opinion is that we want utilization/profiling information for * all function. This pass should run before most passes that modify * the AST (exception being the localization pass, which makes * assumptions about the structure of the AST). We should move the * pass up, list inlined functions or give clients the option to * instrument before or after optimization. * */ class InstrumentFunctions implements CompilerPass { private final AbstractCompiler compiler; private final FunctionNames functionNames; private final String templateFilename; private final String appNameStr; private final String initCodeSource; private final String definedFunctionName; private final String reportFunctionName; private final String reportFunctionExitName; private final String appNameSetter; private final List declarationsToRemove; /** * Creates an instrument functions compiler pass. * * @param compiler The JSCompiler * @param functionNames Assigned function identifiers. * @param templateFilename Template filename; for use during error * reporting only. * @param appNameStr String to pass to appNameSetter. * @param readable Instrumentation template protobuf text. */ InstrumentFunctions(AbstractCompiler compiler, FunctionNames functionNames, String templateFilename, String appNameStr, Readable readable) { this.compiler = compiler; this.functionNames = functionNames; this.templateFilename = templateFilename; this.appNameStr = appNameStr; Instrumentation.Builder builder = Instrumentation.newBuilder(); try { TextFormat.merge(readable, builder); } catch (IOException e) { compiler.report(JSError.make(RhinoErrorReporter.PARSE_ERROR, "Error reading instrumentation template protobuf at " + templateFilename)); this.initCodeSource = ""; this.definedFunctionName = ""; this.reportFunctionName = ""; this.reportFunctionExitName = ""; this.appNameSetter = ""; this.declarationsToRemove = Lists.newArrayList(); return; } Instrumentation template = builder.build(); StringBuilder initCodeSourceBuilder = new StringBuilder(); for (String line : template.getInitList()) { initCodeSourceBuilder.append(line).append("\n"); } this.initCodeSource = initCodeSourceBuilder.toString(); this.definedFunctionName = template.getReportDefined(); this.reportFunctionName = template.getReportCall(); this.reportFunctionExitName = template.getReportExit(); this.appNameSetter = template.getAppNameSetter(); this.declarationsToRemove = ImmutableList.copyOf( template.getDeclarationToRemoveList()); } @Override public void process(Node externs, Node root) { Node initCode = null; if (!initCodeSource.isEmpty()) { Node initCodeRoot = compiler.parseSyntheticCode( templateFilename + ":init", initCodeSource); if (initCodeRoot != null && initCodeRoot.getFirstChild() != null) { initCode = initCodeRoot.removeChildren(); } else { return; // parse failure } } NodeTraversal.traverse(compiler, root, new RemoveCallback(declarationsToRemove)); NodeTraversal.traverse(compiler, root, new InstrumentCallback()); if (!appNameSetter.isEmpty()) { Node call = IR.call( IR.name(appNameSetter), IR.string(appNameStr)); call.putBooleanProp(Node.FREE_CALL, true); Node expr = IR.exprResult(call); Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(expr); compiler.reportCodeChange(); } if (initCode != null) { Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(initCode); compiler.reportCodeChange(); } } /** * The application must refer to these variables to output them so the * application must also declare these variables for the first * {@link VarCheck} pass. These declarations must be removed before the * second {@link VarCheck} pass. Otherwise, the second pass would warn about * duplicate declarations. */ private static class RemoveCallback extends AbstractPostOrderCallback { private final List removable; RemoveCallback(List removable) { this.removable = removable; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isVarDeclaration(n)) { if (removable.contains(n.getString())) { parent.removeChild(n); if (!parent.hasChildren()) { parent.getParent().removeChild(parent); } } } } } /** * Traverse a function's body by instrument return sites by * inserting calls to {@code reportFunctionExitName}. If the * function is missing an explicit return statement in some control * path, this pass inserts a call to {@code reportFunctionExitName} * as the last statement in the function's body. * * Example: * Input: * function f() { * if (pred) { * return a; * } * } * * Template: * reportFunctionExitName: "onExitFn" * * Output: * function f() { * if (pred) { * return onExitFn(0, a); * } * onExitFn(0); * } * **/ private class InstrumentReturns implements NodeTraversal.Callback { private final int functionId; /** * @param functionId Function identifier computed by FunctionNames; * used as first argument to {@code reportFunctionExitName} * {@code reportFunctionExitName} must be a 2 argument function that * returns it's second argument. */ InstrumentReturns(int functionId) { this.functionId = functionId; } /** * @param body body of function with id == this.functionId */ void process(Node body) { NodeTraversal.traverse(compiler, body, this); if (!allPathsReturn(body)) { Node call = newReportFunctionExitNode(); Node expr = IR.exprResult(call); body.addChildToBack(expr); compiler.reportCodeChange(); } } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return !n.isFunction(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isReturn()) { return; } Node call = newReportFunctionExitNode(); Node returnRhs = n.removeFirstChild(); if (returnRhs != null) { call.addChildToBack(returnRhs); } n.addChildToFront(call); compiler.reportCodeChange(); } private Node newReportFunctionExitNode() { Node call = IR.call( IR.name(reportFunctionExitName), IR.number(functionId)); call.putBooleanProp(Node.FREE_CALL, true); return call; } /** * @returns true if all paths from block must exit with an explicit return. */ private boolean allPathsReturn(Node block) { // Computes the control flow graph. ControlFlowAnalysis cfa = new ControlFlowAnalysis( compiler, false, false); cfa.process(null, block); ControlFlowGraph cfg = cfa.getCfg(); Node returnPathsParent = cfg.getImplicitReturn().getValue(); for (DiGraphNode pred : cfg.getDirectedPredNodes(returnPathsParent)) { Node n = pred.getValue(); if (!n.isReturn()) { return false; } } return true; } } private class InstrumentCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isFunction()) { return; } int id = functionNames.getFunctionId(n); if (id < 0) { // Function node was added during compilation; don't instrument. return; } if (!reportFunctionName.isEmpty()) { Node body = n.getFirstChild().getNext().getNext(); Node call = IR.call( IR.name(reportFunctionName), IR.number(id)); call.putBooleanProp(Node.FREE_CALL, true); Node expr = IR.exprResult(call); body.addChildToFront(expr); compiler.reportCodeChange(); } if (!reportFunctionExitName.isEmpty()) { Node body = n.getFirstChild().getNext().getNext(); (new InstrumentReturns(id)).process(body); } if (!definedFunctionName.isEmpty()) { Node call = IR.call( IR.name(definedFunctionName), IR.number(id)); call.putBooleanProp(Node.FREE_CALL, true); Node expr = NodeUtil.newExpr(call); Node addingRoot = null; if (NodeUtil.isFunctionDeclaration(n)) { JSModule module = t.getModule(); addingRoot = compiler.getNodeForCodeInsertion(module); addingRoot.addChildToFront(expr); } else { Node beforeChild = n; for (Node ancestor : n.getAncestors()) { int type = ancestor.getType(); if (type == Token.BLOCK || type == Token.SCRIPT) { addingRoot = ancestor; break; } beforeChild = ancestor; } addingRoot.addChildBefore(expr, beforeChild); } compiler.reportCodeChange(); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CompilationLevel.java0000644000175000017500000001661312115204405026754 0ustar apoapo/* * 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.javascript.jscomp.CompilerOptions.Reach; /** * A CompilationLevel represents the level of optimization that should be * applied when compiling JavaScript code. * * @author bolinfest@google.com (Michael Bolin) */ public enum CompilationLevel { /** * WHITESPACE_ONLY removes comments and extra whitespace in the input JS. */ WHITESPACE_ONLY, /** * SIMPLE_OPTIMIZATIONS performs transformations to the input JS that do not * require any changes to JS that depend on the input JS. For example, * function arguments are renamed (which should not matter to code that * depends on the input JS), but functions themselves are not renamed (which * would otherwise require external code to change to use the renamed function * names). */ SIMPLE_OPTIMIZATIONS, /** * ADVANCED_OPTIMIZATIONS aggressively reduces code size by renaming function * names and variables, removing code which is never called, etc. */ ADVANCED_OPTIMIZATIONS, ; private CompilationLevel() {} public void setOptionsForCompilationLevel(CompilerOptions options) { switch (this) { case WHITESPACE_ONLY: applyBasicCompilationOptions(options); break; case SIMPLE_OPTIMIZATIONS: applySafeCompilationOptions(options); break; case ADVANCED_OPTIMIZATIONS: applyFullCompilationOptions(options); break; default: throw new RuntimeException("Unknown compilation level."); } } public void setDebugOptionsForCompilationLevel(CompilerOptions options) { options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED; options.generatePseudoNames = true; options.removeClosureAsserts = false; // Don't shadow variables as it is too confusing. options.shadowVariables = false; } /** * Gets options that only strip whitespace and comments. * @param options The CompilerOptions object to set the options on. */ private static void applyBasicCompilationOptions(CompilerOptions options) { options.skipAllCompilerPasses(); } /** * Add options that are safe. Safe means options that won't break the * JavaScript code even if no symbols are exported and no coding convention * is used. * @param options The CompilerOptions object to set the options on. */ private static void applySafeCompilationOptions(CompilerOptions options) { // ReplaceIdGenerators is on by default, but should run in simple mode. options.replaceIdGenerators = false; // Does not call applyBasicCompilationOptions(options) because the call to // skipAllCompilerPasses() cannot be easily undone. options.dependencyOptions.setDependencySorting(true); options.closurePass = true; options.setRenamingPolicy( VariableRenamingPolicy.LOCAL, PropertyRenamingPolicy.OFF); options.shadowVariables = true; options.setInlineVariables(Reach.LOCAL_ONLY); options.flowSensitiveInlineVariables = true; options.setInlineFunctions(Reach.LOCAL_ONLY); options.setAssumeClosuresOnlyCaptureReferences(false); options.checkGlobalThisLevel = CheckLevel.OFF; options.foldConstants = true; options.coalesceVariableNames = true; options.deadAssignmentElimination = true; options.collapseVariableDeclarations = true; options.convertToDottedProperties = true; options.labelRenaming = true; options.removeDeadCode = true; options.optimizeArgumentsArray = true; options.setRemoveUnusedVariables(Reach.LOCAL_ONLY); options.collapseObjectLiterals = true; options.protectHiddenSideEffects = true; } /** * Add the options that will work only if the user exported all the symbols * correctly. * @param options The CompilerOptions object to set the options on. */ private static void applyFullCompilationOptions(CompilerOptions options) { // Do not call applySafeCompilationOptions(options) because the call can // create possible conflicts between multiple diagnostic groups. // All the safe optimizations. options.dependencyOptions.setDependencySorting(true); options.closurePass = true; options.foldConstants = true; options.coalesceVariableNames = true; options.deadAssignmentElimination = true; options.extractPrototypeMemberDeclarations = true; options.collapseVariableDeclarations = true; options.convertToDottedProperties = true; options.rewriteFunctionExpressions = true; options.labelRenaming = true; options.removeDeadCode = true; options.optimizeArgumentsArray = true; options.collapseObjectLiterals = true; options.protectHiddenSideEffects = true; // All the advance optimizations. options.removeClosureAsserts = true; options.aliasKeywords = true; options.reserveRawExports = true; options.setRenamingPolicy( VariableRenamingPolicy.ALL, PropertyRenamingPolicy.ALL_UNQUOTED); options.shadowVariables = true; options.removeUnusedPrototypeProperties = true; options.removeUnusedPrototypePropertiesInExterns = true; options.collapseAnonymousFunctions = true; options.collapseProperties = true; options.checkGlobalThisLevel = CheckLevel.WARNING; options.rewriteFunctionExpressions = true; options.smartNameRemoval = true; options.inlineConstantVars = true; options.setInlineFunctions(Reach.ALL); options.setAssumeClosuresOnlyCaptureReferences(false); options.inlineGetters = true; options.setInlineVariables(Reach.ALL); options.flowSensitiveInlineVariables = true; options.computeFunctionSideEffects = true; // Remove unused vars also removes unused functions. options.setRemoveUnusedVariables(Reach.ALL); // Move code around based on the defined modules. options.crossModuleCodeMotion = true; options.crossModuleMethodMotion = true; // Call optimizations options.devirtualizePrototypeMethods = true; options.optimizeParameters = true; options.optimizeReturns = true; options.optimizeCalls = true; } /** * Enable additional optimizations that use type information. * @param options The CompilerOptions object to set the options on. */ public void setTypeBasedOptimizationOptions(CompilerOptions options) { switch (this) { case ADVANCED_OPTIMIZATIONS: options.inferTypes = true; options.disambiguateProperties = true; options.ambiguateProperties = true; options.inlineProperties = true; // TODO(johnlenz) :removeUnusedClassProperties isn't strictly a // type based pass, but add it here for now because I may have to // make it into one. options.removeUnusedClassProperties = true; break; case SIMPLE_OPTIMIZATIONS: // TODO(johnlenz): enable peephole type based optimization. break; case WHITESPACE_ONLY: break; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SourceMap.java0000644000175000017500000001411312115204405025375 0ustar apoapo/* * 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.base.Predicate; import com.google.common.collect.Maps; import com.google.debugging.sourcemap.FilePosition; import com.google.debugging.sourcemap.SourceMapFormat; import com.google.debugging.sourcemap.SourceMapGenerator; import com.google.debugging.sourcemap.SourceMapGeneratorFactory; import com.google.debugging.sourcemap.SourceMapGeneratorV1; import com.google.debugging.sourcemap.SourceMapGeneratorV2; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; /** * Collects information mapping the generated (compiled) source back to * its original source for debugging purposes. * * @see CodeConsumer * @see CodeGenerator * @see CodePrinter * */ public class SourceMap { public static enum Format { V1 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V1)); } }, DEFAULT { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.DEFAULT)); } }, V2 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V2)); } }, V3 { @Override SourceMap getInstance() { return new SourceMap( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3)); } }; abstract SourceMap getInstance(); } /** * Source maps can be very large different levels of detail can be specified. */ public static enum DetailLevel implements Predicate { // ALL is best when the fullest details are needed for debugging or for // code-origin analysis. ALL { @Override public boolean apply(Node node) { return true; } }, // SYMBOLS is intended to be used for stack trace deobfuscation when full // detail is not needed. SYMBOLS { @Override public boolean apply(Node node) { return node.isCall() || node.isNew() || node.isFunction() || node.isName() || NodeUtil.isGet(node) || NodeUtil.isObjectLitKey(node) || (node.isString() && NodeUtil.isGet(node.getParent())); } }; } public static class LocationMapping { final String prefix; final String replacement; public LocationMapping(String prefix, String replacement) { this.prefix = prefix; this.replacement = replacement; } } private final SourceMapGenerator generator; private List prefixMappings = Collections.emptyList(); private final Map sourceLocationFixupCache = Maps.newHashMap(); private SourceMap(SourceMapGenerator generator) { this.generator = generator; } public void addMapping( Node node, FilePosition outputStartPosition, FilePosition outputEndPosition) { String sourceFile = node.getSourceFileName(); // If the node does not have an associated source file or // its line number is -1, then the node does not have sufficient // information for a mapping to be useful. if (sourceFile == null || node.getLineno() < 0) { return; } sourceFile = fixupSourceLocation(sourceFile); String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP); // Strangely, Rhino source lines are one based but columns are // zero based. // We don't change this for the v1 or v2 source maps but for // v3 we make them both 0 based. int lineBaseOffset = 1; if (generator instanceof SourceMapGeneratorV1 || generator instanceof SourceMapGeneratorV2) { lineBaseOffset = 0; } generator.addMapping( sourceFile, originalName, new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()), outputStartPosition, outputEndPosition); } /** * @param sourceFile The source file location to fixup. * @return a remapped source file. */ private String fixupSourceLocation(String sourceFile) { if (prefixMappings.isEmpty()) { return sourceFile; } String fixed = sourceLocationFixupCache.get(sourceFile); if (fixed != null) { return fixed; } // Replace the first prefix found with its replacement for (LocationMapping mapping : prefixMappings) { if (sourceFile.startsWith(mapping.prefix)) { fixed = mapping.replacement + sourceFile.substring( mapping.prefix.length()); break; } } // If none of the mappings match then use the original file path. if (fixed == null) { fixed = sourceFile; } sourceLocationFixupCache.put(sourceFile, fixed); return fixed; } public void appendTo(Appendable out, String name) throws IOException { generator.appendTo(out, name); } public void reset() { generator.reset(); sourceLocationFixupCache.clear(); } public void setStartingPosition(int offsetLine, int offsetIndex) { generator.setStartingPosition(offsetLine, offsetIndex); } public void setWrapperPrefix(String prefix) { generator.setWrapperPrefix(prefix); } public void validate(boolean validate) { generator.validate(validate); } /** * @param sourceMapLocationMappings */ public void setPrefixMappings(List sourceMapLocationMappings) { this.prefixMappings = sourceMapLocationMappings; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FlowSensitiveInlineVariables.java0000644000175000017500000004350612115204405031300 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.MustBeReachingVariableDef.Definition; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Set; /** * Inline variables when possible. Using the information from * {@link MaybeReachingVariableUse} and {@link MustBeReachingVariableDef}, * this pass attempts to inline a variable by placing the value at the * definition where the variable is used. The basic requirements for inlining * are the following: * *
        *
      • There is exactly one reaching definition at the use of that variable *
      • *
      • There is exactly one use for that definition of the variable *
      • *
      * *

      Other requirements can be found in {@link Candidate#canInline}. Currently * this pass does not operate on the global scope due to compilation time. * */ class FlowSensitiveInlineVariables extends AbstractPostOrderCallback implements CompilerPass, ScopedCallback { /** * Implementation: * * This pass first perform a traversal to gather a list of Candidates that * could be inlined using {@link GatherCandiates}. * * The second step involves verifying that each candidate is actually safe * to inline with {@link Candidate#canInline(Scope)} and finally perform * inlining using {@link Candidate#inlineVariable()}. * * The reason for the delayed evaluation of the candidates is because we * need two separate dataflow result. */ private final AbstractCompiler compiler; private final Set inlinedNewDependencies = Sets.newHashSet(); // These two pieces of data is persistent in the whole execution of enter // scope. private ControlFlowGraph cfg; private List candidates; private MustBeReachingVariableDef reachingDef; private MaybeReachingVariableUse reachingUses; private static final Predicate SIDE_EFFECT_PREDICATE = new Predicate() { @Override public boolean apply(Node n) { // When the node is null it means, we reached the implicit return // where the function returns (possibly without an return statement) if (n == null) { return false; } // TODO(user): We only care about calls to functions that // passes one of the dependent variable to a non-side-effect free // function. if (n.isCall() && NodeUtil.functionCallHasSideEffects(n)) { return true; } if (n.isNew() && NodeUtil.constructorCallHasSideEffects(n)) { return true; } if (n.isDelProp()) { return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && apply(c)) { return true; } } return false; } }; public FlowSensitiveInlineVariables(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void enterScope(NodeTraversal t) { if (t.inGlobalScope()) { return; // Don't even brother. All global variables are likely escaped. } if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < t.getScope().getVarCount()) { return; } // Compute the forward reaching definition. ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); // Process the body of the function. Preconditions.checkState(t.getScopeRoot().isFunction()); cfa.process(null, t.getScopeRoot().getLastChild()); cfg = cfa.getCfg(); reachingDef = new MustBeReachingVariableDef(cfg, t.getScope(), compiler); reachingDef.analyze(); candidates = Lists.newLinkedList(); // Using the forward reaching definition search to find all the inline // candidates new NodeTraversal(compiler, new GatherCandiates()).traverse( t.getScopeRoot().getLastChild()); // Compute the backward reaching use. The CFG can be reused. reachingUses = new MaybeReachingVariableUse(cfg, t.getScope(), compiler); reachingUses.analyze(); for (Candidate c : candidates) { if (c.canInline(t.getScope())) { c.inlineVariable(); // If definition c has dependencies, then inlining it may have // introduced new dependencies for our other inlining candidates. // // MustBeReachingVariableDef uses this dependency graph in its // analysis, so some of these candidates may no longer be valid. // We keep track of when the variable dependency graph changed // so that we can back off appropriately. if (!c.defMetadata.depends.isEmpty()) { inlinedNewDependencies.add(t.getScope().getVar(c.varName)); } } } } @Override public void exitScope(NodeTraversal t) {} @Override public void process(Node externs, Node root) { (new NodeTraversal(compiler, this)).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(user): While the helpers do a subtree traversal on the AST, the // compiler pass itself only traverse the AST to look for function // declarations to perform dataflow analysis on. We could combine // the traversal in DataFlowAnalysis's computeEscaped later to save some // time. } /** * Gathers a list of possible candidates for inlining based only on * information from {@link MustBeReachingVariableDef}. The list will be stored * in {@code candidates} and the validity of each inlining Candidate should * be later verified with {@link Candidate#canInline(Scope)} when * {@link MaybeReachingVariableUse} has been performed. */ private class GatherCandiates extends AbstractShallowCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { DiGraphNode graphNode = cfg.getDirectedGraphNode(n); if (graphNode == null) { // Not a CFG node. return; } final Node cfgNode = n; AbstractCfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { // n.getParent() isn't null. This just the case where n is the root // node that gatherCb started at. if (parent == null) { return; } // Make sure that the name node is purely a read. if ((NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || parent.isVar() || parent.isInc() || parent.isDec() || parent.isParamList() || parent.isCatch()) { return; } String name = n.getString(); if (compiler.getCodingConvention().isExported(name)) { return; } Definition def = reachingDef.getDef(name, cfgNode); // TODO(nicksantos): We need to add some notion of @const outer // scope vars. We can inline those just fine. if (def != null && !reachingDef.dependsOnOuterScopeVars(def)) { candidates.add(new Candidate(name, def, n, cfgNode)); } } } }; NodeTraversal.traverse(compiler, cfgNode, gatherCb); } } /** * Models the connection between a definition and a use of that definition. */ private class Candidate { // Name of the variable. private final String varName; // Nodes related to the definition. private Node def; private final Definition defMetadata; // Nodes related to the use. private final Node use; private final Node useCfgNode; // Number of uses of the variable within the CFG node that represented the // use in the CFG. private int numUseWithinUseCfgNode; Candidate(String varName, Definition defMetadata, Node use, Node useCfgNode) { Preconditions.checkArgument(use.isName()); this.varName = varName; this.defMetadata = defMetadata; this.use = use; this.useCfgNode = useCfgNode; } private Node getDefCfgNode() { return defMetadata.node; } private boolean canInline(final Scope scope) { // Cannot inline a parameter. if (getDefCfgNode().isFunction()) { return false; } // If one of our dependencies has been inlined, then our dependency // graph is wrong. Re-computing it would take another CFG computation, // so we just back off for now. for (Var dependency : defMetadata.depends) { if (inlinedNewDependencies.contains(dependency)) { return false; } } getDefinition(getDefCfgNode()); getNumUseInUseCfgNode(useCfgNode); // Definition was not found. if (def == null) { return false; } // Check that the assignment isn't used as a R-Value. // TODO(user): Certain cases we can still inline. if (def.isAssign() && !NodeUtil.isExprAssign(def.getParent())) { return false; } // The right of the definition has side effect: // Example, for x: // x = readProp(b), modifyProp(b); print(x); if (checkRightOf(def, getDefCfgNode(), SIDE_EFFECT_PREDICATE)) { return false; } // Similar check as the above but this time, all the sub-expressions // left of the use of the variable. // x = readProp(b); modifyProp(b), print(x); if (checkLeftOf(use, useCfgNode, SIDE_EFFECT_PREDICATE)) { return false; } // TODO(user): Side-effect is OK sometimes. As long as there are no // side-effect function down all paths to the use. Once we have all the // side-effect analysis tool. if (NodeUtil.mayHaveSideEffects(def.getLastChild(), compiler)) { return false; } // TODO(user): We could inline all the uses if the expression is short. // Finally we have to make sure that there are no more than one use // in the program and in the CFG node. Even when it is semantically // correctly inlining twice increases code size. if (numUseWithinUseCfgNode != 1) { return false; } // Make sure that the name is not within a loop if (NodeUtil.isWithinLoop(use)) { return false; } Collection uses = reachingUses.getUses(varName, getDefCfgNode()); if (uses.size() != 1) { return false; } // We give up inlining stuff with R-Value that has: // 1) GETPROP, GETELEM, // 2) anything that creates a new object. // 3) a direct reference to a catch expression. // Example: // var x = a.b.c; j.c = 1; print(x); // Inlining print(a.b.c) is not safe consider j and be alias to a.b. // TODO(user): We could get more accuracy by looking more in-detail // what j is and what x is trying to into to. // TODO(johnlenz): rework catch expression handling when we // have lexical scope support so catch expressions don't // need to be special cased. if (NodeUtil.has(def.getLastChild(), new Predicate() { @Override public boolean apply(Node input) { switch (input.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: case Token.NEW: return true; case Token.NAME: Var var = scope.getOwnSlot(input.getString()); if (var != null && var.getParentNode().isCatch()) { return true; } } return false; } }, new Predicate() { @Override public boolean apply(Node input) { // Recurse if the node is not a function. return !input.isFunction(); } })) { return false; } // We can skip the side effect check along the paths of two nodes if // they are just next to each other. if (NodeUtil.isStatementBlock(getDefCfgNode().getParent()) && getDefCfgNode().getNext() != useCfgNode) { // Similar side effect check as above but this time the side effect is // else where along the path. // x = readProp(b); while(modifyProp(b)) {}; print(x); CheckPathsBetweenNodes pathCheck = new CheckPathsBetweenNodes( cfg, cfg.getDirectedGraphNode(getDefCfgNode()), cfg.getDirectedGraphNode(useCfgNode), SIDE_EFFECT_PREDICATE, Predicates. >alwaysTrue(), false); if (pathCheck.somePathsSatisfyPredicate()) { return false; } } return true; } /** * Actual transformation. */ private void inlineVariable() { Node defParent = def.getParent(); Node useParent = use.getParent(); if (def.isAssign()) { Node rhs = def.getLastChild(); rhs.detachFromParent(); // Oh yes! I have grandparent to remove this. Preconditions.checkState(defParent.isExprResult()); while (defParent.getParent().isLabel()) { defParent = defParent.getParent(); } defParent.detachFromParent(); useParent.replaceChild(use, rhs); } else if (defParent.isVar()) { Node rhs = def.getLastChild(); def.removeChild(rhs); useParent.replaceChild(use, rhs); } else { Preconditions.checkState(false, "No other definitions can be inlined."); } compiler.reportCodeChange(); } /** * Set the def node * * @param n A node that has a corresponding CFG node in the CFG. */ private void getDefinition(Node n) { AbstractCfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (n.getString().equals(varName) && n.hasChildren()) { def = n; } return; case Token.ASSIGN: Node lhs = n.getFirstChild(); if (lhs.isName() && lhs.getString().equals(varName)) { def = n; } return; } } }; NodeTraversal.traverse(compiler, n, gatherCb); } /** * Computes the number of uses of the variable varName and store it in * numUseWithinUseCfgNode. */ private void getNumUseInUseCfgNode(Node n) { AbstractCfgNodeTraversalCallback gatherCb = new AbstractCfgNodeTraversalCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName() && n.getString().equals(varName) && // do not count in if it is left child of an assignment operator !(parent.isAssign() && (parent.getFirstChild() == n))) { numUseWithinUseCfgNode++; } } }; NodeTraversal.traverse(compiler, n, gatherCb); } } /** * Given an expression by its root and sub-expression n, return true if there * the predicate is true for some expression on the right of n. * * Example: * * NotChecked(), NotChecked(), n, Checked(), Checked(); */ private static boolean checkRightOf( Node n, Node expressionRoot, Predicate predicate) { for (Node p = n; p != expressionRoot; p = p.getParent()) { for (Node cur = p.getNext(); cur != null; cur = cur.getNext()) { if (predicate.apply(cur)) { return true; } } } return false; } /** * Given an expression by its root and sub-expression n, return true if there * the predicate is true for some expression on the left of n. * * Example: * * Checked(), Checked(), n, NotChecked(), NotChecked(); */ private static boolean checkLeftOf( Node n, Node expressionRoot, Predicate predicate) { for (Node p = n.getParent(); p != expressionRoot; p = p.getParent()) { for (Node cur = p.getParent().getFirstChild(); cur != p; cur = cur.getNext()) { if (predicate.apply(cur)) { return true; } } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ForbiddenChange.java0000644000175000017500000000240312115204405026500 0ustar apoapo/* * Copyright 2013 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.rhino.Node; /** * A change handler that throws an exception if any changes are made. * @author nicksantos@google.com (Nick Santos) * @author dimvar@google.com (Dimitris Vardoulakis) */ final class ForbiddenChange extends CodeChangeHandler { @Override public void reportChange() { throw new IllegalStateException("Code changes forbidden"); } @Override public void reportChangedFun(Node n) { throw new IllegalStateException("Code changes forbidden"); } @Override public void reportDeletedFun(Node n) { throw new IllegalStateException("Code changes forbidden"); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/StatementFusion.java0000644000175000017500000001145512115204405026635 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Tries to fuse all the statements in a block into a one statement by using * COMMAs. * * Because COMMAs has the lowest precedence, we never need to insert * extra () around. Once we have only one statement in a block, we can then * eliminate a pair of {}'s. Further more, we can also fold a single * statement IF into && or create further opportunities for all the other * goodies in {@link PeepholeSubstituteAlternateSyntax}. * */ public class StatementFusion extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node n) { // The block of a function body always need { }. if (!n.getParent().isFunction() && canFuseIntoOneStatement(n)) { fuseIntoOneStatement(n); reportCodeChange(); } return n; } private boolean canFuseIntoOneStatement(Node block) { // Fold only statement block. NOT scripts block. if (!block.isBlock()) { return false; } // Nothing to do here. if (!block.hasChildren() || block.hasOneChild()) { return false; } Node last = block.getLastChild(); for (Node c = block.getFirstChild(); c != null; c = c.getNext()) { if (!c.isExprResult() && c != last) { return false; } } // TODO(user): Support more control statement for fusion. // FOR switch(last.getType()) { case Token.IF: case Token.THROW: case Token.SWITCH: case Token.EXPR_RESULT: return true; case Token.RETURN: // We don't want to add a new return value. return last.hasChildren(); case Token.FOR: return NodeUtil.isForIn(last) && // Avoid cases where we have for(var x = foo() in a) { .... !mayHaveSideEffects(last.getFirstChild()); } return false; } private void fuseIntoOneStatement(Node block) { Node cur = block.removeFirstChild(); // Starts building a tree. Node commaTree = cur.removeFirstChild(); while (block.hasMoreThanOneChild()) { Node next = block.removeFirstChild().removeFirstChild(); commaTree = fuseExpressionIntoExpression(commaTree, next); } Preconditions.checkState(block.hasOneChild()); Node last = block.getLastChild(); // Now we are just left with two statements. The comma tree of the first // n - 1 statements (which can be used in an expression) and the last // statement. We perform specific fusion based on the last statement's type. switch(last.getType()) { case Token.IF: case Token.RETURN: case Token.THROW: case Token.SWITCH: case Token.EXPR_RESULT: fuseExpresssonIntoFirstChild(commaTree, last); return; case Token.FOR: if (NodeUtil.isForIn(last)) { fuseExpresssonIntoSecondChild(commaTree, last); } return ; default: throw new IllegalStateException("Statement fusion missing."); } } // exp1, exp1 private static Node fuseExpressionIntoExpression(Node exp1, Node exp2) { Node comma = new Node(Token.COMMA, exp1); comma.copyInformationFrom(exp2); // We can just join the new comma expression with another comma but // lets keep all the comma's in a straight line. That way we can use // tree comparison. if (exp2.isComma()) { Node leftMostChild = exp2; while(leftMostChild.isComma()) { leftMostChild = leftMostChild.getFirstChild(); } Node parent = leftMostChild.getParent(); comma.addChildToBack(leftMostChild.detachFromParent()); parent.addChildToFront(comma); return exp2; } else { comma.addChildToBack(exp2); return comma; } } private static void fuseExpresssonIntoFirstChild(Node exp, Node stmt) { Node val = stmt.removeFirstChild(); Node comma = fuseExpressionIntoExpression(exp, val); stmt.addChildToFront(comma); } private static void fuseExpresssonIntoSecondChild(Node exp, Node stmt) { Node val = stmt.removeChildAfter(stmt.getFirstChild()); Node comma = fuseExpressionIntoExpression(exp, val); stmt.addChildAfter(comma, stmt.getFirstChild()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/js/0000755000175000017500000000000012115204405023250 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/js/base.js0000644000175000017500000000163512115204405024525 0ustar apoapo/* * Copyright 2012 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. */ /** * @fileoverview The base namespace for code injected by the compiler * at compile-time. * * @author nicksantos@google.com (Nick Santos) */ // Because this is injected at compile-time, we don't annotate // it as a constant, because that would just be extra work for // the compiler. var $jscomp = {}; closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/js/runtime_type_check.js0000644000175000017500000002557712115204405027507 0ustar apoapo/* * 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. */ /** * @fileoverview Provides the boilerplate code for run-time type checking. * */ /** @const */ $jscomp.typecheck = {}; /** * A state variable to suspend checking, to avoid infinite calls * caused by calling checked code from the checking functions. * * @type {boolean} */ $jscomp.typecheck.suspendChecking = false; /** * Log and possibly format the run-time type check warning. This * function is customized at compile-time. * * @param {string} warning the warning to log. * @param {*} expr the faulty expression. */ $jscomp.typecheck.log = function(warning, expr) {}; /** * Checks that the given expression matches one of the given checkers, * logging if not, and returning the expression regardless. * * @param {*} expr the expression to check. * @param {!Array.} checkers the checkers to * use in checking, one of these has to match for checking to succeed. * #return {*} the given expression back. */ $jscomp.typecheck.checkType = function(expr, checkers) { if ($jscomp.typecheck.suspendChecking) { return expr; } $jscomp.typecheck.suspendChecking = true; for (var i = 0; i < checkers.length; i++) { var checker = checkers[i]; var ok = checker.check(expr); if (ok) { $jscomp.typecheck.suspendChecking = false; return expr; } } var warning = $jscomp.typecheck.prettify_(expr) + ' not in ' + checkers.join(' '); $jscomp.typecheck.log(warning, expr); $jscomp.typecheck.suspendChecking = false; return expr; }; /** * Prettify the given expression for printing. * * @param {*} expr the expression. * @return {string} a string representation of the given expression. * @private */ $jscomp.typecheck.prettify_ = function(expr) { return $jscomp.typecheck.getClassName_(expr) || String(expr); }; /** * Gets the class name if the given expression is an object. * * @param {*} expr the expression. * @return {string|undefined} the class name or undefined if the * expression is not an object. * @private */ $jscomp.typecheck.getClassName_ = function(expr) { var className = void 0; if (typeof expr == 'object' && expr && expr.constructor) { className = expr.constructor.name; if (!className) { var funNameRe = /function (.{1,})\(/; var m = (funNameRe).exec(expr.constructor.toString()); className = m && m.length > 1 ? m[1] : void 0; } } return className; }; /** * Interface for all checkers. * * @interface */ $jscomp.typecheck.Checker = function() {}; /** * Checks the given expression. * * @param {*} expr the expression to check. * @return {boolean} whether the given expression matches this checker. */ $jscomp.typecheck.Checker.prototype.check = function(expr) {}; /** * A class for all value checkers, except the null checker. * * @param {string} type the value type (e.g. 'number') of this checker. * @constructor * @implements {$jscomp.typecheck.Checker} * @private */ $jscomp.typecheck.ValueChecker_ = function(type) { /** * The value type of this checker. * @type {string} * @private */ this.type_ = type; }; /** @inheritDoc */ $jscomp.typecheck.ValueChecker_.prototype.check = function(expr) { return typeof(expr) == this.type_; }; /** @inheritDoc */ $jscomp.typecheck.ValueChecker_.prototype.toString = function() { return 'value(' + this.type_ + ')'; }; /** * A checker class for null values. * * @constructor * @implements {$jscomp.typecheck.Checker} * @private */ $jscomp.typecheck.NullChecker_ = function() {}; /** @inheritDoc */ $jscomp.typecheck.NullChecker_.prototype.check = function(expr) { return expr === null; }; /** @inheritDoc */ $jscomp.typecheck.NullChecker_.prototype.toString = function() { return 'value(null)'; }; /** * A checker class for a class defined in externs, including built-in * JS types. * *

      If the class type is undefined, then checking is suspended to * avoid spurious warnings. This is necessary because some externs * types are not defined in all browsers. For example, Window is not * defined Chrome, as window has the type DOMWindow. * *

      Another subtlety is that a built-in type may be referenced in a * different frame than the one in which it was created. This causes * instanceOf to return false even though the object is of the correct * type. We work around this by checking as many windows as possible, * redefining open on top and window to keep track of them. * * @param {string} className the name of the extern class to check. * @constructor * @implements {$jscomp.typecheck.Checker} * @private */ $jscomp.typecheck.ExternClassChecker_ = function(className) { /** * The name of the extern class to check. * @type {string} * @private */ this.className_ = className; }; /** * A list of (hopefully all) open windows. * * @type {!Array.} */ $jscomp.typecheck.ExternClassChecker_.windows = []; /** * A list of the original open methods that have been redefined. * * @type {!Array.} */ $jscomp.typecheck.ExternClassChecker_.oldOpenFuns = []; /** * Redefines the open method on the given window, adding tracking. * * @param {!Object} win the window to track. */ $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow = function(win) { if (win.tracked) { return; } win.tracked = true; var key = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.length; $jscomp.typecheck.ExternClassChecker_.oldOpenFuns.push(win.open); $jscomp.typecheck.ExternClassChecker_.windows.push(win); win.open = function() { var w = $jscomp.typecheck.ExternClassChecker_.oldOpenFuns[key].apply( this, arguments); $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(w); return w; }; }; /** * Returns the global 'this' object. This will normally be the same as 'window' * but when running in a worker thread, the DOM is not available. * @return {!Object} * @private */ $jscomp.typecheck.ExternClassChecker_.getGlobalThis_ = function() { return (function() { return this; }).call(null); }; // Install listeners on the global 'this' object. (function() { var globalThis = $jscomp.typecheck.ExternClassChecker_.getGlobalThis_(); $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(globalThis); var theTop = globalThis['top']; if (theTop) { $jscomp.typecheck.ExternClassChecker_.trackOpenOnWindow(theTop); } })(); /** @inheritDoc */ $jscomp.typecheck.ExternClassChecker_.prototype.check = function(expr) { var classTypeDefined = [ false ]; for (var i = 0; i < $jscomp.typecheck.ExternClassChecker_.windows.length; i++) { var w = $jscomp.typecheck.ExternClassChecker_.windows[i]; if (this.checkWindow_(w, expr, classTypeDefined)) { return true; } } return !classTypeDefined[0]; }; /** @inheritDoc */ $jscomp.typecheck.ExternClassChecker_.prototype.toString = function() { return 'ext_class(' + this.className_ + ')'; }; /** * Checks whether the given expression is an instance of this extern * class in this window or any of its frames and subframes. * * @param {!Window} w the window to start checking from. * @param {*} expr the expression to check. * @param {!Array.} classTypeDefined a wrapped boolean * updated to indicate whether the class type was seen in any frame. * @return true if the given expression is an instance of this class. * @private */ $jscomp.typecheck.ExternClassChecker_.prototype.checkWindow_ = function(w, expr, classTypeDefined) { var classType = w[this.className_]; classTypeDefined[0] |= !!classType; if (classType && expr instanceof classType) { return true; } for (var i = 0; i < w.length; i++) { if (this.checkWindow_(w.frames[i], expr, classTypeDefined)) { return true; } } return false; }; /** * A class for all checkers of user-defined classes. * * @param {string} className name of the class to check. * @constructor * @implements {$jscomp.typecheck.Checker} * @private */ $jscomp.typecheck.ClassChecker_ = function(className) { /** * The name of the class to check. * #type {string} * @private */ this.className_ = className; }; /** @inheritDoc */ $jscomp.typecheck.ClassChecker_.prototype.check = function(expr) { return !!(expr && expr['instance_of__' + this.className_]); }; /** @inheritDoc */ $jscomp.typecheck.ClassChecker_.prototype.toString = function() { return 'class(' + this.className_ + ')'; }; /** * A class for all checkers of user-defined interfaces. * * @param {string} interfaceName name of the interface to check. * @constructor * @implements {$jscomp.typecheck.Checker} * @private */ $jscomp.typecheck.InterfaceChecker_ = function(interfaceName) { /** * The name of the interface to check. * #type {string} * @private */ this.interfaceName_ = interfaceName; }; /** @inheritDoc */ $jscomp.typecheck.InterfaceChecker_.prototype.check = function(expr) { return !!(expr && expr['implements__' + this.interfaceName_]); }; /** @inheritDoc */ $jscomp.typecheck.InterfaceChecker_.prototype.toString = function() { return 'interface(' + this.interfaceName_ + ')'; }; /** * A checker for null values. * * #type {!$jscomp.typecheck.Checker} a checker. */ $jscomp.typecheck.nullChecker = new $jscomp.typecheck.NullChecker_(); /** * Creates a checker for the given value type (excluding the null type). * * @param {string} type the value type. * @return {!$jscomp.typecheck.Checker} a checker. */ $jscomp.typecheck.valueChecker = function(type) { return new $jscomp.typecheck.ValueChecker_(type); }; /** * Creates a checker for the given extern class name. * * @param {string} className the class name. * @return {!$jscomp.typecheck.Checker} a checker. */ $jscomp.typecheck.externClassChecker = function(className) { return new $jscomp.typecheck.ExternClassChecker_(className); }; /** * Creates a checker for the given user-defined class. * * @param {string} className the class name. * @return {!$jscomp.typecheck.Checker} a checker. */ $jscomp.typecheck.classChecker = function(className) { return new $jscomp.typecheck.ClassChecker_(className); }; /** * Creates a checker for the given user-defined interface. * * @param {string} interfaceName the interface name. * @return {!$jscomp.typecheck.Checker} a checker. */ $jscomp.typecheck.interfaceChecker = function(interfaceName) { return new $jscomp.typecheck.InterfaceChecker_(interfaceName); }; ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ObjectPropertyStringPostprocess.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ObjectPropertyStringPostprocess.jav0000644000175000017500000000676412115204405031762 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Rewrites * new JSCompiler_ObjectPropertyString(window, foo.prototype.bar) * to new JSCompiler_ObjectPropertyString(foo.prototype, 'bar') * * Rewrites * new JSCompiler_ObjectPropertyString(window, foo[bar]) * to new JSCompiler_ObjectPropertyString(foo, bar) * Rewrites * new JSCompiler_ObjectPropertyString(window, foo$bar$baz) to * new JSCompiler_ObjectPropertyString(window, 'foo$bar$baz') * * @see ObjectPropertyStringPreprocess * */ class ObjectPropertyStringPostprocess implements CompilerPass { private final AbstractCompiler compiler; public ObjectPropertyStringPostprocess(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new Callback()); } private class Callback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isNew()) { return; } Node objectName = n.getFirstChild(); if (!ObjectPropertyStringPreprocess.EXTERN_OBJECT_PROPERTY_STRING.equals( objectName.getQualifiedName())) { return; } Node firstArgument = objectName.getNext(); Node secondArgument = firstArgument.getNext(); int secondArgumentType = secondArgument.getType(); if (secondArgumentType == Token.GETPROP) { // Rewrite "new goog.testing.ObjectPropertyString(window, foo.bar)" // as "new goog.testing.ObjectPropertyString(foo, 'bar')". Node newChild = secondArgument.getFirstChild(); secondArgument.removeChild(newChild); n.replaceChild(firstArgument, newChild); n.replaceChild(secondArgument, IR.string(secondArgument.getFirstChild().getString())); } else if (secondArgumentType == Token.GETELEM) { // Rewrite "new goog.testing.ObjectPropertyString(window, foo[bar])" // as "new goog.testing.ObjectPropertyString(foo, bar)". Node newFirstArgument = secondArgument.getFirstChild(); secondArgument.removeChild(newFirstArgument); Node newSecondArgument = secondArgument.getLastChild(); secondArgument.removeChild(newSecondArgument); n.replaceChild(firstArgument, newFirstArgument); n.replaceChild(secondArgument, newSecondArgument); } else { // Rewrite "new goog.testing.ObjectPropertyString(window, foo)" as // "new goog.testing.ObjectPropertyString(window, 'foo')" n.replaceChild(secondArgument, IR.string(secondArgument.getString())); } compiler.reportCodeChange(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/0000755000175000017500000000000012115204405023615 5ustar apoapo././@LongLink0000644000000000000000000000015700000000000011606 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ChainableReverseAbstractInterpreter.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ChainableReverseAbstractInterp0000644000175000017500000005227412115204405031622 0ustar apoapo/* * Copyright 2007 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.type; import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.CodingConvention; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumElementType; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.TemplatizedType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.TemplateType; import com.google.javascript.rhino.jstype.UnionType; import com.google.javascript.rhino.jstype.Visitor; /** * Chainable reverse abstract interpreter providing basic functionality. * */ public abstract class ChainableReverseAbstractInterpreter implements ReverseAbstractInterpreter { protected final CodingConvention convention; final JSTypeRegistry typeRegistry; private ChainableReverseAbstractInterpreter firstLink; private ChainableReverseAbstractInterpreter nextLink; /** * Constructs an interpreter, which is the only link in a chain. Interpreters * can be appended using {@link #append}. */ public ChainableReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { Preconditions.checkNotNull(convention); this.convention = convention; this.typeRegistry = typeRegistry; firstLink = this; nextLink = null; } /** * Appends a link to {@code this}, returning the updated last link. *

      * The pattern {@code new X().append(new Y())...append(new Z())} forms a * chain starting with X, then Y, then ... Z. * @param lastLink a chainable interpreter, with no next link * @return the updated last link */ public ChainableReverseAbstractInterpreter append( ChainableReverseAbstractInterpreter lastLink) { Preconditions.checkArgument(lastLink.nextLink == null); this.nextLink = lastLink; lastLink.firstLink = this.firstLink; return lastLink; } /** * Gets the first link of this chain. */ public ChainableReverseAbstractInterpreter getFirst() { return firstLink; } /** * Calculates the preciser scope starting with the first link. */ protected FlowScope firstPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return firstLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } /** * Delegates the calculation of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ protected JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; case Token.THIS: // "this" references aren't currently modeled in the CFG. break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor restrictUndefinedVisitor = new Visitor() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(VOID_TYPE)); } @Override public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return null; } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } }; /** * @see #getRestrictedWithoutNull(JSType) */ private final Visitor restrictNullVisitor = new Visitor() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return null; } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(NULL_TYPE)); } @Override public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return getNativeType(VOID_TYPE); } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } }; /** * A class common to all visitors that need to restrict the type based on * {@code typeof}-like conditions. */ abstract class RestrictByTypeOfResultVisitor implements Visitor { /** * Abstracts away the similarities between visiting the unknown type and the * all type. * @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE} * @return the restricted type * @see #caseAllType * @see #caseUnknownType */ protected abstract JSType caseTopType(JSType topType); @Override public JSType caseAllType() { return caseTopType(getNativeType(ALL_TYPE)); } @Override public JSType caseUnknownType() { return caseTopType(getNativeType(CHECKED_UNKNOWN_TYPE)); } @Override public JSType caseUnionType(UnionType type) { JSType restricted = null; for (JSType alternate : type.getAlternates()) { JSType restrictedAlternate = alternate.visit(this); if (restrictedAlternate != null) { if (restricted == null) { restricted = restrictedAlternate; } else { restricted = restrictedAlternate.getLeastSupertype(restricted); } } } return restricted; } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseEnumElementType(EnumElementType enumElementType) { // NOTE(nicksantos): This is a white lie. Suppose we have: // /** @enum {string|number} */ var MyEnum = ...; // if (goog.isNumber(myEnumInstance)) { // /* what is myEnumInstance here? */ // } // There is no type that represents {MyEnum - string}. What we really // need is a notion of "enum subtyping", so that we could dynamically // create a subtype of MyEnum restricted by string. In any case, // this should catch the common case. JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().isEquivalentTo(type)) { return enumElementType; } else { return type; } } @Override public JSType caseTemplatizedType(TemplatizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } } /** * A class common to all visitors that need to restrict the type based on * some {@code typeof}-like condition being true. All base cases return * {@code null}. It is up to the subclasses to override the appropriate ones. */ abstract class RestrictByTrueTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { @Override public JSType caseNoObjectType() { return null; } @Override public JSType caseBooleanType() { return null; } @Override public JSType caseFunctionType(FunctionType type) { return null; } @Override public JSType caseNullType() { return null; } @Override public JSType caseNumberType() { return null; } @Override public JSType caseObjectType(ObjectType type) { return null; } @Override public JSType caseStringType() { return null; } @Override public JSType caseVoidType() { return null; } } /** * A class common to all visitors that need to restrict the type based on * some {@code typeof}-like condition being false. All base cases return * their type. It is up to the subclasses to override the appropriate ones. */ abstract class RestrictByFalseTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { @Override protected JSType caseTopType(JSType topType) { return topType; } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseVoidType() { return getNativeType(VOID_TYPE); } } /** * @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult */ private class RestrictByOneTypeOfResultVisitor extends RestrictByTypeOfResultVisitor { /** * A value known to be equal or not equal to the result of the * {@code typeOf} operation. */ private final String value; /** * {@code true} if the {@code typeOf} result is known to equal * {@code value}; {@code false} if it is known not to equal * {@code value}. */ private final boolean resultEqualsValue; RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) { this.value = value; this.resultEqualsValue = resultEqualsValue; } /** * Computes whether the given result of a {@code typeof} operator matches * expectations, i.e. whether a type that gives such a result should be * kept. */ private boolean matchesExpectation(String result) { return result.equals(value) == resultEqualsValue; } @Override protected JSType caseTopType(JSType topType) { JSType result = topType; if (resultEqualsValue) { JSType typeByName = getNativeTypeForTypeOf(value); if (typeByName != null) { result = typeByName; } } return result; } @Override public JSType caseNoObjectType() { return (value.equals("object") || value.equals("function")) == resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null; } @Override public JSType caseBooleanType() { return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null; } @Override public JSType caseFunctionType(FunctionType type) { return matchesExpectation("function") ? type : null; } @Override public JSType caseNullType() { return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null; } @Override public JSType caseNumberType() { return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null; } @Override public JSType caseObjectType(ObjectType type) { if (value.equals("function")) { JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE); if (resultEqualsValue) { // Objects are restricted to "Function", subtypes are left return ctorType.getGreatestSubtype(type); } else { // Only filter out subtypes of "function" return type.isSubtype(ctorType) ? null : type; } } return matchesExpectation("object") ? type : null; } @Override public JSType caseStringType() { return matchesExpectation("string") ? getNativeType(STRING_TYPE) : null; } @Override public JSType caseVoidType() { return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null; } } /** * Returns a version of type where undefined is not present. */ protected final JSType getRestrictedWithoutUndefined(JSType type) { return type == null ? null : type.visit(restrictUndefinedVisitor); } /** * Returns a version of type where null is not present. */ protected final JSType getRestrictedWithoutNull(JSType type) { return type == null ? null : type.visit(restrictNullVisitor); } /** * Returns a version of {@code type} that is restricted by some knowledge * about the result of the {@code typeof} operation. *

      * The behavior of the {@code typeof} operator can be summarized by the * following table: * * * * * * * * * * * *
      typeresult
      {@code undefined}"undefined"
      {@code null}"object"
      {@code boolean}"boolean"
      {@code number}"number"
      {@code string}"string"
      {@code Object} (which doesn't implement [[Call]])"object"
      {@code Object} (which implements [[Call]])"function"
      * @param type the type to restrict * @param value A value known to be equal or not equal to the result of the * {@code typeof} operation * @param resultEqualsValue {@code true} if the {@code typeOf} result is known * to equal {@code value}; {@code false} if it is known not to * equal {@code value} * @return the restricted type or null if no version of the type matches the * restriction */ JSType getRestrictedByTypeOfResult(JSType type, String value, boolean resultEqualsValue) { if (type == null) { if (resultEqualsValue) { JSType result = getNativeTypeForTypeOf(value); return result == null ? getNativeType(CHECKED_UNKNOWN_TYPE) : result; } else { return null; } } return type.visit( new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue)); } JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } /** * If we definitely know what a type is based on the typeof result, * return it. Otherwise, return null. * * The typeof operation in JS is poorly defined, and this function works * for both the native typeof and goog.typeOf. It should not be made public, * because its semantics are informally defined, and would be wrong in * the general case. */ private JSType getNativeTypeForTypeOf(String value) { if (value.equals("number")) { return getNativeType(NUMBER_TYPE); } else if (value.equals("boolean")) { return getNativeType(BOOLEAN_TYPE); } else if (value.equals("string")) { return getNativeType(STRING_TYPE); } else if (value.equals("undefined")) { return getNativeType(VOID_TYPE); } else if (value.equals("function")) { return getNativeType(U2U_CONSTRUCTOR_TYPE); } else { return null; } } } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ReverseAbstractInterpreter.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ReverseAbstractInterpreter.jav0000644000175000017500000000320312115204405031640 0ustar apoapo/* * Copyright 2007 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.type; import com.google.javascript.rhino.Node; /** * This interface defines what reversed abstract interpreters provide. *

      Abstract interpretation is the process of interpreting a program at an * abstracted level (such as at the type level) instead of the concrete level * (the flow of values). This reversed abstract interpreter reverses the * abstract interpretation process by knowing the outcome of some computation * and calculating a preciser view of the world than the view without knowing * the outcome of the computation.

      * */ public interface ReverseAbstractInterpreter { /** * Calculates a precise version of the scope knowing the outcome of the * condition. * * @param condition the condition's expression * @param blindScope the scope without knowledge about the outcome of the * condition * @param outcome the outcome of the condition */ FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome); } ././@LongLink0000644000000000000000000000015600000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/SemanticReverseAbstractInterpreter.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/SemanticReverseAbstractInterpr0000644000175000017500000004612712115204405031701 0ustar apoapo/* * Copyright 2007 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.type; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.common.base.Function; import com.google.javascript.jscomp.CodingConvention; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSType.TypePair; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.UnionType; import com.google.javascript.rhino.jstype.Visitor; /** * A reverse abstract interpreter using the semantics of the JavaScript * language as a means to reverse interpret computations. This interpreter * expects the parse tree inputs to be typed. * */ public class SemanticReverseAbstractInterpreter extends ChainableReverseAbstractInterpreter { /** * Merging function for equality between types. */ private static final Function EQ = new Function() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderEquality(p.typeB); } }; /** * Merging function for non-equality between types. */ private static final Function NE = new Function() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderInequality(p.typeB); } }; /** * Merging function for strict equality between types. */ private static final Function SHEQ = new Function() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowEquality(p.typeB); } }; /** * Merging function for strict non-equality between types. */ private static final Function SHNE = new Function() { @Override public TypePair apply(TypePair p) { if (p.typeA == null || p.typeB == null) { return null; } return p.typeA.getTypesUnderShallowInequality(p.typeB); } }; /** * Merging function for inequality comparisons between types. */ private final Function INEQ = new Function() { @Override public TypePair apply(TypePair p) { return new TypePair( getRestrictedWithoutUndefined(p.typeA), getRestrictedWithoutUndefined(p.typeB)); } }; /** * Creates a semantic reverse abstract interpreter. */ public SemanticReverseAbstractInterpreter(CodingConvention convention, JSTypeRegistry typeRegistry) { super(convention, typeRegistry); } @Override public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { // Check for the typeof operator. int operatorToken = condition.getType(); switch (operatorToken) { case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.isTypeOf() && right.isString()) { typeOfNode = left; stringNode = right; } else if (right.isTypeOf() && left.isString()) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().isString()) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality(Node left, Node right, FlowScope blindScope, Function merging) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, left, leftType, leftIsRefineable ? merged.typeA : null, right, rightType, rightIsRefineable ? merged.typeB : null); } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; leftType = left.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); } // restricting left type JSType restrictedLeftType = (leftType == null) ? null : leftType.getRestrictedTypeGivenToBooleanOutcome(condition); if (restrictedLeftType == null) { return firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, left, leftType, leftIsRefineable ? restrictedLeftType : null, right, rightType, rightIsRefineable ? restrictedRightType : null); } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); StaticSlot leftVar = leftScope.findUniqueRefinedSlot(blindScope); if (leftVar == null) { return blindScope; } FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); rightScope = firstPreciserScopeKnowingConditionOutcome( right, rightScope, !condition); StaticSlot rightVar = rightScope.findUniqueRefinedSlot(blindScope); if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) { return blindScope; } JSType type = leftVar.getType().getLeastSupertype(rightVar.getType()); FlowScope informed = blindScope.createChildFlowScope(); informed.inferSlotType(leftVar.getName(), type); return informed; } /** * If the restrictedType differs from the originalType, then we should * branch the current flow scope and create a new flow scope with the name * declared with the new type. * * We try not to create spurious child flow scopes as this makes type * inference slower. * * We also do not want spurious slots around in type inference, because * we use these as a signal for "checked unknown" types. A "checked unknown" * type is a symbol that the programmer has already checked and verified that * it's defined, even if we don't know what it is. * * It is OK to pass non-name nodes into this method, as long as you pass * in {@code null} for a restricted type. */ private FlowScope maybeRestrictName( FlowScope blindScope, Node node, JSType originalType, JSType restrictedType) { if (restrictedType != null && restrictedType != originalType) { FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, node, restrictedType); return informed; } return blindScope; } /** * @see #maybeRestrictName */ private FlowScope maybeRestrictTwoNames( FlowScope blindScope, Node left, JSType originalLeftType, JSType restrictedLeftType, Node right, JSType originalRightType, JSType restrictedRightType) { boolean shouldRefineLeft = restrictedLeftType != null && restrictedLeftType != originalLeftType; boolean shouldRefineRight = restrictedRightType != null && restrictedRightType != originalRightType; if (shouldRefineLeft || shouldRefineRight) { FlowScope informed = blindScope.createChildFlowScope(); if (shouldRefineLeft) { declareNameInScope(informed, left, restrictedLeftType); } if (shouldRefineRight) { declareNameInScope(informed, right, restrictedRightType); } return informed; } return blindScope; } private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope, boolean outcome) { JSType type = getTypeIfRefinable(name, blindScope); if (type != null) { return maybeRestrictName( blindScope, name, type, type.getRestrictedTypeGivenToBooleanOutcome(outcome)); } return blindScope; } private FlowScope caseTypeOf(Node node, JSType type, String value, boolean resultEqualsValue, FlowScope blindScope) { return maybeRestrictName( blindScope, node, type, getRestrictedByTypeOfResult(type, value, resultEqualsValue)); } private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope, boolean outcome) { JSType leftType = getTypeIfRefinable(left, blindScope); if (leftType == null) { return blindScope; } JSType rightType = right.getJSType(); ObjectType targetType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); if (rightType != null && rightType.isFunctionType()) { targetType = rightType.toMaybeFunctionType(); } Visitor visitor; if (outcome) { visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); } else { visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); } return maybeRestrictName( blindScope, left, leftType, leftType.visit(visitor)); } /** * Given 'property in object', ensures that the object has the property in the * informed scope by defining it as a qualified name if the object type lacks * the property and it's not in the blind scope. * @param object The node of the right-side of the in. * @param propertyName The string of the left-side of the in. */ private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) { JSType jsType = object.getJSType(); jsType = this.getRestrictedWithoutNull(jsType); jsType = this.getRestrictedWithoutUndefined(jsType); boolean hasProperty = false; ObjectType objectType = ObjectType.cast(jsType); if (objectType != null) { hasProperty = objectType.hasProperty(propertyName); } if (!hasProperty) { String qualifiedName = object.getQualifiedName(); if (qualifiedName != null) { String propertyQualifiedName = qualifiedName + "." + propertyName; if (blindScope.getSlot(propertyQualifiedName) == null) { FlowScope informed = blindScope.createChildFlowScope(); JSType unknownType = typeRegistry.getNativeType( JSTypeNative.UNKNOWN_TYPE); informed.inferQualifiedSlot( object, propertyQualifiedName, unknownType, unknownType); return informed; } } } return blindScope; } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByTrueInstanceOfResultVisitor extends RestrictByTrueTypeOfResultVisitor { private final ObjectType target; RestrictByTrueInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override protected JSType caseTopType(JSType type) { return applyCommonRestriction(type); } @Override public JSType caseUnknownType() { FunctionType funcTarget = JSType.toMaybeFunctionType(target); if (funcTarget != null && funcTarget.hasInstanceType()) { return funcTarget.getInstanceType(); } return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return applyCommonRestriction(type); } @Override public JSType caseUnionType(UnionType type) { return applyCommonRestriction(type); } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type); } private JSType applyCommonRestriction(JSType type) { if (target.isUnknownType()) { return type; } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { return type.getGreatestSubtype(funcTarget.getInstanceType()); } return null; } } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByFalseInstanceOfResultVisitor extends RestrictByFalseTypeOfResultVisitor { private final ObjectType target; RestrictByFalseInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override public JSType caseObjectType(ObjectType type) { if (target.isUnknownType()) { return type; } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { if (type.isSubtype(funcTarget.getInstanceType())) { return null; } return type; } return null; } @Override public JSType caseUnionType(UnionType type) { if (target.isUnknownType()) { return type; } FunctionType funcTarget = target.toMaybeFunctionType(); if (funcTarget.hasInstanceType()) { return type.getRestrictedUnion(funcTarget.getInstanceType()); } return null; } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type); } } } ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ClosureReverseAbstractInterpreter.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/ClosureReverseAbstractInterpre0000644000175000017500000002161412115204405031711 0ustar apoapo/* * Copyright 2007 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.type; import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_VOID; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_STRING_BOOLEAN; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.javascript.jscomp.CodingConvention; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.Visitor; import java.util.Map; /** * A reverse abstract interpreter (RAI) for specific closure patterns such as * {@code goog.isDef}. * */ public class ClosureReverseAbstractInterpreter extends ChainableReverseAbstractInterpreter { /** * For when {@code goog.isArray} returns true. */ private final Visitor restrictToArrayVisitor = new RestrictByTrueTypeOfResultVisitor() { @Override protected JSType caseTopType(JSType topType) { // Ideally, we would like to return any subtype of Array. // Since that's not possible, we don't restrict the type. return topType; } @Override public JSType caseObjectType(ObjectType type) { JSType arrayType = getNativeType(ARRAY_TYPE); return arrayType.isSubtype(type) ? arrayType : null; } }; /** * For when {@code goog.isArray} returns false. */ private final Visitor restrictToNotArrayVisitor = new RestrictByFalseTypeOfResultVisitor() { @Override public JSType caseObjectType(ObjectType type) { return type.isSubtype(getNativeType(ARRAY_TYPE)) ? null : type; } }; /** * For when {@code goog.isObject} returns true. This includes functions, but * not {@code null}. */ private final Visitor restrictToObjectVisitor = new RestrictByTrueTypeOfResultVisitor() { @Override protected JSType caseTopType(JSType topType) { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseFunctionType(FunctionType type) { return type; } }; /** * For when {@code goog.isObject} returns false. */ private final Visitor restrictToNotObjectVisitor = new RestrictByFalseTypeOfResultVisitor() { @Override public JSType caseAllType() { return typeRegistry.createUnionType( getNativeType(NUMBER_STRING_BOOLEAN), getNativeType(NULL_VOID)); } @Override public JSType caseObjectType(ObjectType type) { return null; } @Override public JSType caseFunctionType(FunctionType type) { return null; } }; /** Functions used to restrict types. */ private Map> restricters; /** * Creates a {@link ClosureReverseAbstractInterpreter}. */ public ClosureReverseAbstractInterpreter(CodingConvention convention, final JSTypeRegistry typeRegistry) { super(convention, typeRegistry); this.restricters = new ImmutableMap.Builder>() .put("isDef", new Function() { @Override public JSType apply(TypeRestriction p) { if (p.outcome) { return getRestrictedWithoutUndefined(p.type); } else { return p.type != null ? getNativeType(VOID_TYPE).getGreatestSubtype(p.type) : null; } } }) .put("isNull", new Function() { @Override public JSType apply(TypeRestriction p) { if (p.outcome) { return p.type != null ? getNativeType(NULL_TYPE).getGreatestSubtype(p.type) : null; } else { return getRestrictedWithoutNull(p.type); } } }) .put("isDefAndNotNull", new Function() { @Override public JSType apply(TypeRestriction p) { if (p.outcome) { return getRestrictedWithoutUndefined( getRestrictedWithoutNull(p.type)); } else { return p.type != null ? getNativeType(NULL_VOID).getGreatestSubtype(p.type) : null; } } }) .put("isString", new Function() { @Override public JSType apply(TypeRestriction p) { return getRestrictedByTypeOfResult(p.type, "string", p.outcome); } }) .put("isBoolean", new Function() { @Override public JSType apply(TypeRestriction p) { return getRestrictedByTypeOfResult(p.type, "boolean", p.outcome); } }) .put("isNumber", new Function() { @Override public JSType apply(TypeRestriction p) { return getRestrictedByTypeOfResult(p.type, "number", p.outcome); } }) .put("isFunction", new Function() { @Override public JSType apply(TypeRestriction p) { return getRestrictedByTypeOfResult(p.type, "function", p.outcome); } }) .put("isArray", new Function() { @Override public JSType apply(TypeRestriction p) { if (p.type == null) { return p.outcome ? getNativeType(ARRAY_TYPE) : null; } Visitor visitor = p.outcome ? restrictToArrayVisitor : restrictToNotArrayVisitor; return p.type.visit(visitor); } }) .put("isObject", new Function() { @Override public JSType apply(TypeRestriction p) { if (p.type == null) { return p.outcome ? getNativeType(OBJECT_TYPE) : null; } Visitor visitor = p.outcome ? restrictToObjectVisitor : restrictToNotObjectVisitor; return p.type.visit(visitor); } }) .build(); } @Override public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { if (condition.isCall() && condition.getChildCount() == 2) { Node callee = condition.getFirstChild(); Node param = condition.getLastChild(); if (callee.isGetProp() && param.isQualifiedName()) { JSType paramType = getTypeIfRefinable(param, blindScope); Node left = callee.getFirstChild(); Node right = callee.getLastChild(); if (left.isName() && "goog".equals(left.getString()) && right.isString()) { Function restricter = restricters.get(right.getString()); if (restricter != null) { return restrictParameter(param, paramType, blindScope, restricter, outcome); } } } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope restrictParameter(Node parameter, JSType type, FlowScope blindScope, Function restriction, boolean outcome) { // restricting type = restriction.apply(new TypeRestriction(type, outcome)); // changing the scope if (type != null) { FlowScope informed = blindScope.createChildFlowScope(); declareNameInScope(informed, parameter, type); return informed; } else { return blindScope; } } private static class TypeRestriction { private final JSType type; private final boolean outcome; private TypeRestriction(JSType type, boolean outcome) { this.type = type; this.outcome = outcome; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/package.html0000644000175000017500000000016612115204405026101 0ustar apoapo Provides type-checking data structures and algorithms. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/type/FlowScope.java0000644000175000017500000000552112115204405026364 0ustar apoapo/* * 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.type; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; /** * A symbol table for inferring types during data flow analysis. * * Each flow scope represents the types of all variables in the scope at * a particular point in the flow analysis. * * @author nicksantos@google.com (Nick Santos) */ public interface FlowScope extends StaticScope, LatticeElement { /** * Creates a child of this flow scope, to represent an instruction * directly following this one. */ FlowScope createChildFlowScope(); /** * Defines the type of a symbol at this point in the flow. * @throws IllegalArgumentException If no slot for this symbol exists. */ void inferSlotType(String symbol, JSType type); /** * Infer the type of a qualified name. * * When traversing the control flow of a function, simple names are * declared at the bottom of the flow lattice. But there are far too many * qualified names to be able to do this and be performant. So the bottoms * of qualified names are declared lazily. * * Therefore, when inferring a qualified slot, we need both the "bottom" * type of the slot when we enter the scope, and the current type being * inferred. */ void inferQualifiedSlot(Node node, String symbol, JSType bottomType, JSType inferredType); /** * Optimize this scope and return a new FlowScope with faster lookup. */ FlowScope optimize(); /** * Tries to find a unique refined variable in the refined scope, up to the * the blind scope. * @param blindScope The scope before the refinement, i.e. some parent of the * this scope or itself. * @return The unique refined variable if found or null. */ StaticSlot findUniqueRefinedSlot(FlowScope blindScope); /** * Look through the given scope, and try to find slots where it doesn't * have enough type information. Then fill in that type information * with stuff that we've inferred in the local flow. */ void completeScope(StaticScope scope); } ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AnonymousFunctionNamingCallback.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AnonymousFunctionNamingCallback.jav0000644000175000017500000001112612115204405031604 0ustar apoapo/* * Copyright 2004 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; import com.google.javascript.rhino.Token; /** * Visitor that performs naming operations on anonymous functions by * means of the FunctionNamer interface. Anonymous functions are * named based on context. For example, the anonymous function on the * RHS based on the property at the LHS of the assignment operator. * * goog.string.htmlEscape = function(str) { * } * */ class AnonymousFunctionNamingCallback extends AbstractPostOrderCallback { private final FunctionNamer namer; /** * Interface used by AnonymousFunctionNamingCallback to set the name * of anonymous functions. */ interface FunctionNamer { /** * Generates a string representation of a node for use by * setFunctionName. */ String getName(Node node); /** * Sets the name of an anonymous function. * @param fnNode The function node to update * @param name The name */ void setFunctionName(String name, Node fnNode); /** * Generate a name by "concatenating" the output of multiple calls * to getName. */ String getCombinedName(String lhs, String rhs); } AnonymousFunctionNamingCallback(FunctionNamer namer) { this.namer = namer; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.FUNCTION: // this handles functions that are assigned to variables or // properties // e.g. goog.string.htmlEscape = function(str) { // } // get the function name and see if it's empty Node functionNameNode = n.getFirstChild(); String functionName = functionNameNode.getString(); if (functionName.length() == 0) { if (parent.isAssign()) { // this is an assignment to a property, generally either a // static function or a prototype function // e.g. goog.string.htmlEscape = function() { } or // goog.structs.Map.prototype.getCount = function() { } Node lhs = parent.getFirstChild(); String name = namer.getName(lhs); namer.setFunctionName(name, n); } else if (parent.isName()) { // this is an assignment to a variable // e.g. var handler = function() {} String name = namer.getName(parent); namer.setFunctionName(name, n); } } break; case Token.ASSIGN: // this handles functions that are assigned to a prototype through // an object literal // e.g. BuzzApp.prototype = { // Start : function() { } // } Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (rhs.isObjectLit()) { nameObjectLiteralMethods(rhs, namer.getName(lhs)); } } } private void nameObjectLiteralMethods(Node objectLiteral, String context) { for (Node keyNode = objectLiteral.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { Node valueNode = keyNode.getFirstChild(); // Object literal keys may be STRING_KEY, GETTER_DEF, SETTER_DEF. // Get and Set are skipped because they can not be named. if (keyNode.isStringKey()) { // concatenate the context and key name to get a new qualified name. String name = namer.getCombinedName(context, namer.getName(keyNode)); int type = valueNode.getType(); if (type == Token.FUNCTION) { // set name if function is anonymous Node functionNameNode = valueNode.getFirstChild(); String functionName = functionNameNode.getString(); if (functionName.isEmpty()) { namer.setFunctionName(name, valueNode); } } else if (type == Token.OBJECTLIT) { // process nested object literal nameObjectLiteralMethods(valueNode, name); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FunctionRewriter.java0000644000175000017500000003660412115204405027021 0ustar apoapo/* * 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.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; /** * Reduces the size of common function expressions. * * This pass will rewrite: * * C.prototype.getA = function() { return this.a_ }; * C.prototype.setA = function(newValue) { this.a_ = newValue }; * * as: * * C.prototype.getA = JSCompiler_get("a_); * C.prototype.setA = JSCompiler_set("a_); * * if by doing so we will save bytes, after the helper functions are * added and renaming is done. * */ class FunctionRewriter implements CompilerPass { private final AbstractCompiler compiler; // Safety margin used to avoid growing simple programs by a few bytes. // Selected arbitrarily. private static final int SAVINGS_THRESHOLD = 16; FunctionRewriter(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { List reducers = ImmutableList.of(new ReturnConstantReducer(), new GetterReducer(), new SetterReducer(), new EmptyFunctionReducer(), new IdentityReducer()); Multimap reductionMap = HashMultimap.create(); // Accumulate possible reductions in the reduction multi-map. They // will be applied in the loop below. NodeTraversal.traverse(compiler, root, new ReductionGatherer(reducers, reductionMap)); // Apply reductions iff they will provide some savings. for (Reducer reducer : reducers) { Collection reductions = reductionMap.get(reducer); if (reductions.isEmpty()) { continue; } Node helperCode = parseHelperCode(reducer); if (helperCode == null) { continue; } int helperCodeCost = InlineCostEstimator.getCost(helperCode); // Estimate savings int savings = 0; for (Reduction reduction : reductions) { savings += reduction.estimateSavings(); } // Compare estimated savings against the helper cost. Apply // reductions if doing so will result in some savings. if (savings > (helperCodeCost + SAVINGS_THRESHOLD)) { for (Reduction reduction : reductions) { reduction.apply(); } Node addingRoot = compiler.getNodeForCodeInsertion(null); addingRoot.addChildrenToFront(helperCode); compiler.reportCodeChange(); } } } /** * Parse helper code needed by a reducer. * * @return Helper code root. If parse fails, return null. */ public Node parseHelperCode(Reducer reducer) { Node root = compiler.parseSyntheticCode( reducer.getClass().toString() + ":helper", reducer.getHelperSource()); return (root != null) ? root.removeFirstChild() : null; } private static boolean isReduceableFunctionExpression(Node n) { return NodeUtil.isFunctionExpression(n) && !NodeUtil.isGetOrSetKey(n.getParent()); } /** * Information needed to apply a reduction. */ private class Reduction { private final Node parent; private final Node oldChild; private final Node newChild; Reduction(Node parent, Node oldChild, Node newChild) { this.parent = parent; this.oldChild = oldChild; this.newChild = newChild; } /** * Apply the reduction by replacing the old child with the new child. */ void apply() { parent.replaceChild(oldChild, newChild); compiler.reportCodeChange(); } /** * Estimate number of bytes saved by applying this reduction. */ int estimateSavings() { return InlineCostEstimator.getCost(oldChild) - InlineCostEstimator.getCost(newChild); } } /** * Gathers a list of reductions to apply later by doing an in-order * AST traversal. If a suitable reduction is found, stop traversal * in that branch. */ private class ReductionGatherer implements Callback { private final List reducers; private final Multimap reductions; /** * @param reducers List of reducers to apply during traversal. * @param reductions Reducer -> Reduction multimap, * populated during traversal. */ ReductionGatherer(List reducers, Multimap reductions) { this.reducers = reducers; this.reductions = reductions; } @Override public boolean shouldTraverse(NodeTraversal raversal, Node node, Node parent) { for (Reducer reducer : reducers) { Node replacement = reducer.reduce(node); if (replacement != node) { reductions.put(reducer, new Reduction(parent, node, replacement)); return false; } } return true; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { } } /** * Interface implemented by the strength-reduction optimizers below. */ abstract static class Reducer { /** * @return JS source for helper methods used by this reduction. */ abstract String getHelperSource(); /** * @return root of the reduced subtree if a reduction was applied; * otherwise returns the node argument. */ abstract Node reduce(Node node); /** * Builds a method call based on the the given method name, * argument and history. * * @param methodName Method to call. * @param argumentNode Method argument. */ protected final Node buildCallNode(String methodName, Node argumentNode, Node srcref) { Node call = IR.call(IR.name(methodName)).srcref(srcref); call.putBooleanProp(Node.FREE_CALL, true); if (argumentNode != null) { call.addChildToBack(argumentNode.cloneTree()); } return call; } } /** * Reduces return immutable constant literal methods declarations * with calls to a constant return method factory. * * Example: * a.prototype.b = function() {} * is reduced to: * a.prototype.b = emptyFn(); */ private static class EmptyFunctionReducer extends Reducer { static final String FACTORY_METHOD_NAME = "JSCompiler_emptyFn"; static final String HELPER_SOURCE = "function " + FACTORY_METHOD_NAME + "() {" + " return function() {}" + "}"; @Override public String getHelperSource() { return HELPER_SOURCE; } @Override public Node reduce(Node node) { if (NodeUtil.isEmptyFunctionExpression(node)) { return buildCallNode(FACTORY_METHOD_NAME, null, node); } else { return node; } } } /** * Base class for reducers that match functions that contain a * single return statement. */ abstract static class SingleReturnStatementReducer extends Reducer { /** * @return function return value node if function body contains a * single return statement. Otherwise, null. */ protected final Node maybeGetSingleReturnRValue(Node functionNode) { Node body = functionNode.getLastChild(); if (!body.hasOneChild()) { return null; } Node statement = body.getFirstChild(); if (statement.isReturn()) { return statement.getFirstChild(); } return null; } } /** * Reduces property getter method declarations with calls to a * getter method factory. * * Example: * a.prototype.b = function(a) {return a} * is reduced to: * a.prototype.b = getter(a); */ private static class IdentityReducer extends SingleReturnStatementReducer { static final String FACTORY_METHOD_NAME = "JSCompiler_identityFn"; static final String HELPER_SOURCE = "function " + FACTORY_METHOD_NAME + "() {" + " return function(" + FACTORY_METHOD_NAME + "_value) {" + "return " + FACTORY_METHOD_NAME + "_value}" + "}"; @Override public String getHelperSource() { return HELPER_SOURCE; } @Override public Node reduce(Node node) { if (!isReduceableFunctionExpression(node)) { return node; } if (isIdentityFunction(node)) { return buildCallNode(FACTORY_METHOD_NAME, null, node); } else { return node; } } /** * Checks if the function matches the pattern: * function(, ) {return } * * @return Whether the function matches the pattern. */ private boolean isIdentityFunction(Node functionNode) { Node argList = functionNode.getFirstChild().getNext(); Node paramNode = argList.getFirstChild(); if (paramNode == null) { return false; } Node value = maybeGetSingleReturnRValue(functionNode); if (value != null && value.isName() && value.getString().equals(paramNode.getString())) { return true; } return false; } } /** * Reduces return immutable constant literal methods declarations * with calls to a constant return method factory. * * Example: * a.prototype.b = function() {return 10} * is reduced to: * a.prototype.b = returnconst(10); */ private static class ReturnConstantReducer extends SingleReturnStatementReducer { static final String FACTORY_METHOD_NAME = "JSCompiler_returnArg"; static final String HELPER_SOURCE = "function " + FACTORY_METHOD_NAME + "(" + FACTORY_METHOD_NAME + "_value) {" + " return function() {return " + FACTORY_METHOD_NAME + "_value}" + "}"; @Override public String getHelperSource() { return HELPER_SOURCE; } @Override public Node reduce(Node node) { if (!isReduceableFunctionExpression(node)) { return node; } Node valueNode = getValueNode(node); if (valueNode != null) { return buildCallNode(FACTORY_METHOD_NAME, valueNode, node); } else { return node; } } /** * Checks if the function matches the pattern: * function() {return } * and returns if a match is found. * * @return the immutable value node; or null. */ private Node getValueNode(Node functionNode) { Node value = maybeGetSingleReturnRValue(functionNode); if (value != null && NodeUtil.isImmutableValue(value)) { return value; } return null; } } /** * Reduces property getter method declarations with calls to a * getter method factory. * * Example: * a.prototype.b = function() {return this.b_} * is reduced to: * a.prototype.b = getter("b_"); */ private static class GetterReducer extends SingleReturnStatementReducer { static final String FACTORY_METHOD_NAME = "JSCompiler_get"; static final String HELPER_SOURCE = "function " + FACTORY_METHOD_NAME + "(" + FACTORY_METHOD_NAME + "_name) {" + " return function() {return this[" + FACTORY_METHOD_NAME + "_name]}" + "}"; @Override public String getHelperSource() { return HELPER_SOURCE; } @Override public Node reduce(Node node) { if (!isReduceableFunctionExpression(node)) { return node; } Node propName = getGetPropertyName(node); if (propName != null) { if (!propName.isString()) { throw new IllegalStateException( "Expected STRING, got " + Token.name(propName.getType())); } return buildCallNode(FACTORY_METHOD_NAME, propName, node); } else { return node; } } /** * Checks if the function matches the pattern: * function() {return this.} * and returns if a match is found. * * @return STRING node that is the RHS of a this property get; or null. */ private Node getGetPropertyName(Node functionNode) { Node value = maybeGetSingleReturnRValue(functionNode); if (value != null && value.isGetProp() && value.getFirstChild().isThis()) { return value.getLastChild(); } return null; } } /** * Reduces property setter method declarations with calls to a * setter method factory. * * Example: * a.prototype.setB = function(value) {this.b_ = value} * reduces to: * a.prototype.setB = getter("b_"); */ private static class SetterReducer extends Reducer { static final String FACTORY_METHOD_NAME = "JSCompiler_set"; static final String HELPER_SOURCE = "function " + FACTORY_METHOD_NAME + "(" + FACTORY_METHOD_NAME + "_name) {" + " return function(" + FACTORY_METHOD_NAME + "_value) {" + "this[" + FACTORY_METHOD_NAME + "_name] = " + FACTORY_METHOD_NAME + "_value}" + "}"; @Override public String getHelperSource() { return HELPER_SOURCE; } @Override public Node reduce(Node node) { if (!isReduceableFunctionExpression(node)) { return node; } Node propName = getSetPropertyName(node); if (propName != null) { if (!propName.isString()) { throw new IllegalStateException( "Expected STRING, got " + Token.name(propName.getType())); } return buildCallNode(FACTORY_METHOD_NAME, propName, node); } else { return node; } } /** * Checks if the function matches the pattern: * function(, ) {this. = } * and returns if a match is found. * * @return STRING node that is the RHS of a this property get; or null. */ private Node getSetPropertyName(Node functionNode) { Node body = functionNode.getLastChild(); if (!body.hasOneChild()) { return null; } Node argList = functionNode.getFirstChild().getNext(); Node paramNode = argList.getFirstChild(); if (paramNode == null) { return null; } Node statement = body.getFirstChild(); if (!NodeUtil.isExprAssign(statement)) { return null; } Node assign = statement.getFirstChild(); Node lhs = assign.getFirstChild(); if (lhs.isGetProp() && lhs.getFirstChild().isThis()) { Node rhs = assign.getLastChild(); if (rhs.isName() && rhs.getString().equals(paramNode.getString())) { Node propertyName = lhs.getLastChild(); return propertyName; } } return null; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CoalesceVariableNames.java0000644000175000017500000003542412115204405027657 0ustar apoapo/* * 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.AbstractCfgNodeTraversalCallback; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; import com.google.javascript.jscomp.LiveVariablesAnalysis.LiveVariableLattice; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.graph.GraphColoring; import com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; import com.google.javascript.jscomp.graph.UndiGraph; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Comparator; import java.util.Deque; import java.util.Iterator; import java.util.Set; /** * Reuse variable names if possible. * *

      For example, from var x = 1; print(x); var y = 2; print(y); * to var x = 1; print(x); x = 2; print(x). The benefits are * slightly shorter code because of the removed var declaration, * less unique variables in hope for better renaming, and finally better gzip * compression. * *

      The pass operates similar to a typical register allocator found in an * optimizing compiler by first computing live ranges with * {@link LiveVariablesAnalysis} and a variable interference graph. Then it uses * graph coloring in {@link GraphColoring} to determine which two variables can * be merge together safely. * */ class CoalesceVariableNames extends AbstractPostOrderCallback implements CompilerPass, ScopedCallback { private final AbstractCompiler compiler; private final Deque> colorings; private final boolean usePseudoNames; private static final Comparator coloringTieBreaker = new Comparator() { @Override public int compare(Var v1, Var v2) { return v1.index - v2.index; } }; /** * @param usePseudoNames For debug purposes, when merging variable foo and bar * to foo, rename both variable to foo_bar. */ CoalesceVariableNames(AbstractCompiler compiler, boolean usePseudoNames) { Preconditions.checkState(!compiler.getLifeCycleStage().isNormalized()); this.compiler = compiler; colorings = Lists.newLinkedList(); this.usePseudoNames = usePseudoNames; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } private static boolean shouldOptimizeScope(Scope scope) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. if (scope.isGlobal()) { return false; } if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < scope.getVarCount()) { return false; } return true; } @Override public void enterScope(NodeTraversal t) { Scope scope = t.getScope(); if (!shouldOptimizeScope(scope)) { return; } ControlFlowGraph cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) { liveness.markAllParametersEscaped(); } liveness.analyze(); UndiGraph interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring coloring = new GreedyGraphColoring(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { if (!shouldOptimizeScope(t.getScope())) { return; } colorings.pop(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (colorings.isEmpty() || !n.isName() || parent.isFunction()) { // Don't rename named functions. return; } Var var = t.getScope().getVar(n.getString()); GraphNode vNode = colorings.peek().getGraph().getNode(var); if (vNode == null) { // This is not a local. return; } Var coalescedVar = colorings.peek().getPartitionSuperNode(var); if (!usePseudoNames) { if (vNode.getValue().equals(coalescedVar)) { // The coalesced name is itself, nothing to do. return; } // Rename. n.setString(coalescedVar.name); compiler.reportCodeChange(); if (parent.isVar()) { removeVarDeclaration(n); } } else { // This code block is slow but since usePseudoName is for debugging, // we should not sacrifice performance for non-debugging compilation to // make this fast. String pseudoName = null; Set allMergedNames = Sets.newTreeSet(); for (Iterator i = t.getScope().getVars(); i.hasNext();) { Var iVar = i.next(); // Look for all the variables that can be merged (in the graph by now) // and it is merged with the current coalescedVar. if (colorings.peek().getGraph().getNode(iVar) != null && coalescedVar.equals(colorings.peek().getPartitionSuperNode(iVar))) { allMergedNames.add(iVar.name); } } // Keep its original name. if (allMergedNames.size() == 1) { return; } pseudoName = Joiner.on("_").join(allMergedNames); while (t.getScope().isDeclared(pseudoName, true)) { pseudoName += "$"; } n.setString(pseudoName); compiler.reportCodeChange(); if (!vNode.getValue().equals(coalescedVar) && parent.isVar()) { removeVarDeclaration(n); } } } private UndiGraph computeVariableNamesInterferenceGraph( NodeTraversal t, ControlFlowGraph cfg, Set escaped) { UndiGraph interferenceGraph = LinkedUndirectedGraph.create(); Scope scope = t.getScope(); // First create a node for each non-escaped variable. for (Iterator i = scope.getVars(); i.hasNext();) { Var v = i.next(); if (!escaped.contains(v)) { // TODO(user): In theory, we CAN coalesce function names just like // any variables. Our Liveness analysis captures this just like it as // described in the specification. However, we saw some zipped and // and unzipped size increase after this. We are not totally sure why // that is but, for now, we will respect the dead functions and not play // around with it. if (!v.getParentNode().isFunction()) { interferenceGraph.createNode(v); } } } // Go through each variable and try to connect them. for (Iterator i1 = scope.getVars(); i1.hasNext();) { Var v1 = i1.next(); NEXT_VAR_PAIR: for (Iterator i2 = scope.getVars(); i2.hasNext();) { Var v2 = i2.next(); // Skip duplicate pairs. if (v1.index >= v2.index) { continue; } if (!interferenceGraph.hasNode(v1) || !interferenceGraph.hasNode(v2)) { // Skip nodes that were not added. They are globals and escaped // locals. Also avoid merging a variable with itself. continue NEXT_VAR_PAIR; } if (v1.getParentNode().isParamList() && v2.getParentNode().isParamList()) { interferenceGraph.connectIfNotFound(v1, null, v2); continue NEXT_VAR_PAIR; } // Go through every CFG node in the program and look at // this variable pair. If they are both live at the same // time, add an edge between them and continue to the next pair. NEXT_CROSS_CFG_NODE: for (DiGraphNode cfgNode : cfg.getDirectedGraphNodes()) { if (cfg.isImplicitReturn(cfgNode)) { continue NEXT_CROSS_CFG_NODE; } FlowState state = cfgNode.getAnnotation(); // Check the live states and add edge when possible. if ((state.getIn().isLive(v1) && state.getIn().isLive(v2)) || (state.getOut().isLive(v1) && state.getOut().isLive(v2))) { interferenceGraph.connectIfNotFound(v1, null, v2); continue NEXT_VAR_PAIR; } } // v1 and v2 might not have an edge between them! woohoo. there's // one last sanity check that we have to do: we have to check // if there's a collision *within* the cfg node. NEXT_INTRA_CFG_NODE: for (DiGraphNode cfgNode : cfg.getDirectedGraphNodes()) { if (cfg.isImplicitReturn(cfgNode)) { continue NEXT_INTRA_CFG_NODE; } FlowState state = cfgNode.getAnnotation(); boolean v1OutLive = state.getOut().isLive(v1); boolean v2OutLive = state.getOut().isLive(v2); CombinedLiveRangeChecker checker = new CombinedLiveRangeChecker( new LiveRangeChecker(v1, v2OutLive ? null : v2), new LiveRangeChecker(v2, v1OutLive ? null : v1)); NodeTraversal.traverse( compiler, cfgNode.getValue(), checker); if (checker.connectIfCrossed(interferenceGraph)) { continue NEXT_VAR_PAIR; } } } } return interferenceGraph; } /** * A simple wrapper calls to call two AbstractCfgNodeTraversalCallback * callback during the same traversal. Both traversals must have the same * "shouldTraverse" conditions. */ private static class CombinedLiveRangeChecker extends AbstractCfgNodeTraversalCallback { private final LiveRangeChecker callback1; private final LiveRangeChecker callback2; CombinedLiveRangeChecker( LiveRangeChecker callback1, LiveRangeChecker callback2) { this.callback1 = callback1; this.callback2 = callback2; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (LiveRangeChecker.shouldVisit(n)) { callback1.visit(t, n, parent); callback2.visit(t, n, parent); } } boolean connectIfCrossed(UndiGraph interferenceGraph) { if (callback1.crossed || callback2.crossed) { Var v1 = callback1.getDef(); Var v2 = callback2.getDef(); interferenceGraph.connectIfNotFound(v1, null, v2); return true; } return false; } } /** * Tries to remove variable declaration if the variable has been coalesced * with another variable that has already been declared. */ private void removeVarDeclaration(Node name) { Node var = name.getParent(); Node parent = var.getParent(); // Special case when we are in FOR-IN loop. if (NodeUtil.isForIn(parent)) { var.removeChild(name); parent.replaceChild(var, name); } else if (var.hasOneChild()) { // The removal is easy when there is only one variable in the VAR node. if (name.hasChildren()) { Node value = name.removeFirstChild(); var.removeChild(name); Node assign = IR.assign(name, value).srcref(name); // We don't need to wrapped it with EXPR node if it is within a FOR. if (!parent.isFor()) { assign = NodeUtil.newExpr(assign); } parent.replaceChild(var, assign); } else { // In a FOR( ; ; ) node, we must replace it with an EMPTY or else it // becomes a FOR-IN node. NodeUtil.removeChild(parent, var); } } else { if (!name.hasChildren()) { var.removeChild(name); } // We are going to leave duplicated declaration otherwise. } } private static class LiveRangeChecker extends AbstractCfgNodeTraversalCallback { boolean defFound = false; boolean crossed = false; private final Var def; private final Var use; public LiveRangeChecker(Var def, Var use) { this.def = def; this.use = use; } Var getDef() { return def; } /** * @return Whether any LiveRangeChecker would be interested in the node. */ public static boolean shouldVisit(Node n) { return (n.isName() || (n.hasChildren() && n.getFirstChild().isName())); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!defFound && isAssignTo(def, n, parent)) { defFound = true; } if (defFound && (use == null || isReadFrom(use, n))) { crossed = true; } } private static boolean isAssignTo(Var var, Node n, Node parent) { if (n.isName() && var.getName().equals(n.getString()) && parent != null) { if (parent.isParamList()) { // In a function declaration, the formal parameters are assigned. return true; } else if (parent.isVar()) { // If this is a VAR declaration, if the name node has a child, we are // assigning to that name. return n.hasChildren(); } return false; // Definitely a read. } else { // Lastly, any assignmentOP is also an assign. Node name = n.getFirstChild(); return name != null && name.isName() && var.getName().equals(name.getString()) && NodeUtil.isAssignmentOp(n); } } private static boolean isReadFrom(Var var, Node name) { return name != null && name.isName() && var.getName().equals(name.getString()) && !NodeUtil.isVarOrSimpleAssignLhs(name, name.getParent()); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RenameLabels.java0000644000175000017500000002065112115204405026035 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Supplier; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /** * RenameLabels renames all the labels so that they have short names, to reduce * code size and also to obfuscate the code. * * Label names have a unique namespace, so variable or function names clashes * are not a concern, but keywords clashes are. * * Additionally, labels names are only within the statements include in the * label and do not cross function boundaries. This means that it is possible to * create one label name that is used for labels at any given depth of label * nesting. Typically, the name "a" will be used for all top-level labels, "b" * for the next nested label, and so on. For example: * * * function bar() { * a: { * b: { * foo(); * } * } * * a: { * b: break a; * } * } * * * The general processes is as follows: process() is the entry point for the * CompilerPass, and from there a standard "ScopedCallback" traversal is done, * where "shouldTraverse" is called when descending the tree, and the "visit" is * called in a depth first manner. The name for the label is selected during the * decent in "shouldTraverse", and the references to the label name are renamed * as they are encountered during the "visit". This means that if the label is * unreferenced, it is known when the label node is visited, and, if so, can be * safely removed. * * @author johnlenz@google.com (John Lenz) */ final class RenameLabels implements CompilerPass { private final AbstractCompiler compiler; private final Supplier nameSupplier; private final boolean removeUnused; RenameLabels(AbstractCompiler compiler) { this(compiler, new DefaultNameSupplier(), true); } RenameLabels( AbstractCompiler compiler, Supplier supplier, boolean removeUnused) { this.compiler = compiler; this.nameSupplier = supplier; this.removeUnused = removeUnused; } static class DefaultNameSupplier implements Supplier { // NameGenerator is used to create safe label names. final NameGenerator nameGenerator = new NameGenerator(new HashSet(), "", null); @Override public String get() { return nameGenerator.generateNextName(); } } /** * Iterate through the nodes, renaming all the labels. */ class ProcessLabels implements ScopedCallback { ProcessLabels() { // Create a entry for global scope. namespaceStack.push(new LabelNamespace()); } // A stack of labels namespaces. Labels in an outer scope aren't part of an // inner scope, so a new namespace is created each time a scope is entered. final Deque namespaceStack = Lists.newLinkedList(); // The list of generated names. Typically, the first name will be "a", // the second "b", etc. final ArrayList names = new ArrayList(); @Override public void enterScope(NodeTraversal nodeTraversal) { // Start a new namespace for label names. namespaceStack.push(new LabelNamespace()); } @Override public void exitScope(NodeTraversal nodeTraversal) { namespaceStack.pop(); } /** * shouldTraverse is call when descending into the Node tree, so it is used * here to build the context for label renames. * * {@inheritDoc} */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node node, Node parent) { if (node.isLabel()) { // Determine the new name for this label. LabelNamespace current = namespaceStack.peek(); int currentDepth = current.renameMap.size() + 1; String name = node.getFirstChild().getString(); // Store the context for this label name. LabelInfo li = new LabelInfo(currentDepth); Preconditions.checkState(!current.renameMap.containsKey(name)); current.renameMap.put(name, li); // Create a new name, if needed, for this depth. if (names.size() < currentDepth) { names.add(nameSupplier.get()); } String newName = getNameForId(currentDepth); compiler.addToDebugLog("label renamed: " + name + " => " + newName); } return true; } /** * Delegate the actual processing of the node to visitLabel and * visitBreakOrContinue. * * {@inheritDoc} */ @Override public void visit(NodeTraversal nodeTraversal, Node node, Node parent) { switch (node.getType()) { case Token.LABEL: visitLabel(node, parent); break; case Token.BREAK: case Token.CONTINUE: visitBreakOrContinue(node); break; } } /** * Rename label references in breaks and continues. * @param node The break or continue node. */ private void visitBreakOrContinue(Node node) { Node nameNode = node.getFirstChild(); if (nameNode != null) { // This is a named break or continue; String name = nameNode.getString(); Preconditions.checkState(name.length() != 0); LabelInfo li = getLabelInfo(name); if (li != null) { String newName = getNameForId(li.id); // Mark the label as referenced so it isn't removed. li.referenced = true; if (!name.equals(newName)) { // Give it the short name. nameNode.setString(newName); compiler.reportCodeChange(); } } } } /** * Rename or remove labels. * @param node The label node. * @param parent The parent of the label node. */ private void visitLabel(Node node, Node parent) { Node nameNode = node.getFirstChild(); Preconditions.checkState(nameNode != null); String name = nameNode.getString(); LabelInfo li = getLabelInfo(name); // This is a label... if (li.referenced || !removeUnused) { String newName = getNameForId(li.id); if (!name.equals(newName)) { // ... and it is used, give it the short name. nameNode.setString(newName); compiler.reportCodeChange(); } } else { // ... and it is not referenced, just remove it. Node newChild = node.getLastChild(); node.removeChild(newChild); parent.replaceChild(node, newChild); if (newChild.isBlock()) { NodeUtil.tryMergeBlock(newChild); } compiler.reportCodeChange(); } // Remove the label from the current stack of labels. namespaceStack.peek().renameMap.remove(name); } /** * @param id The id, which is the depth of the label in the current context, * for which to get a short name. * @return The short name of the identified label. */ String getNameForId(int id) { return names.get(id - 1); } /** * @param name The name to retrieve information about. * @return The structure representing the name in the current context. */ LabelInfo getLabelInfo(String name) { return namespaceStack.peek().renameMap.get(name); } } @Override public void process(Node externs, Node root) { // Do variable reference counting. NodeTraversal.traverse(compiler, root, new ProcessLabels()); } private static class LabelInfo { boolean referenced = false; final int id; LabelInfo(int id) { this.id = id; } } private static class LabelNamespace { final Map renameMap = new HashMap(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SourceFile.java0000644000175000017500000003647312115204405025554 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import com.google.common.io.Files; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.nio.charset.Charset; import java.util.Arrays; /** * An abstract representation of a source file that provides access to * language-neutral features. The source file can be loaded from various * locations, such as from disk or from a preloaded string. * * @author nicksantos@google.com (Nick Santos) */ public class SourceFile implements StaticSourceFile, Serializable { private static final long serialVersionUID = 1L; /** A JavaScript source code provider. The value should * be cached so that the source text stays consistent throughout a single * compile. */ public interface Generator { public String getCode(); } /** * Number of lines in the region returned by {@link #getRegion(int)}. * This length must be odd. */ private static final int SOURCE_EXCERPT_REGION_LENGTH = 5; private final String fileName; private boolean isExternFile = false; // The fileName may not always identify the original file - for example, // supersourced Java inputs, or Java inputs that come from Jar files. This // is an optional field that the creator of an AST or SourceFile can set. // It could be a path to the original file, or in case this SourceFile came // from a Jar, it could be the path to the Jar. private String originalPath = null; // Source Line Information private int[] lineOffsets = null; private String code = null; /** * Construct a new abstract source file. * * @param fileName The file name of the source file. It does not necessarily * need to correspond to a real path. But it should be unique. Will * appear in warning messages emitted by the compiler. */ public SourceFile(String fileName) { if (fileName == null || fileName.isEmpty()) { throw new IllegalArgumentException("a source must have a name"); } this.fileName = fileName; } @Override public int getLineOffset(int lineno) { findLineOffsets(); if (lineno < 1 || lineno > lineOffsets.length) { throw new IllegalArgumentException( "Expected line number between 1 and " + lineOffsets.length + "\nActual: " + lineno); } return lineOffsets[lineno - 1]; } /** @return The number of lines in this source file. */ int getNumLines() { findLineOffsets(); return lineOffsets.length; } private void findLineOffsets() { if (lineOffsets != null) { return; } try { String[] sourceLines = getCode().split("\n"); lineOffsets = new int[sourceLines.length]; for (int ii = 1; ii < sourceLines.length; ++ii) { lineOffsets[ii] = lineOffsets[ii - 1] + sourceLines[ii - 1].length() + 1; } } catch (IOException e) { lineOffsets = new int[1]; lineOffsets[0] = 0; } } ////////////////////////////////////////////////////////////////////////////// // Implementation /** * Gets all the code in this source file. * @throws IOException */ public String getCode() throws IOException { return code; } /** * Gets a reader for the code in this source file. */ public Reader getCodeReader() throws IOException { return new StringReader(getCode()); } @VisibleForTesting String getCodeNoCache() { return code; } private void setCode(String sourceCode) { code = sourceCode; } public String getOriginalPath() { return originalPath != null ? originalPath : fileName; } public void setOriginalPath(String originalPath) { this.originalPath = originalPath; } // For SourceFile types which cache source code that can be regenerated // easily, flush the cache. We maintain the cache mostly to speed up // generating source when displaying error messages, so dumping the file // contents after the compile is a fine thing to do. public void clearCachedSource() { // By default, do nothing. Not all kinds of SourceFiles can regenerate // code. } boolean hasSourceInMemory() { return code != null; } /** Returns a unique name for the source file. */ @Override public String getName() { return fileName; } /** Returns whether this is an extern. */ @Override public boolean isExtern() { return isExternFile; } /** Sets that this is an extern. */ void setIsExtern(boolean newVal) { isExternFile = newVal; } @Override public int getLineOfOffset(int offset) { findLineOffsets(); int search = Arrays.binarySearch(lineOffsets, offset); if (search >= 0) { return search + 1; // lines are 1-based. } else { int insertionPoint = -1 * (search + 1); return Math.min(insertionPoint - 1, lineOffsets.length - 1) + 1; } } @Override public int getColumnOfOffset(int offset) { int line = getLineOfOffset(offset); return offset - lineOffsets[line - 1]; } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { findLineOffsets(); if (lineNumber > lineOffsets.length) { return null; } if (lineNumber < 1) { lineNumber = 1; } int pos = lineOffsets[lineNumber - 1]; String js = ""; try { // NOTE(nicksantos): Right now, this is optimized for few warnings. // This is probably the right trade-off, but will be slow if there // are lots of warnings in one file. js = getCode(); } catch (IOException e) { return null; } if (js.indexOf('\n', pos) == -1) { // If next new line cannot be found, there are two cases // 1. pos already reaches the end of file, then null should be returned // 2. otherwise, return the contents between pos and the end of file. if (pos >= js.length()) { return null; } else { return js.substring(pos, js.length()); } } else { return js.substring(pos, js.indexOf('\n', pos)); } } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { String js = ""; try { js = getCode(); } catch (IOException e) { return null; } int pos = 0; int startLine = Math.max(1, lineNumber - (SOURCE_EXCERPT_REGION_LENGTH + 1) / 2 + 1); for (int n = 1; n < startLine; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { break; } pos = nextpos + 1; } int end = pos; int endLine = startLine; for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) { end = js.indexOf('\n', end); if (end == -1) { break; } end++; } if (lineNumber >= endLine) { return null; } if (end == -1) { int last = js.length() - 1; if (js.charAt(last) == '\n') { return new SimpleRegion(startLine, endLine, js.substring(pos, last)); } else { return new SimpleRegion(startLine, endLine, js.substring(pos)); } } else { return new SimpleRegion(startLine, endLine, js.substring(pos, end)); } } @Override public String toString() { return fileName; } public static SourceFile fromFile(String fileName, Charset c) { return builder().withCharset(c).buildFromFile(fileName); } public static SourceFile fromFile(String fileName) { return builder().buildFromFile(fileName); } public static SourceFile fromFile(File file, Charset c) { return builder().withCharset(c).buildFromFile(file); } public static SourceFile fromFile(File file) { return builder().buildFromFile(file); } public static SourceFile fromCode(String fileName, String code) { return builder().buildFromCode(fileName, code); } public static SourceFile fromCode(String fileName, String originalPath, String code) { return builder().withOriginalPath(originalPath) .buildFromCode(fileName, code); } public static SourceFile fromInputStream(String fileName, InputStream s) throws IOException { return builder().buildFromInputStream(fileName, s); } public static SourceFile fromInputStream(String fileName, String originalPath, InputStream s) throws IOException { return builder().withOriginalPath(originalPath) .buildFromInputStream(fileName, s); } public static SourceFile fromReader(String fileName, Reader r) throws IOException { return builder().buildFromReader(fileName, r); } public static SourceFile fromGenerator(String fileName, Generator generator) { return builder().buildFromGenerator(fileName, generator); } /** Create a new builder for source files. */ public static Builder builder() { return new Builder(); } /** * A builder interface for source files. * * Allows users to customize the Charset, and the original path of * the source file (if it differs from the path on disk). */ public static class Builder { private Charset charset = Charsets.UTF_8; private String originalPath = null; public Builder() {} /** Set the charset to use when reading from an input stream or file. */ public Builder withCharset(Charset charset) { this.charset = charset; return this; } /** Set the original path to use. */ public Builder withOriginalPath(String originalPath) { this.originalPath = originalPath; return this; } public SourceFile buildFromFile(String fileName) { return buildFromFile(new File(fileName)); } public SourceFile buildFromFile(File file) { return new OnDisk(file, originalPath, charset); } public SourceFile buildFromCode(String fileName, String code) { return new Preloaded(fileName, originalPath, code); } public SourceFile buildFromInputStream(String fileName, InputStream s) throws IOException { return buildFromCode(fileName, CharStreams.toString(new InputStreamReader(s, charset))); } public SourceFile buildFromReader(String fileName, Reader r) throws IOException { return buildFromCode(fileName, CharStreams.toString(r)); } public SourceFile buildFromGenerator(String fileName, Generator generator) { return new Generated(fileName, originalPath, generator); } } ////////////////////////////////////////////////////////////////////////////// // Implementations /** * A source file where the code has been preloaded. */ static class Preloaded extends SourceFile { private static final long serialVersionUID = 1L; Preloaded(String fileName, String originalPath, String code) { super(fileName); super.setOriginalPath(originalPath); super.setCode(code); } } /** * A source file where the code will be dynamically generated * from the injected interface. */ static class Generated extends SourceFile { private static final long serialVersionUID = 1L; private final Generator generator; // Not private, so that LazyInput can extend it. Generated(String fileName, String originalPath, Generator generator) { super(fileName); super.setOriginalPath(originalPath); this.generator = generator; } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = generator.getCode(); super.setCode(cachedCode); } return cachedCode; } // Clear out the generated code when finished with a compile; we can // regenerate it if we ever need it again. @Override public void clearCachedSource() { super.setCode(null); } } /** * A source file where the code is only read into memory if absolutely * necessary. We will try to delay loading the code into memory as long as * possible. */ static class OnDisk extends SourceFile { private static final long serialVersionUID = 1L; private final File file; // This is stored as a String, but passed in and out as a Charset so that // we can serialize the class. // Default input file format for JSCompiler has always been UTF_8. private String inputCharset = Charsets.UTF_8.name(); OnDisk(File file, String originalPath, Charset c) { super(file.getPath()); this.file = file; super.setOriginalPath(originalPath); if (c != null) { this.setCharset(c); } } @Override public synchronized String getCode() throws IOException { String cachedCode = super.getCode(); if (cachedCode == null) { cachedCode = Files.toString(file, this.getCharset()); super.setCode(cachedCode); } return cachedCode; } /** * Gets a reader for the code in this source file. */ @Override public Reader getCodeReader() throws IOException { if (hasSourceInMemory()) { return super.getCodeReader(); } else { // If we haven't pulled the code into memory yet, don't. return new FileReader(file); } } // Flush the cached code after the compile; we can read it off disk // if we need it again. @Override public void clearCachedSource() { super.setCode(null); } /** * Store the Charset specification as the string version of the name, * rather than the Charset itself. This allows us to serialize the * SourceFile class. * @param c charset to use when reading the input. */ public void setCharset(Charset c) { inputCharset = c.name(); } /** * Get the Charset specifying how we're supposed to read the file * in off disk and into UTF-16. This is stored as a strong to allow * SourceFile to be serialized. * @return Charset object representing charset to use. */ public Charset getCharset() { return Charset.forName(inputCharset); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RhinoErrorReporter.java0000644000175000017500000001751412115204405027323 0ustar apoapo/* * 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.ImmutableMap; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.ScriptRuntime; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; /** * An error reporter for serializing Rhino errors into our error format. * @author nicksantos@google.com (Nick Santos) */ class RhinoErrorReporter { static final DiagnosticType PARSE_ERROR = DiagnosticType.error("JSC_PARSE_ERROR", "Parse error. {0}"); static final DiagnosticType TYPE_PARSE_ERROR = DiagnosticType.warning("JSC_TYPE_PARSE_ERROR", "{0}"); // Special-cased errors, so that they can be configured via the // warnings API. static final DiagnosticType TRAILING_COMMA = DiagnosticType.error("JSC_TRAILING_COMMA", "Parse error. IE8 (and below) will parse trailing commas in " + "array and object literals incorrectly. " + "If you are targeting newer versions of JS, " + "set the appropriate language_in option."); static final DiagnosticType DUPLICATE_PARAM = DiagnosticType.error("JSC_DUPLICATE_PARAM", "Parse error. {0}"); static final DiagnosticType BAD_JSDOC_ANNOTATION = DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}"); static final DiagnosticType MISPLACED_TYPE_ANNOTATION = DiagnosticType.warning("JSC_MISPLACED_TYPE_ANNOTATION", "Type annotations are not allowed here. " + "Are you missing parentheses?"); static final DiagnosticType PARSE_TREE_TOO_DEEP = DiagnosticType.error("PARSE_TREE_TOO_DEEP", "Parse tree too deep."); // A map of Rhino messages to their DiagnosticType. private final Map typeMap; final AbstractCompiler compiler; /** * For each message such as "Not a good use of {0}", replace the place * holder {0} with a wild card that matches all possible strings. * Also put the any non-place-holder in quotes for regex matching later. */ private Pattern replacePlaceHolders(String s) { s = Pattern.quote(s); return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q")); } private RhinoErrorReporter(AbstractCompiler compiler) { this.compiler = compiler; typeMap = ImmutableMap.builder() // Trailing comma .put(replacePlaceHolders( com.google.javascript.rhino.head.ScriptRuntime.getMessage0( "msg.extra.trailing.comma")), TRAILING_COMMA) // Duplicate parameter .put(replacePlaceHolders( com.google.javascript.rhino.head.ScriptRuntime.getMessage0( "msg.dup.parms")), DUPLICATE_PARAM) // Unknown @annotations. .put(replacePlaceHolders(ScriptRuntime.getMessage0("msg.bad.jsdoc.tag")), BAD_JSDOC_ANNOTATION) .put(Pattern.compile("^Type annotations are not allowed here.*"), MISPLACED_TYPE_ANNOTATION) // Type annotation errors. .put(Pattern.compile("^Bad type annotation.*"), TYPE_PARSE_ERROR) // Parse tree too deep. .put(replacePlaceHolders( com.google.javascript.rhino.head.ScriptRuntime.getMessage0( "msg.too.deep.parser.recursion")), PARSE_TREE_TOO_DEEP) .build(); } public static com.google.javascript.rhino.head.ErrorReporter forNewRhino(AbstractCompiler compiler) { return new NewRhinoErrorReporter(compiler); } public static ErrorReporter forOldRhino(AbstractCompiler compiler) { return new OldRhinoErrorReporter(compiler); } void warningAtLine(String message, String sourceName, int line, int lineOffset) { compiler.report( makeError(message, sourceName, line, lineOffset, CheckLevel.WARNING)); } void errorAtLine(String message, String sourceName, int line, int lineOffset) { compiler.report( makeError(message, sourceName, line, lineOffset, CheckLevel.ERROR)); } protected DiagnosticType mapError(String message) { for (Entry entry : typeMap.entrySet()) { if (entry.getKey().matcher(message).matches()) { return entry.getValue(); } } return null; } private JSError makeError(String message, String sourceName, int line, int lineOffset, CheckLevel defaultLevel) { // Try to see if the message is one of the rhino errors we want to // expose as DiagnosticType by matching it with the regex key. DiagnosticType type = mapError(message); if (type != null) { return JSError.make( sourceName, line, lineOffset, type, message); } return JSError.make(sourceName, line, lineOffset, defaultLevel, PARSE_ERROR, message); } private static class OldRhinoErrorReporter extends RhinoErrorReporter implements ErrorReporter { private OldRhinoErrorReporter(AbstractCompiler compiler) { super(compiler); } @Override public void error(String message, String sourceName, int line, int lineOffset) { super.errorAtLine(message, sourceName, line, lineOffset); } @Override public void warning(String message, String sourceName, int line, int lineOffset) { super.warningAtLine(message, sourceName, line, lineOffset); } } private static class NewRhinoErrorReporter extends RhinoErrorReporter implements com.google.javascript.rhino.head.ast.IdeErrorReporter { private NewRhinoErrorReporter(AbstractCompiler compiler) { super(compiler); } @Override public com.google.javascript.rhino.head.EvaluatorException runtimeError(String message, String sourceName, int line, String lineSource, int lineOffset) { DiagnosticType type = mapError(message); if (type != null) { super.errorAtLine(message, sourceName, line, lineOffset); } return new com.google.javascript.rhino.head.EvaluatorException( message, sourceName, line, lineSource, lineOffset); } @Override public void error(String message, String sourceName, int line, String sourceLine, int lineOffset) { super.errorAtLine(message, sourceName, line, lineOffset); } @Override public void error(String message, String sourceName, int offset, int length) { int line = 1; int column = 0; SourceFile file = this.compiler.getSourceFileByName(sourceName); if (file != null) { line = file.getLineOfOffset(offset); column = file.getColumnOfOffset(offset); } super.errorAtLine(message, sourceName, line, column); } @Override public void warning(String message, String sourceName, int line, String sourceLine, int lineOffset) { super.warningAtLine(message, sourceName, line, lineOffset); } @Override public void warning(String message, String sourceName, int offset, int length) { int line = 1; int column = 0; SourceFile file = this.compiler.getSourceFileByName(sourceName); if (file != null) { line = file.getLineOfOffset(offset); column = file.getColumnOfOffset(offset); } super.errorAtLine(message, sourceName, line, column); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/TypeCheck.java0000644000175000017500000021332312115204405025362 0ustar apoapo/* * Copyright 2006 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.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.BOOLEAN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NULL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NUMBER_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_FUNCTION_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.REGEXP_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.STRING_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.type.ReverseAbstractInterpreter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.EnumType; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.TernaryValue; import java.util.HashMap; import java.util.Iterator; import java.util.Set; /** *

      Checks the types of JS expressions against any declared type * information.

      * */ public class TypeCheck implements NodeTraversal.Callback, CompilerPass { // // Internal errors // static final DiagnosticType UNEXPECTED_TOKEN = DiagnosticType.error( "JSC_INTERNAL_ERROR_UNEXPECTED_TOKEN", "Internal Error: Don't know how to handle {0}"); // // User warnings // protected static final String OVERRIDING_PROTOTYPE_WITH_NON_OBJECT = "overriding prototype with non-object"; // TODO(user): make all the non private messages private once the // TypedScopeCreator has been merged with the type checker. static final DiagnosticType DETERMINISTIC_TEST = DiagnosticType.warning( "JSC_DETERMINISTIC_TEST", "condition always evaluates to {2}\n" + "left : {0}\n" + "right: {1}"); static final DiagnosticType DETERMINISTIC_TEST_NO_RESULT = DiagnosticType.warning( "JSC_DETERMINISTIC_TEST_NO_RESULT", "condition always evaluates to the same value\n" + "left : {0}\n" + "right: {1}"); static final DiagnosticType INEXISTENT_ENUM_ELEMENT = DiagnosticType.warning( "JSC_INEXISTENT_ENUM_ELEMENT", "element {0} does not exist on this enum"); // disabled by default. This one only makes sense if you're using // well-typed externs. static final DiagnosticType INEXISTENT_PROPERTY = DiagnosticType.disabled( "JSC_INEXISTENT_PROPERTY", "Property {0} never defined on {1}"); protected static final DiagnosticType NOT_A_CONSTRUCTOR = DiagnosticType.warning( "JSC_NOT_A_CONSTRUCTOR", "cannot instantiate non-constructor"); static final DiagnosticType BIT_OPERATION = DiagnosticType.warning( "JSC_BAD_TYPE_FOR_BIT_OPERATION", "operator {0} cannot be applied to {1}"); static final DiagnosticType NOT_CALLABLE = DiagnosticType.warning( "JSC_NOT_FUNCTION_TYPE", "{0} expressions are not callable"); static final DiagnosticType CONSTRUCTOR_NOT_CALLABLE = DiagnosticType.warning( "JSC_CONSTRUCTOR_NOT_CALLABLE", "Constructor {0} should be called with the \"new\" keyword"); static final DiagnosticType FUNCTION_MASKS_VARIABLE = DiagnosticType.warning( "JSC_FUNCTION_MASKS_VARIABLE", "function {0} masks variable (IE bug)"); static final DiagnosticType MULTIPLE_VAR_DEF = DiagnosticType.warning( "JSC_MULTIPLE_VAR_DEF", "declaration of multiple variables with shared type information"); static final DiagnosticType ENUM_DUP = DiagnosticType.error("JSC_ENUM_DUP", "enum element {0} already defined"); static final DiagnosticType ENUM_NOT_CONSTANT = DiagnosticType.warning("JSC_ENUM_NOT_CONSTANT", "enum key {0} must be a syntactic constant"); static final DiagnosticType INVALID_INTERFACE_MEMBER_DECLARATION = DiagnosticType.warning( "JSC_INVALID_INTERFACE_MEMBER_DECLARATION", "interface members can only be empty property declarations," + " empty functions{0}"); static final DiagnosticType INTERFACE_FUNCTION_NOT_EMPTY = DiagnosticType.warning( "JSC_INTERFACE_FUNCTION_NOT_EMPTY", "interface member functions must have an empty body"); static final DiagnosticType CONFLICTING_SHAPE_TYPE = DiagnosticType.warning( "JSC_CONFLICTING_SHAPE_TYPE", "{1} cannot extend this type; {0}s can only extend {0}s"); static final DiagnosticType CONFLICTING_EXTENDED_TYPE = DiagnosticType.warning( "JSC_CONFLICTING_EXTENDED_TYPE", "{1} cannot extend this type; {0}s can only extend {0}s"); static final DiagnosticType CONFLICTING_IMPLEMENTED_TYPE = DiagnosticType.warning( "JSC_CONFLICTING_IMPLEMENTED_TYPE", "{0} cannot implement this type; " + "an interface can only extend, but not implement interfaces"); static final DiagnosticType BAD_IMPLEMENTED_TYPE = DiagnosticType.warning( "JSC_IMPLEMENTS_NON_INTERFACE", "can only implement interfaces"); static final DiagnosticType HIDDEN_SUPERCLASS_PROPERTY = DiagnosticType.warning( "JSC_HIDDEN_SUPERCLASS_PROPERTY", "property {0} already defined on superclass {1}; " + "use @override to override it"); static final DiagnosticType HIDDEN_INTERFACE_PROPERTY = DiagnosticType.warning( "JSC_HIDDEN_INTERFACE_PROPERTY", "property {0} already defined on interface {1}; " + "use @override to override it"); static final DiagnosticType HIDDEN_SUPERCLASS_PROPERTY_MISMATCH = DiagnosticType.warning("JSC_HIDDEN_SUPERCLASS_PROPERTY_MISMATCH", "mismatch of the {0} property type and the type " + "of the property it overrides from superclass {1}\n" + "original: {2}\n" + "override: {3}"); static final DiagnosticType UNKNOWN_OVERRIDE = DiagnosticType.warning( "JSC_UNKNOWN_OVERRIDE", "property {0} not defined on any superclass of {1}"); static final DiagnosticType INTERFACE_METHOD_OVERRIDE = DiagnosticType.warning( "JSC_INTERFACE_METHOD_OVERRIDE", "property {0} is already defined by the {1} extended interface"); static final DiagnosticType UNKNOWN_EXPR_TYPE = DiagnosticType.warning("JSC_UNKNOWN_EXPR_TYPE", "could not determine the type of this expression"); static final DiagnosticType UNRESOLVED_TYPE = DiagnosticType.warning("JSC_UNRESOLVED_TYPE", "could not resolve the name {0} to a type"); static final DiagnosticType WRONG_ARGUMENT_COUNT = DiagnosticType.warning( "JSC_WRONG_ARGUMENT_COUNT", "Function {0}: called with {1} argument(s). " + "Function requires at least {2} argument(s){3}."); static final DiagnosticType ILLEGAL_IMPLICIT_CAST = DiagnosticType.warning( "JSC_ILLEGAL_IMPLICIT_CAST", "Illegal annotation on {0}. @implicitCast may only be used in " + "externs."); static final DiagnosticType INCOMPATIBLE_EXTENDED_PROPERTY_TYPE = DiagnosticType.warning( "JSC_INCOMPATIBLE_EXTENDED_PROPERTY_TYPE", "Interface {0} has a property {1} with incompatible types in " + "its super interfaces {2} and {3}"); static final DiagnosticType EXPECTED_THIS_TYPE = DiagnosticType.warning( "JSC_EXPECTED_THIS_TYPE", "\"{0}\" must be called with a \"this\" type"); static final DiagnosticType IN_USED_WITH_STRUCT = DiagnosticType.warning("JSC_IN_USED_WITH_STRUCT", "Cannot use the IN operator with structs"); static final DiagnosticType ILLEGAL_PROPERTY_CREATION = DiagnosticType.warning("JSC_ILLEGAL_PROPERTY_CREATION", "Cannot add a property to a struct instance " + "after it is constructed."); static final DiagnosticType ILLEGAL_OBJLIT_KEY = DiagnosticType.warning( "ILLEGAL_OBJLIT_KEY", "Illegal key, the object literal is a {0}"); static final DiagnosticGroup ALL_DIAGNOSTICS = new DiagnosticGroup( DETERMINISTIC_TEST, DETERMINISTIC_TEST_NO_RESULT, INEXISTENT_ENUM_ELEMENT, INEXISTENT_PROPERTY, NOT_A_CONSTRUCTOR, BIT_OPERATION, NOT_CALLABLE, CONSTRUCTOR_NOT_CALLABLE, FUNCTION_MASKS_VARIABLE, MULTIPLE_VAR_DEF, ENUM_DUP, ENUM_NOT_CONSTANT, INVALID_INTERFACE_MEMBER_DECLARATION, INTERFACE_FUNCTION_NOT_EMPTY, CONFLICTING_SHAPE_TYPE, CONFLICTING_EXTENDED_TYPE, CONFLICTING_IMPLEMENTED_TYPE, BAD_IMPLEMENTED_TYPE, HIDDEN_SUPERCLASS_PROPERTY, HIDDEN_INTERFACE_PROPERTY, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, UNKNOWN_OVERRIDE, INTERFACE_METHOD_OVERRIDE, UNKNOWN_EXPR_TYPE, UNRESOLVED_TYPE, WRONG_ARGUMENT_COUNT, ILLEGAL_IMPLICIT_CAST, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, EXPECTED_THIS_TYPE, IN_USED_WITH_STRUCT, ILLEGAL_PROPERTY_CREATION, ILLEGAL_OBJLIT_KEY, RhinoErrorReporter.TYPE_PARSE_ERROR, TypedScopeCreator.UNKNOWN_LENDS, TypedScopeCreator.LENDS_ON_NON_OBJECT, TypedScopeCreator.CTOR_INITIALIZER, TypedScopeCreator.IFACE_INITIALIZER, FunctionTypeBuilder.THIS_TYPE_NON_OBJECT); private final AbstractCompiler compiler; private final TypeValidator validator; private final ReverseAbstractInterpreter reverseInterpreter; private final JSTypeRegistry typeRegistry; private Scope topScope; private MemoizedScopeCreator scopeCreator; private final CheckLevel reportMissingOverride; private final CheckLevel reportUnknownTypes; // This may be expensive, so don't emit these warnings if they're // explicitly turned off. private boolean reportMissingProperties = true; private InferJSDocInfo inferJSDocInfo = null; // These fields are used to calculate the percentage of expressions typed. private int typedCount = 0; private int nullCount = 0; private int unknownCount = 0; private boolean inExterns; // A state boolean to see we are currently in @notypecheck section of the // code. private int noTypeCheckSection = 0; public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, Scope topScope, MemoizedScopeCreator scopeCreator, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.reverseInterpreter = reverseInterpreter; this.typeRegistry = typeRegistry; this.topScope = topScope; this.scopeCreator = scopeCreator; this.reportMissingOverride = reportMissingOverride; this.reportUnknownTypes = reportUnknownTypes; this.inferJSDocInfo = new InferJSDocInfo(compiler); } public TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry, CheckLevel reportMissingOverride, CheckLevel reportUnknownTypes) { this(compiler, reverseInterpreter, typeRegistry, null, null, reportMissingOverride, reportUnknownTypes); } TypeCheck(AbstractCompiler compiler, ReverseAbstractInterpreter reverseInterpreter, JSTypeRegistry typeRegistry) { this(compiler, reverseInterpreter, typeRegistry, null, null, CheckLevel.WARNING, CheckLevel.OFF); } /** Turn on the missing property check. Returns this for easy chaining. */ TypeCheck reportMissingProperties(boolean report) { reportMissingProperties = report; return this; } /** * Main entry point for this phase of processing. This follows the pattern for * JSCompiler phases. * * @param externsRoot The root of the externs parse tree. * @param jsRoot The root of the input parse tree to be checked. */ @Override public void process(Node externsRoot, Node jsRoot) { Preconditions.checkNotNull(scopeCreator); Preconditions.checkNotNull(topScope); Node externsAndJs = jsRoot.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState( externsRoot == null || externsAndJs.hasChild(externsRoot)); if (externsRoot != null) { check(externsRoot, true); } check(jsRoot, false); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } private void checkNoTypeCheckSection(Node n, boolean enterSection) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.VAR: case Token.FUNCTION: case Token.ASSIGN: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { if (enterSection) { noTypeCheckSection++; } else { noTypeCheckSection--; } } validator.setShouldReport(noTypeCheckSection == 0); break; } } private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType, String... arguments) { if (noTypeCheckSection == 0) { t.report(n, diagnosticType, arguments); } } @Override public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { checkNoTypeCheckSection(n, true); switch (n.getType()) { case Token.FUNCTION: // normal type checking final Scope outerScope = t.getScope(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { report(t, n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.CAST: Node expr = n.getFirstChild(); ensureTyped(t, n, getJSType(expr)); // If the cast, tightens the type apply it, so it is available post // normalization. JSType castType = getJSType(n); JSType exprType = getJSType(expr); if (castType.isSubtype(exprType)) { expr.setJSType(castType); } break; case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: typeable = false; break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: ensureTyped(t, n, STRING_TYPE); break; case Token.STRING_KEY: typeable = false; break; case Token.GETTER_DEF: case Token.SETTER_DEF: // Object literal keys are handled with OBJECTLIT break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.isAssign() && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); break; case Token.CALL: visitCall(t, n); typeable = !parent.isExprResult(); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); checkPropCreation(t, left); validator.expectNumber(t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: case Token.SHEQ: case Token.SHNE: { left = n.getFirstChild(); right = n.getLastChild(); if (left.isTypeOf()) { if (right.isString()) { checkTypeofString(t, right, right.getString()); } } else if (right.isTypeOf() && left.isString()) { checkTypeofString(t, left, left.getString()); } leftType = getJSType(left); rightType = getJSType(right); // We do not want to warn about explicit comparisons to VOID. People // often do this if they think their type annotations screwed up. // // We do want to warn about cases where people compare things like // (Array|null) == (Function|null) // because it probably means they screwed up. // // This heuristic here is not perfect, but should catch cases we // care about without too many false negatives. JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = TernaryValue.UNKNOWN; if (n.getType() == Token.EQ || n.getType() == Token.NE) { result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (n.isNE()) { result = result.not(); } } else { // SHEQ or SHNE if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { result = n.getType() == Token.SHEQ ? TernaryValue.FALSE : TernaryValue.TRUE; } } if (result != TernaryValue.UNKNOWN) { report(t, n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotNullOrUndefined( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotNullOrUndefined( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); rightType = getJSType(right); validator.expectString(t, left, getJSType(left), "left side of 'in'"); validator.expectObject(t, n, rightType, "'in' requires an object"); if (rightType.isStruct()) { report(t, right, IN_USED_WITH_STRUCT); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, getJSType(left), "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: checkPropCreation(t, n.getFirstChild()); // fall through case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject(t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT_CASE: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.IF: case Token.WHILE: typeable = false; break; case Token.FOR: if (NodeUtil.isForIn(n)) { Node obj = n.getChildAtIndex(1); if (getJSType(obj).isStruct()) { report(t, obj, IN_USED_WITH_STRUCT); } } typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t, n); } else { // If this is an enum, then give that type to the objectlit as well. if ((n.isObjectLit()) && (parent.getJSType() instanceof EnumType)) { ensureTyped(t, n, parent.getJSType()); } else { ensureTyped(t, n); } } if (n.isObjectLit()) { JSType typ = getJSType(n); for (Node key : n.children()) { visitObjLitKey(t, key, n, typ); } } break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType())); ensureTyped(t, n); break; } // Don't count externs since the user's code may not even use that part. typeable = typeable && !inExterns; if (typeable) { doPercentTypedAccounting(t, n); } checkNoTypeCheckSection(n, false); } private void checkTypeofString(NodeTraversal t, Node n, String s) { if (!(s.equals("number") || s.equals("string") || s.equals("boolean") || s.equals("undefined") || s.equals("function") || s.equals("object") || s.equals("unknown"))) { validator.expectValidTypeofName(t, n, s); } } /** * Counts the given node in the typed statistics. * @param n a node that should be typed */ private void doPercentTypedAccounting(NodeTraversal t, Node n) { JSType type = n.getJSType(); if (type == null) { nullCount++; } else if (type.isUnknownType()) { if (reportUnknownTypes.isOn()) { compiler.report( t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE)); } unknownCount++; } else { typedCount++; } } /** * Visits an assignment lvalue = rvalue. If the * lvalue is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (assign.isAssign() is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); // Check property sets to 'object.property' when 'object' is known. if (lvalue.isGetProp()) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); Node property = lvalue.getLastChild(); String pname = property.getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.isGetProp()) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, pname, lvalue, rvalue); } } checkEnumAlias(t, info, rvalue); checkPropCreation(t, lvalue); // Prototype assignments are special, because they actually affect // the definition of a class. These are mostly validated // during TypedScopeCreator, and we only look for the "dumb" cases here. // object.prototype = ...; if (pname.equals("prototype")) { if (objectJsType != null && objectJsType.isFunctionType()) { FunctionType functionType = objectJsType.toMaybeFunctionType(); if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); // Only assign structs to the prototype of a @struct constructor if (functionType.makesStructs() && !rvalueType.isStruct()) { String funName = functionType.getTypeOfThis().toString(); compiler.report(t.makeError(assign, CONFLICTING_SHAPE_TYPE, "struct", funName)); } return; } } } // The generic checks for 'object.property' when 'object' is known, // and 'property' is declared on it. // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(pname) && !type.isPropertyTypeInferred(pname) && !propertyIsImplicitCast(type, pname)) { JSType expectedType = type.getPropertyType(pname); if (!expectedType.isUnknownType()) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), expectedType, object, pname); checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, expectedType); return; } } } // If we couldn't get the property type with normal object property // lookups, then check inheritance anyway with the unknown type. checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, getNativeType(UNKNOWN_TYPE)); } // Check qualified name sets to 'object' and 'object.property'. // This can sometimes handle cases when the type of 'object' is not known. // e.g., // var obj = createUnknownType(); // /** @type {number} */ obj.foo = true; JSType leftType = getJSType(lvalue); if (lvalue.isQualifiedName()) { // variable with inferred type case Var var = t.getScope().getVar(lvalue.getQualifiedName()); if (var != null) { if (var.isTypeInferred()) { return; } if (NodeUtil.getRootOfQualifiedName(lvalue).isThis() && t.getScope() != var.getScope()) { // Don't look at "this.foo" variables from other scopes. return; } if (var.getType() != null) { leftType = var.getType(); } } } // Fall through case for arbitrary LHS and arbitrary RHS. Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** * After a struct object is created, we can't add new properties to it, with * one exception. We allow creation of "static" properties like * Foo.prototype.bar = baz; * where Foo.prototype is a struct, if the assignment happens at the top level * and the constructor Foo is defined in the same file. */ private void checkPropCreation(NodeTraversal t, Node lvalue) { if (lvalue.isGetProp()) { Node obj = lvalue.getFirstChild(); Node prop = lvalue.getLastChild(); JSType objType = getJSType(obj); String pname = prop.getString(); if (!objType.isStruct() || objType.hasProperty(pname)) { return; } Scope s = t.getScope(); if (obj.isThis() && getJSType(s.getRootNode()).isConstructor()) { return; } // Prop created outside ctor, check that it's a static prop Node assgnStm = lvalue.getParent().getParent(); if (objType instanceof ObjectType && s.isGlobal() && NodeUtil.isPrototypePropertyDeclaration(assgnStm)) { ObjectType instance = objType.toObjectType().getOwnerFunction().getInstanceType(); String file = lvalue.getSourceFileName(); Node ctor = instance.getConstructor().getSource(); if (ctor != null && ctor.getSourceFileName().equals(file)) { return; } } report(t, prop, ILLEGAL_PROPERTY_CREATION); } } private void checkPropertyInheritanceOnGetpropAssign( NodeTraversal t, Node assign, Node object, String property, JSDocInfo info, JSType propertyType) { // Inheritance checks for prototype properties. // // TODO(nicksantos): This isn't the right place to do this check. We // really want to do this when we're looking at the constructor. // We'd find all its properties and make sure they followed inheritance // rules, like we currently do for @implements to make sure // all the methods are implemented. // // As-is, this misses many other ways to override a property. // // object.prototype.property = ...; if (object.isGetProp()) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = getJSType(object2); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, propertyType); } } } } } /** * Visits an object literal field definition key : value. * * If the lvalue is a prototype modification, we change the * schema of the object type it is referring to. * * @param t the traversal * @param key the assign node */ private void visitObjLitKey( NodeTraversal t, Node key, Node objlit, JSType litType) { // Do not validate object lit value types in externs. We don't really care, // and it makes it easier to generate externs. if (objlit.isFromExterns()) { ensureTyped(t, key); return; } // Structs must have unquoted keys and dicts must have quoted keys if (litType.isStruct() && key.isQuotedString()) { report(t, key, ILLEGAL_OBJLIT_KEY, "struct"); } else if (litType.isDict() && !key.isQuotedString()) { report(t, key, ILLEGAL_OBJLIT_KEY, "dict"); } // TODO(johnlenz): Validate get and set function declarations are valid // as is the functions can have "extraneous" bits. // For getter and setter property definitions the // r-value type != the property type. Node rvalue = key.getFirstChild(); JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( key, getJSType(rvalue)); if (rightType == null) { rightType = getNativeType(UNKNOWN_TYPE); } Node owner = objlit; // Validate value is assignable to the key type. JSType keyType = getJSType(key); JSType allowedValueType = keyType; if (allowedValueType.isEnumElementType()) { allowedValueType = allowedValueType.toMaybeEnumElementType().getPrimitiveType(); } boolean valid = validator.expectCanAssignToPropertyOf(t, key, rightType, allowedValueType, owner, NodeUtil.getObjectLitKeyName(key)); if (valid) { ensureTyped(t, key, rightType); } else { ensureTyped(t, key); } // Validate that the key type is assignable to the object property type. // This is necessary as the objlit may have been cast to a non-literal // object type. // TODO(johnlenz): consider introducing a CAST node to the AST (or // perhaps a parentheses node). JSType objlitType = getJSType(objlit); ObjectType type = ObjectType.cast( objlitType.restrictByNotNullOrUndefined()); if (type != null) { String property = NodeUtil.getObjectLitKeyName(key); if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, key, keyType, type.getPropertyType(property), owner, property); } return; } } /** * Returns true if any type in the chain has an implicitCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // If the supertype doesn't resolve correctly, we've warned about this // already. if (hasUnknownOrEmptySupertype(ctorType)) { return; } FunctionType superClass = ctorType.getSuperClassConstructor(); boolean superClassHasProperty = superClass != null && superClass.getInstanceType().hasProperty(propertyName); boolean superClassHasDeclaredProperty = superClass != null && superClass.getInstanceType().isPropertyTypeDeclared(propertyName); // For interface boolean superInterfaceHasProperty = false; boolean superInterfaceHasDeclaredProperty = false; if (ctorType.isInterface()) { for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { superInterfaceHasProperty = superInterfaceHasProperty || interfaceType.hasProperty(propertyName); superInterfaceHasDeclaredProperty = superInterfaceHasDeclaredProperty || interfaceType.isPropertyTypeDeclared(propertyName); } } boolean declaredOverride = info != null && info.isOverride(); boolean foundInterfaceProperty = false; if (ctorType.isConstructor()) { for (JSType implementedInterface : ctorType.getAllImplementedInterfaces()) { if (implementedInterface.isUnknownType() || implementedInterface.isEmptyType()) { continue; } FunctionType interfaceType = implementedInterface.toObjectType().getConstructor(); Preconditions.checkNotNull(interfaceType); boolean interfaceHasProperty = interfaceType.getPrototype().hasProperty(propertyName); foundInterfaceProperty = foundInterfaceProperty || interfaceHasProperty; if (reportMissingOverride.isOn() && !declaredOverride && interfaceHasProperty) { // @override not present, but the property does override an interface // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_INTERFACE_PROPERTY, propertyName, interfaceType.getTopMostDefiningType(propertyName).toString())); } } } if (!declaredOverride && !superClassHasProperty && !superInterfaceHasProperty) { // nothing to do here, it's just a plain new property return; } ObjectType topInstanceType = superClassHasDeclaredProperty ? superClass.getTopMostDefiningType(propertyName) : null; boolean declaredLocally = ctorType.isConstructor() && (ctorType.getPrototype().hasOwnProperty(propertyName) || ctorType.getInstanceType().hasOwnProperty(propertyName)); if (reportMissingOverride.isOn() && !declaredOverride && superClassHasDeclaredProperty && declaredLocally) { // @override not present, but the property does override a superclass // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_SUPERCLASS_PROPERTY, propertyName, topInstanceType.toString())); } // @override is present and we have to check that it is ok if (superClassHasDeclaredProperty) { // there is a superclass implementation JSType superClassPropType = superClass.getInstanceType().getPropertyType(propertyName); if (!propertyType.isSubtype(superClassPropType)) { compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superClassPropType.toString(), propertyType.toString())); } } else if (superInterfaceHasDeclaredProperty) { // there is an super interface property for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { JSType superPropertyType = interfaceType.getPropertyType(propertyName); if (!propertyType.isSubtype(superPropertyType)) { topInstanceType = interfaceType.getConstructor(). getTopMostDefiningType(propertyName); compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superPropertyType.toString(), propertyType.toString())); } } } } else if (!foundInterfaceProperty && !superClassHasProperty && !superInterfaceHasProperty) { // there is no superclass nor interface implementation compiler.report( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Given a constructor or an interface type, find out whether the unknown * type is a supertype of the current type. */ private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) { Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); Preconditions.checkArgument(!ctor.isUnknownType()); // The type system should notice inheritance cycles on its own // and break the cycle. while (true) { ObjectType maybeSuperInstanceType = ctor.getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return false; } if (maybeSuperInstanceType.isUnknownType() || maybeSuperInstanceType.isEmptyType()) { return true; } ctor = maybeSuperInstanceType.getConstructor(); if (ctor == null) { return false; } Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); } } /** * Visits an ASSIGN node for cases such as *
         * interface.property2.property = ...;
         * 
      */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); // Only 2 values are allowed for methods: // goog.abstractMethod // function () {}; // or for properties, no assignment such as: // InterfaceFoo.prototype.foobar; String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isFunctionType()) { // This is bad i18n style but we don't localize our compiler errors. String abstractMethodMessage = (abstractMethodName != null) ? ", or " + abstractMethodName : ""; compiler.report( t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of n */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); if (childType.isDict()) { report(t, property, TypeValidator.ILLEGAL_PROPERTY_ACCESS, "'.'", "dict"); } else if (validator.expectNotNullOrUndefined(t, n, childType, "No properties on this expression", getNativeType(OBJECT_TYPE))) { checkPropertyAccess(childType, property.getString(), t, n); } ensureTyped(t, n); } /** * Emit a warning if we can prove that a property cannot possibly be * defined on an object. Note the difference between JS and a strictly * statically typed language: we're checking if the property * *cannot be defined*, whereas a java compiler would check if the * property *can be undefined*. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { // If the property type is unknown, check the object type to see if it // can ever be defined. We explicitly exclude CHECKED_UNKNOWN (for // properties where we've checked that it exists, or for properties on // objects that aren't in this binary). JSType propType = getJSType(n); if (propType.isEquivalentTo(typeRegistry.getNativeType(UNKNOWN_TYPE))) { childType = childType.autobox(); ObjectType objectType = ObjectType.cast(childType); if (objectType != null) { // We special-case object types so that checks on enums can be // much stricter, and so that we can use hasProperty (which is much // faster in most cases). if (!objectType.hasProperty(propName) || objectType.isEquivalentTo( typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { report(t, n, INEXISTENT_ENUM_ELEMENT, propName); } else { checkPropertyAccessHelper(objectType, propName, t, n); } } } else { checkPropertyAccessHelper(childType, propName, t, n); } } } private void checkPropertyAccessHelper(JSType objectType, String propName, NodeTraversal t, Node n) { if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { report(t, n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirstChild(), true)); } } } /** * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; case Token.NOT: return parent.getParent().isOr() && parent.getParent().getFirstChild() == parent; case Token.CAST: return isPropertyTest(parent); } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { validator.expectIndexMatch( t, n, getJSType(n.getFirstChild()), getJSType(n.getLastChild())); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } checkEnumAlias(t, info, value); if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null) { visitParameterList(t, n, fnType); ensureTyped(t, n, fnType.getInstanceType()); } else { ensureTyped(t, n); } } else { report(t, n, NOT_A_CONSTRUCTOR); ensureTyped(t, n); } } /** * Check whether there's any property conflict for for a particular super * interface * @param t The node traversal object that supplies context * @param n The node being visited * @param functionName The function name being checked * @param properties The property names in the super interfaces that have * been visited * @param currentProperties The property names in the super interface * that have been visited * @param interfaceType The super interface that is being visited */ private void checkInterfaceConflictProperties(NodeTraversal t, Node n, String functionName, HashMap properties, HashMap currentProperties, ObjectType interfaceType) { ObjectType implicitProto = interfaceType.getImplicitPrototype(); Set currentPropertyNames; if (implicitProto == null) { // This can be the case if interfaceType is proxy to a non-existent // object (which is a bad type annotation, but shouldn't crash). currentPropertyNames = ImmutableSet.of(); } else { currentPropertyNames = implicitProto.getOwnPropertyNames(); } for (String name : currentPropertyNames) { ObjectType oType = properties.get(name); if (oType != null) { if (!interfaceType.getPropertyType(name).isEquivalentTo( oType.getPropertyType(name))) { compiler.report( t.makeError(n, INCOMPATIBLE_EXTENDED_PROPERTY_TYPE, functionName, name, oType.toString(), interfaceType.toString())); } } currentProperties.put(name, interfaceType); } for (ObjectType iType : interfaceType.getCtorExtendedInterfaces()) { checkInterfaceConflictProperties(t, n, functionName, properties, currentProperties, iType); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { FunctionType functionType = JSType.toMaybeFunctionType(n.getJSType()); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isConstructor()) { FunctionType baseConstructor = functionType.getSuperClassConstructor(); if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && baseConstructor != null && baseConstructor.isInterface()) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, "constructor", functionPrivateName)); } else { if (baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE)) { ObjectType proto = functionType.getPrototype(); if (functionType.makesStructs() && !proto.isStruct()) { compiler.report(t.makeError(n, CONFLICTING_SHAPE_TYPE, "struct", functionPrivateName)); } else if (functionType.makesDicts() && !proto.isDict()) { compiler.report(t.makeError(n, CONFLICTING_SHAPE_TYPE, "dict", functionPrivateName)); } } // All interfaces are properly implemented by a class for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null) { FunctionType interfaceConstructor = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { report(t, n, BAD_IMPLEMENTED_TYPE, functionPrivateName); } } // check properties validator.expectAllInterfaceProperties(t, n, functionType); } } else if (functionType.isInterface()) { // Interface must extend only interfaces for (ObjectType extInterface : functionType.getExtendedInterfaces()) { if (extInterface.getConstructor() != null && !extInterface.getConstructor().isInterface()) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, "interface", functionPrivateName)); } } // Check whether the extended interfaces have any conflicts if (functionType.getExtendedInterfacesCount() > 1) { // Only check when extending more than one interfaces HashMap properties = new HashMap(); HashMap currentProperties = new HashMap(); for (ObjectType interfaceType : functionType.getExtendedInterfaces()) { currentProperties.clear(); checkInterfaceConflictProperties(t, n, functionPrivateName, properties, currentProperties, interfaceType); properties.putAll(currentProperties); } } } } /** * Visits a CALL node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitCall(NodeTraversal t, Node n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { report(t, n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType.isFunctionType()) { FunctionType functionType = childType.toMaybeFunctionType(); boolean isExtern = false; JSDocInfo functionJSDocInfo = functionType.getJSDocInfo(); if (functionJSDocInfo != null && functionJSDocInfo.getAssociatedNode() != null) { isExtern = functionJSDocInfo.getAssociatedNode().isFromExterns(); } // Non-native constructors should not be called directly // unless they specify a return type and are defined // in an extern. if (functionType.isConstructor() && !functionType.isNativeObjectType() && (functionType.getReturnType().isUnknownType() || functionType.getReturnType().isVoidType() || !isExtern)) { report(t, n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } // Functions with explicit 'this' types must be called in a GETPROP // or GETELEM. if (functionType.isOrdinaryFunction() && !functionType.getTypeOfThis().isUnknownType() && !(functionType.getTypeOfThis().toObjectType() != null && functionType.getTypeOfThis().toObjectType().isNativeObjectType()) && !(child.isGetElem() || child.isGetProp())) { report(t, n, EXPECTED_THIS_TYPE, functionType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType.getReturnType()); } else { ensureTyped(t, n); } // TODO: Add something to check for calls of RegExp objects, which is not // supported by IE. Either say something about the return type or warn // about the non-portability of the call or both. } /** * Visits the parameters of a CALL or a NEW node. */ private void visitParameterList(NodeTraversal t, Node call, FunctionType functionType) { Iterator arguments = call.children().iterator(); arguments.next(); // skip the function name Iterator parameters = functionType.getParameters().iterator(); int ordinal = 0; Node parameter = null; Node argument = null; while (arguments.hasNext() && (parameters.hasNext() || parameter != null && parameter.isVarArgs())) { // If there are no parameters left in the list, then the while loop // above implies that this must be a var_args function. if (parameters.hasNext()) { parameter = parameters.next(); } argument = arguments.next(); ordinal++; validator.expectArgumentMatchesParameter(t, argument, getJSType(argument), getJSType(parameter), call, ordinal); } int numArgs = call.getChildCount() - 1; int minArgs = functionType.getMinArguments(); int maxArgs = functionType.getMaxArguments(); if (minArgs > numArgs || maxArgs < numArgs) { report(t, call, WRONG_ARGUMENT_COUNT, validator.getReadableJSTypeName(call.getFirstChild(), false), String.valueOf(numArgs), String.valueOf(minArgs), maxArgs != Integer.MAX_VALUE ? " and no more than " + maxArgs + " argument(s)" : ""); } } /** * Visits a RETURN node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitReturn(NodeTraversal t, Node n) { JSType jsType = getJSType(t.getEnclosingFunction()); if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { report(t, left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { report(t, right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break; default: report(t, n, UNEXPECTED_TOKEN, Token.name(op)); } ensureTyped(t, n); } /** *

      Checks enum aliases. * *

      We verify that the enum element type of the enum used * for initialization is a subtype of the enum element type of * the enum the value is being copied in.

      * *

      Example:

      *
      var myEnum = myOtherEnum;
      * *

      Enum aliases are irregular, so we need special code for this :(

      * * @param value the value used for initialization of the enum */ private void checkEnumAlias( NodeTraversal t, JSDocInfo declInfo, Node value) { if (declInfo == null || !declInfo.hasEnumParameterType()) { return; } JSType valueType = getJSType(value); if (!valueType.isEnumType()) { return; } EnumType valueEnumType = valueType.toMaybeEnumType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, declInfo.getEnumParameterType().evaluate(t.getScope(), typeRegistry), "incompatible enum element types"); } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, * attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(!n.isFunction() || type.isFunctionType() || type.isUnknownType()); JSDocInfo info = n.getJSDocInfo(); if (info != null) { if (info.hasType()) { // TODO(johnlenz): Change this so that we only look for casts on CAST // nodes one the misplaced type annotation warning is on by default and // people have been given a chance to fix them. As is, this is here // simply for legacy casts. JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry); validator.expectCanCast(t, n, infoType, type); type = infoType; } if (info.isImplicitCast() && !inExterns) { String propName = n.isGetProp() ? n.getLastChild().getString() : "(missing)"; compiler.report( t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName)); } } if (n.getJSType() == null) { n.setJSType(type); } } /** * Returns the percentage of nodes typed by the type checker. * @return a number between 0.0 and 100.0 */ double getTypedPercent() { int total = nullCount + unknownCount + typedCount; return (total == 0) ? 0.0 : (100.0 * typedCount) / total; } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DiagnosticGroupWarningsGuard.java0000644000175000017500000000325212115204405031276 0ustar apoapo/* * 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.javascript.jscomp.CheckLevel; /** * Sets the level for a particular DiagnosticGroup. * @author nicksantos@google.com (Nick Santos) */ public class DiagnosticGroupWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; final DiagnosticGroup group; final CheckLevel level; public DiagnosticGroupWarningsGuard( DiagnosticGroup group, CheckLevel level) { this.group = group; this.level = level; } @Override public CheckLevel level(JSError error) { return group.matches(error) ? level : null; } @Override public boolean disables(DiagnosticGroup otherGroup) { return !level.isOn() && group.isSubGroup(otherGroup); } @Override public boolean enables(DiagnosticGroup otherGroup) { if (level.isOn()) { for (DiagnosticType type : otherGroup.getTypes()) { if (group.matches(type)) { return true; } } } return false; } @Override public String toString() { return group + "(" + level + ")"; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VariableVisibilityAnalysis.java0000644000175000017500000001123012115204405030775 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import java.util.Map; /** * An analysis pass that determines the visibility of variables -- that is, * whether a variable is truly local, a local captured by an inner scope, a * parameter, or a global variable. * * SideEffectsAnalysis uses this class to partition a potentially infinite * number of concrete storage locations into a (small) finite number of * abstract storage locations based on a variable's storage visibility. * * @author dcc@google.com (Devin Coughlin) */ class VariableVisibilityAnalysis implements CompilerPass { enum VariableVisibility { /** Local variable, not captured by closure */ LOCAL, /** Local variable captured by a closure */ CAPTURED_LOCAL, /** * Formal parameter declaration variable * * Parameters are different than local variables because they can be * aliased by elements of the arguments object. */ PARAMETER, /** A global variable */ GLOBAL } private AbstractCompiler compiler; /** * Maps the declaring name node for a variable to that variable's * visibility. */ private Map visibilityByDeclaringNameNode; public VariableVisibilityAnalysis(AbstractCompiler compiler) { this.compiler = compiler; visibilityByDeclaringNameNode = Maps.newHashMap(); } /** * Returns the visibility of of a variable, given that variable's declaring * name node. * * The name node's parent must be one of: *
         *    Token.VAR (for a variable declaration)
         *    Token.FUNCTION (for a function declaration)
         *    Token.PARAM_LIST (for a function formal parameter)
         * 
      * * The returned visibility will be one of: *
         *    LOCAL_VARIABLE : the variable is a local variable used only in its
         *        declared scope
         *    CAPTURED_LOCAL_VARIABLE : A local variable that is used in a capturing
         *        closure
         *     PARAMETER_VARIABLE : the variable is a formal parameter
         *     GLOBAL_VARIABLE : the variable is declared in the global scope
         *  
      * * @param declaringNameNode The name node for a declaration. */ public VariableVisibility getVariableVisibility(Node declaringNameNode) { Node parent = declaringNameNode.getParent(); Preconditions.checkArgument(parent.isVar() || parent.isFunction() || parent.isParamList()); return visibilityByDeclaringNameNode.get(declaringNameNode); } /** * Determines the visibility class for each variable in root. */ @Override public void process(Node externs, Node root) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback(compiler, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); NodeTraversal.traverse(compiler, root, callback); for (Var variable : callback.getAllSymbols()) { ReferenceCollection referenceCollection = callback.getReferences(variable); VariableVisibility visibility; if (variableIsParameter(variable)) { visibility = VariableVisibility.PARAMETER; } else if (variable.isLocal()) { if (referenceCollection.isEscaped()) { visibility = VariableVisibility.CAPTURED_LOCAL; } else { visibility = VariableVisibility.LOCAL; } } else if (variable.isGlobal()) { visibility = VariableVisibility.GLOBAL; } else { throw new IllegalStateException("Un-handled variable visibility for " + variable); } visibilityByDeclaringNameNode.put(variable.getNameNode(), visibility); } } /** * Returns true if the variable is a formal parameter. */ private static boolean variableIsParameter(Var variable) { Node variableParent = variable.getParentNode(); return variableParent != null && variableParent.isParamList(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CrossModuleMethodMotion.java0000644000175000017500000001725712115204405030301 0ustar apoapo/* * 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.javascript.jscomp.AnalyzePrototypeProperties.NameInfo; import com.google.javascript.jscomp.AnalyzePrototypeProperties.Property; import com.google.javascript.jscomp.AnalyzePrototypeProperties.Symbol; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; /** * Move prototype methods into later modules. * * @author nicksantos@google.com (Nick Santos) */ class CrossModuleMethodMotion implements CompilerPass { // Internal errors static final DiagnosticType NULL_COMMON_MODULE_ERROR = DiagnosticType.error( "JSC_INTERNAL_ERROR_MODULE_DEPEND", "null deepest common module"); private final AbstractCompiler compiler; private final IdGenerator idGenerator; private final AnalyzePrototypeProperties analyzer; private final JSModuleGraph moduleGraph; static final String STUB_METHOD_NAME = "JSCompiler_stubMethod"; static final String UNSTUB_METHOD_NAME = "JSCompiler_unstubMethod"; // Visible for testing static final String STUB_DECLARATIONS = "var JSCompiler_stubMap = [];" + "function JSCompiler_stubMethod(JSCompiler_stubMethod_id) {" + " return function() {" + " return JSCompiler_stubMap[JSCompiler_stubMethod_id].apply(" + " this, arguments);" + " };" + "}" + "function JSCompiler_unstubMethod(" + " JSCompiler_unstubMethod_id, JSCompiler_unstubMethod_body) {" + " return JSCompiler_stubMap[JSCompiler_unstubMethod_id] = " + " JSCompiler_unstubMethod_body;" + "}"; /** * Creates a new pass for moving prototype properties. * @param compiler The compiler. * @param idGenerator An id generator for method stubs. * @param canModifyExterns If true, then we can move prototype * properties that are declared in the externs file. */ CrossModuleMethodMotion(AbstractCompiler compiler, IdGenerator idGenerator, boolean canModifyExterns) { this.compiler = compiler; this.idGenerator = idGenerator; this.moduleGraph = compiler.getModuleGraph(); this.analyzer = new AnalyzePrototypeProperties(compiler, moduleGraph, canModifyExterns, false); } @Override public void process(Node externRoot, Node root) { // If there are < 2 modules, then we will never move anything, // so we're done. if (moduleGraph != null && moduleGraph.getModuleCount() > 1) { analyzer.process(externRoot, root); moveMethods(analyzer.getAllNameInfo()); } } /** * Move methods deeper in the module graph when possible. */ private void moveMethods(Collection allNameInfo) { boolean hasStubDeclaration = idGenerator.hasGeneratedAnyIds(); for (NameInfo nameInfo : allNameInfo) { if (!nameInfo.isReferenced()) { // The code below can't do anything with unreferenced name // infos. They should be skipped to avoid NPE since their // deepestCommonModuleRef is null. continue; } if (nameInfo.readsClosureVariables()) { continue; } JSModule deepestCommonModuleRef = nameInfo.getDeepestCommonModuleRef(); if (deepestCommonModuleRef == null) { compiler.report(JSError.make(NULL_COMMON_MODULE_ERROR)); continue; } Iterator declarations = nameInfo.getDeclarations().descendingIterator(); while (declarations.hasNext()) { Symbol symbol = declarations.next(); if (!(symbol instanceof Property)) { continue; } Property prop = (Property) symbol; // We should only move a property across modules if: // 1) We can move it deeper in the module graph, and // 2) it's a function, and // 3) it is not a GETTER_DEF or a SETTER_DEF, and // 4) the class is available in the global scope. // // #1 should be obvious. #2 is more subtle. It's possible // to copy off of a prototype, as in the code: // for (var k in Foo.prototype) { // doSomethingWith(Foo.prototype[k]); // } // This is a common way to implement pseudo-multiple inheritance in JS. // // So if we move a prototype method into a deeper module, we must // replace it with a stub function so that it preserves its original // behavior. if (prop.getRootVar() == null || !prop.getRootVar().isGlobal()) { continue; } Node value = prop.getValue(); if (moduleGraph.dependsOn(deepestCommonModuleRef, prop.getModule()) && value.isFunction()) { Node valueParent = value.getParent(); if (valueParent.isGetterDef() || valueParent.isSetterDef()) { // TODO(johnlenz): a GET or SET can't be deferred like a normal // FUNCTION property definition as a mix-in would get the result // of a GET instead of the function itself. continue; } Node proto = prop.getPrototype(); int stubId = idGenerator.newId(); // example: JSCompiler_stubMethod(id); Node stubCall = IR.call( IR.name(STUB_METHOD_NAME), IR.number(stubId)) .copyInformationFromForTree(value); stubCall.putBooleanProp(Node.FREE_CALL, true); // stub out the method in the original module // A.prototype.b = JSCompiler_stubMethod(id); valueParent.replaceChild(value, stubCall); // unstub the function body in the deeper module Node unstubParent = compiler.getNodeForCodeInsertion( deepestCommonModuleRef); Node unstubCall = IR.call( IR.name(UNSTUB_METHOD_NAME), IR.number(stubId), value); unstubCall.putBooleanProp(Node.FREE_CALL, true); unstubParent.addChildToFront( // A.prototype.b = JSCompiler_unstubMethod(id, body); IR.exprResult( IR.assign( IR.getprop( proto.cloneTree(), IR.string(nameInfo.name)), unstubCall)) .copyInformationFromForTree(value)); compiler.reportCodeChange(); } } } if (!hasStubDeclaration && idGenerator.hasGeneratedAnyIds()) { // Declare stub functions in the top-most module. Node declarations = compiler.parseSyntheticCode(STUB_DECLARATIONS); compiler.getNodeForCodeInsertion(null).addChildrenToFront( declarations.removeChildren()); } } static class IdGenerator implements Serializable { private static final long serialVersionUID = 0L; /** * Ids for cross-module method stubbing, so that each method has * a unique id. */ private int currentId = 0; /** * Returns whether we've generated any new ids. */ boolean hasGeneratedAnyIds() { return currentId != 0; } /** * Creates a new id for stubbing a method. */ int newId() { return currentId++; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReplaceMessages.java0000644000175000017500000003062512115204405026550 0ustar apoapo/* * Copyright 2004 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.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import javax.annotation.Nullable; /** * ReplaceMessages replaces user-visible messages with alternatives. * It uses Google specific JsMessageVisitor implementation. * */ class ReplaceMessages extends JsMessageVisitor { private final MessageBundle bundle; private final boolean strictReplacement; static final DiagnosticType BUNDLE_DOES_NOT_HAVE_THE_MESSAGE = DiagnosticType.error("JSC_BUNDLE_DOES_NOT_HAVE_THE_MESSAGE", "Message with id = {0} could not be found in replacement bundle"); ReplaceMessages(AbstractCompiler compiler, MessageBundle bundle, boolean checkDuplicatedMessages, JsMessage.Style style, boolean strictReplacement) { super(compiler, checkDuplicatedMessages, style, bundle.idGenerator()); this.bundle = bundle; this.strictReplacement = strictReplacement; } @Override void processMessageFallback( Node callNode, JsMessage message1, JsMessage message2) { boolean isFirstMessageTranslated = (bundle.getMessage(message1.getId()) != null); boolean isSecondMessageTranslated = (bundle.getMessage(message2.getId()) != null); Node replacementNode = isSecondMessageTranslated && !isFirstMessageTranslated ? callNode.getChildAtIndex(2) : callNode.getChildAtIndex(1); callNode.getParent().replaceChild(callNode, replacementNode.detachFromParent()); } @Override void processJsMessage(JsMessage message, JsMessageDefinition definition) { // Get the replacement. JsMessage replacement = bundle.getMessage(message.getId()); if (replacement == null) { if (strictReplacement) { compiler.report(JSError.make(message.getSourceName(), definition.getMessageNode(), BUNDLE_DOES_NOT_HAVE_THE_MESSAGE, message.getId())); // Fallback to the default message return; } else { // In case if it is not a strict replacement we could leave original // message. replacement = message; } } // Replace the message. Node newValue; Node msgNode = definition.getMessageNode(); try { newValue = getNewValueNode(replacement, msgNode); } catch (MalformedException e) { compiler.report(JSError.make(message.getSourceName(), e.getNode(), MESSAGE_TREE_MALFORMED, e.getMessage())); newValue = msgNode; } if (newValue != msgNode) { newValue.copyInformationFromForTree(msgNode); definition.getMessageParentNode().replaceChild(msgNode, newValue); compiler.reportCodeChange(); } } /** * Constructs a node representing a message's value, or, if possible, just * modifies {@code origValueNode} so that it accurately represents the * message's value. * * @param message a message * @param origValueNode the message's original value node * @return a Node that can replace {@code origValueNode} * * @throws MalformedException if the passed node's subtree structure is * not as expected */ private Node getNewValueNode(JsMessage message, Node origValueNode) throws MalformedException { switch (origValueNode.getType()) { case Token.FUNCTION: // The message is a function. Modify the function node. updateFunctionNode(message, origValueNode); return origValueNode; case Token.STRING: // The message is a simple string. Modify the string node. String newString = message.toString(); if (!origValueNode.getString().equals(newString)) { origValueNode.setString(newString); compiler.reportCodeChange(); } return origValueNode; case Token.ADD: // The message is a simple string. Create a string node. return IR.string(message.toString()); case Token.CALL: // The message is a function call. Replace it with a string expression. return replaceCallNode(message, origValueNode); default: throw new MalformedException( "Expected FUNCTION, STRING, or ADD node; found: " + origValueNode.getType(), origValueNode); } } /** * Updates the descendants of a FUNCTION node to represent a message's value. *

      * The tree looks something like: *

         * function
         *  |-- name
         *  |-- lp
         *  |   |-- name 
         *  |    -- name 
         *   -- block
         *      |
         *       --return
         *           |
         *            --add
         *               |-- string foo
         *                -- name 
         * 
      * * @param message a message * @param functionNode the message's original FUNCTION value node * * @throws MalformedException if the passed node's subtree structure is * not as expected */ private void updateFunctionNode(JsMessage message, Node functionNode) throws MalformedException { checkNode(functionNode, Token.FUNCTION); Node nameNode = functionNode.getFirstChild(); checkNode(nameNode, Token.NAME); Node argListNode = nameNode.getNext(); checkNode(argListNode, Token.PARAM_LIST); Node oldBlockNode = argListNode.getNext(); checkNode(oldBlockNode, Token.BLOCK); Iterator iterator = message.parts().iterator(); Node valueNode = iterator.hasNext() ? constructAddOrStringNode(iterator, argListNode) : IR.string(""); Node newBlockNode = IR.block(IR.returnNode(valueNode)); // TODO(user): checkTreeEqual is overkill. I am in process of rewriting // these functions. if (newBlockNode.checkTreeEquals(oldBlockNode) != null) { newBlockNode.copyInformationFromForTree(oldBlockNode); functionNode.replaceChild(oldBlockNode, newBlockNode); compiler.reportCodeChange(); } } /** * Creates a parse tree corresponding to the remaining message parts in * an iteration. The result will contain only STRING nodes, NAME nodes * (corresponding to placeholder references), and/or ADD nodes used to * combine the other two types. * * @param partsIterator an iterator over message parts * @param argListNode an LP node whose children are valid placeholder names * @return the root of the constructed parse tree * * @throws MalformedException if {@code partsIterator} contains a * placeholder reference that does not correspond to a valid argument in * the arg list */ private Node constructAddOrStringNode(Iterator partsIterator, Node argListNode) throws MalformedException { CharSequence part = partsIterator.next(); Node partNode = null; if (part instanceof JsMessage.PlaceholderReference) { JsMessage.PlaceholderReference phRef = (JsMessage.PlaceholderReference) part; for (Node node : argListNode.children()) { if (node.isName()) { String arg = node.getString(); // We ignore the case here because the transconsole only supports // uppercase placeholder names, but function arguments in JavaScript // code can have mixed case. if (arg.equalsIgnoreCase(phRef.getName())) { partNode = IR.name(arg); } } } if (partNode == null) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phRef.getName(), argListNode); } } else { // The part is just a string literal. partNode = IR.string(part.toString()); } if (partsIterator.hasNext()) { return IR.add(partNode, constructAddOrStringNode(partsIterator, argListNode)); } else { return partNode; } } /** * Replaces a CALL node with an inlined message value. *

      * The call tree looks something like: *

         * call
         *  |-- getprop
         *  |   |-- name 'goog'
         *  |   +-- string 'getMsg'
         *  |
         *  |-- string 'Hi {$userName}! Welcome to {$product}.'
         *  +-- objlit
         *      |-- string 'userName'
         *      |-- name 'someUserName'
         *      |-- string 'product'
         *      +-- call
         *          +-- name 'getProductName'
         * 
         * 

      * For that example, we'd return: *

         * add
         *  |-- string 'Hi '
         *  +-- add
         *      |-- name someUserName
         *      +-- add
         *          |-- string '! Welcome to '
         *          +-- add
         *              |-- call
         *              |   +-- name 'getProductName'
         *              +-- string '.'
         * 
      * @param message a message * @param callNode the message's original CALL value node * @return a STRING node, or an ADD node that does string concatenation, if * the message has one or more placeholders * * @throws MalformedException if the passed node's subtree structure is * not as expected */ private Node replaceCallNode(JsMessage message, Node callNode) throws MalformedException { checkNode(callNode, Token.CALL); Node getPropNode = callNode.getFirstChild(); checkNode(getPropNode, Token.GETPROP); Node stringExprNode = getPropNode.getNext(); checkStringExprNode(stringExprNode); Node objLitNode = stringExprNode.getNext(); // Build the replacement tree. return constructStringExprNode(message.parts().iterator(), objLitNode); } /** * Creates a parse tree corresponding to the remaining message parts in an * iteration. The result consists of one or more STRING nodes, placeholder * replacement value nodes (which can be arbitrary expressions), and ADD * nodes. * * @param parts an iterator over message parts * @param objLitNode an OBJLIT node mapping placeholder names to values * @return the root of the constructed parse tree * * @throws MalformedException if {@code parts} contains a placeholder * reference that does not correspond to a valid placeholder name */ private Node constructStringExprNode(Iterator parts, Node objLitNode) throws MalformedException { CharSequence part = parts.next(); Node partNode = null; if (part instanceof JsMessage.PlaceholderReference) { JsMessage.PlaceholderReference phRef = (JsMessage.PlaceholderReference) part; // The translated message is null if (objLitNode == null) { throw new MalformedException("Empty placeholder value map " + "for a translated message with placeholders.", objLitNode); } for (Node key = objLitNode.getFirstChild(); key != null; key = key.getNext()) { if (key.getString().equals(phRef.getName())) { Node valueNode = key.getFirstChild(); partNode = valueNode.cloneTree(); } } if (partNode == null) { throw new MalformedException( "Unrecognized message placeholder referenced: " + phRef.getName(), objLitNode); } } else { // The part is just a string literal. partNode = IR.string(part.toString()); } if (parts.hasNext()) { return IR.add(partNode, constructStringExprNode(parts, objLitNode)); } else { return partNode; } } /** * Checks that a node is a valid string expression (either a string literal * or a concatenation of string literals). * * @throws IllegalArgumentException if the node is null or the wrong type */ private void checkStringExprNode(@Nullable Node node) { if (node == null) { throw new IllegalArgumentException("Expected a string; found: null"); } switch (node.getType()) { case Token.STRING: break; case Token.ADD: Node c = node.getFirstChild(); checkStringExprNode(c); checkStringExprNode(c.getNext()); break; default: throw new IllegalArgumentException( "Expected a string; found: " + node.getType()); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AbstractMessageFormatter.java0000644000175000017500000000463512115204405030443 0ustar apoapo/* * Copyright 2007 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.javascript.jscomp.CheckLevel; import java.util.Set; /** * Abstract message formatter providing default behavior for implementations * of {@link MessageFormatter} needing a {@link SourceExcerptProvider}. * */ public abstract class AbstractMessageFormatter implements MessageFormatter { private final SourceExcerptProvider source; private boolean colorize; public AbstractMessageFormatter(SourceExcerptProvider source) { this.source = source; } public void setColorize(boolean colorize) { this.colorize = colorize; } /** * Get the source excerpt provider. */ protected final SourceExcerptProvider getSource() { return source; } private static final Set SUPPORTED_COLOR_TERMINALS = ImmutableSet.of("xterm", "xterm-color", "xterm-256color", "screen-bce"); static boolean termSupportsColor(String term) { return SUPPORTED_COLOR_TERMINALS.contains(term); } private static enum Color { ERROR("\033[31m"), WARNING("\033[35m"), RESET("\033[39m"); private final String controlCharacter; Color(String controlCharacter) { this.controlCharacter = controlCharacter; } public String getControlCharacter() { return controlCharacter; } } String getLevelName(CheckLevel level) { switch (level) { case ERROR: return maybeColorize("ERROR", Color.ERROR); case WARNING: return maybeColorize("WARNING", Color.WARNING); default: return level.toString(); } } private String maybeColorize(String text, Color color) { if (!colorize) return text; return color.getControlCharacter() + text + Color.RESET.getControlCharacter(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JsMessageExtractor.java0000644000175000017500000001062612115204405027261 0ustar apoapo/* * Copyright 2004 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 java.io.IOException; import java.util.Collection; import java.util.List; /** * Extracts messages and message comments from JS code. * *

      Uses a special prefix (e.g. {@code MSG_}) to determine which variables * are messages. Here are the recognized formats: * * * var MSG_FOO = "foo"; * var MSG_FOO_HELP = "this message is used for foo"; * * * * var MSG_BAR = function(a, b) { * return a + " bar " + b; * } * var MSG_BAR_HELP = "the bar message"; * * *

      This class enforces the policy that message variable names must be unique * across all JS files. * */ public class JsMessageExtractor { private final JsMessage.Style style; private final JsMessage.IdGenerator idGenerator; private final CompilerOptions options; public JsMessageExtractor( JsMessage.IdGenerator idGenerator, JsMessage.Style style) { this(idGenerator, style, new CompilerOptions()); } public JsMessageExtractor( JsMessage.IdGenerator idGenerator, JsMessage.Style style, CompilerOptions options) { this.idGenerator = idGenerator; this.style = style; this.options = options; } /** * Visitor that collects messages. */ private class ExtractMessagesVisitor extends JsMessageVisitor { // We use List here as we want to preserve insertion-order for found // messages. // Take into account that messages with the same id could be present in the // result list. Message could have the same id only in case if they are // unnamed and have the same text but located in different source files. private final List messages = Lists.newLinkedList(); private ExtractMessagesVisitor(AbstractCompiler compiler) { super(compiler, true, style, idGenerator); } @Override void processJsMessage(JsMessage message, JsMessageDefinition definition) { if (!message.isExternal()) { messages.add(message); } } /** * Returns extracted messages. * * @return collection of JsMessage objects that was found in js sources. */ public Collection getMessages() { return messages; } } /** * Extracts JS messages from JavaScript code. */ public Collection extractMessages(SourceFile... inputs) throws IOException { return extractMessages(ImmutableList.copyOf(inputs)); } /** * Extracts JS messages from JavaScript code. * * @param inputs the JavaScript source code inputs * @return the extracted messages collection * @throws IOException if there is a problem reading the JS code * @throws RuntimeException if there are problems parsing the JS code or the * JS messages, or if two messages have the same key */ public Collection extractMessages( Iterable inputs) throws IOException { Compiler compiler = new Compiler(); compiler.init( ImmutableList.of(), Lists.newArrayList(inputs), options); compiler.parseInputs(); ExtractMessagesVisitor extractCompilerPass = new ExtractMessagesVisitor(compiler); if (compiler.getErrors().length == 0) { extractCompilerPass.process(null, compiler.getRoot()); } JSError[] errors = compiler.getErrors(); // Check for errors. if (errors.length > 0) { StringBuilder msg = new StringBuilder("JSCompiler errors\n"); MessageFormatter formatter = new LightweightMessageFormatter(compiler); for (JSError e : errors) { msg.append(formatter.formatError(e)); } throw new RuntimeException(msg.toString()); } return extractCompilerPass.getMessages(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Tracer.java0000644000175000017500000010305212115204405024720 0ustar apoapo/* * Copyright 2002 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.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; /** * Tracer provides a simple way to trace the handling of a request. * * By timing likely slow points in the code you can quickly pinpoint * why a request is slow. * *

      Example usage: *

       * Tracer.initCurrentThreadTrace(); // must be called in each Thread
       * Tracer wholeRequest = new Tracer(null, "Request " + params);
       * try {
       *   ...
       *   t = new Tracer("Database", "getName()");
       *   try {
       *     name = database.getName();
       *   } finally {
       *     t.stop();
       *   }
       *   ...
       *   t = new Tracer(null, "call sendmail");
       *   try {
       *     sendMessage();
       *   } finally {
       *     t.stop();
       *   }
       *   ...
       *   t = new Tracer("Database", "updateinfo()");
       *   try {
       *     database.updateinfo("new info");
       *   } finally {
       *     t.stop();
       *   }
       *   ...
       * } finally {
       *   if (wholeRequest.stop() > 1000) {
       *     // more than a second, better log
       *     Tracer.logAndClearCurrentThreadTrace();
       *   } else {
       *     Tracer.clearCurrentThreadTrace();
       *   }
       * }
       * 
      * * Now slow requests will produce a report like this: *
       *       10.452 Start        Request cmd=dostuff
       *     3 10.455 Start        [Database] getName()
       *    34 10.489 Done   34 ms [Database] getName()
       *     3 10.491 Start        call sendmail
       *  1042 11.533 Done 1042 ms call sendmail
       *     0 11.533 Start        [Database] updateinfo()
       *     3 11.536 Done    3 ms [Database] updateinfo()
       *    64 11.600 Done 1148 ms Request cmd=dostuff
       *   TOTAL Database 2 (37 ms)
       * 
      * * If you enabled pretty-printing by calling {@link Tracer#setPrettyPrint}, * it will print more easily readable reports that use indentation to visualize * the tracer hierarchy and dynamically adjusts the padding to handle large * durations. Like: *
       *       10.452 Start        Request cmd=dostuff
       *     3 10.455 Start        | [Database] getName()
       *    34 10.489 Done   34 ms | [Database] getName()
       *     3 10.491 Start        | call sendmail
       *  1042 11.533 Done 1042 ms | call sendmail
       *     0 11.533 Start        | [Database] updateinfo()
       *     3 11.536 Done    3 ms | [Database] updateinfo()
       *    64 11.600 Done 1148 ms Request cmd=dostuff
       *   TOTAL Database 2 (37 ms)
       * 
      * Pretty-printing is an application global setting and should only be called * in the main setup of an application, not in library code. * * Now you can easily see that sendmail is causing your problems, not * the two database calls. * * You can easily add additional tracing statistics to your Trace output by * adding additional tracing statistics. Simply add to your initialization code: *
       *    Tracer.addTracingStatistic(myTracingStatistic)
       * 
      * where myTracingStatistic implements the {@link TracingStatistic} interface. * The class com.google.monitoring.tracing.TracingStatistics contains * several useful statistics such as CPU time, wait time, and memory usage. * If you add your own tracing statistics, the output is not quite as pretty, * but includes additional useful information. *

      If a Trace is given a type (the first argument to the constructor) and * multiple Traces are done on that type then a "TOTAL line will be * produced showing the total number of traces and the sum of the time * ("TOTAL Database 2 (37 ms)" in our example). These traces should be * mutually exclusive or else the sum won't make sense (the time will * be double counted if the second starts before the first ends). * *

      It is also possible to have a "silent" Tracer which does not appear * in the trace because it was faster than the silence threshold. This * threshold can be set for the for the current ThreadTrace with * setDefaultSilenceThreshold(threshold), or on a per-Tracer basis with * t.stop(threshold). Silent tracers are still counted in the type * totals, so these events are not completely lost. * *

      WARNING: This code makes a big assumption that * everything for a given trace is done within a single thread. * It uses threads to identify requests. It is fine to have multiple * requests traced in multiple simultaneous threads but it is not ok * to have any given request traced in multiple threads. (the results * will be scattered across reports). * * Java objects do not support destructors (as in C++) so Tracer is not robust * when exceptions are thrown. Each Tracer object should be wrapped in a * try/finally block so that if an exception is thrown, the Tracer.stop() * method is guaranteed to be called. * *

      A thread must call {@link Tracer#initCurrentThreadTrace()} to enable the * Tracer logging, otherwise Tracer does nothing. The requirement to call * {@code initCurrentThreadTrace} avoids the situation where Tracer is called * without the explicit knowledge of the application authors because they * happen to use a class in another package that uses Tracer. If {@link * Tracer#logCurrentThreadTrace} is called without calling {@link * Tracer#initCurrentThreadTrace()}, then a Third Eye WARNING message is logged, * which should help track down the problem. * */ final class Tracer { // package-private for access from unit tests static final Logger logger = Logger.getLogger(Tracer.class.getName()); /** * Whether pretty printing is enabled. This is intended to be set once * at application startup. */ private static volatile boolean defaultPrettyPrint; /* This list is guaranteed to only increase in length. It contains * a list of additional statistics that the user wants to keep track * of. */ private static List extraTracingStatistics = new CopyOnWriteArrayList(); /** Values returned by extraTracingStatistics */ private long[] extraTracingValues; /** The type for grouping traces, may be null */ private final @Nullable String type; /** A comment string for the report */ private final String comment; /** Start time of the trace */ private final long startTimeMs; /** Stop time of the trace, non-final */ private long stopTimeMs; /** * Record our starter thread in order to trap Traces that are started in one * thread and stopped in another */ final Thread startThread; /** * We limit the number of events in a Trace in order to catch memory * leaks (a thread that keeps logging events and never clears them). * This number is arbitrary and can be increased if necessary (though * if there are more than 1000 events then the Tracer is probably being * misused). */ static final int MAX_TRACE_SIZE = 1000; /** * For unit testing. Can't use {@link com.google.common.time.Clock} because * this code is in base and has minimal dependencies. */ static interface InternalClock { long currentTimeMillis(); } /** * Default clock that calls through to the system clock. Can be overridden * in unit tests. */ static InternalClock clock = new InternalClock() { @Override public long currentTimeMillis() { return System.currentTimeMillis(); } }; /** * Create and start a tracer. * Both type and comment may be null. See class comment for usage. * * @param type The type for totaling * @param comment Comment about this tracer */ Tracer(@Nullable String type, @Nullable String comment) { this.type = type; this.comment = comment == null ? "" : comment; startTimeMs = clock.currentTimeMillis(); startThread = Thread.currentThread(); if (!extraTracingStatistics.isEmpty()) { int size = extraTracingStatistics.size(); extraTracingValues = new long[size]; int i = 0; for (TracingStatistic tracingStatistic : extraTracingStatistics) { extraTracingValues[i] = tracingStatistic.start(startThread); i++; } } ThreadTrace trace = getThreadTrace(); // Do nothing if the current thread trace wasn't initialized. if (!trace.isInitialized()) { return; } // Check if we are creating too many Tracers. if (trace.events.size() >= MAX_TRACE_SIZE) { logger.log(Level.WARNING, "Giant thread trace. Too many Tracers created. " + "Clearing to avoid memory leak.", new Throwable(trace.toString())); trace.truncateEvents(); } // Check if we forgot to close the Tracers. if (trace.outstandingEvents.size() >= MAX_TRACE_SIZE) { logger.log(Level.WARNING, "Too many outstanding Tracers. Tracer.stop() is missing " + "or Tracer.stop() is not wrapped in a " + "try/finally block. " + "Clearing to avoid memory leak.", new Throwable(trace.toString())); trace.truncateOutstandingEvents(); } trace.startEvent(this); } /** * Create a tracer that isn't summed as part of a total * * @param comment Comment about this tracer */ Tracer(String comment) { this(null, comment); } /** * Construct a tracer whose type is based on the short name of the object * @param object Object to use as type name * @param comment A comment * @return new Tracer. */ static Tracer shortName(Object object, String comment) { if (object == null) { return new Tracer(comment); } return new Tracer(object.getClass().getSimpleName(), comment); } /** * Converts 'v' to a string and pads it with up to 16 spaces for * improved alignment. * @param v The value to convert. * @param digits_column_width The desired with of the string. */ private static String longToPaddedString(long v, int digits_column_width) { int digit_width = numDigits(v); StringBuilder sb = new StringBuilder(); appendSpaces(sb, digits_column_width - digit_width); sb.append(v); return sb.toString(); } /** * Gets the number of digits in an integer when printed in base 10. Assumes * a positive integer. * @param n The value. * @return The number of digits in the string. */ private static int numDigits(long n) { int i = 0; do { i++; n = n / 10; } while (n > 0); return i; } /** * Gets a string of spaces of the length specified. * @param sb The string builder to append to. * @param numSpaces The number of spaces in the string. */ @VisibleForTesting static void appendSpaces(StringBuilder sb, int numSpaces) { if (numSpaces > 16) { logger.warning("Tracer.appendSpaces called with large numSpaces"); // Avoid long loop in case some bug in the caller numSpaces = 16; } while (numSpaces >= 5) { sb.append(" "); numSpaces -= 5; } // We know it's less than 5 now switch (numSpaces) { case 1: sb.append(" "); break; case 2: sb.append(" "); break; case 3: sb.append(" "); break; case 4: sb.append(" "); break; } } /** * Adds a new tracing statistic to a trace * * @param tracingStatistic to enable a run * @return The index of this statistic (for use with stat.extraInfo()), or * -1 if the statistic is not enabled. */ static int addTracingStatistic(TracingStatistic tracingStatistic) { // Check to see if we can enable the tracing statistic before actually // adding it. if (tracingStatistic.enable()) { // No synchronization needed, since this is a copy-on-write array. extraTracingStatistics.add(tracingStatistic); // 99.9% of the time, this will be O(1) and return // extraTracingStatistics.length - 1 return extraTracingStatistics.lastIndexOf(tracingStatistic); } else { return -1; } } /** * For testing purposes only. These removes all current tracers. * Severe errors can occur if there are any active tracers going on * when this is called. * * The test suite uses this to remove any tracers that it has added. */ @VisibleForTesting static void clearTracingStatisticsTestingOnly() { extraTracingStatistics.clear(); } /** * Stop the trace. * This may only be done once and must be done from the same thread * that started it. * @param silence_threshold Traces for time less than silence_threshold * ms will be left out of the trace report. A value of -1 indicates * that the current ThreadTrace silence_threshold should be used. * @return The time that this trace actually ran */ long stop(int silence_threshold) { Preconditions.checkState(Thread.currentThread() == startThread); ThreadTrace trace = getThreadTrace(); // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } stopTimeMs = clock.currentTimeMillis(); if (extraTracingValues != null) { // We use extraTracingValues.length rather than // extraTracingStatistics.size() because a new statistic may // have been added for (int i = 0; i < extraTracingValues.length; i++) { long value = extraTracingStatistics.get(i).stop(startThread); extraTracingValues[i] = value - extraTracingValues[i]; } } // Do nothing if the thread trace was not initialized. if (!trace.isInitialized()) { return 0; } trace.endEvent(this, silence_threshold); return stopTimeMs - startTimeMs; } /** Stop the trace using the default silence_threshold * * @return The time that this trace actually ran. */ long stop() { return stop(-1); } @Override public String toString() { if (type == null) { return comment; } else { return "[" + type + "] " + comment; } } static void setDefaultSilenceThreshold(int threshold) { getThreadTrace().defaultSilenceThreshold = threshold; } /** * Initialize the trace associated with the current thread by clearing * out any existing trace. There shouldn't be a trace so if one is * found we log it as an error. */ static void initCurrentThreadTrace() { ThreadTrace events = getThreadTrace(); if (!events.isEmpty()) { logger.log(Level.WARNING, "Non-empty timer log:\n" + events, new Throwable()); clearThreadTrace(); // Grab a new thread trace if we find a previous non-empty ThreadTrace. events = getThreadTrace(); } // Mark the thread trace as initialized. events.init(); } static void initCurrentThreadTrace(int default_silence_threshold) { initCurrentThreadTrace(); setDefaultSilenceThreshold(default_silence_threshold); } /** * Returns a timer report similar to the one described in the class comment. * * @return The timer report as a string */ static String getCurrentThreadTraceReport() { return getThreadTrace().toString(); } /** * Logs a timer report similar to the one described in the class comment. */ static void logCurrentThreadTrace() { ThreadTrace trace = getThreadTrace(); // New threads must call Tracer.initCurrentThreadTrace() before Tracer // statistics are gathered. This is a recent change (Jun 2007) that // prevents spurious Third Eye messages when an application uses a class in // a different package that happens to call Tracer without knowledge of the // application authors. if (!trace.isInitialized()) { logger.log(Level.WARNING, "Tracer log requested for this thread but was not " + "initialized using Tracer.initCurrentThreadTrace().", new Throwable()); return; } if (!trace.isEmpty()) { logger.log(Level.WARNING, "timers:\n{0}", getCurrentThreadTraceReport()); } } /** * Throw away any Trace associated with the current thread. */ static void clearCurrentThreadTrace() { clearThreadTrace(); } /** * logCurrentThreadTrace() then clearCurrentThreadTrace() */ static void logAndClearCurrentThreadTrace() { logCurrentThreadTrace(); clearThreadTrace(); } /** * Sets whether pretty printing is enabled. See class-level comment. This * only affects tracers created after this is called. * @param enabled Whether to enable pretty printing. */ static void setPrettyPrint(boolean enabled) { defaultPrettyPrint = enabled; } /** Statistics for a given tracer type */ static final class Stat { private int count; private int silent; private int clockTime; private int[] extraInfo; /** total count of tracers of a type, including silent * * @return total count of tracers, including silent tracers */ int getCount() { return count; } /** total count of silent tracers of a type * * @return total count of silent tracers */ int getSilentCount() { return silent; } /** total time spent in tracers of a type, in ms * * @return total time spent in tracer, in ms */ int getTotalTime() { return clockTime; } /** total time spent doing additional things that we are clocking */ @VisibleForTesting int getExtraInfo(int index) { return index >= extraInfo.length ? 0 : extraInfo[index]; } } /** * This map tracks counts of tracers for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToCountMap; /** * This map tracks counts of silent tracers for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToSilentMap; /** * This map tracks time (ms) for each type over all time. */ private static @Nullable AtomicTracerStatMap typeToTimeMap; /** * This method MUST be called before getTypeToCountMap (and friends) * will return a valid map. This is because computing this information * imposes a synchronization penalty on every Tracer that is stopped. */ static synchronized void enableTypeMaps() { if (typeToCountMap == null) { typeToCountMap = new AtomicTracerStatMap(); typeToSilentMap = new AtomicTracerStatMap(); typeToTimeMap = new AtomicTracerStatMap(); } } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called, this will return null. */ static @Nullable Map getTypeToCountMap() { return typeToCountMap != null ? typeToCountMap.getMap() : null; } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called, this will return null. */ static @Nullable Map getTypeToSilentMap() { return typeToSilentMap != null ? typeToSilentMap.getMap() : null; } /** * Used for exporting this data via varz. Accesses to this * map must be synchronized on the map. If enableTypeMaps has not * been called, this will return null. */ static @Nullable Map getTypeToTimeMap() { return typeToTimeMap != null ? typeToTimeMap.getMap() : null; } /** Gets the Stat for a tracer type; never returns null */ static Stat getStatsForType(String type) { Stat stat = getThreadTrace().stats.get(type); return stat != null ? stat : ZERO_STAT; } private static final Stat ZERO_STAT = new Stat(); /** Return the sec.ms part of time (if time = "20:06:11.566", "11.566") */ private static String formatTime(long time) { int sec = (int) ((time / 1000) % 60); int ms = (int) (time % 1000); return String.format("%02d.%03d", sec, ms); } /** An event is created every time a Tracer is created or stopped */ private static final class Event { boolean isStart; // else is_stop Tracer tracer; Event(boolean start, Tracer t) { isStart = start; tracer = t; } long eventTime() { return isStart ? tracer.startTimeMs : tracer.stopTimeMs; } /** * Converts the event to a formatted string. * @param prevEventTime The time of the previous event which appears at * the left most part of the trace line. * @param indent The indentation to put before the tracer to show the * hierarchy. * @param digitsColWidth How many characters the digits should use. * @return The formatted string. */ String toString(long prevEventTime, String indent, int digitsColWidth) { StringBuilder sb = new StringBuilder(120); if (prevEventTime == -1) { appendSpaces(sb, digitsColWidth); } else { sb.append(longToPaddedString( eventTime() - prevEventTime, digitsColWidth)); } sb.append(' '); sb.append(formatTime(eventTime())); if (isStart) { sb.append(" Start "); appendSpaces(sb, digitsColWidth); sb.append(" "); } else { sb.append(" Done "); long delta = tracer.stopTimeMs - tracer.startTimeMs; sb.append(longToPaddedString(delta, digitsColWidth)); sb.append(" ms "); if (tracer.extraTracingValues != null) { for (int i = 0; i < tracer.extraTracingValues.length; i++) { delta = tracer.extraTracingValues[i]; sb.append(String.format("%4d", delta)); sb.append(extraTracingStatistics.get(i).getUnits()); sb.append("; "); } } } sb.append(indent); sb.append(tracer.toString()); return sb.toString(); } } /** Stores a thread's Trace */ static final class ThreadTrace { /** Events taking less than this number of milliseconds are not reported. */ int defaultSilenceThreshold; // non-final /** The Events corresponding to each startEvent/stopEvent */ final ArrayList events = new ArrayList(); /** Tracers that have not had their .stop() called */ final HashSet outstandingEvents = new HashSet(); /** Map from type to Stat object */ final Map stats = new HashMap(); /** * True if {@code outstandingEvents} has been cleared because we exceeded * the max trace limit. */ boolean isOutstandingEventsTruncated = false; /** * True if {@code events} has been cleared because we exceeded the max * trace limit. */ boolean isEventsTruncated = false; /** * Set to true if {@link Tracer#initCurrentThreadTrace()} was called by * the current thread. */ boolean isInitialized = false; /** * Whether pretty printing is enabled for the trace. */ boolean prettyPrint = false; /** Initialize the trace. */ void init() { isInitialized = true; } /** Is initialized? */ boolean isInitialized() { return isInitialized; } /** * Called by the constructor {@link Tracer#Tracer(String, String)} to create * a start event. */ void startEvent(Tracer t) { events.add(new Event(true, t)); boolean notAlreadyOutstanding = outstandingEvents.add(t); Preconditions.checkState(notAlreadyOutstanding); } /** * Called by {@link Tracer#stop()} to create a stop event. */ void endEvent(Tracer t, int silenceThreshold) { boolean wasOutstanding = outstandingEvents.remove(t); if (!wasOutstanding) { if (isOutstandingEventsTruncated) { // The events stack overflowed and was truncated, so just log a // warning. Otherwise, we get an exception which is extremely // confusing. logger.log(Level.WARNING, "event not found, probably because the event stack " + "overflowed and was truncated", new Throwable()); } else { // throw an exception if the event was not found and the events stack // is pristine throw new IllegalStateException(); } } long elapsed = t.stopTimeMs - t.startTimeMs; if (silenceThreshold == -1) { // use default silenceThreshold = defaultSilenceThreshold; } if (elapsed < silenceThreshold) { // If this one is silent then we need to remove the start Event boolean removed = false; for (int i = 0; i < events.size(); i++) { Event e = events.get(i); if (e.tracer == t) { Preconditions.checkState(e.isStart); events.remove(i); removed = true; break; } } // Only assert if we didn't find the original and the events // weren't truncated. Preconditions.checkState(removed || isEventsTruncated); } else { events.add(new Event(false, t)); } if (t.type != null) { Stat stat = stats.get(t.type); if (stat == null) { stat = new Stat(); if (!extraTracingStatistics.isEmpty()) { stat.extraInfo = new int[extraTracingStatistics.size()]; } stats.put(t.type, stat); } stat.count++; if (typeToCountMap != null) { typeToCountMap.incrementBy(t.type, 1); } stat.clockTime += elapsed; if (typeToTimeMap != null) { typeToTimeMap.incrementBy(t.type, elapsed); } if (stat.extraInfo != null && t.extraTracingValues != null) { int overlapLength = Math.min(stat.extraInfo.length, t.extraTracingValues.length); for (int i = 0; i < overlapLength; i++) { stat.extraInfo[i] += t.extraTracingValues[i]; AtomicTracerStatMap map = extraTracingStatistics.get(i).getTracingStat(); if (map != null) { map.incrementBy(t.type, t.extraTracingValues[i]); } } } if (elapsed < silenceThreshold) { stat.silent++; if (typeToSilentMap != null) { typeToSilentMap.incrementBy(t.type, 1); } } } } boolean isEmpty() { return events.size() == 0 && outstandingEvents.size() == 0; } void truncateOutstandingEvents() { isOutstandingEventsTruncated = true; outstandingEvents.clear(); } void truncateEvents() { isEventsTruncated = true; events.clear(); } /** Produces the lovely Trace seen in the class comments */ // Nullness checker does not understand that prettyPrint => indent != null @SuppressWarnings("nullness") @Override public String toString() { int numDigits = getMaxDigits(); StringBuilder sb = new StringBuilder(); long etime = -1; LinkedList indent = prettyPrint ? new LinkedList() : null; for (Event e : events) { if (prettyPrint && !e.isStart && !indent.isEmpty()) { indent.pop(); } sb.append(" "); if (prettyPrint) { sb.append(e.toString(etime, Joiner.on("").join(indent), numDigits)); } else { sb.append(e.toString(etime, "", 4)); } etime = e.eventTime(); sb.append('\n'); if (prettyPrint && e.isStart) { indent.push("| "); } } if (outstandingEvents.size() != 0) { long now = clock.currentTimeMillis(); sb.append(" Unstopped timers:\n"); for (Tracer t : outstandingEvents) { sb.append(" "). append(t). append(" ("). append(now - t.startTimeMs). append(" ms, started at "). append(formatTime(t.startTimeMs)). append(")\n"); } } for (String key : stats.keySet()) { Stat stat = stats.get(key); if (stat.count > 1) { sb.append(" TOTAL "). append(key). append(" "). append(stat.count). append(" ("). append(stat.clockTime). append(" ms"); if (stat.extraInfo != null) { for (int i = 0; i < stat.extraInfo.length; i++) { sb.append("; "); sb.append(stat.extraInfo[i]). append(' '). append(extraTracingStatistics.get(i).getUnits()); } } sb.append(")\n"); } } return sb.toString(); } /** * Gets the maximum number of digits that can appear in the tracer output * in the gaps between tracers or the duration of a tracer. This is used * by the pretty printing case so that all of the tracers are aligned. */ private int getMaxDigits() { long etime = -1; long max_time = 0; for (Event e : events) { if (etime != -1) { long time = e.eventTime() - etime; max_time = Math.max(max_time, time); } if (!e.isStart) { long time = e.tracer.stopTimeMs - e.tracer.startTimeMs; max_time = Math.max(max_time, time); } etime = e.eventTime(); } // Minimum is 3 to preserve an indent even when max is small. return Math.max(3, numDigits(max_time)); } } /** Holds the ThreadTrace for each thread. */ private static ThreadLocal traces = new ThreadLocal(); /** * Get the ThreadTrace for the current thread, creating one if necessary. */ static ThreadTrace getThreadTrace() { ThreadTrace t = traces.get(); if (t == null) { t = new ThreadTrace(); t.prettyPrint = defaultPrettyPrint; traces.set(t); } return t; } /** Remove any ThreadTrace associated with the current thread */ static void clearThreadTrace() { traces.remove(); } /** * A TracingStatistic allows the program to add additional optional * statistics to the trace output. * * The class com.google.monitoring.tracing.TracingStatistics * contains several useful tracing statistics * */ static interface TracingStatistic { /** * This method is called at the start of the trace. It should * return a numeric result indicating the amount of the specific * resource in use before the call started * @param thread The current thread * @return A numeric value indicating the amount of the resource * already used. */ long start(Thread thread); /** * This method is called at the end of the trace. It should * return a numeric result indicating the amount of the specific * resource in use after the call ends. The actual reported result * will be the result end() - start() * @param thread The current thread * @return A numeric value indicating the amount of the resource * currently used. */ long stop(Thread thread); /** * Called when this tracing statistic is first enabled. A return * value of True indicates that this statistic can successfully * run in the current JVM. * * @return An indication of whether this statistic can be * implemented in the current JVM. */ boolean enable(); /** Returns this tracing statistic's trace map. * * @return This tracing statistic's trace map. */ AtomicTracerStatMap getTracingStat(); /** A string that should be appended to the numeric output * indicating what this is. * * @return A string indicating the units of this statistic and what it is. */ String getUnits(); } /** * This class encapsulates a map for keeping track of tracing statistics. * It allows the caller to atomically increment named fields. * */ static final class AtomicTracerStatMap { private ConcurrentMap map = new ConcurrentHashMap(); /** * Atomically increment the specified field by the specified amount. * * @param key the name of the field * @param delta the amount by which to increment the field */ // Nullness checker is not powerful enough to prove null-safety of // this method @SuppressWarnings("nullness") void incrementBy(String key, long delta) { // We use a compareAndSet strategy to update the map, which is much // faster when there isn't too much contention. Look at a value, and // conditionally update the map if the value hasn't changed. // If it has changed, repeat. Long oldValue = map.get(key); if (oldValue == null) { // Currently, the slot is empty oldValue = map.putIfAbsent(key, delta); if (oldValue == null) { // The slot was still empty when we set it return; } else { // Someone filled in the slot behind our back. oldValue has // its current value } } while (true) { if (map.replace(key, oldValue, oldValue + delta)) { break; } // Nullness checker doesn't understand that this cannot return null. oldValue = map.get(key); } } /** * Returns a map of key:value pairs. */ Map getMap() { return map; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GoogleCodingConvention.java0000644000175000017500000001041112115204405030077 0ustar apoapo/* * Copyright 2007 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.rhino.Node; import java.util.regex.Pattern; /** * This describes the Google-specific JavaScript coding conventions. * Within Google, variable names are semantically significant. * */ public class GoogleCodingConvention extends CodingConventions.Proxy { private static final long serialVersionUID = 1L; private static final String OPTIONAL_ARG_PREFIX = "opt_"; private static final String VAR_ARGS_NAME = "var_args"; private static final Pattern ENUM_KEY_PATTERN = Pattern.compile("[A-Z0-9][A-Z0-9_]*"); /** By default, decorate the ClosureCodingConvention. */ public GoogleCodingConvention() { this(new ClosureCodingConvention()); } /** Decorates a wrapped CodingConvention. */ public GoogleCodingConvention(CodingConvention convention) { super(convention); } /** * {@inheritDoc} * *

      This enforces the Google const name convention, that the first character * after the last $ must be an upper-case letter and all subsequent letters * must be upper case. The name must be at least 2 characters long. * *

      Examples: *

         *      aaa          Not constant - lower-case letters in the name
         *      A            Not constant - too short
         *      goog$A       Constant - letters after the $ are upper-case.
         *      AA17         Constant - digits can appear after the first letter
         *      goog$7A      Not constant - first character after the $ must be
         *                   upper case.
         *      $A           Constant - doesn't have to be anything in front of the $
         * 
      */ @Override public boolean isConstant(String name) { if (name.length() <= 1) { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * *

      This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * *

      Examples: *

        *
      • A
      • *
      • 213
      • *
      • FOO_BAR
      • *
      */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * *

      In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * *

      In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) { return super.isExported(name, local) || (!local && name.startsWith("_")); } /** * {@inheritDoc} * *

      In Google code, private names end with an underscore, and exported * names are never considered private (see {@link #isExported}). */ @Override public boolean isPrivate(String name) { return name.endsWith("_") && !isExported(name); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/IgnoreCajaProperties.java0000644000175000017500000000713012115204405027557 0ustar apoapo/* * 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.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; /** * Caja is a system that rewrites web content (JavaScript, CSS, HTML) * into web content that is safe to inline directly into a page. * The rewritten ("cajoled") code runs in the presence of a JS library * that adds some properties to Object.prototype. Because JS does not * yet (until ES5) allow programmers to mark properties as DontEnum, * for..in loops will see unexpected properties. * * This pass adds a conditional to for..in loops that filters out these * properties. * */ class IgnoreCajaProperties implements CompilerPass { final AbstractCompiler compiler; // Counts the number of temporary variables introduced. int counter; public IgnoreCajaProperties(AbstractCompiler compiler) { this.compiler = compiler; this.counter = 0; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, new Traversal()); } private class Traversal extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { // Look for a for..in loop. if (n.isFor() && n.getChildCount() == 3) { Node body = n.getLastChild(); n.removeChild(body); Node key = n.getFirstChild(); n.removeChild(key); Node tmp = IR.name( "JSCompiler_IgnoreCajaProperties_" + counter++); n.addChildToFront(IR.var(tmp)); Node ifBody; // Construct the body of the if statement. if (key.isVar()) { // for (var key in x) { body; } // => // for (var tmp in x) { // if (!tmp.match(/___$/)) { // var key; // key = tmp; // body; // } // } ifBody = IR.block( key, IR.exprResult( IR.assign( key.getFirstChild().cloneNode(), tmp.cloneTree())), body); } else { // for (key in x) { body; } // => // for (var tmp in x) { // if (!tmp.match(/___$/)) { // key = tmp; // body; // } // } ifBody = IR.block( IR.exprResult( IR.assign( key, tmp.cloneTree())), body); } // Construct the new body of the for loop. Node newBody = IR.block( IR.ifNode( IR.not( IR.call( IR.getprop( tmp.cloneTree(), IR.string("match")), IR.regexp( IR.string("___$")))), ifBody)); n.addChildToBack(newBody); compiler.reportCodeChange(); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodePrinter.java0000644000175000017500000005054312115204405025724 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.base.Throwables; import com.google.debugging.sourcemap.FilePosition; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSTypeRegistry; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; /** * CodePrinter prints out JS code in either pretty format or compact format. * * @see CodeGenerator */ class CodePrinter { // The number of characters after which we insert a line break in the code static final int DEFAULT_LINE_LENGTH_THRESHOLD = 500; // There are two separate CodeConsumers, one for pretty-printing and // another for compact printing. // There are two implementations because the CompactCodePrinter // potentially has a very different implementation to the pretty // version. private abstract static class MappedCodePrinter extends CodeConsumer { final private Deque mappings; final private List allMappings; final private boolean createSrcMap; final private SourceMap.DetailLevel sourceMapDetailLevel; protected final StringBuilder code = new StringBuilder(1024); protected final int lineLengthThreshold; protected int lineLength = 0; protected int lineIndex = 0; MappedCodePrinter( int lineLengthThreshold, boolean createSrcMap, SourceMap.DetailLevel sourceMapDetailLevel) { Preconditions.checkState(sourceMapDetailLevel != null); this.lineLengthThreshold = lineLengthThreshold <= 0 ? Integer.MAX_VALUE : lineLengthThreshold; this.createSrcMap = createSrcMap; this.sourceMapDetailLevel = sourceMapDetailLevel; this.mappings = createSrcMap ? new ArrayDeque() : null; this.allMappings = createSrcMap ? new ArrayList() : null; } /** * Maintains a mapping from a given node to the position * in the source code at which its generated form was * placed. This position is relative only to the current * run of the CodeConsumer and will be normalized * later on by the SourceMap. * * @see SourceMap */ private static class Mapping { Node node; FilePosition start; FilePosition end; } /** * Starts the source mapping for the given * node at the current position. */ @Override void startSourceMapping(Node node) { Preconditions.checkState(sourceMapDetailLevel != null); Preconditions.checkState(node != null); if (createSrcMap && node.getSourceFileName() != null && node.getLineno() > 0 && sourceMapDetailLevel.apply(node)) { int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); Mapping mapping = new Mapping(); mapping.node = node; mapping.start = new FilePosition(line, index); mappings.push(mapping); allMappings.add(mapping); } } /** * Finishes the source mapping for the given * node at the current position. */ @Override void endSourceMapping(Node node) { if (createSrcMap && !mappings.isEmpty() && mappings.peek().node == node) { Mapping mapping = mappings.pop(); int line = getCurrentLineIndex(); int index = getCurrentCharIndex(); Preconditions.checkState(line >= 0); mapping.end = new FilePosition(line, index); } } /** * Generates the source map from the given code consumer, * appending the information it saved to the SourceMap * object given. */ void generateSourceMap(SourceMap map){ if (createSrcMap) { for (Mapping mapping : allMappings) { map.addMapping(mapping.node, mapping.start, mapping.end); } } } /** * Reports to the code consumer that the given line has been cut at the * given position, i.e. a \n has been inserted there. Or that a cut has * been undone, i.e. a previously inserted \n has been removed. * All mappings in the source maps after that position will be renormalized * as needed. */ void reportLineCut(int lineIndex, int charIndex, boolean insertion) { if (createSrcMap) { for (Mapping mapping : allMappings) { mapping.start = convertPosition(mapping.start, lineIndex, charIndex, insertion); if (mapping.end != null) { mapping.end = convertPosition(mapping.end, lineIndex, charIndex, insertion); } } } } /** * Converts the given position by normalizing it against the insertion * or removal of a newline at the given line and character position. * * @param position The existing position before the newline was inserted. * @param lineIndex The index of the line at which the newline was inserted. * @param characterPosition The position on the line at which the newline * was inserted. * @param insertion True if a newline was inserted, false if a newline was * removed. * * @return The normalized position. * @throws IllegalStateException if an attempt to reverse a line cut is * made on a previous line rather than the current line. */ private FilePosition convertPosition(FilePosition position, int lineIndex, int characterPosition, boolean insertion) { int originalLine = position.getLine(); int originalChar = position.getColumn(); if (insertion) { if (originalLine == lineIndex && originalChar >= characterPosition) { // If the position falls on the line itself, then normalize it // if it falls at or after the place the newline was inserted. return new FilePosition( originalLine + 1, originalChar - characterPosition); } else { return position; } } else { if (originalLine == lineIndex) { return new FilePosition( originalLine - 1, originalChar + characterPosition); } else if (originalLine > lineIndex) { // Not supported, can only undo a cut on the most recent line. To // do this on a previous lines would require reevaluating the cut // positions on all subsequent lines. throw new IllegalStateException( "Cannot undo line cut on a previous line."); } else { return position; } } } public String getCode() { return code.toString(); } @Override char getLastChar() { return (code.length() > 0) ? code.charAt(code.length() - 1) : '\0'; } protected final int getCurrentCharIndex() { return lineLength; } protected final int getCurrentLineIndex() { return lineIndex; } } static class PrettyCodePrinter extends MappedCodePrinter { // The number of characters after which we insert a line break in the code static final String INDENT = " "; private int indent = 0; /** * @param lineLengthThreshold The length of a line after which we force * a newline when possible. * @param createSourceMap Whether to generate source map data. * @param sourceMapDetailLevel A filter to control which nodes get mapped * into the source map. */ private PrettyCodePrinter( int lineLengthThreshold, boolean createSourceMap, SourceMap.DetailLevel sourceMapDetailLevel) { super(lineLengthThreshold, createSourceMap, sourceMapDetailLevel); } /** * Appends a string to the code, keeping track of the current line length. */ @Override void append(String str) { // For pretty printing: indent at the beginning of the line if (lineLength == 0) { for (int i = 0; i < indent; i++) { code.append(INDENT); lineLength += INDENT.length(); } } code.append(str); lineLength += str.length(); } /** * Adds a newline to the code, resetting the line length and handling * indenting for pretty printing. */ @Override void startNewLine() { if (lineLength > 0) { code.append('\n'); lineIndex++; lineLength = 0; } } @Override void maybeLineBreak() { maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { startNewLine(); } } @Override void endLine() { startNewLine(); } @Override void appendBlockStart() { append(" {"); indent++; } @Override void appendBlockEnd() { endLine(); indent--; append("}"); } @Override void listSeparator() { add(", "); maybeLineBreak(); } @Override void endFunction(boolean statementContext) { super.endFunction(statementContext); if (statementContext) { startNewLine(); } } @Override void beginCaseBody() { super.beginCaseBody(); indent++; endLine(); } @Override void endCaseBody() { super.endCaseBody(); indent--; endStatement(); } @Override void appendOp(String op, boolean binOp) { if (binOp) { if (getLastChar() != ' ' && op.charAt(0) != ',') { append(" "); } append(op); append(" "); } else { append(op); } } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? * {@inheritDoc} */ @Override boolean shouldPreserveExtraBlocks() { // When pretty-printing, always place the statement in its own block // so it is printed on a separate line. This allows breakpoints to be // placed on the statement. return true; } /** * @return The TRY node for the specified CATCH node. */ private Node getTryForCatch(Node n) { return n.getParent().getParent(); } /** * @return Whether the a line break should be added after the specified * BLOCK. */ @Override boolean breakAfterBlockFor(Node n, boolean isStatementContext) { Preconditions.checkState(n.isBlock()); Node parent = n.getParent(); if (parent != null) { int type = parent.getType(); switch (type) { case Token.DO: // Don't break before 'while' in DO-WHILE statements. return false; case Token.FUNCTION: // FUNCTIONs are handled separately, don't break here. return false; case Token.TRY: // Don't break before catch return n != parent.getFirstChild(); case Token.CATCH: // Don't break before finally return !NodeUtil.hasFinally(getTryForCatch(parent)); case Token.IF: // Don't break before else return n == parent.getLastChild(); } } return true; } @Override void endFile() { maybeEndStatement(); } } static class CompactCodePrinter extends MappedCodePrinter { // The CompactCodePrinter tries to emit just enough newlines to stop there // being lines longer than the threshold. Since the output is going to be // gzipped, it makes sense to try to make the newlines appear in similar // contexts so that gzip can encode them for 'free'. // // This version tries to break the lines at 'preferred' places, which are // between the top-level forms. This works because top-level forms tend to // be more uniform than arbitrary legal contexts. Better compression would // probably require explicit modeling of the gzip algorithm. private final boolean lineBreak; private final boolean preferLineBreakAtEndOfFile; private int lineStartPosition = 0; private int preferredBreakPosition = 0; private int prevCutPosition = 0; private int prevLineStartPosition = 0; /** * @param lineBreak break the lines a bit more aggressively * @param lineLengthThreshold The length of a line after which we force * a newline when possible. * @param createSrcMap Whether to gather source position * mapping information when printing. * @param sourceMapDetailLevel A filter to control which nodes get mapped into * the source map. */ private CompactCodePrinter(boolean lineBreak, boolean preferLineBreakAtEndOfFile, int lineLengthThreshold, boolean createSrcMap, SourceMap.DetailLevel sourceMapDetailLevel) { super(lineLengthThreshold, createSrcMap, sourceMapDetailLevel); this.lineBreak = lineBreak; this.preferLineBreakAtEndOfFile = preferLineBreakAtEndOfFile; } /** * Appends a string to the code, keeping track of the current line length. */ @Override void append(String str) { code.append(str); lineLength += str.length(); } /** * Adds a newline to the code, resetting the line length. */ @Override void startNewLine() { if (lineLength > 0) { prevCutPosition = code.length(); prevLineStartPosition = lineStartPosition; code.append('\n'); lineLength = 0; lineIndex++; lineStartPosition = code.length(); } } @Override void maybeLineBreak() { if (lineBreak) { if (sawFunction) { startNewLine(); sawFunction = false; } } // Since we are at a legal line break, can we upgrade the // preferred break position? We prefer to break after a // semicolon rather than before it. int len = code.length(); if (preferredBreakPosition == len - 1) { char ch = code.charAt(len - 1); if (ch == ';') { preferredBreakPosition = len; } } maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { // Use the preferred position provided it will break the line. if (preferredBreakPosition > lineStartPosition && preferredBreakPosition < lineStartPosition + lineLength) { int position = preferredBreakPosition; code.insert(position, '\n'); prevCutPosition = position; reportLineCut(lineIndex, position - lineStartPosition, true); lineIndex++; lineLength -= (position - lineStartPosition); lineStartPosition = position + 1; } else { startNewLine(); } } } @Override void notePreferredLineBreak() { preferredBreakPosition = code.length(); } @Override void endFile() { super.endFile(); if (!preferLineBreakAtEndOfFile) { return; } if (lineLength > lineLengthThreshold / 2) { // Add an extra break at end of file. append(";"); startNewLine(); } else if (prevCutPosition > 0) { // Shift the previous break to end of file by replacing it with a // and adding a new break at end of file. Adding the space // handles cases like instanceof\nfoo. (it would be nice to avoid this) code.setCharAt(prevCutPosition, ' '); lineStartPosition = prevLineStartPosition; lineLength = code.length() - lineStartPosition; reportLineCut(lineIndex, prevCutPosition + 1, false); lineIndex--; prevCutPosition = 0; prevLineStartPosition = 0; append(";"); startNewLine(); } else { // A small file with no line breaks. We do nothing in this case to // avoid excessive line breaks. It's not ideal if a lot of these pile // up, but that is reasonably unlikely. } } } static class Builder { private final Node root; private CompilerOptions options = new CompilerOptions(); private boolean outputTypes = false; private SourceMap sourceMap = null; private boolean tagAsStrict; private JSTypeRegistry registry; /** * Sets the root node from which to generate the source code. * @param node The root node. */ Builder(Node node) { root = node; } /** * Sets the output options from compiler options. */ Builder setCompilerOptions(CompilerOptions options) { try { this.options = (CompilerOptions) options.clone(); } catch (CloneNotSupportedException e) { throw Throwables.propagate(e); } return this; } Builder setTypeRegistry(JSTypeRegistry registry) { this.registry = registry; return this; } /** * Sets whether pretty printing should be used. * @param prettyPrint If true, pretty printing will be used. */ Builder setPrettyPrint(boolean prettyPrint) { options.prettyPrint = prettyPrint; return this; } /** * Sets whether line breaking should be done automatically. * @param lineBreak If true, line breaking is done automatically. */ Builder setLineBreak(boolean lineBreak) { options.lineBreak = lineBreak; return this; } /** * Sets whether to output closure-style type annotations. * @param outputTypes If true, outputs closure-style type annotations. */ Builder setOutputTypes(boolean outputTypes) { this.outputTypes = outputTypes; return this; } /** * Sets the source map to which to write the metadata about * the generated source code. * * @param sourceMap The source map. */ Builder setSourceMap(SourceMap sourceMap) { this.sourceMap = sourceMap; return this; } /** * Set whether the output should be tags as ECMASCRIPT 5 Strict. */ Builder setTagAsStrict(boolean tagAsStrict) { this.tagAsStrict = tagAsStrict; return this; } /** * Generates the source code and returns it. */ String build() { if (root == null) { throw new IllegalStateException( "Cannot build without root node being specified"); } Format outputFormat = outputTypes ? Format.TYPED : options.prettyPrint ? Format.PRETTY : Format.COMPACT; return toSource(root, outputFormat, options, registry, sourceMap, tagAsStrict); } } enum Format { COMPACT, PRETTY, TYPED } /** * Converts a tree to JS code */ private static String toSource(Node root, Format outputFormat, CompilerOptions options, JSTypeRegistry registry, SourceMap sourceMap, boolean tagAsStrict) { Preconditions.checkState(options.sourceMapDetailLevel != null); boolean createSourceMap = (sourceMap != null); MappedCodePrinter mcp = outputFormat == Format.COMPACT ? new CompactCodePrinter( options.lineBreak, options.preferLineBreakAtEndOfFile, options.lineLengthThreshold, createSourceMap, options.sourceMapDetailLevel) : new PrettyCodePrinter( options.lineLengthThreshold, createSourceMap, options.sourceMapDetailLevel); CodeGenerator cg = outputFormat == Format.TYPED ? new TypedCodeGenerator(mcp, options, registry) : new CodeGenerator(mcp, options); if (tagAsStrict) { cg.tagAsStrict(); } cg.add(root); mcp.endFile(); String code = mcp.getCode(); if (createSourceMap) { mcp.generateSourceMap(sourceMap); } return code; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeReplaceKnownMethods.java0000644000175000017500000005612412115204405031105 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.List; import java.util.Locale; /** * Just to fold known methods when they are called with constants. * */ class PeepholeReplaceKnownMethods extends AbstractPeepholeOptimization{ // The LOCALE independent "locale" private static final Locale ROOT_LOCALE = new Locale(""); private final boolean late; /** * @param late When late is true, this mean we are currently running after * most of the other optimizations. In this case we avoid changes that make * the code larger (but otherwise easier to analyze - such as using string * splitting). */ PeepholeReplaceKnownMethods(boolean late) { this.late = late; } @Override Node optimizeSubtree(Node subtree) { if (subtree.isCall() ){ return tryFoldKnownMethods(subtree); } return subtree; } private Node tryFoldKnownMethods(Node subtree) { // For now we only support string methods .join(), // .indexOf(), .substring() and .substr() // and numeric methods parseInt() and parseFloat(). subtree = tryFoldArrayJoin(subtree); if (subtree.isCall()) { Node callTarget = subtree.getFirstChild(); if (callTarget == null) { return subtree; } if (NodeUtil.isGet(callTarget)) { subtree = tryFoldKnownStringMethods(subtree); } else { subtree = tryFoldKnownNumericMethods(subtree); } } return subtree; } /** * Try to evaluate known String methods * .indexOf(), .substr(), .substring() */ private Node tryFoldKnownStringMethods(Node subtree) { Preconditions.checkArgument(subtree.isCall()); // check if this is a call on a string method // then dispatch to specific folding method. Node callTarget = subtree.getFirstChild(); if (callTarget == null) { return subtree; } if (!NodeUtil.isGet(callTarget)) { return subtree; } Node stringNode = callTarget.getFirstChild(); Node functionName = stringNode.getNext(); if ((!stringNode.isString()) || (!functionName.isString())) { return subtree; } String functionNameString = functionName.getString(); Node firstArg = callTarget.getNext(); if (functionNameString.equals("split")) { subtree = tryFoldStringSplit(subtree, stringNode, firstArg); } else if (firstArg == null) { if (functionNameString.equals("toLowerCase")) { subtree = tryFoldStringToLowerCase(subtree, stringNode); } else if (functionNameString.equals("toUpperCase")) { subtree = tryFoldStringToUpperCase(subtree, stringNode); } return subtree; } else if (NodeUtil.isImmutableValue(firstArg)) { if (functionNameString.equals("indexOf") || functionNameString.equals("lastIndexOf")) { subtree = tryFoldStringIndexOf(subtree, functionNameString, stringNode, firstArg); } else if (functionNameString.equals("substr")) { subtree = tryFoldStringSubstr(subtree, stringNode, firstArg); } else if (functionNameString.equals("substring")) { subtree = tryFoldStringSubstring(subtree, stringNode, firstArg); } else if (functionNameString.equals("charAt")) { subtree = tryFoldStringCharAt(subtree, stringNode, firstArg); } else if (functionNameString.equals("charCodeAt")) { subtree = tryFoldStringCharCodeAt(subtree, stringNode, firstArg); } } return subtree; } /** * Try to evaluate known Numeric methods * .parseInt(), parseFloat() */ private Node tryFoldKnownNumericMethods(Node subtree) { Preconditions.checkArgument(subtree.isCall()); if (isASTNormalized()) { // check if this is a call on a string method // then dispatch to specific folding method. Node callTarget = subtree.getFirstChild(); if (!callTarget.isName()) { return subtree; } String functionNameString = callTarget.getString(); Node firstArgument = callTarget.getNext(); if ((firstArgument != null) && (firstArgument.isString() || firstArgument.isNumber())) { if (functionNameString.equals("parseInt") || functionNameString.equals("parseFloat")) { subtree = tryFoldParseNumber(subtree, functionNameString, firstArgument); } } } return subtree; } /** * @return The lowered string Node. */ private Node tryFoldStringToLowerCase(Node subtree, Node stringNode) { // From Rhino, NativeString.java. See ECMA 15.5.4.11 String lowered = stringNode.getString().toLowerCase(ROOT_LOCALE); Node replacement = IR.string(lowered); subtree.getParent().replaceChild(subtree, replacement); reportCodeChange(); return replacement; } /** * @return The upped string Node. */ private Node tryFoldStringToUpperCase(Node subtree, Node stringNode) { // From Rhino, NativeString.java. See ECMA 15.5.4.12 String upped = stringNode.getString().toUpperCase(ROOT_LOCALE); Node replacement = IR.string(upped); subtree.getParent().replaceChild(subtree, replacement); reportCodeChange(); return replacement; } /** * @param input string representation of a number * @return string with leading and trailing zeros removed */ private String normalizeNumericString(String input) { if (input == null || input.length() == 0) { return input; } int startIndex = 0, endIndex = input.length() - 1; // Remove leading zeros while (startIndex < input.length() && input.charAt(startIndex) == '0' && input.charAt(startIndex) != '.') { startIndex++; } // Remove trailing zeros only after the decimal if (input.indexOf('.') >= 0) { while (endIndex >= 0 && input.charAt(endIndex) == '0') { endIndex--; } if (input.charAt(endIndex) == '.') { endIndex--; } } if (startIndex >= endIndex) { return input; } return input.substring(startIndex, endIndex + 1); } /** * Try to evaluate parseInt, parseFloat: * parseInt("1") -> 1 * parseInt("1", 10) -> 1 * parseFloat("1.11") -> 1.11 */ private Node tryFoldParseNumber( Node n, String functionName, Node firstArg) { Preconditions.checkArgument(n.isCall()); boolean isParseInt = functionName.equals("parseInt"); Node secondArg = firstArg.getNext(); // Second argument is only used as the radix for parseInt int radix = 0; if (secondArg != null) { if (!isParseInt) { return n; } // Third-argument and non-numeric second arg are problematic. Discard. if (secondArg.getNext() != null || !secondArg.isNumber()) { return n; } else { double tmpRadix = secondArg.getDouble(); if (tmpRadix != (int)tmpRadix) return n; radix = (int)tmpRadix; if (radix < 0 || radix == 1 || radix > 36) { return n; } } } // stringVal must be a valid string. String stringVal = null; Double checkVal; if (firstArg.isNumber()) { checkVal = NodeUtil.getNumberValue(firstArg); if (!(radix == 0 || radix == 10) && isParseInt) { //Convert a numeric first argument to a different base stringVal = String.valueOf(checkVal.intValue()); } else { // If parseFloat is called with a numeric argument, // replace it with just the number. // If parseInt is called with a numeric first argument and the radix // is 10 or omitted, just replace it with the number Node numericNode; if (isParseInt) { numericNode = IR.number(checkVal.intValue()); } else { numericNode = IR.number(checkVal); } n.getParent().replaceChild(n, numericNode); reportCodeChange(); return numericNode; } } else { stringVal = NodeUtil.getStringValue(firstArg); if (stringVal == null) { return n; } //Check that the string is in a format we can recognize checkVal = NodeUtil.getStringNumberValue(stringVal); if (checkVal == null) { return n; } stringVal = NodeUtil.trimJsWhiteSpace(stringVal); if (stringVal.length() == 0) { return n; } } Node newNode; if (stringVal.equals("0")) { // Special case for parseInt("0") or parseFloat("0") newNode = IR.number(0); } else if (isParseInt) { if (radix == 0 || radix == 16) { if (stringVal.length() > 1 && stringVal.substring(0, 2).equalsIgnoreCase("0x")) { radix = 16; stringVal = stringVal.substring(2); } else if (radix == 0) { // if a radix is not specified or is 0 and the most // significant digit is "0", the string will parse // with a radix of 8 on some browsers, so leave // this case alone. This check does not apply in // script mode ECMA5 or greater if (!isEcmaScript5OrGreater() && stringVal.substring(0, 1).equals("0")) { return n; } radix = 10; } } int newVal = 0; try { newVal = Integer.parseInt(stringVal, radix); } catch (NumberFormatException e) { return n; } newNode = IR.number(newVal); } else { String normalizedNewVal = "0"; try { double newVal = Double.parseDouble(stringVal); newNode = IR.number(newVal); normalizedNewVal = normalizeNumericString(String.valueOf(newVal)); } catch(NumberFormatException e) { return n; } // Make sure that the parsed number matches the original string // This prevents rounding differences between the Java implementation // and native script. if (!normalizeNumericString(stringVal).equals(normalizedNewVal)) { return n; } } n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } /** * Try to evaluate String.indexOf/lastIndexOf: * "abcdef".indexOf("bc") -> 1 * "abcdefbc".indexOf("bc", 3) -> 6 */ private Node tryFoldStringIndexOf( Node n, String functionName, Node lstringNode, Node firstArg) { Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(lstringNode.isString()); String lstring = NodeUtil.getStringValue(lstringNode); boolean isIndexOf = functionName.equals("indexOf"); Node secondArg = firstArg.getNext(); String searchValue = NodeUtil.getStringValue(firstArg); // searchValue must be a valid string. if (searchValue == null) { return n; } int fromIndex = isIndexOf ? 0 : lstring.length(); if (secondArg != null) { // Third-argument and non-numeric second arg are problematic. Discard. if (secondArg.getNext() != null || !secondArg.isNumber()) { return n; } else { fromIndex = (int) secondArg.getDouble(); } } int indexVal = isIndexOf ? lstring.indexOf(searchValue, fromIndex) : lstring.lastIndexOf(searchValue, fromIndex); Node newNode = IR.number(indexVal); n.getParent().replaceChild(n, newNode); reportCodeChange(); return newNode; } /** * Try to fold an array join: ['a', 'b', 'c'].join('') -> 'abc'; */ private Node tryFoldArrayJoin(Node n) { Node callTarget = n.getFirstChild(); if (callTarget == null || !callTarget.isGetProp()) { return n; } Node right = callTarget.getNext(); if (right != null) { if (right.getNext() != null || !NodeUtil.isImmutableValue(right)) { return n; } } Node arrayNode = callTarget.getFirstChild(); Node functionName = arrayNode.getNext(); if (!arrayNode.isArrayLit() || !functionName.getString().equals("join")) { return n; } if (right != null && right.isString() && ",".equals(right.getString())) { // "," is the default, it doesn't need to be explicit n.removeChild(right); reportCodeChange(); } String joinString = (right == null) ? "," : NodeUtil.getStringValue(right); List arrayFoldedChildren = Lists.newLinkedList(); StringBuilder sb = null; int foldedSize = 0; Node prev = null; Node elem = arrayNode.getFirstChild(); // Merges adjacent String nodes. while (elem != null) { if (NodeUtil.isImmutableValue(elem) || elem.isEmpty()) { if (sb == null) { sb = new StringBuilder(); } else { sb.append(joinString); } sb.append(NodeUtil.getArrayElementStringValue(elem)); } else { if (sb != null) { Preconditions.checkNotNull(prev); // + 2 for the quotes. foldedSize += sb.length() + 2; arrayFoldedChildren.add( IR.string(sb.toString()).copyInformationFrom(prev)); sb = null; } foldedSize += InlineCostEstimator.getCost(elem); arrayFoldedChildren.add(elem); } prev = elem; elem = elem.getNext(); } if (sb != null) { Preconditions.checkNotNull(prev); // + 2 for the quotes. foldedSize += sb.length() + 2; arrayFoldedChildren.add( IR.string(sb.toString()).copyInformationFrom(prev)); } // one for each comma. foldedSize += arrayFoldedChildren.size() - 1; int originalSize = InlineCostEstimator.getCost(n); switch (arrayFoldedChildren.size()) { case 0: Node emptyStringNode = IR.string(""); n.getParent().replaceChild(n, emptyStringNode); reportCodeChange(); return emptyStringNode; case 1: Node foldedStringNode = arrayFoldedChildren.remove(0); if (foldedSize > originalSize) { return n; } arrayNode.detachChildren(); if (!foldedStringNode.isString()) { // If the Node is not a string literal, ensure that // it is coerced to a string. Node replacement = IR.add( IR.string("").srcref(n), foldedStringNode); foldedStringNode = replacement; } n.getParent().replaceChild(n, foldedStringNode); reportCodeChange(); return foldedStringNode; default: // No folding could actually be performed. if (arrayFoldedChildren.size() == arrayNode.getChildCount()) { return n; } int kJoinOverhead = "[].join()".length(); foldedSize += kJoinOverhead; foldedSize += (right != null) ? InlineCostEstimator.getCost(right) : 0; if (foldedSize > originalSize) { return n; } arrayNode.detachChildren(); for (Node node : arrayFoldedChildren) { arrayNode.addChildToBack(node); } reportCodeChange(); break; } return n; } /** * Try to fold .substr() calls on strings */ private Node tryFoldStringSubstr(Node n, Node stringNode, Node arg1) { Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(stringNode.isString()); int start, length; String stringAsString = stringNode.getString(); // TODO(nicksantos): We really need a NodeUtil.getNumberValue // function. if (arg1 != null && arg1.isNumber()) { start = (int) arg1.getDouble(); } else { return n; } Node arg2 = arg1.getNext(); if (arg2 != null) { if (arg2.isNumber()) { length = (int) arg2.getDouble(); } else { return n; } if (arg2.getNext() != null) { // If we got more args than we expected, bail out. return n; } } else { // parameter 2 not passed length = stringAsString.length() - start; } // Don't handle these cases. The specification actually does // specify the behavior in some of these cases, but we haven't // done a thorough investigation that it is correctly implemented // in all browsers. if ((start + length) > stringAsString.length() || (length < 0) || (start < 0)) { return n; } String result = stringAsString.substring(start, start + length); Node resultNode = IR.string(result); Node parent = n.getParent(); parent.replaceChild(n, resultNode); reportCodeChange(); return resultNode; } /** * Try to fold .substring() calls on strings */ private Node tryFoldStringSubstring(Node n, Node stringNode, Node arg1) { Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(stringNode.isString()); int start, end; String stringAsString = stringNode.getString(); if (arg1 != null && arg1.isNumber()) { start = (int) arg1.getDouble(); } else { return n; } Node arg2 = arg1.getNext(); if (arg2 != null) { if (arg2.isNumber()) { end = (int) arg2.getDouble(); } else { return n; } if (arg2.getNext() != null) { // If we got more args than we expected, bail out. return n; } } else { // parameter 2 not passed end = stringAsString.length(); } // Don't handle these cases. The specification actually does // specify the behavior in some of these cases, but we haven't // done a thorough investigation that it is correctly implemented // in all browsers. if ((end > stringAsString.length()) || (start > stringAsString.length()) || (end < 0) || (start < 0)) { return n; } String result = stringAsString.substring(start, end); Node resultNode = IR.string(result); Node parent = n.getParent(); parent.replaceChild(n, resultNode); reportCodeChange(); return resultNode; } /** * Try to fold .charAt() calls on strings */ private Node tryFoldStringCharAt(Node n, Node stringNode, Node arg1) { Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(stringNode.isString()); int index; String stringAsString = stringNode.getString(); if (arg1 != null && arg1.isNumber() && arg1.getNext() == null) { index = (int) arg1.getDouble(); } else { return n; } if (index < 0 || stringAsString.length() <= index) { // http://es5.github.com/#x15.5.4.4 says "" is returned when index is // out of bounds but we bail. return n; } Node resultNode = IR.string( stringAsString.substring(index, index + 1)); Node parent = n.getParent(); parent.replaceChild(n, resultNode); reportCodeChange(); return resultNode; } /** * Try to fold .charCodeAt() calls on strings */ private Node tryFoldStringCharCodeAt(Node n, Node stringNode, Node arg1) { Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(stringNode.isString()); int index; String stringAsString = stringNode.getString(); if (arg1 != null && arg1.isNumber() && arg1.getNext() == null) { index = (int) arg1.getDouble(); } else { return n; } if (index < 0 || stringAsString.length() <= index) { // http://es5.github.com/#x15.5.4.5 says NaN is returned when index is // out of bounds but we bail. return n; } Node resultNode = IR.number(stringAsString.charAt(index)); Node parent = n.getParent(); parent.replaceChild(n, resultNode); reportCodeChange(); return resultNode; } /** * Support function for jsSplit, find the first occurrence of * separator within stringValue starting at startIndex. */ private int jsSplitMatch(String stringValue, int startIndex, String separator) { if (startIndex + separator.length() > stringValue.length()) { return -1; } int matchIndex = stringValue.indexOf(separator, startIndex); if (matchIndex < 0) { return -1; } return matchIndex; } /** * Implement the JS String.split method using a string separator. */ private String[] jsSplit(String stringValue, String separator, int limit) { Preconditions.checkArgument(limit >= 0); Preconditions.checkArgument(stringValue != null); // For limits of 0, return an empty array if (limit == 0) { return new String[0]; } // If a separator is not specified, return the entire string as // the only element of an array. if (separator == null) { return new String[] {stringValue}; } List splitStrings = Lists.newArrayList(); // If an empty string is specified for the separator, split apart each // character of the string. if (separator.length() == 0) { for (int i = 0; i < stringValue.length() && i < limit; i++) { splitStrings.add(stringValue.substring(i, i + 1)); } } else { int startIndex = 0, matchIndex; while ((matchIndex = jsSplitMatch(stringValue, startIndex, separator)) >= 0 && splitStrings.size() < limit) { splitStrings.add(stringValue.substring(startIndex, matchIndex)); startIndex = matchIndex + separator.length(); } if (splitStrings.size() < limit) { if (startIndex < stringValue.length()) { splitStrings.add(stringValue.substring(startIndex)); } else { splitStrings.add(""); } } } return splitStrings.toArray(new String[splitStrings.size()]); } /** * Try to fold .split() calls on strings */ private Node tryFoldStringSplit(Node n, Node stringNode, Node arg1) { if (late) { return n; } Preconditions.checkArgument(n.isCall()); Preconditions.checkArgument(stringNode.isString()); String separator = null; String stringValue = stringNode.getString(); // Maximum number of possible splits int limit = stringValue.length() + 1; if (arg1 != null) { if (arg1.isString()) { separator = arg1.getString(); } else if (!arg1.isNull()) { return n; } Node arg2 = arg1.getNext(); if (arg2 != null) { if (arg2.isNumber()) { limit = Math.min((int) arg2.getDouble(), limit); if (limit < 0) { return n; } } else { return n; } } } // Split the string and convert the returned array into JS nodes String[] stringArray = jsSplit(stringValue, separator, limit); Node arrayOfStrings = IR.arraylit(); for (int i = 0; i < stringArray.length; i++) { arrayOfStrings.addChildToBack( IR.string(stringArray[i]).srcref(stringNode)); } Node parent = n.getParent(); parent.replaceChild(n, arrayOfStrings); reportCodeChange(); return arrayOfStrings; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeSimplifyRegExp.java0000644000175000017500000000603112115204405030070 0ustar apoapo/* * Copyright 2011 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.regex.RegExpTree; import com.google.javascript.rhino.Node; /** * Simplifies regular expression patterns and flags. * * @author Mike Samuel */ class PeepholeSimplifyRegExp extends AbstractPeepholeOptimization { @Override Node optimizeSubtree(Node subtree) { if (subtree.isRegExp()) { // Split regexp into pattern and flags. String pattern = subtree.getFirstChild().getString(); String flags = subtree.getChildCount() == 2 ? subtree.getLastChild().getString() : ""; // Parse to an AST and optimize. RegExpTree regexTree; try { regexTree = RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { // Warnings are propagated in the CheckRegExp pass. return subtree; } regexTree = regexTree.simplify(flags); // Decompose the AST. String literal = regexTree.toString(); String newPattern = literal.substring(1, literal.length() - 1); // Remove unnecessary flags and order them consistently for gzip. String newFlags = ( // The g flags cannot match or replace more than one instance if it is // anchored at the front and back as in /^foo$/ and if the anchors are // relative to the whole string. // But if the regex has capturing groups, then the match operator // would return capturing groups without the g flag. (flags.contains("g") && (!RegExpTree.matchesWholeInput(regexTree, flags) || regexTree.hasCapturingGroup()) ? "g" : "") // Remove the i flag if it doesn't have any effect. // E.g. /[a-z0-9_]/i -> /\w/ + (flags.contains("i") && regexTree.isCaseSensitive() ? "i" : "") // If the regular expression contains no anchors, then the m flag has // no effect. + (flags.contains("m") && regexTree.containsAnchor() ? "m" : "")); // Update the original if something was done. if (!(newPattern.equals(pattern) && newFlags.equals(flags))) { subtree.getFirstChild().setString(newPattern); if (!"".equals(newFlags)) { subtree.getLastChild().setString(newFlags); } else if (subtree.getChildCount() == 2) { subtree.getLastChild().detachFromParent(); } reportCodeChange(); } } return subtree; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/package.html0000644000175000017500000000024712115204405025120 0ustar apoapo Provides the core compiler and its public API. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckRegExp.java0000644000175000017500000000556612115204405025643 0ustar apoapo/* * 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.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !first))) { t.report(n, REGEXP_REFERENCE); globalRegExpPropertiesUsed = true; } } // Check the syntax of regular expression patterns. } else if (n.isRegExp()) { String pattern = n.getFirstChild().getString(); String flags = n.getChildCount() == 2 ? n.getLastChild().getString() : ""; try { RegExpTree.parseRegExp(pattern, flags); } catch (IllegalArgumentException ex) { t.report(n, MALFORMED_REGEXP, ex.getMessage()); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ConcreteType.java0000644000175000017500000006162212115204405026112 0ustar apoapo/* * 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; import com.google.javascript.rhino.jstype.UnknownType; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; /** * Represents a reference type for which the exact definition in the source is * known. Unlike a {@code JSType} reference type, a concrete instance type of A * indicates that an instance of A -- not a subclass of A -- is a possible * value. Other concrete types are functions (whose definitions are known), * arrays containing concrete types, and unions of concrete types. * * These types are computed by {@code TightenTypes}. * */ abstract class ConcreteType implements LatticeElement { /** Static instance of the empty set of concrete types. */ static final ConcreteType NONE = new ConcreteNoneType(); /** Static instance of the set of all concrete types. */ static final ConcreteType ALL = new ConcreteAll(); /** Constant empty list of function types. */ private static final List NO_FUNCTIONS = Lists.newArrayList(); /** Constant empty list of instance types. */ private static final List NO_INSTANCES = Lists.newArrayList(); /** Constant empty list of slots. */ private static final List> NO_SLOTS = Lists.>newArrayList(); protected static ConcreteType createForTypes(Collection types) { if (types == null || types.size() == 0) { return NONE; } else if (types.size() == 1) { return types.iterator().next(); } else { return new ConcreteUnionType(Sets.newHashSet(types)); } } /** Indicates whether this is an empty set of types. */ boolean isNone() { return false; } /** Indicates whether this type is a function. */ boolean isFunction() { return false; } /** * Indicates whether this type is an instance of some type (or a prototype * instance of a type). * */ boolean isInstance() { return false; } /** Indicates whether this type is a union of concrete types. */ boolean isUnion() { return false; } /** Indicates whether this type is the set of all types. */ boolean isAll() { return false; } /** Indicates whether this represents exactly one type. */ boolean isSingleton() { return !isNone() && !isUnion() && !isAll(); } /** Returns this as a function, if it is one, or null, if not. */ ConcreteFunctionType toFunction() { return null; } /** Returns this as an instance, if it is one, or null, if not. */ ConcreteInstanceType toInstance() { return null; } /** Returns this as a union, if it is one, or null, if not. */ ConcreteUnionType toUnion() { return null; } /** Returns the scope for the type, or null if not applicable. */ StaticScope getScope() { return null; } /** Returns the union of this type with the given one. */ ConcreteType unionWith(ConcreteType other) { Preconditions.checkState(this.isSingleton()); // Sets must override. if (!other.isSingleton()) { return other.unionWith(this); } else if (equals(other)) { return this; } else { return new ConcreteUnionType(this, other); } } /** Returns the intersection of this type with the given one. */ ConcreteType intersectWith(ConcreteType other) { if (!other.isSingleton()) { return other.intersectWith(this); } else if (equals(other)) { return this; } else { return NONE; } } /** * Calls {@code filter()} on each type, adding it to the returned list if it * is not null. */ private List getMatchingTypes(TypeFilter filter) { C type = null; if (isUnion()) { List list = Lists.newArrayList(); for (ConcreteType alt : toUnion().getAlternatives()) { if ((type = filter.filter(alt)) != null) { list.add(type); } } return list; } else if ((type = filter.filter(this)) != null) { List list = Lists.newArrayList(); list.add(type); return list; } else { return filter.emptyList; } } /** * Provides one function to filter an input, either returning the filtered * version of the input, or null if the input does not have a corresponding * output. */ abstract class TypeFilter { /** The empty list for a caller to use if there are no non-null outputs. */ final List emptyList; TypeFilter(List emptyList) { this.emptyList = emptyList; } abstract protected C filter(ConcreteType type); } /** Returns all function types in this set. */ List getFunctions() { return getMatchingTypes(new TypeFilter(NO_FUNCTIONS) { @Override public ConcreteFunctionType filter(ConcreteType type) { return type.isFunction() ? type.toFunction() : null; } }); } /** Returns all instance types in this set. */ List getInstances() { return getMatchingTypes(new TypeFilter(NO_INSTANCES) { @Override public ConcreteInstanceType filter(ConcreteType type) { return type.isInstance() ? type.toInstance() : null; } }); } /** Returns the (non-null) instance types of all functions in this set. */ List getFunctionInstanceTypes() { return getMatchingTypes(new TypeFilter(NO_INSTANCES) { @Override public ConcreteInstanceType filter(ConcreteType type) { if (type.isFunction()) { return type.toFunction().getInstanceType(); } return null; } }); } /** Returns all (non-null) function prototype types in this set. */ List getPrototypeTypes() { return getMatchingTypes(new TypeFilter(NO_INSTANCES) { @Override public ConcreteInstanceType filter(ConcreteType type) { if (type.isInstance() && type.toInstance().isFunctionPrototype()) { return type.toInstance(); } return null; } }); } /** Returns the (non-null) superclasses of all functions in this set. */ List getSuperclassTypes() { return getMatchingTypes(new TypeFilter(NO_FUNCTIONS) { @Override public ConcreteFunctionType filter(ConcreteType type) { return type.isFunction() && type.toFunction().getSuperclassType() != null ? type.toFunction().getSuperclassType() : null; } }); } /** Returns the (non-null) index-th parameters of functions in this set. */ List> getParameterSlots(final int index) { return getMatchingTypes(new TypeFilter>(NO_SLOTS) { @Override public StaticSlot filter(ConcreteType type) { return type.isFunction() && toFunction().getParameterSlot(index) != null ? toFunction().getParameterSlot(index) : null; } }); } /** * Returns the (non-null) slots for properties with the given name in all * instance types in this set. */ List> getPropertySlots(final String name) { return getMatchingTypes(new TypeFilter>(NO_SLOTS) { @Override public StaticSlot filter(ConcreteType type) { StaticSlot slot = null; if (type.isInstance()) { slot = type.toInstance().getPropertySlot(name); } return slot; } }); } /** * Returns the concrete type for the given property from the given type. * If the given type is a union type, returns the union of types for the slots * of the property. */ ConcreteType getPropertyType(final String name) { ConcreteType ret = NONE; for (StaticSlot slot : getPropertySlots(name)) { ret = ret.unionWith(slot.getType()); } return ret; } /** Implements the empty set of types. */ private static class ConcreteNoneType extends ConcreteType { @Override boolean isNone() { return true; } @Override ConcreteType unionWith(ConcreteType other) { return other; } @Override ConcreteType intersectWith(ConcreteType other) { return NONE; } @Override public String toString() { return "()"; } } /** * Represents a specific function in the source code. Note that we assume the * factory creates only a single instance of this class for a given * declaration, so we do not need to override {@code Object.equals}. * * {@code bodyScope} contains a slot for each local variable in the function * body's scope as well as special slots to keep track of whether the * function is called, the this type, and the return type. */ static class ConcreteFunctionType extends ConcreteType { /** Name used for the call slot (see {@code getCallSlot}). */ static final String CALL_SLOT_NAME = ":call"; /** Name used for the this slot (see {@code getThisSlot}). */ static final String THIS_SLOT_NAME = ":this"; /** Name used for the return slot (see {@code getReturnSlot}). */ static final String RETURN_SLOT_NAME = ":return"; private final Factory factory; private final Node declaration; private final StaticScope parentScope; private StaticScope bodyScope; private ConcreteInstanceType instanceType; private ConcreteInstanceType prototypeType; ConcreteFunctionType(Factory factory, Node declaration, StaticScope parentScope) { this.factory = factory; this.declaration = declaration; this.parentScope = parentScope; Preconditions.checkArgument(declaration.isFunction()); Preconditions.checkArgument(declaration.getJSType() != null); Preconditions.checkArgument(declaration.getJSType().isFunctionType()); } @Override boolean isFunction() { return true; } @Override ConcreteFunctionType toFunction() { return this; } /** * Returns the slot representing that a call to it occurred. This is * assigned a type if the function is called. This ensures that the body of * the function is processed even if it has no arguments or if the arguments * do not take any concrete types as arguments. */ StaticSlot getCallSlot() { return getScope().getOwnSlot(CALL_SLOT_NAME); } /** Returns the slot representing the value of 'this' in the body. */ StaticSlot getThisSlot() { return getScope().getOwnSlot(THIS_SLOT_NAME); } /** Returns the slot representing the values returned. */ StaticSlot getReturnSlot() { return getScope().getOwnSlot(RETURN_SLOT_NAME); } /** Returns the slot representing the index-th parameter. */ StaticSlot getParameterSlot(int index) { return getScope().getOwnSlot(getParameterName(index)); } /** Returns the name for the index-th parameter within the function. */ private String getParameterName(int index) { int count = 0; for (Node n = getFirstParameter(); n != null; n = n.getNext()) { if (count++ == index) { return n.getString(); } } return null; } /** Returns the node containing the first parameter's name. */ private Node getFirstParameter() { return declaration.getFirstChild().getNext().getFirstChild(); } /** Returns the JSType of this function. */ public FunctionType getJSType() { return JSType.toMaybeFunctionType(declaration.getJSType()); } /** * Returns the concrete type representing instances of this type or null if * it has none. */ ConcreteInstanceType getInstanceType() { if (instanceType == null) { if (getJSType().isConstructor()) { instanceType = factory.createConcreteInstance(getJSType().getInstanceType()); } } return instanceType; } /** Returns the concrete type representing the prototype of this type. */ ConcreteInstanceType getPrototypeType() { if (prototypeType == null) { prototypeType = factory.createConcreteInstance(getJSType().getPrototype()); } return prototypeType; } /** Returns the type of the superclass (or null if none exists). */ ConcreteFunctionType getSuperclassType() { FunctionType superConstructor = getJSType().getSuperClassConstructor(); return (superConstructor != null) ? factory.getConcreteFunction(superConstructor) : null; } /** Returns the scope for the body of this function. */ @Override StaticScope getScope() { if (bodyScope == null) { bodyScope = factory.createFunctionScope(declaration, parentScope); } return bodyScope; } /** * Informally, a function is represented by * {@code function (params): returnType} where the {@code params} is a comma * separated list of types, the first one being a special * {@code this:T} if the function expects a known type for {@code this}. */ @Override public String toString() { StringBuilder b = new StringBuilder(32); b.append("function ("); boolean hasKnownTypeOfThis = !getThisSlot().getType().isNone(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getThisSlot().getType().toString()); } Node n = getFirstParameter(); if (hasKnownTypeOfThis && n != null) { b.append(", "); } for (int i = 0; n != null; ++i, n = n.getNext()) { String paramName = n.getString(); StaticSlot var = getScope().getOwnSlot(paramName); b.append(var.getType()); getParameterSlot(i).getType(); if (n.getNext() != null) { b.append(", "); } } b.append(")"); if (getReturnSlot().getType() != null) { b.append(": "); b.append(getReturnSlot().getType().toString()); } return b.toString(); } } /** * Represents a specific constructor in the source code. Note that we assume * the factory creates only a single instance of this class for a given * declaration, so we do not need to override {@code Object.equals}. * * The {@code StaticScope} contains a slot for each property defined on the * instance type and the scope parent chain follows the prototype chain * hierarchy. */ static class ConcreteInstanceType extends ConcreteType { /** Factory for creating types and scopes. */ private final Factory factory; /** Stores the normal type information for this instance. */ public final ObjectType instanceType; /** The type information for the implicit prototype of this type, if any. */ private ConcreteInstanceType prototype; /** * A scope containing the properties of this instance, created on demand. * Its parent scope corresponds to the scope of the implicit prototype. */ private StaticScope scope; ConcreteInstanceType(Factory factory, ObjectType instanceType) { this.factory = factory; this.instanceType = instanceType; Preconditions.checkArgument(!(instanceType instanceof UnknownType)); } @Override boolean isInstance() { return true; } @Override ConcreteInstanceType toInstance() { return this; } /** Determines whether this is a function prototype type. */ boolean isFunctionPrototype() { return instanceType.isFunctionPrototypeType(); } /** Returns the slot representing the property with the given name. */ StaticSlot getPropertySlot(String propName) { return getScope().getSlot(propName); } /** * Returns the closest instance type in the prototype chain that contains * the given property. */ ConcreteInstanceType getInstanceTypeWithProperty(String propName) { if (getScope().getOwnSlot(propName) != null) { // Normalize the instance type into the prototype, to be as // consistent as possible with non-type tightened behavior. // // TODO(nicksantos|user): There's a larger issue here. // When JSCompiler infers property types on instance types, // that means that someone is just assigning a property // without declaring it. In this case, we can't meaningfully // tell when the property is being pulled off the subtype // vs. when it's being pulled off the supertype. So we should // probably invalidate properties of this sort. if (instanceType.getConstructor() != null) { return getConstructorType().getPrototypeType(); } return this; } else if (getImplicitPrototype() != null) { return getImplicitPrototype().getInstanceTypeWithProperty(propName); } else { return null; } } /** Returns the type representing the implicit prototype. */ ConcreteInstanceType getImplicitPrototype() { if ((prototype == null) && (instanceType.getImplicitPrototype() != null)) { ObjectType proto = instanceType.getImplicitPrototype(); if ((proto != instanceType) && !(proto instanceof UnknownType)) { prototype = factory.createConcreteInstance(proto); } } return prototype; } /** Returns the type of the constructor or null if this has none. */ ConcreteFunctionType getConstructorType() { if (instanceType.isFunctionPrototypeType()) { return factory.getConcreteFunction(instanceType.getOwnerFunction()); } else { FunctionType constructor = instanceType.getConstructor(); return (constructor != null) ? factory.getConcreteFunction(constructor) : null; } } /** Returns the scope of this type in the prototype chain. */ @Override StaticScope getScope() { if (scope == null) { scope = factory.createInstanceScope(instanceType); } return scope; } @Override public String toString() { return instanceType.toString(); } } /** * Represents a finite set of possible alternatives for this type. Note that * we make no effort to merge different array types into one array type, so * clients should not assume there is only one array in a set. */ static class ConcreteUnionType extends ConcreteType { private final Set alternatives; ConcreteUnionType(ConcreteType... alternatives) { this(Sets.newHashSet(alternatives)); } ConcreteUnionType(Set alternatives) { Preconditions.checkArgument(alternatives.size() > 1); this.alternatives = alternatives; } @Override boolean isUnion() { return true; } @Override ConcreteUnionType toUnion() { return this; } @Override ConcreteType unionWith(ConcreteType other) { if (other.isSingleton()) { if (alternatives.contains(other)) { return this; } else { Set alts = Sets.newHashSet(alternatives); alts.add(other); return new ConcreteUnionType(alts); } } else if (other.isUnion()) { ConcreteUnionType otherUnion = other.toUnion(); if (alternatives.containsAll(otherUnion.alternatives)) { return this; } else if (otherUnion.alternatives.containsAll(alternatives)) { return otherUnion; } else { Set alts = Sets.newHashSet(alternatives); alts.addAll(otherUnion.alternatives); return new ConcreteUnionType(alts); } } else { Preconditions.checkArgument(other.isNone() || other.isAll()); return other.unionWith(this); } } @Override ConcreteType intersectWith(ConcreteType other) { if (other.isSingleton()) { if (alternatives.contains(other)) { return other; } else { return NONE; } } else if (other.isUnion()) { Set types = Sets.newHashSet(alternatives); types.retainAll(other.toUnion().alternatives); return createForTypes(types); } else { Preconditions.checkArgument(other.isNone() || other.isAll()); return other.intersectWith(this); } } /** Returns all of the types in this set of alternatives. */ Set getAlternatives() { return alternatives; } @Override public boolean equals(Object obj) { return (obj instanceof ConcreteUnionType) && alternatives.equals(((ConcreteUnionType) obj).alternatives); } @Override public int hashCode() { return alternatives.hashCode() ^ 0x5f6e7d8c; } @Override public String toString() { List names = Lists.newArrayList(); for (ConcreteType type : alternatives) { names.add(type.toString()); } Collections.sort(names); return "(" + Joiner.on(",").join(names) + ")"; } } /** Implements the set of all concrete types. */ private static class ConcreteAll extends ConcreteType { @Override boolean isAll() { return true; } @Override ConcreteType unionWith(ConcreteType other) { return this; } @Override ConcreteType intersectWith(ConcreteType other) { return other; } @Override public String toString() { return "*"; } } /** * Represents an opaque singleton type that is different from any other. * This is used by DisambiguateProperties to rename GETPROP nodes that are * never reached in the TightenTypes flow analysis. This helps subsequent * passes remove unreferenced properties and functions. ID passed to the * constructor should be unique per-instance as it is used for generating * nice, unique, names in {@code toString()}. */ static class ConcreteUniqueType extends ConcreteType { private final int id; ConcreteUniqueType(int id) { this.id = id; Preconditions.checkArgument(id >= 0); } @Override public boolean equals(Object o) { return (o instanceof ConcreteUniqueType) && (id == ((ConcreteUniqueType) o).id); } @Override public int hashCode() { return ConcreteUniqueType.class.hashCode() ^ id; } @Override public String toString() { return "Unique$" + id; } } /** * Factory for function and instance (singleton) types and scopes. It is * important that both function and instance types are singletons because * callers may try to create the same one multiple times, and if multiple * exist, they will not necessarily all receive the same type information. */ interface Factory { /** Returns the singleton concrete type for the given function. */ ConcreteFunctionType createConcreteFunction( Node declaration, StaticScope parent); /** Returns the singleton concrete type for the given instance type. */ ConcreteInstanceType createConcreteInstance(ObjectType instanceType); /** * Returns the already created concrete function type for the given type or * null if none exists. */ ConcreteFunctionType getConcreteFunction(FunctionType function); /** * Returns the already created concrete instance type for the given type or * null if none exists. */ ConcreteInstanceType getConcreteInstance(ObjectType instance); /** * Returns a (nested) scope for the given function. This will include * slots for $call, $return, each parameter, and the slots declared in the * body of the function. */ StaticScope createFunctionScope( Node declaration, StaticScope parent); /** * Returns a scope for the given instance type, nested inside the given * scope of the prototype. This will include slots for each of the * properties on our type. */ StaticScope createInstanceScope(ObjectType instanceType); /** Returns the type registry used by this factory. */ JSTypeRegistry getTypeRegistry(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ShowByPathWarningsGuard.java0000644000175000017500000000444612115204405030233 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import java.util.List; /** * Control whether warnings should be restricted or suppressed for specified * paths. * * @author anatol@google.com (Anatol Pomazau) */ public class ShowByPathWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; /** * Controls whether warnings should be restricted to a specified path or * suppressed within the specified path. */ public enum ShowType { INCLUDE, // Suppress warnings outside the path. EXCLUDE; // Suppress warnings within the path. } private final ByPathWarningsGuard warningsGuard; public ShowByPathWarningsGuard(String checkWarningsOnlyForPath) { this(checkWarningsOnlyForPath, ShowType.INCLUDE); } public ShowByPathWarningsGuard(String[] checkWarningsOnlyForPath) { this(checkWarningsOnlyForPath, ShowType.INCLUDE); } public ShowByPathWarningsGuard(String path, ShowType showType) { this(new String[] { path }, showType); } public ShowByPathWarningsGuard(String[] paths, ShowType showType) { Preconditions.checkArgument(paths != null); Preconditions.checkArgument(showType != null); List pathList = Lists.newArrayList(paths); if (showType == ShowType.INCLUDE) { warningsGuard = ByPathWarningsGuard.exceptPath(pathList, CheckLevel.OFF); } else { warningsGuard = ByPathWarningsGuard.forPath(pathList, CheckLevel.OFF); } } @Override public CheckLevel level(JSError error) { return warningsGuard.level(error); } @Override protected int getPriority() { return warningsGuard.getPriority(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AliasStrings.java0000644000175000017500000003571712115204405026117 0ustar apoapo/* * Copyright 2007 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.Maps; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.CRC32; /** * A {@link Compiler} pass for aliasing strings. String declarations * contribute to garbage collection, which becomes a problem in large * applications. Strings that should be aliased occur many times in the code, * or occur on codepaths that get executed frequently. * */ class AliasStrings extends AbstractPostOrderCallback implements CompilerPass { private static final Logger logger = Logger.getLogger(AliasStrings.class.getName()); /** Prefix for variable names for the aliased strings */ private static final String STRING_ALIAS_PREFIX = "$$S_"; private final AbstractCompiler compiler; private final JSModuleGraph moduleGraph; // Regular expression matcher for a blacklisting strings in aliasing. private Matcher blacklist = null; /** * Strings that can be aliased, or null if all strings except 'undefined' * should be aliased */ private final Set aliasableStrings; private final boolean outputStringUsage; private final SortedMap stringInfoMap = Maps.newTreeMap(); private final Set usedHashedAliases = new LinkedHashSet(); /** * Map from module to the node in that module that should parent any string * variable declarations that have to be moved into that module */ private final Map moduleVarParentMap = new HashMap(); /** package private. This value is AND-ed with the hash function to allow * unit tests to reduce the range of hash values to test collision cases */ long unitTestHashReductionMask = ~0L; /** * Creates an instance. * * @param compiler The compiler * @param moduleGraph The module graph, or null if there are no modules * @param strings Set of strings to be aliased. If null, all strings except * 'undefined' will be aliased. * @param blacklistRegex The regex to blacklist words in aliasing strings. * @param outputStringUsage Outputs all strings and the number of times they * were used in the application to the server log. */ AliasStrings(AbstractCompiler compiler, JSModuleGraph moduleGraph, Set strings, String blacklistRegex, boolean outputStringUsage) { this.compiler = compiler; this.moduleGraph = moduleGraph; this.aliasableStrings = strings; if (blacklistRegex.length() != 0) { this.blacklist = Pattern.compile(blacklistRegex).matcher(""); } else { this.blacklist = null; } this.outputStringUsage = outputStringUsage; } @Override public void process(Node externs, Node root) { logger.fine("Aliasing common strings"); // Traverse the tree and collect strings NodeTraversal.traverse(compiler, root, this); // 1st edit pass: replace some strings with aliases replaceStringsWithAliases(); // 2nd edit pass: add variable declarations for aliased strings. addAliasDeclarationNodes(); if (outputStringUsage) { outputStringUsage(); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isString() && !parent.isGetProp() && !parent.isRegExp()) { String str = n.getString(); // "undefined" is special-cased, since it needs to be used when JS code // is unloading and therefore variable references aren't available. // This is because of a bug in Firefox. if ("undefined".equals(str)) { return; } if (blacklist != null && blacklist.reset(str).find()) { return; } if (aliasableStrings == null || aliasableStrings.contains(str)) { StringOccurrence occurrence = new StringOccurrence(n, parent); StringInfo info = getOrCreateStringInfo(str); info.occurrences.add(occurrence); info.numOccurrences++; if (t.inGlobalScope() || isInThrowExpression(n)) { info.numOccurrencesInfrequentlyExecuted++; } // The current module. JSModule module = t.getModule(); if (info.numOccurrences != 1) { // Check whether the current module depends on the module containing // the declaration. if (module != null && info.moduleToContainDecl != null && module != info.moduleToContainDecl && !moduleGraph.dependsOn(module, info.moduleToContainDecl)) { // We need to declare this string in the deepest module in the // module dependency graph that both of these modules depend on. module = moduleGraph.getDeepestCommonDependency( module, info.moduleToContainDecl); } else { // use the previously saved insertion location. return; } } Node varParent = moduleVarParentMap.get(module); if (varParent == null) { varParent = compiler.getNodeForCodeInsertion(module); moduleVarParentMap.put(module, varParent); } info.moduleToContainDecl = module; info.parentForNewVarDecl = varParent; info.siblingToInsertVarDeclBefore = varParent.getFirstChild(); } } } /** * Looks up the {@link StringInfo} object for a JavaScript string. Creates * it if necessary. */ private StringInfo getOrCreateStringInfo(String string) { StringInfo info = stringInfoMap.get(string); if (info == null) { info = new StringInfo(stringInfoMap.size()); stringInfoMap.put(string, info); } return info; } /** * Is the {@link Node} currently within a 'throw' expression? */ private static boolean isInThrowExpression(Node n) { // Look up the traversal stack to find a THROW node for (Node ancestor : n.getAncestors()) { switch (ancestor.getType()) { case Token.THROW: return true; case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: case Token.SWITCH: case Token.CASE: case Token.DEFAULT_CASE: case Token.BLOCK: case Token.SCRIPT: case Token.FUNCTION: case Token.TRY: case Token.CATCH: case Token.RETURN: case Token.EXPR_RESULT: // early exit - these nodes types can't be within a THROW return false; } } return false; } /** * Replace strings with references to alias variables. */ private void replaceStringsWithAliases() { for (Entry entry : stringInfoMap.entrySet()) { String literal = entry.getKey(); StringInfo info = entry.getValue(); if (shouldReplaceWithAlias(literal, info)) { for (StringOccurrence occurrence : info.occurrences) { replaceStringWithAliasName( occurrence, info.getVariableName(literal), info); } } } } /** * Creates a var declaration for each aliased string. Var declarations are * inserted as close to the first use of the string as possible. */ private void addAliasDeclarationNodes() { for (Entry entry : stringInfoMap.entrySet()) { StringInfo info = entry.getValue(); if (!info.isAliased) { continue; } String alias = info.getVariableName(entry.getKey()); Node var = IR.var(IR.name(alias), IR.string(entry.getKey())); if (info.siblingToInsertVarDeclBefore == null) { info.parentForNewVarDecl.addChildToFront(var); } else { info.parentForNewVarDecl.addChildBefore( var, info.siblingToInsertVarDeclBefore); } compiler.reportCodeChange(); } } /** * Dictates the policy for replacing a string with an alias. * * @param str The string literal * @param info Accumulated information about a string */ private static boolean shouldReplaceWithAlias(String str, StringInfo info) { // Optimize for application performance. If there are any uses of the // string that are not 'infrequent uses', assume they are frequent and // create an alias. if (info.numOccurrences > info.numOccurrencesInfrequentlyExecuted) { return true; } // Optimize for code size. Are aliases smaller than strings? // // This logic optimizes for the size of uncompressed code, but it tends to // get good results for the size of the gzipped code too. // // gzip actually prefers that strings are not aliased - it compresses N // string literals better than 1 string literal and N+1 short variable // names, provided each string is within 32k of the previous copy. We // follow the uncompressed logic as insurance against there being multiple // strings more than 32k apart. int sizeOfLiteral = 2 + str.length(); int sizeOfStrings = info.numOccurrences * sizeOfLiteral; int sizeOfVariable = 3; // '6' comes from: 'var =;' in var XXX="..."; int sizeOfAliases = 6 + sizeOfVariable + sizeOfLiteral // declaration + info.numOccurrences * sizeOfVariable; // + uses return sizeOfAliases < sizeOfStrings; } /** * Replaces a string literal with a reference to the string's alias variable. */ private void replaceStringWithAliasName(StringOccurrence occurrence, String name, StringInfo info) { occurrence.parent.replaceChild(occurrence.node, IR.name(name)); info.isAliased = true; compiler.reportCodeChange(); } /** * Outputs a log of all strings used more than once in the code. */ private void outputStringUsage() { StringBuilder sb = new StringBuilder("Strings used more than once:\n"); for (Entry stringInfoEntry : stringInfoMap.entrySet()) { StringInfo info = stringInfoEntry.getValue(); if (info.numOccurrences > 1) { sb.append(info.numOccurrences); sb.append(": "); sb.append(stringInfoEntry.getKey()); sb.append('\n'); } } // TODO(user): Make this save to file OR output to the application logger.fine(sb.toString()); } // ------------------------------------------------------------------------- /** * A class that holds the location of a single JavaScript string literal */ private static final class StringOccurrence { final Node node; final Node parent; StringOccurrence(Node node, Node parent) { this.node = node; this.parent = parent; } } /** * A class that holds information about a JavaScript string that might become * aliased. */ private final class StringInfo { final int id; boolean isAliased; // set to 'true' when reference to alias created final List occurrences; int numOccurrences; int numOccurrencesInfrequentlyExecuted; JSModule moduleToContainDecl; Node parentForNewVarDecl; Node siblingToInsertVarDeclBefore; String aliasName; StringInfo(int id) { this.id = id; this.occurrences = new ArrayList(); this.isAliased = false; } /** Returns the JS variable name to be substituted for this string. */ String getVariableName(String stringLiteral) { if (aliasName == null) { aliasName = encodeStringAsIdentifier(STRING_ALIAS_PREFIX, stringLiteral); } return aliasName; } /** * Returns a legal identifier that uniquely characterizes string 's'. * * We want the identifier to be a function of the string value because that * makes the identifiers stable as the program is changed. * * The digits of a good hash function would be adequate, but for short * strings the following algorithm is easier to work with for unit tests. * * ASCII alphanumerics are mapped to themselves. Other characters are * mapped to $XXX or $XXX_ where XXX is a variable number of hex digits. * The underscore is inserted as necessary to avoid ambiguity when the * character following is a hex digit. E.g. '\n1' maps to '$a_1', * distinguished by the underscore from '\u00A1' which maps to '$a1'. * * If the string is short enough, this is sufficient. Longer strings are * truncated after encoding an initial prefix and appended with a hash * value. */ String encodeStringAsIdentifier(String prefix, String s) { // Limit to avoid generating very long identifiers final int MAX_LIMIT = 20; final int length = s.length(); final int limit = Math.min(length, MAX_LIMIT); StringBuilder sb = new StringBuilder(); sb.append(prefix); boolean protectHex = false; for (int i = 0; i < limit; i++) { char ch = s.charAt(i); if (protectHex) { if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')) { // toHexString generate lowercase sb.append('_'); } protectHex = false; } if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { sb.append(ch); } else { sb.append('$'); sb.append(Integer.toHexString(ch)); protectHex = true; } } if (length == limit) { return sb.toString(); } // The identifier is not unique because we omitted part, so add a // checksum as a hashcode. CRC32 crc32 = new CRC32(); crc32.update(s.getBytes()); long hash = crc32.getValue() & unitTestHashReductionMask; sb.append('_'); sb.append(Long.toHexString(hash)); String encoded = sb.toString(); if (!usedHashedAliases.add(encoded)) { // A collision has been detected (which is very rare). Use the sequence // id to break the tie. This means that the name is no longer invariant // across source code changes and recompilations. encoded += "_" + id; } return encoded; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AnonymousFunctionNamingPolicy.java0000644000175000017500000000376012115204405031515 0ustar apoapo/* * 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; /** * Strategies for how to do naming of anonymous functions that occur as * r-values in assignments and variable declarations. */ public enum AnonymousFunctionNamingPolicy { /** Don't give anonymous functions names */ OFF(null), /** * Generates names that are based on the left-hand side of the assignment. * Runs after variable and property renaming, so that the generated names * will be short and obfuscated. * @see NameAnonymousFunctions */ UNMAPPED(new char[] { NameAnonymousFunctions.DELIMITER }), /** * Generates short unique names and provides a mapping from them back to a * more meaningful name that's based on the left-hand side of the * assignment. * @see NameAnonymousFunctionsMapped */ MAPPED(new char[] { NameAnonymousFunctionsMapped.PREFIX }), ; private final char[] reservedCharacters; AnonymousFunctionNamingPolicy(char[] reservedCharacters) { this.reservedCharacters = reservedCharacters; } /** * Gets characters that are reserved for use in anonymous function names and * can't be used in variable or property names. * @return reserved characters or null if no characters are reserved */ public char[] getReservedCharacters() { // TODO(user) - for MAPPED, only the first character is reserved which // can be used to further optimize return reservedCharacters; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/LiveVariablesAnalysis.java0000644000175000017500000002263512115204405027743 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.LatticeElement; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.BitSet; import java.util.List; import java.util.Set; /** * Compute the "liveness" of all local variables. A variable is "live" at a * point of a program if the value it is currently holding might be read later. * Otherwise, the variable is considered "dead" if we know for sure that it will * no longer be read. Dead variables are candidates for dead assignment * elimination and variable name sharing. The worst case safe assumption is to * assume that all variables are live. In that case, we will have no opportunity * for optimizations. This is especially the case within a TRY block when an * assignment is not guaranteed to take place. We bail out by assuming that * all variables are live. *

      * Due to the possibility of inner functions and closures, certain "local" * variables can escape the function. These variables will be considered as * global and they can be retrieved with {@link #getEscapedLocals()}. * */ class LiveVariablesAnalysis extends DataFlowAnalysis { // 100 = ((# of original Power Rangers) ^ // (# years of Warren Harding in office)) * // (# of Ninja Turtles) static final int MAX_VARIABLES_TO_ANALYZE = 100; public static final String ARGUMENT_ARRAY_ALIAS = "arguments"; private static class LiveVariableJoinOp implements JoinOp { @Override public LiveVariableLattice apply(List in) { LiveVariableLattice result = new LiveVariableLattice(in.get(0)); for (int i = 1; i < in.size(); i++) { result.liveSet.or(in.get(i).liveSet); } return result; } } /** * The lattice that stores the liveness of all local variables at a given * point in the program. The whole lattice is the power set of all local * variables and a variable is live if it is in the set. */ static class LiveVariableLattice implements LatticeElement { private final BitSet liveSet; /** * @param numVars Number of all local variables. */ private LiveVariableLattice(int numVars) { this.liveSet = new BitSet(numVars); } private LiveVariableLattice(LiveVariableLattice other) { Preconditions.checkNotNull(other); this.liveSet = (BitSet) other.liveSet.clone(); } @Override public boolean equals(Object other) { Preconditions.checkNotNull(other); return (other instanceof LiveVariableLattice) && this.liveSet.equals(((LiveVariableLattice) other).liveSet); } public boolean isLive(Var v) { Preconditions.checkNotNull(v); return liveSet.get(v.index); } public boolean isLive(int index) { return liveSet.get(index); } @Override public String toString() { return liveSet.toString(); } @Override public int hashCode() { return liveSet.hashCode(); } } // The scope of the function that we are analyzing. private final Scope jsScope; private final Set escaped; LiveVariablesAnalysis(ControlFlowGraph cfg, Scope jsScope, AbstractCompiler compiler) { super(cfg, new LiveVariableJoinOp()); this.jsScope = jsScope; this.escaped = Sets.newHashSet(); computeEscaped(jsScope, escaped, compiler); } public Set getEscapedLocals() { return escaped; } public int getVarIndex(String var) { return jsScope.getVar(var).index; } @Override boolean isForward() { return false; } @Override LiveVariableLattice createEntryLattice() { return new LiveVariableLattice(jsScope.getVarCount()); } @Override LiveVariableLattice createInitialEstimateLattice() { return new LiveVariableLattice(jsScope.getVarCount()); } @Override LiveVariableLattice flowThrough(Node node, LiveVariableLattice input) { final BitSet gen = new BitSet(input.liveSet.size()); final BitSet kill = new BitSet(input.liveSet.size()); // Make kills conditional if the node can end abruptly by an exception. boolean conditional = false; List> edgeList = getCfg().getOutEdges(node); for (DiGraphEdge edge : edgeList) { if (Branch.ON_EX.equals(edge.getValue())) { conditional = true; } } computeGenKill(node, gen, kill, conditional); LiveVariableLattice result = new LiveVariableLattice(input); // L_in = L_out - Kill + Gen result.liveSet.andNot(kill); result.liveSet.or(gen); return result; } /** * Computes the GEN and KILL set. * * @param n Root node. * @param gen Local variables that are live because of the instruction at * {@code n} will be added to this set. * @param kill Local variables that are killed because of the instruction at * {@code n} will be added to this set. * @param conditional {@code true} if any assignments encountered are * conditionally executed. These assignments might not kill a variable. */ private void computeGenKill(Node n, BitSet gen, BitSet kill, boolean conditional) { switch (n.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.FUNCTION: return; case Token.WHILE: case Token.DO: case Token.IF: computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); if (lhs.isVar()) { // for(var x in y) {...} lhs = lhs.getLastChild(); } if (lhs.isName()) { addToSetIfLocal(lhs, kill); addToSetIfLocal(lhs, gen); } else { computeGenKill(lhs, gen, kill, conditional); } // rhs is executed only once so we don't go into it every loop. } return; case Token.VAR: for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.hasChildren()) { computeGenKill(c.getFirstChild(), gen, kill, conditional); if (!conditional) { addToSetIfLocal(c, kill); } } } return; case Token.AND: case Token.OR: computeGenKill(n.getFirstChild(), gen, kill, conditional); // May short circuit. computeGenKill(n.getLastChild(), gen, kill, true); return; case Token.HOOK: computeGenKill(n.getFirstChild(), gen, kill, conditional); // Assume both sides are conditional. computeGenKill(n.getFirstChild().getNext(), gen, kill, true); computeGenKill(n.getLastChild(), gen, kill, true); return; case Token.NAME: if (isArgumentsName(n)) { markAllParametersEscaped(); } else { addToSetIfLocal(n, gen); } return; default: if (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) { Node lhs = n.getFirstChild(); if (!conditional) { addToSetIfLocal(lhs, kill); } if (!n.isAssign()) { // assignments such as a += 1 reads a. addToSetIfLocal(lhs, gen); } computeGenKill(lhs.getNext(), gen, kill, conditional); } else { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { computeGenKill(c, gen, kill, conditional); } } return; } } private void addToSetIfLocal(Node node, BitSet set) { Preconditions.checkState(node.isName()); String name = node.getString(); if (!jsScope.isDeclared(name, false)) { return; } Var var = jsScope.getVar(name); if (!escaped.contains(var)) { set.set(var.index); } } /** * Give up computing liveness of formal parameter by putting all the parameter * names in the escaped set. */ void markAllParametersEscaped() { Node lp = jsScope.getRootNode().getFirstChild().getNext(); for(Node arg = lp.getFirstChild(); arg != null; arg = arg.getNext()) { escaped.add(jsScope.getVar(arg.getString())); } } private boolean isArgumentsName(Node n) { if (!n.isName()|| !n.getString().equals(ARGUMENT_ARRAY_ALIAS) || jsScope.isDeclared(ARGUMENT_ARRAY_ALIAS, false)) { return false; } else { return true; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameReferenceGraph.java0000644000175000017500000002734612115204405027174 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.DefinitionsRemover.AssignmentDefinition; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.DefinitionsRemover.NamedFunctionDefinition; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; /** * A graph represents all the referencing of global names in the program. In * other words, it is a call and variable-name graph. * *

      The NameReferenceGraph G for a program P is a directed graph G = (V, E) * where: * *

      V ({@link Name}) represents all global names in P and E = (v, v'), v and * v' in V ({@link Reference} represents a reference use or definition from the * name v to v' in P. * *

      There are two core results we are trying to compute. The first being able * to precisely identify the function body at any given call site with * {@link #getReferencesAt(Node)}. * *

      The second result come directly from the previous one. The directed edge * provides us with dependency information. If A->B, B might be needed (in this * module) if A is needed (in this module). The converse of the this result is * more useful. B is not needed if A is not needed. * */ class NameReferenceGraph extends LinkedDirectedGraph implements DefinitionProvider { // This is the key result of the name graph. Given a node in the AST, this map // will give us the Reference edges. For example a CALL node will map to a // list of possible call edge destinations. private final Multimap referenceMap = HashMultimap.create(); // Given a qualified name, provides the Name object. private Map nameMap = Maps.newHashMap(); // The following are some implicit nodes of the graph. // If we have a call site that we absolutely have no idea what variable it // it calls or reference, we'd point it to UNKNOWN. final Name UNKNOWN; // Represents the "main" global block as well as externs. final Name MAIN; // The implicit "window" object. final Name WINDOW; final AbstractCompiler compiler; public NameReferenceGraph(AbstractCompiler compiler) { super(true, true); this.compiler = compiler; // Initialize builtins. UNKNOWN = new Name("{UNKNOWN}", true); UNKNOWN.isAliased = true; UNKNOWN.type = compiler.getTypeRegistry().getNativeType( JSTypeNative.NO_TYPE); this.createNode(UNKNOWN); MAIN = new Name("{Global Main}", true); this.createNode(MAIN); WINDOW = new Name("window", true); this.createNode(WINDOW); } public Name defineNameIfNotExists(String name, boolean isExtern) { Name symbol = null; if (nameMap.containsKey(name)) { // This is a re-declaration. symbol = nameMap.get(name); } else { symbol = new Name(name, isExtern); nameMap.put(name, symbol); createNode(symbol); } return symbol; } /** * Retrieves a list of all possible Names that this site is referring to. */ public List getReferencesAt(Node site) { Preconditions.checkArgument( site.isGetProp() || site.isName()); List result = new ArrayList(); for (Name target : referenceMap.get(site)) { result.add(target); } return result; } @Override public Collection getDefinitionsReferencedAt(Node useSite) { List nameRefs = getReferencesAt(useSite); if (nameRefs.isEmpty()) { return null; } List result = Lists.newArrayList(); for (Name nameRef : nameRefs) { List decls = nameRef.getDeclarations(); if (!decls.isEmpty()) { result.addAll(decls); } } if (!result.isEmpty()) { return result; } else { return null; } } public Name getSymbol(String name) { return nameMap.get(name); } @Override public GraphNode createNode(Name value) { nameMap.put(value.qName, value); return super.createNode(value); } @Override public void connect(Name src, Reference ref, Name dest) { super.connect(src, ref, dest); referenceMap.put(ref.site, dest); } /** * Represents function or variable names that can be referenced globally. */ class Name { // Full name private final String qName; private JSType type; // A list (re)declarations private List declarations = Lists.newLinkedList(); final boolean isExtern; private boolean isExported = false; private boolean isAliased = false; // Function invocations that use ".call" and ".apply" syntax may prevent // several of the possible optimizations. We keep track of all functions // invoked in this way so those passes can exclude them. // Ex: // some_func.call(some_obj, 1, 2 , 3); // The name graph does not currently recognize this as a call to some_func. // This Set is meant to keep track of such occurrence until the name graph // becomes aware of those cases. private boolean exposedToCallOrApply = false; public Name(String qName, boolean isExtern) { this.qName = qName; this.isExtern = isExtern; int lastDot = qName.lastIndexOf('.'); String name = (lastDot == -1) ? qName : qName.substring(lastDot + 1); this.isExported = compiler.getCodingConvention().isExported(name); this.type = compiler.getTypeRegistry().getNativeType( JSTypeNative.UNKNOWN_TYPE); } public JSType getType() { return type; } public void setType(JSType type) { this.type = type; } public List getDeclarations() { return declarations; } public void addAssignmentDeclaration(Node node) { declarations.add(new AssignmentDefinition(node, isExtern)); } public void addFunctionDeclaration(Node node) { declarations.add(new NamedFunctionDefinition(node, isExtern)); } public boolean isExtern() { return isExtern; } public void markExported() { this.isExported = true; } public boolean isExported() { return isExported; } /** Removes all of the declarations of this name. */ public final void remove() { for (Definition declaration : getDeclarations()) { declaration.remove(); } } /** * @return {@code} True if this name has been dereferenced. Removing from * the program or the module is no longer safe unless further analysis * can prove otherwise. */ public boolean isAliased() { return isAliased; } public void setAliased(boolean isAliased) { this.isAliased = isAliased; } public boolean hasSideEffect() { return isCallable(); } public String getQualifiedName() { return qName; } /** * @return The short property name of this object if it is a property, else * {@code null}. */ public String getPropertyName() { int lastIndexOfDot = qName.lastIndexOf('.'); if (lastIndexOfDot == -1) { return null; } else { return qName.substring(lastIndexOfDot + 1); } } public boolean isCallable() { return type.canBeCalled(); } public boolean exposedToCallOrApply() { return exposedToCallOrApply; } public void markExposedToCallOrApply() { exposedToCallOrApply = true; } @Override public String toString() { return qName + " : " + type; } @Override public int hashCode() { return qName.hashCode(); } /** * Return true if it's safe to change the signature of the function * references by this name. It is safe to change the signature if the Name * is: *

        *
      • callable
      • *
      • not an extern
      • *
      • not been aliased
      • *
      • not been exported
      • *
      • Referred by call or apply functions
      • *
      • The function uses the arguments property
      • *
      * * @return true if it's safe to change the signature of the name. */ public boolean canChangeSignature() { // Ignore anything that is extern as they should not be changed. // Also skip over any non-function names. Finally if a function has been // alias, we don't know all of its callers and should not optimize. // // Also, if the function is called using .call or .apply, we don't try to // optimize those call because the name graph does not give us enough // information on the parameters. // TODO(user) We'll be able to remove the check for call or apply once // the name graph handles those call. The issue for now is that those // calls aren't edges in the graph, so we don't have enough information to // know if it's safe to change the method's signature. return !(isExtern() || !isCallable() || isAliased() || isExported() || exposedToCallOrApply() || nameUsesArgumentsProperty()); } /** * Returns true if the the arguments property is used in any of the function * definition. * Ex. function foo(a,b,c) {return arguments.size;}; * @return True is arguments is present in one of the definitions. */ private boolean nameUsesArgumentsProperty() { for (Definition definition : getDeclarations()) { if (NodeUtil.isVarArgsFunction(definition.getRValue())) { return true; } } return false; } } /** * A reference site for a function or a variable reference. It can be a * reference use or an assignment to that name. */ static class Reference { // The node that references the name. public final Node site; // Parent pointer. public final Node parent; private JSModule module = null; // A reference is unknown because we don't know the object's type. // If A.x->B.y in the name graph and the edge is unknown. It implies // A.x() reference to someObject.y and B.y MAY be the site. private boolean isUnknown = false; public Reference(Node site, Node parent) { this.site = site; this.parent = parent; } public boolean isUnknown() { return isUnknown; } public void setUnknown(boolean isUnknown) { this.isUnknown = isUnknown; } public JSModule getModule() { return module; } public void setModule(JSModule module) { this.module = module; } boolean isCall() { return site.isCall(); } /** * Get accessor for retrieving the actual node corresponding to the * reference. * * @return node representing the access/reference/call site */ public Node getSite() { return site; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MarkNoSideEffectCalls.java0000644000175000017500000001407012115204405027571 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.List; import java.util.Set; /** * Set the NoSideEffects property for function and constructor calls * that refer to functions that are known to have no side effects. * Current implementation relies on @nosideeffects annotations at * function definition sites; eventually we should traverse function * bodies to determine if they have side effects. * */ class MarkNoSideEffectCalls implements CompilerPass { static final DiagnosticType INVALID_NO_SIDE_EFFECT_ANNOTATION = DiagnosticType.error( "JSC_INVALID_NO_SIDE_EFFECT_ANNOTATION", "@nosideeffects may only appear in externs files."); private final AbstractCompiler compiler; // Left hand side expression associated with a function node that // has a @nosideeffects annotation. private final Set noSideEffectFunctionNames; MarkNoSideEffectCalls(AbstractCompiler compiler) { this.compiler = compiler; this.noSideEffectFunctionNames = Sets.newHashSet(); } @Override public void process(Node externs, Node root) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); // Gather the list of function nodes that have @nosideeffects annotations. // For use by SetNoSideEffectCallProperty. NodeTraversal.traverse( compiler, externs, new GatherNoSideEffectFunctions(true)); NodeTraversal.traverse( compiler, root, new GatherNoSideEffectFunctions(false)); NodeTraversal.traverse(compiler, root, new SetNoSideEffectCallProperty(defFinder)); } /** * Determines if the type of the value of the RHS expression can * be a function node. */ private static boolean definitionTypeContainsFunctionType(Definition def) { Node rhs = def.getRValue(); if (rhs == null) { return true; } switch (rhs.getType()) { case Token.ASSIGN: case Token.AND: case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.FUNCTION: case Token.HOOK: case Token.NAME: case Token.NEW: case Token.OR: return true; default: return false; } } /** * Get the value of the @nosideeffects annotation stored in the * doc info. */ private static boolean hasNoSideEffectsAnnotation(Node node) { JSDocInfo docInfo = node.getJSDocInfo(); return docInfo != null && docInfo.isNoSideEffects(); } /** * Gather function nodes that have @nosideeffects annotations. */ private class GatherNoSideEffectFunctions extends AbstractPostOrderCallback { private final boolean inExterns; GatherNoSideEffectFunctions(boolean inExterns) { this.inExterns = inExterns; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { if (!inExterns && hasNoSideEffectsAnnotation(node)) { traversal.report(node, INVALID_NO_SIDE_EFFECT_ANNOTATION); } if (node.isGetProp()) { if (parent.isExprResult() && hasNoSideEffectsAnnotation(node)) { noSideEffectFunctionNames.add(node); } } else if (node.isFunction()) { // The annotation may attached to the function node, the // variable declaration or assignment expression. boolean hasAnnotation = hasNoSideEffectsAnnotation(node); List nameNodes = Lists.newArrayList(); nameNodes.add(node.getFirstChild()); if (parent.isName()) { Node gramp = parent.getParent(); if (gramp.isVar() && gramp.hasOneChild() && hasNoSideEffectsAnnotation(gramp)) { hasAnnotation = true; } nameNodes.add(parent); } else if (parent.isAssign()) { if (hasNoSideEffectsAnnotation(parent)) { hasAnnotation = true; } nameNodes.add(parent.getFirstChild()); } if (hasAnnotation) { noSideEffectFunctionNames.addAll(nameNodes); } } } } /** * Set the no side effects property for CALL and NEW nodes that * refer to function names that are known to have no side effects. */ private class SetNoSideEffectCallProperty extends AbstractPostOrderCallback { private final SimpleDefinitionFinder defFinder; SetNoSideEffectCallProperty(SimpleDefinitionFinder defFinder) { this.defFinder = defFinder; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { if (!node.isCall() && !node.isNew()) { return; } Collection definitions = defFinder.getDefinitionsReferencedAt(node.getFirstChild()); if (definitions == null) { return; } for (Definition def : definitions) { Node lValue = def.getLValue(); Preconditions.checkNotNull(lValue); if (!noSideEffectFunctionNames.contains(lValue) && definitionTypeContainsFunctionType(def)) { return; } } node.setSideEffectFlags(Node.NO_SIDE_EFFECTS); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/StrictModeCheck.java0000644000175000017500000002053312115204405026515 0ustar apoapo/* * 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.Lists; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Set; /** * Checks that the code obeys the static restrictions of strict mode: *
        *
      1. No use of "with". *
      2. No deleting variables, functions, or arguments. *
      3. No re-declarations or assignments of "eval" or arguments. *
      4. No use of "eval" (optional check for Caja). *
      * */ class StrictModeCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType UNKNOWN_VARIABLE = DiagnosticType.warning( "JSC_UNKNOWN_VARIABLE", "unknown variable {0}"); static final DiagnosticType EVAL_USE = DiagnosticType.error( "JSC_EVAL_USE", "\"eval\" cannot be used in Caja"); static final DiagnosticType EVAL_DECLARATION = DiagnosticType.warning( "JSC_EVAL_DECLARATION", "\"eval\" cannot be redeclared in ES5 strict mode"); static final DiagnosticType EVAL_ASSIGNMENT = DiagnosticType.warning( "JSC_EVAL_ASSIGNMENT", "the \"eval\" object cannot be reassigned in ES5 strict mode"); static final DiagnosticType ARGUMENTS_DECLARATION = DiagnosticType.warning( "JSC_ARGUMENTS_DECLARATION", "\"arguments\" cannot be redeclared in ES5 strict mode"); static final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning( "JSC_ARGUMENTS_ASSIGNMENT", "the \"arguments\" object cannot be reassigned in ES5 strict mode"); static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning( "JSC_DELETE_VARIABLE", "variables, functions, and arguments cannot be deleted in " + "ES5 strict mode"); static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error( "JSC_ILLEGAL_NAME", "identifiers ending in '__' cannot be used in Caja"); static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning( "JSC_DUPLICATE_OBJECT_KEY", "object literals cannot contain duplicate keys in ES5 strict mode"); static final DiagnosticType BAD_FUNCTION_DECLARATION = DiagnosticType.error( "JSC_BAD_FUNCTION_DECLARATION", "functions can only be declared at top level or immediately within " + "another function in ES5 strict mode"); private final AbstractCompiler compiler; private final boolean noVarCheck; private final boolean noCajaChecks; StrictModeCheck(AbstractCompiler compiler) { this(compiler, false, false); } StrictModeCheck( AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { this.compiler = compiler; this.noVarCheck = noVarCheck; this.noCajaChecks = noCajaChecks; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); NodeTraversal.traverse(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { checkFunctionUse(t, n); } else if (n.isName()) { if (!isDeclaration(n)) { checkNameUse(t, n); } } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** Checks that the function is used legally. */ private void checkFunctionUse(NodeTraversal t, Node n) { if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { t.report(n, BAD_FUNCTION_DECLARATION); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. if (noCajaChecks) { t.report(n, EVAL_ASSIGNMENT); } } } } /** Checks that variables, functions, and arguments are not deleted. */ private void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys are valid. */ private void checkObjectLiteral(NodeTraversal t, Node n) { Set getters = Sets.newHashSet(); Set setters = Sets.newHashSet(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!noCajaChecks && key.getString().endsWith("__")) { t.report(key, ILLEGAL_NAME); } if (!key.isSetterDef()) { // normal property and getter cases if (getters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { getters.add(key.getString()); } } if (!key.isGetterDef()) { // normal property and setter cases if (setters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { setters.add(key.getString()); } } } } /** Checks that label names are valid. */ private void checkLabel(NodeTraversal t, Node n) { if (n.getFirstChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getFirstChild(), ILLEGAL_NAME); } } } /** Checks that are performed on non-extern code only. */ private class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((n.isName()) && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkProperty(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } else if (n.getString().endsWith("__")) { if (!noCajaChecks) { t.report(n, ILLEGAL_NAME); } } } /** Checks for illegal property accesses. */ private void checkProperty(NodeTraversal t, Node n) { if (n.getLastChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getLastChild(), ILLEGAL_NAME); } } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ControlStructureCheck.java0000644000175000017500000000405012115204405027775 0ustar apoapo/* * 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.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Check for usage of 'with'. * */ class ControlStructureCheck implements HotSwapCompilerPass { private final AbstractCompiler compiler; static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The use of the 'with' structure should be avoided."); ControlStructureCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { check(root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { check(scriptRoot); } /** * Reports errors for any invalid use of control structures. * * @param node Current node to check. */ private void check(Node node) { switch (node.getType()) { case Token.WITH: JSDocInfo info = node.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { report(node, USE_OF_WITH); } break; } for (Node bChild = node.getFirstChild(); bChild != null;) { Node next = bChild.getNext(); check(bChild); bChild = next; } } private void report(Node n, DiagnosticType error) { compiler.report(JSError.make(n.getSourceFileName(), n, error)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ErrorFormat.java0000644000175000017500000000414212115204405025742 0ustar apoapo/* * 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.javascript.jscomp.SourceExcerptProvider.SourceExcerpt; /** * Error formats available. */ public enum ErrorFormat { LEGACY { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { VerboseMessageFormatter formatter = new VerboseMessageFormatter(source); formatter.setColorize(colorize); return formatter; } }, SINGLELINE { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = new LightweightMessageFormatter( source); formatter.setColorize(colorize); return formatter; } }, MULTILINE { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = new LightweightMessageFormatter( source, SourceExcerpt.REGION); formatter.setColorize(colorize); return formatter; } }, SOURCELESS { @Override public MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize) { LightweightMessageFormatter formatter = LightweightMessageFormatter.withoutSource(); formatter.setColorize(colorize); return formatter; } }; /** * Convert to a concrete formatter. */ public abstract MessageFormatter toFormatter( SourceExcerptProvider source, boolean colorize); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CleanupPasses.java0000644000175000017500000001024512115204405026247 0ustar apoapo/* * Copyright 2012 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.DefaultPassConfig.HotSwapPassFactory; import com.google.javascript.jscomp.GlobalVarReferenceMap.GlobalVarRefCleanupPass; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import java.util.List; /** * Provides passes that should be run before hot-swap/incremental builds. * * @author tylerg@google.com (Tyler Goodwin) */ class CleanupPasses extends PassConfig { private State state; public CleanupPasses(CompilerOptions options) { super(options); } @Override protected List getChecks() { List checks = Lists.newArrayList(); checks.add(fieldCleanupPassFactory); checks.add(scopeCleanupPassFactory); checks.add(globalVarRefCleanupPassFactory); return checks; } @Override protected State getIntermediateState() { return state; } @Override protected List getOptimizations() { return ImmutableList.of(); } @Override protected void setIntermediateState(State state) { this.state = state; } final PassFactory fieldCleanupPassFactory = new HotSwapPassFactory("FieldCleaupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new FieldCleanupPass(compiler); } }; final PassFactory scopeCleanupPassFactory = new HotSwapPassFactory("ScopeCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new MemoizedScopeCleanupPass(compiler); } }; final PassFactory globalVarRefCleanupPassFactory = new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new GlobalVarRefCleanupPass(compiler); } }; /** * A CleanupPass implementation that will remove stored scopes from the * MemoizedScopeCreator of the compiler instance for a the hot swapped script. *

      * This pass will also clear out Source Nodes of Function Types declared on * Vars tracked by MemoizedScopeCreator */ static class MemoizedScopeCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public MemoizedScopeCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ScopeCreator creator = compiler.getTypedScopeCreator(); if (creator instanceof MemoizedScopeCreator) { MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator; String newSrc = scriptRoot.getSourceFileName(); for (Var var : scopeCreator.getAllSymbols()) { JSType type = var.getType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null && newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) { fnType.setSource(null); } } } scopeCreator.removeScopesForScript(originalRoot.getSourceFileName()); } } @Override public void process(Node externs, Node root) { // MemoizedScopeCleanupPass should not do work during process. } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ProcessDefines.java0000644000175000017500000004401112115204405026413 0ustar apoapo/* * Copyright 2007 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.GlobalNamespace.Name; import com.google.javascript.jscomp.GlobalNamespace.Ref; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import java.text.MessageFormat; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Set; /** * Process variables annotated as {@code @define}. A define is * a special constant that may be overridden by later files and * manipulated by the compiler, much like C preprocessor {@code #define}s. * * @author nicksantos@google.com (Nick Santos) */ class ProcessDefines implements CompilerPass { /** * Defines in this set will not be flagged with "unknown define" warnings. * There are legacy flags that always set these defines, even when they * might not be in the binary. */ private static final Set KNOWN_DEFINES = Sets.newHashSet("COMPILED"); private final AbstractCompiler compiler; private final Map dominantReplacements; private GlobalNamespace namespace = null; // Warnings static final DiagnosticType UNKNOWN_DEFINE_WARNING = DiagnosticType.warning( "JSC_UNKNOWN_DEFINE_WARNING", "unknown @define variable {0}"); // Errors static final DiagnosticType INVALID_DEFINE_TYPE_ERROR = DiagnosticType.error( "JSC_INVALID_DEFINE_TYPE_ERROR", "@define tag only permits literal types"); static final DiagnosticType INVALID_DEFINE_INIT_ERROR = DiagnosticType.error( "JSC_INVALID_DEFINE_INIT_ERROR", "illegal initialization of @define variable {0}"); static final DiagnosticType NON_GLOBAL_DEFINE_INIT_ERROR = DiagnosticType.error( "JSC_NON_GLOBAL_DEFINE_INIT_ERROR", "@define variable {0} assignment must be global"); static final DiagnosticType DEFINE_NOT_ASSIGNABLE_ERROR = DiagnosticType.error( "JSC_DEFINE_NOT_ASSIGNABLE_ERROR", "@define variable {0} cannot be reassigned due to code at {1}."); private static final MessageFormat REASON_DEFINE_NOT_ASSIGNABLE = new MessageFormat("line {0} of {1}"); /** * Create a pass that overrides define constants. * * TODO(nicksantos): Write a builder to help JSCompiler induce * {@code replacements} from command-line flags * * @param replacements A hash table of names of defines to their replacements. * All replacements must be literals. */ ProcessDefines(AbstractCompiler compiler, Map replacements) { this.compiler = compiler; dominantReplacements = replacements; } /** * Injects a pre-computed global namespace, so that the same namespace * can be re-used for multiple check passes. Returns {@code this} for * easy chaining. */ ProcessDefines injectNamespace(GlobalNamespace namespace) { this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } overrideDefines(collectDefines(root, namespace)); } private void overrideDefines(Map allDefines) { boolean changed = false; for (Map.Entry def : allDefines.entrySet()) { String defineName = def.getKey(); DefineInfo info = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation List allDefines = Lists.newArrayList(); for (Name name : namespace.getNameIndex().values()) { Ref decl = name.getDeclaration(); if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( decl.getSourceName(), decl.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else { for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map assignableDefines; private final Map allDefines; private final Map allRefInfo; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque assignAllowed; CollectDefines(AbstractCompiler compiler, List listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap(); assignableDefines = Maps.newHashMap(); assignAllowed = new ArrayDeque(); assignAllowed.push(1); // Create a map of references to defines keyed by node for easy lookup allRefInfo = Maps.newHashMap(); for (Name name : listOfDefines) { Ref decl = name.getDeclaration(); if (decl != null) { allRefInfo.put(decl.node, new RefInfo(decl, name)); } for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } // If there's a TWIN def, only put one of the twins in. if (ref.getTwin() == null || !ref.getTwin().isSet()) { allRefInfo.put(ref.node, new RefInfo(ref, name)); } } } } /** * Get a map of {@link DefineInfo} structures, keyed by the name of * the define. */ Map getAllDefines() { return allDefines; } /** * Keeps track of whether the traversal is in a conditional branch. * We traverse all nodes of the parse tree. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { updateAssignAllowedStack(n, true); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { RefInfo refInfo = allRefInfo.get(n); if (refInfo != null) { Ref ref = refInfo.ref; Name name = refInfo.name; String fullName = name.getFullName(); switch (ref.type) { case SET_FROM_GLOBAL: case SET_FROM_LOCAL: Node valParent = getValueParent(ref); Node val = valParent.getLastChild(); if (valParent.isAssign() && name.isSimpleName() && name.getDeclaration() == ref) { // For defines, it's an error if a simple name is assigned // before it's declared compiler.report( t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, especially because // the intended use of defines is with config_files, where // all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( t.makeError(value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info); assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().isVar() ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param info Represents the define variable. * @param t The current traversal. */ private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) { info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE, t.getLineNumber(), t.getSourceName())); } /** * A simple data structure for associating a Ref with the name * that it references. */ private static class RefInfo { final Ref ref; final Name name; RefInfo(Ref ref, Name name) { this.ref = ref; this.name = name; } } } /** * A simple class for storing information about a define. * Gathers the initial value, the last assigned value, and whether * the define can be safely assigned a new value. */ private static final class DefineInfo { public final Node initialValueParent; public final Node initialValue; private Node lastValue; private boolean isAssignable; private String reasonNotAssignable; /** * Initializes a define. */ public DefineInfo(Node initialValue, Node initialValueParent) { this.initialValueParent = initialValueParent; this.initialValue = initialValue; lastValue = initialValue; isAssignable = true; } /** * Records the fact that this define can't be assigned a value anymore. * * @param reason A message describing the reason why it can't be assigned. */ public void setNotAssignable(String reason) { isAssignable = false; reasonNotAssignable = reason; } /** * Gets the reason why a define is not assignable. */ public String getReasonWhyNotAssignable() { return reasonNotAssignable; } /** * Records an assigned value. * * @return False if there was an error. */ public boolean recordAssignment(Node value) { lastValue = value; return isAssignable; } /** * Gets the last assigned value. */ public Node getLastValue() { return lastValue; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/MinimizeExitPoints.java0000644000175000017500000002351412115204405027314 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; /** * Transform the structure of the AST so that the number of explicit exits * are minimized. * * @author johnlenz@google.com (John Lenz) */ class MinimizeExitPoints extends AbstractPostOrderCallback implements CompilerPass { AbstractCompiler compiler; MinimizeExitPoints(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.LABEL: tryMinimizeExits( n.getLastChild(), Token.BREAK, n.getFirstChild().getString()); break; case Token.FOR: case Token.WHILE: tryMinimizeExits(NodeUtil.getLoopCodeBlock(n), Token.CONTINUE, null); break; case Token.DO: tryMinimizeExits(NodeUtil.getLoopCodeBlock(n), Token.CONTINUE, null); Node cond = NodeUtil.getConditionExpression(n); if (NodeUtil.getImpureBooleanValue(cond) == TernaryValue.FALSE) { // Normally, we wouldn't be able to optimize BREAKs inside a loop // but as we know the condition will always false, we can treat them // as we would a CONTINUE. tryMinimizeExits(n.getFirstChild(), Token.BREAK, null); } break; case Token.FUNCTION: tryMinimizeExits(n.getLastChild(), Token.RETURN, null); break; } } /** * Attempts to minimize the number of explicit exit points in a control * structure to take advantage of the implied exit at the end of the * structure. This is accomplished by removing redundant statements, and * moving statements following a qualifying IF node into that node. * For example: * * function () { * if (x) return; * else blah(); * foo(); * } * * becomes: * * function () { * if (x) ; * else { * blah(); * foo(); * } * * @param n The execution node of a parent to inspect. * @param exitType The type of exit to look for. * @param labelName If parent is a label the name of the label to look for, * null otherwise. * @nullable labelName non-null only for breaks within labels. */ void tryMinimizeExits(Node n, int exitType, String labelName) { // Just an 'exit'. if (matchingExitNode(n, exitType, labelName)) { NodeUtil.removeChild(n.getParent(), n); compiler.reportCodeChange(); return; } // Just an 'if'. if (n.isIf()) { Node ifBlock = n.getFirstChild().getNext(); tryMinimizeExits(ifBlock, exitType, labelName); Node elseBlock = ifBlock.getNext(); if (elseBlock != null) { tryMinimizeExits(elseBlock, exitType, labelName); } return; } // Just a 'try/catch/finally'. if (n.isTry()) { Node tryBlock = n.getFirstChild(); tryMinimizeExits(tryBlock, exitType, labelName); Node allCatchNodes = NodeUtil.getCatchBlock(n); if (NodeUtil.hasCatchHandler(allCatchNodes)) { Preconditions.checkState(allCatchNodes.hasOneChild()); Node catchNode = allCatchNodes.getFirstChild(); Node catchCodeBlock = catchNode.getLastChild(); tryMinimizeExits(catchCodeBlock, exitType, labelName); } if (NodeUtil.hasFinally(n)) { Node finallyBlock = n.getLastChild(); tryMinimizeExits(finallyBlock, exitType, labelName); } } // Just a 'label'. if (n.isLabel()) { Node labelBlock = n.getLastChild(); tryMinimizeExits(labelBlock, exitType, labelName); } // TODO(johnlenz): The last case of SWITCH statement? // The rest assumes a block with at least one child, bail on anything else. if (!n.isBlock() || n.getLastChild() == null) { return; } // Multiple if-exits can be converted in a single pass. // Convert "if (blah) break; if (blah2) break; other_stmt;" to // become "if (blah); else { if (blah2); else { other_stmt; } }" // which will get converted to "if (!blah && !blah2) { other_stmt; }". for (Node c : n.children()) { // An 'if' block to process below. if (c.isIf()) { Node ifTree = c; Node trueBlock, falseBlock; // First, the true condition block. trueBlock = ifTree.getFirstChild().getNext(); falseBlock = trueBlock.getNext(); tryMinimizeIfBlockExits(trueBlock, falseBlock, ifTree, exitType, labelName); // Now the else block. // The if blocks may have changed, get them again. trueBlock = ifTree.getFirstChild().getNext(); falseBlock = trueBlock.getNext(); if (falseBlock != null) { tryMinimizeIfBlockExits(falseBlock, trueBlock, ifTree, exitType, labelName); } } if (c == n.getLastChild()) { break; } } // Now try to minimize the exits of the last child, if it is removed // look at what has become the last child. for (Node c = n.getLastChild(); c != null; c = n.getLastChild()) { tryMinimizeExits(c, exitType, labelName); // If the node is still the last child, we are done. if (c == n.getLastChild()) { break; } } } /** * Look for exits (returns, breaks, or continues, depending on the context) at * the end of a block and removes them by moving the if node's siblings, * if any, into the opposite condition block. * * @param srcBlock The block to inspect. * @param destBlock The block to move sibling nodes into. * @param ifNode The if node to work with. * @param exitType The type of exit to look for. * @param labelName The name associated with the exit, if any. * @nullable labelName null for anything excepted for named-break associated * with a label. */ private void tryMinimizeIfBlockExits(Node srcBlock, Node destBlock, Node ifNode, int exitType, String labelName) { Node exitNodeParent = null; Node exitNode = null; // Pick an exit node candidate. if (srcBlock.isBlock()) { if (!srcBlock.hasChildren()) { return; } exitNodeParent = srcBlock; exitNode = exitNodeParent.getLastChild(); } else { // Just a single statement, if it isn't an exit bail. exitNodeParent = ifNode; exitNode = srcBlock; } // Verify the candidate. if (!matchingExitNode(exitNode, exitType, labelName)) { return; } // Take case of the if nodes siblings, if any. if (ifNode.getNext() != null) { // Move siblings of the if block into the opposite // logic block of the exit. Node newDestBlock = IR.block().srcref(ifNode); if (destBlock == null) { // Only possible if this is the false block. ifNode.addChildToBack(newDestBlock); } else if (destBlock.isEmpty()) { // Use the new block. ifNode.replaceChild(destBlock, newDestBlock); } else if (destBlock.isBlock()) { // Reuse the existing block. newDestBlock = destBlock; } else { // Add the existing statement to the new block. ifNode.replaceChild(destBlock, newDestBlock); newDestBlock.addChildToBack(destBlock); } // Move all the if node's following siblings. moveAllFollowing(ifNode, ifNode.getParent(), newDestBlock); compiler.reportCodeChange(); } } /** * Determines if n matches the type and name for the following types of * "exits": * - return without values * - continues and breaks with or without names. * @param n The node to inspect. * @param type The Token type to look for. * @param labelName The name that must be associated with the exit type. * @nullable labelName non-null only for breaks associated with labels. * @return Whether the node matches the specified block-exit type. */ private static boolean matchingExitNode(Node n, int type, String labelName) { if (n.getType() == type) { if (type == Token.RETURN) { // only returns without expressions. return !n.hasChildren(); } else { if (labelName == null) { return !n.hasChildren(); } else { return n.hasChildren() && labelName.equals(n.getFirstChild().getString()); } } } return false; } /** * Move all the child nodes following start in srcParent to the end of * destParent's child list. * @param start The start point in the srcParent child list. * @param srcParent The parent node of start. * @param destParent The destination node. */ private static void moveAllFollowing( Node start, Node srcParent, Node destParent) { for (Node n = start.getNext(); n != null; n = start.getNext()) { boolean isFunctionDeclaration = NodeUtil.isFunctionDeclaration(n); srcParent.removeChild(n); if (isFunctionDeclaration) { destParent.addChildToFront(n); } else { destParent.addChildToBack(n); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DeadAssignmentsElimination.java0000644000175000017500000003550112115204405030745 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.DataFlowAnalysis.FlowState; import com.google.javascript.jscomp.LiveVariablesAnalysis.LiveVariableLattice; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Removes local variable assignments that are useless based on information from * {@link LiveVariablesAnalysis}. If there is an assignment to variable * {@code x} and {@code x} is dead after this assignment, we know that the * current content of {@code x} will not be read and this assignment is useless. * */ class DeadAssignmentsElimination extends AbstractPostOrderCallback implements CompilerPass, ScopedCallback { private final AbstractCompiler compiler; private LiveVariablesAnalysis liveness; // Matches all assignment operators and increment/decrement operators. // Does *not* match VAR initialization, since RemoveUnusedVariables // will already remove variables that are initialized but unused. private static final Predicate matchRemovableAssigns = new Predicate() { @Override public boolean apply(Node n) { return (NodeUtil.isAssignmentOp(n) && n.getFirstChild().isName()) || n.isInc() || n.isDec(); } }; public DeadAssignmentsElimination(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(externs); Preconditions.checkNotNull(root); NodeTraversal.traverse(compiler, root, this); } @Override public void enterScope(NodeTraversal t) { Scope scope = t.getScope(); // Global scope _SHOULD_ work, however, liveness won't finish without // -Xmx1024 in closure. We might have to look at coding conventions for // exported variables as well. if (scope.isGlobal()) { return; } if (LiveVariablesAnalysis.MAX_VARIABLES_TO_ANALYZE < t.getScope().getVarCount()) { return; } // We are not going to do any dead assignment elimination in when there is // at least one inner function because in most browsers, when there is a // closure, ALL the variables are saved (escaped). Node fnBlock = t.getScopeRoot().getLastChild(); if (NodeUtil.containsFunction(fnBlock)) { return; } // We don't do any dead assignment elimination if there are no assigns // to eliminate. :) if (!NodeUtil.has(fnBlock, matchRemovableAssigns, Predicates.alwaysTrue())) { return; } // Computes liveness information first. ControlFlowGraph cfg = t.getControlFlowGraph(); liveness = new LiveVariablesAnalysis(cfg, scope, compiler); liveness.analyze(); tryRemoveDeadAssignments(t, cfg); } @Override public void exitScope(NodeTraversal t) { } @Override public void visit(NodeTraversal t, Node n, Node parent) { } /** * Try to remove useless assignments from a control flow graph that has been * annotated with liveness information. * * @param t The node traversal. * @param cfg The control flow graph of the program annotated with liveness * information. */ private void tryRemoveDeadAssignments(NodeTraversal t, ControlFlowGraph cfg) { Iterable> nodes = cfg.getDirectedGraphNodes(); for (DiGraphNode cfgNode : nodes) { FlowState state = cfgNode.getAnnotation(); Node n = cfgNode.getValue(); if (n == null) { continue; } switch (n.getType()) { case Token.IF: case Token.WHILE: case Token.DO: tryRemoveAssignment(t, NodeUtil.getConditionExpression(n), state); continue; case Token.FOR: if (!NodeUtil.isForIn(n)) { tryRemoveAssignment( t, NodeUtil.getConditionExpression(n), state); } continue; case Token.SWITCH: case Token.CASE: case Token.RETURN: if (n.hasChildren()) { tryRemoveAssignment(t, n.getFirstChild(), state); } continue; // TODO(user): case Token.VAR: Remove var a=1;a=2;..... } tryRemoveAssignment(t, n, state); } } private void tryRemoveAssignment(NodeTraversal t, Node n, FlowState state) { tryRemoveAssignment(t, n, n, state); } /** * Determines if any local variables are dead after the instruction {@code n} * and are assigned within the subtree of {@code n}. Removes those assignments * if there are any. * * @param n Target instruction. * @param exprRoot The CFG node where the liveness information in state is * still correct. * @param state The liveness information at {@code n}. */ private void tryRemoveAssignment(NodeTraversal t, Node n, Node exprRoot, FlowState state) { Node parent = n.getParent(); if (NodeUtil.isAssignmentOp(n) || n.isInc() || n.isDec()) { Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); // Recurse first. Example: dead_x = dead_y = 1; We try to clean up dead_y // first. if (rhs != null) { tryRemoveAssignment(t, rhs, exprRoot, state); rhs = lhs.getNext(); } Scope scope = t.getScope(); if (!lhs.isName()) { return; // Not a local variable assignment. } String name = lhs.getString(); if (!scope.isDeclared(name, false)) { return; } Var var = scope.getVar(name); if (liveness.getEscapedLocals().contains(var)) { return; // Local variable that might be escaped due to closures. } // If we have an identity assignment such as a=a, always remove it // regardless of what the liveness results because it // does not change the result afterward. if (rhs != null && rhs.isName() && rhs.getString().equals(var.name) && n.isAssign()) { n.removeChild(rhs); n.getParent().replaceChild(n, rhs); compiler.reportCodeChange(); return; } if (state.getOut().isLive(var)) { return; // Variable not dead. } if (state.getIn().isLive(var) && isVariableStillLiveWithinExpression(n, exprRoot, var.name)) { // The variable is killed here but it is also live before it. // This is possible if we have say: // if (X = a && a = C) {..} ; .......; a = S; // In this case we are safe to remove "a = C" because it is dead. // However if we have: // if (a = C && X = a) {..} ; .......; a = S; // removing "a = C" is NOT correct, although the live set at the node // is exactly the same. // TODO(user): We need more fine grain CFA or we need to keep track // of GEN sets when we recurse here. return; } if (n.isAssign()) { n.removeChild(rhs); n.getParent().replaceChild(n, rhs); } else if (NodeUtil.isAssignmentOp(n)) { n.removeChild(rhs); n.removeChild(lhs); Node op = new Node(NodeUtil.getOpFromAssignmentOp(n), lhs, rhs); parent.replaceChild(n, op); } else if (n.isInc() || n.isDec()) { if (parent.isExprResult()) { parent.replaceChild(n, IR.voidNode(IR.number(0).srcref(n))); } else if(n.isComma() && n != parent.getLastChild()) { parent.removeChild(n); } else if (parent.isFor() && !NodeUtil.isForIn(parent) && NodeUtil.getConditionExpression(parent) != n) { parent.replaceChild(n, IR.empty()); } else { // Cannot replace x = a++ with x = a because that's not valid // when a is not a number. return; } } else { // Not reachable. Preconditions.checkState(false, "Unknown statement"); } compiler.reportCodeChange(); return; } else { for (Node c = n.getFirstChild(); c != null;) { Node next = c.getNext(); if (!ControlFlowGraph.isEnteringNewCfgNode(c)) { tryRemoveAssignment(t, c, exprRoot, state); } c = next; } return; } } /** * Given a variable, node n in the tree and a sub-tree denoted by exprRoot as * the root, this function returns true if there exists a read of that * variable before a write to that variable that is on the right side of n. * * For example, suppose the node is x = 1: * * y = 1, x = 1; // false, there is no reads at all. * y = 1, x = 1, print(x) // true, there is a read right of n. * y = 1, x = 1, x = 2, print(x) // false, there is a read right of n but * // it is after a write. * * @param n The current node we should look at. * @param exprRoot The node */ private boolean isVariableStillLiveWithinExpression( Node n, Node exprRoot, String variable) { while (n != exprRoot) { VariableLiveness state = VariableLiveness.MAYBE_LIVE; switch (n.getParent().getType()) { case Token.OR: case Token.AND: // If the currently node is the first child of // AND/OR, be conservative only consider the READs // of the second operand. if (n.getNext() != null) { state = isVariableReadBeforeKill( n.getNext(), variable); if (state == VariableLiveness.KILL) { state = VariableLiveness.MAYBE_LIVE; } } break; case Token.HOOK: // If current node is the condition, check each following // branch, otherwise it is a conditional branch and the // other branch can be ignored. if (n.getNext() != null && n.getNext().getNext() != null) { state = checkHookBranchReadBeforeKill( n.getNext(), n.getNext().getNext(), variable); } break; default: for(Node sibling = n.getNext(); sibling != null; sibling = sibling.getNext()) { state = isVariableReadBeforeKill(sibling, variable); if (state != VariableLiveness.MAYBE_LIVE) { break; } } } // If we see a READ or KILL there is no need to continue. if (state == VariableLiveness.READ) { return true; } else if (state == VariableLiveness.KILL) { return false; } n = n.getParent(); } return false; } // The current liveness of the variable private enum VariableLiveness { MAYBE_LIVE, // May be still live in the current expression tree. READ, // Known there is a read left of it. KILL, // Known there is a write before any read. } /** * Give an expression and a variable. It returns READ, if the first * reference of that variable is a read. It returns KILL, if the first * reference of that variable is an assignment. It returns MAY_LIVE otherwise. */ private VariableLiveness isVariableReadBeforeKill( Node n, String variable) { if (ControlFlowGraph.isEnteringNewCfgNode(n)) { // Not a FUNCTION return VariableLiveness.MAYBE_LIVE; } if (n.isName() && variable.equals(n.getString())) { if (NodeUtil.isVarOrSimpleAssignLhs(n, n.getParent())) { Preconditions.checkState(n.getParent().isAssign()); // The expression to which the assignment is made is evaluated before // the RHS is evaluated (normal left to right evaluation) but the KILL // occurs after the RHS is evaluated. Node rhs = n.getNext(); VariableLiveness state = isVariableReadBeforeKill(rhs, variable); if (state == VariableLiveness.READ) { return state; } return VariableLiveness.KILL; } else { return VariableLiveness.READ; } } switch (n.getType()) { // Conditionals case Token.OR: case Token.AND: VariableLiveness v1 = isVariableReadBeforeKill( n.getFirstChild(), variable); VariableLiveness v2 = isVariableReadBeforeKill( n.getLastChild(), variable); // With a AND/OR the first branch always runs, but the second is // may not. if (v1 != VariableLiveness.MAYBE_LIVE) { return v1; } else if (v2 == VariableLiveness.READ) { return VariableLiveness.READ; } else { return VariableLiveness.MAYBE_LIVE; } case Token.HOOK: VariableLiveness first = isVariableReadBeforeKill( n.getFirstChild(), variable); if (first != VariableLiveness.MAYBE_LIVE) { return first; } return checkHookBranchReadBeforeKill( n.getFirstChild().getNext(), n.getLastChild(), variable); default: // Expressions are evaluated left-right, depth first. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { VariableLiveness state = isVariableReadBeforeKill(child, variable); if (state != VariableLiveness.MAYBE_LIVE) { return state; } } } return VariableLiveness.MAYBE_LIVE; } private VariableLiveness checkHookBranchReadBeforeKill( Node trueCase, Node falseCase, String variable) { VariableLiveness v1 = isVariableReadBeforeKill( trueCase, variable); VariableLiveness v2 = isVariableReadBeforeKill( falseCase, variable); // With a hook it is unknown which branch will run, so // we must be conservative. A read by either is a READ, and // a KILL is only considered if both KILL. if (v1 == VariableLiveness.READ || v2 == VariableLiveness.READ) { return VariableLiveness.READ; } else if (v1 == VariableLiveness.KILL && v2 == VariableLiveness.KILL) { return VariableLiveness.KILL; } else { return VariableLiveness.MAYBE_LIVE; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ProcessClosurePrimitives.java0000644000175000017500000011340612115204405030533 0ustar apoapo/* * Copyright 2006 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfoBuilder; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Replaces goog.provide calls, removes goog.require calls, verifies that * goog.require has a corresponding goog.provide and some closure specific * simplifications. * */ class ProcessClosurePrimitives extends AbstractPostOrderCallback implements HotSwapCompilerPass { static final DiagnosticType NULL_ARGUMENT_ERROR = DiagnosticType.error( "JSC_NULL_ARGUMENT_ERROR", "method \"{0}\" called without an argument"); static final DiagnosticType EXPECTED_OBJECTLIT_ERROR = DiagnosticType.error( "JSC_EXPECTED_OBJECTLIT_ERROR", "method \"{0}\" expected an object literal argument"); static final DiagnosticType EXPECTED_STRING_ERROR = DiagnosticType.error( "JSC_EXPECTED_STRING_ERROR", "method \"{0}\" expected an object string argument"); static final DiagnosticType INVALID_ARGUMENT_ERROR = DiagnosticType.error( "JSC_INVALID_ARGUMENT_ERROR", "method \"{0}\" called with invalid argument"); static final DiagnosticType INVALID_STYLE_ERROR = DiagnosticType.error( "JSC_INVALID_CSS_NAME_MAP_STYLE_ERROR", "Invalid CSS name map style {0}"); static final DiagnosticType TOO_MANY_ARGUMENTS_ERROR = DiagnosticType.error( "JSC_TOO_MANY_ARGUMENTS_ERROR", "method \"{0}\" called with more than one argument"); static final DiagnosticType DUPLICATE_NAMESPACE_ERROR = DiagnosticType.error( "JSC_DUPLICATE_NAMESPACE_ERROR", "namespace \"{0}\" cannot be provided twice"); static final DiagnosticType FUNCTION_NAMESPACE_ERROR = DiagnosticType.error( "JSC_FUNCTION_NAMESPACE_ERROR", "\"{0}\" cannot be both provided and declared as a function"); static final DiagnosticType MISSING_PROVIDE_ERROR = DiagnosticType.error( "JSC_MISSING_PROVIDE_ERROR", "required \"{0}\" namespace never provided"); static final DiagnosticType LATE_PROVIDE_ERROR = DiagnosticType.error( "JSC_LATE_PROVIDE_ERROR", "required \"{0}\" namespace not provided yet"); static final DiagnosticType INVALID_PROVIDE_ERROR = DiagnosticType.error( "JSC_INVALID_PROVIDE_ERROR", "\"{0}\" is not a valid JS property name"); static final DiagnosticType XMODULE_REQUIRE_ERROR = DiagnosticType.warning( "JSC_XMODULE_REQUIRE_ERROR", "namespace \"{0}\" provided in module {1} " + "but required in module {2}"); static final DiagnosticType NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR = DiagnosticType.error( "JSC_NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR", "goog.setCssNameMapping only takes an object literal with string values"); static final DiagnosticType INVALID_CSS_RENAMING_MAP = DiagnosticType.warning( "INVALID_CSS_RENAMING_MAP", "Invalid entries in css renaming map: {0}"); static final DiagnosticType BASE_CLASS_ERROR = DiagnosticType.error( "JSC_BASE_CLASS_ERROR", "incorrect use of goog.base: {0}"); /** The root Closure namespace */ static final String GOOG = "goog"; private final AbstractCompiler compiler; private final JSModuleGraph moduleGraph; // The goog.provides must be processed in a deterministic order. private final Map providedNames = Maps.newLinkedHashMap(); private final List unrecognizedRequires = Lists.newArrayList(); private final Set exportedVariables = Sets.newHashSet(); private final CheckLevel requiresLevel; private final PreprocessorSymbolTable preprocessorSymbolTable; ProcessClosurePrimitives(AbstractCompiler compiler, @Nullable PreprocessorSymbolTable preprocessorSymbolTable, CheckLevel requiresLevel) { this.compiler = compiler; this.preprocessorSymbolTable = preprocessorSymbolTable; this.moduleGraph = compiler.getModuleGraph(); this.requiresLevel = requiresLevel; // goog is special-cased because it is provided in Closure's base library. providedNames.put(GOOG, new ProvidedName(GOOG, null, null, false /* implicit */)); } Set getExportedVariableNames() { return exportedVariables; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverse(root); for (ProvidedName pn : providedNames.values()) { pn.replace(); } if (requiresLevel.isOn()) { for (UnrecognizedRequire r : unrecognizedRequires) { DiagnosticType error; ProvidedName expectedName = providedNames.get(r.namespace); if (expectedName != null && expectedName.firstNode != null) { // The namespace ended up getting provided after it was required. error = LATE_PROVIDE_ERROR; } else { error = MISSING_PROVIDE_ERROR; } compiler.report(JSError.make( r.inputName, r.requireNode, requiresLevel, error, r.namespace)); } } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { // TODO(bashir): Implement a real hot-swap version instead and make it fully // consistent with the full version. this.compiler.process(this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: boolean isExpr = parent.isExprResult(); Node left = n.getFirstChild(); if (left.isGetProp()) { Node name = left.getFirstChild(); if (name.isName() && GOOG.equals(name.getString())) { // For the sake of simplicity, we report code changes // when we see a provides/requires, and don't worry about // reporting the change when we actually do the replacement. String methodName = name.getNext().getString(); if ("base".equals(methodName)) { processBaseClassCall(t, n); } else if (!isExpr) { // All other methods must be called in an EXPR. break; } else if ("require".equals(methodName)) { processRequireCall(t, n, parent); } else if ("provide".equals(methodName)) { processProvideCall(t, n, parent); } else if ("exportSymbol".equals(methodName)) { Node arg = left.getNext(); if (arg.isString()) { int dot = arg.getString().indexOf('.'); if (dot == -1) { exportedVariables.add(arg.getString()); } else { exportedVariables.add(arg.getString().substring(0, dot)); } } } else if ("addDependency".equals(methodName)) { CodingConvention convention = compiler.getCodingConvention(); List typeDecls = convention.identifyTypeDeclarationCall(n); if (typeDecls != null) { for (String typeDecl : typeDecls) { compiler.getTypeRegistry().forwardDeclareType(typeDecl); } } // We can't modify parent, so just create a node that will // get compiled out. parent.replaceChild(n, IR.number(0)); compiler.reportCodeChange(); } else if ("setCssNameMapping".equals(methodName)) { processSetCssNameMapping(t, n, parent); } } } break; case Token.ASSIGN: case Token.NAME: // If this is an assignment to a provided name, remove the provided // object. handleCandidateProvideDefinition(t, n, parent); break; case Token.EXPR_RESULT: handleTypedefDefinition(t, n); break; case Token.FUNCTION: // If this is a declaration of a provided named function, this is an // error. Hoisted functions will explode if they're provided. if (t.inGlobalScope() && !NodeUtil.isFunctionExpression(n)) { String name = n.getFirstChild().getString(); ProvidedName pn = providedNames.get(name); if (pn != null) { compiler.report(t.makeError(n, FUNCTION_NAMESPACE_ERROR, name)); } } break; case Token.GETPROP: if (n.getFirstChild().isName() && !parent.isCall() && !parent.isAssign() && "goog.base".equals(n.getQualifiedName())) { reportBadBaseClassUse(t, n, "May only be called directly."); } break; } } /** * Handles a goog.require call. */ private void processRequireCall(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyArgument(t, left, arg)) { String ns = arg.getString(); ProvidedName provided = providedNames.get(ns); if (provided == null || !provided.isExplicitlyProvided()) { unrecognizedRequires.add( new UnrecognizedRequire(n, ns, t.getSourceName())); } else { JSModule providedModule = provided.explicitModule; // This must be non-null, because there was an explicit provide. Preconditions.checkNotNull(providedModule); JSModule module = t.getModule(); if (moduleGraph != null && module != providedModule && !moduleGraph.dependsOn(module, providedModule)) { compiler.report( t.makeError(n, XMODULE_REQUIRE_ERROR, ns, providedModule.getName(), module.getName())); } } maybeAddToSymbolTable(left); maybeAddStringNodeToSymbolTable(arg); // Requires should be removed before runtime. The one // exception is if the type has not been provided yet and // errors for broken requires are turned off, in which case, // we will be doing a later pass that may error, so we can // leave this here this time and let it error next time if it // is still not provided. if (provided != null || requiresLevel.isOn()) { parent.detachFromParent(); compiler.reportCodeChange(); } } } /** * Handles a goog.provide call. */ private void processProvideCall(NodeTraversal t, Node n, Node parent) { Node left = n.getFirstChild(); Node arg = left.getNext(); if (verifyProvide(t, left, arg)) { String ns = arg.getString(); maybeAddToSymbolTable(left); maybeAddStringNodeToSymbolTable(arg); if (providedNames.containsKey(ns)) { ProvidedName previouslyProvided = providedNames.get(ns); if (!previouslyProvided.isExplicitlyProvided()) { previouslyProvided.addProvide(parent, t.getModule(), true); } else { compiler.report( t.makeError(n, DUPLICATE_NAMESPACE_ERROR, ns)); } } else { registerAnyProvidedPrefixes(ns, parent, t.getModule()); providedNames.put( ns, new ProvidedName(ns, parent, t.getModule(), true)); } } } /** * Handles a typedef definition for a goog.provided name. * @param n EXPR_RESULT node. */ private void handleTypedefDefinition( NodeTraversal t, Node n) { JSDocInfo info = n.getFirstChild().getJSDocInfo(); if (t.inGlobalScope() && info != null && info.hasTypedefType()) { String name = n.getFirstChild().getQualifiedName(); if (name != null) { ProvidedName pn = providedNames.get(name); if (pn != null) { pn.addDefinition(n, t.getModule()); } } } } /** * Handles a candidate definition for a goog.provided name. */ private void handleCandidateProvideDefinition( NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { String name = null; if (n.isName() && parent.isVar()) { name = n.getString(); } else if (n.isAssign() && parent.isExprResult()) { name = n.getFirstChild().getQualifiedName(); } if (name != null) { if (parent.getBooleanProp(Node.IS_NAMESPACE)) { processProvideFromPreviousPass(t, name, parent); } else { ProvidedName pn = providedNames.get(name); if (pn != null) { pn.addDefinition(parent, t.getModule()); } } } } } /** * Processes the base class call. */ private void processBaseClassCall(NodeTraversal t, Node n) { // Two things must hold for every goog.base call: // 1) We must be calling it on "this". // 2) We must be calling it on a prototype method of the same name as // the one we're in, OR we must be calling it from a constructor. // If both of those things are true, then we can rewrite: //

          // function Foo() {
          //   goog.base(this);
          // }
          // goog.inherits(Foo, BaseFoo);
          // Foo.prototype.bar = function() {
          //   goog.base(this, 'bar', 1);
          // };
          // 
      // as the easy-to-optimize: //
          // function Foo() {
          //   BaseFoo.call(this);
          // }
          // goog.inherits(Foo, BaseFoo);
          // Foo.prototype.bar = function() {
          //   Foo.superClass_.bar.call(this, 1);
          // };
          //
          // Most of the logic here is just to make sure the AST's
          // structure is what we expect it to be.
      
          Node callee = n.getFirstChild();
          Node thisArg = callee.getNext();
          if (thisArg == null || !thisArg.isThis()) {
            reportBadBaseClassUse(t, n, "First argument must be 'this'.");
            return;
          }
      
          Node enclosingFnNameNode = getEnclosingDeclNameNode(t);
          if (enclosingFnNameNode == null) {
            reportBadBaseClassUse(t, n, "Could not find enclosing method.");
            return;
          }
      
          String enclosingQname = enclosingFnNameNode.getQualifiedName();
          if (enclosingQname.indexOf(".prototype.") == -1) {
            // Handle constructors.
            Node enclosingParent = enclosingFnNameNode.getParent();
            Node maybeInheritsExpr = (enclosingParent.isAssign() ?
                enclosingParent.getParent() : enclosingParent).getNext();
            Node baseClassNode = null;
            if (maybeInheritsExpr != null &&
                maybeInheritsExpr.isExprResult() &&
                maybeInheritsExpr.getFirstChild().isCall()) {
              Node callNode = maybeInheritsExpr.getFirstChild();
              if ("goog.inherits".equals(
                      callNode.getFirstChild().getQualifiedName()) &&
                  callNode.getLastChild().isQualifiedName()) {
                baseClassNode = callNode.getLastChild();
              }
            }
      
            if (baseClassNode == null) {
              reportBadBaseClassUse(
                  t, n, "Could not find goog.inherits for base class");
              return;
            }
      
            // We're good to go.
            n.replaceChild(
                callee,
                NodeUtil.newQualifiedNameNode(
                  compiler.getCodingConvention(),
                  String.format("%s.call", baseClassNode.getQualifiedName()),
                  callee, "goog.base"));
            compiler.reportCodeChange();
          } else {
            // Handle methods.
            Node methodNameNode = thisArg.getNext();
            if (methodNameNode == null || !methodNameNode.isString()) {
              reportBadBaseClassUse(t, n, "Second argument must name a method.");
              return;
            }
      
            String methodName = methodNameNode.getString();
            String ending = ".prototype." + methodName;
            if (enclosingQname == null ||
                !enclosingQname.endsWith(ending)) {
              reportBadBaseClassUse(
                  t, n, "Enclosing method does not match " + methodName);
              return;
            }
      
            // We're good to go.
            Node className =
                enclosingFnNameNode.getFirstChild().getFirstChild();
            n.replaceChild(
                callee,
                NodeUtil.newQualifiedNameNode(
                  compiler.getCodingConvention(),
                  String.format("%s.superClass_.%s.call",
                      className.getQualifiedName(), methodName),
                  callee, "goog.base"));
            n.removeChild(methodNameNode);
            compiler.reportCodeChange();
          }
        }
      
        /**
         * Returns the qualified name node of the function whose scope we're in,
         * or null if it cannot be found.
         */
        private Node getEnclosingDeclNameNode(NodeTraversal t) {
          Node scopeRoot = t.getScopeRoot();
          if (NodeUtil.isFunctionDeclaration(scopeRoot)) {
            // function x() {...}
            return scopeRoot.getFirstChild();
          } else {
            Node parent = scopeRoot.getParent();
            if (parent != null) {
              if (parent.isAssign() ||
                  parent.getLastChild() == scopeRoot &&
                  parent.getFirstChild().isQualifiedName()) {
                // x.y.z = function() {...};
                return parent.getFirstChild();
              } else if (parent.isName()) {
                // var x = function() {...};
                return parent;
              }
            }
          }
      
          return null;
        }
      
        /** Reports an incorrect use of super-method calling. */
        private void reportBadBaseClassUse(
            NodeTraversal t, Node n, String extraMessage) {
          compiler.report(t.makeError(n, BASE_CLASS_ERROR, extraMessage));
        }
      
        /**
         * Processes the output of processed-provide from a previous pass.  This will
         * update our data structures in the same manner as if the provide had been
         * processed in this pass.
         */
        private void processProvideFromPreviousPass(
            NodeTraversal t, String name, Node parent) {
          if (!providedNames.containsKey(name)) {
            // Record this provide created on a previous pass, and create a dummy
            // EXPR node as a placeholder to simulate an explicit provide.
            Node expr = new Node(Token.EXPR_RESULT);
            expr.copyInformationFromForTree(parent);
            parent.getParent().addChildBefore(expr, parent);
            compiler.reportCodeChange();
      
            JSModule module = t.getModule();
            registerAnyProvidedPrefixes(name, expr, module);
      
            ProvidedName provided = new ProvidedName(name, expr, module, true);
            providedNames.put(name, provided);
            provided.addDefinition(parent, module);
          } else {
            // Remove this provide if it came from a previous pass since we have an
            // replacement already.
            if (isNamespacePlaceholder(parent)) {
              parent.getParent().removeChild(parent);
              compiler.reportCodeChange();
            }
          }
        }
      
        /**
         * Processes a call to goog.setCssNameMapping(). Either the argument to
         * goog.setCssNameMapping() is valid, in which case it will be used to create
         * a CssRenamingMap for the compiler of this CompilerPass, or it is invalid
         * and a JSCompiler error will be reported.
         * @see #visit(NodeTraversal, Node, Node)
         */
        private void processSetCssNameMapping(NodeTraversal t, Node n, Node parent) {
          Node left = n.getFirstChild();
          Node arg = left.getNext();
          if (verifySetCssNameMapping(t, left, arg)) {
            // Translate OBJECTLIT into SubstitutionMap. All keys and
            // values must be strings, or an error will be thrown.
            final Map cssNames = Maps.newHashMap();
      
            for (Node key = arg.getFirstChild(); key != null;
                key = key.getNext()) {
              Node value = key.getFirstChild();
              if (!key.isStringKey()
                  || value == null
                  || !value.isString()) {
                compiler.report(
                    t.makeError(n,
                        NON_STRING_PASSED_TO_SET_CSS_NAME_MAPPING_ERROR));
                return;
              }
              cssNames.put(key.getString(), value.getString());
            }
      
            String styleStr = "BY_PART";
            if (arg.getNext() != null) {
              styleStr = arg.getNext().getString();
            }
      
            final CssRenamingMap.Style style;
            try {
              style = CssRenamingMap.Style.valueOf(styleStr);
            } catch (IllegalArgumentException e) {
              compiler.report(
                  t.makeError(n, INVALID_STYLE_ERROR, styleStr));
              return;
            }
      
            if (style == CssRenamingMap.Style.BY_PART) {
              // Make sure that no keys contain -'s
              List errors = Lists.newArrayList();
              for (String key : cssNames.keySet()) {
                if (key.contains("-")) {
                  errors.add(key);
                }
              }
              if (errors.size() != 0) {
                compiler.report(
                  t.makeError(n, INVALID_CSS_RENAMING_MAP, errors.toString()));
              }
            } else if (style == CssRenamingMap.Style.BY_WHOLE) {
              // Verifying things is a lot trickier here. We just do a quick
              // n^2 check over the map which makes sure that if "a-b" in
              // the map, then map(a-b) = map(a)-map(b).
              // To speed things up, only consider cases where len(b) <= 10
              List errors = Lists.newArrayList();
              for (Map.Entry b : cssNames.entrySet()) {
                if (b.getKey().length() > 10) continue;
                for (Map.Entry a : cssNames.entrySet()) {
                  String combined = cssNames.get(a.getKey() + "-" + b.getKey());
                  if (combined != null &&
                      !combined.equals(a.getValue() + "-" + b.getValue())) {
                    errors.add("map(" + a.getKey() + "-" + b.getKey() +") != map(" +
                               a.getKey() + ")-map(" + b.getKey() +")");
                  }
                }
              }
              if (errors.size() != 0) {
                compiler.report(
                  t.makeError(n, INVALID_CSS_RENAMING_MAP, errors.toString()));
              }
            }
      
            CssRenamingMap cssRenamingMap = new CssRenamingMap() {
              @Override
              public String get(String value) {
                if (cssNames.containsKey(value)) {
                  return cssNames.get(value);
                } else {
                  return value;
                }
              }
      
              @Override
              public CssRenamingMap.Style getStyle() {
                return style;
              }
            };
            compiler.setCssRenamingMap(cssRenamingMap);
            parent.getParent().removeChild(parent);
            compiler.reportCodeChange();
          }
        }
      
        /**
         * Verifies that a provide method call has exactly one argument,
         * and that it's a string literal and that the contents of the string are
         * valid JS tokens. Reports a compile error if it doesn't.
         *
         * @return Whether the argument checked out okay
         */
        private boolean verifyProvide(NodeTraversal t, Node methodName, Node arg) {
          if (!verifyArgument(t, methodName, arg)) {
            return false;
          }
      
          for (String part : arg.getString().split("\\.")) {
            if (!NodeUtil.isValidPropertyName(part)) {
              compiler.report(t.makeError(arg, INVALID_PROVIDE_ERROR, part));
              return false;
            }
          }
          return true;
        }
      
        /**
         * Verifies that a method call has exactly one argument, and that it's a
         * string literal. Reports a compile error if it doesn't.
         *
         * @return Whether the argument checked out okay
         */
        private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg) {
          return verifyArgument(t, methodName, arg, Token.STRING);
        }
      
        /**
         * Verifies that a method call has exactly one argument, and that it is of the
         * desired type. Reports a compile error if it doesn't.
         *
         * @return Whether the argument checked out okay
         */
        private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg,
            int desiredType) {
          DiagnosticType diagnostic = null;
          if (arg == null) {
            diagnostic = NULL_ARGUMENT_ERROR;
          } else if (arg.getType() != desiredType) {
            diagnostic = INVALID_ARGUMENT_ERROR;
          } else if (arg.getNext() != null) {
            diagnostic = TOO_MANY_ARGUMENTS_ERROR;
          }
          if (diagnostic != null) {
            compiler.report(
                t.makeError(methodName,
                    diagnostic, methodName.getQualifiedName()));
            return false;
          }
          return true;
        }
      
        /**
         * Verifies that setCssNameMapping is called with the correct methods.
         *
         * @return Whether the arguments checked out okay
         */
        private boolean verifySetCssNameMapping(NodeTraversal t, Node methodName,
            Node firstArg) {
          DiagnosticType diagnostic = null;
          if (firstArg == null) {
            diagnostic = NULL_ARGUMENT_ERROR;
          } else if (!firstArg.isObjectLit()) {
            diagnostic = EXPECTED_OBJECTLIT_ERROR;
          } else if (firstArg.getNext() != null) {
            Node secondArg = firstArg.getNext();
            if (!secondArg.isString()) {
              diagnostic = EXPECTED_STRING_ERROR;
            } else if (secondArg.getNext() != null) {
              diagnostic = TOO_MANY_ARGUMENTS_ERROR;
            }
          }
          if (diagnostic != null) {
            compiler.report(
                t.makeError(methodName,
                    diagnostic, methodName.getQualifiedName()));
            return false;
          }
          return true;
        }
      
        /**
         * Registers ProvidedNames for prefix namespaces if they haven't
         * already been defined. The prefix namespaces must be registered in
         * order from shortest to longest.
         *
         * @param ns The namespace whose prefixes may need to be provided.
         * @param node The EXPR of the provide call.
         * @param module The current module.
         */
        private void registerAnyProvidedPrefixes(
            String ns, Node node, JSModule module) {
          int pos = ns.indexOf('.');
          while (pos != -1) {
            String prefixNs = ns.substring(0, pos);
            pos = ns.indexOf('.', pos + 1);
            if (providedNames.containsKey(prefixNs)) {
              providedNames.get(prefixNs).addProvide(
                  node, module, false /* implicit */);
            } else {
              providedNames.put(
                  prefixNs,
                  new ProvidedName(prefixNs, node, module, false /* implicit */));
            }
          }
        }
      
        // -------------------------------------------------------------------------
      
        /**
         * Information required to replace a goog.provide call later in the traversal.
         */
        private class ProvidedName {
          private final String namespace;
      
          // The node and module where the call was explicitly or implicitly
          // goog.provided.
          private final Node firstNode;
          private final JSModule firstModule;
      
          // The node where the call was explicitly goog.provided. May be null
          // if the namespace is always provided implicitly.
          private Node explicitNode = null;
          private JSModule explicitModule = null;
      
          // The candidate definition.
          private Node candidateDefinition = null;
      
          // The minimum module where the provide must appear.
          private JSModule minimumModule = null;
      
          // The replacement declaration.
          private Node replacementNode = null;
      
          ProvidedName(String namespace, Node node, JSModule module,
              boolean explicit) {
            Preconditions.checkArgument(
                node == null /* The base case */ ||
                node.isExprResult());
            this.namespace = namespace;
            this.firstNode = node;
            this.firstModule = module;
      
            addProvide(node, module, explicit);
          }
      
          /**
           * Add an implicit or explicit provide.
           */
          void addProvide(Node node, JSModule module, boolean explicit) {
            if (explicit) {
              Preconditions.checkState(explicitNode == null);
              Preconditions.checkArgument(node.isExprResult());
              explicitNode = node;
              explicitModule = module;
            }
            updateMinimumModule(module);
          }
      
          boolean isExplicitlyProvided() {
            return explicitNode != null;
          }
      
          /**
           * Record function declaration, variable declaration or assignment that
           * refers to the same name as the provide statement.  Give preference to
           * declarations; if no declaration exists, record a reference to an
           * assignment so it repurposed later.
           */
          void addDefinition(Node node, JSModule module) {
            Preconditions.checkArgument(node.isExprResult() || // assign
                                        node.isFunction() ||
                                        node.isVar());
            Preconditions.checkArgument(explicitNode != node);
            if ((candidateDefinition == null) || !node.isExprResult()) {
              candidateDefinition = node;
              updateMinimumModule(module);
            }
          }
      
          private void updateMinimumModule(JSModule newModule) {
            if (minimumModule == null) {
              minimumModule = newModule;
            } else if (moduleGraph != null) {
              minimumModule = moduleGraph.getDeepestCommonDependencyInclusive(
                  minimumModule, newModule);
            } else {
              // If there is no module graph, then there must be exactly one
              // module in the program.
              Preconditions.checkState(newModule == minimumModule,
                                       "Missing module graph");
            }
          }
      
          /**
           * Replace the provide statement.
           *
           * If we're providing a name with no definition, then create one.
           * If we're providing a name with a duplicate definition, then make sure
           * that definition becomes a declaration.
           */
          void replace() {
            if (firstNode == null) {
              // Don't touch the base case ('goog').
              replacementNode = candidateDefinition;
              return;
            }
      
            // Handle the case where there is a duplicate definition for an explicitly
            // provided symbol.
            if (candidateDefinition != null && explicitNode != null) {
              explicitNode.detachFromParent();
              compiler.reportCodeChange();
      
              // Does this need a VAR keyword?
              replacementNode = candidateDefinition;
              if (candidateDefinition.isExprResult() &&
                  !candidateDefinition.getFirstChild().isQualifiedName()) {
                candidateDefinition.putBooleanProp(Node.IS_NAMESPACE, true);
                Node assignNode = candidateDefinition.getFirstChild();
                Node nameNode = assignNode.getFirstChild();
                if (nameNode.isName()) {
                  // Need to convert this assign to a var declaration.
                  Node valueNode = nameNode.getNext();
                  assignNode.removeChild(nameNode);
                  assignNode.removeChild(valueNode);
                  nameNode.addChildToFront(valueNode);
                  Node varNode = IR.var(nameNode);
                  varNode.copyInformationFrom(candidateDefinition);
                  candidateDefinition.getParent().replaceChild(
                      candidateDefinition, varNode);
                  nameNode.setJSDocInfo(assignNode.getJSDocInfo());
                  compiler.reportCodeChange();
                  replacementNode = varNode;
                }
              }
            } else {
              // Handle the case where there's not a duplicate definition.
              replacementNode = createDeclarationNode();
              if (firstModule == minimumModule) {
                firstNode.getParent().addChildBefore(replacementNode, firstNode);
              } else {
                // In this case, the name was implicitly provided by two independent
                // modules. We need to move this code up to a common module.
                int indexOfDot = namespace.lastIndexOf('.');
                if (indexOfDot == -1) {
                  // Any old place is fine.
                  compiler.getNodeForCodeInsertion(minimumModule)
                      .addChildToBack(replacementNode);
                } else {
                  // Add it after the parent namespace.
                  ProvidedName parentName =
                      providedNames.get(namespace.substring(0, indexOfDot));
                  Preconditions.checkNotNull(parentName);
                  Preconditions.checkNotNull(parentName.replacementNode);
                  parentName.replacementNode.getParent().addChildAfter(
                      replacementNode, parentName.replacementNode);
                }
              }
              if (explicitNode != null) {
                explicitNode.detachFromParent();
              }
              compiler.reportCodeChange();
            }
          }
      
          /**
           * Create the declaration node for this name, without inserting it
           * into the AST.
           */
          private Node createDeclarationNode() {
            if (namespace.indexOf('.') == -1) {
              return makeVarDeclNode();
            } else {
              return makeAssignmentExprNode();
            }
          }
      
          /**
           * Creates a simple namespace variable declaration
           * (e.g. var foo = {};).
           */
          private Node makeVarDeclNode() {
            Node name = IR.name(namespace);
            name.addChildToFront(createNamespaceLiteral());
      
            Node decl = IR.var(name);
            decl.putBooleanProp(Node.IS_NAMESPACE, true);
      
            // TODO(nicksantos): ew ew ew. Create a mutator package.
            if (compiler.getCodingConvention().isConstant(namespace)) {
              name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
            }
            if (candidateDefinition == null) {
              name.setJSDocInfo(createConstantJsDoc());
            }
      
            Preconditions.checkState(isNamespacePlaceholder(decl));
            setSourceInfo(decl);
            return decl;
          }
      
          /**
           * There are some special cases where clients of the compiler
           * do not run TypedScopeCreator after running this pass.
           * So always give the namespace literal a type.
           */
          private Node createNamespaceLiteral() {
            Node objlit = IR.objectlit();
            objlit.setJSType(
                compiler.getTypeRegistry().createAnonymousObjectType(null));
            return objlit;
          }
      
          /**
           * Creates a dotted namespace assignment expression
           * (e.g. foo.bar = {};).
           */
          private Node makeAssignmentExprNode() {
            Node decl = IR.exprResult(
                IR.assign(
                    NodeUtil.newQualifiedNameNode(
                        compiler.getCodingConvention(), namespace,
                        firstNode /* real source info will be filled in below */,
                        namespace),
                    createNamespaceLiteral()));
            decl.putBooleanProp(Node.IS_NAMESPACE, true);
            if (candidateDefinition == null) {
              decl.getFirstChild().setJSDocInfo(createConstantJsDoc());
            }
            Preconditions.checkState(isNamespacePlaceholder(decl));
            setSourceInfo(decl);
            return decl;
          }
      
          private JSDocInfo createConstantJsDoc() {
            JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
            builder.recordConstancy();
            return builder.build(null);
          }
      
          /**
           * Copy source info to the new node.
           */
          private void setSourceInfo(Node newNode) {
            Node provideStringNode = getProvideStringNode();
            int offset = getSourceInfoOffset(provideStringNode);
            Node sourceInfoNode = provideStringNode == null
                ? firstNode : provideStringNode;
            newNode.copyInformationFromForTree(sourceInfoNode);
            if (offset != 0) {
              newNode.setSourceEncodedPositionForTree(
                  sourceInfoNode.getSourcePosition() + offset);
            }
          }
      
          /**
           * Get the offset into the provide node where the symbol appears.
           */
          private int getSourceInfoOffset(Node provideStringNode) {
            if (provideStringNode == null) {
              return 0;
            }
      
            int indexOfLastDot = namespace.lastIndexOf('.');
      
            // +1 for the opening quote
            // +1 for the dot
            // if there's no dot, then the -1 index cancels it out
            // so elegant!
            return 2 + indexOfLastDot;
          }
      
          private Node getProvideStringNode() {
            return (firstNode.getFirstChild() != null &&
                    NodeUtil.isExprCall(firstNode)) ?
                firstNode.getFirstChild().getLastChild() :
                null;
          }
        }
      
        /**
         * @return Whether the node is namespace placeholder.
         */
        private static boolean isNamespacePlaceholder(Node n) {
          if (!n.getBooleanProp(Node.IS_NAMESPACE)) {
            return false;
          }
      
          Node value = null;
          if (n.isExprResult()) {
            Node assign = n.getFirstChild();
            value = assign.getLastChild();
          } else if (n.isVar()) {
            Node name = n.getFirstChild();
            value = name.getFirstChild();
          }
      
          return value != null
            && value.isObjectLit()
            && !value.hasChildren();
        }
      
        /**
         * The string in {@code n} is a reference name. Create a synthetic
         * node for it with all the proper source info, and add it to the symbol
         * table.
         */
        private void maybeAddStringNodeToSymbolTable(Node n) {
          if (preprocessorSymbolTable == null) {
            return;
          }
      
          String name = n.getString();
          Node syntheticRef = NodeUtil.newQualifiedNameNode(
              compiler.getCodingConvention(), name,
              n /* real source offsets will be filled in below */,
              name);
      
          // Offsets to add to source. Named for documentation purposes.
          final int FOR_QUOTE = 1;
          final int FOR_DOT = 1;
      
          Node current = null;
          for (current = syntheticRef;
               current.isGetProp();
               current = current.getFirstChild()) {
            int fullLen = current.getQualifiedName().length();
            int namespaceLen = current.getFirstChild().getQualifiedName().length();
      
            current.setSourceEncodedPosition(n.getSourcePosition() + FOR_QUOTE);
            current.setLength(fullLen);
      
            current.getLastChild().setSourceEncodedPosition(
                n.getSourcePosition() + namespaceLen + FOR_QUOTE + FOR_DOT);
            current.getLastChild().setLength(
                current.getLastChild().getString().length());
          }
      
          current.setSourceEncodedPosition(n.getSourcePosition() + FOR_QUOTE);
          current.setLength(current.getString().length());
      
          maybeAddToSymbolTable(syntheticRef);
        }
      
        /**
         * Add the given qualified name node to the symbol table.
         */
        private void maybeAddToSymbolTable(Node n) {
          if (preprocessorSymbolTable != null) {
            preprocessorSymbolTable.addReference(n);
          }
        }
      
        // -------------------------------------------------------------------------
      
        /**
         * Information required to create a {@code MISSING_PROVIDE_ERROR} warning.
         */
        private class UnrecognizedRequire {
          final Node requireNode;
          final String namespace;
          final String inputName;
      
          UnrecognizedRequire(Node requireNode, String namespace, String inputName) {
            this.requireNode = requireNode;
            this.namespace = namespace;
            this.inputName = inputName;
          }
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/FindExportableNodes.java0000644000175000017500000000767312115204405027413 0ustar  apoapo/*
       * 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.Maps;
      import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
      import com.google.javascript.rhino.JSDocInfo;
      import com.google.javascript.rhino.Node;
      import com.google.javascript.rhino.Token;
      
      import java.util.LinkedHashMap;
      
      /**
       * Records all of the symbols and properties that should be exported.
       *
       * Currently applies to:
       * - function foo() {}
       * - var foo = function() {}
       * - foo.bar = function() {}
       * - var FOO = ...;
       * - foo.BAR = ...;
       *
       * FOO = BAR = 5;
       * and
       * var FOO = BAR = 5;
       * are not supported because the annotation is ambiguous to whether it applies
       * to all the variables or only the first one.
       *
       */
      public class FindExportableNodes extends AbstractPostOrderCallback {
      
        static final DiagnosticType NON_GLOBAL_ERROR =
            DiagnosticType.error("JSC_NON_GLOBAL_ERROR",
                "@export only applies to symbols/properties defined in the " +
                "global scope.");
      
        /**
         * It's convenient to be able to iterate over exports in the order in which
         * they are encountered.
         */
        private final LinkedHashMap exports;
      
        private final AbstractCompiler compiler;
      
        public FindExportableNodes(AbstractCompiler compiler) {
          this.compiler = compiler;
          this.exports = Maps.newLinkedHashMap();
        }
      
        @Override
        public void visit(NodeTraversal t, Node n, Node parent) {
          JSDocInfo docInfo = n.getJSDocInfo();
          if (docInfo != null && docInfo.isExport()) {
            String export = null;
            GenerateNodeContext context = null;
      
            switch (n.getType()) {
              case Token.FUNCTION:
                if (parent.isScript()) {
                  export = NodeUtil.getFunctionName(n);
                  context = new GenerateNodeContext(n, parent, n);
                }
                break;
              case Token.ASSIGN:
                Node grandparent = parent.getParent();
                if (grandparent != null && grandparent.isScript() &&
                    parent.isExprResult() &&
                    !n.getLastChild().isAssign()) {
                  export = n.getFirstChild().getQualifiedName();
                  context = new GenerateNodeContext(n, grandparent, parent);
                }
                break;
              case Token.VAR:
                if (parent.isScript()) {
                  if (n.getFirstChild().hasChildren() &&
                      !n.getFirstChild().getFirstChild().isAssign()) {
                    export = n.getFirstChild().getString();
                    context = new GenerateNodeContext(n, parent, n);
                  }
                }
            }
      
            if (export != null) {
              exports.put(export, context);
            } else {
              compiler.report(t.makeError(n, NON_GLOBAL_ERROR));
            }
          }
        }
      
        public LinkedHashMap getExports() {
          return exports;
        }
      
        /**
         * Context holding the node references required for generating the export
         * calls.
         */
        public static class GenerateNodeContext {
          private final Node scriptNode;
          private final Node contextNode;
          private final Node node;
      
          public GenerateNodeContext(Node node, Node scriptNode, Node contextNode) {
            this.node = node;
            this.scriptNode = scriptNode;
            this.contextNode = contextNode;
          }
      
          public Node getNode() {
            return node;
          }
      
          public Node getScriptNode() {
            return scriptNode;
          }
      
          public Node getContextNode() {
            return contextNode;
          }
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JSModule.java0000644000175000017500000002041412115204405025162 0ustar  apoapo/*
       * Copyright 2005 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.base.Preconditions;
      import com.google.common.collect.ImmutableList;
      import com.google.common.collect.Lists;
      import com.google.common.collect.Sets;
      import com.google.javascript.jscomp.deps.DependencyInfo;
      import com.google.javascript.jscomp.deps.SortedDependencies;
      import com.google.javascript.jscomp.deps.SortedDependencies.CircularDependencyException;
      
      import java.io.Serializable;
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.Collections;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Set;
      
      /**
       * A JavaScript module has a unique name, consists of a list of compiler inputs,
       * and can depend on other modules.
       *
       */
      public class JSModule implements DependencyInfo, Serializable {
        private static final long serialVersionUID = 1;
      
        static final DiagnosticType CIRCULAR_DEPENDENCY_ERROR =
            DiagnosticType.error("JSC_CIRCULAR_DEP",
                "Circular dependency detected: {0}");
      
        /** Module name */
        private final String name;
      
        /** Source code inputs */
        private final List inputs = new ArrayList();
      
        /** Modules that this module depends on */
        private final List deps = new ArrayList();
      
        private int depth;
        /**
         * Creates an instance.
         *
         * @param name A unique name for the module
         */
        public JSModule(String name) {
          this.name = name;
          this.depth = -1;
        }
      
        /** Gets the module name. */
        @Override
        public String getName() {
          return name;
        }
      
        @Override
        public List getProvides() {
          return ImmutableList.of(name);
        }
      
        @Override
        public List getRequires() {
          ImmutableList.Builder builder = ImmutableList.builder();
          for (JSModule m : deps) {
            builder.add(m.getName());
          }
          return builder.build();
        }
      
        @Override
        public String getPathRelativeToClosureBase() {
          throw new UnsupportedOperationException();
        }
      
        /** Adds a source file input to this module. */
        public void add(SourceFile file) {
          add(new CompilerInput(file));
        }
      
        /** Adds a source file input to this module. */
        public void addFirst(SourceFile file) {
          addFirst(new CompilerInput(file));
        }
      
        /** Adds a source code input to this module. */
        public void add(CompilerInput input) {
          inputs.add(input);
          input.setModule(this);
        }
      
        /**
         * Adds a source code input to this module. Call only if the input might
         * already be associated with a module. Otherwise, use
         * add(CompilerInput input).
         */
        void addAndOverrideModule(CompilerInput input) {
          inputs.add(input);
          input.overrideModule(this);
        }
      
        /** Adds a source code input to this module. */
        public void addFirst(CompilerInput input) {
          inputs.add(0, input);
          input.setModule(this);
        }
      
        /** Adds a source code input to this module directly after other. */
        public void addAfter(CompilerInput input, CompilerInput other) {
          Preconditions.checkState(inputs.contains(other));
          inputs.add(inputs.indexOf(other), input);
          input.setModule(this);
        }
      
        /** Adds a dependency on another module. */
        public void addDependency(JSModule dep) {
          Preconditions.checkNotNull(dep);
          Preconditions.checkState(dep != this);
          deps.add(dep);
        }
      
        /** Removes an input from this module. */
        public void remove(CompilerInput input) {
          input.setModule(null);
          inputs.remove(input);
        }
      
        /** Removes all of the inputs from this module. */
        public void removeAll() {
          for (CompilerInput input : inputs) {
            input.setModule(null);
          }
          inputs.clear();
        }
      
        /**
         * Gets the list of modules that this module depends on.
         *
         * @return A list that may be empty but not null
         */
        public List getDependencies() {
          return deps;
        }
      
        /**
         * Gets the names of the modules that this module depends on,
         * sorted alphabetically.
         */
        List getSortedDependencyNames() {
          List names = Lists.newArrayList();
          for (JSModule module : getDependencies()) {
            names.add(module.getName());
          }
          Collections.sort(names);
          return names;
        }
      
        /**
         * Returns the transitive closure of dependencies starting from the
         * dependencies of this module.
         */
        public Set getAllDependencies() {
          Set allDeps = Sets.newHashSet(deps);
          List workList = Lists.newArrayList(deps);
          while (workList.size() > 0) {
            JSModule module = workList.remove(workList.size() - 1);
            for (JSModule dep : module.getDependencies()) {
              if (allDeps.add(dep)) {
                workList.add(dep);
              }
            }
          }
          return allDeps;
        }
      
        /** Returns this module and all of its dependencies in one list. */
        public Set getThisAndAllDependencies() {
          Set deps = getAllDependencies();
          deps.add(this);
          return deps;
        }
      
        /**
         * Gets this module's list of source code inputs.
         *
         * @return A list that may be empty but not null
         */
        public List getInputs() {
          return inputs;
        }
      
        /** Returns the input with the given name or null if none. */
        public CompilerInput getByName(String name) {
          for (CompilerInput input : inputs) {
            if (name.equals(input.getName())) {
              return input;
            }
          }
          return null;
        }
      
        /**
         * Removes any input with the given name. Returns whether any were removed.
         */
        public boolean removeByName(String name) {
          boolean found = false;
          Iterator iter = inputs.iterator();
          while (iter.hasNext()) {
            CompilerInput file = iter.next();
            if (name.equals(file.getName())) {
              iter.remove();
              file.setModule(null);
              found = true;
            }
          }
          return found;
        }
      
        /** Returns the module name (primarily for debugging). */
        @Override
        public String toString() {
          return name;
        }
      
        /**
         * Removes any references to nodes of the AST.  This method is needed to
         * allow the ASTs to be garbage collected if the modules are kept around.
         */
        public void clearAsts() {
          for (CompilerInput input : inputs) {
            input.clearAst();
          }
        }
      
        /**
         * Puts the JS files into a topologically sorted order by their dependencies.
         */
        public void sortInputsByDeps(Compiler compiler) {
          // Set the compiler, so that we can parse requires/provides and report
          // errors properly.
          for (CompilerInput input : inputs) {
            input.setCompiler(compiler);
          }
      
          // Sort the JSModule in this order.
          try {
            List sortedList =
                (new SortedDependencies(
                    Collections.unmodifiableList(inputs)))
                .getSortedList();
            inputs.clear();
            inputs.addAll(sortedList);
          } catch (CircularDependencyException e) {
            compiler.report(
                JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage()));
          }
        }
      
        /**
         * Returns the given collection of modules in topological order.
         *
         * Note that this will return the modules in the same order if they are
         * already sorted, and in general, will only change the order as necessary to
         * satisfy the ordering dependencies.  This can be important for cases where
         * the modules do not properly specify all dependencies.
         */
        public static JSModule[] sortJsModules(Collection modules)
            throws CircularDependencyException {
          // Sort the JSModule in this order.
          List sortedList = (new SortedDependencies(
                  Lists.newArrayList(modules))).getSortedList();
          return sortedList.toArray(new JSModule[sortedList.size()]);
        }
      
        /**
         * @param dep the depth to set
         */
        public void setDepth(int dep) {
          this.depth = dep;
        }
      
        /**
         * @return the depth
         */
        public int getDepth() {
          return depth;
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AbstractPeepholeOptimization.java0000644000175000017500000001213512115204405031335 0ustar  apoapo/*
       * 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.common.annotations.VisibleForTesting;
      import com.google.common.base.Preconditions;
      import com.google.javascript.rhino.Node;
      
      /**
       * An abstract class whose implementations run peephole optimizations:
       * optimizations that look at a small section of code and either remove
       * that code (if it is not needed) or replaces it with smaller code.
       *
       */
      abstract class AbstractPeepholeOptimization {
      
        private AbstractCompiler compiler;
      
        /**
         * Given a node to optimize and a traversal, optimize the node. Subclasses
         * should override to provide their own peephole optimization.
         *
         * @param subtree The subtree that will be optimized.
         * @return The new version of the subtree (or null if the subtree or one of
         * its parents was removed from the AST). If the subtree has not changed,
         * this method must return {@code subtree}.
         */
        abstract Node optimizeSubtree(Node subtree);
      
        /**
         * Helper method for reporting an error to the compiler when applying a
         * peephole optimization.
         *
         * @param diagnostic The error type
         * @param n The node for which the error should be reported
         */
        protected void report(DiagnosticType diagnostic, Node n) {
          JSError error =
              JSError.make(NodeUtil.getSourceName(n), n, diagnostic, n.toString());
          compiler.report(error);
        }
      
        /**
         * Helper method for telling the compiler that something has changed.
         * Subclasses must call these if they have changed the AST.
         */
        protected void reportCodeChange() {
          Preconditions.checkNotNull(compiler);
          compiler.reportCodeChange();
        }
      
        /**
         * Are the nodes equal for the purpose of inlining?
         * If type aware optimizations are on, type equality is checked.
         */
        protected boolean areNodesEqualForInlining(Node n1, Node n2) {
          /* Our implementation delegates to the compiler. We provide this
           * method because we don't want to expose Compiler to PeepholeOptimizations.
           */
          Preconditions.checkNotNull(compiler);
          return compiler.areNodesEqualForInlining(n1, n2);
        }
      
        /**
         *  Is the current AST normalized? (e.g. has the Normalize pass been run
         *  and has the Denormalize pass not yet been run?)
         */
        protected boolean isASTNormalized() {
          Preconditions.checkNotNull(compiler);
      
          return compiler.getLifeCycleStage().isNormalized();
        }
      
        /**
         * Informs the optimization that a traversal will begin.
         */
        void beginTraversal(AbstractCompiler compiler) {
          this.compiler = compiler;
        }
      
        /**
         * Informs the optimization that a traversal has completed.
         * @param compiler The current compiler.
         */
        void endTraversal(AbstractCompiler compiler) {
          this.compiler = null;
        }
      
        // NodeUtil's mayEffectMutableState and mayHaveSideEffects need access to the
        // compiler object, route them through here to give them access.
      
        /**
         * @return Whether the node may create new mutable state, or change existing
         * state.
         */
        boolean mayEffectMutableState(Node n) {
          return NodeUtil.mayEffectMutableState(n, compiler);
        }
      
        /**
         * @return Whether the node may have side effects when executed.
         */
        boolean mayHaveSideEffects(Node n) {
          return NodeUtil.mayHaveSideEffects(n, compiler);
        }
      
        /**
         * Returns true if the current node's type implies side effects.
         *
         * This is a non-recursive version of the may have side effects
         * check; used to check wherever the current node's type is one of
         * the reason's why a subtree has side effects.
         */
        boolean nodeTypeMayHaveSideEffects(Node n) {
          return NodeUtil.nodeTypeMayHaveSideEffects(n, compiler);
        }
      
        /**
         * @return Whether the source code version is ECMAScript 5 or later.
         *     Workarounds for quirks in browsers that do not support ES5 can be
         *     ignored when this is true.
         */
        boolean isEcmaScript5OrGreater() {
          return compiler != null
              && compiler.acceptEcmaScript5();
        }
      
        /**
         * @return the current coding convention.
         */
        CodingConvention getCodingConvention() {
          // Note: this assumes a thread safe coding convention object.
          return compiler.getCodingConvention();
        }
      
        /**
         * Check if the specified node is null or is still in the AST.
         */
        @VisibleForTesting
        static Node validateResult(Node n) {
          done: {
            if (n != null && !n.isScript()
                && (!n.isBlock() || !n.isSyntheticBlock())) {
              for (Node parent : n.getAncestors()) {
                if (parent.isScript()) {
                  break done;
                }
              }
              Preconditions.checkState(false);
            }
          }
          return n;
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReplaceMessagesForChrome.java0000644000175000017500000000770512115204405030360 0ustar  apoapo/*
       * Copyright 2012 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.JsMessage.PlaceholderReference;
      import com.google.javascript.rhino.IR;
      import com.google.javascript.rhino.Node;
      import com.google.javascript.rhino.Token;
      
      import java.util.Collections;
      import java.util.List;
      
      
      /**
       * Replaces user-visible messages with appropriate calls to
       * chrome.i18n.getMessage. The first argument to getMessage is the id of the
       * message, as a string. If the message contains placeholders, the second
       * argument is an array of the values being used for the placeholders, in the
       * order they appear in the source code.
       *
       */
      class ReplaceMessagesForChrome extends JsMessageVisitor {
      
        ReplaceMessagesForChrome(AbstractCompiler compiler,
            JsMessage.IdGenerator idGenerator,
            boolean checkDuplicatedMessages, JsMessage.Style style) {
      
          super(compiler, checkDuplicatedMessages, style, idGenerator);
        }
      
        private static Node getChromeI18nGetMessageNode(String messageId) {
          Node chromeI18n = IR.getprop(IR.name("chrome"), IR.string("i18n"));
          Node getMessage =  IR.getprop(chromeI18n, IR.string("getMessage"));
          return IR.call(getMessage, IR.string(messageId));
        }
      
        @Override
        protected void processJsMessage(
            JsMessage message, JsMessageDefinition definition) {
          try {
            Node msgNode = definition.getMessageNode();
            Node newValue = getNewValueNode(msgNode, message);
            newValue.copyInformationFromForTree(msgNode);
      
            definition.getMessageParentNode().replaceChild(msgNode, newValue);
            compiler.reportCodeChange();
          } catch (MalformedException e) {
            compiler.report(JSError.make(message.getSourceName(), e.getNode(),
                MESSAGE_TREE_MALFORMED, e.getMessage()));
          }
        }
      
        private Node getNewValueNode(Node origNode, JsMessage message)
            throws MalformedException {
          Node newValueNode = getChromeI18nGetMessageNode(message.getId());
      
          if (!message.placeholders().isEmpty()) {
            Node placeholderValues = origNode.getLastChild();
            checkNode(placeholderValues, Token.OBJECTLIT);
      
            // Output the placeholders, sorted alphabetically by placeholder name,
            // regardless of what order they appear in the original message.
            List placeholderNames = Lists.newArrayList();
            for (CharSequence cs : message.parts()) {
              if (cs instanceof PlaceholderReference) {
                String placeholderName = ((PlaceholderReference) cs).getName();
                placeholderNames.add(placeholderName);
              }
            }
            Collections.sort(placeholderNames);
      
            Node placeholderValueArray = IR.arraylit();
            for (String name : placeholderNames) {
              Node value = getPlaceholderValue(placeholderValues, name);
              if (value == null) {
                throw new MalformedException(
                    "No value was provided for placeholder " + name,
                    origNode);
              }
              placeholderValueArray.addChildToBack(value);
            }
            newValueNode.addChildToBack(placeholderValueArray);
          }
      
          newValueNode.copyInformationFromForTree(origNode);
          return newValueNode;
        }
      
        private Node getPlaceholderValue(
            Node placeholderValues, String placeholderName) {
          for (Node key : placeholderValues.children()) {
            if (key.getString().equals(placeholderName)) {
              return key.getFirstChild().cloneTree();
            }
          }
          return null;
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineVariables.java0000644000175000017500000006252712115204405026562 0ustar  apoapo/*
       * 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.base.Preconditions;
      import com.google.common.base.Predicate;
      import com.google.common.base.Predicates;
      import com.google.common.collect.Maps;
      import com.google.common.collect.Sets;
      import com.google.javascript.jscomp.CodingConvention.SubclassRelationship;
      import com.google.javascript.jscomp.ReferenceCollectingCallback.Behavior;
      import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference;
      import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection;
      import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceMap;
      import com.google.javascript.jscomp.Scope.Var;
      import com.google.javascript.rhino.Node;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      
      
      /**
       * Using the infrastructure provided by VariableReferencePass, identify
       * variables that are used only once and in a way that is safe to move, and then
       * inline them.
       *
       * This pass has two "modes." One mode only inlines variables declared as
       * constants, for legacy compiler clients. The second mode inlines any
       * variable that we can provably inline. Note that the second mode is a
       * superset of the first mode. We only support the first mode for
       * backwards-compatibility with compiler clients that don't want
       * --inline_variables.
       *
       * The approach of this pass is similar to {@link CrossModuleCodeMotion}
       *
       * @author kushal@google.com (Kushal Dave)
       * @author nicksantos@google.com (Nick Santos)
       */
      class InlineVariables implements CompilerPass {
      
        private final AbstractCompiler compiler;
      
        enum Mode {
          // Only inline things explicitly marked as constant.
          CONSTANTS_ONLY,
          // Locals only
          LOCALS_ONLY,
          ALL
        }
      
        private final Mode mode;
      
        // Inlines all strings, even if they increase the size of the gzipped binary.
        private final boolean inlineAllStrings;
      
        private final IdentifyConstants identifyConstants = new IdentifyConstants();
      
        InlineVariables(
            AbstractCompiler compiler,
            Mode mode,
            boolean inlineAllStrings) {
          this.compiler = compiler;
          this.mode = mode;
          this.inlineAllStrings = inlineAllStrings;
        }
      
        @Override
        public void process(Node externs, Node root) {
          ReferenceCollectingCallback callback = new ReferenceCollectingCallback(
              compiler, new InliningBehavior(), getFilterForMode());
          callback.process(externs, root);
        }
      
        private Predicate getFilterForMode() {
          switch (mode) {
            case ALL:
              return Predicates.alwaysTrue();
            case LOCALS_ONLY:
              return new IdentifyLocals();
            case CONSTANTS_ONLY:
              return new IdentifyConstants();
            default:
              throw new IllegalStateException();
          }
        }
      
        /**
         * Filters variables declared as "constant", and declares them in the outer
         * declaredConstants map.
         *
         * In Google coding conventions, this means anything declared with @const
         * or named in all caps, and initialized to an immutable value.
         * CheckConsts has already verified that these are truly constants.
         */
        private class IdentifyConstants implements Predicate {
          @Override
          public boolean apply(Var var) {
            return var.isConst();
          }
        }
      
        /**
         * Filters non-global variables.
         */
        private class IdentifyLocals implements Predicate {
          @Override
          public boolean apply(Var var) {
            return var.scope.isLocal();
          }
        }
      
        private static class AliasCandidate {
          private final Var alias;
          private final ReferenceCollection refInfo;
      
          AliasCandidate(Var alias, ReferenceCollection refInfo) {
            this.alias = alias;
            this.refInfo = refInfo;
          }
        }
      
        /**
         * Builds up information about nodes in each scope. When exiting the
         * scope, inspects all variables in that scope, and inlines any
         * that we can.
         */
        private class InliningBehavior implements Behavior {
      
          /**
           * A list of variables that should not be inlined, because their
           * reference information is out of sync with the state of the AST.
           */
          private final Set staleVars = Sets.newHashSet();
      
          /**
           * Stored possible aliases of variables that never change, with
           * all the reference info about those variables. Hashed by the NAME
           * node of the variable being aliased.
           */
          final Map aliasCandidates = Maps.newHashMap();
      
          @Override
          public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {
            collectAliasCandidates(t, referenceMap);
            doInlinesForScope(t, referenceMap);
          }
      
          /**
           * If any of the variables are well-defined and alias other variables,
           * mark them as aliasing candidates.
           */
          private void collectAliasCandidates(NodeTraversal t,
              ReferenceMap referenceMap) {
            if (mode != Mode.CONSTANTS_ONLY) {
              for (Iterator it = t.getScope().getVars(); it.hasNext();) {
                Var v = it.next();
                ReferenceCollection referenceInfo = referenceMap.getReferences(v);
      
                // NOTE(nicksantos): Don't handle variables that are never used.
                // The tests are much easier to write if you don't, and there's
                // another pass that handles unused variables much more elegantly.
                if (referenceInfo != null && referenceInfo.references.size() >= 2 &&
                    referenceInfo.isWellDefined() &&
                    referenceInfo.isAssignedOnceInLifetime()) {
                  Reference init = referenceInfo.getInitializingReference();
                  Node value = init.getAssignedValue();
                  if (value != null && value.isName()) {
                    aliasCandidates.put(value, new AliasCandidate(v, referenceInfo));
                  }
                }
              }
            }
          }
      
          /**
           * For all variables in this scope, see if they are only used once.
           * If it looks safe to do so, inline them.
           */
          private void doInlinesForScope(NodeTraversal t, ReferenceMap referenceMap) {
      
            boolean maybeModifiedArguments =
                maybeEscapedOrModifiedArguments(t.getScope(), referenceMap);
            for (Iterator it = t.getScope().getVars(); it.hasNext();) {
              Var v = it.next();
      
              ReferenceCollection referenceInfo = referenceMap.getReferences(v);
      
              // referenceInfo will be null if we're in constants-only mode
              // and the variable is not a constant.
              if (referenceInfo == null || isVarInlineForbidden(v)) {
                // Never try to inline exported variables or variables that
                // were not collected or variables that have already been inlined.
                continue;
              } else if (isInlineableDeclaredConstant(v, referenceInfo)) {
                Reference init = referenceInfo.getInitializingReferenceForConstants();
                Node value = init.getAssignedValue();
                inlineDeclaredConstant(v, value, referenceInfo.references);
                staleVars.add(v);
              } else if (mode == Mode.CONSTANTS_ONLY) {
                // If we're in constants-only mode, don't run more aggressive
                // inlining heuristics. See InlineConstantsTest.
                continue;
              } else {
                inlineNonConstants(v, referenceInfo, maybeModifiedArguments);
              }
            }
          }
      
          private boolean maybeEscapedOrModifiedArguments(
              Scope scope, ReferenceMap referenceMap) {
            if (scope.isLocal()) {
              Var arguments = scope.getArgumentsVar();
              ReferenceCollection refs = referenceMap.getReferences(arguments);
              if (refs != null && !refs.references.isEmpty()) {
                for (Reference ref : refs.references) {
                  Node refNode = ref.getNode();
                  Node refParent = ref.getParent();
                  // Any reference that is not a read of the arguments property
                  // consider a escape of the arguments object.
                  if (!(NodeUtil.isGet(refParent)
                      && refNode == ref.getParent().getFirstChild()
                      && !isLValue(refParent))) {
                    return true;
                  }
                }
              }
            }
            return false;
          }
      
          private boolean isLValue(Node n) {
            Node parent = n.getParent();
            return (parent.isInc()
                || parent.isDec()
                || (NodeUtil.isAssignmentOp(parent)
                && parent.getFirstChild() == n));
          }
      
          private void inlineNonConstants(
              Var v, ReferenceCollection referenceInfo,
              boolean maybeModifiedArguments) {
            int refCount = referenceInfo.references.size();
            Reference declaration = referenceInfo.references.get(0);
            Reference init = referenceInfo.getInitializingReference();
            int firstRefAfterInit = (declaration == init) ? 2 : 3;
      
            if (refCount > 1 &&
                isImmutableAndWellDefinedVariable(v, referenceInfo)) {
              // if the variable is referenced more than once, we can only
              // inline it if it's immutable and never defined before referenced.
              Node value;
              if (init != null) {
                value = init.getAssignedValue();
              } else {
                // Create a new node for variable that is never initialized.
                Node srcLocation = declaration.getNode();
                value = NodeUtil.newUndefinedNode(srcLocation);
              }
              Preconditions.checkNotNull(value);
              inlineWellDefinedVariable(v, value, referenceInfo.references);
              staleVars.add(v);
            } else if (refCount == firstRefAfterInit) {
              // The variable likely only read once, try some more
              // complex inlining heuristics.
              Reference reference = referenceInfo.references.get(
                  firstRefAfterInit - 1);
              if (canInline(declaration, init, reference)) {
                inline(v, declaration, init, reference);
                staleVars.add(v);
              }
            } else if (declaration != init && refCount == 2) {
              if (isValidDeclaration(declaration) && isValidInitialization(init)) {
                // The only reference is the initialization, remove the assignment and
                // the variable declaration.
                Node value = init.getAssignedValue();
                Preconditions.checkNotNull(value);
                inlineWellDefinedVariable(v, value, referenceInfo.references);
                staleVars.add(v);
              }
            }
      
            // If this variable was not inlined normally, check if we can
            // inline an alias of it. (If the variable was inlined, then the
            // reference data is out of sync. We're better off just waiting for
            // the next pass.)
            if (!maybeModifiedArguments &&
                !staleVars.contains(v) && referenceInfo.isWellDefined() &&
                referenceInfo.isAssignedOnceInLifetime()) {
              List refs = referenceInfo.references;
              for (int i = 1 /* start from a read */; i < refs.size(); i++) {
                Node nameNode = refs.get(i).getNode();
                if (aliasCandidates.containsKey(nameNode)) {
                  AliasCandidate candidate = aliasCandidates.get(nameNode);
                  if (!staleVars.contains(candidate.alias) &&
                      !isVarInlineForbidden(candidate.alias)) {
                    Reference aliasInit;
                    aliasInit = candidate.refInfo.getInitializingReference();
                    Node value = aliasInit.getAssignedValue();
                    Preconditions.checkNotNull(value);
                    inlineWellDefinedVariable(candidate.alias,
                        value,
                        candidate.refInfo.references);
                    staleVars.add(candidate.alias);
                  }
                }
              }
            }
          }
      
          /**
           * If there are any variable references in the given node tree, blacklist
           * them to prevent the pass from trying to inline the variable.
           */
          private void blacklistVarReferencesInTree(Node root, Scope scope) {
            for (Node c = root.getFirstChild(); c != null; c = c.getNext()) {
              blacklistVarReferencesInTree(c, scope);
            }
      
            if (root.isName()) {
              staleVars.add(scope.getVar(root.getString()));
            }
          }
      
          /**
           * Whether the given variable is forbidden from being inlined.
           */
          private boolean isVarInlineForbidden(Var var) {
            // A variable may not be inlined if:
            // 1) The variable is exported,
            // 2) A reference to the variable has been inlined. We're downstream
            //    of the mechanism that creates variable references, so we don't
            //    have a good way to update the reference. Just punt on it.
            // 3) Don't inline the special RENAME_PROPERTY_FUNCTION_NAME
            return var.isExtern()
                || compiler.getCodingConvention().isExported(var.name)
                || RenameProperties.RENAME_PROPERTY_FUNCTION_NAME.equals(var.name)
                || staleVars.contains(var);
          }
      
          /**
           * Do the actual work of inlining a single declaration into a single
           * reference.
           */
          private void inline(Var v, Reference declaration,
                              Reference init, Reference reference) {
            Node value = init.getAssignedValue();
            Preconditions.checkState(value != null);
            // Check for function declarations before the value is moved in the AST.
            boolean isFunctionDeclaration = NodeUtil.isFunctionDeclaration(value);
      
            inlineValue(v, reference, value.detachFromParent());
            if (declaration != init) {
              Node expressRoot = init.getGrandparent();
              Preconditions.checkState(expressRoot.isExprResult());
              NodeUtil.removeChild(expressRoot.getParent(), expressRoot);
            }
      
            // Function declarations have already been removed.
            if (!isFunctionDeclaration) {
              removeDeclaration(declaration);
            } else {
              compiler.reportCodeChange();
            }
          }
      
          /**
           * Inline an immutable variable into all of its references.
           */
          private void inlineWellDefinedVariable(Var v, Node value,
              List refSet) {
            Reference decl = refSet.get(0);
            for (int i = 1; i < refSet.size(); i++) {
              inlineValue(v, refSet.get(i), value.cloneTree());
            }
            removeDeclaration(decl);
          }
      
          /**
           * Inline a declared constant.
           */
          private void inlineDeclaredConstant(Var v, Node value,
              List refSet) {
            // Replace the references with the constant value
            Reference decl = null;
      
            for (Reference r : refSet) {
              if (r.getNode() == v.getNameNode()) {
                decl = r;
              } else {
                inlineValue(v, r, value.cloneTree());
              }
            }
      
            removeDeclaration(decl);
          }
      
          /**
           * Remove the given VAR declaration.
           */
          private void removeDeclaration(Reference declaration) {
            Node varNode = declaration.getParent();
            Node grandparent = declaration.getGrandparent();
      
            varNode.removeChild(declaration.getNode());
      
            // Remove var node if empty
            if (!varNode.hasChildren()) {
              Preconditions.checkState(varNode.isVar());
              NodeUtil.removeChild(grandparent, varNode);
            }
      
            compiler.reportCodeChange();
          }
      
          /**
           * Replace the given reference with the given value node.
           *
           * @param v The variable that's referenced.
           * @param ref The reference to replace.
           * @param value The node tree to replace it with. This tree should be safe
           *     to re-parent.
           */
          private void inlineValue(Var v, Reference ref, Node value) {
            if (ref.isSimpleAssignmentToName()) {
              // This is the initial assignment.
              ref.getGrandparent().replaceChild(ref.getParent(), value);
            } else {
              ref.getParent().replaceChild(ref.getNode(), value);
            }
      
            blacklistVarReferencesInTree(value, v.scope);
            compiler.reportCodeChange();
          }
      
          /**
           * Determines whether the given variable is declared as a constant
           * and may be inlined.
           */
          private boolean isInlineableDeclaredConstant(Var var,
              ReferenceCollection refInfo) {
            if (!identifyConstants.apply(var)) {
              return false;
            }
      
            if (!refInfo.isAssignedOnceInLifetime()) {
              return false;
            }
      
            Reference init = refInfo.getInitializingReferenceForConstants();
            if (init == null) {
              return false;
            }
      
            Node value = init.getAssignedValue();
            if (value == null) {
              // This constant is either externally defined or initialized indirectly
              // (e.g. in an function expression used to hide
              // temporary variables), so the constant is ineligible for inlining.
              return false;
            }
      
            // Is the constant's value immutable?
            if (!NodeUtil.isImmutableValue(value)) {
              return false;
            }
      
            // Determine if we should really inline a String or not.
            return !value.isString() ||
                isStringWorthInlining(var, refInfo.references);
          }
      
          /**
           * Compute whether the given string is worth inlining.
           */
          private boolean isStringWorthInlining(Var var, List refs) {
            if (!inlineAllStrings && !var.isDefine()) {
              int len = var.getInitialValue().getString().length() + "''".length();
      
              // if not inlined: var xx="value"; .. xx .. xx ..
              // The 4 bytes per reference is just a heuristic:
              // 2 bytes per var name plus maybe 2 bytes if we don't inline, e.g.
              // in the case of "foo " + CONST + " bar"
              int noInlineBytes = "var xx=;".length() + len +
                                  4 * (refs.size() - 1);
      
              // if inlined:
              // I'm going to assume that half of the quotes will be eliminated
              // thanks to constant folding, therefore I subtract 1 (2/2=1) from
              // the string length.
              int inlineBytes = (len - 1) * (refs.size() - 1);
      
              // Not inlining if doing so uses more bytes, or this constant is being
              // defined.
              return noInlineBytes >= inlineBytes;
            }
      
            return true;
          }
      
          /**
           * @return true if the provided reference and declaration can be safely
           *         inlined according to our criteria
           */
          private boolean canInline(
              Reference declaration,
              Reference initialization,
              Reference reference) {
            if (!isValidDeclaration(declaration)
                || !isValidInitialization(initialization)
                || !isValidReference(reference)) {
              return false;
            }
      
            // If the value is read more than once, skip it.
            // VAR declarations and EXPR_RESULT don't need the value, but other
            // ASSIGN expressions parents do.
            if (declaration != initialization &&
                !initialization.getGrandparent().isExprResult()) {
              return false;
            }
      
            // Be very conservative and do no cross control structures or
            // scope boundaries
            if (declaration.getBasicBlock() != initialization.getBasicBlock()
                || declaration.getBasicBlock() != reference.getBasicBlock()) {
              return false;
            }
      
            // Do not inline into a call node. This would change
            // the context in which it was being called. For example,
            //   var a = b.c;
            //   a();
            // should not be inlined, because it calls a in the context of b
            // rather than the context of the window.
            //   var a = b.c;
            //   f(a)
            // is OK.
            Node value = initialization.getAssignedValue();
            Preconditions.checkState(value != null);
            if (value.isGetProp()
                && reference.getParent().isCall()
                && reference.getParent().getFirstChild() == reference.getNode()) {
              return false;
            }
      
            if (value.isFunction()) {
              Node callNode = reference.getParent();
              if (reference.getParent().isCall()) {
                CodingConvention convention = compiler.getCodingConvention();
                // Bug 2388531: Don't inline subclass definitions into class defining
                // calls as this confused class removing logic.
                SubclassRelationship relationship =
                    convention.getClassesDefinedByCall(callNode);
                if (relationship != null) {
                  return false;
                }
      
                // issue 668: Don't inline singleton getter methods
                // calls as this confused class removing logic.
                if (convention.getSingletonGetterClassName(callNode) != null) {
                  return false;
                }
              }
            }
      
            return canMoveAggressively(value) ||
                canMoveModerately(initialization, reference);
          }
      
          /**
           * If the value is a literal, we can cross more boundaries to inline it.
           */
          private boolean canMoveAggressively(Node value) {
            // Function expressions and other mutable objects can move within
            // the same basic block.
            return NodeUtil.isLiteralValue(value, true)
                || value.isFunction();
          }
      
          /**
           * If the value of a variable is not constant, then it may read or modify
           * state. Therefore it cannot be moved past anything else that may modify
           * the value being read or read values that are modified.
           */
          private boolean canMoveModerately(
              Reference initialization,
              Reference reference) {
            // Check if declaration can be inlined without passing
            // any side-effect causing nodes.
            Iterator it;
            if (initialization.getParent().isVar()) {
              it = NodeIterators.LocalVarMotion.forVar(
                  initialization.getNode(),     // NAME
                  initialization.getParent(),       // VAR
                  initialization.getGrandparent()); // VAR container
            } else if (initialization.getParent().isAssign()) {
              Preconditions.checkState(
                  initialization.getGrandparent().isExprResult());
              it = NodeIterators.LocalVarMotion.forAssign(
                  initialization.getNode(),     // NAME
                  initialization.getParent(),       // ASSIGN
                  initialization.getGrandparent(),  // EXPR_RESULT
                  initialization.getGrandparent().getParent()); // EXPR container
            } else {
              throw new IllegalStateException("Unexpected initialization parent " +
                  initialization.getParent().toStringTree());
            }
            Node targetName = reference.getNode();
            while (it.hasNext()) {
              Node curNode = it.next();
              if (curNode == targetName) {
                return true;
              }
            }
      
            return false;
          }
      
          /**
           * @return true if the reference is a normal VAR or FUNCTION declaration.
           */
          private boolean isValidDeclaration(Reference declaration) {
            return (declaration.getParent().isVar()
                && !declaration.getGrandparent().isFor())
                || NodeUtil.isFunctionDeclaration(declaration.getParent());
          }
      
          /**
           * @return Whether there is a initial value.
           */
          private boolean isValidInitialization(Reference initialization) {
            if (initialization == null) {
              return false;
            } else if (initialization.isDeclaration()) {
              // The reference is a FUNCTION declaration or normal VAR declaration
              // with a value.
              if (!NodeUtil.isFunctionDeclaration(initialization.getParent())
                  && initialization.getNode().getFirstChild() == null) {
                return false;
              }
            } else {
              Node parent = initialization.getParent();
              Preconditions.checkState(
                  parent.isAssign()
                  && parent.getFirstChild() == initialization.getNode());
            }
      
            Node n = initialization.getAssignedValue();
            if (n.isFunction()) {
              return compiler.getCodingConvention().isInlinableFunction(n);
            }
      
            return true;
          }
      
          /**
           * @return true if the reference is a candidate for inlining
           */
          private boolean isValidReference(Reference reference) {
            return !reference.isDeclaration() && !reference.isLvalue();
          }
      
          /**
           * Determines whether the reference collection describes a variable that
           * is initialized to an immutable value, never modified, and defined before
           * every reference.
           */
          private boolean isImmutableAndWellDefinedVariable(Var v,
              ReferenceCollection refInfo) {
            List refSet = refInfo.references;
            int startingReadRef = 1;
            Reference refDecl = refSet.get(0);
            if (!isValidDeclaration(refDecl)) {
              return false;
            }
      
            boolean isNeverAssigned = refInfo.isNeverAssigned();
            // For values that are never assigned, only the references need to be
            // checked.
            if (!isNeverAssigned) {
              Reference refInit = refInfo.getInitializingReference();
              if (!isValidInitialization(refInit)) {
                return false;
              }
      
              if (refDecl != refInit) {
                Preconditions.checkState(refInit == refSet.get(1));
                startingReadRef = 2;
              }
      
              if (!refInfo.isWellDefined()) {
                return false;
              }
      
              Node value = refInit.getAssignedValue();
              Preconditions.checkNotNull(value);
      
              boolean isImmutableValueWorthInlining =
                  NodeUtil.isImmutableValue(value) &&
                  (!value.isString() ||
                      isStringWorthInlining(v, refInfo.references));
              boolean isInlinableThisAlias =
                  value.isThis() &&
                  !refInfo.isEscaped();
              if (!isImmutableValueWorthInlining && !isInlinableThisAlias) {
                return false;
              }
            }
      
            for (int i = startingReadRef; i < refSet.size(); i++) {
              Reference ref = refSet.get(i);
              if (!isValidReference(ref)) {
                return false;
              }
            }
      
            return true;
          }
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/JSSourceFile.java0000644000175000017500000000544612115204405026005 0ustar  apoapo/*
       * 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.annotations.VisibleForTesting;
      import com.google.common.base.Charsets;
      
      import java.io.File;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.Serializable;
      import java.nio.charset.Charset;
      
      /**
       * An abstract representation of a JavaScript source file, as input to
       * JSCompiler.
       *
       * @author nicksantos@google.com (Nick Santos)
       * @author moedinger@google.com (Andrew Moedinger)
       * @deprecated JSSourceFile is an empty wrapper around SourceFile. Just
       *     use SourceFile directly.
       */
      // TODO(nicksantos): Delete this file.
      @Deprecated
      public class JSSourceFile extends SourceFile implements Serializable {
        private static final long serialVersionUID = 1L;
      
        public static JSSourceFile fromFile(String fileName, Charset charSet) {
          return new JSSourceFile(SourceFile.fromFile(fileName, charSet));
        }
      
        public static JSSourceFile fromFile(String fileName) {
          return new JSSourceFile(SourceFile.fromFile(fileName, Charsets.UTF_8));
        }
      
        public static JSSourceFile fromFile(File file, Charset charSet) {
          return new JSSourceFile(SourceFile.fromFile(file, charSet));
        }
      
        public static JSSourceFile fromFile(File file) {
          return new JSSourceFile(SourceFile.fromFile(file, Charsets.UTF_8));
        }
      
        public static JSSourceFile fromCode(String fileName, String code) {
          return new JSSourceFile(SourceFile.fromCode(fileName, code));
        }
      
        public static JSSourceFile fromInputStream(String fileName, InputStream s)
            throws IOException {
          return new JSSourceFile(SourceFile.fromInputStream(fileName, s));
        }
      
        public static JSSourceFile fromGenerator(String fileName,
            Generator generator) {
          return new JSSourceFile(SourceFile.fromGenerator(fileName, generator));
        }
      
      
        private SourceFile referenced;
      
        private JSSourceFile(SourceFile referenced) {
          super(referenced.getName());
          this.referenced = referenced;
        }
      
        @Override
        public String getCode() throws IOException {
          return referenced.getCode();
        }
      
        @Override
        public void clearCachedSource() {
          referenced.clearCachedSource();
        }
      
        @Override
        @VisibleForTesting
        String getCodeNoCache() {
          return referenced.getCodeNoCache();
        }
      }
      closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CompilerOptions.java0000644000175000017500000020013712115204405026630 0ustar  apoapo/*
       * 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.base.Preconditions;
      import com.google.common.collect.ImmutableSet;
      import com.google.common.collect.Lists;
      import com.google.common.collect.Maps;
      import com.google.common.collect.Multimap;
      import com.google.common.collect.Sets;
      import com.google.javascript.rhino.IR;
      import com.google.javascript.rhino.Node;
      import com.google.javascript.rhino.SourcePosition;
      
      import java.io.Serializable;
      import java.nio.charset.Charset;
      import java.text.ParseException;
      import java.util.Collections;
      import java.util.List;
      import java.util.Map;
      import java.util.Set;
      
      /**
       * Compiler options
       * @author nicksantos@google.com (Nick Santos)
       */
      public class CompilerOptions implements Serializable, Cloneable {
      
        // Unused. For people using reflection to circumvent access control.
        @SuppressWarnings("unused")
        private boolean manageClosureDependencies = false;
      
        // A common enum for compiler passes that can run either globally or locally.
        public enum Reach {
          ALL,
          LOCAL_ONLY,
          NONE
        }
      
        // TODO(nicksantos): All public properties of this class should be made
        // package-private, and have a public setter.
      
        private static final long serialVersionUID = 7L;
      
        /**
         * The JavaScript language version accepted.
         */
        private LanguageMode languageIn;
      
        /**
         * The JavaScript language version that should be produced.
         * Currently, this is always the same as {@link #languageIn}.
         */
        private LanguageMode languageOut;
      
        /**
         * Whether the compiler accepts the `const' keyword.
         */
        boolean acceptConstKeyword;
      
        /**
         * Whether the compiler should assume that a function's "this" value
         * never needs coercion (for example in non-strict "null" or "undefined" will
         * be coerced to the global "this" and primitives to objects).
         */
        private boolean assumeStrictThis;
      
        /**
         * Configures the compiler for use as an IDE backend.  In this mode:
         * 
        *
      • No optimization passes will run.
      • *
      • The last time custom passes are invoked is * {@link CustomPassExecutionTime#BEFORE_OPTIMIZATIONS}
      • *
      • The compiler will always try to process all inputs fully, even * if it encounters errors.
      • *
      • The compiler may record more information than is strictly * needed for codegen.
      • *
      */ public boolean ideMode; boolean saveDataStructures = false; /** * Even if checkTypes is disabled, clients might want to still infer types. * This is mostly used when ideMode is enabled. */ boolean inferTypes; /** * Configures the compiler to skip as many passes as possible. */ boolean skipAllPasses; /** * If true, name anonymous functions only. All others passes will be skipped. */ boolean nameAnonymousFunctionsOnly; /** * Configures the compiler to run expensive sanity checks after * every pass. Only intended for internal development. */ DevMode devMode; /** * Configures the compiler to log a hash code of the AST after * every pass. Only intended for internal development. */ private boolean checkDeterminism; //-------------------------------- // Input Options //-------------------------------- DependencyOptions dependencyOptions = new DependencyOptions(); /** Returns localized replacement for MSG_* variables */ // Transient so that clients don't have to implement Serializable. public transient MessageBundle messageBundle = null; //-------------------------------- // Checks //-------------------------------- /** Checks that all symbols are defined */ public boolean checkSymbols; public CheckLevel aggressiveVarCheck; /** Checks for suspicious variable definitions and undefined variables */ public void setAggressiveVarCheck(CheckLevel level) { this.aggressiveVarCheck = level; } /** Checks for suspicious statements that have no effect */ public boolean checkSuspiciousCode; /** Checks for invalid control structures */ public boolean checkControlStructures; /** Checks types on expressions */ public boolean checkTypes; boolean tightenTypes; /** Tightens types based on a global analysis. Experimental. */ public void setTightenTypes(boolean tighten) { tightenTypes = tighten; } public CheckLevel reportMissingOverride; /** * Flags a warning if a property is missing the @override annotation, but it * overrides a base class property. */ public void setReportMissingOverride(CheckLevel level) { reportMissingOverride = level; } CheckLevel reportUnknownTypes; /** Flags a warning for every node whose type could not be determined. */ public void setReportUnknownTypes(CheckLevel level) { reportUnknownTypes = level; } /** Checks for missing goog.require() calls **/ public CheckLevel checkRequires; public void setCheckRequires(CheckLevel level) { checkRequires = level; } public CheckLevel checkProvides; /** Checks for missing goog.provides() calls **/ public void setCheckProvides(CheckLevel level) { checkProvides = level; } public CheckLevel checkGlobalNamesLevel; /** * Checks the integrity of references to qualified global names. * (e.g. "a.b") */ public void setCheckGlobalNamesLevel(CheckLevel level) { checkGlobalNamesLevel = level; } public CheckLevel brokenClosureRequiresLevel; /** Sets the check level for bad Closure require calls. */ public void setBrokenClosureRequiresLevel(CheckLevel level) { brokenClosureRequiresLevel = level; } public CheckLevel checkGlobalThisLevel; /** * Checks for certain uses of the {@code this} keyword that are considered * unsafe because they are likely to reference the global {@code this} * object unintentionally. * * If this is off, but collapseProperties is on, then the compiler will * usually ignore you and run this check anyways. */ public void setCheckGlobalThisLevel(CheckLevel level) { this.checkGlobalThisLevel = level; } public CheckLevel checkMissingGetCssNameLevel; /** * Checks that certain string literals only appear in strings used as * goog.getCssName arguments. */ public void setCheckMissingGetCssNameLevel(CheckLevel level) { this.checkMissingGetCssNameLevel = level; } /** * Regex of string literals that may only appear in goog.getCssName arguments. */ public String checkMissingGetCssNameBlacklist; /** Checks that the syntactic restrictions of Caja are met. */ boolean checkCaja; public void setCheckCaja(boolean check) { checkCaja = check; } /** * A set of extra annotation names which are accepted and silently ignored * when encountered in a source file. Defaults to null which has the same * effect as specifying an empty set. */ Set extraAnnotationNames; //-------------------------------- // Optimizations //-------------------------------- /** Folds constants (e.g. (2 + 3) to 5) */ public boolean foldConstants; /** Remove assignments to values that can not be referenced */ public boolean deadAssignmentElimination; /** Inlines constants (symbols that are all CAPS) */ public boolean inlineConstantVars; /** Inlines global functions */ public boolean inlineFunctions; /** Inlines functions defined in local scopes */ public boolean inlineLocalFunctions; /** More aggressive function inlining */ boolean assumeClosuresOnlyCaptureReferences; /** Inlines properties */ boolean inlineProperties; /** Move code to a deeper module */ public boolean crossModuleCodeMotion; /** Merge two variables together as one. */ public boolean coalesceVariableNames; /** Move methods to a deeper module */ public boolean crossModuleMethodMotion; /** Inlines trivial getters */ public boolean inlineGetters; /** Inlines variables */ public boolean inlineVariables; /** Inlines variables */ boolean inlineLocalVariables; // TODO(user): This is temporary. Once flow sensitive inlining is stable // Remove this. public boolean flowSensitiveInlineVariables; /** Removes code associated with unused global names */ public boolean smartNameRemoval; /** Removes code that will never execute */ public boolean removeDeadCode; public CheckLevel checkUnreachableCode; /** Checks for unreachable code */ public void setCheckUnreachableCode(CheckLevel level) { this.checkUnreachableCode = level; } public CheckLevel checkMissingReturn; /** Checks for missing return statements */ public void setCheckMissingReturn(CheckLevel level) { this.checkMissingReturn = level; } /** Extracts common prototype member declarations */ public boolean extractPrototypeMemberDeclarations; /** Removes unused member prototypes */ public boolean removeUnusedPrototypeProperties; /** Tells AnalyzePrototypeProperties it can remove externed props. */ public boolean removeUnusedPrototypePropertiesInExterns; /** Removes unused member properties */ public boolean removeUnusedClassProperties; /** Removes unused variables */ public boolean removeUnusedVars; /** Removes unused variables in local scope. */ public boolean removeUnusedLocalVars; /** Adds variable aliases for externals to reduce code size */ public boolean aliasExternals; String aliasableGlobals; /** * A comma separated white-list of global names. When {@link #aliasExternals} * is enable, if set to a non-empty string, only externals with these names * will be considered for aliasing. */ public void setAliasableGlobals(String names) { aliasableGlobals = names; } String unaliasableGlobals; /** * A comma separated white-list of global names. When {@link #aliasExternals} * is enable, these global names will not be aliased. */ public void setUnaliasableGlobals(String names) { unaliasableGlobals = names; } /** Collapses multiple variable declarations into one */ public boolean collapseVariableDeclarations; /** Group multiple variable declarations into one */ boolean groupVariableDeclarations; /** * Collapses anonymous function declarations into named function * declarations */ public boolean collapseAnonymousFunctions; /** * If set to a non-empty set, those strings literals will be aliased to a * single global instance per string, to avoid creating more objects than * necessary. */ public Set aliasableStrings; /** * A blacklist in the form of a regular expression to block strings that * contains certain words from being aliased. * If the value is the empty string, no words are blacklisted. */ public String aliasStringsBlacklist; /** * Aliases all string literals to global instances, to avoid creating more * objects than necessary (if true, overrides any set of strings passed in * to aliasableStrings) */ public boolean aliasAllStrings; /** Print string usage as part of the compilation log. */ boolean outputJsStringUsage; /** Converts quoted property accesses to dot syntax (a['b'] -> a.b) */ public boolean convertToDottedProperties; /** Reduces the size of common function expressions. */ public boolean rewriteFunctionExpressions; /** * Remove unused and constant parameters. */ public boolean optimizeParameters; /** * Remove unused return values. */ public boolean optimizeReturns; /** * Remove unused parameters from call sites. */ public boolean optimizeCalls; /** * Provide formal names for elements of arguments array. */ public boolean optimizeArgumentsArray; /** Chains calls to functions that return this. */ boolean chainCalls; //-------------------------------- // Renaming //-------------------------------- /** Controls which variables get renamed. */ public VariableRenamingPolicy variableRenaming; /** Controls which properties get renamed. */ public PropertyRenamingPolicy propertyRenaming; /** Should we use affinity information when generating property names. */ boolean propertyAffinity; /** Controls label renaming. */ public boolean labelRenaming; /** Reserve property names on the global this object. */ public boolean reserveRawExports; /** Should shadow variable names in outer scope. */ boolean shadowVariables; /** * Generate pseudo names for variables and properties for debugging purposes. */ public boolean generatePseudoNames; /** Specifies a prefix for all globals */ public String renamePrefix; /** * Specifies the name of an object that will be used to store all non-extern * globals. */ public String renamePrefixNamespace; /** Aliases true, false, and null to variables with shorter names. */ public boolean aliasKeywords; /** Flattens multi-level property names (e.g. a$b = x) */ public boolean collapseProperties; /** Split object literals into individual variables when possible. */ boolean collapseObjectLiterals; public void setCollapseObjectLiterals(boolean enabled) { collapseObjectLiterals = enabled; } /** Flattens multi-level property names on extern types (e.g. String$f = x) */ boolean collapsePropertiesOnExternTypes; /** * Devirtualize prototype method by rewriting them to be static calls that * take the this pointer as their first argument */ public boolean devirtualizePrototypeMethods; /** * Use @nosideeffects annotations, function bodies and name graph * to determine if calls have side effects. Requires --check_types. */ public boolean computeFunctionSideEffects; /** * Where to save debug report for compute function side effects. */ String debugFunctionSideEffectsPath; /** * Rename properties to disambiguate between unrelated fields based on * type information. */ public boolean disambiguateProperties; /** Rename unrelated properties to the same name to reduce code size. */ public boolean ambiguateProperties; /** Give anonymous functions names for easier debugging */ public AnonymousFunctionNamingPolicy anonymousFunctionNaming; /** Input anonymous function renaming map. */ VariableMap inputAnonymousFunctionNamingMap; /** Input variable renaming map. */ VariableMap inputVariableMap; /** Input property renaming map. */ VariableMap inputPropertyMap; /** Whether to export test functions. */ public boolean exportTestFunctions; boolean specializeInitialModule; /** Specialize the initial module at the cost of later modules */ public void setSpecializeInitialModule(boolean enabled) { specializeInitialModule = enabled; } //-------------------------------- // Special-purpose alterations //-------------------------------- /** * Replace UI strings with chrome.i18n.getMessage calls. * Used by Chrome extensions/apps. */ boolean replaceMessagesWithChromeI18n; String tcProjectId; public void setReplaceMessagesWithChromeI18n( boolean replaceMessagesWithChromeI18n, String tcProjectId) { if (replaceMessagesWithChromeI18n && messageBundle != null && !(messageBundle instanceof EmptyMessageBundle)) { throw new RuntimeException("When replacing messages with " + "chrome.i18n.getMessage, a message bundle should not be specified."); } this.replaceMessagesWithChromeI18n = replaceMessagesWithChromeI18n; this.tcProjectId = tcProjectId; } /** Inserts run-time type assertions for debugging. */ boolean runtimeTypeCheck; /** * A JS function to be used for logging run-time type assertion * failures. It will be passed the warning as a string and the * faulty expression as arguments. */ String runtimeTypeCheckLogFunction; /** A CodingConvention to use during the compile. */ private CodingConvention codingConvention; boolean ignoreCajaProperties; /** Add code to skip properties that Caja adds to Object.prototype */ public void setIgnoreCajaProperties(boolean enabled) { ignoreCajaProperties = enabled; } public String syntheticBlockStartMarker; public String syntheticBlockEndMarker; /** Compiling locale */ public String locale; /** Sets the special "COMPILED" value to true */ public boolean markAsCompiled; /** Removes try...catch...finally blocks for easier debugging */ public boolean removeTryCatchFinally; /** Processes goog.provide() and goog.require() calls */ public boolean closurePass; /** Processes jQuery aliases */ public boolean jqueryPass; /** Processes AngularJS-specific annotations */ boolean angularPass; /** Remove goog.abstractMethod assignments. */ boolean removeAbstractMethods; /** Remove goog.asserts calls. */ boolean removeClosureAsserts; /** Gather CSS names (requires closurePass) */ public boolean gatherCssNames; /** Names of types to strip */ public Set stripTypes; /** Name suffixes that determine which variables and properties to strip */ public Set stripNameSuffixes; /** Name prefixes that determine which variables and properties to strip */ public Set stripNamePrefixes; /** Qualified type name prefixes that determine which types to strip */ public Set stripTypePrefixes; /** Custom passes */ public transient Multimap customPasses; /** Mark no side effect calls */ public boolean markNoSideEffectCalls; /** Replacements for @defines. Will be Boolean, Numbers, or Strings */ private Map defineReplacements; /** What kind of processing to do for goog.tweak functions. */ private TweakProcessing tweakProcessing; /** Replacements for tweaks. Will be Boolean, Numbers, or Strings */ private Map tweakReplacements; /** Move top-level function declarations to the top */ public boolean moveFunctionDeclarations; /** Instrumentation template to use with #recordFunctionInformation */ public String instrumentationTemplate; String appNameStr; /** * App identifier string for use by the instrumentation template's * app_name_setter. @see #instrumentationTemplate */ public void setAppNameStr(String appNameStr) { this.appNameStr = appNameStr; } /** Record function information */ public boolean recordFunctionInformation; public boolean generateExports; /** Map used in the renaming of CSS class names. */ public CssRenamingMap cssRenamingMap; /** Whitelist used in the renaming of CSS class names. */ Set cssRenamingWhitelist; /** Process instances of goog.testing.ObjectPropertyString. */ boolean processObjectPropertyString; /** Replace id generators */ boolean replaceIdGenerators = true; // true by default for legacy reasons. /** Id generators to replace. */ Set idGenerators; /** * A previous map of ids (serialized to a string by a previous compile). * This will be used as a hint during the ReplaceIdGenerators pass, which * will attempt to reuse the same ids. */ String idGeneratorsMapSerialized; /** Configuration strings */ List replaceStringsFunctionDescriptions; String replaceStringsPlaceholderToken; // A list of strings that should not be used as replacements Set replaceStringsReservedStrings; // A previous map of replacements to strings. VariableMap replaceStringsInputMap; /** List of properties that we report invalidation errors for. */ Map propertyInvalidationErrors; /** Transform AMD to CommonJS modules. */ boolean transformAMDToCJSModules = false; /** Rewrite CommonJS modules so that they can be concatenated together. */ boolean processCommonJSModules = false; /** CommonJS module prefix. */ String commonJSModulePathPrefix = ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; //-------------------------------- // Output options //-------------------------------- /** Output in pretty indented format */ public boolean prettyPrint; /** Line break the output a bit more aggressively */ public boolean lineBreak; /** Prefer line breaks at end of file */ public boolean preferLineBreakAtEndOfFile; /** Prints a separator comment before each JS script */ public boolean printInputDelimiter; /** The string to use as the separator for printInputDelimiter */ public String inputDelimiter = "// Input %num%"; boolean preferSingleQuotes; /** * Normally, when there are an equal number of single and double quotes * in a string, the compiler will use double quotes. Set this to true * to prefer single quotes. */ public void setPreferSingleQuotes(boolean enabled) { this.preferSingleQuotes = enabled; } boolean trustedStrings; /** * Some people want to put arbitrary user input into strings, which are then * run through the compiler. These scripts are then put into HTML. * By default, we assume strings are untrusted. If the compiler is run * from the command-line, we assume that strings are trusted. */ public void setTrustedStrings(boolean yes) { trustedStrings = yes; } String reportPath; /** Where to save a report of global name usage */ public void setReportPath(String reportPath) { this.reportPath = reportPath; } TracerMode tracer; public TracerMode getTracerMode() { return tracer; } public void setTracerMode(TracerMode mode) { tracer = mode; } private boolean colorizeErrorOutput; public ErrorFormat errorFormat; private ComposeWarningsGuard warningsGuard = new ComposeWarningsGuard(); int summaryDetailLevel = 1; int lineLengthThreshold = CodePrinter.DEFAULT_LINE_LENGTH_THRESHOLD; //-------------------------------- // Special Output Options //-------------------------------- /** * Whether the exports should be made available via {@link Result} after * compilation. This is implicitly true if {@link #externExportsPath} is set. */ private boolean externExports; /** The output path for the created externs file. */ String externExportsPath; String nameReferenceReportPath; /** Where to save a cross-reference report from the name reference graph */ public void setNameReferenceReportPath(String filePath) { nameReferenceReportPath = filePath; } String nameReferenceGraphPath; /** Where to save the name reference graph */ public void setNameReferenceGraphPath(String filePath) { nameReferenceGraphPath = filePath; } //-------------------------------- // Debugging Options //-------------------------------- /** The output path for the source map. */ public String sourceMapOutputPath; /** The detail level for the generated source map. */ public SourceMap.DetailLevel sourceMapDetailLevel = SourceMap.DetailLevel.SYMBOLS; /** The source map file format */ public SourceMap.Format sourceMapFormat = SourceMap.Format.DEFAULT; public List sourceMapLocationMappings = Collections.emptyList(); /** * Charset to use when generating code. If null, then output ASCII. * This needs to be a string because CompilerOptions is serializable. */ String outputCharset; /** * Whether the named objects types included 'undefined' by default. */ boolean looseTypes; /** * When set, assume that apparently side-effect free code is meaningful. */ boolean protectHiddenSideEffects; /** * When enabled, assume that apparently side-effect free code is meaningful. */ public void setProtectHiddenSideEffects(boolean enable) { this.protectHiddenSideEffects = enable; } /** * Data holder Alias Transformation information accumulated during a compile. */ private transient AliasTransformationHandler aliasHandler; /** * Handler for compiler warnings and errors. */ transient ErrorHandler errorHandler; /** * Initializes compiler options. All options are disabled by default. * * Command-line frontends to the compiler should set these properties * like a builder. */ public CompilerOptions() { // Accepted language languageIn = LanguageMode.ECMASCRIPT3; // Language variation acceptConstKeyword = false; // Checks skipAllPasses = false; nameAnonymousFunctionsOnly = false; devMode = DevMode.OFF; checkDeterminism = false; checkSymbols = false; aggressiveVarCheck = CheckLevel.OFF; checkSuspiciousCode = false; checkControlStructures = false; checkTypes = false; tightenTypes = false; reportMissingOverride = CheckLevel.OFF; reportUnknownTypes = CheckLevel.OFF; checkRequires = CheckLevel.OFF; checkProvides = CheckLevel.OFF; checkGlobalNamesLevel = CheckLevel.OFF; brokenClosureRequiresLevel = CheckLevel.ERROR; checkGlobalThisLevel = CheckLevel.OFF; checkUnreachableCode = CheckLevel.OFF; checkMissingReturn = CheckLevel.OFF; checkMissingGetCssNameLevel = CheckLevel.OFF; checkMissingGetCssNameBlacklist = null; checkCaja = false; computeFunctionSideEffects = false; chainCalls = false; extraAnnotationNames = null; // Optimizations foldConstants = false; coalesceVariableNames = false; deadAssignmentElimination = false; inlineConstantVars = false; inlineFunctions = false; inlineLocalFunctions = false; assumeStrictThis = false; assumeClosuresOnlyCaptureReferences = false; inlineProperties = false; crossModuleCodeMotion = false; crossModuleMethodMotion = false; inlineGetters = false; inlineVariables = false; inlineLocalVariables = false; smartNameRemoval = false; removeDeadCode = false; extractPrototypeMemberDeclarations = false; removeUnusedPrototypeProperties = false; removeUnusedPrototypePropertiesInExterns = false; removeUnusedClassProperties = false; removeUnusedVars = false; removeUnusedLocalVars = false; aliasExternals = false; collapseVariableDeclarations = false; groupVariableDeclarations = false; collapseAnonymousFunctions = false; aliasableStrings = Collections.emptySet(); aliasStringsBlacklist = ""; aliasAllStrings = false; outputJsStringUsage = false; convertToDottedProperties = false; rewriteFunctionExpressions = false; optimizeParameters = false; optimizeReturns = false; // Renaming variableRenaming = VariableRenamingPolicy.OFF; propertyRenaming = PropertyRenamingPolicy.OFF; propertyAffinity = false; labelRenaming = false; generatePseudoNames = false; shadowVariables = false; renamePrefix = null; aliasKeywords = false; collapseProperties = false; collapsePropertiesOnExternTypes = false; collapseObjectLiterals = false; devirtualizePrototypeMethods = false; disambiguateProperties = false; ambiguateProperties = false; anonymousFunctionNaming = AnonymousFunctionNamingPolicy.OFF; exportTestFunctions = false; // Alterations runtimeTypeCheck = false; runtimeTypeCheckLogFunction = null; ignoreCajaProperties = false; syntheticBlockStartMarker = null; syntheticBlockEndMarker = null; locale = null; markAsCompiled = false; removeTryCatchFinally = false; closurePass = false; jqueryPass = false; angularPass = false; removeAbstractMethods = true; removeClosureAsserts = false; stripTypes = Collections.emptySet(); stripNameSuffixes = Collections.emptySet(); stripNamePrefixes = Collections.emptySet(); stripTypePrefixes = Collections.emptySet(); customPasses = null; markNoSideEffectCalls = false; defineReplacements = Maps.newHashMap(); tweakProcessing = TweakProcessing.OFF; tweakReplacements = Maps.newHashMap(); moveFunctionDeclarations = false; instrumentationTemplate = null; appNameStr = ""; recordFunctionInformation = false; generateExports = false; cssRenamingMap = null; cssRenamingWhitelist = null; processObjectPropertyString = false; idGenerators = Collections.emptySet(); replaceStringsFunctionDescriptions = Collections.emptyList(); replaceStringsPlaceholderToken = ""; replaceStringsReservedStrings = Collections.emptySet(); propertyInvalidationErrors = Maps.newHashMap(); // Output printInputDelimiter = false; prettyPrint = false; lineBreak = false; preferLineBreakAtEndOfFile = false; reportPath = null; tracer = TracerMode.OFF; colorizeErrorOutput = false; errorFormat = ErrorFormat.SINGLELINE; debugFunctionSideEffectsPath = null; externExports = false; nameReferenceReportPath = null; nameReferenceGraphPath = null; // Debugging aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER; errorHandler = null; } /** * @return Whether to attempt to remove unused class properties */ public boolean isRemoveUnusedClassProperties() { return removeUnusedClassProperties; } /** * @param removeUnusedClassProperties Whether to attempt to remove * unused class properties */ public void setRemoveUnusedClassProperties(boolean removeUnusedClassProperties) { this.removeUnusedClassProperties = removeUnusedClassProperties; } /** * Returns the map of define replacements. */ public Map getDefineReplacements() { return getReplacementsHelper(defineReplacements); } /** * Returns the map of tweak replacements. */ public Map getTweakReplacements() { return getReplacementsHelper(tweakReplacements); } /** * Creates a map of String->Node from a map of String->Number/String/Boolean. */ private static Map getReplacementsHelper( Map source) { Map map = Maps.newHashMap(); for (Map.Entry entry : source.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); if (value instanceof Boolean) { map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue())); } else if (value instanceof Integer) { map.put(name, IR.number(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, IR.number(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, IR.string((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void setDefineToBooleanLiteral(String defineName, boolean value) { defineReplacements.put(defineName, new Boolean(value)); } /** * Sets the value of the {@code @define} variable in JS to a * String literal. */ public void setDefineToStringLiteral(String defineName, String value) { defineReplacements.put(defineName, value); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToNumberLiteral(String defineName, int value) { defineReplacements.put(defineName, new Integer(value)); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToDoubleLiteral(String defineName, double value) { defineReplacements.put(defineName, new Double(value)); } /** * Sets the value of the tweak in JS * to a boolean literal. */ public void setTweakToBooleanLiteral(String tweakId, boolean value) { tweakReplacements.put(tweakId, new Boolean(value)); } /** * Sets the value of the tweak in JS to a * String literal. */ public void setTweakToStringLiteral(String tweakId, String value) { tweakReplacements.put(tweakId, value); } /** * Sets the value of the tweak in JS to a * number literal. */ public void setTweakToNumberLiteral(String tweakId, int value) { tweakReplacements.put(tweakId, new Integer(value)); } /** * Sets the value of the tweak in JS to a * number literal. */ public void setTweakToDoubleLiteral(String tweakId, double value) { tweakReplacements.put(tweakId, new Double(value)); } /** * Skip all possible passes, to make the compiler as fast as possible. */ public void skipAllCompilerPasses() { skipAllPasses = true; } /** * Whether the warnings guard in this Options object enables the given * group of warnings. */ boolean enables(DiagnosticGroup type) { return warningsGuard.enables(type); } /** * Whether the warnings guard in this Options object disables the given * group of warnings. */ boolean disables(DiagnosticGroup type) { return warningsGuard.disables(type); } /** * Configure the given type of warning to the given level. */ public void setWarningLevel(DiagnosticGroup type, CheckLevel level) { addWarningsGuard(new DiagnosticGroupWarningsGuard(type, level)); } WarningsGuard getWarningsGuard() { return warningsGuard; } /** * Reset the warnings guard. */ public void resetWarningsGuard() { warningsGuard = new ComposeWarningsGuard(); } /** * The emergency fail safe removes all strict and ERROR-escalating * warnings guards. */ void useEmergencyFailSafe() { warningsGuard = warningsGuard.makeEmergencyFailSafeGuard(); } /** * Add a guard to the set of warnings guards. */ public void addWarningsGuard(WarningsGuard guard) { warningsGuard.addGuard(guard); } /** * Sets the variable and property renaming policies for the compiler, * in a way that clears warnings about the renaming policy being * uninitialized from flags. */ public void setRenamingPolicy(VariableRenamingPolicy newVariablePolicy, PropertyRenamingPolicy newPropertyPolicy) { this.variableRenaming = newVariablePolicy; this.propertyRenaming = newPropertyPolicy; } public void setPropertyAffinity(boolean useAffinity) { this.propertyAffinity = useAffinity; } /** Should shadow outer scope variable name during renaming. */ public void setShadowVariables(boolean shadow) { this.shadowVariables = shadow; } /** * If true, flattens multi-level property names on extern types * (e.g. String$f = x). This should only be used with the typed version of * the externs files. */ public void setCollapsePropertiesOnExternTypes(boolean collapse) { collapsePropertiesOnExternTypes = collapse; } /** * If true, process goog.testing.ObjectPropertyString instances. */ public void setProcessObjectPropertyString(boolean process) { processObjectPropertyString = process; } /** * @param replaceIdGenerators the replaceIdGenerators to set */ public void setReplaceIdGenerators(boolean replaceIdGenerators) { this.replaceIdGenerators = replaceIdGenerators; } /** * Sets the id generators to replace. */ public void setIdGenerators(Set idGenerators) { this.idGenerators = Sets.newHashSet(idGenerators); } /** * A previous map of ids (serialized to a string by a previous compile). * This will be used as a hint during the ReplaceIdGenerators pass, which * will attempt to reuse the same ids. */ public void setIdGeneratorsMap(String previousMappings) { this.idGeneratorsMapSerialized = previousMappings; } /** * Set the function inlining policy for the compiler. */ public void setInlineFunctions(Reach reach) { switch (reach) { case ALL: this.inlineFunctions = true; this.inlineLocalFunctions = true; break; case LOCAL_ONLY: this.inlineFunctions = false; this.inlineLocalFunctions = true; break; case NONE: this.inlineFunctions = false; this.inlineLocalFunctions = false; break; default: throw new IllegalStateException("unexpected"); } } /** * Set the variable inlining policy for the compiler. */ public void setInlineVariables(Reach reach) { switch (reach) { case ALL: this.inlineVariables = true; this.inlineLocalVariables = true; break; case LOCAL_ONLY: this.inlineVariables = false; this.inlineLocalVariables = true; break; case NONE: this.inlineVariables = false; this.inlineLocalVariables = false; break; default: throw new IllegalStateException("unexpected"); } } /** * Set the function inlining policy for the compiler. */ public void setInlineProperties(boolean enable) { inlineProperties = enable; } /** * Set the variable removal policy for the compiler. */ @Deprecated public void setRemoveUnusedVariable(Reach reach) { setRemoveUnusedVariables(reach); } /** * Set the variable removal policy for the compiler. */ public void setRemoveUnusedVariables(Reach reach) { switch (reach) { case ALL: this.removeUnusedVars = true; this.removeUnusedLocalVars = true; break; case LOCAL_ONLY: this.removeUnusedVars = false; this.removeUnusedLocalVars = true; break; case NONE: this.removeUnusedVars = false; this.removeUnusedLocalVars = false; break; default: throw new IllegalStateException("unexpected"); } } /** * Sets the functions whose debug strings to replace. */ public void setReplaceStringsConfiguration( String placeholderToken, List functionDescriptors) { this.replaceStringsPlaceholderToken = placeholderToken; this.replaceStringsFunctionDescriptions = Lists.newArrayList(functionDescriptors); } @Deprecated public void setRewriteNewDateGoogNow(boolean rewrite) { } public void setRemoveAbstractMethods(boolean remove) { this.removeAbstractMethods = remove; } public void setRemoveClosureAsserts(boolean remove) { this.removeClosureAsserts = remove; } /** * If true, name anonymous functions only. All other passes will be skipped. */ public void setNameAnonymousFunctionsOnly(boolean value) { this.nameAnonymousFunctionsOnly = value; } public void setColorizeErrorOutput(boolean colorizeErrorOutput) { this.colorizeErrorOutput = colorizeErrorOutput; } public boolean shouldColorizeErrorOutput() { return colorizeErrorOutput; } /** * If true, chain calls to functions that return this. */ public void setChainCalls(boolean value) { this.chainCalls = value; } /** * If true, accept `const' keyword. */ public void setAcceptConstKeyword(boolean value) { this.acceptConstKeyword = value; } /** * Enable run-time type checking, which adds JS type assertions for debugging. * * @param logFunction A JS function to be used for logging run-time type * assertion failures. */ public void enableRuntimeTypeCheck(String logFunction) { this.runtimeTypeCheck = true; this.runtimeTypeCheckLogFunction = logFunction; } public void disableRuntimeTypeCheck() { this.runtimeTypeCheck = false; } public void setGenerateExports(boolean generateExports) { this.generateExports = generateExports; } public void setAngularPass(boolean angularPass) { this.angularPass = angularPass; } public void setCodingConvention(CodingConvention codingConvention) { this.codingConvention = codingConvention; } public CodingConvention getCodingConvention() { return codingConvention; } /** * Sets dependency options. See the DependencyOptions class for more info. * This supersedes manageClosureDependencies. */ public void setDependencyOptions(DependencyOptions options) { Preconditions.checkNotNull(options); this.dependencyOptions = options; } /** * Sort inputs by their goog.provide/goog.require calls, and prune inputs * whose symbols are not required. */ public void setManageClosureDependencies(boolean newVal) { dependencyOptions.setDependencySorting( newVal || dependencyOptions.shouldSortDependencies()); dependencyOptions.setDependencyPruning( newVal || dependencyOptions.shouldPruneDependencies()); dependencyOptions.setMoocherDropping(false); manageClosureDependencies = newVal; } /** * Sort inputs by their goog.provide/goog.require calls. * * @param entryPoints Entry points to the program. Must be goog.provide'd * symbols. Any goog.provide'd symbols that are not a transitive * dependency of the entry points will be deleted. * Files without goog.provides, and their dependencies, * will always be left in. */ public void setManageClosureDependencies(List entryPoints) { Preconditions.checkNotNull(entryPoints); setManageClosureDependencies(true); dependencyOptions.setEntryPoints(entryPoints); } /** * Controls how detailed the compilation summary is. Values: * 0 (never print summary), 1 (print summary only if there are * errors or warnings), 2 (print summary if type checking is on, * see --check_types), 3 (always print summary). The default level * is 1 */ public void setSummaryDetailLevel(int summaryDetailLevel) { this.summaryDetailLevel = summaryDetailLevel; } /** * @deprecated replaced by {@link #setExternExports} */ @Deprecated public void enableExternExports(boolean enabled) { this.externExports = enabled; } public void setExtraAnnotationNames(Iterable extraAnnotationNames) { this.extraAnnotationNames = ImmutableSet.copyOf(extraAnnotationNames); } public boolean isExternExportsEnabled() { return externExports; } /** * Sets the output charset by name. */ public void setOutputCharset(String charsetName) { this.outputCharset = charsetName; } /** * Gets the output charset as a rich object. */ Charset getOutputCharset() { return outputCharset == null ? null : Charset.forName(outputCharset); } /** * Sets how goog.tweak calls are processed. */ public void setTweakProcessing(TweakProcessing tweakProcessing) { this.tweakProcessing = tweakProcessing; } public TweakProcessing getTweakProcessing() { return tweakProcessing; } /** * Sets how goog.tweak calls are processed. */ public void setLanguageIn(LanguageMode languageIn) { this.languageIn = languageIn; this.languageOut = languageIn; } public LanguageMode getLanguageIn() { return languageIn; } public LanguageMode getLanguageOut() { return languageOut; } /** * Whether to include "undefined" in the default types. * For example: * "{Object}" is normally "Object|null" becomes "Object|null|undefined" * "{?string}" is normally "string|null" becomes "string|null|undefined" * In either case "!" annotated types excluded both null and undefined. */ public void setLooseTypes(boolean looseTypes) { this.looseTypes = looseTypes; } @Override public Object clone() throws CloneNotSupportedException { CompilerOptions clone = (CompilerOptions) super.clone(); // TODO(bolinfest): Add relevant custom cloning. return clone; } public void setAliasTransformationHandler( AliasTransformationHandler changes) { this.aliasHandler = changes; } public AliasTransformationHandler getAliasTransformationHandler() { return this.aliasHandler; } /** * Set a custom handler for warnings and errors. * * This is mostly used for piping the warnings and errors to * a file behind the scenes. * * If you want to filter warnings and errors, you should use a WarningsGuard. * * If you want to change how warnings and errors are reported to the user, * you should set a ErrorManager on the Compiler. An ErrorManager is * intended to summarize the errors for a single compile job. */ public void setErrorHandler(ErrorHandler handler) { this.errorHandler = handler; } /** * If true, enables type inference. If checkTypes is enabled, this flag has * no effect. */ public void setInferTypes(boolean enable) { inferTypes = enable; } /** * Gets the inferTypes flag. Note that if checkTypes is enabled, this flag * is ignored when configuring the compiler. */ public boolean getInferTypes() { return inferTypes; } /** * @return Whether assumeStrictThis is set. */ public boolean assumeStrictThis() { return assumeStrictThis; } /** * If true, enables enables additional optimizations. */ public void setAssumeStrictThis(boolean enable) { this.assumeStrictThis = enable; } /** * @return Whether assumeClosuresOnlyCaptureReferences is set. */ public boolean assumeClosuresOnlyCaptureReferences() { return assumeClosuresOnlyCaptureReferences; } /** * Whether to assume closures capture only what they reference. This allows * more aggressive function inlining. */ public void setAssumeClosuresOnlyCaptureReferences(boolean enable) { this.assumeClosuresOnlyCaptureReferences = enable; } /** * Sets the list of properties that we report property invalidation errors * for. */ public void setPropertyInvalidationErrors( Map propertyInvalidationErrors) { this.propertyInvalidationErrors = Maps.newHashMap(propertyInvalidationErrors); } public void setLanguageOut(LanguageMode languageOut) { this.languageOut = languageOut; } public void setIdeMode(boolean ideMode) { this.ideMode = ideMode; } /** * Whether to keep internal data structures around after we're * finished compiling. We do this by default when IDE mode is on. */ public void setSaveDataStructures(boolean save) { this.saveDataStructures = save; } public void setSkipAllPasses(boolean skipAllPasses) { this.skipAllPasses = skipAllPasses; } public void setDevMode(DevMode devMode) { this.devMode = devMode; } public void setCheckDeterminism(boolean checkDeterminism) { this.checkDeterminism = checkDeterminism; } public boolean getCheckDeterminism() { return checkDeterminism; } public void setMessageBundle(MessageBundle messageBundle) { this.messageBundle = messageBundle; } public void setCheckSymbols(boolean checkSymbols) { this.checkSymbols = checkSymbols; } public void setCheckSuspiciousCode(boolean checkSuspiciousCode) { this.checkSuspiciousCode = checkSuspiciousCode; } public void setCheckControlStructures(boolean checkControlStructures) { this.checkControlStructures = checkControlStructures; } public void setCheckTypes(boolean checkTypes) { this.checkTypes = checkTypes; } public void setCheckMissingGetCssNameBlacklist(String blackList) { this.checkMissingGetCssNameBlacklist = blackList; } public void setFoldConstants(boolean foldConstants) { this.foldConstants = foldConstants; } public void setDeadAssignmentElimination(boolean deadAssignmentElimination) { this.deadAssignmentElimination = deadAssignmentElimination; } public void setInlineConstantVars(boolean inlineConstantVars) { this.inlineConstantVars = inlineConstantVars; } public void setInlineFunctions(boolean inlineFunctions) { this.inlineFunctions = inlineFunctions; } public void setInlineLocalFunctions(boolean inlineLocalFunctions) { this.inlineLocalFunctions = inlineLocalFunctions; } public void setCrossModuleCodeMotion(boolean crossModuleCodeMotion) { this.crossModuleCodeMotion = crossModuleCodeMotion; } public void setCoalesceVariableNames(boolean coalesceVariableNames) { this.coalesceVariableNames = coalesceVariableNames; } public void setCrossModuleMethodMotion(boolean crossModuleMethodMotion) { this.crossModuleMethodMotion = crossModuleMethodMotion; } public void setInlineGetters(boolean inlineGetters) { this.inlineGetters = inlineGetters; } public void setInlineVariables(boolean inlineVariables) { this.inlineVariables = inlineVariables; } public void setInlineLocalVariables(boolean inlineLocalVariables) { this.inlineLocalVariables = inlineLocalVariables; } public void setFlowSensitiveInlineVariables(boolean enabled) { this.flowSensitiveInlineVariables = enabled; } public void setSmartNameRemoval(boolean smartNameRemoval) { this.smartNameRemoval = smartNameRemoval; } public void setRemoveDeadCode(boolean removeDeadCode) { this.removeDeadCode = removeDeadCode; } public void setExtractPrototypeMemberDeclarations(boolean enabled) { this.extractPrototypeMemberDeclarations = enabled; } public void setRemoveUnusedPrototypeProperties(boolean enabled) { this.removeUnusedPrototypeProperties = enabled; } public void setRemoveUnusedPrototypePropertiesInExterns( boolean enabled) { this.removeUnusedPrototypePropertiesInExterns = enabled; } public void setRemoveUnusedVars(boolean removeUnusedVars) { this.removeUnusedVars = removeUnusedVars; } public void setRemoveUnusedLocalVars(boolean removeUnusedLocalVars) { this.removeUnusedLocalVars = removeUnusedLocalVars; } public void setAliasExternals(boolean aliasExternals) { this.aliasExternals = aliasExternals; } public void setCollapseVariableDeclarations(boolean enabled) { this.collapseVariableDeclarations = enabled; } public void setGroupVariableDeclarations(boolean enabled) { this.groupVariableDeclarations = enabled; } public void setCollapseAnonymousFunctions(boolean enabled) { this.collapseAnonymousFunctions = enabled; } public void setAliasableStrings(Set aliasableStrings) { this.aliasableStrings = aliasableStrings; } public void setAliasStringsBlacklist(String aliasStringsBlacklist) { this.aliasStringsBlacklist = aliasStringsBlacklist; } public void setAliasAllStrings(boolean aliasAllStrings) { this.aliasAllStrings = aliasAllStrings; } public void setOutputJsStringUsage(boolean outputJsStringUsage) { this.outputJsStringUsage = outputJsStringUsage; } public void setConvertToDottedProperties(boolean convertToDottedProperties) { this.convertToDottedProperties = convertToDottedProperties; } public void setRewriteFunctionExpressions(boolean rewriteFunctionExpressions) { this.rewriteFunctionExpressions = rewriteFunctionExpressions; } public void setOptimizeParameters(boolean optimizeParameters) { this.optimizeParameters = optimizeParameters; } public void setOptimizeReturns(boolean optimizeReturns) { this.optimizeReturns = optimizeReturns; } public void setOptimizeCalls(boolean optimizeCalls) { this.optimizeCalls = optimizeCalls; } public void setOptimizeArgumentsArray(boolean optimizeArgumentsArray) { this.optimizeArgumentsArray = optimizeArgumentsArray; } public void setVariableRenaming(VariableRenamingPolicy variableRenaming) { this.variableRenaming = variableRenaming; } public void setPropertyRenaming(PropertyRenamingPolicy propertyRenaming) { this.propertyRenaming = propertyRenaming; } public void setLabelRenaming(boolean labelRenaming) { this.labelRenaming = labelRenaming; } public void setReserveRawExports(boolean reserveRawExports) { this.reserveRawExports = reserveRawExports; } public void setGeneratePseudoNames(boolean generatePseudoNames) { this.generatePseudoNames = generatePseudoNames; } public void setRenamePrefix(String renamePrefix) { this.renamePrefix = renamePrefix; } public void setRenamePrefixNamespace(String renamePrefixNamespace) { this.renamePrefixNamespace = renamePrefixNamespace; } public void setAliasKeywords(boolean aliasKeywords) { this.aliasKeywords = aliasKeywords; } public void setCollapseProperties(boolean collapseProperties) { this.collapseProperties = collapseProperties; } public void setDevirtualizePrototypeMethods(boolean devirtualizePrototypeMethods) { this.devirtualizePrototypeMethods = devirtualizePrototypeMethods; } public void setComputeFunctionSideEffects(boolean computeFunctionSideEffects) { this.computeFunctionSideEffects = computeFunctionSideEffects; } public void setDebugFunctionSideEffectsPath(String debugFunctionSideEffectsPath) { this.debugFunctionSideEffectsPath = debugFunctionSideEffectsPath; } public void setDisambiguateProperties(boolean disambiguateProperties) { this.disambiguateProperties = disambiguateProperties; } public void setAmbiguateProperties(boolean ambiguateProperties) { this.ambiguateProperties = ambiguateProperties; } public void setAnonymousFunctionNaming( AnonymousFunctionNamingPolicy anonymousFunctionNaming) { this.anonymousFunctionNaming = anonymousFunctionNaming; } public void setInputAnonymousFunctionNamingMap(VariableMap inputMap) { this.inputAnonymousFunctionNamingMap = inputMap; } @Deprecated public void setInputVariableMapSerialized(byte[] inputVariableMapSerialized) throws ParseException { this.inputVariableMap = VariableMap.fromBytes(inputVariableMapSerialized); } public void setInputVariableMap(VariableMap inputVariableMap) { this.inputVariableMap = inputVariableMap; } @Deprecated public void setInputPropertyMapSerialized(byte[] inputPropertyMapSerialized) throws ParseException { this.inputPropertyMap = VariableMap.fromBytes(inputPropertyMapSerialized); } public void setInputPropertyMap(VariableMap inputPropertyMap) { this.inputPropertyMap = inputPropertyMap; } public void setExportTestFunctions(boolean exportTestFunctions) { this.exportTestFunctions = exportTestFunctions; } public void setRuntimeTypeCheck(boolean runtimeTypeCheck) { this.runtimeTypeCheck = runtimeTypeCheck; } public void setRuntimeTypeCheckLogFunction(String runtimeTypeCheckLogFunction) { this.runtimeTypeCheckLogFunction = runtimeTypeCheckLogFunction; } public void setSyntheticBlockStartMarker(String syntheticBlockStartMarker) { this.syntheticBlockStartMarker = syntheticBlockStartMarker; } public void setSyntheticBlockEndMarker(String syntheticBlockEndMarker) { this.syntheticBlockEndMarker = syntheticBlockEndMarker; } public void setLocale(String locale) { this.locale = locale; } public void setMarkAsCompiled(boolean markAsCompiled) { this.markAsCompiled = markAsCompiled; } public void setRemoveTryCatchFinally(boolean removeTryCatchFinally) { this.removeTryCatchFinally = removeTryCatchFinally; } public void setClosurePass(boolean closurePass) { this.closurePass = closurePass; } public void setGatherCssNames(boolean gatherCssNames) { this.gatherCssNames = gatherCssNames; } public void setStripTypes(Set stripTypes) { this.stripTypes = stripTypes; } public void setStripNameSuffixes(Set stripNameSuffixes) { this.stripNameSuffixes = stripNameSuffixes; } public void setStripNamePrefixes(Set stripNamePrefixes) { this.stripNamePrefixes = stripNamePrefixes; } public void setStripTypePrefixes(Set stripTypePrefixes) { this.stripTypePrefixes = stripTypePrefixes; } public void setCustomPasses(Multimap customPasses) { this.customPasses = customPasses; } public void setMarkNoSideEffectCalls(boolean markNoSideEffectCalls) { this.markNoSideEffectCalls = markNoSideEffectCalls; } public void setDefineReplacements(Map defineReplacements) { this.defineReplacements = defineReplacements; } public void setTweakReplacements(Map tweakReplacements) { this.tweakReplacements = tweakReplacements; } public void setMoveFunctionDeclarations(boolean moveFunctionDeclarations) { this.moveFunctionDeclarations = moveFunctionDeclarations; } public void setInstrumentationTemplate(String instrumentationTemplate) { this.instrumentationTemplate = instrumentationTemplate; } public void setRecordFunctionInformation(boolean recordFunctionInformation) { this.recordFunctionInformation = recordFunctionInformation; } public void setCssRenamingMap(CssRenamingMap cssRenamingMap) { this.cssRenamingMap = cssRenamingMap; } public void setCssRenamingWhitelist(Set whitelist) { this.cssRenamingWhitelist = whitelist; } public void setReplaceStringsFunctionDescriptions(List replaceStringsFunctionDescriptions) { this.replaceStringsFunctionDescriptions = replaceStringsFunctionDescriptions; } public void setReplaceStringsPlaceholderToken(String replaceStringsPlaceholderToken) { this.replaceStringsPlaceholderToken = replaceStringsPlaceholderToken; } public void setReplaceStringsReservedStrings(Set replaceStringsReservedStrings) { this.replaceStringsReservedStrings = replaceStringsReservedStrings; } public void setReplaceStringsInputMap(VariableMap serializedMap) { this.replaceStringsInputMap = serializedMap; } public void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; } public void setLineBreak(boolean lineBreak) { this.lineBreak = lineBreak; } public void setPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd) { this.preferLineBreakAtEndOfFile = lineBreakAtEnd; } public void setPrintInputDelimiter(boolean printInputDelimiter) { this.printInputDelimiter = printInputDelimiter; } public void setInputDelimiter(String inputDelimiter) { this.inputDelimiter = inputDelimiter; } public void setTracer(TracerMode tracer) { this.tracer = tracer; } public void setErrorFormat(ErrorFormat errorFormat) { this.errorFormat = errorFormat; } public void setWarningsGuard(ComposeWarningsGuard warningsGuard) { this.warningsGuard = warningsGuard; } public void setLineLengthThreshold(int lineLengthThreshold) { this.lineLengthThreshold = lineLengthThreshold; } public void setExternExports(boolean externExports) { this.externExports = externExports; } public void setExternExportsPath(String externExportsPath) { this.externExportsPath = externExportsPath; } public void setSourceMapOutputPath(String sourceMapOutputPath) { this.sourceMapOutputPath = sourceMapOutputPath; } public void setSourceMapDetailLevel(SourceMap.DetailLevel sourceMapDetailLevel) { this.sourceMapDetailLevel = sourceMapDetailLevel; } public void setSourceMapFormat(SourceMap.Format sourceMapFormat) { this.sourceMapFormat = sourceMapFormat; } public void setSourceMapLocationMappings(List sourceMapLocationMappings) { this.sourceMapLocationMappings = sourceMapLocationMappings; } /** * Activates transformation of AMD to CommonJS modules. */ public void setTransformAMDToCJSModules(boolean transformAMDToCJSModules) { this.transformAMDToCJSModules = transformAMDToCJSModules; } /** * Rewrites CommonJS modules so that modules can be concatenated together, * by renaming all globals to avoid conflicting with other modules. */ public void setProcessCommonJSModules(boolean processCommonJSModules) { this.processCommonJSModules = processCommonJSModules; } /** * Sets a path prefix for CommonJS modules. */ public void setCommonJSModulePathPrefix(String commonJSModulePathPrefix) { this.commonJSModulePathPrefix = commonJSModulePathPrefix; } ////////////////////////////////////////////////////////////////////////////// // Enums /** When to do the extra sanity checks */ public static enum LanguageMode { /** * Traditional JavaScript */ ECMASCRIPT3, /** * Shiny new JavaScript */ ECMASCRIPT5, /** * Nitpicky, shiny new JavaScript */ ECMASCRIPT5_STRICT; public static LanguageMode fromString(String value) { if (value.equals("ECMASCRIPT5_STRICT") || value.equals("ES5_STRICT")) { return CompilerOptions.LanguageMode.ECMASCRIPT5_STRICT; } else if (value.equals("ECMASCRIPT5") || value.equals("ES5")) { return CompilerOptions.LanguageMode.ECMASCRIPT5; } else if (value.equals("ECMASCRIPT3") || value.equals("ES3")) { return CompilerOptions.LanguageMode.ECMASCRIPT3; } return null; } } /** When to do the extra sanity checks */ static enum DevMode { /** * Don't do any extra sanity checks. */ OFF, /** * After the initial parse */ START, /** * At the start and at the end of all optimizations. */ START_AND_END, /** * After every pass */ EVERY_PASS } public static enum TracerMode { ALL, // Collect all timing and size metrics. RAW_SIZE, // Collect all timing and size metrics, except gzipped size. TIMING_ONLY, // Collect timing metrics only. OFF; // Collect no timing and size metrics. boolean isOn() { return this != OFF; } } public static enum TweakProcessing { OFF, // Do not run the ProcessTweaks pass. CHECK, // Run the pass, but do not strip out the calls. STRIP; // Strip out all calls to goog.tweak.*. public boolean isOn() { return this != OFF; } public boolean shouldStrip() { return this == STRIP; } } /** * A Role Specific Interface for JS Compiler that represents a data holder * object which is used to store goog.scope alias code changes to code made * during a compile. There is no guarantee that individual alias changes are * invoked in the order they occur during compilation, so implementations * should not assume any relevance to the order changes arrive. *

      * Calls to the mutators are expected to resolve very quickly, so * implementations should not perform expensive operations in the mutator * methods. * * @author tylerg@google.com (Tyler Goodwin) */ public interface AliasTransformationHandler { /** * Builds an AliasTransformation implementation and returns it to the * caller. *

      * Callers are allowed to request multiple AliasTransformation instances for * the same file, though it is expected that the first and last char values * for multiple instances will not overlap. *

      * This method is expected to have a side-effect of storing off the created * AliasTransformation, which guarantees that invokers of this interface * cannot leak AliasTransformation to this implementation that the * implementor did not create * * @param sourceFile the source file the aliases re contained in. * @param position the region of the source file associated with the * goog.scope call. The item of the SourcePosition is the returned * AliasTransformation */ public AliasTransformation logAliasTransformation( String sourceFile, SourcePosition position); } /** * A Role Specific Interface for the JS Compiler to report aliases used to * change the code during a compile. *

      * While aliases defined by goog.scope are expected to by only 1 per file, and * the only top-level structure in the file, this is not enforced. */ public interface AliasTransformation { /** * Adds an alias definition to the AliasTransformation instance. *

      * Last definition for a given alias is kept if an alias is inserted * multiple times (since this is generally the behavior in JavaScript code). * * @param alias the name of the alias. * @param definition the definition of the alias. */ void addAlias(String alias, String definition); } /** * A Null implementation of the CodeChanges interface which performs all * operations as a No-Op */ static final AliasTransformationHandler NULL_ALIAS_TRANSFORMATION_HANDLER = new NullAliasTransformationHandler(); private static class NullAliasTransformationHandler implements AliasTransformationHandler, Serializable { private static final long serialVersionUID = 0L; private static final AliasTransformation NULL_ALIAS_TRANSFORMATION = new NullAliasTransformation(); @Override public AliasTransformation logAliasTransformation( String sourceFile, SourcePosition position) { position.setItem(NULL_ALIAS_TRANSFORMATION); return NULL_ALIAS_TRANSFORMATION; } private static class NullAliasTransformation implements AliasTransformation, Serializable { private static final long serialVersionUID = 0L; @Override public void addAlias(String alias, String definition) { } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SideEffectsAnalysis.java0000644000175000017500000007670312115204405027404 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.jscomp.ReferenceCollectingCallback.Reference; import com.google.javascript.jscomp.ReferenceCollectingCallback.ReferenceCollection; import com.google.javascript.jscomp.Scope.Var; import com.google.javascript.jscomp.VariableVisibilityAnalysis.VariableVisibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.ArrayList; import java.util.Map; import java.util.Set; /** * A pass that analyzes side effects to determine when it is safe to move * code from one program point to another. * * In its current form, SideEffectsAnalysis is very incomplete; this is * mostly a sketch to prototype the interface and the broad strokes of * a possible implementation based on flow-insensitive MOD and REF sets. * * See: * * Banning, John. "An efficient way to find the side effects of procedure * calls and the aliases of variables." POPL '79. * * For an introduction to MOD and REF sets. * * @author dcc@google.com (Devin Coughlin) */ class SideEffectsAnalysis implements CompilerPass { /** * The type of location abstraction to use for this analysis. */ enum LocationAbstractionMode { /** See {@link DegenerateLocationAbstraction} for details. */ DEGENERATE, /** See {@link VisibilityLocationAbstraction} for details. */ VISIBILITY_BASED } private static final Predicate NOT_FUNCTION_PREDICATE = new Predicate() { @Override public boolean apply(Node input) { return !input.isFunction(); } }; private AbstractCompiler compiler; /** The location abstraction used to calculate the effects of code */ private LocationAbstraction locationAbstraction; /** The kind of location abstraction to use */ private final LocationAbstractionMode locationAbstractionIdentifier; /** * Constructs a new SideEffectsAnalysis with the given location abstraction. * * @param compiler A compiler instance * @param locationAbstractionMode The location abstraction to use. {@code * DEGENERATE} will use {@link DegenerateLocationAbstraction} while * {@code VISIBILITY_BASED} will use {@link VisibilityLocationAbstraction} * */ public SideEffectsAnalysis(AbstractCompiler compiler, LocationAbstractionMode locationAbstractionMode) { this.compiler = compiler; this.locationAbstractionIdentifier = locationAbstractionMode; } public SideEffectsAnalysis(AbstractCompiler compiler) { this(compiler, LocationAbstractionMode.DEGENERATE); } @Override public void process(Node externs, Node root) { switch(locationAbstractionIdentifier) { case DEGENERATE: locationAbstraction = new DegenerateLocationAbstraction(); break; case VISIBILITY_BASED: locationAbstraction = createVisibilityAbstraction(externs, root); break; default: throw new IllegalStateException("Unrecognized location abstraction " + "identifier: " + locationAbstractionIdentifier); } // In the future, this method // will construct a callgraph and calculate side effects summaries // for all functions. // TODO(dcc): Add per-function side effects summaries. } private LocationAbstraction createVisibilityAbstraction(Node externs, Node root) { VariableVisibilityAnalysis variableVisibility = new VariableVisibilityAnalysis(compiler); variableVisibility.process(externs, root); VariableUseDeclarationMap variableMap = new VariableUseDeclarationMap(compiler); variableMap.mapUses(root); return new VisibilityLocationAbstraction(compiler, variableVisibility, variableMap); } /** * Determines whether it is safe to move code ({@code source}) across * an environment to another program point (immediately preceding * {@code destination}). * *

      The notion of "environment" is optimization-specific, but it should * include any code that could be executed between the source program point * and the destination program point. * * {@code destination} must not be a descendant of {@code source}. * * @param source The node that would be moved * @param environment An environment representing the code across which * the source will be moved. * @param destination The node before which the source would be moved * @return Whether it is safe to move the source to the destination */ public boolean safeToMoveBefore(Node source, AbstractMotionEnvironment environment, Node destination) { Preconditions.checkNotNull(locationAbstraction); Preconditions.checkArgument(!nodeHasAncestor(destination, source)); // It is always safe to move pure code. if (isPure(source)) { return true; } // Don't currently support interprocedural analysis if (nodeHasCall(source)) { return false; } LocationSummary sourceLocationSummary = locationAbstraction.calculateLocationSummary(source); EffectLocation sourceModSet = sourceLocationSummary.getModSet(); // If the source has side effects, then we require that the source // is executed exactly as many times as the destination. if (!sourceModSet.isEmpty() && !nodesHaveSameControlFlow(source, destination)) { return false; } EffectLocation sourceRefSet = sourceLocationSummary.getRefSet(); Set environmentNodes = environment.calculateEnvironment(); for (Node environmentNode : environmentNodes) { if (nodeHasCall(environmentNode)) { return false; } } LocationSummary environmentLocationSummary = locationAbstraction.calculateLocationSummary(environmentNodes); EffectLocation environmentModSet = environmentLocationSummary.getModSet(); EffectLocation environmentRefSet = environmentLocationSummary.getRefSet(); // If MOD(environment) intersects REF(source) then moving the // source across the environment could cause the source // to read an incorrect value. // If REF(environment) intersects MOD(source) then moving the // source across the environment could cause the environment // to read an incorrect value. // If MOD(environment) intersects MOD(source) then moving the // source across the environment could cause some later code that reads // a modified location to get an incorrect value. if (!environmentModSet.intersectsLocation(sourceRefSet) && !environmentRefSet.intersectsLocation(sourceModSet) && !environmentModSet.intersectsLocation(sourceModSet)) { return true; } return false; } /** * Returns true if the node is pure, that is it side effect free and does it * not depend on its environment? * @param node node to check. */ private boolean isPure(Node node) { // For now, we conservatively assume all code is not pure. // TODO(dcc): Implement isPure(). return false; } /** * Returns true if the two nodes have the same control flow properties, * that is, is node1 be executed every time node2 is executed and vice versa? */ private static boolean nodesHaveSameControlFlow(Node node1, Node node2) { /* * We conservatively approximate this with the following criteria: * * Define the "deepest control dependent block" for a node to be the * closest ancestor whose *parent* is a control structure and where that * ancestor may or may be executed depending on the parent. * * So, for example, in: * if (a) { * b; * } else { * c; * } * * a has not deepest control dependent block. * b's deepest control dependent block is the "then" block of the IF. * c's deepest control dependent block is the "else" block of the IF. * * We'll say two nodes have the same control flow if * * 1) they have the same deepest control dependent block * 2) that block is either a CASE (which can't have early exits) or it * doesn't have any early exits (e.g. breaks, continues, returns.) * */ Node node1DeepestControlDependentBlock = closestControlDependentAncestor(node1); Node node2DeepestControlDependentBlock = closestControlDependentAncestor(node2); if (node1DeepestControlDependentBlock == node2DeepestControlDependentBlock) { if (node2DeepestControlDependentBlock != null) { // CASE is complicated because we have to deal with fall through and // because some BREAKs are early exits and some are not. // For now, we don't allow movement within a CASE. // // TODO(dcc): be less conservative about movement within CASE if (node2DeepestControlDependentBlock.isCase()) { return false; } // Don't allow breaks, continues, returns in control dependent // block because we don't actually create a control-flow graph // and so don't know if early exits site between the source // and the destination. // // This is overly conservative as it doesn't allow, for example, // moving in the following case: // while (a) { // source(); // // while(b) { // break; // } // // destination(); // } // // To fully support this kind of movement, we'll probably have to use // a CFG-based analysis rather than just looking at the AST. // // TODO(dcc): have nodesHaveSameControlFlow() use a CFG Predicate isEarlyExitPredicate = new Predicate() { @Override public boolean apply(Node input) { int nodeType = input.getType(); return nodeType == Token.RETURN || nodeType == Token.BREAK || nodeType == Token.CONTINUE; } }; return !NodeUtil.has(node2DeepestControlDependentBlock, isEarlyExitPredicate, NOT_FUNCTION_PREDICATE); } else { return true; } } else { return false; } } /** * Returns true if the number of times the child executes depends on the * parent. * * For example, the guard of an IF is not control dependent on the * IF, but its two THEN/ELSE blocks are. * * Also, the guard of WHILE and DO are control dependent on the parent * since the number of times it executes depends on the parent. */ private static boolean isControlDependentChild(Node child) { Node parent = child.getParent(); if (parent == null) { return false; } ArrayList siblings = Lists.newArrayList(parent.children()); int indexOfChildInParent = siblings.indexOf(child); switch(parent.getType()) { case Token.IF: case Token.HOOK: return (indexOfChildInParent == 1 || indexOfChildInParent == 2); case Token.WHILE: case Token.DO: return true; case Token.FOR: // Only initializer is not control dependent return indexOfChildInParent != 0; case Token.SWITCH: return indexOfChildInParent > 0; case Token.AND: return true; case Token.OR: return true; case Token.FUNCTION: return true; default: return false; } } private static Node closestControlDependentAncestor(Node node) { if (isControlDependentChild(node)) { return node; } // Note: node is not considered one of its ancestors for (Node ancestor : node.getAncestors()) { if (isControlDependentChild(ancestor)) { return ancestor; } } return null; } /** * Returns true if {@code possibleAncestor} is an ancestor of{@code node}. * A node is not considered to be an ancestor of itself. */ private static boolean nodeHasAncestor(Node node, Node possibleAncestor) { // Note node is not in node.getAncestors() for (Node ancestor : node.getAncestors()) { if (ancestor == possibleAncestor) { return true; } } return false; } /** * Returns true if a node has a CALL or a NEW descendant. */ private boolean nodeHasCall(Node node) { return NodeUtil.has(node, new Predicate() { @Override public boolean apply(Node input) { return input.isCall() || input.isNew(); }}, NOT_FUNCTION_PREDICATE); } /** * Represents an environment across which code might be moved, i.e. the set * of code that could be run in between the source and the destination. * * SideEffectAnalysis characterizes the code to be moved and the environment * in order to determine if they interact in such a way as to make the move * unsafe. * * Since determining the environment for an optimization can be tricky, * we provide several concrete subclasses that common classes of optimizations * may be able to reuse. */ public abstract static class AbstractMotionEnvironment { /** * Calculates the set of nodes that this environment represents. */ public abstract Set calculateEnvironment(); } /** * An environment for motion within a function. Given a * control flow graph and a source and destination node in the control * flow graph, instances of this object will calculate the environment between * the source and destination. */ public static class IntraproceduralMotionEnvironment extends AbstractMotionEnvironment { /** * Creates an intraprocedural motion environment. * * @param controlFlowGraph A control flow graph for function in which * code will be moved * @param cfgSource The code to be moved * @param cfgDestination The node immediately before which cfgSource * will be moved */ public IntraproceduralMotionEnvironment( ControlFlowGraph controlFlowGraph, Node cfgSource, Node cfgDestination) { } @Override public Set calculateEnvironment() { // TODO(dcc): Implement IntraproceduralMotionEnvironment return null; } } /** * An environment for motion between modules. Given a * module graph and as well as source and destination nodes and modules, * instances of this object will calculate the environment between the source * and destination. */ public static class CrossModuleMotionEnvironment extends AbstractMotionEnvironment { /** * Creates a cross module code motion environment. * * @param sourceNode The code to be moved * @param sourceModule The module for the code to be moved * @param destinationNode The node before which sourceNode will be inserted * @param destinationModule The module that destination is in * @param moduleGraph The module graph of the entire program */ public CrossModuleMotionEnvironment(Node sourceNode, JSModule sourceModule, Node destinationNode, JSModule destinationModule, JSModuleGraph moduleGraph) { } @Override public Set calculateEnvironment() { // TODO(dcc): Implement CrossModuleMotionEnvironment return null; } } /** * A low-level concrete environment that allows the client to specify * the environment nodes directly. Clients may wish to use this environment * if none of the higher-level environments fit their needs. */ public static class RawMotionEnvironment extends AbstractMotionEnvironment { Set environment; public RawMotionEnvironment(Set environment) { this.environment = environment; } @Override public Set calculateEnvironment() { return environment; } } /* * A combined representation for location set summaries. * * Basically, it is often easier to shuffle MOD/REF around together; this is * a value class for that purpose. */ private static class LocationSummary { private EffectLocation modSet; private EffectLocation refSet; public LocationSummary(EffectLocation modSet, EffectLocation refSet) { this.modSet = modSet; this.refSet = refSet; } public EffectLocation getModSet() { return modSet; } public EffectLocation getRefSet() { return refSet; } } /** * Interface representing the notion of an effect location -- an abstract * location that can be modified or referenced. * *

      Since there are an infinite number of possible concrete locations * in a running program, this abstraction must be imprecise (i.e. there * will be some distinct concrete locations that are indistinguishable * under the abstraction). * *

      Different location abstractions will provide their * own implementations of this interface, based on the level and kind * of precision they provide. */ private static interface EffectLocation { /** * Does the receiver's effect location intersect a given effect location? * That is, could any of the concrete storage locations (fields, variables, * etc.) represented by the receiver be contained in the set of concrete * storage locations represented by the given abstract effect location. */ public boolean intersectsLocation(EffectLocation otherLocation); /** * Returns the result of merging the given effect location with * the receiver. The concrete locations represented by the result must * include all the concrete locations represented by each of the merged * locations and may also possibly include more (i.e., a join may * introduce a loss of precision). */ public EffectLocation join(EffectLocation otherLocation); /** * Does the effect location represent any possible concrete locations? */ public boolean isEmpty(); } /** * An abstract class representing a location abstraction. (Here "abstraction" * means an imprecise representation of concrete side effects.) * *

      Implementations of this class will each provide own their * implementation(s) of SideEffectLocation and methods to determine the side * effect locations of a given piece of code. */ private abstract static class LocationAbstraction { /** Calculates the abstraction-specific side effects * for the node. */ abstract LocationSummary calculateLocationSummary(Node node); /** * Returns an abstraction-specific EffectLocation representing * no location. * *

      The bottom location joined with any location should return * that location. */ abstract EffectLocation getBottomLocation(); /** * Calculates the abstraction-specific side effects * for the node. */ public LocationSummary calculateLocationSummary(Set nodes) { EffectLocation modAccumulator = getBottomLocation(); EffectLocation refAccumulator = getBottomLocation(); for (Node node : nodes) { LocationSummary nodeLocationSummary = calculateLocationSummary(node); modAccumulator = modAccumulator.join(nodeLocationSummary.getModSet()); refAccumulator = refAccumulator.join(nodeLocationSummary.getRefSet()); } return new LocationSummary(modAccumulator, refAccumulator); } } /** * A very imprecise location abstraction in which there are only two abstract * locations: one representing all concrete locations and one for bottom * (no concrete locations). * * This implementation is a thin wrapper on NodeUtil.mayHaveSideEffects() * and NodeUtil.canBeSideEffected() -- it doesn't add any real value other * than to prototype the LocationAbstraction interface. */ private static class DegenerateLocationAbstraction extends LocationAbstraction { private static final EffectLocation EVERY_LOCATION = new DegenerateEffectLocation(); private static final EffectLocation NO_LOCATION = new DegenerateEffectLocation(); @Override EffectLocation getBottomLocation() { return NO_LOCATION; } @Override public LocationSummary calculateLocationSummary(Node node) { return new LocationSummary(calculateModSet(node), calculateRefSet(node)); } EffectLocation calculateRefSet(Node node) { if (NodeUtil.canBeSideEffected(node)) { return EVERY_LOCATION; } else { return NO_LOCATION; } } EffectLocation calculateModSet(Node node) { if (NodeUtil.mayHaveSideEffects(node)) { return EVERY_LOCATION; } else { return NO_LOCATION; } } private static class DegenerateEffectLocation implements EffectLocation { @Override public EffectLocation join(EffectLocation otherLocation) { if (otherLocation == EVERY_LOCATION) { return otherLocation; } else { return this; } } @Override public boolean intersectsLocation(EffectLocation otherLocation) { return this == EVERY_LOCATION && otherLocation == EVERY_LOCATION; } @Override public boolean isEmpty() { return this == NO_LOCATION; } } } /** * A location abstraction based on the visibility of concrete locations. * * A global variables are treated as one common location, as are all heap * storage locations. * * Local variables are broken up into two classes, one for truly local * variables and one for local variables captured by an inner scope. Each * of these classes has their own separate location representing the * variables in the class. * * Parameter variables are considered to be heap locations since they * can be accessed via the arguments object which itself can be aliased. * * A more precise analysis could: * 1) put parameters on the heap only when "arguments" is actually used * in a method * 2) recognize that GETPROPs cannot access or modify parameters, only * GETELEMs * * TODO(dcc): Don't merge parameters with the heap unless necessary. * * Internally, abstract locations are represented as integers * with bits set (masks) representing the storage classes in the location, so * that joining is bit-wise ORing and intersection is bitwise AND. */ private static class VisibilityLocationAbstraction extends LocationAbstraction { /** The "bottom" location. Used to signify an empty location set */ private static final int VISIBILITY_LOCATION_NONE = 0; /** The "top" location. Used to signify the set containing all locations */ private static final int UNKNOWN_LOCATION_MASK = 0xFFFFFFFF; private static final int LOCAL_VARIABLE_LOCATION_MASK = 1 << 1; private static final int CAPTURED_LOCAL_VARIABLE_LOCATION_MASK = 1 << 2; private static final int GLOBAL_VARIABLE_LOCATION_MASK = 1 << 3; private static final int HEAP_LOCATION_MASK = 1 << 4; AbstractCompiler compiler; VariableVisibilityAnalysis variableVisibilityAnalysis; VariableUseDeclarationMap variableUseMap; private VisibilityLocationAbstraction(AbstractCompiler compiler, VariableVisibilityAnalysis variableVisibilityAnalysis, VariableUseDeclarationMap variableUseMap) { this.compiler = compiler; this.variableVisibilityAnalysis = variableVisibilityAnalysis; this.variableUseMap = variableUseMap; } /** * Calculates the MOD/REF summary for the given node. */ @Override LocationSummary calculateLocationSummary(Node node) { int visibilityRefLocations = VISIBILITY_LOCATION_NONE; int visibilityModLocations = VISIBILITY_LOCATION_NONE; for (Node reference : findStorageLocationReferences(node)) { int effectMask; if (reference.isName()) { // Variable access effectMask = effectMaskForVariableReference(reference); } else { // Heap access effectMask = HEAP_LOCATION_MASK; } if (storageNodeIsLValue(reference)) { visibilityModLocations |= effectMask; } if (storageNodeIsRValue(reference)) { visibilityRefLocations |= effectMask; } } VisibilityBasedEffectLocation modSet = new VisibilityBasedEffectLocation(visibilityModLocations); VisibilityBasedEffectLocation refSet = new VisibilityBasedEffectLocation(visibilityRefLocations); return new LocationSummary(modSet, refSet); } /** * Returns the set of references to storage locations (both variables * and the heap) under {@code root}. */ private Set findStorageLocationReferences(Node root) { final Set references = Sets.newHashSet(); NodeTraversal.traverse(compiler, root, new AbstractShallowCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isGet(n) || (n.isName() && !parent.isFunction())) { references.add(n); } } }); return references; } /** * Calculates the effect mask for a variable reference. */ private int effectMaskForVariableReference(Node variableReference) { Preconditions.checkArgument(variableReference.isName()); int effectMask = VISIBILITY_LOCATION_NONE; Node declaringNameNode = variableUseMap.findDeclaringNameNodeForUse(variableReference); if (declaringNameNode != null) { VariableVisibility visibility = variableVisibilityAnalysis.getVariableVisibility(declaringNameNode); switch (visibility) { case LOCAL: effectMask = LOCAL_VARIABLE_LOCATION_MASK; break; case CAPTURED_LOCAL: effectMask = CAPTURED_LOCAL_VARIABLE_LOCATION_MASK; break; case PARAMETER: // Parameters are considered to be on the heap since they // can be accessed via the arguments object. effectMask = HEAP_LOCATION_MASK; break; case GLOBAL: effectMask = GLOBAL_VARIABLE_LOCATION_MASK; break; default: throw new IllegalStateException("Unrecognized variable" + " visibility: " + visibility); } } else { // Couldn't find a variable for the reference effectMask = UNKNOWN_LOCATION_MASK; } return effectMask; } @Override EffectLocation getBottomLocation() { return new VisibilityBasedEffectLocation(VISIBILITY_LOCATION_NONE); } /** * Returns true if the node is a storage node. * * Only NAMEs, GETPROPs, and GETELEMs are storage nodes. */ private static boolean isStorageNode(Node node) { return node.isName() || NodeUtil.isGet(node); } /** * Return true if the storage node is an r-value. */ private static boolean storageNodeIsRValue(Node node) { Preconditions.checkArgument(isStorageNode(node)); // We consider all names to be r-values unless // LHS of Token.ASSIGN // LHS of of for in expression // Child of VAR Node parent = node.getParent(); if (storageNodeIsLValue(node)) { // Assume l-value is NOT an r-value // unless it is a non-simple assign // or an increment/decrement boolean nonSimpleAssign = NodeUtil.isAssignmentOp(parent) && !parent.isAssign(); return (nonSimpleAssign || parent.isDec() || parent.isInc()); } return true; } /** * Return true if the storage node is an l-value. */ private static boolean storageNodeIsLValue(Node node) { Preconditions.checkArgument(isStorageNode(node)); return NodeUtil.isLValue(node); } /** * An abstract effect location based the visibility of the * concrete storage location. * * See {@link VisibilityLocationAbstraction} for deeper description * of this abstraction. * * The effect locations are stored as bits set on an integer, so * intersect, join, etc. are the standard bitwise operations. */ private static class VisibilityBasedEffectLocation implements EffectLocation { int visibilityMask = VISIBILITY_LOCATION_NONE; public VisibilityBasedEffectLocation(int visibilityMask) { this.visibilityMask = visibilityMask; } @Override public boolean intersectsLocation(EffectLocation otherLocation) { Preconditions.checkArgument(otherLocation instanceof VisibilityBasedEffectLocation); int otherMask = ((VisibilityBasedEffectLocation) otherLocation).visibilityMask; return (visibilityMask & otherMask) > 0; } @Override public boolean isEmpty() { return visibilityMask == VISIBILITY_LOCATION_NONE; } @Override public EffectLocation join(EffectLocation otherLocation) { Preconditions.checkArgument(otherLocation instanceof VisibilityBasedEffectLocation); int otherMask = ((VisibilityBasedEffectLocation) otherLocation).visibilityMask; int joinedMask = visibilityMask | otherMask; return new VisibilityBasedEffectLocation(joinedMask); } } } /** * Maps NAME nodes that refer to variables to the NAME * nodes that declared them. */ private static class VariableUseDeclarationMap { private AbstractCompiler compiler; // Maps a using name to its declaring name private Map referencesByNameNode; public VariableUseDeclarationMap(AbstractCompiler compiler) { this.compiler = compiler; } /** * Adds a map from each use NAME in {@code root} to its corresponding * declaring name, *provided the declaration is also under root*. * * If the declaration is not under root, then the reference will * not be added to the map. */ public void mapUses(Node root) { referencesByNameNode = Maps.newHashMap(); ReferenceCollectingCallback callback = new ReferenceCollectingCallback(compiler, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); NodeTraversal.traverse(compiler, root, callback); for (Var variable : callback.getAllSymbols()) { ReferenceCollection referenceCollection = callback.getReferences(variable); for (Reference reference : referenceCollection.references) { Node referenceNameNode = reference.getNode(); // Note that this counts a declaration as a reference to itself referencesByNameNode.put(referenceNameNode, variable.getNameNode()); } } } /** * Returns the NAME node for the declaration of the variable * that {@code usingNameNode} refers to, if it is in the map, * or {@code null} otherwise. */ public Node findDeclaringNameNodeForUse(Node usingNameNode) { Preconditions.checkArgument(usingNameNode.isName()); return referencesByNameNode.get(usingNameNode); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ByPathWarningsGuard.java0000644000175000017500000000671212115204405027370 0ustar apoapo/* * 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.base.Preconditions; import java.util.List; /** * An implementation of a {@link WarningsGuard} that can modify the * {@link CheckLevel} based on the file that caused the warning, and whether * this file matches a set of paths (specified either as include or exclude * of path name parts). * *

      For example: *

       * List paths = new ArrayList();
       * paths.add("foo");
       * WarningsGuard guard =
       *     ByPathWarningsGuard.forPath(paths, CheckLevel.ERROR, 1);
       * 
      * * This guard will convert any warning that came from a file that contains "foo" * in its path to an error. * */ public class ByPathWarningsGuard extends WarningsGuard { private static final long serialVersionUID = 1L; private final List paths; private final boolean include; private final int priority; private CheckLevel level; /** * Constructs a new instance. The priority is determined by the * {@link CheckLevel}: ERROR have Priority.STRICT, and OFF have priority * FILTER_BY_PATH. * * Use {@link #forPath} or {@link #exceptPath} to actually create a new * instance. */ private ByPathWarningsGuard( List paths, boolean include, CheckLevel level) { Preconditions.checkArgument(paths != null); Preconditions.checkArgument( level == CheckLevel.OFF || level == CheckLevel.ERROR); this.paths = paths; this.include = include; this.level = level; this.priority = level == CheckLevel.ERROR ? WarningsGuard.Priority.STRICT.value : WarningsGuard.Priority.FILTER_BY_PATH.value; } /** * @param paths Paths for matching. * @param level The {@link CheckLevel} to apply on affected files. * @return a new {@link ByPathWarningsGuard} that would affect any file in the * given set of paths. */ public static ByPathWarningsGuard forPath( List paths, CheckLevel level) { return new ByPathWarningsGuard(paths, true, level); } /** * @param paths Paths for matching. * @param level The {@link CheckLevel} to apply on affected files. * @return a new {@link ByPathWarningsGuard} that would affect any file not * in the given set of paths. */ public static ByPathWarningsGuard exceptPath( List paths, CheckLevel level) { return new ByPathWarningsGuard(paths, false, level); } @Override public CheckLevel level(JSError error) { final String errorPath = error.sourceName; CheckLevel defaultLevel = error.getDefaultLevel(); if (defaultLevel != CheckLevel.ERROR && errorPath != null) { boolean inPath = false; for (String path : paths) { inPath |= errorPath.contains(path); } if (inPath == include) { return level; } } return null; } @Override protected int getPriority() { return priority; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InlineCostEstimator.java0000644000175000017500000000531712115204405027444 0ustar apoapo/* * 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.javascript.rhino.Node; /** * For use with CodeGenerator to determine the cost of generated code. * * @see CodeGenerator * @see CodePrinter */ class InlineCostEstimator { // For now simply assume identifiers are 2 characters. private static final String ESTIMATED_IDENTIFIER = "ab"; static final int ESTIMATED_IDENTIFIER_COST = ESTIMATED_IDENTIFIER.length(); private InlineCostEstimator() { } /** * Determines the size of the JS code. */ static int getCost(Node root) { return getCost(root, Integer.MAX_VALUE); } /** * Determines the estimated size of the JS snippet represented by the node. */ static int getCost(Node root, int costThreshhold) { CompiledSizeEstimator estimator = new CompiledSizeEstimator(costThreshhold); estimator.add(root); return estimator.getCost(); } /** * Code consumer that estimates compiled size by assuming names are * shortened and all whitespace is stripped. */ private static class CompiledSizeEstimator extends CodeConsumer { private int maxCost; private int cost = 0; private char last = '\0'; private boolean continueProcessing = true; CompiledSizeEstimator(int costThreshhold) { this.maxCost = costThreshhold; } void add(Node root) { CodeGenerator cg = CodeGenerator.forCostEstimation(this); cg.add(root); } int getCost() { return cost; } @Override boolean continueProcessing() { return continueProcessing; } @Override char getLastChar() { return last; } @Override void append(String str){ last = str.charAt(str.length() - 1); cost += str.length(); if (maxCost <= cost) { continueProcessing = false; } } @Override void addIdentifier(String identifier) { add(ESTIMATED_IDENTIFIER); } /** * Constants (true, false, null) are considered basically free, * because it's likely that they will get folded when we're done. */ @Override void addConstant(String newcode) { add("0"); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ReplaceStrings.java0000644000175000017500000004111312115204405026424 0ustar apoapo/* * 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.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; /** * Replaces JavaScript strings in the list of supplied methods with shortened * forms. Useful for replacing debug message such as: throw new * Error("Something bad happened"); with generated codes like: throw new * Error("a"); This makes the compiled JavaScript smaller and prevents us from * leaking details about the source code. * * Based in concept on the work by Jared Jacobs. */ class ReplaceStrings extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType BAD_REPLACEMENT_CONFIGURATION = DiagnosticType.warning( "JSC_BAD_REPLACEMENT_CONFIGURATION", "Bad replacement configuration."); private final String DEFAULT_PLACEHOLDER_TOKEN = "`"; private final String placeholderToken; private static final String REPLACE_ONE_MARKER = "?"; private static final String REPLACE_ALL_MARKER = "*"; private final AbstractCompiler compiler; private final JSTypeRegistry registry; // private final Map functions = Maps.newHashMap(); private final Multimap methods = HashMultimap.create(); private final NameGenerator nameGenerator; private final Map results = Maps.newLinkedHashMap(); /** * Describes a function to look for a which parameters to replace. */ private class Config { // TODO(johnlenz): Support name "groups" so that unrelated strings can // reuse strings. For example, event-id can reuse the names used for logger // classes. final String name; final int parameter; static final int REPLACE_ALL_VALUE = 0; Config(String name, int parameter) { this.name = name; this.parameter = parameter; } } /** * Describes a replacement that occurred. */ class Result { // The original message with non-static content replaced with // {@code placeholderToken}. public final String original; public final String replacement; public final List replacementLocations = Lists.newLinkedList(); Result(String original, String replacement) { this.original = original; this.replacement = replacement; } void addLocation(Node n) { replacementLocations.add(new Location( n.getSourceFileName(), n.getLineno(), n.getCharno())); } } /** Represent a source location where a replacement occurred. */ class Location { public final String sourceFile; public final int line; public final int column; Location(String sourceFile, int line, int column) { this.sourceFile = sourceFile; this.line = line; this.column = column; } } /** * @param placeholderToken Separator to use between string parts. Used to replace * non-static string content. * @param functionsToInspect A list of function configurations in the form of * function($,,,) * or * class.prototype.method($,,,) * @param blacklisted A set of names that should not be used as replacement * strings. Useful to prevent unwanted strings for appearing in the * final output. * where '$' is used to indicate which parameter should be replaced. */ ReplaceStrings( AbstractCompiler compiler, String placeholderToken, List functionsToInspect, Set blacklisted, VariableMap previousMappings) { this.compiler = compiler; this.placeholderToken = placeholderToken.isEmpty() ? DEFAULT_PLACEHOLDER_TOKEN : placeholderToken; this.registry = compiler.getTypeRegistry(); Iterable reservedNames = blacklisted; if (previousMappings != null) { Set previous = previousMappings.getOriginalNameToNewNameMap().keySet(); reservedNames = Iterables.concat(blacklisted, previous); initMapping(previousMappings, blacklisted); } this.nameGenerator = createNameGenerator(reservedNames); // Initialize the map of functions to inspect for renaming candidates. parseConfiguration(functionsToInspect); } private void initMapping( VariableMap previousVarMap, Set reservedNames) { Map previous = previousVarMap.getOriginalNameToNewNameMap(); for (Map.Entry entry : previous.entrySet()) { String key = entry.getKey(); if (!reservedNames.contains(key)) { String value = entry.getValue(); results.put(value, new Result(value, key)); } } } static final Predicate USED_RESULTS = new Predicate() { @Override public boolean apply(Result result) { // The list of locations may be empty if the map // was pre-populated from a previous map. return !result.replacementLocations.isEmpty(); } }; // Get the list of all replacements performed. List getResult() { return ImmutableList.copyOf( Iterables.filter(results.values(), USED_RESULTS)); } // Get the list of replaces as a VariableMap VariableMap getStringMap() { ImmutableMap.Builder map = ImmutableMap.builder(); for (Result result : Iterables.filter(results.values(), USED_RESULTS)) { map.put(result.replacement, result.original); } VariableMap stringMap = new VariableMap(map.build()); return stringMap; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // TODO(johnlenz): Determine if it is necessary to support ".call" or // ".apply". switch (n.getType()) { case Token.NEW: // e.g. new Error('msg'); case Token.CALL: // e.g. Error('msg'); Node calledFn = n.getFirstChild(); // Look for calls to static functions. String name = calledFn.getQualifiedName(); if (name != null) { Config config = findMatching(name); if (config != null) { doSubstitutions(t, config, n); return; } } // Look for calls to class methods. if (NodeUtil.isGet(calledFn)) { Node rhs = calledFn.getLastChild(); if (rhs.isName() || rhs.isString()) { String methodName = rhs.getString(); Collection classes = methods.get(methodName); if (classes != null) { Node lhs = calledFn.getFirstChild(); if (lhs.getJSType() != null) { JSType type = lhs.getJSType().restrictByNotNullOrUndefined(); Config config = findMatchingClass(type, classes); if (config != null) { doSubstitutions(t, config, n); return; } } } } } break; } } /** * @param name The function name to find. * @return The Config object for the name or null if no match was found. */ private Config findMatching(String name) { Config config = functions.get(name); if (config == null) { name = name.replace('$', '.'); config = functions.get(name); } return config; } /** * @return The Config object for the class match the specified type or null * if no match was found. */ private Config findMatchingClass( JSType callClassType, Collection declarationNames) { if (!callClassType.isNoObjectType() && !callClassType.isUnknownType()) { for (String declarationName : declarationNames) { String className = getClassFromDeclarationName(declarationName); JSType methodClassType = registry.getType(className); if (methodClassType != null && callClassType.isSubtype(methodClassType)) { return functions.get(declarationName); } } } return null; } /** * Replace the parameters specified in the config, if possible. */ private void doSubstitutions(NodeTraversal t, Config config, Node n) { Preconditions.checkState( n.isNew() || n.isCall()); if (config.parameter != Config.REPLACE_ALL_VALUE) { // Note: the first child is the function, but the parameter id is 1 based. Node arg = n.getChildAtIndex(config.parameter); if (arg != null) { replaceExpression(t, arg, n); } } else { // Replace all parameters. Node firstParam = n.getFirstChild().getNext(); for (Node arg = firstParam; arg != null; arg = arg.getNext()) { arg = replaceExpression(t, arg, n); } } } /** * Replaces a string expression with a short encoded string expression. * * @param t The traversal * @param expr The expression node * @param parent The expression node's parent * @return The replacement node (or the original expression if no replacement * is made) */ private Node replaceExpression(NodeTraversal t, Node expr, Node parent) { Node replacement; String key = null; String replacementString; switch (expr.getType()) { case Token.STRING: key = expr.getString(); replacementString = getReplacement(key); replacement = IR.string(replacementString); break; case Token.ADD: StringBuilder keyBuilder = new StringBuilder(); Node keyNode = IR.string(""); replacement = buildReplacement(expr, keyNode, keyBuilder); key = keyBuilder.toString(); replacementString = getReplacement(key); keyNode.setString(replacementString); break; case Token.NAME: // If the referenced variable is a constant, use its value. Scope.Var var = t.getScope().getVar(expr.getString()); if (var != null && var.isConst()) { Node value = var.getInitialValue(); if (value != null && value.isString()) { key = value.getString(); replacementString = getReplacement(key); replacement = IR.string(replacementString); break; } } return expr; default: // This may be a function call or a variable reference. We don't // replace these. return expr; } Preconditions.checkNotNull(key); Preconditions.checkNotNull(replacementString); recordReplacement(expr, key); parent.replaceChild(expr, replacement); compiler.reportCodeChange(); return replacement; } /** * Get a replacement string for the provide key text. */ private String getReplacement(String key) { Result result = results.get(key); if (result != null) { return result.replacement; } String replacement = nameGenerator.generateNextName(); result = new Result(key, replacement); results.put(key, result); return replacement; } /** * Record the location the replacement was made. */ private void recordReplacement(Node n, String key) { Result result = results.get(key); Preconditions.checkState(result != null); result.addLocation(n); } /** * Builds a replacement abstract syntax tree for the string expression {@code * expr}. Appends any string literal values that are encountered to * {@code keyBuilder}, to build the expression's replacement key. * * @param expr A JS expression that evaluates to a string value * @param prefix The JS expression to which {@code expr}'s replacement is * logically being concatenated. It is a partial solution to the * problem at hand and will either be this method's return value or a * descendant of it. * @param keyBuilder A builder of the string expression's replacement key * @return The abstract syntax tree that should replace {@code expr} */ private Node buildReplacement( Node expr, Node prefix, StringBuilder keyBuilder) { switch (expr.getType()) { case Token.ADD: Node left = expr.getFirstChild(); Node right = left.getNext(); prefix = buildReplacement(left, prefix, keyBuilder); return buildReplacement(right, prefix, keyBuilder); case Token.STRING: keyBuilder.append(expr.getString()); return prefix; default: keyBuilder.append(placeholderToken); prefix = IR.add(prefix, IR.string(placeholderToken)); return IR.add(prefix, expr.cloneTree()); } } /** * From a provide name extract the method name. */ private String getMethodFromDeclarationName(String fullDeclarationName) { String[] parts = fullDeclarationName.split("\\.prototype\\."); Preconditions.checkState(parts.length == 1 || parts.length == 2); if (parts.length == 2) { return parts[1]; } return null; } /** * From a provide name extract the class name. */ private String getClassFromDeclarationName(String fullDeclarationName) { String[] parts = fullDeclarationName.split("\\.prototype\\."); Preconditions.checkState(parts.length == 1 || parts.length == 2); if (parts.length == 2) { return parts[0]; } return null; } /** * Build the data structures need by this pass from the provided * list of functions and methods. */ private void parseConfiguration(List functionsToInspect) { for (String function : functionsToInspect) { Config config = parseConfiguration(function); functions.put(config.name, config); String method = getMethodFromDeclarationName(config.name); if (method != null) { methods.put(method, config.name); } } } /** * Convert the provide string into a Config. The string can be a static function: * foo(,,?) * foo.bar(?) * or a class method: * foo.prototype.bar(?) * And is allowed to either replace all parameters using "*" or one parameter "?". * "," is used as a placeholder for ignored parameters. */ private Config parseConfiguration(String function) { // Looks like this function_name(,$,) int first = function.indexOf('('); int last = function.indexOf(')'); // TODO(johnlenz): Make parsing precondition checks JSErrors reports. Preconditions.checkState(first != -1 && last != -1); String name = function.substring(0, first); String params = function.substring(first+1, last); int paramCount = 0; int replacementParameter = -1; String[] parts = params.split(","); for (String param : parts) { paramCount++; if (param.equals(REPLACE_ALL_MARKER)) { Preconditions.checkState(paramCount == 1 && parts.length == 1); replacementParameter = Config.REPLACE_ALL_VALUE; } else if (param.equals(REPLACE_ONE_MARKER)) { // TODO(johnlenz): Support multiple. Preconditions.checkState(replacementParameter == -1); replacementParameter = paramCount; } else { // TODO(johnlenz): report an error. Preconditions.checkState(param.isEmpty(), "Unknown marker", param); } } Preconditions.checkState(replacementParameter != -1); return new Config(name, replacementParameter); } /** * Use a name generate to create names so the names overlap with the names * used for variable and properties. */ private static NameGenerator createNameGenerator(Iterable reserved) { final String namePrefix = ""; final char[] reservedChars = new char[0]; return new NameGenerator( ImmutableSet.copyOf(reserved), namePrefix, reservedChars); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/InvocationsCallback.java0000644000175000017500000000343212115204405027412 0ustar apoapo/* * Copyright 2007 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; /** * Traversal callback that finds method invocations of the form * *
       * call
       *   getprop
       *     ...
       *     string
       *   ...
       * 
      * * and invokes a method defined by subclasses for processing these invocations. * */ abstract class InvocationsCallback extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isCall()) { return; } Node function = n.getFirstChild(); if (!function.isGetProp()) { return; } Node nameNode = function.getFirstChild().getNext(); // Don't care about numerical or variable indexes if (!nameNode.isString()) { return; } visit(t, n, parent, nameNode.getString()); } /** * Called for each callnode that is a method invocation. * * @param callNode node of type call * @param parent parent of callNode * @param callName name of method invoked by first child of call */ abstract void visit(NodeTraversal t, Node callNode, Node parent, String callName); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ClosureCodingConvention.java0000644000175000017500000003514112115204405030306 0ustar apoapo/* * Copyright 2007 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.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Collection; import java.util.List; import java.util.Set; /** * This describes the Closure-specific JavaScript coding conventions. * */ public class ClosureCodingConvention extends CodingConventions.Proxy { private static final long serialVersionUID = 1L; static final DiagnosticType OBJECTLIT_EXPECTED = DiagnosticType.warning( "JSC_REFLECT_OBJECTLIT_EXPECTED", "Object literal expected as second argument"); private final Set indirectlyDeclaredProperties; public ClosureCodingConvention() { this(CodingConventions.getDefault()); } public ClosureCodingConvention(CodingConvention wrapped) { super(wrapped); Set props = Sets.newHashSet( "superClass_", "instance_", "getInstance"); props.addAll(wrapped.getIndirectlyDeclaredProperties()); indirectlyDeclaredProperties = ImmutableSet.copyOf(props); } /** * Closure's goog.inherits adds a {@code superClass_} property to the * subclass, and a {@code constructor} property. */ @Override public void applySubclassRelationship(FunctionType parentCtor, FunctionType childCtor, SubclassType type) { super.applySubclassRelationship(parentCtor, childCtor, type); if (type == SubclassType.INHERITS) { childCtor.defineDeclaredProperty("superClass_", parentCtor.getPrototype(), childCtor.getSource()); childCtor.getPrototype().defineDeclaredProperty("constructor", // Notice that constructor functions do not need to be covariant // on the superclass. // So if G extends F, new G() and new F() can accept completely // different argument types, but G.prototype.constructor needs // to be covariant on F.prototype.constructor. // To get around this, we just turn off type-checking on arguments // and return types of G.prototype.constructor. childCtor.cloneWithoutArrowType(), childCtor.getSource()); } } /** * {@inheritDoc} * *

      Understands several different inheritance patterns that occur in * Google code (various uses of {@code inherits} and {@code mixin}). */ @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { SubclassRelationship relationship = super.getClassesDefinedByCall(callNode); if (relationship != null) return relationship; Node callName = callNode.getFirstChild(); SubclassType type = typeofClassDefiningName(callName); if (type != null) { Node subclass = null; Node superclass = callNode.getLastChild(); // There are six possible syntaxes for a class-defining method: // SubClass.inherits(SuperClass) // goog.inherits(SubClass, SuperClass) // goog$inherits(SubClass, SuperClass) // SubClass.mixin(SuperClass.prototype) // goog.mixin(SubClass.prototype, SuperClass.prototype) // goog$mixin(SubClass.prototype, SuperClass.prototype) boolean isDeprecatedCall = callNode.getChildCount() == 2 && callName.isGetProp(); if (isDeprecatedCall) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } else { return null; } if (type == SubclassType.MIXIN) { // Only consider mixins that mix two prototypes as related to // inheritance. if (!endsWithPrototype(superclass)) { return null; } if (!isDeprecatedCall) { if (!endsWithPrototype(subclass)) { return null; } // Strip off the prototype from the name. subclass = subclass.getFirstChild(); } superclass = superclass.getFirstChild(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { return new SubclassRelationship(type, subclass, superclass); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.isGetProp()) { methodName = callName.getLastChild().getString(); } else if (callName.isName()) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName) || super.isSuperClassReference(propertyName); } /** * Given a qualified name node, returns whether "prototype" is at the end. * For example: * a.b.c => false * a.b.c.prototype => true */ private boolean endsWithPrototype(Node qualifiedName) { return qualifiedName.isGetProp() && qualifiedName.getLastChild().getString().equals("prototype"); } /** * Extracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Extracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.isGetProp()) { String qualifiedName = callee.getQualifiedName(); if (functionName.equals(qualifiedName)) { Node target = callee.getNext(); if (target != null && target.isString()) { className = target.getString(); } } } } return className; } /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.isArrayLit()) { List typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.isString()) { typeNames.add(name.getString()); } } return typeNames; } } return super.identifyTypeDeclarationCall(n); } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callArg = callNode.getFirstChild(); String callName = callArg.getQualifiedName(); // Use both the original name and the post-CollapseProperties name. if (!("goog.addSingletonGetter".equals(callName) || "goog$addSingletonGetter".equals(callName)) || callNode.getChildCount() != 2) { return super.getSingletonGetterClassName(callNode); } return callArg.getNext().getQualifiedName(); } @Override public void applySingletonGetter(FunctionType functionType, FunctionType getterType, ObjectType objectType) { super.applySingletonGetter(functionType, getterType, objectType); functionType.defineDeclaredProperty("getInstance", getterType, functionType.getSource()); functionType.defineDeclaredProperty("instance_", objectType, functionType.getSource()); } @Override public String getGlobalObject() { return "goog.global"; } private final Set propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()) || super.isPropertyTestFunction(call); } @Override public ObjectLiteralCast getObjectLiteralCast(Node callNode) { Preconditions.checkArgument(callNode.isCall()); ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode); if (proxyCast != null) { return proxyCast; } Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callNode.getChildCount() != 3) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (!objectNode.isObjectLit()) { return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED); } return new ObjectLiteralCast( typeNode.getQualifiedName(), typeNode.getNext(), null); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection getAssertionFunctions() { return ImmutableList.of( new AssertionFunctionSpec("goog.asserts.assert"), new AssertionFunctionSpec("goog.asserts.assertNumber", JSTypeNative.NUMBER_TYPE), new AssertionFunctionSpec("goog.asserts.assertString", JSTypeNative.STRING_TYPE), new AssertionFunctionSpec("goog.asserts.assertFunction", JSTypeNative.FUNCTION_INSTANCE_TYPE), new AssertionFunctionSpec("goog.asserts.assertObject", JSTypeNative.OBJECT_TYPE), new AssertionFunctionSpec("goog.asserts.assertArray", JSTypeNative.ARRAY_TYPE), new AssertInstanceofSpec("goog.asserts.assertInstanceof") ); } @Override public Bind describeFunctionBind(Node n, boolean useTypeInfo) { Bind result = super.describeFunctionBind(n, useTypeInfo); if (result != null) { return result; } if (!n.isCall()) { return null; } Node callTarget = n.getFirstChild(); String name = callTarget.getQualifiedName(); if (name != null) { if (name.equals("goog.bind") || name.equals("goog$bind")) { // goog.bind(fn, self, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = safeNext(fn); Node parameters = safeNext(thisValue); return new Bind(fn, thisValue, parameters); } if (name.equals("goog.partial") || name.equals("goog$partial")) { // goog.partial(fn, args...); Node fn = callTarget.getNext(); if (fn == null) { return null; } Node thisValue = null; Node parameters = safeNext(fn); return new Bind(fn, thisValue, parameters); } } return null; } @Override public Collection getIndirectlyDeclaredProperties() { return indirectlyDeclaredProperties; } private Node safeNext(Node n) { if (n != null) { return n.getNext(); } return null; } /** * A function that will throw an exception when if the value is not * an instanceof a specific type. */ public static class AssertInstanceofSpec extends AssertionFunctionSpec { public AssertInstanceofSpec(String functionName) { super(functionName, JSTypeNative.OBJECT_TYPE); } /** * Returns the type for a type assertion, or null if the function asserts * that the node must not be null or undefined. */ @Override public JSType getAssertedType(Node call, JSTypeRegistry registry) { if (call.getChildCount() > 2) { Node constructor = call.getFirstChild().getNext().getNext(); if (constructor != null) { JSType ownerType = constructor.getJSType(); if (ownerType != null && ownerType.isFunctionType() && ownerType.isConstructor()) { FunctionType functionType = ((FunctionType) ownerType); return functionType.getInstanceType(); } } } return super.getAssertedType(call, registry); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AmbiguateProperties.java0000644000175000017500000005135312115204405027461 0ustar apoapo/* * 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.TypeValidator.TypeMismatch; import com.google.javascript.jscomp.graph.AdjacencyGraph; import com.google.javascript.jscomp.graph.Annotation; import com.google.javascript.jscomp.graph.GraphColoring; import com.google.javascript.jscomp.graph.GraphColoring.GreedyGraphColoring; import com.google.javascript.jscomp.graph.GraphNode; import com.google.javascript.jscomp.graph.SubGraph; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.BitSet; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.logging.Logger; /** * Renames unrelated properties to the same name, using type information. * This allows better compression as more properties can be given short names. * *

      Properties are considered unrelated if they are never referenced from the * same type or from a subtype of each others' types, thus this pass is only * effective if type checking is enabled. * * Example: * * Foo.fooprop = 0; * Foo.fooprop2 = 0; * Bar.barprop = 0; * * * becomes: * * * Foo.a = 0; * Foo.b = 0; * Bar.a = 0; * * */ class AmbiguateProperties implements CompilerPass { private static final Logger logger = Logger.getLogger( AmbiguateProperties.class.getName()); private final AbstractCompiler compiler; private final List stringNodesToRename = Lists.newArrayList(); private final char[] reservedCharacters; /** Map from property name to Property object */ private final Map propertyMap = Maps.newHashMap(); /** Property names that don't get renamed */ private final Set externedNames = Sets.newHashSet(); /** Names to which properties shouldn't be renamed, to avoid name conflicts */ private final Set quotedNames = Sets.newHashSet(); /** Map from original property name to new name. */ private final Map renamingMap = Maps.newHashMap(); /** * Sorts Property objects by their count, breaking ties alphabetically to * ensure a deterministic total ordering. */ private static final Comparator FREQUENCY_COMPARATOR = new Comparator() { @Override public int compare(Property p1, Property p2) { if (p1.numOccurrences != p2.numOccurrences) { return p2.numOccurrences - p1.numOccurrences; } return p1.oldName.compareTo(p2.oldName); } }; /** A map from JSType to a unique representative Integer. */ private BiMap intForType = HashBiMap.create(); /** * A map from JSType to JSTypeBitSet representing the types related * to the type. */ private Map relatedBitsets = Maps.newHashMap(); /** A set of types that invalidate properties from ambiguation. */ private final Set invalidatingTypes; /** * Prefix of properties to skip renaming. These should be renamed in the * RenameProperties pass. */ static final String SKIP_PREFIX = "JSAbstractCompiler"; AmbiguateProperties(AbstractCompiler compiler, char[] reservedCharacters) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); this.compiler = compiler; this.reservedCharacters = reservedCharacters; JSTypeRegistry r = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( r.getNativeType(JSTypeNative.ALL_TYPE), r.getNativeType(JSTypeNative.NO_OBJECT_TYPE), r.getNativeType(JSTypeNative.NO_TYPE), r.getNativeType(JSTypeNative.NULL_TYPE), r.getNativeType(JSTypeNative.VOID_TYPE), r.getNativeType(JSTypeNative.FUNCTION_FUNCTION_TYPE), r.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), r.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), r.getNativeType(JSTypeNative.GLOBAL_THIS), r.getNativeType(JSTypeNative.OBJECT_TYPE), r.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), r.getNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE), r.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), r.getNativeType(JSTypeNative.UNKNOWN_TYPE)); for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA); addInvalidatingType(mis.typeB); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt); } } invalidatingTypes.add(type); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.isInstanceType()) { invalidatingTypes.add(objType.getImplicitPrototype()); } } Map getRenamingMap() { return renamingMap; } /** Returns an integer that uniquely identifies a JSType. */ private int getIntForType(JSType type) { if (intForType.containsKey(type)) { return intForType.get(type).intValue(); } int newInt = intForType.size() + 1; intForType.put(type, newInt); return newInt; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, new ProcessExterns()); NodeTraversal.traverse(compiler, root, new ProcessProperties()); Set reservedNames = new HashSet(externedNames.size() + quotedNames.size()); reservedNames.addAll(externedNames); reservedNames.addAll(quotedNames); int numRenamedPropertyNames = 0; int numSkippedPropertyNames = 0; Set propsByFreq = new TreeSet(FREQUENCY_COMPARATOR); for (Property p : propertyMap.values()) { if (!p.skipAmbiguating) { ++numRenamedPropertyNames; propsByFreq.add(p); } else { ++numSkippedPropertyNames; reservedNames.add(p.oldName); } } PropertyGraph graph = new PropertyGraph(Lists.newLinkedList(propsByFreq)); GraphColoring coloring = new GreedyGraphColoring(graph, FREQUENCY_COMPARATOR); int numNewPropertyNames = coloring.color(); NameGenerator nameGen = new NameGenerator( reservedNames, "", reservedCharacters); Map colorMap = Maps.newHashMap(); for (int i = 0; i < numNewPropertyNames; ++i) { colorMap.put(i, nameGen.generateNextName()); } for (GraphNode node : graph.getNodes()) { node.getValue().newName = colorMap.get(node.getAnnotation().hashCode()); renamingMap.put(node.getValue().oldName, node.getValue().newName); } // Update the string nodes. for (Node n : stringNodesToRename) { String oldName = n.getString(); Property p = propertyMap.get(oldName); if (p != null && p.newName != null) { Preconditions.checkState(oldName.equals(p.oldName)); if (!p.newName.equals(oldName)) { n.setString(p.newName); compiler.reportCodeChange(); } } } logger.fine("Collapsed " + numRenamedPropertyNames + " properties into " + numNewPropertyNames + " and skipped renaming " + numSkippedPropertyNames + " properties."); } private BitSet getRelatedTypesOnNonUnion(JSType type) { // All of the types we encounter should have been added to the // relatedBitsets via computeRelatedTypes. if (relatedBitsets.containsKey(type)) { return relatedBitsets.get(type); } else { throw new RuntimeException("Related types should have been computed for" + " type: " + type + " but have not been."); } } /** * Adds subtypes - and implementors, in the case of interfaces - of the type * to its JSTypeBitSet of related types. Union types are decomposed into their * alternative types. * *

      The 'is related to' relationship is best understood graphically. Draw an * arrow from each instance type to the prototype of each of its * subclass. Draw an arrow from each prototype to its instance type. Draw an * arrow from each interface to its implementors. A type is related to another * if there is a directed path in the graph from the type to other. Thus, the * 'is related to' relationship is reflexive and transitive. * *

      Example with Foo extends Bar which extends Baz and Bar implements I: *

         * Foo -> Bar.prototype -> Bar -> Baz.prototype -> Baz
         *                          ^
         *                          |
         *                          I
         * 
      * *

      Note that we don't need to correctly handle the relationships between * functions, because the function type is invalidating (i.e. its properties * won't be ambiguated). */ private void computeRelatedTypes(JSType type) { if (type.isUnionType()) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { computeRelatedTypes(alt); } return; } } if (relatedBitsets.containsKey(type)) { // We only need to generate the bit set once. return; } JSTypeBitSet related = new JSTypeBitSet(intForType.size()); relatedBitsets.put(type, related); related.set(getIntForType(type)); // A prototype is related to its instance. if (type.isFunctionPrototypeType()) { addRelatedInstance(((ObjectType) type).getOwnerFunction(), related); return; } // An instance is related to its subclasses. FunctionType constructor = type.toObjectType().getConstructor(); if (constructor != null && constructor.getSubTypes() != null) { for (FunctionType subType : constructor.getSubTypes()) { addRelatedInstance(subType, related); } } // An interface is related to its implementors. for (FunctionType implementor : compiler.getTypeRegistry() .getDirectImplementors(type.toObjectType())) { addRelatedInstance(implementor, related); } } /** * Adds the instance of the given constructor, its implicit prototype and all * its related types to the given bit set. */ private void addRelatedInstance( FunctionType constructor, JSTypeBitSet related) { // TODO(user): A constructor which doesn't have an instance type // (e.g. it's missing the @constructor annotation) should be an invalidating // type which doesn't reach this code path. if (constructor.hasInstanceType()) { ObjectType instanceType = constructor.getInstanceType(); related.set(getIntForType(instanceType.getImplicitPrototype())); computeRelatedTypes(instanceType); related.or(relatedBitsets.get(instanceType)); } } class PropertyGraph implements AdjacencyGraph { protected final Map nodes = Maps.newHashMap(); PropertyGraph(Collection props) { for (Property prop : props) { nodes.put(prop, new PropertyGraphNode(prop)); } } @Override public List> getNodes() { return Lists.>newArrayList(nodes.values()); } @Override public GraphNode getNode(Property property) { return nodes.get(property); } @Override public SubGraph newSubGraph() { return new PropertySubGraph(); } @Override public void clearNodeAnnotations() { for (PropertyGraphNode node : nodes.values()) { node.setAnnotation(null); } } @Override public int getWeight(Property value) { return value.numOccurrences; } } /** * A {@link SubGraph} that represents properties. The related types of * the properties are used to efficiently calculate adjacency information. */ class PropertySubGraph implements SubGraph { /** Types related to properties referenced in this subgraph. */ JSTypeBitSet relatedTypes = new JSTypeBitSet(intForType.size()); /** * Returns true if prop is in an independent set from all properties in this * sub graph. That is, if none of its related types intersects with the * related types for this sub graph. */ @Override public boolean isIndependentOf(Property prop) { return !relatedTypes.intersects(prop.relatedTypes); } /** * Adds the node to the sub graph, adding all its related types to the * related types for the sub graph. */ @Override public void addNode(Property prop) { relatedTypes.or(prop.relatedTypes); } } class PropertyGraphNode implements GraphNode { Property property; protected Annotation annotation; PropertyGraphNode(Property property) { this.property = property; } @Override public Property getValue() { return property; } @Override @SuppressWarnings("unchecked") public A getAnnotation() { return (A) annotation; } @Override public void setAnnotation(Annotation data) { annotation = data; } } /** A traversal callback that collects externed property names. */ private class ProcessExterns extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: Node dest = n.getFirstChild().getNext(); externedNames.add(dest.getString()); break; case Token.OBJECTLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // names: STRING, GET, SET externedNames.add(child.getString()); } break; } } } /** Finds all property references, recording the types on which they occur. */ private class ProcessProperties extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: { Node propNode = n.getFirstChild().getNext(); JSType jstype = getJSType(n.getFirstChild()); maybeMarkCandidate(propNode, jstype, t); break; } case Token.OBJECTLIT: // The children of an OBJECTLIT node are keys, where the values // are the children of the keys. for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { // We only want keys that were unquoted. // Keys are STRING, GET, SET if (!key.isQuotedString()) { JSType jstype = getJSType(n.getFirstChild()); maybeMarkCandidate(key, jstype, t); } else { // Ensure that we never rename some other property in a way // that could conflict with this quoted key. quotedNames.add(key.getString()); } } break; case Token.GETELEM: // If this is a quoted property access (e.g. x['myprop']), we need to // ensure that we never rename some other property in a way that // could conflict with this quoted name. Node child = n.getLastChild(); if (child.isString()) { quotedNames.add(child.getString()); } break; } } /** * If a property node is eligible for renaming, stashes a reference to it * and increments the property name's access count. * * @param n The STRING node for a property * @param t The traversal */ private void maybeMarkCandidate(Node n, JSType type, NodeTraversal t) { String name = n.getString(); if (!externedNames.contains(name)) { stringNodesToRename.add(n); recordProperty(name, type); } } private Property recordProperty(String name, JSType type) { Property prop = getProperty(name); prop.addType(type); return prop; } } /** Returns true if properties on this type should not be renamed. */ private boolean isInvalidatingType(JSType type) { if (type.isUnionType()) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { if (isInvalidatingType(alt)) { return true; } } return false; } } ObjectType objType = ObjectType.cast(type); return objType == null || invalidatingTypes.contains(objType) || !objType.hasReferenceName() || objType.isUnknownType() || objType.isEmptyType() /* unresolved types */ || objType.isEnumType() || objType.autoboxesTo() != null; } private Property getProperty(String name) { Property prop = propertyMap.get(name); if (prop == null) { prop = new Property(name); propertyMap.put(name, prop); } return prop; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return compiler.getTypeRegistry().getNativeType( JSTypeNative.UNKNOWN_TYPE); } else { return jsType; } } /** Encapsulates the information needed for renaming a property. */ private class Property { final String oldName; String newName; int numOccurrences; boolean skipAmbiguating; JSTypeBitSet relatedTypes = new JSTypeBitSet(intForType.size()); Property(String name) { this.oldName = name; // Properties with this suffix are handled in RenameProperties. if (name.startsWith(SKIP_PREFIX)) { skipAmbiguating = true; } } /** Add this type to this property, calculating */ void addType(JSType newType) { if (skipAmbiguating) { return; } ++numOccurrences; if (newType.isUnionType()) { newType = newType.restrictByNotNullOrUndefined(); if (newType.isUnionType()) { for (JSType alt : newType.toMaybeUnionType().getAlternates()) { addNonUnionType(alt); } return; } } addNonUnionType(newType); } private void addNonUnionType(JSType newType) { if (skipAmbiguating || isInvalidatingType(newType)) { skipAmbiguating = true; return; } if (!relatedTypes.get(getIntForType(newType))) { computeRelatedTypes(newType); relatedTypes.or(getRelatedTypesOnNonUnion(newType)); } } } // A BitSet that stores type info. Adds pretty-print routines. private class JSTypeBitSet extends BitSet { private static final long serialVersionUID = 1L; private JSTypeBitSet(int size) { super(size); } private JSTypeBitSet() { super(); } /** * Pretty-printing, for diagnostic purposes. */ @Override public String toString() { int from = 0; int current = 0; List types = Lists.newArrayList(); while (-1 != (current = nextSetBit(from))) { types.add(intForType.inverse().get(current).toString()); from = current + 1; } return Joiner.on(" && ").join(types); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/BasicErrorManager.java0000644000175000017500000001243312115204405027030 0ustar apoapo/* * Copyright 2007 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.Sets; import com.google.javascript.jscomp.CheckLevel; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.SortedSet; /** *

      A basic error manager that sorts all errors and warnings reported to it to * generate a sorted report when the {@link #generateReport()} method * is called.

      * *

      This error manager does not produce any output, but subclasses can * override the {@link #println(CheckLevel, JSError)} method to generate custom * output.

      * */ public abstract class BasicErrorManager implements ErrorManager { private final SortedSet messages = Sets.newTreeSet(new LeveledJSErrorComparator()); private int errorCount = 0; private int warningCount = 0; private double typedPercent = 0.0; @Override public void report(CheckLevel level, JSError error) { if (messages.add(new ErrorWithLevel(error, level))) { if (level == CheckLevel.ERROR) { errorCount++; } else if (level == CheckLevel.WARNING) { warningCount++; } } } @Override public void generateReport() { for (ErrorWithLevel message : messages) { println(message.level, message.error); } printSummary(); } /** * Print a message with a trailing new line. This method is called by the * {@link #generateReport()} method when generating messages. */ public abstract void println(CheckLevel level, JSError error); /** * Print the summary of the compilation - number of errors and warnings. */ protected abstract void printSummary(); @Override public int getErrorCount() { return errorCount; } @Override public int getWarningCount() { return warningCount; } @Override public JSError[] getErrors() { return toArray(CheckLevel.ERROR); } @Override public JSError[] getWarnings() { return toArray(CheckLevel.WARNING); } @Override public void setTypedPercent(double typedPercent) { this.typedPercent = typedPercent; } @Override public double getTypedPercent() { return typedPercent; } private JSError[] toArray(CheckLevel level) { List errors = new ArrayList(messages.size()); for (ErrorWithLevel p : messages) { if (p.level == level) { errors.add(p.error); } } return errors.toArray(new JSError[errors.size()]); } /** *

      Comparator of {@link JSError} with an associated {@link CheckLevel}. * The ordering is the standard lexical ordering on the quintuple * (file name, line number, {@link CheckLevel}, * character number, description).

      * *

      Note: this comparator imposes orderings that are inconsistent with * {@link JSError#equals(Object)}.

      */ static final class LeveledJSErrorComparator implements Comparator { private static final int P1_LT_P2 = -1; private static final int P1_GT_P2 = 1; @Override public int compare(ErrorWithLevel p1, ErrorWithLevel p2) { // null is the smallest value if (p2 == null) { if (p1 == null) { return 0; } else { return P1_GT_P2; } } // check level if (p1.level != p2.level) { return p2.level.compareTo(p1.level); } // sourceName comparison String source1 = p1.error.sourceName; String source2 = p2.error.sourceName; if (source1 != null && source2 != null) { int sourceCompare = source1.compareTo(source2); if (sourceCompare != 0) { return sourceCompare; } } else if (source1 == null && source2 != null) { return P1_LT_P2; } else if (source1 != null && source2 == null) { return P1_GT_P2; } // lineno comparison int lineno1 = p1.error.lineNumber; int lineno2 = p2.error.lineNumber; if (lineno1 != lineno2) { return lineno1 - lineno2; } else if (lineno1 < 0 && 0 <= lineno2) { return P1_LT_P2; } else if (0 <= lineno1 && lineno2 < 0) { return P1_GT_P2; } // charno comparison int charno1 = p1.error.getCharno(); int charno2 = p2.error.getCharno(); if (charno1 != charno2) { return charno1 - charno2; } else if (charno1 < 0 && 0 <= charno2) { return P1_LT_P2; } else if (0 <= charno1 && charno2 < 0) { return P1_GT_P2; } // description return p1.error.description.compareTo(p2.error.description); } } static class ErrorWithLevel { final JSError error; final CheckLevel level; ErrorWithLevel(JSError error, CheckLevel level) { this.error = error; this.level = level; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CommandLineRunner.java0000644000175000017500000010703512115204405027065 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.common.io.LimitInputStream; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.OptionDef; import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Parameters; import org.kohsuke.args4j.spi.Setter; import org.kohsuke.args4j.spi.StringOptionHandler; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.nio.charset.Charset; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; /** * CommandLineRunner translates flags into Java API calls on the Compiler. * * This class may be extended and used to create other Java classes * that behave the same as running the Compiler from the command line. If you * want to run the compiler in-process in Java, you should look at this class * for hints on what API calls to make, but you should not use this class * directly. * * Example: *
       * class MyCommandLineRunner extends CommandLineRunner {
       *   MyCommandLineRunner(String[] args) {
       *     super(args);
       *   }
       *
       *   {@code @Override} protected CompilerOptions createOptions() {
       *     CompilerOptions options = super.createOptions();
       *     addMyCrazyCompilerPassThatOutputsAnExtraFile(options);
       *     return options;
       *   }
       *
       *   public static void main(String[] args) {
       *     MyCommandLineRunner runner = new MyCommandLineRunner(args);
       *     if (runner.shouldRunCompiler()) {
       *       runner.run();
       *     } else {
       *       System.exit(-1);
       *     }
       *   }
       * }
       * 
      * * This class is totally not thread-safe. * * @author bolinfest@google.com (Michael Bolin) */ public class CommandLineRunner extends AbstractCommandLineRunner { private static class GuardLevel { final String name; final CheckLevel level; GuardLevel(String name, CheckLevel level) { this.name = name; this.level = level; } } // I don't really care about unchecked warnings in this class. @SuppressWarnings("unchecked") private static class Flags { private static List guardLevels = Lists.newArrayList(); @Option(name = "--help", handler = BooleanOptionHandler.class, usage = "Displays this message") private boolean display_help = false; @Option(name = "--print_tree", handler = BooleanOptionHandler.class, usage = "Prints out the parse tree and exits") private boolean print_tree = false; @Option(name = "--print_ast", handler = BooleanOptionHandler.class, usage = "Prints a dot file describing the internal abstract syntax" + " tree and exits") private boolean print_ast = false; @Option(name = "--print_pass_graph", handler = BooleanOptionHandler.class, usage = "Prints a dot file describing the passes that will get run" + " and exits") private boolean print_pass_graph = false; // Turn on (very slow) extra sanity checks for use when modifying the // compiler. @Option(name = "--jscomp_dev_mode", // hidden, no usage aliases = {"--dev_mode"}) private CompilerOptions.DevMode jscomp_dev_mode = CompilerOptions.DevMode.OFF; @Option(name = "--logging_level", usage = "The logging level (standard java.util.logging.Level" + " values) for Compiler progress. Does not control errors or" + " warnings for the JavaScript code under compilation") private String logging_level = Level.WARNING.getName(); @Option(name = "--externs", usage = "The file containing JavaScript externs. You may specify" + " multiple") private List externs = Lists.newArrayList(); @Option(name = "--js", usage = "The JavaScript filename. You may specify multiple") private List js = Lists.newArrayList(); @Option(name = "--js_output_file", usage = "Primary output filename. If not specified, output is " + "written to stdout") private String js_output_file = ""; @Option(name = "--module", usage = "A JavaScript module specification. The format is " + ":[:[,...][:]]]. Module names must be " + "unique. Each dep is the name of a module that this module " + "depends on. Modules must be listed in dependency order, and JS " + "source files must be listed in the corresponding order. Where " + "--module flags occur in relation to --js flags is unimportant. " + "Provide the value 'auto' to trigger module creation from CommonJS" + "modules.") private List module = Lists.newArrayList(); @Option(name = "--variable_map_input_file", usage = "File containing the serialized version of the variable " + "renaming map produced by a previous compilation") private String variable_map_input_file = ""; @Option(name = "--property_map_input_file", usage = "File containing the serialized version of the property " + "renaming map produced by a previous compilation") private String property_map_input_file = ""; @Option(name = "--variable_map_output_file", usage = "File where the serialized version of the variable " + "renaming map produced should be saved") private String variable_map_output_file = ""; @Option(name = "--create_name_map_files", handler = BooleanOptionHandler.class, usage = "If true, variable renaming and property renaming map " + "files will be produced as {binary name}_vars_map.out and " + "{binary name}_props_map.out. Note that this flag cannot be used " + "in conjunction with either variable_map_output_file or " + "property_map_output_file") private boolean create_name_map_files = false; @Option(name = "--property_map_output_file", usage = "File where the serialized version of the property " + "renaming map produced should be saved") private String property_map_output_file = ""; @Option(name = "--third_party", handler = BooleanOptionHandler.class, usage = "Check source validity but do not enforce Closure style " + "rules and conventions") private boolean third_party = false; @Option(name = "--summary_detail_level", usage = "Controls how detailed the compilation summary is. Values:" + " 0 (never print summary), 1 (print summary only if there are " + "errors or warnings), 2 (print summary if the 'checkTypes' " + "diagnostic group is enabled, see --jscomp_warning), " + "3 (always print summary). The default level is 1") private int summary_detail_level = 1; @Option(name = "--output_wrapper", usage = "Interpolate output into this string at the place denoted" + " by the marker token %output%. Use marker token %output|jsstring%" + " to do js string escaping on the output.") private String output_wrapper = ""; @Option(name = "--module_wrapper", usage = "An output wrapper for a JavaScript module (optional). " + "The format is :. The module name must correspond " + "with a module specified using --module. The wrapper must " + "contain %s as the code placeholder. The %basename% placeholder can " + "also be used to substitute the base name of the module output file.") private List module_wrapper = Lists.newArrayList(); @Option(name = "--module_output_path_prefix", usage = "Prefix for filenames of compiled JS modules. " + ".js will be appended to this prefix. Directories " + "will be created as needed. Use with --module") private String module_output_path_prefix = "./"; @Option(name = "--create_source_map", usage = "If specified, a source map file mapping the generated " + "source files back to the original source file will be " + "output to the specified path. The %outname% placeholder will " + "expand to the name of the output file that the source map " + "corresponds to.") private String create_source_map = ""; @Option(name = "--source_map_format", usage = "The source map format to produce. " + "Options: V1, V2, V3, DEFAULT. DEFAULT produces V2.") private SourceMap.Format source_map_format = SourceMap.Format.DEFAULT; // Used to define the flag, values are stored by the handler. @SuppressWarnings("unused") @Option(name = "--jscomp_error", handler = WarningGuardErrorOptionHandler.class, usage = "Make the named class of warnings an error. Options:" + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) private List jscomp_error = Lists.newArrayList(); // Used to define the flag, values are stored by the handler. @SuppressWarnings("unused") @Option(name = "--jscomp_warning", handler = WarningGuardWarningOptionHandler.class, usage = "Make the named class of warnings a normal warning. " + "Options:" + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) private List jscomp_warning = Lists.newArrayList(); // Used to define the flag, values are stored by the handler. @SuppressWarnings("unused") @Option(name = "--jscomp_off", handler = WarningGuardOffOptionHandler.class, usage = "Turn off the named class of warnings. Options:" + DiagnosticGroups.DIAGNOSTIC_GROUP_NAMES) private List jscomp_off = Lists.newArrayList(); @Option(name = "--define", aliases = {"--D", "-D"}, usage = "Override the value of a variable annotated @define. " + "The format is [=], where is the name of a @define " + "variable and is a boolean, number, or a single-quoted string " + "that contains no single quotes. If [=] is omitted, " + "the variable is marked true") private List define = Lists.newArrayList(); @Option(name = "--charset", usage = "Input and output charset for all files. By default, we " + "accept UTF-8 as input and output US_ASCII") private String charset = ""; @Option(name = "--compilation_level", usage = "Specifies the compilation level to use. Options: " + "WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS") private CompilationLevel compilation_level = CompilationLevel.SIMPLE_OPTIMIZATIONS; @Option(name = "--use_types_for_optimization", usage = "Experimental: perform additional optimizations " + "based on available information. Inaccurate type annotations " + "may result in incorrect results.") private boolean use_types_for_optimization = false; @Option(name = "--warning_level", usage = "Specifies the warning level to use. Options: " + "QUIET, DEFAULT, VERBOSE") private WarningLevel warning_level = WarningLevel.DEFAULT; @Option(name = "--use_only_custom_externs", handler = BooleanOptionHandler.class, usage = "Specifies whether the default externs should be excluded") private boolean use_only_custom_externs = false; @Option(name = "--debug", handler = BooleanOptionHandler.class, usage = "Enable debugging options") private boolean debug = false; @Option(name = "--generate_exports", handler = BooleanOptionHandler.class, usage = "Generates export code for those marked with @export") private boolean generate_exports = false; @Option(name = "--formatting", usage = "Specifies which formatting options, if any, should be " + "applied to the output JS. Options: " + "PRETTY_PRINT, PRINT_INPUT_DELIMITER, SINGLE_QUOTES") private List formatting = Lists.newArrayList(); @Option(name = "--process_common_js_modules", usage = "Process CommonJS modules to a concatenable form.") private boolean process_common_js_modules = false; @Option(name = "--common_js_module_path_prefix", usage = "Path prefix to be removed from CommonJS module names.") private String common_js_path_prefix = ProcessCommonJSModules.DEFAULT_FILENAME_PREFIX; @Option(name = "--common_js_entry_module", usage = "Root of your common JS dependency hierarchy. "+ "Your main script.") private String common_js_entry_module; @Option(name = "--transform_amd_modules", usage = "Transform AMD to CommonJS modules.") private boolean transform_amd_modules = false; @Option(name = "--process_closure_primitives", handler = BooleanOptionHandler.class, usage = "Processes built-ins from the Closure library, such as " + "goog.require(), goog.provide(), and goog.exportSymbol()") private boolean process_closure_primitives = true; @Option(name = "--manage_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Automatically sort dependencies so that a file that " + "goog.provides symbol X will always come before a file that " + "goog.requires symbol X. If an input provides symbols, and " + "those symbols are never required, then that input will not " + "be included in the compilation.") private boolean manage_closure_dependencies = false; @Option(name = "--only_closure_dependencies", handler = BooleanOptionHandler.class, usage = "Only include files in the transitive dependency of the " + "entry points (specified by closure_entry_point). Files that do " + "not provide dependencies will be removed. This supersedes" + "manage_closure_dependencies") private boolean only_closure_dependencies = false; @Option(name = "--closure_entry_point", usage = "Entry points to the program. Must be goog.provide'd " + "symbols. Any goog.provide'd symbols that are not a transitive " + "dependency of the entry points will be removed. Files without " + "goog.provides, and their dependencies, will always be left in. " + "If any entry points are specified, then the " + "manage_closure_dependencies option will be set to true and " + "all files will be sorted in dependency order.") private List closure_entry_point = Lists.newArrayList(); @Option(name = "--process_jquery_primitives", handler = BooleanOptionHandler.class, usage = "Processes built-ins from the Jquery library, such as " + "jQuery.fn and jQuery.extend()") private boolean process_jquery_primitives = false; @Option(name = "--angular_pass", handler = BooleanOptionHandler.class, usage = "Generate $inject properties for AngularJS for functions " + "annotated with @ngInject") private boolean angular_pass = false; @Option(name = "--output_manifest", usage = "Prints out a list of all the files in the compilation. " + "If --manage_closure_dependencies is on, this will not include " + "files that got dropped because they were not required. " + "The %outname% placeholder expands to the JS output file. " + "If you're using modularization, using %outname% will create " + "a manifest for each module.") private String output_manifest = ""; @Option(name = "--output_module_dependencies", usage = "Prints out a JSON file of dependencies between modules.") private String output_module_dependencies = ""; @Option(name = "--accept_const_keyword", usage = "Allows usage of const keyword.") private boolean accept_const_keyword = false; @Option(name = "--language_in", usage = "Sets what language spec that input sources conform. " + "Options: ECMASCRIPT3 (default), ECMASCRIPT5, ECMASCRIPT5_STRICT") private String language_in = "ECMASCRIPT3"; @Option(name = "--version", handler = BooleanOptionHandler.class, usage = "Prints the compiler version to stderr.") private boolean version = false; @Option(name = "--translations_file", usage = "Source of translated messages. Currently only supports XTB.") private String translationsFile = ""; @Option(name = "--translations_project", usage = "Scopes all translations to the specified project." + "When specified, we will use different message ids so that messages " + "in different projects can have different translations.") private String translationsProject = null; @Option(name = "--flagfile", usage = "A file containing additional command-line options.") private String flag_file = ""; @Option(name = "--warnings_whitelist_file", usage = "A file containing warnings to suppress. Each line should be " + "of the form\n" + ":? ") private String warnings_whitelist_file = ""; @Option(name = "--extra_annotation_name", usage = "A whitelist of tag names in JSDoc. You may specify multiple") private List extra_annotation_name = Lists.newArrayList(); @Argument private List arguments = Lists.newArrayList(); /** * Users may specify JS inputs via the legacy {@code --js} option, as well * as via additional arguments to the Closure Compiler. For example, it is * convenient to leverage the additional arguments feature when using the * Closure Compiler in combination with {@code find} and {@code xargs}: *
           * find MY_JS_SRC_DIR -name '*.js' \
           *     | xargs java -jar compiler.jar --manage_closure_dependencies
           * 
      * The {@code find} command will produce a list of '*.js' source files in * the {@code MY_JS_SRC_DIR} directory while {@code xargs} will convert them * to a single, space-delimited set of arguments that are appended to the * {@code java} command to run the Compiler. *

      * Note that it is important to use the * {@code --manage_closure_dependencies} option in this case because the * order produced by {@code find} is unlikely to be sorted correctly with * respect to {@code goog.provide()} and {@code goog.requires()}. */ List getJsFiles() { List allJsInputs = Lists.newArrayListWithCapacity( js.size() + arguments.size()); allJsInputs.addAll(js); allJsInputs.addAll(arguments); return allJsInputs; } // Our own option parser to be backwards-compatible. // It needs to be public because of the crazy reflection that args4j does. public static class BooleanOptionHandler extends OptionHandler { private static final Set TRUES = Sets.newHashSet("true", "on", "yes", "1"); private static final Set FALSES = Sets.newHashSet("false", "off", "no", "0"); public BooleanOptionHandler( CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, setter); } @Override public int parseArguments(Parameters params) throws CmdLineException { String param = null; try { param = params.getParameter(0); } catch (CmdLineException e) {} if (param == null) { setter.addValue(true); return 0; } else { String lowerParam = param.toLowerCase(); if (TRUES.contains(lowerParam)) { setter.addValue(true); } else if (FALSES.contains(lowerParam)) { setter.addValue(false); } else { setter.addValue(true); return 0; } return 1; } } @Override public String getDefaultMetaVariable() { return null; } } // Our own parser for warning guards that preserves the original order // of the flags. public static class WarningGuardErrorOptionHandler extends StringOptionHandler { public WarningGuardErrorOptionHandler( CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, new WarningGuardSetter(setter, CheckLevel.ERROR)); } } public static class WarningGuardWarningOptionHandler extends StringOptionHandler { public WarningGuardWarningOptionHandler( CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, new WarningGuardSetter(setter, CheckLevel.WARNING)); } } public static class WarningGuardOffOptionHandler extends StringOptionHandler { public WarningGuardOffOptionHandler( CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, new WarningGuardSetter(setter, CheckLevel.OFF)); } } private static class WarningGuardSetter implements Setter { private final Setter proxy; private final CheckLevel level; private WarningGuardSetter( Setter proxy, CheckLevel level) { this.proxy = proxy; this.level = level; } @Override public boolean isMultiValued() { return proxy.isMultiValued(); } @Override public Class getType() { return (Class) proxy.getType(); } @Override public void addValue(String value) throws CmdLineException { proxy.addValue(value); guardLevels.add(new GuardLevel(value, level)); } } public static WarningGuardSpec getWarningGuardSpec() { WarningGuardSpec spec = new WarningGuardSpec(); for (GuardLevel guardLevel : guardLevels) { spec.add(guardLevel.level, guardLevel.name); } return spec; } } /** * Set of options that can be used with the --formatting flag. */ private static enum FormattingOption { PRETTY_PRINT, PRINT_INPUT_DELIMITER, SINGLE_QUOTES ; private void applyToOptions(CompilerOptions options) { switch (this) { case PRETTY_PRINT: options.prettyPrint = true; break; case PRINT_INPUT_DELIMITER: options.printInputDelimiter = true; break; case SINGLE_QUOTES: options.setPreferSingleQuotes(true); break; default: throw new RuntimeException("Unknown formatting option: " + this); } } } private final Flags flags = new Flags(); private boolean isConfigValid = false; /** * Create a new command-line runner. You should only need to call * the constructor if you're extending this class. Otherwise, the main * method should instantiate it. */ protected CommandLineRunner(String[] args) { super(); initConfigFromFlags(args, System.err); } protected CommandLineRunner(String[] args, PrintStream out, PrintStream err) { super(out, err); initConfigFromFlags(args, err); } /** * Split strings into tokens delimited by whitespace, but treat quoted * strings as single tokens. Non-whitespace characters adjacent to quoted * strings will be returned as part of the token. For example, the string * {@code "--js='/home/my project/app.js'"} would be returned as a single * token. * * @param lines strings to tokenize * @return a list of tokens */ private List tokenizeKeepingQuotedStrings(List lines) { List tokens = Lists.newArrayList(); Pattern tokenPattern = Pattern.compile("(?:[^ \t\f\\x0B'\"]|(?:'[^']*'|\"[^\"]*\"))+"); for (String line : lines) { Matcher matcher = tokenPattern.matcher(line); while (matcher.find()) { tokens.add(matcher.group(0)); } } return tokens; } private List processArgs(String[] args) { // Args4j has a different format that the old command-line parser. // So we use some voodoo to get the args into the format that args4j // expects. Pattern argPattern = Pattern.compile("(--[a-zA-Z_]+)=(.*)"); Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$"); List processedArgs = Lists.newArrayList(); for (String arg : args) { Matcher matcher = argPattern.matcher(arg); if (matcher.matches()) { processedArgs.add(matcher.group(1)); String value = matcher.group(2); Matcher quotesMatcher = quotesPattern.matcher(value); if (quotesMatcher.matches()) { processedArgs.add(quotesMatcher.group(1)); } else { processedArgs.add(value); } } else { processedArgs.add(arg); } } return processedArgs; } private void processFlagFile(PrintStream err) throws CmdLineException, IOException { File flagFileInput = new File(flags.flag_file); List argsInFile = tokenizeKeepingQuotedStrings( Files.readLines(flagFileInput, Charset.defaultCharset())); flags.flag_file = ""; List processedFileArgs = processArgs(argsInFile.toArray(new String[] {})); CmdLineParser parserFileArgs = new CmdLineParser(flags); // Command-line warning levels should override flag file settings, // which means they should go last. List previous = Lists.newArrayList(Flags.guardLevels); Flags.guardLevels.clear(); parserFileArgs.parseArgument(processedFileArgs.toArray(new String[] {})); Flags.guardLevels.addAll(previous); // Currently we are not supporting this (prevent direct/indirect loops) if (!flags.flag_file.equals("")) { err.println("ERROR - Arguments in the file cannot contain " + "--flagfile option."); isConfigValid = false; } } private void initConfigFromFlags(String[] args, PrintStream err) { List processedArgs = processArgs(args); CmdLineParser parser = new CmdLineParser(flags); Flags.guardLevels.clear(); isConfigValid = true; try { parser.parseArgument(processedArgs.toArray(new String[] {})); // For contains --flagfile flag if (!flags.flag_file.equals("")) { processFlagFile(err); } } catch (CmdLineException e) { err.println(e.getMessage()); isConfigValid = false; } catch (IOException ioErr) { err.println("ERROR - " + flags.flag_file + " read error."); isConfigValid = false; } if (flags.version) { err.println( "Closure Compiler (http://code.google.com/closure/compiler)\n" + "Version: " + Compiler.getReleaseVersion() + "\n" + "Built on: " + Compiler.getReleaseDate()); err.flush(); } if (flags.process_common_js_modules) { flags.process_closure_primitives = true; flags.manage_closure_dependencies = true; if (flags.common_js_entry_module == null) { err.println("Please specify --common_js_entry_module."); err.flush(); isConfigValid = false; } flags.closure_entry_point = Lists.newArrayList( ProcessCommonJSModules.toModuleName(flags.common_js_entry_module)); } if (!isConfigValid || flags.display_help) { isConfigValid = false; parser.printUsage(err); } else { CodingConvention conv; if (flags.third_party) { conv = CodingConventions.getDefault(); } else if (flags.process_jquery_primitives) { conv = new JqueryCodingConvention(); } else { conv = new ClosureCodingConvention(); } getCommandLineConfig() .setPrintTree(flags.print_tree) .setPrintAst(flags.print_ast) .setPrintPassGraph(flags.print_pass_graph) .setJscompDevMode(flags.jscomp_dev_mode) .setLoggingLevel(flags.logging_level) .setExterns(flags.externs) .setJs(flags.getJsFiles()) .setJsOutputFile(flags.js_output_file) .setModule(flags.module) .setVariableMapInputFile(flags.variable_map_input_file) .setPropertyMapInputFile(flags.property_map_input_file) .setVariableMapOutputFile(flags.variable_map_output_file) .setCreateNameMapFiles(flags.create_name_map_files) .setPropertyMapOutputFile(flags.property_map_output_file) .setCodingConvention(conv) .setSummaryDetailLevel(flags.summary_detail_level) .setOutputWrapper(flags.output_wrapper) .setModuleWrapper(flags.module_wrapper) .setModuleOutputPathPrefix(flags.module_output_path_prefix) .setCreateSourceMap(flags.create_source_map) .setSourceMapFormat(flags.source_map_format) .setWarningGuardSpec(Flags.getWarningGuardSpec()) .setDefine(flags.define) .setCharset(flags.charset) .setManageClosureDependencies(flags.manage_closure_dependencies) .setOnlyClosureDependencies(flags.only_closure_dependencies) .setClosureEntryPoints(flags.closure_entry_point) .setOutputManifest(ImmutableList.of(flags.output_manifest)) .setOutputModuleDependencies(flags.output_module_dependencies) .setAcceptConstKeyword(flags.accept_const_keyword) .setLanguageIn(flags.language_in) .setProcessCommonJSModules(flags.process_common_js_modules) .setCommonJSModulePathPrefix(flags.common_js_path_prefix) .setTransformAMDToCJSModules(flags.transform_amd_modules) .setWarningsWhitelistFile(flags.warnings_whitelist_file); } } @Override protected CompilerOptions createOptions() { CompilerOptions options = new CompilerOptions(); if (flags.process_jquery_primitives) { options.setCodingConvention(new JqueryCodingConvention()); } else { options.setCodingConvention(new ClosureCodingConvention()); } options.setExtraAnnotationNames(flags.extra_annotation_name); CompilationLevel level = flags.compilation_level; level.setOptionsForCompilationLevel(options); if (flags.debug) { level.setDebugOptionsForCompilationLevel(options); } if (flags.use_types_for_optimization) { level.setTypeBasedOptimizationOptions(options); } if (flags.generate_exports) { options.setGenerateExports(flags.generate_exports); } WarningLevel wLevel = flags.warning_level; wLevel.setOptionsForWarningLevel(options); for (FormattingOption formattingOption : flags.formatting) { formattingOption.applyToOptions(options); } options.closurePass = flags.process_closure_primitives; options.jqueryPass = flags.process_jquery_primitives && CompilationLevel.ADVANCED_OPTIMIZATIONS == level; options.angularPass = flags.angular_pass; if (!flags.translationsFile.isEmpty()) { try { options.messageBundle = new XtbMessageBundle( new FileInputStream(flags.translationsFile), flags.translationsProject); } catch (IOException e) { throw new RuntimeException("Reading XTB file", e); } } else if (CompilationLevel.ADVANCED_OPTIMIZATIONS == level) { // In SIMPLE or WHITESPACE mode, if the user hasn't specified a // translations file, they might reasonably try to write their own // implementation of goog.getMsg that makes the substitution at // run-time. // // In ADVANCED mode, goog.getMsg is going to be renamed anyway, // so we might as well inline it. options.messageBundle = new EmptyMessageBundle(); } return options; } @Override protected Compiler createCompiler() { return new Compiler(getErrorPrintStream()); } @Override protected List createExterns() throws FlagUsageException, IOException { List externs = super.createExterns(); if (flags.use_only_custom_externs || isInTestMode()) { return externs; } else { List defaultExterns = getDefaultExterns(); defaultExterns.addAll(externs); return defaultExterns; } } // The externs expected in externs.zip, in sorted order. private static final List DEFAULT_EXTERNS_NAMES = ImmutableList.of( // JS externs "es3.js", "es5.js", // Event APIs "w3c_event.js", "w3c_event3.js", "gecko_event.js", "ie_event.js", "webkit_event.js", // DOM apis "w3c_dom1.js", "w3c_dom2.js", "w3c_dom3.js", "gecko_dom.js", "ie_dom.js", "webkit_dom.js", // CSS apis "w3c_css.js", "gecko_css.js", "ie_css.js", "webkit_css.js", // Top-level namespaces "google.js", "deprecated.js", "fileapi.js", "flash.js", "gears_symbols.js", "gears_types.js", "gecko_xml.js", "html5.js", "ie_vml.js", "iphone.js", "webstorage.js", "w3c_anim_timing.js", "w3c_css3d.js", "w3c_elementtraversal.js", "w3c_geolocation.js", "w3c_indexeddb.js", "w3c_navigation_timing.js", "w3c_range.js", "w3c_selectors.js", "w3c_xml.js", "window.js", "webkit_notifications.js", "webgl.js"); /** * @return a mutable list * @throws IOException */ public static List getDefaultExterns() throws IOException { InputStream input = CommandLineRunner.class.getResourceAsStream( "/externs.zip"); if (input == null) { // In some environments, the externs.zip is relative to this class. input = CommandLineRunner.class.getResourceAsStream("externs.zip"); } Preconditions.checkNotNull(input); ZipInputStream zip = new ZipInputStream(input); Map externsMap = Maps.newHashMap(); for (ZipEntry entry = null; (entry = zip.getNextEntry()) != null; ) { BufferedInputStream entryStream = new BufferedInputStream( new LimitInputStream(zip, entry.getSize())); externsMap.put(entry.getName(), SourceFile.fromInputStream( // Give the files an odd prefix, so that they do not conflict // with the user's files. "externs.zip//" + entry.getName(), entryStream)); } Preconditions.checkState( externsMap.keySet().equals(Sets.newHashSet(DEFAULT_EXTERNS_NAMES)), "Externs zip must match our hard-coded list of externs."); // Order matters, so the resources must be added to the result list // in the expected order. List externs = Lists.newArrayList(); for (String key : DEFAULT_EXTERNS_NAMES) { externs.add(externsMap.get(key)); } return externs; } /** * @return Whether the configuration is valid. */ public boolean shouldRunCompiler() { return this.isConfigValid; } /** * Runs the Compiler. Exits cleanly in the event of an error. */ public static void main(String[] args) { CommandLineRunner runner = new CommandLineRunner(args); if (runner.shouldRunCompiler()) { runner.run(); } else { System.exit(-1); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ClosureOptimizePrimitives.java0000644000175000017500000000730712115204405030717 0ustar apoapo/* * Copyright 2011 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.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; /** *

      Compiler pass that converts all calls to: * goog.object.create(key1, val1, key2, val2, ...) where all of the keys * are literals into object literals.

      * * @author agrieve@google.com (Andrew Grieve) */ final class ClosureOptimizePrimitives implements CompilerPass { /** Reference to the JS compiler */ private final AbstractCompiler compiler; /** * Identifies all calls to goog.object.create. */ private class FindObjectCreateCalls extends AbstractPostOrderCallback { List callNodes = Lists.newArrayList(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { String fnName = n.getFirstChild().getQualifiedName(); if ("goog$object$create".equals(fnName) || "goog.object.create".equals(fnName)) { callNodes.add(n); } } } } /** * @param compiler The AbstractCompiler */ ClosureOptimizePrimitives(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { FindObjectCreateCalls pass = new FindObjectCreateCalls(); NodeTraversal.traverse(compiler, root, pass); processObjectCreateCalls(pass.callNodes); } /** * Converts all of the given call nodes to object literals that are safe to * do so. */ private void processObjectCreateCalls(List callNodes) { for (Node callNode : callNodes) { Node curParam = callNode.getFirstChild().getNext(); if (canOptimizeObjectCreate(curParam)) { Node objNode = IR.objectlit().srcref(callNode); while (curParam != null) { Node keyNode = curParam; Node valueNode = curParam.getNext(); curParam = valueNode.getNext(); callNode.removeChild(keyNode); callNode.removeChild(valueNode); if (!keyNode.isString()) { keyNode = IR.string(NodeUtil.getStringValue(keyNode)) .srcref(keyNode); } keyNode.setType(Token.STRING_KEY); keyNode.setQuotedString(); objNode.addChildToBack(IR.propdef(keyNode, valueNode)); } callNode.getParent().replaceChild(callNode, objNode); compiler.reportCodeChange(); } } } /** * Returns whether the given call to goog.object.create can be converted to an * object literal. */ private boolean canOptimizeObjectCreate(Node firstParam) { Node curParam = firstParam; while (curParam != null) { // All keys must be strings or numbers. if (!curParam.isString() && !curParam.isNumber()) { return false; } curParam = curParam.getNext(); // Check for an odd number of parameters. if (curParam == null) { return false; } curParam = curParam.getNext(); } return true; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Denormalize.java0000644000175000017500000001127012115204405025751 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * The goal with this pass is to reverse the simplifications done in the * normalization pass that are not handled by other passes (such as * CollapseVariableDeclarations) to avoid making the resulting code larger. * * Currently this pass only does one thing pushing statements into for-loop * initializer. This: * var a = 0; for(;a<0;a++) {} * becomes: * for(var a = 0;a<0;a++) {} * * @author johnlenz@google.com (johnlenz) */ class Denormalize implements CompilerPass, Callback { private final AbstractCompiler compiler; Denormalize(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { maybeCollapseIntoForStatements(n, parent); } /** * Collapse VARs and EXPR_RESULT node into FOR loop initializers where * possible. */ private void maybeCollapseIntoForStatements(Node n, Node parent) { // Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into. // LABELs are not supported here. if (parent == null || !NodeUtil.isStatementBlock(parent)) { return; } // Is the current node something that can be in a for loop initializer? if (!n.isExprResult() && !n.isVar()) { return; } // Is the next statement a valid FOR? Node nextSibling = n.getNext(); if (nextSibling == null) { return; } else if (NodeUtil.isForIn(nextSibling)) { Node forNode = nextSibling; Node forVar = forNode.getFirstChild(); if (forVar.isName() && n.isVar() && n.hasOneChild()) { Node name = n.getFirstChild(); if (!name.hasChildren() && forVar.getString().equals(name.getString())) { // OK, the names match, and the var declaration does not have an // initializer. Move it into the loop. parent.removeChild(n); forNode.replaceChild(forVar, n); compiler.reportCodeChange(); } } } else if (nextSibling.isFor() && nextSibling.getFirstChild().isEmpty()) { // Does the current node contain an in operator? If so, embedding // the expression in a for loop can cause some JavaScript parsers (such // as the PlayStation 3's browser based on Access's NetFront // browser) to fail to parse the code. // See bug 1778863 for details. if (NodeUtil.containsType(n, Token.IN)) { return; } // Move the current node into the FOR loop initializer. Node forNode = nextSibling; Node oldInitializer = forNode.getFirstChild(); parent.removeChild(n); Node newInitializer; if (n.isVar()) { newInitializer = n; } else { // Extract the expression from EXPR_RESULT node. Preconditions.checkState(n.hasOneChild()); newInitializer = n.getFirstChild(); n.removeChild(newInitializer); } forNode.replaceChild(oldInitializer, newInitializer); compiler.reportCodeChange(); } } static class StripConstantAnnotations extends AbstractPostOrderCallback implements CompilerPass { private AbstractCompiler compiler; StripConstantAnnotations(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node js) { NodeTraversal.traverse(compiler, externs, this); NodeTraversal.traverse(compiler, js, this); } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (node.isName() || node.isString() || node.isStringKey()) { node.removeProp(Node.IS_CONSTANT_NAME); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GatherRawExports.java0000644000175000017500000000470112115204405026752 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import java.util.Set; /** * External references of the form: "window['xx']" indicate names that must * be reserved when variable renaming to avoid conflicts. * * @author johnlenz@google.com (John Lenz) */ class GatherRawExports extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private static final String GLOBAL_THIS_NAMES[] = { "window", "top" }; private final Set exportedVariables = Sets.newHashSet(); GatherRawExports(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { Node sibling = n.getNext(); if (sibling != null && sibling.isString() && NodeUtil.isGet(parent)) { // TODO(johnlenz): Should we warn if we see a property name that // hasn't been exported? if (isGlobalThisObject(t, n)) { exportedVariables.add(sibling.getString()); } } } private boolean isGlobalThisObject(NodeTraversal t, Node n) { if (n.isThis()) { return t.inGlobalScope(); } else if (n.isName()) { String varName = n.getString(); int items = GLOBAL_THIS_NAMES.length; for (int i = 0; i < items; i++) { if (varName.equals(GLOBAL_THIS_NAMES[i])) { return true; } } } return false; } public Set getExportedVariableNames() { return exportedVariables; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SourceInformationAnnotator.java0000644000175000017500000000555712115204405031047 0ustar apoapo/* * 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.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Annotates nodes with information from their original input file * before the compiler performs work that changes this information (such * as its original location, its original name, etc). * * Information saved: * * - Annotates all NAME nodes with an ORIGINALNAME_PROP indicating its original * name. * * - Annotates all string GET_PROP nodes with an ORIGINALNAME_PROP. * * - Annotates all OBJECT_LITERAL unquoted string key nodes with an * ORIGINALNAME_PROP. * * - Annotates all FUNCTION nodes with an ORIGINALNAME_PROP indicating its * nearest original name. * */ class SourceInformationAnnotator extends NodeTraversal.AbstractPostOrderCallback { private final String sourceFile; private final boolean doSanityChecks; public SourceInformationAnnotator( String sourceFile, boolean doSanityChecks) { this.sourceFile = sourceFile; this.doSanityChecks = doSanityChecks; } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Verify the source file is annotated. if (doSanityChecks && sourceFile != null) { Preconditions.checkState(sourceFile.equals( n.getSourceFileName())); } // Annotate the original name. switch (n.getType()) { case Token.GETPROP: Node propNode = n.getLastChild(); setOriginalName(n, propNode.getString()); break; case Token.FUNCTION: String functionName = NodeUtil.getNearestFunctionName(n); if (functionName != null) { setOriginalName(n, functionName); } break; case Token.NAME: setOriginalName(n, n.getString()); break; case Token.OBJECTLIT: for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { // We only want keys were unquoted. if (!key.isQuotedString()) { setOriginalName(key, key.getString()); } } break; } } void setOriginalName(Node n, String name) { if (!name.isEmpty() && n.getProp(Node.ORIGINALNAME_PROP) == null) { n.putProp(Node.ORIGINALNAME_PROP, name); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/HotSwapCompilerPass.java0000644000175000017500000000334112115204405027407 0ustar apoapo/* * Copyright 2011 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.rhino.Node; /** * Interface for compiler passes that can be used in a hot-swap fashion. *

      * The additional method is {@code hotSwapScript} which runs this pass on a * subtree of the AST. Each pass that is intended to support hot-swap style * should implement this interface. *

      * It is assumed that {@code Node} argument of {@code hotSwapScript} is the root * of a sub-tree in AST that represents a JS file and so is of type {@code * Token.SCRIPT}. * * @author bashir@google.com (Bashir Sadjad) */ public interface HotSwapCompilerPass extends CompilerPass { /** * Process the JS with root node root. This is supposed to be significantly * faster compared to corresponding full-compiler passes. * * @param scriptRoot Root node corresponding to the file that is modified, * should be of type {@code Token.SCRIPT}. * @param originalRoot Root node corresponding to the original version of the * file that is modified. Should be of type {@code token.SCRIPT}. */ void hotSwapScript(Node scriptRoot, Node originalRoot); } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CodeGenerator.java0000644000175000017500000011461712115204405026232 0ustar apoapo/* * Copyright 2004 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.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.Map; /** * CodeGenerator generates codes from a parse tree, sending it to the specified * CodeConsumer. * */ class CodeGenerator { private static final String LT_ESCAPED = "\\x3c"; private static final String GT_ESCAPED = "\\x3e"; // A memoizer for formatting strings as JS strings. private final Map ESCAPED_JS_STRINGS = Maps.newHashMap(); private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private final CodeConsumer cc; private final CharsetEncoder outputCharsetEncoder; private final boolean preferSingleQuotes; private final boolean trustedStrings; private CodeGenerator(CodeConsumer consumer) { cc = consumer; outputCharsetEncoder = null; preferSingleQuotes = false; trustedStrings = true; } static CodeGenerator forCostEstimation(CodeConsumer consumer) { return new CodeGenerator(consumer); } CodeGenerator( CodeConsumer consumer, CompilerOptions options) { cc = consumer; Charset outputCharset = options.getOutputCharset(); if (outputCharset == null || outputCharset == Charsets.US_ASCII) { // If we want our default (pretending to be UTF-8, but escaping anything // outside of straight ASCII), then don't use the encoder, but // just special-case the code. This keeps the normal path through // the code identical to how it's been for years. this.outputCharsetEncoder = null; } else { this.outputCharsetEncoder = outputCharset.newEncoder(); } this.preferSingleQuotes = options.preferSingleQuotes; this.trustedStrings = options.trustedStrings; } /** * Insert a ECMASCRIPT 5 strict annotation. */ public void tagAsStrict() { add("'use strict';"); } void add(String str) { cc.add(str); } private void addIdentifier(String identifier) { cc.addIdentifier(identifierEscape(identifier)); } void add(Node n) { add(n, Context.OTHER); } void add(Node n, Context context) { if (!cc.continueProcessing()) { return; } int type = n.getType(); String opstr = NodeUtil.opToStr(type); int childCount = n.getChildCount(); Node first = n.getFirstChild(); Node last = n.getLastChild(); // Handle all binary operators if (opstr != null && first != last) { Preconditions.checkState( childCount == 2, "Bad binary operator \"%s\": expected 2 arguments but got %s", opstr, childCount); int p = NodeUtil.precedence(type); // For right-hand-side of operations, only pass context if it's // the IN_FOR_INIT_CLAUSE one. Context rhsContext = getContextForNoInOperator(context); // Handle associativity. // e.g. if the parse tree is a * (b * c), // we can simply generate a * b * c. if (last.getType() == type && NodeUtil.isAssociative(type)) { addExpr(first, p, context); cc.addOp(opstr, true); addExpr(last, p, rhsContext); } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { // Assignments are the only right-associative binary operators addExpr(first, p, context); cc.addOp(opstr, true); addExpr(last, p, rhsContext); } else { unrollBinaryOperator(n, type, opstr, context, rhsContext, p, p + 1); } return; } cc.startSourceMapping(n); switch (type) { case Token.TRY: { Preconditions.checkState(first.getNext().isBlock() && !first.getNext().hasMoreThanOneChild()); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild(); if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 2); add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.isEmpty()) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.isComma()) { addExpr(first, NodeUtil.precedence(Token.ASSIGN), Context.OTHER); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addArrayList(first); add("]"); break; case Token.PARAM_LIST: add("("); addList(first); add(")"); break; case Token.COMMA: Preconditions.checkState(childCount == 2); unrollBinaryOperator(n, Token.COMMA, ",", context, Context.OTHER, 0, 0); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type), Context.OTHER); break; } case Token.NEG: { Preconditions.checkState(childCount == 1); // It's important to our sanity checker that the code // we print produces the same AST as the code we parse back. // NEG is a weird case because Rhino parses "- -2" as "2". if (n.getFirstChild().isNumber()) { cc.addNumber(-n.getFirstChild().getDouble()); } else { cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type), Context.OTHER); } break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), 1, Context.OTHER); cc.addOp(":", true); addExpr(last, 1, Context.OTHER); break; } case Token.REGEXP: if (!first.isString() || !last.isString()) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.GETTER_DEF: case Token.SETTER_DEF: Preconditions.checkState(n.getParent().isObjectLit()); Preconditions.checkState(childCount == 1); Preconditions.checkState(first.isFunction()); // Get methods are unnamed Preconditions.checkState(first.getFirstChild().getString().isEmpty()); if (type == Token.GETTER_DEF) { // Get methods have no parameters. Preconditions.checkState(!first.getChildAtIndex(1).hasChildren()); add("get "); } else { // Set methods have one parameter. Preconditions.checkState(first.getChildAtIndex(1).hasOneChild()); add("set "); } // The name is on the GET or SET node. String name = n.getString(); Node fn = first; Node parameters = fn.getChildAtIndex(1); Node body = fn.getLastChild(); // Add the property name. if (!n.isQuotedString() && TokenStream.isJSIdentifier(name) && // do not encode literally any non-literal characters that were // Unicode escaped. NodeUtil.isLatin(name)) { add(name); } else { // Determine if the string is a simple number. double d = getSimpleNumber(name); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addJsString(n); } } add(parameters); add(body, Context.PRESERVE_BLOCK); break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean preserveBlock = context == Context.PRESERVE_BLOCK; if (preserveBlock) { cc.beginBlock(); } boolean preferLineBreaks = type == Token.SCRIPT || (type == Token.BLOCK && !preserveBlock && n.getParent() != null && n.getParent().isScript()); for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.isVar()) { cc.endStatement(); } if (c.isFunction()) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top-level statements are more homogeneous. if (preferLineBreaks) { cc.notePreferredLineBreak(); } } if (preserveBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.isVar()) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyStatement(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState( childCount == 2, "Bad GETPROP: expected 2 children, but got %s", childCount); Preconditions.checkState( last.isString(), "Bad GETPROP: RHS should be STRING"); boolean needsParens = (first.isNumber()); if (needsParens) { add("("); } addExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState( childCount == 2, "Bad GETELEM: expected 2 children but got %s", childCount); addExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc/dec. if (postProp != 0) { addExpr(first, NodeUtil.precedence(type), context); cc.addOp(o, false); } else { cc.addOp(o, false); add(first); } break; } case Token.CALL: // We have two special cases here: // 1) If the left hand side of the call is a direct reference to eval, // then it must have a DIRECT_EVAL annotation. If it does not, then // that means it was originally an indirect call to eval, and that // indirectness must be preserved. // 2) If the left hand side of the call is a property reference, // then the call must not a FREE_CALL annotation. If it does, then // that means it was originally an call without an explicit this and // that must be preserved. if (isIndirectEval(first) || n.getBooleanProp(Node.FREE_CALL) && NodeUtil.isGet(first)) { add("(0,"); addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER); add(")"); } else { addExpr(first, NodeUtil.precedence(type), context); } add("("); addList(first.getNext()); add(")"); break; case Token.IF: boolean hasElse = childCount == 3; boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse; if (ambiguousElseClause) { cc.beginBlock(); } add("if("); add(first); add(")"); if (hasElse) { addNonEmptyStatement( first.getNext(), Context.BEFORE_DANGLING_ELSE, false); add("else"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), false); } else { addNonEmptyStatement(first.getNext(), Context.OTHER, false); Preconditions.checkState(childCount == 2); } if (ambiguousElseClause) { cc.endBlock(); } break; case Token.NULL: Preconditions.checkState(childCount == 0); cc.addConstant("null"); break; case Token.THIS: Preconditions.checkState(childCount == 0); add("this"); break; case Token.FALSE: Preconditions.checkState(childCount == 0); cc.addConstant("false"); break; case Token.TRUE: Preconditions.checkState(childCount == 0); cc.addConstant("true"); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_RESULT: Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parentheses. Otherwise, when parsed, NEW will bind to the // first viable parentheses (don't traverse into functions). if (NodeUtil.containsType( first, Token.CALL, NodeUtil.MATCH_NOT_FUNCTION)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence, Context.OTHER); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING_KEY: Preconditions.checkState( childCount == 1, "Object lit key must have 1 child"); addJsString(n); break; case Token.STRING: Preconditions.checkState( childCount == 0, "A string may not have children"); addJsString(n); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext()) { if (c != first) { cc.listSeparator(); } if (c.isGetterDef() || c.isSetterDef()) { add(c); } else { Preconditions.checkState(c.isStringKey()); String key = c.getString(); // Object literal property names don't have to be quoted if they // are not JavaScript keywords if (!c.isQuotedString() && !TokenStream.isKeyword(key) && TokenStream.isJSIdentifier(key) && // do not encode literally any non-literal characters that // were Unicode escaped. NodeUtil.isLatin(key)) { add(key); } else { // Determine if the string is a simple number. double d = getSimpleNumber(key); if (!Double.isNaN(d)) { cc.addNumber(d); } else { addExpr(c, 1, Context.OTHER); } } add(":"); addExpr(c.getFirstChild(), 1, Context.OTHER); } } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break; case Token.DEFAULT_CASE: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (!first.isLabelName()) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyStatement( last, getContextForNonEmptyExpression(context), true); break; case Token.CAST: add("("); add(first); add(")"); break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } /** * We could use addList recursively here, but sometimes we produce * very deeply nested operators and run out of stack space, so we * just unroll the recursion when possible. * * We assume nodes are left-recursive. */ private void unrollBinaryOperator( Node n, int op, String opStr, Context context, Context rhsContext, int leftPrecedence, int rightPrecedence) { Node firstNonOperator = n.getFirstChild(); while (firstNonOperator.getType() == op) { firstNonOperator = firstNonOperator.getFirstChild(); } addExpr(firstNonOperator, leftPrecedence, context); Node current = firstNonOperator; do { current = current.getParent(); cc.addOp(opStr, true); addExpr(current.getFirstChild().getNext(), rightPrecedence, rhsContext); } while (current != n); } static boolean isSimpleNumber(String s) { int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c < '0' || c > '9') { return false; } } return len > 0 && s.charAt(0) != '0'; } static double getSimpleNumber(String s) { if (isSimpleNumber(s)) { try { long l = Long.parseLong(s); if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) { return l; } } catch (NumberFormatException e) { // The number was too long to parse. Fall through to NaN. } } return Double.NaN; } /** * @return Whether the name is an indirect eval. */ private boolean isIndirectEval(Node n) { return n.isName() && "eval".equals(n.getString()) && !n.getBooleanProp(Node.DIRECT_EVAL); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyStatement( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && !n.isBlock()) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.isBlock()) { int count = getNonEmptyChildCount(n, 2); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } else { cc.endStatement(true); } return; } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DOs. Node firstAndOnlyChild = getFirstNonEmptyChild(n); boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); if (alwaysWrapInBlock || isOneExactlyFunctionOrDo(firstAndOnlyChild)) { cc.beginBlock(); add(firstAndOnlyChild, Context.STATEMENT); cc.maybeLineBreak(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); return; } else { // Continue with the only child. nodeToProcess = firstAndOnlyChild; } } if (count > 1) { context = Context.PRESERVE_BLOCK; } } if (nodeToProcess.isEmpty()) { cc.endStatement(true); } else { add(nodeToProcess, context); // VAR doesn't include ';' since it gets used in expressions - so any // VAR in a statement context needs a call to endStatement() here. if (nodeToProcess.isVar()) { cc.endStatement(); } } } /** * @return Whether the Node is a DO or FUNCTION (with or without * labels). */ private boolean isOneExactlyFunctionOrDo(Node n) { if (n.isLabel()) { Node labeledStatement = n.getLastChild(); if (!labeledStatement.isBlock()) { return isOneExactlyFunctionOrDo(labeledStatement); } else { // For labels with block children, we need to ensure that a // labeled FUNCTION or DO isn't generated when extraneous BLOCKs // are skipped. if (getNonEmptyChildCount(n, 2) == 1) { return isOneExactlyFunctionOrDo(getFirstNonEmptyChild(n)); } else { // Either a empty statement or an block with more than one child, // way it isn't a FUNCTION or DO. return false; } } } else { return (n.isFunction() || n.isDo()); } } private void addExpr(Node n, int minPrecedence, Context context) { if ((NodeUtil.precedence(n.getType()) < minPrecedence) || ((context == Context.IN_FOR_INIT_CLAUSE) && n.isIn())){ add("("); add(n, Context.OTHER); add(")"); } else { add(n, context); } } void addList(Node firstInList) { addList(firstInList, true, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument) { addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument, Context lhsContext) { for (Node n = firstInList; n != null; n = n.getNext()) { boolean isFirst = n == firstInList; if (isFirst) { addExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); } else { cc.listSeparator(); addExpr(n, isArrayOrFunctionArgument ? 1 : 0, Context.OTHER); } } } /** * This function adds a comma-separated list as is specified by an ARRAYLIT * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). */ void addArrayList(Node firstInList) { boolean lastWasEmpty = false; for (Node n = firstInList; n != null; n = n.getNext()) { if (n != firstInList) { cc.listSeparator(); } addExpr(n, 1, Context.OTHER); lastWasEmpty = n.isEmpty(); } if (lastWasEmpty) { cc.listSeparator(); } } void addCaseBody(Node caseBody) { cc.beginCaseBody(); add(caseBody); cc.endCaseBody(); } void addAllSiblings(Node n) { for (Node c = n; c != null; c = c.getNext()) { add(c); } } /** Outputs a JS string, using the optimal (single/double) quote character */ private void addJsString(Node n) { String s = n.getString(); boolean useSlashV = n.getBooleanProp(Node.SLASH_V); if (useSlashV) { add(jsString(n.getString(), useSlashV)); } else { String cached = ESCAPED_JS_STRINGS.get(s); if (cached == null) { cached = jsString(n.getString(), useSlashV); ESCAPED_JS_STRINGS.put(s, cached); } add(cached); } } private String jsString(String s, boolean useSlashV) { int singleq = 0, doubleq = 0; // could count the quotes and pick the optimal quote character for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '"': doubleq++; break; case '\'': singleq++; break; } } String doublequote, singlequote; char quote; if (preferSingleQuotes ? (singleq <= doubleq) : (singleq < doubleq)) { // more double quotes so enclose in single quotes. quote = '\''; doublequote = "\""; singlequote = "\\\'"; } else { // more single quotes so escape the doubles quote = '\"'; doublequote = "\\\""; singlequote = "\'"; } return strEscape(s, quote, doublequote, singlequote, "\\\\", outputCharsetEncoder, useSlashV, false); } /** Escapes regular expression */ String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder, false, true); } /** * Escapes the given string to a double quoted (") JavaScript/JSON string */ String escapeToDoubleQuotedJsString(String s) { return strEscape(s, '"', "\\\"", "\'", "\\\\", null, false, false); } /* If the user doesn't want to specify an output charset encoder, assume they want Latin/ASCII characters only. */ String regexpEscape(String s) { return regexpEscape(s, null); } /** Helper to escape JavaScript string as well as regular expression */ private String strEscape( String s, char quote, String doublequoteEscape, String singlequoteEscape, String backslashEscape, CharsetEncoder outputCharsetEncoder, boolean useSlashV, boolean isRegexp) { StringBuilder sb = new StringBuilder(s.length() + 2); sb.append(quote); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '\0': sb.append("\\x00"); break; case '\u000B': if (useSlashV) { sb.append("\\v"); } else { sb.append("\\x0B"); } break; // From the SingleEscapeCharacter grammar production. case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '\\': sb.append(backslashEscape); break; case '\"': sb.append(doublequoteEscape); break; case '\'': sb.append(singlequoteEscape); break; // From LineTerminators (ES5 Section 7.3, Table 3) case '\u2028': sb.append("\\u2028"); break; case '\u2029': sb.append("\\u2029"); break; case '=': // '=' is a syntactically signficant regexp character. if (trustedStrings || isRegexp) { sb.append(c); } else { sb.append("\\x3d"); } break; case '&': if (trustedStrings || isRegexp) { sb.append(c); } else { sb.append("\\x26"); } break; case '>': if (!trustedStrings && !isRegexp) { sb.append(GT_ESCAPED); break; } // Break --> into --\> or ]]> into ]]\> // // This is just to prevent developers from shooting themselves in the // foot, and does not provide the level of security that you get // with trustedString == false. if (i >= 2 && ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || (s.charAt(i - 1) == ']' && s.charAt(i - 2) == ']'))) { sb.append(GT_ESCAPED); } else { sb.append(c); } break; case '<': if (!trustedStrings && !isRegexp) { sb.append(LT_ESCAPED); break; } // Break 0x1f && c < 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some JS parsers, // or perhaps mangled by proxies along the way, // so we play it safe and Unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-Latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser's rules for valid identifier characters are // crazy. if (c > 0x1F && c < 0x7F) { sb.append(c); } else { appendHexJavaScriptRepresentation(sb, c); } } return sb.toString(); } /** * @param maxCount The maximum number of children to look for. * @return The number of children of this node that are non empty up to * maxCount. */ private static int getNonEmptyChildCount(Node n, int maxCount) { int i = 0; Node c = n.getFirstChild(); for (; c != null && i < maxCount; c = c.getNext()) { if (c.isBlock()) { i += getNonEmptyChildCount(c, maxCount-i); } else if (!c.isEmpty()) { i++; } } return i; } /** Gets the first non-empty child of the given node. */ private static Node getFirstNonEmptyChild(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.isBlock()) { Node result = getFirstNonEmptyChild(c); if (result != null) { return result; } } else if (!c.isEmpty()) { return c; } } return null; } // Information on the current context. Used for disambiguating special cases. // For example, a "{" could indicate the start of an object literal or a // block, depending on the current context. enum Context { STATEMENT, BEFORE_DANGLING_ELSE, // a hack to resolve the else-clause ambiguity START_OF_EXPR, PRESERVE_BLOCK, // Are we inside the init clause of a for loop? If so, the containing // expression can't contain an in operator. Pass this context flag down // until we reach expressions which no longer have the limitation. IN_FOR_INIT_CLAUSE, OTHER } private Context getContextForNonEmptyExpression(Context currentContext) { return currentContext == Context.BEFORE_DANGLING_ELSE ? Context.BEFORE_DANGLING_ELSE : Context.OTHER; } /** * If we're in a IN_FOR_INIT_CLAUSE, we can't permit in operators in the * expression. Pass on the IN_FOR_INIT_CLAUSE flag through subexpressions. */ private Context getContextForNoInOperator(Context context) { return (context == Context.IN_FOR_INIT_CLAUSE ? Context.IN_FOR_INIT_CLAUSE : Context.OTHER); } /** * @see #appendHexJavaScriptRepresentation(int, Appendable) */ private static void appendHexJavaScriptRepresentation( StringBuilder sb, char c) { try { appendHexJavaScriptRepresentation(c, sb); } catch (IOException ex) { // StringBuilder does not throw IOException. throw new RuntimeException(ex); } } /** * Returns a JavaScript representation of the character in a hex escaped * format. * * @param codePoint The code point to append. * @param out The buffer to which the hex representation should be appended. */ private static void appendHexJavaScriptRepresentation( int codePoint, Appendable out) throws IOException { if (Character.isSupplementaryCodePoint(codePoint)) { // Handle supplementary Unicode values which are not representable in // JavaScript. We deal with these by escaping them as two 4B sequences // so that they will round-trip properly when sent from Java to JavaScript // and back. char[] surrogates = Character.toChars(codePoint); appendHexJavaScriptRepresentation(surrogates[0], out); appendHexJavaScriptRepresentation(surrogates[1], out); return; } out.append("\\u") .append(HEX_CHARS[(codePoint >>> 12) & 0xf]) .append(HEX_CHARS[(codePoint >>> 8) & 0xf]) .append(HEX_CHARS[(codePoint >>> 4) & 0xf]) .append(HEX_CHARS[codePoint & 0xf]); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/EmptyMessageBundle.java0000644000175000017500000000252712115204405027242 0ustar apoapo/* * 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.ImmutableList; import com.google.javascript.jscomp.JsMessage.IdGenerator; /** * An implementation of MessageBundle that has no translations. * * @author nicksantos@google.com (Nick Santos) */ public class EmptyMessageBundle implements MessageBundle { /** * Gets a dummy message ID generator. */ @Override public IdGenerator idGenerator() { return null; } /** * Returns null, to indicate it has no message replacements. */ @Override public JsMessage getMessage(String id) { return null; } /** * Returns an empty list of messages. */ @Override public Iterable getAllMessages() { return ImmutableList.of(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ErrorPass.java0000644000175000017500000000224612115204405025423 0ustar apoapo/* * 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.rhino.Node; /** * A compiler pass that just reports an error. * */ class ErrorPass implements CompilerPass { private final AbstractCompiler compiler; private final JSError error; ErrorPass(AbstractCompiler compiler, DiagnosticType error) { this(compiler, JSError.make(error)); } ErrorPass(AbstractCompiler compiler, JSError error) { this.compiler = compiler; this.error = error; } @Override public void process(Node externs, Node root) { compiler.report(error); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NameReferenceGraphReport.java0000644000175000017500000002561612115204405030366 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NameReferenceGraph.Reference; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * Generate a nice HTML file describing the name reference graph. * For each declaration, list the sites where the declaration's name * is referenced, and list all the names that the declaration references. * For each, name exactly where use occurs in the source code. * *

      This report should be useful both for internal compiler * developers and for engineers trying to understand running behavior * of their code or who want to understand why the compiler won't * move their code into a new module. * * @author bowdidge@google.com (Robert Bowdidge) */ final class NameReferenceGraphReport { private NameReferenceGraph graph = null; /** * Create a NameReferenceGraphReport object. * * @param g name reference graph to describe in report. */ NameReferenceGraphReport(NameReferenceGraph g) { this.graph = g; } /** * Generate a nice HTML file describing the name reference graph. * For each declaration, list the sites where the declaration's name * is referenced, and list all the names that the declaration references. * For each, name exactly where use occurs in the source code. * *

      This report should be useful both for internal compiler * developers and for engineers trying to understand running * behavior of their code or who want to understand why * AbstractCompiler won't move their code into a new module. * * @return String containing the entire HTML for the report. */ public String getHtmlReport() { StringBuilder builder = new StringBuilder(); List> nodes = Lists.newArrayList( graph.getDirectedGraphNodes()); generateHtmlReportHeader(builder); builder.append("

      Name Reference Graph Dump

      \n"); builder.append("OVERALL STATS\n"); builder.append("
        \n"); builder.append("
      • Total names: " + nodes.size()); builder.append("
      \n"); builder.append("ALL NAMES\n"); builder.append("
        \n"); // Sort declarations in alphabetical order. Collections.sort(nodes, new DiGraphNodeComparator()); for (DiGraphNode n : nodes) { // Generate the HTML describing the declaration itself. generateDeclarationReport(builder, n); // Next, list the places where this name is used (REFERS TO), and the // names that this declaration refers to (REFERENCED BY). List> outEdges = graph.getOutEdges(n.getValue()); List> inEdges = graph.getInEdges(n.getValue()); // Don't bother to create the dotted list if we don't have anything to // put in it. if (!outEdges.isEmpty() || !inEdges.isEmpty()) { builder.append("
          "); if (outEdges.size() > 0) { builder.append("
        • REFERS TO:
          \n"); builder.append("
            "); for (DiGraphEdge edge : outEdges) { generateEdgeReport(builder, edge.getDestination().getValue(), edge); } builder.append("
          \n"); } if (inEdges.size() > 0) { builder.append("
        • REFERENCED BY:
          \n"); builder.append("
            "); for (DiGraphEdge edge : inEdges) { generateEdgeReport(builder, edge.getSource().getValue(), edge); } builder.append("
          "); } builder.append("
        \n"); } } builder.append("
      \n"); generateHtmlReportFooter(builder); return builder.toString(); } /** * Given a node, find the name of the containing source file. * * @param node Parse tree node whose filename is requested * @return String containing name of source file, or empty string if name * cannot be identified. */ private String getSourceFile(Node node) { String filename = node.getSourceFileName(); if (filename == null) { return ""; } return filename; } /** * Generate the HTML for describing a specific declaration. * @param builder contents of report to be generated * @param declarationNode declaration to describe */ private void generateDeclarationReport(StringBuilder builder, DiGraphNode declarationNode) { // Provide the name and location of declaration, // with an anchor to allow navigation to this declaration. String declName = declarationNode.getValue().getQualifiedName(); JSType declType = declarationNode.getValue().getType(); builder.append("
    11. "); builder.append(""); builder.append(declName); builder.append("\n"); // Provide the type of the declaration. // This is helpful for debugging. generateType(builder, declType); // List all the definitions of this name that were found in the code. // For each, list List defs = declarationNode.getValue().getDeclarations(); if (defs.size() == 0) { builder.append("
      No definitions found
      "); } else { // Otherwise, provide a list of definitions in a dotted list. // For each definition, print the location where that definition is // found. builder.append("
        "); for (DefinitionsRemover.Definition def : defs) { Node fnDef = def.getRValue(); String sourceFileName = getSourceFile(fnDef); builder.append("
      • Defined: "); generateSourceReferenceLink(builder, sourceFileName, fnDef.getLineno(), fnDef.getCharno()); } builder.append("
      "); } } /** * Generate the HTML header for the report style. * Borrowed straight from NameAnalyzer's report style. * * @param builder contents of the report to be generated */ private void generateHtmlReportHeader(StringBuilder builder) { builder.append("\n" + "" + "" + "" + "Name Reference Graph Dump" + "\n"); } /** * Generate the HTML footer for the report style. */ private void generateHtmlReportFooter(StringBuilder builder) { builder.append(""); } /** * Generate a description of a specific edge between two nodes. * For each edge, name the element being linked, the location of the * reference in the source file, and the type of the reference. * * @param builder contents of the report to be generated * @param referencedDecl name of the declaration being referenced * @param edge the graph edge being described */ private void generateEdgeReport(StringBuilder builder, Name referencedDecl, DiGraphEdge edge) { String srcDeclName = referencedDecl.getQualifiedName(); builder.append("
    12. "); builder.append(srcDeclName); builder.append(" "); Node def = edge.getValue().getSite(); int lineNumber = def.getLineno(); int columnNumber = def.getCharno(); String sourceFile = getSourceFile(def); generateSourceReferenceLink(builder, sourceFile, lineNumber, columnNumber); JSType defType = edge.getValue().getSite().getJSType(); generateType(builder, defType); } /** * Generate a link and text for a reference to a particular location * in a source file. Selecting the link should take the programmer * to a browsable version of the file. * * @param builder contents of the report to be generated * @param sourceFile Path to the file * @param lineNumber line where the object to view is located * @param columnNumber column where the object to highlight is located. */ private void generateSourceReferenceLink(StringBuilder builder, String sourceFile, int lineNumber, int columnNumber) { assert(sourceFile != null); builder.append("("); // Print out the text path so the user knows where things come from. builder.append(sourceFile + ":" + lineNumber + "," + columnNumber); builder.append(")"); } /** * Dump a type in a nice, readable way. * * @param builder contents of the report to be generated. * @param defType type to describe */ private void generateType(StringBuilder builder, JSType defType) { if (defType == null) { builder.append(" (type: null) "); } else if (defType.isUnknownType()) { builder.append(" (type: unknown) "); } else { builder.append(" (type: " + defType.toString() + ") "); } } /** * DiGraphNodeComparator gives us a way to generate sorted lists * of DiGraphNodes. It provides a compare function used by the * String class's sort method. */ class DiGraphNodeComparator implements Comparator> { @Override public int compare(DiGraphNode node1, DiGraphNode node2) { Preconditions.checkNotNull(node1.getValue()); Preconditions.checkNotNull(node2.getValue()); if ((node1.getValue().getQualifiedName() == null) && (node2.getValue().getQualifiedName() == null)) { return 0; } // Node 1, if null, comes before node 2. if (node1.getValue().getQualifiedName() == null) { return -1; } // Node 2, if null, comes before node 1. if (node2.getValue().getQualifiedName() == null) { return 1; } return node1.getValue().getQualifiedName().compareTo( node2.getValue().getQualifiedName()); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SyntheticAst.java0000644000175000017500000000334312115204405026124 0ustar apoapo/* * 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.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; /** * An AST generated totally by the compiler. * * @author nicksantos@google.com (Nick Santos) */ public class SyntheticAst implements SourceAst { private static final long serialVersionUID = 1L; private final InputId inputId; private final SourceFile sourceFile; private Node root; SyntheticAst(String sourceName) { this.inputId = new InputId(sourceName); this.sourceFile = new SourceFile(sourceName); clearAst(); } @Override public Node getAstRoot(AbstractCompiler compiler) { return root; } @Override public void clearAst() { root = IR.script(); root.setInputId(inputId); root.setStaticSourceFile(sourceFile); } @Override public InputId getInputId() { return inputId; } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { throw new IllegalStateException( "Cannot set a source file for a synthetic AST"); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RecordFunctionInformation.java0000644000175000017500000001007412115204405030633 0ustar apoapo/* * 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.Iterables; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import java.util.Comparator; import java.util.TreeSet; /** * Records information about functions and modules. * */ class RecordFunctionInformation extends AbstractPostOrderCallback implements CompilerPass { private final Compiler compiler; private final FunctionNames functionNames; private final JSModuleGraph moduleGraph; /** * Protocol buffer builder. */ private final FunctionInformationMap.Builder mapBuilder; /** * Creates a record function information compiler pass. * * @param compiler The JSCompiler * @param functionNames Assigned function identifiers. */ RecordFunctionInformation(Compiler compiler, FunctionNames functionNames) { this.compiler = compiler; this.moduleGraph = compiler.getModuleGraph(); this.functionNames = functionNames; this.mapBuilder = FunctionInformationMap.newBuilder(); } /** * Returns the built-out map. */ FunctionInformationMap getMap() { return mapBuilder.build(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); if (moduleGraph == null) { addModuleInformation(null); } else { // The test expects a consistent module order. TreeSet modules = Sets.newTreeSet(new Comparator() { @Override public int compare(JSModule o1, JSModule o2) { return o1.getName().compareTo(o2.getName()); } }); Iterables.addAll(modules, moduleGraph.getAllModules()); for (JSModule m : modules) { addModuleInformation(m); } } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isFunction()) { return; } int id = functionNames.getFunctionId(n); if (id < 0) { // Function node was added during compilation; don't instrument. return; } String compiledSource = compiler.toSource(n); JSModule module = t.getModule(); mapBuilder.addEntry(FunctionInformationMap.Entry.newBuilder() .setId(id) .setSourceName(NodeUtil.getSourceName(n)) .setLineNumber(n.getLineno()) .setModuleName(moduleGraph == null ? "" : module.getName()) .setSize(compiledSource.length()) .setName(functionNames.getFunctionName(n)) .setCompiledSource(compiledSource).build()); } /** * Record a module's compiled source. The view of the source we get * from function sources alone is not complete; it doesn't contain * assignments and function calls in the global scope which are * crucial to understanding how the application works. * * This version of the source is also written out to js_output_file, * module_output_path_prefix or other places. Duplicating it here * simplifies the process of writing tools that combine and present * module and function for debugging purposes. */ private void addModuleInformation(JSModule module) { String name; String source; if (module != null) { name = module.getName(); source = compiler.toSource(module); } else { name = ""; source = compiler.toSource(); } mapBuilder.addModule(FunctionInformationMap.Module.newBuilder() .setName(name) .setCompiledSource(source).build()); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/AliasKeywords.java0000644000175000017500000003637312115204405026274 0ustar apoapo/* * 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.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Map; import java.util.Set; /** *

      Replaces references to aliasable keyword literals (true, false, * null) with variables and statements (throw) with functions declared in the * global scope. When combined with RenameVars, this pass helps to reduce the * number of bytes taken up by references to these keywords by replacing them * with references to variables and functions with shorter names.

      * */ class AliasKeywords implements CompilerPass { /** Callback that finds the nodes that we will alias. */ private class FindAliasableNodes extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { final int type = n.getType(); if (isAliasableType(type)) { visitAliasableNode(n, parent); } else if (type == Token.NAME) { visitNameNode(n); } } /** * Find the AliasSpecification associated with the node, and tell * that AliasSpecification about the new node. */ private void visitAliasableNode(Node n, Node parent) { AliasSpecification aliasableNodes = aliasTypes.get(n.getType()); aliasableNodes.visit(n, parent); } /** * Sanity check that our aliases are not already defined by * someone else. */ private void visitNameNode(Node n) { if (isAliasDefinition(n)) { throw new IllegalStateException( "Existing alias definition for " + Token.name(n.getType())); } } } /** * An AliasSpecification encapsulates all of the logic to find * aliasable nodes and alias those nodes, for a given alias name. Subclasses * fill in template methods, allowing for various kinds of aliasing. */ private abstract static class AliasSpecification { /** List of nodes to alias (e.g. all 'null' nodes). */ private final Map nodes = Maps.newHashMap(); /** * Have we declared the alias (e.g. did we inject var * $$ALIAS_NULL=null; into the parse tree)? */ private boolean isAliased = false; private String aliasName; private int tokenId; /** * @param aliasName name being used as alias * @param tokenId type of node being replaced with alias */ public AliasSpecification(String aliasName, int tokenId) { this.aliasName = aliasName; this.tokenId = tokenId; } public void visit(Node n, Node parent) { nodes.put(n, parent); } /** * Insert a node that declares our alias into the parse tree, as a * child of the specified var node. Only do so if we haven't * already and there are enough references to the aliased node to * save bytes. * @return Whether the alias has been inserted. */ boolean maybeInsertAliasDeclarationIntoParseTree(Node codeRoot) { if (nodes.size() >= minOccurrencesRequiredToAlias()) { insertAliasDeclaration(codeRoot); isAliased = true; return true; } return false; } /** * Update all of the nodes with a reference to the corresponding * alias node. */ public void doAlias(AbstractCompiler compiler) { if (isAliased) { for (Map.Entry entry : nodes.entrySet()) { Node n = entry.getKey(); Node parent = entry.getValue(); aliasNode(n, parent); compiler.reportCodeChange(); } } } public int getTokenId() { return tokenId; } public String getAliasName() { return aliasName; } /** * Returns the minimum number of nodes that should be present for aliasing * to take place. */ protected abstract int minOccurrencesRequiredToAlias(); /** * Creates a node that defines the alias and attaches it to the parse tree. * * @param codeRoot The root of the script. Functions can be attached here, * e.g., function alias(p){throw p;}. */ protected abstract void insertAliasDeclaration(Node codeRoot); /** Replaces the node n with its alias. */ protected abstract void aliasNode(Node n, Node parent); } /** Aliases throw statements with a function call. */ // TODO(user): Generalize this to work with typeof expressions. private class ThrowAliasSpecification extends AliasSpecification { ThrowAliasSpecification(String aliasName) { super(aliasName, Token.THROW); } @Override protected void aliasNode(Node throwNode, Node parent) { Node name = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), throwNode, getAliasName()); Node aliasCall = IR.call( name, throwNode.removeFirstChild()); aliasCall.putBooleanProp(Node.FREE_CALL, true); Node exprResult = IR.exprResult(aliasCall); parent.replaceChild(throwNode, exprResult); } @Override /** * Adds alias function to codeRoot. See {@link #createAliasFunctionNode}). */ protected void insertAliasDeclaration(Node codeRoot) { codeRoot.addChildToFront(createAliasFunctionNode(getAliasName())); } @Override protected int minOccurrencesRequiredToAlias() { return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW; } } /** * Calculates the minimum number of occurrences of throw needed to alias * throw. */ static int estimateMinOccurrencesRequriedToAlias() { // Assuming that the alias function name is two bytes in length, two bytes // will be saved per occurrence of throw: // throw e;, compared to // TT(e);. // However, the alias definition is some length, N, e.g., // function TT(t){throw t;} // Hence there must be more than N/2 occurrences of throw to reduce // the code size. Node alias = createAliasFunctionNode("TT"); return InlineCostEstimator.getCost(alias) / 2 + 1; } /** * Creates a function node that takes a single argument, the object to * throw. The function throws the object. */ private static Node createAliasFunctionNode(String aliasName) { final String PARAM_NAME = "jscomp_throw_param"; return IR.function( IR.name(aliasName), IR.paramList(IR.name(PARAM_NAME)), IR.block( IR.throwNode(IR.name(PARAM_NAME)))); } /** Aliases literal keywords (e.g., null) with variable names. */ private class KeywordAliasSpecification extends AliasSpecification { KeywordAliasSpecification(String aliasName, int tokenId) { super(aliasName, tokenId); } @Override protected int minOccurrencesRequiredToAlias() { return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; } @Override protected void aliasNode(Node n, Node parent) { Node aliasNode = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), n, getAliasName()); parent.replaceChild(n, aliasNode); } @Override /** * Create the alias declaration (e.g. var $$ALIAS_NULL=null;). */ protected void insertAliasDeclaration(Node codeRoot) { Node varNode = new Node(Token.VAR); Node value = new Node(getTokenId()); Node name = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), varNode, getAliasName()); name.addChildToBack(value); varNode.addChildToBack(name); codeRoot.addChildrenToFront(varNode); } } /** Aliases literal keywords (e.g., null) with variable names. */ private class VoidKeywordAliasSpecification extends AliasSpecification { VoidKeywordAliasSpecification(String aliasName, int tokenId) { super(aliasName, tokenId); } @Override public void visit(Node n, Node parent) { Node value = n.getFirstChild(); if (value.isNumber() && value.getDouble() == 0) { super.visit(n, parent); } } @Override protected int minOccurrencesRequiredToAlias() { return MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL; } @Override protected void aliasNode(Node n, Node parent) { Node aliasNode = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), n, getAliasName()); parent.replaceChild(n, aliasNode); } @Override /** * Create the alias declaration (e.g. var $$ALIAS_VOID=void 0;). */ protected void insertAliasDeclaration(Node codeRoot) { Node varNode = new Node(Token.VAR); Node value = IR.voidNode(IR.number(0)); Node name = NodeUtil.newName( compiler.getCodingConvention(), getAliasName(), varNode, getAliasName()); name.addChildToBack(value); varNode.addChildToBack(name); codeRoot.addChildrenToFront(varNode); } } static final String ALIAS_NULL = "JSCompiler_alias_NULL"; static final String ALIAS_TRUE = "JSCompiler_alias_TRUE"; static final String ALIAS_FALSE = "JSCompiler_alias_FALSE"; static final String ALIAS_THROW = "JSCompiler_alias_THROW"; static final String ALIAS_VOID = "JSCompiler_alias_VOID"; /** * Don't alias a keyword unless it's referenced at least * MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL times. Aliasing a keyword has a * cost (e.g. 'var XX=true;' costs 12 bytes). We make up for this * cost by replacing references to the keyword with variables that * have shorter names. If there are only a few references to a * keyword, the cost outweighs the benefit. It is not possible to * determine the exact break-even point without compiling twice * (once with aliasing, another without) and comparing the * post-gzipped size, so we define a minimum number of references * required in order to alias. We choose 6 because the alias cost is * ~7-12 bytes (12 bytes for 'var XX=true;', 7 bytes for a * subsequent declaration that does not require its own 'var ' or * semicolon, e.g. var XX=true,XY=null;), but each reference saves * 2-3 bytes (2 for true and null, 3 for false). Thus, the break * even point is 3 at best, and 6 at worst. We could use a * CostEstimator to be precise, but requiring a constant number of * occurrences is much simpler, and the added precision of a * CostEstimator would save us <10 bytes for some unlikely edge * cases (e.g. where false is referenced exactly 5 times, but does * not get aliased). */ static final int MIN_OCCURRENCES_REQUIRED_TO_ALIAS_LITERAL = 6; /** * Don't alias throw statements unless throw is used at least * MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW times. */ static final int MIN_OCCURRENCES_REQUIRED_TO_ALIAS_THROW = estimateMinOccurrencesRequriedToAlias(); /** Reference to JS Compiler */ private final AbstractCompiler compiler; /** List of alias specifications, stored in order which transformations * should be applied. See {@link #createAliasSpecifications}. */ private final List aliasSpecifications; /** Map from rhino nodes to the corresponding AliasSpecification */ private final Map aliasTypes; /** Set of alias names. */ private final Set aliasNames; AliasKeywords(AbstractCompiler compiler) { this.compiler = compiler; aliasSpecifications = createAliasSpecifications(); aliasTypes = Maps.newLinkedHashMap(); aliasNames = Sets.newLinkedHashSet(); for (AliasSpecification specification : aliasSpecifications) { aliasTypes.put(specification.getTokenId(), specification); aliasNames.add(specification.getAliasName()); } } /** * Do all processing on the root node. */ @Override public void process(Node externs, Node root) { // Find candidates to alias. NodeTraversal.traverse(compiler, root, new FindAliasableNodes()); if (needsAliases()) { // Inject alias nodes for null, true, and false into the global scope. addAliasNodes(compiler.getNodeForCodeInsertion(null)); // Update references to null/true/false with references to the aliases. for (AliasSpecification spec : aliasSpecifications) { spec.doAlias(compiler); } } } private boolean needsAliases() { for (AliasSpecification spec : aliasSpecifications) { if (!spec.nodes.isEmpty()) { return true; } } return false; } /** * Inject alias nodes into the global scope. e.g. * var $$ALIAS_NULL=null,$$ALIAS_TRUE=true,$$ALIAS_FALSE=false;. */ private void addAliasNodes(Node codeRoot) { boolean codeChanged = false; for (AliasSpecification spec : aliasSpecifications) { if (spec.maybeInsertAliasDeclarationIntoParseTree(codeRoot)) { codeChanged = true; } } if (codeChanged) { compiler.reportCodeChange(); } } /** * Does the given node define one of our aliases? */ private boolean isAliasDefinition(Node n) { if (!n.isName()) { return false; } if (!isAliasName(n.getString())) { // The given Node's string contents is not an alias. Skip it. return false; } /* * A definition must have a child node (otherwise it's just a * reference to the alias). */ return n.getFirstChild() != null; } /** * Is this one of the Rhino token types that we're aliasing? */ private boolean isAliasableType(int type) { return aliasTypes.containsKey(type); } /** * Is this one of our alias names? */ private boolean isAliasName(String name) { return aliasNames.contains(name); } /** * Create the AliasSpecifications, one for each type we're aliasing. The * order of the elements in the list is significant. Transformations should * be applied in the given order. */ private List createAliasSpecifications() { List l = Lists.newArrayList(); l.add(new KeywordAliasSpecification(ALIAS_FALSE, Token.FALSE)); l.add(new KeywordAliasSpecification(ALIAS_NULL, Token.NULL)); l.add(new KeywordAliasSpecification(ALIAS_TRUE, Token.TRUE)); l.add(new VoidKeywordAliasSpecification(ALIAS_VOID, Token.VOID)); // Process throw nodes after literal keyword nodes. This is important when // a literal keyword is thrown (e.g., throw true;). // KeywordAliasSpecification needs to know what the parent of the node being // replaced with an alias is. Because ThrowAliasSpecification replaces the // parent of the node being aliased, ThrowAliasSpecification invalidates the // record of the node's parent that KeywordAliasSpecification stores. l.add(new ThrowAliasSpecification(ALIAS_THROW)); return l; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/SourceExcerptProvider.java0000644000175000017500000000554712115204405030020 0ustar apoapo/* * Copyright 2007 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; /** * A source excerpt provider is responsible for building source code excerpt * of specific locations, such as a specific line or a region around a * given line number. * */ public interface SourceExcerptProvider { /** * Source excerpt variety. */ enum SourceExcerpt { /** * Line excerpt. */ LINE { @Override public String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter) { return formatter.formatLine( source.getSourceLine(sourceName, lineNumber), lineNumber); } }, /** * Region excerpt. */ REGION { @Override public String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter) { return formatter.formatRegion( source.getSourceRegion(sourceName, lineNumber)); } }; /** * Get a source excerpt string based on the type of the source excerpt. */ public abstract String get(SourceExcerptProvider source, String sourceName, int lineNumber, ExcerptFormatter formatter); } /** * Get the line indicated by the line number. This call will return only the * specific line. * * @param lineNumber the line number, 1 being the first line of the file * @return the line indicated, or {@code null} if it does not exist */ String getSourceLine(String sourceName, int lineNumber); /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file * @return the region around the line number indicated, or null * if it does not exist */ Region getSourceRegion(String sourceName, int lineNumber); /** * A excerpt formatter is responsible of formatting source excerpts. */ interface ExcerptFormatter { /** * Format a line excerpt. */ String formatLine(String line, int lineNumber); /** * Format a region excerpt. */ String formatRegion(Region region); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CheckGlobalThis.java0000644000175000017500000001413112115204405026465 0ustar apoapo/* * Copyright 2007 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.Callback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Checks for certain uses of the {@code this} keyword that are considered * unsafe because they are likely to reference the global {@code this} object * unintentionally. * *

      A use of {@code this} is considered unsafe if it's on the left side of an * assignment or a property access, and not inside one of the following: *

        *
      1. a prototype method *
      2. a function annotated with {@code @constructor} *
      3. a function annotated with {@code @this}. *
      4. a function where there's no logical place to put a * {@code this} annotation. *
      * *

      Note that this check does not track assignments of {@code this} to * variables or objects. The code *

       * function evil() {
       *   var a = this;
       *   a.useful = undefined;
       * }
       * 
      * will not get flagged, even though it is semantically equivalent to *
       * function evil() {
       *   this.useful = undefined;
       * }
       * 
      * which would get flagged. * */ final class CheckGlobalThis implements Callback { static final DiagnosticType GLOBAL_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING_KEY)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; } } } if (parent != null && parent.isAssign()) { Node lhs = parent.getFirstChild(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (NodeUtil.isGet(lhs)) { if (lhs.isGetProp() && lhs.getLastChild().getString().equals("prototype")) { return false; } Node llhs = lhs.getFirstChild(); if (llhs.isGetProp() && llhs.getLastChild().getString().equals("prototype")) { return false; } } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isThis() && shouldReportThis(n)) { compiler.report(t.makeError(n, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n) { Node parent = n.getParent(); if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. return parent != null && NodeUtil.isGet(parent); } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): *
         * ... function() {}
         * ... x = function() {};
         * var ... x = function() {};
         * ... var x = function() {};
         * 
      */ private JSDocInfo getFunctionJsDocInfo(Node n) { JSDocInfo jsDoc = n.getJSDocInfo(); Node parent = n.getParent(); if (jsDoc == null) { int parentType = parent.getType(); if (parentType == Token.NAME || parentType == Token.ASSIGN) { jsDoc = parent.getJSDocInfo(); if (jsDoc == null && parentType == Token.NAME) { Node gramps = parent.getParent(); if (gramps.isVar()) { jsDoc = gramps.getJSDocInfo(); } } } } return jsDoc; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/OptimizeReturns.java0000644000175000017500000001240512115204405026664 0ustar apoapo/* * 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.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import java.util.Collection; import java.util.List; /** * A compiler pass for optimize function return results. Currently this * pass looks for results that are complete unused and rewrite then to be: * "return x()" -->"x(); return" * , but it can easily be * expanded to look for use context to avoid unneeded type coercion: * - "return x.toString()" --> "return x" * - "return !!x" --> "return x" * @author johnlenz@google.com (John Lenz) */ class OptimizeReturns implements OptimizeCalls.CallGraphCompilerPass, CompilerPass { private AbstractCompiler compiler; OptimizeReturns(AbstractCompiler compiler) { this.compiler = compiler; } @Override @VisibleForTesting public void process(Node externs, Node root) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); process(externs, root, defFinder); } @Override public void process( Node externs, Node root, SimpleDefinitionFinder definitions) { // Find all function nodes whose callers ignore the return values. List toOptimize = Lists.newArrayList(); for (DefinitionSite defSite : definitions.getDefinitionSites()) { if (!defSite.inExterns && !callResultsMaybeUsed(definitions, defSite)) { toOptimize.add(defSite.definition.getRValue()); } } // Optimize the return statements. for (Node node : toOptimize) { rewriteReturns(definitions, node); } } /** * Determines if a function result might be used. A result might be use if: * - Function must is exported. * - The definition is never accessed outside a function call context. */ private boolean callResultsMaybeUsed( SimpleDefinitionFinder defFinder, DefinitionSite definitionSite) { Definition definition = definitionSite.definition; // Assume non-function definitions results are used. Node rValue = definition.getRValue(); if (rValue == null || !rValue.isFunction()) { return true; } // Be conservative, don't try to optimize any declaration that isn't as // simple function declaration or assignment. if (!SimpleDefinitionFinder.isSimpleFunctionDeclaration(rValue)) { return true; } if (!defFinder.canModifyDefinition(definition)) { return true; } Collection useSites = defFinder.getUseSites(definition); for (UseSite site : useSites) { // Assume indirect definitions references use the result Node useNodeParent = site.node.getParent(); if (isCall(site)) { Node callNode = useNodeParent; Preconditions.checkState(callNode.isCall()); if (NodeUtil.isExpressionResultUsed(callNode)) { return true; } } else { // Allow a standalone name reference. // var a; if (!useNodeParent.isVar()) { return true; } } // TODO(johnlenz): Add specialization support. } // No possible use of the definition result return false; } /** * For the supplied function node, rewrite all the return expressions so that: * return foo(); * becomes: * foo(); return; * Useless return will be removed later by the peephole optimization passes. */ private void rewriteReturns( final SimpleDefinitionFinder defFinder, Node fnNode) { Preconditions.checkState(fnNode.isFunction()); NodeUtil.visitPostOrder( fnNode.getLastChild(), new NodeUtil.Visitor() { @Override public void visit(Node node) { if (node.isReturn() && node.hasOneChild()) { boolean keepValue = NodeUtil.mayHaveSideEffects( node.getFirstChild(), compiler); if (!keepValue) { defFinder.removeReferences(node.getFirstChild()); } Node result = node.removeFirstChild(); if (keepValue) { node.getParent().addChildBefore( IR.exprResult(result).srcref(result), node); } compiler.reportCodeChange(); } } }, new NodeUtil.MatchShallowStatement()); } /** * Determines if the name node acts as the function name in a call expression. */ private static boolean isCall(UseSite site) { Node node = site.node; Node parent = node.getParent(); return (parent.getFirstChild() == node) && parent.isCall(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PreprocessorSymbolTable.java0000644000175000017500000000577012115204405030334 0ustar apoapo/* * Copyright 2011 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.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.SimpleReference; import com.google.javascript.rhino.jstype.SimpleSlot; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSymbolTable; import java.util.Collections; import java.util.Map; /** * A symbol table for references that are removed by preprocessor passes * (like {@code ProcessClosurePrimitives}). * * @author nicksantos@google.com (Nick Santos) */ final class PreprocessorSymbolTable implements StaticScope, StaticSymbolTable { /** * All preprocessor symbols are globals. */ private final Map symbols = Maps.newHashMap(); private final Multimap refs = ArrayListMultimap.create(); private final Node root; PreprocessorSymbolTable(Node root) { this.root = root; } @Override public Node getRootNode() { return root; } @Override public JSType getTypeOfThis() { return null; } @Override public StaticScope getParentScope() { return null; } @Override public SimpleSlot getSlot(String name) { return symbols.get(name); } @Override public SimpleSlot getOwnSlot(String name) { return getSlot(name); } @Override public Iterable getReferences(SimpleSlot symbol) { return Collections.unmodifiableCollection(refs.get(symbol.getName())); } @Override public Iterable getAllSymbols() { return Collections.unmodifiableCollection(symbols.values()); } @Override public StaticScope getScope(SimpleSlot slot) { return this; } void addReference(Node node) { String name = node.getQualifiedName(); Preconditions.checkNotNull(name); if (!symbols.containsKey(name)) { symbols.put(name, new SimpleSlot(name, null, true)); } refs.put(name, new Reference(symbols.get(name), node)); } static final class Reference extends SimpleReference { Reference(SimpleSlot symbol, Node node) { super(symbol, node); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/RenameProperties.java0000644000175000017500000004721712115204405026776 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.NodeTraversal.ScopedCallback; import com.google.javascript.jscomp.graph.Graph.GraphEdge; import com.google.javascript.jscomp.graph.LinkedUndirectedGraph; import com.google.javascript.jscomp.graph.UndiGraph; import com.google.javascript.jscomp.graph.UndiGraph.UndiGraphEdge; import com.google.javascript.jscomp.graph.UndiGraph.UndiGraphNode; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.annotation.Nullable; /** * RenameProperties renames properties (including methods) of all JavaScript * objects. This includes prototypes, functions, object literals, etc. * *

      If provided a VariableMap of previously used names, it tries to reuse * those names. * *

      To prevent a property from getting renamed you may extern it (add it to * your externs file) or put it in quotes. * *

      To avoid run-time JavaScript errors, use quotes when accessing properties * that are defined using quotes. * *

       *   var a = {'myprop': 0}, b = a['myprop'];  // correct
       *   var x = {'myprop': 0}, y = x.myprop;     // incorrect
       * 
      * */ class RenameProperties implements CompilerPass { private final AbstractCompiler compiler; private final boolean generatePseudoNames; /** Property renaming map from a previous compilation. */ private final VariableMap prevUsedPropertyMap; private final List stringNodesToRename = new ArrayList(); private final Map callNodeToParentMap = new HashMap(); private final char[] reservedCharacters; // Map from property name to Property object private final Map propertyMap = new HashMap(); /** * A graph of property affinity information. * * Suppose property X and Y are access in the same function N times. * * The graph would have X -> Y with the edge of N. */ private final UndiGraph affinityGraph; // Property names that don't get renamed private final Set externedNames = new HashSet( Arrays.asList("prototype")); // Names to which properties shouldn't be renamed, to avoid name conflicts private final Set quotedNames = new HashSet(); private static final Comparator FREQUENCY_COMPARATOR = new Comparator() { @Override public int compare(Property p1, Property p2) { /** * First a frequently used names would always be picked first. */ if (p1.numOccurrences != p2.numOccurrences) { return p2.numOccurrences - p1.numOccurrences; /** * If both properties are used equally frequent. We'll let the property * with a high affinity score get a name first. * * see #computeAffinityScores() for how the score is computed. */ } else if (p1.affinityScore != p2.affinityScore) { return p2.affinityScore - p1.affinityScore; } /** * Finally, for determinism, we compare them based on the old name. */ return p1.oldName.compareTo(p2.oldName); } }; /** * The name of a special function that this pass replaces. It takes one * argument: a string literal containing one or more dot-separated JS * identifiers. This pass will replace them as though they were JS property * references. Here are two examples: * JSCompiler_renameProperty('propertyName') -> 'jYq' * JSCompiler_renameProperty('myProp.nestedProp.innerProp') -> 'e4.sW.C$' */ static final String RENAME_PROPERTY_FUNCTION_NAME = "JSCompiler_renameProperty"; static final DiagnosticType BAD_CALL = DiagnosticType.error( "JSC_BAD_RENAME_PROPERTY_FUNCTION_NAME_CALL", "Bad " + RENAME_PROPERTY_FUNCTION_NAME + " call - " + "argument must be a string literal"); static final DiagnosticType BAD_ARG = DiagnosticType.error( "JSC_BAD_RENAME_PROPERTY_FUNCTION_NAME_ARG", "Bad " + RENAME_PROPERTY_FUNCTION_NAME + " argument - " + "'{0}' is not a valid JavaScript identifier"); /** * Creates an instance. * * @param compiler The JSCompiler * @param affinity Optimize for affinity information. * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead * of compact obfuscated names. This is used for debugging. */ RenameProperties(AbstractCompiler compiler, boolean affinity, boolean generatePseudoNames) { this(compiler, affinity, generatePseudoNames, null, null); } /** * Creates an instance. * * @param compiler The JSCompiler. * @param affinity Optimize for affinity information. * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead * of compact obfuscated names. This is used for debugging. * @param prevUsedPropertyMap The property renaming map used in a previous * compilation. */ RenameProperties(AbstractCompiler compiler, boolean affinity, boolean generatePseudoNames, VariableMap prevUsedPropertyMap) { this(compiler, affinity, generatePseudoNames, prevUsedPropertyMap, null); } /** * Creates an instance. * * @param compiler The JSCompiler. * @param affinity Optimize for affinity information. * @param generatePseudoNames Generate pseudo names. e.g foo -> $foo$ instead * of compact obfuscated names. This is used for debugging. * @param prevUsedPropertyMap The property renaming map used in a previous * compilation. * @param reservedCharacters If specified these characters won't be used in * generated names */ RenameProperties(AbstractCompiler compiler, boolean affinity, boolean generatePseudoNames, VariableMap prevUsedPropertyMap, @Nullable char[] reservedCharacters) { this.compiler = compiler; this.generatePseudoNames = generatePseudoNames; this.prevUsedPropertyMap = prevUsedPropertyMap; this.reservedCharacters = reservedCharacters; if (affinity) { this.affinityGraph = LinkedUndirectedGraph.createWithoutAnnotations(); } else { this.affinityGraph = null; } } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, externs, new ProcessExterns()); NodeTraversal.traverse(compiler, root, new ProcessProperties()); Set reservedNames = new HashSet(externedNames.size() + quotedNames.size()); reservedNames.addAll(externedNames); reservedNames.addAll(quotedNames); // First, try and reuse as many property names from the previous compilation // as possible. if (prevUsedPropertyMap != null) { reusePropertyNames(reservedNames, propertyMap.values()); } compiler.addToDebugLog("JS property assignments:"); if (affinityGraph != null) { computeAffinityScores(); } // Assign names, sorted by descending frequency to minimize code size. Set propsByFreq = new TreeSet(FREQUENCY_COMPARATOR); propsByFreq.addAll(propertyMap.values()); generateNames(propsByFreq, reservedNames); // Update the string nodes. boolean changed = false; for (Node n : stringNodesToRename) { String oldName = n.getString(); Property p = propertyMap.get(oldName); if (p != null && p.newName != null) { Preconditions.checkState(oldName.equals(p.oldName)); n.setString(p.newName); changed = changed || !p.newName.equals(oldName); } } // Update the call nodes. for (Map.Entry nodeEntry : callNodeToParentMap.entrySet()) { Node parent = nodeEntry.getValue(); Node firstArg = nodeEntry.getKey().getFirstChild().getNext(); StringBuilder sb = new StringBuilder(); for (String oldName : firstArg.getString().split("[.]")) { Property p = propertyMap.get(oldName); String replacement; if (p != null && p.newName != null) { Preconditions.checkState(oldName.equals(p.oldName)); replacement = p.newName; } else { replacement = oldName; } if (sb.length() > 0) { sb.append('.'); } sb.append(replacement); } parent.replaceChild(nodeEntry.getKey(), IR.string(sb.toString())); changed = true; } if (changed) { compiler.reportCodeChange(); } compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED_OBFUSCATED); } /** * Runs through the list of properties and renames as many as possible with * names from the previous compilation. Also, updates reservedNames with the * set of reused names. * @param reservedNames Reserved names to use during renaming. * @param allProps Properties to rename. */ private void reusePropertyNames(Set reservedNames, Collection allProps) { for (Property prop : allProps) { // Check if this node can reuse a name from a previous compilation - if // it can set the newName for the property too. String prevName = prevUsedPropertyMap.lookupNewName(prop.oldName); if (!generatePseudoNames && prevName != null) { // We can reuse prevName if it's not reserved. if (reservedNames.contains(prevName)) { continue; } prop.newName = prevName; reservedNames.add(prevName); } } } /** * A X property gets an affinity score: * * score = sum (# of times X appears Y * frequency(Y)) for all Y where * frequency(Y) > frequency (X). * * This way a property would have a name closer to all high frequency names. * Also two property of the same frequency would have very close names if * they always appear together. */ private void computeAffinityScores() { for (Property p : propertyMap.values()) { UndiGraphNode node = affinityGraph.getUndirectedGraphNode(p); int affinityScore = 0; for (Iterator> edgeIterator = node.getNeighborEdgesIterator(); edgeIterator.hasNext();) { UndiGraphEdge edge = edgeIterator.next(); affinityScore += edge.getValue().affinity + (node == edge.getNodeA() ? edge.getNodeB().getValue().numOccurrences : edge.getNodeA().getValue().numOccurrences); } node.getValue().affinityScore = affinityScore; } } /** * Generates new names for properties. * * @param props Properties to generate new names for * @param reservedNames A set of names to which properties should not be * renamed */ private void generateNames(Set props, Set reservedNames) { NameGenerator nameGen = new NameGenerator( reservedNames, "", reservedCharacters); for (Property p : props) { if (generatePseudoNames) { p.newName = "$" + p.oldName + "$"; } else { // If we haven't already given this property a reusable name. if (p.newName == null) { p.newName = nameGen.generateNextName(); } } reservedNames.add(p.newName); compiler.addToDebugLog(p.oldName + " => " + p.newName); } } /** * Gets the property renaming map (the "answer key"). * * @return A mapping from original names to new names */ VariableMap getPropertyMap() { ImmutableMap.Builder map = ImmutableMap.builder(); for (Property p : propertyMap.values()) { if (p.newName != null) { map.put(p.oldName, p.newName); } } return new VariableMap(map.build()); } // ------------------------------------------------------------------------- /** * A traversal callback that collects externed property names. */ private class ProcessExterns extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: Node dest = n.getFirstChild().getNext(); if (dest.isString()) { externedNames.add(dest.getString()); } break; case Token.OBJECTLIT: for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { externedNames.add(child.getString()); } break; } } } // ------------------------------------------------------------------------- /** * A traversal callback that collects property names and counts how * frequently each property name occurs. */ private class ProcessProperties extends AbstractPostOrderCallback implements ScopedCallback { private Set currentHighAffinityProperties = null; @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.GETPROP: Node propNode = n.getFirstChild().getNext(); if (propNode.isString()) { maybeMarkCandidate(propNode); } break; case Token.OBJECTLIT: for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!key.isQuotedString()) { maybeMarkCandidate(key); } else { // Ensure that we never rename some other property in a way // that could conflict with this quoted key. quotedNames.add(key.getString()); } } break; case Token.GETELEM: // If this is a quoted property access (e.g. x['myprop']), we need to // ensure that we never rename some other property in a way that // could conflict with this quoted name. Node child = n.getLastChild(); if (child != null && child.isString()) { quotedNames.add(child.getString()); } break; case Token.CALL: // We replace a JSCompiler_renameProperty function call with a string // containing the renamed property. Node fnName = n.getFirstChild(); if (fnName.isName() && RENAME_PROPERTY_FUNCTION_NAME.equals(fnName.getString())) { callNodeToParentMap.put(n, parent); countCallCandidates(t, n); } break; case Token.FUNCTION: // We eliminate any stub implementations of JSCompiler_renameProperty // that we encounter. if (NodeUtil.isFunctionDeclaration(n)) { String name = n.getFirstChild().getString(); if (RENAME_PROPERTY_FUNCTION_NAME.equals(name)) { if (parent.isExprResult()) { parent.detachFromParent(); } else { parent.removeChild(n); } compiler.reportCodeChange(); } } else if (parent.isName() && RENAME_PROPERTY_FUNCTION_NAME.equals(parent.getString())) { Node varNode = parent.getParent(); if (varNode.isVar()) { varNode.removeChild(parent); if (!varNode.hasChildren()) { varNode.detachFromParent(); } compiler.reportCodeChange(); } } break; } } /** * If a property node is eligible for renaming, stashes a reference to it * and increments the property name's access count. * * @param n The STRING node for a property */ private void maybeMarkCandidate(Node n) { String name = n.getString(); if (!externedNames.contains(name)) { stringNodesToRename.add(n); countPropertyOccurrence(name); } } /** * Counts references to property names that occur in a special function * call. * * @param callNode The CALL node for a property * @param t The traversal */ private void countCallCandidates(NodeTraversal t, Node callNode) { Node firstArg = callNode.getFirstChild().getNext(); if (!firstArg.isString()) { t.report(callNode, BAD_CALL); return; } for (String name : firstArg.getString().split("[.]")) { if (!TokenStream.isJSIdentifier(name)) { t.report(callNode, BAD_ARG, name); continue; } if (!externedNames.contains(name)) { countPropertyOccurrence(name); } } } /** * Increments the occurrence count for a property name. * * @param name The property name */ private void countPropertyOccurrence(String name) { Property prop = propertyMap.get(name); if (prop == null) { prop = new Property(name); propertyMap.put(name, prop); if (affinityGraph != null) { affinityGraph.createNode(prop); } } prop.numOccurrences++; if (currentHighAffinityProperties != null) { currentHighAffinityProperties.add(prop); } } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope() && t.getScope().getParent().isGlobal()) { currentHighAffinityProperties = Sets.newHashSet(); } } @Override public void exitScope(NodeTraversal t) { if (affinityGraph == null) { return; } if (!t.inGlobalScope() && t.getScope().getParent().isGlobal()) { for (Property p1 : currentHighAffinityProperties) { for (Property p2 : currentHighAffinityProperties) { if (p1.oldName.compareTo(p2.oldName) < 0) { GraphEdge edge = affinityGraph.getFirstEdge(p1, p2); if (edge == null) { affinityGraph.connect(p1, new PropertyAffinity(1), p2); } else { edge.getValue().increase(); } } } } currentHighAffinityProperties = null; } } } // ------------------------------------------------------------------------- /** * Encapsulates the information needed for renaming a property. */ private class Property { final String oldName; String newName; int numOccurrences; int affinityScore = 0; Property(String name) { this.oldName = name; } } private class PropertyAffinity { // This will forever be zero if no affinity information was gathered. private int affinity = 0; private PropertyAffinity(int affinity) { this.affinity = affinity; } private void increase() { affinity++; } } } ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallback.javaclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/GatherSideEffectSubexpressionsCallb0000644000175000017500000002701212115204405031630 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CodingConvention.SubclassRelationship; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.List; import java.util.Set; /** * Callback that gathers subexpressions that may have side effects * and appends copies of those subexpressions to the replacements * list. In the case of branching subexpressions, it simplifies the * subexpression before adding it to the replacement list. * */ class GatherSideEffectSubexpressionsCallback implements Callback { /** * Used by GatherSideEffectSubexpressionsCallback to notify client * code about side effect expressions that should be kept. */ interface SideEffectAccumulator { /** * Returns true if the "mixin" and "inherits" function calls * should be treated as if they had side effects. */ boolean classDefiningCallsHaveSideEffects(); /** * Adds subtree to the list of nodes that have side effects. * * @param original - root of the tree. */ void keepSubTree(Node original); /** * Simplifies a subtree whose root node is an AND or OR expression * and adds the resulting subtree to the list of nodes that have * side effects. * * @param original - root of the and/or expression. */ void keepSimplifiedShortCircuitExpression(Node original); /** * Simplifies a subtree whose root node is a HOOK expression * and adds the resulting subtree to the list of nodes that have * side effects. * * @param hook - root of the hook expression. * @param thenHasSideEffects - then branch has side effects * @param elseHasSideEffects - else branch has side effects */ void keepSimplifiedHookExpression(Node hook, boolean thenHasSideEffects, boolean elseHasSideEffects); } /** * Populates the provided replacement list by appending copies of * subtrees that have side effects. * * It is OK if this class tears up the original tree, because * we're going to throw the tree out anyway. */ static final class GetReplacementSideEffectSubexpressions implements SideEffectAccumulator { private final AbstractCompiler compiler; private final List replacements; /** * Creates the accumulator. * * @param compiler - the AbstractCompiler * @param replacements - list to accumulate into */ GetReplacementSideEffectSubexpressions(AbstractCompiler compiler, List replacements) { this.compiler = compiler; this.replacements = replacements; } @Override public boolean classDefiningCallsHaveSideEffects() { return true; } @Override public void keepSubTree(Node original) { if (original.getParent() != null) { original.detachFromParent(); } replacements.add(original); } @Override public void keepSimplifiedShortCircuitExpression(Node original) { Preconditions.checkArgument( (original.isAnd()) || (original.isOr()), "Expected: AND or OR, Got: %s", Token.name(original.getType())); Node left = original.getFirstChild(); Node right = left.getNext(); Node simplifiedRight = simplifyShortCircuitBranch(right); original.detachChildren(); original.addChildToBack(left); original.addChildToBack(simplifiedRight); keepSubTree(original); } @Override public void keepSimplifiedHookExpression(Node hook, boolean thenHasSideEffects, boolean elseHasSideEffects) { Preconditions.checkArgument(hook.isHook(), "Expected: HOOK, Got: %s", Token.name(hook.getType())); Node condition = hook.getFirstChild(); Node thenBranch = condition.getNext(); Node elseBranch = thenBranch.getNext(); if (thenHasSideEffects && elseHasSideEffects) { hook.detachChildren(); hook.addChildToBack(condition); hook.addChildToBack(simplifyShortCircuitBranch(thenBranch)); hook.addChildToBack(simplifyShortCircuitBranch(elseBranch)); keepSubTree(hook); } else if (thenHasSideEffects || elseHasSideEffects) { int type = thenHasSideEffects ? Token.AND : Token.OR; Node body = thenHasSideEffects ? thenBranch : elseBranch; Node simplified = new Node( type, condition.detachFromParent(), simplifyShortCircuitBranch(body)) .copyInformationFrom(hook); keepSubTree(simplified); } else { throw new IllegalArgumentException( "keepSimplifiedHookExpression must keep at least 1 branch"); } } private Node simplifyShortCircuitBranch(Node node) { List parts = Lists.newArrayList(); NodeTraversal.traverse( compiler, node, new GatherSideEffectSubexpressionsCallback( compiler, new GetReplacementSideEffectSubexpressions(compiler, parts))); Node ret = null; for (Node part : parts) { if (ret != null) { ret = IR.comma(ret, part).srcref(node); } else { ret = part; } } if (ret == null) { throw new IllegalArgumentException( "expected at least one side effect subexpression in short " + "circuit branch."); } return ret; } } private static final Set FORBIDDEN_TYPES = ImmutableSet.of( Token.BLOCK, Token.SCRIPT, Token.VAR, Token.EXPR_RESULT, Token.RETURN); private final AbstractCompiler compiler; private final SideEffectAccumulator accumulator; /** * @param compiler - AbstractCompiler object * @param accumulator - object that will accumulate roots of * subtrees that have side effects. */ GatherSideEffectSubexpressionsCallback(AbstractCompiler compiler, SideEffectAccumulator accumulator) { this.compiler = compiler; this.accumulator = accumulator; } /** * Determines if a call defines a class inheritance or mixing * relation, according to the current coding convention. */ private boolean isClassDefiningCall(Node callNode) { SubclassRelationship classes = compiler.getCodingConvention().getClassesDefinedByCall(callNode); return classes != null; } /** * Computes the list of subtrees whose root nodes have side effects. * *

      If the current subtree's root has side effects this method should * call accumulator.keepSubTree and return 'false' to add the * subtree to the result list and avoid avoid traversing the nodes children. * *

      Branching nodes whose then or else branch contain side effects * must be simplified by doing a recursive traversal; this method * should call the appropriate accumulator 'keepSimplified' method * and return 'false' to stop the regular traversal. */ @Override public boolean shouldTraverse( NodeTraversal traversal, Node node, Node parent) { if (FORBIDDEN_TYPES.contains(node.getType()) || NodeUtil.isControlStructure(node)) { throw new IllegalArgumentException( Token.name(node.getType()) + " nodes are not supported."); } // Do not recurse into nested functions. if (node.isFunction()) { return false; } // simplify and maybe keep hook expression. if (node.isHook()) { return processHook(node); } // simplify and maybe keep AND/OR expression. if ((node.isAnd()) || (node.isOr())) { return processShortCircuitExpression(node); } if (!NodeUtil.nodeTypeMayHaveSideEffects(node, compiler)) { return true; } else { // Node type suggests that the expression has side effects. if (node.isCall()) { return processFunctionCall(node); } else if (node.isNew()) { return processConstructorCall(node); } else { accumulator.keepSubTree(node); return false; } } } /** * Processes an AND or OR expression. * * @return true to continue traversal, false otherwise */ boolean processShortCircuitExpression(Node node) { Preconditions.checkArgument( (node.isAnd()) || (node.isOr()), "Expected: AND or OR, Got: %s", Token.name(node.getType())); // keep whole expression if RHS of the branching expression // contains a call. Node left = node.getFirstChild(); Node right = left.getNext(); if (NodeUtil.mayHaveSideEffects(right, compiler)) { accumulator.keepSimplifiedShortCircuitExpression(node); return false; } else { return true; } } /** * Processes a HOOK expression. * * @return true to continue traversal, false otherwise */ boolean processHook(Node node) { Preconditions.checkArgument(node.isHook(), "Expected: HOOK, Got: %s", Token.name(node.getType())); Node condition = node.getFirstChild(); Node ifBranch = condition.getNext(); Node elseBranch = ifBranch.getNext(); boolean thenHasSideEffects = NodeUtil.mayHaveSideEffects( ifBranch, compiler); boolean elseHasSideEffects = NodeUtil.mayHaveSideEffects( elseBranch, compiler); if (thenHasSideEffects || elseHasSideEffects) { accumulator.keepSimplifiedHookExpression( node, thenHasSideEffects, elseHasSideEffects); return false; } else { return true; } } /** * Processes a CALL expression. * * @return true to continue traversal, false otherwise */ boolean processFunctionCall(Node node) { Preconditions.checkArgument(node.isCall(), "Expected: CALL, Got: %s", Token.name(node.getType())); // Calls to functions that are known to be "pure" have no side // effects. Node functionName = node.getFirstChild(); if (functionName.isName() || functionName.isGetProp()) { if (!accumulator.classDefiningCallsHaveSideEffects() && isClassDefiningCall(node)) { return true; } } if (!NodeUtil.functionCallHasSideEffects(node)) { return true; } accumulator.keepSubTree(node); return false; } /** * Processes a NEW expression. * * @return true to continue traversal, false otherwise */ boolean processConstructorCall(Node node) { Preconditions.checkArgument(node.isNew(), "Expected: NEW, Got: %s", Token.name(node.getType())); // Calls to constructors that are known to be "pure" have no // side effects. if (!NodeUtil.constructorCallHasSideEffects(node)) { return true; } accumulator.keepSubTree(node); return false; } @Override public void visit(NodeTraversal traversal, Node node, Node parent) {} } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/ExternExportsPass.java0000644000175000017500000004214612115204405027167 0ustar apoapo/* * 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.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * Creates an externs file containing all exported symbols and properties * for later consumption. * */ final class ExternExportsPass extends NodeTraversal.AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE = DiagnosticType.warning( "JSC_EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE", "Unable to determine type of parameter {0} for exported function {1}"); static final DiagnosticType EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE = DiagnosticType.warning( "JSC_EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE", "Unable to determine return type for exported function {0}"); /** The exports found. */ private final List exports; /** A map of all assigns to their parent nodes. */ private final Map definitionMap; /** The parent compiler. */ private final AbstractCompiler compiler; /** The AST root which holds the externs generated. */ private final Node externsRoot; /** A mapping of internal paths to exported paths. */ private final Map mappedPaths; /** A list of exported paths. */ private final Set alreadyExportedPaths; /** A list of function names used to export symbols. */ private List exportSymbolFunctionNames; /** A list of function names used to export properties. */ private List exportPropertyFunctionNames; private abstract class Export { protected final String symbolName; protected final Node value; Export(String symbolName, Node value) { this.symbolName = symbolName; this.value = value; } /** * Generates the externs representation of this export and appends * it to the externsRoot AST. */ void generateExterns() { appendExtern(getExportedPath(), getValue(value)); } /** * Returns the path exported by this export. */ abstract String getExportedPath(); /** * Appends the exported function and all paths necessary for the path to be * declared. For example, for a property "a.b.c", the initializers for * paths "a", "a.b" will be appended (if they have not already) and a.b.c * will be initialized with the exported version of the function: *

           * var a = {};
           * a.b = {};
           * a.b.c = function(x,y) { }
           * 
      */ void appendExtern(String path, Node valueToExport) { List pathPrefixes = computePathPrefixes(path); for (int i = 0; i < pathPrefixes.size(); ++i) { String pathPrefix = pathPrefixes.get(i); /* The complete path (the last path prefix) must be emitted and * it gets initialized to the externed version of the value. */ boolean isCompletePathPrefix = (i == pathPrefixes.size() - 1); boolean skipPathPrefix = pathPrefix.endsWith(".prototype") || (alreadyExportedPaths.contains(pathPrefix) && !isCompletePathPrefix); if (!skipPathPrefix) { Node initializer; /* Namespaces get initialized to {}, functions to * externed versions of their value, and if we can't * figure out where the value came from we initialize * it to {}. * * Since externs are always exported in sorted order, * we know that if we export a.b = function() {} and later * a.b.c = function then a.b will always be in alreadyExportedPaths * when we emit a.b.c and thus we will never overwrite the function * exported for a.b with a namespace. */ if (isCompletePathPrefix && valueToExport != null) { if (valueToExport.isFunction()) { initializer = createExternFunction(valueToExport); } else { Preconditions.checkState(valueToExport.isObjectLit()); initializer = createExternObjectLit(valueToExport); } } else { initializer = IR.empty(); } appendPathDefinition(pathPrefix, initializer); } } } /** * Computes a list of the path prefixes constructed from the components * of the path. *
           * E.g., if the path is:
           *      "a.b.c"
           * then then path prefixes will be
           *    ["a","a.b","a.b.c"]:
           * 
      */ private List computePathPrefixes(String path) { List pieces = Lists.newArrayList(path.split("\\.")); List pathPrefixes = Lists.newArrayList(); for (int i = 0; i < pieces.size(); i++) { pathPrefixes.add(Joiner.on(".").join(Iterables.limit(pieces, i + 1))); } return pathPrefixes; } private void appendPathDefinition(String path, Node initializer) { Node pathDefinition; if (!path.contains(".")) { if (initializer.isEmpty()) { pathDefinition = IR.var(IR.name(path)); } else { pathDefinition = NodeUtil.newVarNode(path, initializer); } } else { Node qualifiedPath = NodeUtil.newQualifiedNameNode( compiler.getCodingConvention(), path); if (initializer.isEmpty()) { pathDefinition = NodeUtil.newExpr(qualifiedPath); } else { pathDefinition = NodeUtil.newExpr( IR.assign(qualifiedPath, initializer)); } } externsRoot.addChildToBack(pathDefinition); alreadyExportedPaths.add(path); } /** * Given a function to export, create the empty function that * will be put in the externs file. This extern function should have * the same type as the original function and the same parameter * name but no function body. * * We create a warning here if the the function to export is missing * parameter or return types. */ private Node createExternFunction(Node exportedFunction) { Node paramList = NodeUtil.getFunctionParameters(exportedFunction) .cloneTree(); Node externFunction = IR.function(IR.name(""), paramList, IR.block()); checkForFunctionsWithUnknownTypes(exportedFunction); externFunction.setJSType(exportedFunction.getJSType()); return externFunction; } /** * Given an object literal to export, create an object lit with all its * string properties. We don't care what the values of those properties * are because they are not checked. */ private Node createExternObjectLit(Node exportedObjectLit) { Node lit = IR.objectlit(); lit.setJSType(exportedObjectLit.getJSType()); // This is an indirect way of telling the typed code generator // "print the type of this" lit.setJSDocInfo(new JSDocInfo()); int index = 1; for (Node child = exportedObjectLit.getFirstChild(); child != null; child = child.getNext()) { // TODO: handle getters or setters? if (child.isStringKey()) { lit.addChildToBack( IR.propdef( IR.stringKey(child.getString()), IR.number(index++))); } } return lit; } /** * Warn the user if there is an exported function for which a parameter * or return type is unknown. */ private void checkForFunctionsWithUnknownTypes(Node function) { Preconditions.checkArgument(function.isFunction()); FunctionType functionType = JSType.toMaybeFunctionType(function.getJSType()); if (functionType == null) { // No type information is available (CheckTypes was probably not run) // so just bail. return; } JSType returnType = functionType.getReturnType(); /* It is OK if a constructor doesn't have a return type */ if (!functionType.isConstructor() && (returnType == null || returnType.isUnknownType())) { reportUnknownReturnType(function); } /* We can't just use the function's type's getParameters() to get the * parameter nodes because the nodes returned from that method * do not have names or locations. Similarly, the function's AST parameter * nodes do not have JSTypes(). So we walk both lists of parameter nodes * in lock step getting parameter names from the first and types from the * second. */ Node astParameterIterator = NodeUtil.getFunctionParameters(function) .getFirstChild(); Node typeParameterIterator = functionType.getParametersNode() .getFirstChild(); while (astParameterIterator != null) { JSType parameterType = typeParameterIterator.getJSType(); if (parameterType == null || parameterType.isUnknownType()) { reportUnknownParameterType(function, astParameterIterator); } astParameterIterator = astParameterIterator.getNext(); typeParameterIterator = typeParameterIterator.getNext(); } } private void reportUnknownParameterType(Node function, Node parameter) { compiler.report(JSError.make(NodeUtil.getSourceName(function), parameter, CheckLevel.WARNING, EXPORTED_FUNCTION_UNKNOWN_PARAMETER_TYPE, NodeUtil.getFunctionName(function), parameter.getString())); } private void reportUnknownReturnType(Node function) { compiler.report(JSError.make(NodeUtil.getSourceName(function), function, CheckLevel.WARNING, EXPORTED_FUNCTION_UNKNOWN_RETURN_TYPE, NodeUtil.getFunctionName(function))); } /** * If the given value is a qualified name which refers * a function or object literal, the node is returned. Otherwise, * {@code null} is returned. */ protected Node getValue(Node qualifiedNameNode) { String qualifiedName = value.getQualifiedName(); if (qualifiedName == null) { return null; } Node definitionParent = definitionMap.get(qualifiedName); if (definitionParent == null) { return null; } Node definition; switch (definitionParent.getType()) { case Token.ASSIGN: definition = definitionParent.getLastChild(); break; case Token.VAR: definition = definitionParent.getLastChild().getLastChild(); break; default: return null; } if (!definition.isFunction() && !definition.isObjectLit()) { return null; } return definition; } } /** * A symbol export. */ private class SymbolExport extends Export { public SymbolExport(String symbolName, Node value) { super(symbolName, value); String qualifiedName = value.getQualifiedName(); if (qualifiedName != null) { mappedPaths.put(qualifiedName, symbolName); } } @Override String getExportedPath() { return symbolName; } } /** * A property export. */ private class PropertyExport extends Export { private final String exportPath; public PropertyExport(String exportPath, String symbolName, Node value) { super(symbolName, value); this.exportPath = exportPath; } @Override String getExportedPath() { // Find the longest path that has been mapped (if any). List pieces = Lists.newArrayList(exportPath.split("\\.")); for (int i = pieces.size(); i > 0; i--) { // Find the path of the current length. String cPath = Joiner.on(".").join(Iterables.limit(pieces, i)); // If this path is mapped, return the mapped path plus any remaining // pieces. if (mappedPaths.containsKey(cPath)) { String newPath = mappedPaths.get(cPath); if (i < pieces.size()) { newPath += "." + Joiner.on(".").join(Iterables.skip(pieces, i)); } return newPath + "." + symbolName; } } return exportPath + "." + symbolName; } } /** * Creates an instance. */ ExternExportsPass(AbstractCompiler compiler) { this.exports = Lists.newArrayList(); this.compiler = compiler; this.definitionMap = Maps.newHashMap(); this.externsRoot = IR.block(); this.externsRoot.setIsSyntheticBlock(true); this.alreadyExportedPaths = Sets.newHashSet(); this.mappedPaths = Maps.newHashMap(); initExportMethods(); } private void initExportMethods() { exportSymbolFunctionNames = Lists.newArrayList(); exportPropertyFunctionNames = Lists.newArrayList(); // From Closure: // goog.exportSymbol = function(publicName, symbol) // goog.exportProperty = function(object, publicName, symbol) CodingConvention convention = compiler.getCodingConvention(); exportSymbolFunctionNames.add(convention.getExportSymbolFunction()); exportPropertyFunctionNames.add(convention.getExportPropertyFunction()); // Another common one used inside google: exportSymbolFunctionNames.add("google_exportSymbol"); exportPropertyFunctionNames.add("google_exportProperty"); } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverse(root); // Sort by path length to ensure that the longer // paths (which may depend on the shorter ones) // come later. Set sorted = new TreeSet(new Comparator() { @Override public int compare(Export e1, Export e2) { return e1.getExportedPath().compareTo(e2.getExportedPath()); } }); sorted.addAll(exports); for (Export export : sorted) { export.generateExterns(); } } /** * Returns the generated externs. */ public String getGeneratedExterns() { CodePrinter.Builder builder = new CodePrinter.Builder(externsRoot) .setPrettyPrint(true) .setOutputTypes(true) .setTypeRegistry(compiler.getTypeRegistry()); return builder.build(); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: case Token.GETPROP: String name = n.getQualifiedName(); if (name == null) { return; } if (parent.isAssign() || parent.isVar()) { definitionMap.put(name, parent); } // Only handle function calls. This avoids assignments // that do not export items directly. if (!parent.isCall()) { return; } if (exportPropertyFunctionNames.contains(name)) { handlePropertyExport(parent); } if (exportSymbolFunctionNames.contains(name)) { handleSymbolExport(parent); } } } private void handleSymbolExport(Node parent) { // Ensure that we only check valid calls with the 2 arguments // (plus the GETPROP node itself). if (parent.getChildCount() != 3) { return; } Node thisNode = parent.getFirstChild(); Node nameArg = thisNode.getNext(); Node valueArg = nameArg.getNext(); // Confirm the arguments are the expected types. If they are not, // then we have an export that we cannot statically identify. if (!nameArg.isString()) { return; } // Add the export to the list. this.exports.add(new SymbolExport(nameArg.getString(), valueArg)); } private void handlePropertyExport(Node parent) { // Ensure that we only check valid calls with the 3 arguments // (plus the GETPROP node itself). if (parent.getChildCount() != 4) { return; } Node thisNode = parent.getFirstChild(); Node objectArg = thisNode.getNext(); Node nameArg = objectArg.getNext(); Node valueArg = nameArg.getNext(); // Confirm the arguments are the expected types. If they are not, // then we have an export that we cannot statically identify. if (!objectArg.isQualifiedName()) { return; } if (!nameArg.isString()) { return; } // Add the export to the list. this.exports.add( new PropertyExport(objectArg.getQualifiedName(), nameArg.getString(), valueArg)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java0000644000175000017500000007267312115204405030006 0ustar apoapo/* * Copyright 2004 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.base.Preconditions; import com.google.common.base.Predicates; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.TernaryValue; import javax.annotation.Nullable; /** * Peephole optimization to remove useless code such as IF's with false * guard conditions, comma operator left hand sides with no side effects, etc. * */ class PeepholeRemoveDeadCode extends AbstractPeepholeOptimization { // TODO(dcc): Some (all) of these can probably be better achieved // using the control flow graph (like CheckUnreachableCode). // There is an existing CFG pass (UnreachableCodeElimination) that // could be changed to use code from CheckUnreachableCode to do this. @Override Node optimizeSubtree(Node subtree) { switch(subtree.getType()) { case Token.ASSIGN: return tryFoldAssignment(subtree); case Token.COMMA: return tryFoldComma(subtree); case Token.SCRIPT: case Token.BLOCK: return tryOptimizeBlock(subtree); case Token.EXPR_RESULT: subtree = tryFoldExpr(subtree); return subtree; case Token.HOOK: return tryFoldHook(subtree); case Token.SWITCH: return tryOptimizeSwitch(subtree); case Token.IF: return tryFoldIf(subtree); case Token.WHILE: return tryFoldWhile(subtree); case Token.FOR: { Node condition = NodeUtil.getConditionExpression(subtree); if (condition != null) { tryFoldForCondition(condition); } } return tryFoldFor(subtree); case Token.DO: return tryFoldDo(subtree); case Token.TRY: return tryFoldTry(subtree); default: return subtree; } } /** * Remove try blocks without catch blocks and with empty or not * existent finally blocks. * Or, only leave the finally blocks if try body blocks are empty * @return the replacement node, if changed, or the original if not */ private Node tryFoldTry(Node n) { Preconditions.checkState(n.isTry()); Node body = n.getFirstChild(); Node catchBlock = body.getNext(); Node finallyBlock = catchBlock.getNext(); // Removes TRYs that had its CATCH removed and/or empty FINALLY. if (!catchBlock.hasChildren() && (finallyBlock == null || !finallyBlock.hasChildren())) { n.removeChild(body); n.getParent().replaceChild(n, body); reportCodeChange(); return body; } // Only leave FINALLYs if TRYs are empty if (!body.hasChildren()) { NodeUtil.redeclareVarsInsideBranch(catchBlock); if (finallyBlock != null) { n.removeChild(finallyBlock); n.getParent().replaceChild(n, finallyBlock); } else { n.getParent().removeChild(n); } reportCodeChange(); return finallyBlock; } return n; } /** * Try removing identity assignments * @return the replacement node, if changed, or the original if not */ private Node tryFoldAssignment(Node subtree) { Preconditions.checkState(subtree.isAssign()); Node left = subtree.getFirstChild(); Node right = subtree.getLastChild(); // Only names if (left.isName() && right.isName() && left.getString().equals(right.getString())) { subtree.getParent().replaceChild(subtree, right.detachFromParent()); reportCodeChange(); return right; } return subtree; } /** * Try folding EXPR_RESULT nodes by removing useless Ops and expressions. * @return the replacement node, if changed, or the original if not */ private Node tryFoldExpr(Node subtree) { Node result = trySimplifyUnusedResult(subtree.getFirstChild()); if (result == null) { Node parent = subtree.getParent(); // If the EXPR_RESULT no longer has any children, remove it as well. if (parent.isLabel()) { Node replacement = IR.block().srcref(subtree); parent.replaceChild(subtree, replacement); subtree = replacement; } else { subtree.detachFromParent(); subtree = null; } } return subtree; } /** * General cascading unused operation node removal. * @param n The root of the expression to simplify. * @return The replacement node, or null if the node was is not useful. */ private Node trySimplifyUnusedResult(Node n) { return trySimplifyUnusedResult(n, true); } /** * General cascading unused operation node removal. * @param n The root of the expression to simplify. * @param removeUnused If true, the node is removed from the AST if * it is not useful, otherwise it replaced with an EMPTY node. * @return The replacement node, or null if the node was is not useful. */ private Node trySimplifyUnusedResult(Node n, boolean removeUnused) { Node result = n; // Simplify the results of conditional expressions switch (n.getType()) { case Token.HOOK: Node trueNode = trySimplifyUnusedResult(n.getFirstChild().getNext()); Node falseNode = trySimplifyUnusedResult(n.getLastChild()); // If one or more of the conditional children were removed, // transform the HOOK to an equivalent operation: // x() ? foo() : 1 --> x() && foo() // x() ? 1 : foo() --> x() || foo() // x() ? 1 : 1 --> x() // x ? 1 : 1 --> null if (trueNode == null && falseNode != null) { n.setType(Token.OR); Preconditions.checkState(n.getChildCount() == 2); } else if (trueNode != null && falseNode == null) { n.setType(Token.AND); Preconditions.checkState(n.getChildCount() == 2); } else if (trueNode == null && falseNode == null) { result = trySimplifyUnusedResult(n.getFirstChild()); } else { // The structure didn't change. result = n; } break; case Token.AND: case Token.OR: // Try to remove the second operand from a AND or OR operations: // x() || f --> x() // x() && f --> x() Node conditionalResultNode = trySimplifyUnusedResult( n.getLastChild()); if (conditionalResultNode == null) { Preconditions.checkState(n.hasOneChild()); // The conditionally executed code was removed, so // replace the AND/OR with its LHS or remove it if it isn't useful. result = trySimplifyUnusedResult(n.getFirstChild()); } break; case Token.FUNCTION: // A function expression isn't useful if it isn't used, remove it and // don't bother to look at its children. result = null; break; case Token.COMMA: // We rewrite other operations as COMMA expressions (which will later // get split into individual EXPR_RESULT statement, if possible), so // we special case COMMA (we don't want to rewrite COMMAs as new COMMAs // nodes. Node left = trySimplifyUnusedResult(n.getFirstChild()); Node right = trySimplifyUnusedResult(n.getLastChild()); if (left == null && right == null) { result = null; } else if (left == null) { result = right; } else if (right == null){ result = left; } else { // The structure didn't change. result = n; } break; default: if (!nodeTypeMayHaveSideEffects(n)) { // This is the meat of this function. The node itself doesn't generate // any side-effects but preserve any side-effects in the children. Node resultList = null; for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); c = trySimplifyUnusedResult(c); if (c != null) { c.detachFromParent(); if (resultList == null) { // The first side-effect can be used stand-alone. resultList = c; } else { // Leave the side-effects in-place, simplifying it to a COMMA // expression. resultList = IR.comma(resultList, c).srcref(c); } } } result = resultList; } } // Fix up the AST, replace or remove the an unused node (if requested). if (n != result) { Node parent = n.getParent(); if (result == null) { if (removeUnused) { parent.removeChild(n); } else { result = IR.empty().srcref(n); parent.replaceChild(n, result); } } else { // A new COMMA expression may not have an existing parent. if (result.getParent() != null) { result.detachFromParent(); } n.getParent().replaceChild(n, result); } reportCodeChange(); } return result; } /** * Remove useless switches and cases. */ private Node tryOptimizeSwitch(Node n) { Preconditions.checkState(n.isSwitch()); Node defaultCase = tryOptimizeDefaultCase(n); // Removing cases when there exists a default case is not safe. if (defaultCase == null) { Node cond = n.getFirstChild(), prev = null, next = null, cur; for (cur = cond.getNext(); cur != null; cur = next) { next = cur.getNext(); if (!mayHaveSideEffects(cur.getFirstChild()) && isUselessCase(cur, prev)) { removeCase(n, cur); } else { prev = cur; } } // Optimize switches with constant condition if (NodeUtil.isLiteralValue(cond, false)) { Node caseLabel; TernaryValue caseMatches = TernaryValue.TRUE; // Remove cases until you find one that may match for (cur = cond.getNext(); cur != null; cur = next) { next = cur.getNext(); caseLabel = cur.getFirstChild(); caseMatches = PeepholeFoldConstants.evaluateComparison( Token.SHEQ, cond, caseLabel); if (caseMatches == TernaryValue.TRUE) { break; } else if (caseMatches == TernaryValue.UNKNOWN) { break; } else { removeCase(n, cur); } } if (caseMatches != TernaryValue.UNKNOWN) { Node block, lastStm; // Skip cases until you find one whose last stm is a break while (cur != null) { block = cur.getLastChild(); lastStm = block.getLastChild(); cur = cur.getNext(); if (lastStm != null && lastStm.isBreak()) { block.removeChild(lastStm); reportCodeChange(); break; } } // Remove any remaining cases for (; cur != null; cur = next) { next = cur.getNext(); removeCase(n, cur); } // If there is one case left, we may be able to fold it cur = cond.getNext(); if (cur != null && cur.getNext() == null) { block = cur.getLastChild(); if (!(NodeUtil.containsType(block, Token.BREAK, NodeUtil.MATCH_NOT_FUNCTION))) { cur.removeChild(block); n.getParent().replaceChild(n, block); reportCodeChange(); return block; } } } } } // Remove the switch if there are no remaining cases. if (n.hasOneChild()) { Node condition = n.removeFirstChild(); Node replacement = IR.exprResult(condition).srcref(n); n.getParent().replaceChild(n, replacement); reportCodeChange(); return replacement; } return null; } /** * @return the default case node or null if there is no default case or * if the default case is removed. */ private Node tryOptimizeDefaultCase(Node n) { Preconditions.checkState(n.isSwitch()); Node lastNonRemovable = n.getFirstChild(); // The switch condition // The first child is the switch conditions skip it when looking for cases. for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) { if (c.isDefaultCase()) { // Remove cases that fall-through to the default case Node caseToRemove = lastNonRemovable.getNext(); for (Node next; caseToRemove != c; caseToRemove = next) { next = caseToRemove.getNext(); removeCase(n, caseToRemove); } // Don't use the switch condition as the previous case. Node prevCase = (lastNonRemovable == n.getFirstChild()) ? null : lastNonRemovable; // Remove the default case if we can if (isUselessCase(c, prevCase)) { removeCase(n, c); return null; } return c; } else { Preconditions.checkState(c.isCase()); if (c.getLastChild().hasChildren() || mayHaveSideEffects(c.getFirstChild())) { lastNonRemovable = c; } } } return null; } /** * Remove the case from the switch redeclaring any variables declared in it. * @param caseNode The case to remove. */ private void removeCase(Node switchNode, Node caseNode) { NodeUtil.redeclareVarsInsideBranch(caseNode); switchNode.removeChild(caseNode); reportCodeChange(); } /** * The function assumes that when checking a CASE node there is no * DEFAULT node in the SWITCH. * @return Whether the CASE or DEFAULT block does anything useful. */ private boolean isUselessCase(Node caseNode, @Nullable Node previousCase) { Preconditions.checkState( previousCase == null || previousCase.getNext() == caseNode); // A case isn't useless can't be useless if a previous case falls // through to it unless it happens to be the last case in the switch. Node switchNode = caseNode.getParent(); if (switchNode.getLastChild() != caseNode && previousCase != null) { Node previousBlock = previousCase.getLastChild(); if (!previousBlock.hasChildren() || !isExit(previousBlock.getLastChild())) { return false; } } Node executingCase = caseNode; while (executingCase != null) { Preconditions.checkState(executingCase.isDefaultCase() || executingCase.isCase()); // We only expect a DEFAULT case if the case we are checking is the // DEFAULT case. Otherwise, we assume the DEFAULT case has already // been removed. Preconditions.checkState(caseNode == executingCase || !executingCase.isDefaultCase()); Node block = executingCase.getLastChild(); Preconditions.checkState(block.isBlock()); if (block.hasChildren()) { for (Node blockChild : block.children()) { // If this is a block with a labelless break, it is useless. switch (blockChild.getType()) { case Token.BREAK: // A break to a different control structure isn't useless. return blockChild.getFirstChild() == null; case Token.VAR: if (blockChild.hasOneChild() && blockChild.getFirstChild().getFirstChild() == null) { // Variable declarations without initializations are OK. continue; } return false; default: return false; } } } else { // Look at the fallthrough case executingCase = executingCase.getNext(); } } return true; } /** * @return Whether the node is an obvious control flow exit. */ private boolean isExit(Node n) { switch (n.getType()) { case Token.BREAK: case Token.CONTINUE: case Token.RETURN: case Token.THROW: return true; default: return false; } } private Node tryFoldComma(Node n) { // If the left side does nothing replace the comma with the result. Node parent = n.getParent(); Node left = n.getFirstChild(); Node right = left.getNext(); left = trySimplifyUnusedResult(left); if (left == null || !mayHaveSideEffects(left)) { // Fold it! n.removeChild(right); parent.replaceChild(n, right); reportCodeChange(); return right; } return n; } /** * Try removing unneeded block nodes and their useless children */ Node tryOptimizeBlock(Node n) { // Remove any useless children for (Node c = n.getFirstChild(); c != null; ) { Node next = c.getNext(); // save c.next, since 'c' may be removed if (!isUnremovableNode(c) && !mayHaveSideEffects(c)) { // TODO(johnlenz): determine what this is actually removing. Candidates // include: EMPTY nodes, control structures without children // (removing infinite loops), empty try blocks. What else? n.removeChild(c); // lazy kids reportCodeChange(); } else { tryOptimizeConditionalAfterAssign(c); } c = next; } if (n.isSyntheticBlock() || n.isScript() || n.getParent() == null) { return n; } // Try to remove the block. if (NodeUtil.tryMergeBlock(n)) { reportCodeChange(); return null; } return n; } /** * Some nodes unremovable node don't have side-effects. */ private boolean isUnremovableNode(Node n) { return (n.isBlock() && n.isSyntheticBlock()) || n.isScript(); } // TODO(johnlenz): Consider moving this to a separate peephole pass. /** * Attempt to replace the condition of if or hook immediately that is a * reference to a name that is assigned immediately before. */ private void tryOptimizeConditionalAfterAssign(Node n) { Node next = n.getNext(); // Look for patterns like the following and replace the if-condition with // a constant value so it can later be folded: // var a = /a/; // if (a) {foo(a)} // or // a = 0; // a ? foo(a) : c; // or // a = 0; // a || foo(a); // or // a = 0; // a && foo(a) // // TODO(johnlenz): This would be better handled by control-flow sensitive // constant propagation. As the other case that I want to handle is: // i=0; for(;i<0;i++){} // as right now nothing facilitates removing a loop like that. // This is here simply to remove the cruft left behind goog.userAgent and // similar cases. if (isSimpleAssignment(n) && isConditionalStatement(next)) { Node lhsAssign = getSimpleAssignmentName(n); Node condition = getConditionalStatementCondition(next); if (lhsAssign.isName() && condition.isName() && lhsAssign.getString().equals(condition.getString())) { Node rhsAssign = getSimpleAssignmentValue(n); TernaryValue value = NodeUtil.getImpureBooleanValue(rhsAssign); if (value != TernaryValue.UNKNOWN) { Node replacementConditionNode = NodeUtil.booleanNode(value.toBoolean(true)); condition.getParent().replaceChild(condition, replacementConditionNode); reportCodeChange(); } } } } /** * @return whether the node is a assignment to a simple name, or simple var * declaration with initialization. */ private boolean isSimpleAssignment(Node n) { // For our purposes we define a simple assignment to be a assignment // to a NAME node, or a VAR declaration with one child and a initializer. if (NodeUtil.isExprAssign(n) && n.getFirstChild().getFirstChild().isName()) { return true; } else if (n.isVar() && n.hasOneChild() && n.getFirstChild().getFirstChild() != null) { return true; } return false; } /** * @return The name being assigned to. */ private Node getSimpleAssignmentName(Node n) { Preconditions.checkState(isSimpleAssignment(n)); if (NodeUtil.isExprAssign(n)) { return n.getFirstChild().getFirstChild(); } else { // A var declaration. return n.getFirstChild(); } } /** * @return The value assigned in the simple assignment */ private Node getSimpleAssignmentValue(Node n) { Preconditions.checkState(isSimpleAssignment(n)); return n.getFirstChild().getLastChild(); } /** * @return Whether the node is a conditional statement. */ private boolean isConditionalStatement(Node n) { // We defined a conditional statement to be a IF or EXPR_RESULT rooted with // a HOOK, AND, or OR node. return n != null && (n.isIf() || isExprConditional(n)); } /** * @return Whether the node is a rooted with a HOOK, AND, or OR node. */ private boolean isExprConditional(Node n) { if (n.isExprResult()) { switch (n.getFirstChild().getType()) { case Token.HOOK: case Token.AND: case Token.OR: return true; } } return false; } /** * @return The condition of a conditional statement. */ private Node getConditionalStatementCondition(Node n) { if (n.isIf()) { return NodeUtil.getConditionExpression(n); } else { Preconditions.checkState(isExprConditional(n)); return n.getFirstChild().getFirstChild(); } } /** * Try folding IF nodes by removing dead branches. * @return the replacement node, if changed, or the original if not */ private Node tryFoldIf(Node n) { Preconditions.checkState(n.isIf()); Node parent = n.getParent(); Preconditions.checkNotNull(parent); int type = n.getType(); Node cond = n.getFirstChild(); Node thenBody = cond.getNext(); Node elseBody = thenBody.getNext(); // if (x) { .. } else { } --> if (x) { ... } if (elseBody != null && !mayHaveSideEffects(elseBody)) { n.removeChild(elseBody); elseBody = null; reportCodeChange(); } // if (x) { } else { ... } --> if (!x) { ... } if (!mayHaveSideEffects(thenBody) && elseBody != null) { n.removeChild(elseBody); n.replaceChild(thenBody, elseBody); Node notCond = new Node(Token.NOT); n.replaceChild(cond, notCond); notCond.addChildToFront(cond); cond = notCond; thenBody = cond.getNext(); elseBody = null; reportCodeChange(); } // if (x()) { } if (!mayHaveSideEffects(thenBody) && elseBody == null) { if (mayHaveSideEffects(cond)) { // x() has side effects, just leave the condition on its own. n.removeChild(cond); Node replacement = NodeUtil.newExpr(cond); parent.replaceChild(n, replacement); reportCodeChange(); return replacement; } else { // x() has no side effects, the whole tree is useless now. NodeUtil.removeChild(parent, n); reportCodeChange(); return null; } } // Try transforms that apply to both IF and HOOK. TernaryValue condValue = NodeUtil.getImpureBooleanValue(cond); if (condValue == TernaryValue.UNKNOWN) { return n; // We can't remove branches otherwise! } if (mayHaveSideEffects(cond)) { // Transform "if (a = 2) {x =2}" into "if (true) {a=2;x=2}" boolean newConditionValue = condValue == TernaryValue.TRUE; // Add an elseBody if it is needed. if (!newConditionValue && elseBody == null) { elseBody = IR.block().srcref(n); n.addChildToBack(elseBody); } Node newCond = NodeUtil.booleanNode(newConditionValue); n.replaceChild(cond, newCond); Node branchToKeep = newConditionValue ? thenBody : elseBody; branchToKeep.addChildToFront(IR.exprResult(cond).srcref(cond)); reportCodeChange(); cond = newCond; } boolean condTrue = condValue.toBoolean(true); if (n.getChildCount() == 2) { Preconditions.checkState(type == Token.IF); if (condTrue) { // Replace "if (true) { X }" with "X". Node thenStmt = n.getFirstChild().getNext(); n.removeChild(thenStmt); parent.replaceChild(n, thenStmt); reportCodeChange(); return thenStmt; } else { // Remove "if (false) { X }" completely. NodeUtil.redeclareVarsInsideBranch(n); NodeUtil.removeChild(parent, n); reportCodeChange(); return null; } } else { // Replace "if (true) { X } else { Y }" with X, or // replace "if (false) { X } else { Y }" with Y. Node trueBranch = n.getFirstChild().getNext(); Node falseBranch = trueBranch.getNext(); Node branchToKeep = condTrue ? trueBranch : falseBranch; Node branchToRemove = condTrue ? falseBranch : trueBranch; NodeUtil.redeclareVarsInsideBranch(branchToRemove); n.removeChild(branchToKeep); parent.replaceChild(n, branchToKeep); reportCodeChange(); return branchToKeep; } } /** * Try folding HOOK (?:) if the condition results of the condition is known. * @return the replacement node, if changed, or the original if not */ private Node tryFoldHook(Node n) { Preconditions.checkState(n.isHook()); Node parent = n.getParent(); Preconditions.checkNotNull(parent); Node cond = n.getFirstChild(); Node thenBody = cond.getNext(); Node elseBody = thenBody.getNext(); TernaryValue condValue = NodeUtil.getImpureBooleanValue(cond); if (condValue == TernaryValue.UNKNOWN) { // If the result nodes are equivalent, then one of the nodes can be // removed and it doesn't matter which. if (!areNodesEqualForInlining(thenBody, elseBody)) { return n; // We can't remove branches otherwise! } } // Transform "(a = 2) ? x =2 : y" into "a=2,x=2" n.detachChildren(); Node branchToKeep = condValue.toBoolean(true) ? thenBody : elseBody; Node replacement; if (mayHaveSideEffects(cond)) { replacement = IR.comma(cond, branchToKeep).srcref(n); } else { replacement = branchToKeep; } parent.replaceChild(n, replacement); reportCodeChange(); return replacement; } /** * Removes WHILEs that always evaluate to false. */ Node tryFoldWhile(Node n) { Preconditions.checkArgument(n.isWhile()); Node cond = NodeUtil.getConditionExpression(n); if (NodeUtil.getPureBooleanValue(cond) != TernaryValue.FALSE) { return n; } NodeUtil.redeclareVarsInsideBranch(n); NodeUtil.removeChild(n.getParent(), n); reportCodeChange(); return null; } /** * Removes FORs that always evaluate to false. */ Node tryFoldFor(Node n) { Preconditions.checkArgument(n.isFor()); // If this is a FOR-IN loop skip it. if (NodeUtil.isForIn(n)) { return n; } Node init = n.getFirstChild(); Node cond = init.getNext(); Node increment = cond.getNext(); if (!init.isEmpty() && !init.isVar()) { init = trySimplifyUnusedResult(init, false); } if (!increment.isEmpty()) { increment = trySimplifyUnusedResult(increment, false); } // There is an initializer skip it if (!n.getFirstChild().isEmpty()) { return n; } if (NodeUtil.getImpureBooleanValue(cond) != TernaryValue.FALSE) { return n; } NodeUtil.redeclareVarsInsideBranch(n); if (!mayHaveSideEffects(cond)) { NodeUtil.removeChild(n.getParent(), n); } else { Node statement = IR.exprResult(cond.detachFromParent()) .copyInformationFrom(cond); n.getParent().replaceChild(n, statement); } reportCodeChange(); return null; } /** * Removes DOs that always evaluate to false. This leaves the * statements that were in the loop in a BLOCK node. * The block will be removed in a later pass, if possible. */ Node tryFoldDo(Node n) { Preconditions.checkArgument(n.isDo()); Node cond = NodeUtil.getConditionExpression(n); if (NodeUtil.getImpureBooleanValue(cond) != TernaryValue.FALSE) { return n; } // TODO(johnlenz): The do-while can be turned into a label with // named breaks and the label optimized away (maybe). if (hasBreakOrContinue(n)) { return n; } Preconditions.checkState( NodeUtil.isControlStructureCodeBlock(n, n.getFirstChild())); Node block = n.removeFirstChild(); Node parent = n.getParent(); parent.replaceChild(n, block); if (mayHaveSideEffects(cond)) { Node condStatement = IR.exprResult(cond.detachFromParent()) .srcref(cond); parent.addChildAfter(condStatement, block); } reportCodeChange(); return n; } /** * */ boolean hasBreakOrContinue(Node n) { // TODO(johnlenz): This is overkill as named breaks may refer to outer // loops or labels, and any break my refer to an inner loop. // More generally, this check may be more expensive than we like. return NodeUtil.has( n, Predicates.or( new NodeUtil.MatchNodeType(Token.BREAK), new NodeUtil.MatchNodeType(Token.CONTINUE)), NodeUtil.MATCH_NOT_FUNCTION); } /** * Remove always true loop conditions. */ private void tryFoldForCondition(Node forCondition) { if (NodeUtil.getPureBooleanValue(forCondition) == TernaryValue.TRUE) { forCondition.getParent().replaceChild(forCondition, IR.empty()); reportCodeChange(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/CallGraph.java0000644000175000017500000006251712115204405025347 0ustar apoapo/* * 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.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.jscomp.NameReferenceGraph.Name; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.graph.DiGraph; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collection; import java.util.LinkedList; import java.util.Map; /** * A pass the uses a {@link DefinitionProvider} to compute a call graph for an * AST. * *

      A {@link CallGraph} connects {@link Function}s to {@link Callsite}s and * vice versa: each function in the graph links to the callsites it contains and * each callsite links to the functions it could call. Similarly, each callsite * links to the function that contains it and each function links to the * callsites that could call it. * *

      The callgraph is not precise. That is, a callsite may indicate it can * call a function when in fact it does not do so in the running program. * *

      The callgraph is also not complete: in some cases it may be unable to * determine some targets of a callsite. In this case, * Callsite.hasUnknownTarget() will return true. * *

      The CallGraph doesn't (currently) have functions for externally defined * functions; however, callsites that target externs will have hasExternTarget() * return true. * *

      TODO(dcc): Have CallGraph (optionally?) include functions for externs. * * @author dcc@google.com (Devin Coughlin) */ public class CallGraph implements CompilerPass { private AbstractCompiler compiler; /** * Maps an AST node (with type Token.CALL or Token.NEW) to a Callsite object. */ private Map callsitesByNode; /** Maps an AST node (with type Token.FUNCTION) to a Function object. */ private Map functionsByNode; /** * Will the call graph support looking up the callsites that could call a * given function? */ private boolean computeBackwardGraph; /** * Will the call graph support looking up the functions that a given callsite * can call? */ private boolean computeForwardGraph; /** * If true, then the callgraph will use NameReferenceGraph as a * definition provider; otherwise, use the faster SimpleDefinitionProvider. */ private boolean useNameReferenceGraph = false; /** Has the CallGraph already been constructed? */ private boolean alreadyRun = false; /** The name we give the main function. */ @VisibleForTesting public static final String MAIN_FUNCTION_NAME = "{main}"; /** * Represents the global function. Calling getBody() on this * function will yield the global script/block. * * TODO(dcc): having a single main function is somewhat misleading. Perhaps * it might be better to make CallGraph module aware and have one per * module? */ private Function mainFunction; /** * Creates a call graph object supporting the specified lookups. * * At least one (and possibly both) of computeForwardGraph and * computeBackwardGraph must be true. * * @param compiler The compiler * @param computeForwardGraph Should the call graph allow lookup of the target * functions a given callsite could call? * @param computeBackwardGraph Should the call graph allow lookup of the * callsites that could call a given function? */ public CallGraph(AbstractCompiler compiler, boolean computeForwardGraph, boolean computeBackwardGraph) { Preconditions.checkArgument(computeForwardGraph || computeBackwardGraph); this.compiler = compiler; this.computeForwardGraph = computeForwardGraph; this.computeBackwardGraph = computeBackwardGraph; callsitesByNode = Maps.newLinkedHashMap(); functionsByNode = Maps.newLinkedHashMap(); } /** * Creates a call graph object support both forward and backward lookups. */ public CallGraph(AbstractCompiler compiler) { this(compiler, true, true); } /** * Builds a call graph for the given externsRoot and jsRoot. * This method must not be called more than once per CallGraph instance. */ @Override public void process(Node externsRoot, Node jsRoot) { Preconditions.checkState(alreadyRun == false); DefinitionProvider definitionProvider = constructDefinitionProvider(externsRoot, jsRoot); createFunctionsAndCallsites(jsRoot, definitionProvider); fillInFunctionInformation(definitionProvider); alreadyRun = true; } /** * Returns the call graph Function object corresponding to the provided * AST Token.FUNCTION node, or null if no such object exists. */ public Function getFunctionForAstNode(Node functionNode) { Preconditions.checkArgument(functionNode.isFunction()); return functionsByNode.get(functionNode); } /** * Returns a Function object representing the "main" global function. */ public Function getMainFunction() { return mainFunction; } /** * Returns a collection of all functions (including the main function) * in the call graph. */ public Collection getAllFunctions() { return functionsByNode.values(); } /** * Finds a function with the given name. Throws an exception if * there are no functions or multiple functions with the name. This is * for testing purposes only. */ @VisibleForTesting public Function getUniqueFunctionWithName(final String desiredName) { Collection functions = Collections2.filter(getAllFunctions(), new Predicate() { @Override public boolean apply(Function function) { String functionName = function.getName(); // Anonymous functions will have null names, // so it is important to handle that correctly here if (functionName != null && desiredName != null) { return desiredName.equals(functionName); } else { return desiredName == functionName; } } }); if (functions.size() == 1) { return functions.iterator().next(); } else { throw new IllegalStateException("Found " + functions.size() + " functions with name " + desiredName); } } /** * Returns the call graph Callsite object corresponding to the provided * AST Token.CALL or Token.NEW node, or null if no such object exists. */ public Callsite getCallsiteForAstNode(Node callsiteNode) { Preconditions.checkArgument(callsiteNode.isCall() || callsiteNode.isNew()); return callsitesByNode.get(callsiteNode); } /** * Returns a collection of all callsites in the call graph. */ public Collection getAllCallsites() { return callsitesByNode.values(); } /** * Creates {@link Function}s and {@link Callsite}s in a single * AST traversal. */ private void createFunctionsAndCallsites(Node jsRoot, final DefinitionProvider provider) { // Create fake function representing global execution mainFunction = createFunction(jsRoot); NodeTraversal.traverse(compiler, jsRoot, new AbstractPostOrderCallback() { @Override public void visit(NodeTraversal t, Node n, Node parent) { int nodeType = n.getType(); if (nodeType == Token.CALL || nodeType == Token.NEW) { Callsite callsite = createCallsite(n); Node containingFunctionNode = t.getScopeRoot(); Function containingFunction = functionsByNode.get(containingFunctionNode); if (containingFunction == null) { containingFunction = createFunction(containingFunctionNode); } callsite.containingFunction = containingFunction; containingFunction.addCallsiteInFunction(callsite); connectCallsiteToTargets(callsite, provider); } else if (n.isFunction()) { if (!functionsByNode.containsKey(n)) { createFunction(n); } } } }); } /** * Create a Function object for given an Token.FUNCTION AST node. * * This is the bottleneck for Function creation: all Functions should * be created with this method. */ private Function createFunction(Node functionNode) { Function function = new Function(functionNode); functionsByNode.put(functionNode, function); return function; } private Callsite createCallsite(Node callsiteNode) { Callsite callsite = new Callsite(callsiteNode); callsitesByNode.put(callsiteNode, callsite); return callsite; } /** * Maps a Callsite to the Function(s) it could call * and each Function to the Callsite(s) that could call it. * * If the definitionProvider cannot determine the target of the Callsite, * the Callsite's hasUnknownTarget field is set to true. * * If the definitionProvider determines that the target of the Callsite * could be an extern-defined function, then the Callsite's hasExternTarget * field is set to true. * * @param callsite The callsite for which target functions should be found * @param definitionProvider The DefinitionProvider used to determine * targets of callsites. */ private void connectCallsiteToTargets(Callsite callsite, DefinitionProvider definitionProvider) { Collection definitions = lookupDefinitionsForTargetsOfCall(callsite.getAstNode(), definitionProvider); if (definitions == null) { callsite.hasUnknownTarget = true; } else { for (Definition definition : definitions) { if (definition.isExtern()) { callsite.hasExternTarget = true; } else { Node target = definition.getRValue(); if (target != null && target.isFunction()) { Function targetFunction = functionsByNode.get(target); if (targetFunction == null) { targetFunction = createFunction(target); } if (computeForwardGraph) { callsite.addPossibleTarget(targetFunction); } if (computeBackwardGraph) { targetFunction.addCallsitePossiblyTargetingFunction(callsite); } } else { callsite.hasUnknownTarget = true; } } } } } /** * Fills in function information (such as whether the function is ever * aliased or whether it is exposed to .call or .apply) using the * definition provider. * * We do this here, rather than when connecting the callgraph, to make sure * that we have correct information for all functions, rather than just * functions that are actually called. */ private void fillInFunctionInformation(DefinitionProvider provider) { if (useNameReferenceGraph) { NameReferenceGraph referenceGraph = (NameReferenceGraph) provider; for (Function function : getAllFunctions()) { if (!function.isMain()) { String functionName = function.getName(); if (functionName != null) { Name symbol = referenceGraph.getSymbol(functionName); updateFunctionForName(function, symbol); } } } } else { SimpleDefinitionFinder finder = (SimpleDefinitionFinder) provider; for (DefinitionSite definitionSite : finder.getDefinitionSites()) { Definition definition = definitionSite.definition; Function function = lookupFunctionForDefinition(definition); if (function != null) { for (UseSite useSite : finder.getUseSites(definition)) { updateFunctionForUse(function, useSite.node); } } } } } /** * Updates {@link Function} information (such as whether is is aliased * or exposed to .apply or .call from a {@link NameReferenceGraph.Name}. * * Note: this method may be called multiple times per Function, each time * with a different name. */ private void updateFunctionForName(Function function, Name name) { if (name.isAliased()) { function.isAliased = true; } if (name.exposedToCallOrApply()) { function.isExposedToCallOrApply = true; } } /** * Updates {@link Function} information (such as whether is is aliased * or exposed to .apply or .call based a site where the function is used. * * Note: this method may be called multiple times per Function, each time * with a different useNode. */ private void updateFunctionForUse(Function function, Node useNode) { Node useParent = useNode.getParent(); int parentType = useParent.getType(); if ((parentType == Token.CALL || parentType == Token.NEW) && useParent.getFirstChild() == useNode) { // Regular call sites don't count as aliases } else if (NodeUtil.isGet(useParent)) { // GET{PROP,ELEM} don't count as aliases // but we have to check for using them in .call and .apply. if (useParent.isGetProp()) { Node gramps = useParent.getParent(); if (NodeUtil.isFunctionObjectApply(gramps) || NodeUtil.isFunctionObjectCall(gramps)) { function.isExposedToCallOrApply = true; } } } else { function.isAliased = true; } } /** * Returns a {@link CallGraph.Function} for the passed in {@link Definition} * or null if the definition isn't for a function. */ private Function lookupFunctionForDefinition(Definition definition) { if (definition != null && !definition.isExtern()) { Node rValue = definition.getRValue(); if (rValue != null && rValue.isFunction()) { Function function = functionsByNode.get(rValue); Preconditions.checkNotNull(function); return function; } } return null; } /** * Constructs and returns a directed graph where the nodes are functions and * the edges are callsites connecting callers to callees. * * It is safe to call this method on both forward and backwardly constructed * CallGraphs. */ public DiGraph getForwardDirectedGraph() { return constructDirectedGraph(true); } /** * Constructs and returns a directed graph where the nodes are functions and * the edges are callsites connecting callees to callers. * * It is safe to call this method on both forward and backwardly constructed * CallGraphs. */ public DiGraph getBackwardDirectedGraph() { return constructDirectedGraph(false); } private static void digraphConnect(DiGraph digraph, Function caller, Callsite callsite, Function callee, boolean forward) { Function source; Function destination; if (forward) { source = caller; destination = callee; } else { source = callee; destination = caller; } digraph.connect(source, callsite, destination); } /** * Constructs a digraph of the call graph. If {@code forward} is true, then * the edges in the digraph will go from callers to callees, if false then * the edges will go from callees to callers. * * It is safe to run this method on both a forwardly constructed callgraph * and a backwardly constructed callgraph, regardless of the value of * {@code forward}. * * @param forward If true then the digraph will be a forward digraph. */ private DiGraph constructDirectedGraph(boolean forward) { DiGraphdigraph = LinkedDirectedGraph.createWithoutAnnotations(); // Create nodes in call graph for (Function function : getAllFunctions()) { digraph.createNode(function); } if (computeForwardGraph) { // The CallGraph is a forward graph, so go from callers to callees for (Function caller : getAllFunctions()) { for (Callsite callsite : caller.getCallsitesInFunction()) { for (Function callee : callsite.getPossibleTargets()) { digraphConnect(digraph, caller, callsite, callee, forward); } } } } else { // The CallGraph is a backward graph, so go from callees to callers for (Function callee : getAllFunctions()) { for (Callsite callsite : callee.getCallsitesPossiblyTargetingFunction()) { Function caller = callsite.getContainingFunction(); digraphConnect(digraph, caller, callsite, callee, forward); } } } return digraph; } /** * Constructs a DefinitionProvider that can be used to determine the * targets of callsites. * * This construction is the main cost of building the callgraph, so we offer * the client a choice of NameReferenceGraph, which is slow and hopefully more * precise, and SimpleDefinitionFinder, which is fast and perhaps not as * precise. * * We use SimpleNameFinder as the default because in practice it does * not appear to be less precise than NameReferenceGraph and is at least an * order of magnitude faster on large compiles. */ private DefinitionProvider constructDefinitionProvider(Node externsRoot, Node jsRoot) { if (useNameReferenceGraph) { // Name reference graph is very, very slow NameReferenceGraphConstruction graphConstruction = new NameReferenceGraphConstruction(compiler); graphConstruction.process(externsRoot, jsRoot); return graphConstruction.getNameReferenceGraph(); } else { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externsRoot, jsRoot); return defFinder; } } /** * Queries the definition provider for the definitions that could be the * targets of the given callsite node. * * This is complicated by the fact that NameReferenceGraph and * SimpleDefinitionProvider (the two definition providers we currently * use) differ on the types of target nodes they will analyze. */ private Collection lookupDefinitionsForTargetsOfCall( Node callsite, DefinitionProvider definitionProvider) { Preconditions.checkArgument(callsite.isCall() || callsite.isNew()); Node targetExpression = callsite.getFirstChild(); // NameReferenceGraph throws an exception unless the node is // a GETPROP or a NAME if (!useNameReferenceGraph || (targetExpression.isGetProp() || targetExpression.isName())) { Collection definitions = definitionProvider.getDefinitionsReferencedAt(targetExpression); if (definitions != null && !definitions.isEmpty()) { return definitions; } } return null; } /** * An inner class that represents functions in the call graph. * A Function knows how to get its AST node and what Callsites * it contains. */ public class Function { private Node astNode; private boolean isAliased = false; private boolean isExposedToCallOrApply = false; private Collection callsitesInFunction; private Collection callsitesPossiblyTargetingFunction; private Function(Node functionAstNode) { astNode = functionAstNode; } /** * Does this function represent the global "main" function? */ public boolean isMain() { return (this == CallGraph.this.mainFunction); } /** * Returns the underlying AST node for the function. This usually * has type Token.FUNCTION but in the case of the "main" function * will have type Token.BLOCK. */ public Node getAstNode() { return astNode; } /** * Returns the AST node for the body of the function. If this function * is the main function, it will return the global block. */ public Node getBodyNode() { if (isMain()) { return astNode; } else { return NodeUtil.getFunctionBody(astNode); } } /** * Gets the name of this function. Returns null if the function is * anonymous. */ public String getName() { if (isMain()) { return MAIN_FUNCTION_NAME; } else { return NodeUtil.getFunctionName(astNode); } } /** * Returns the callsites in this function. */ public Collection getCallsitesInFunction() { if (callsitesInFunction != null) { return callsitesInFunction; } else { return ImmutableList.of(); } } private void addCallsiteInFunction(Callsite callsite) { if (callsitesInFunction == null) { callsitesInFunction = new LinkedList(); } callsitesInFunction.add(callsite); } /** * Returns a collection of callsites that might call this function. * * getCallsitesPossiblyTargetingFunction() is a best effort only: the * collection may include callsites that do not actually call this function * and if this function is exported or aliased may be missing actual * targets. * * This method should not be called on a Function from a CallGraph * that was constructed with {@code computeBackwardGraph} {@code false}. */ public Collection getCallsitesPossiblyTargetingFunction() { if (computeBackwardGraph) { if (callsitesPossiblyTargetingFunction != null) { return callsitesPossiblyTargetingFunction; } else { return ImmutableList.of(); } } else { throw new UnsupportedOperationException("Cannot call " + "getCallsitesPossiblyTargetingFunction() on a Function " + "from a non-backward CallGraph"); } } private void addCallsitePossiblyTargetingFunction(Callsite callsite) { Preconditions.checkState(computeBackwardGraph); if (callsitesPossiblyTargetingFunction == null) { callsitesPossiblyTargetingFunction = new LinkedList(); } callsitesPossiblyTargetingFunction.add(callsite); } /** * Returns true if the function is aliased. */ public boolean isAliased() { return isAliased; } /** * Returns true if the function is ever exposed to ".call" or ".apply". */ public boolean isExposedToCallOrApply() { return isExposedToCallOrApply; } } /** * An inner class that represents call sites in the call graph. * A Callsite knows how to get its AST node, what its containing * Function is, and what its target Functions are. */ public class Callsite { private Node astNode; private boolean hasUnknownTarget = false; private boolean hasExternTarget = false; private Function containingFunction = null; private Collection possibleTargets; private Callsite(Node callsiteAstNode) { astNode = callsiteAstNode; } public Node getAstNode() { return astNode; } public Function getContainingFunction() { return containingFunction; } /** * Returns the possible target functions that this callsite could call. * * These targets do not include functions defined in externs. If this * callsite could call an extern function, then hasExternTarget() will * return true. * * getKnownTargets() is a best effort only: the collection may include * other functions that are not actual targets and (if hasUnknownTargets() * is true) may be missing actual targets. * * This method should not be called on a Callsite from a CallGraph * that was constructed with {@code computeForwardGraph} {@code false}. */ public Collection getPossibleTargets() { if (computeForwardGraph) { if (possibleTargets != null) { return possibleTargets; } else { return ImmutableList.of(); } } else { throw new UnsupportedOperationException("Cannot call " + "getPossibleTargets() on a Callsite from a non-forward " + "CallGraph"); } } private void addPossibleTarget(Function target) { Preconditions.checkState(computeForwardGraph); if (possibleTargets == null) { possibleTargets = new LinkedList(); } possibleTargets.add(target); } /** * If true, then DefinitionProvider used in callgraph construction * was unable find all target functions of this callsite. * * If false, then getKnownTargets() contains all the possible targets of * this callsite (and, perhaps, additional targets as well). */ public boolean hasUnknownTarget() { return hasUnknownTarget; } /** * If true, then this callsite could target a function defined in the * externs. If false, then not. */ public boolean hasExternTarget() { return hasExternTarget; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/DevirtualizePrototypeMethods.java0000644000175000017500000003427312115204405031431 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.jscomp.DefinitionsRemover.Definition; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import java.util.Collection; import java.util.List; /** * Rewrites prototyped methods calls as static calls that take "this" * as their first argument. This transformation simplifies the call * graph so smart name removal, cross module code motion and other * passes can do more. * *

      This pass should only be used in production code if property * and variable renaming are turned on. Resulting code may also * benefit from --collapse_anonymous_functions and * --collapse_variable_declarations * *

      This pass only rewrites functions that are part of an objects * prototype. Functions that access the "arguments" variable * arguments object are not eligible for this optimization. * *

      For example: *

       *     A.prototype.accumulate = function(value) {
       *       this.total += value; return this.total
       *     }
       *     var total = a.accumulate(2)
       * 
      * *

      will be rewritten as: * *

       *     var accumulate = function(self, value) {
       *       self.total += value; return self.total
       *     }
       *     var total = accumulate(a, 2)
       * 
      * */ class DevirtualizePrototypeMethods implements OptimizeCalls.CallGraphCompilerPass, SpecializationAwareCompilerPass { private final AbstractCompiler compiler; private SpecializeModule.SpecializationState specializationState; DevirtualizePrototypeMethods(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void enableSpecialization(SpecializeModule.SpecializationState state) { this.specializationState = state; } @Override public void process(Node externs, Node root) { SimpleDefinitionFinder defFinder = new SimpleDefinitionFinder(compiler); defFinder.process(externs, root); process(externs, root, defFinder); } @Override public void process( Node externs, Node root, SimpleDefinitionFinder definitions) { for (DefinitionSite defSite : definitions.getDefinitionSites()) { rewriteDefinitionIfEligible(defSite, definitions); } } /** * Determines if the name node acts as the function name in a call expression. */ private static boolean isCall(UseSite site) { Node node = site.node; Node parent = node.getParent(); return (parent.getFirstChild() == node) && parent.isCall(); } /** * Determines if the current node is a function prototype definition. */ private static boolean isPrototypeMethodDefinition(Node node) { Node parent = node.getParent(); if (parent == null) { return false; } Node gramp = parent.getParent(); if (gramp == null) { return false; } if (node.isGetProp()) { if (parent.getFirstChild() != node) { return false; } if (!NodeUtil.isExprAssign(gramp)) { return false; } Node functionNode = parent.getLastChild(); if ((functionNode == null) || !functionNode.isFunction()) { return false; } Node nameNode = node.getFirstChild(); return nameNode.isGetProp() && nameNode.getLastChild().getString().equals("prototype"); } else if (node.isStringKey()) { Preconditions.checkState(parent.isObjectLit()); if (!gramp.isAssign()) { return false; } if (gramp.getLastChild() != parent) { return false; } Node greatGramp = gramp.getParent(); if (greatGramp == null || !greatGramp.isExprResult()) { return false; } Node functionNode = node.getFirstChild(); if ((functionNode == null) || !functionNode.isFunction()) { return false; } Node target = gramp.getFirstChild(); return target.isGetProp() && target.getLastChild().getString().equals("prototype"); } else { return false; } } private String getMethodName(Node node) { if (node.isGetProp()) { return node.getLastChild().getString(); } else if (node.isStringKey()) { return node.getString(); } else { throw new IllegalStateException("unexpected"); } } /** * @returns The new name for a rewritten method. */ private String getRewrittenMethodName(String originalMethodName) { return "JSCompiler_StaticMethods_" + originalMethodName; } /** * Rewrites method definition and call sites if the method is * defined in the global scope exactly once. * * Definition and use site information is provided by the * {@link SimpleDefinitionFinder} passed in as an argument. * * @param defSite definition site to process. * @param defFinder structure that hold Node -> Definition and * Definition -> [UseSite] maps. */ private void rewriteDefinitionIfEligible(DefinitionSite defSite, SimpleDefinitionFinder defFinder) { if (defSite.inExterns || !defSite.inGlobalScope || !isEligibleDefinition(defFinder, defSite)) { return; } Node node = defSite.node; if (!isPrototypeMethodDefinition(node)) { return; } for (Node ancestor = node.getParent(); ancestor != null; ancestor = ancestor.getParent()) { if (NodeUtil.isControlStructure(ancestor)) { return; } } // TODO(user) The code only works if there is a single definition // associated with a property name. Once this pass starts using // the NameReferenceGraph to disambiguate call sites, it will be // necessary to consider type information when generating static // method names and/or append unique ids to duplicate static // method names. // Whatever scheme we use should not break stable renaming. String newMethodName = getRewrittenMethodName( getMethodName(node)); rewriteDefinition(node, newMethodName); rewriteCallSites(defFinder, defSite.definition, newMethodName); } /** * Determines if a method definition is eligible for rewrite as a * global function. In order to be eligible for rewrite, the * definition must: * * - Refer to a function that takes a fixed number of arguments. * - Function must not be exported. * - Function must be used at least once. * - Property is never accessed outside a function call context. * - The definition under consideration must be the only possible * choice at each call site. * - Definition must happen in a module loaded before the first use. */ private boolean isEligibleDefinition(SimpleDefinitionFinder defFinder, DefinitionSite definitionSite) { Definition definition = definitionSite.definition; JSModule definitionModule = definitionSite.module; // Only functions may be rewritten. // Functions that access "arguments" are not eligible since // rewrite changes the structure of this object. Node rValue = definition.getRValue(); if (rValue == null || !rValue.isFunction() || NodeUtil.isVarArgsFunction(rValue)) { return false; } // Exporting a method prevents rewrite. Node lValue = definition.getLValue(); if ((lValue == null) || !lValue.isGetProp()) { return false; } CodingConvention codingConvention = compiler.getCodingConvention(); if (codingConvention.isExported(lValue.getLastChild().getString())) { return false; } Collection useSites = defFinder.getUseSites(definition); // Rewriting unused methods is not sound. if (useSites.isEmpty()) { return false; } JSModuleGraph moduleGraph = compiler.getModuleGraph(); for (UseSite site : useSites) { // Accessing the property directly prevents rewrite. if (!isCall(site)) { return false; } Node nameNode = site.node; // Don't rewrite methods called in functions that can't be specialized // if we are specializing if (specializationState != null && !specializationState.canFixupSpecializedFunctionContainingNode( nameNode)) { return false; } // Multiple definitions prevent rewrite. Collection singleSiteDefinitions = defFinder.getDefinitionsReferencedAt(nameNode); if (singleSiteDefinitions.size() > 1) { return false; } Preconditions.checkState(!singleSiteDefinitions.isEmpty()); Preconditions.checkState(singleSiteDefinitions.contains(definition)); // Accessing the property in a module loaded before the // definition module prevents rewrite; accessing a variable // before definition results in a parse error. JSModule callModule = site.module; if ((definitionModule != callModule) && ((callModule == null) || !moduleGraph.dependsOn(callModule, definitionModule))) { return false; } } return true; } /** * Rewrites object method call sites as calls to global functions * that take "this" as their first argument. * * Before: * o.foo(a, b, c) * * After: * foo(o, a, b, c) */ private void rewriteCallSites(SimpleDefinitionFinder defFinder, Definition definition, String newMethodName) { Collection useSites = defFinder.getUseSites(definition); for (UseSite site : useSites) { Node node = site.node; Node parent = node.getParent(); Node objectNode = node.getFirstChild(); node.removeChild(objectNode); parent.replaceChild(node, objectNode); parent.addChildToFront(IR.name(newMethodName).srcref(node)); Preconditions.checkState(parent.isCall()); parent.putBooleanProp(Node.FREE_CALL, true); compiler.reportCodeChange(); if (specializationState != null) { specializationState.reportSpecializedFunctionContainingNode(parent); } } } /** * Rewrites method definitions as global functions that take "this" * as their first argument. * * Before: * a.prototype.b = function(a, b, c) {...} * * After: * var b = function(self, a, b, c) {...} */ private void rewriteDefinition(Node node, String newMethodName) { boolean isObjLitDefKey = node.isStringKey(); Node parent = node.getParent(); Node refNode = isObjLitDefKey ? node : parent.getFirstChild(); Node newNameNode = IR.name(newMethodName).copyInformationFrom(refNode); Node newVarNode = IR.var(newNameNode).copyInformationFrom(refNode); Node functionNode; if (!isObjLitDefKey) { Preconditions.checkState(parent.isAssign()); functionNode = parent.getLastChild(); Node expr = parent.getParent(); Node block = expr.getParent(); parent.removeChild(functionNode); newNameNode.addChildToFront(functionNode); block.replaceChild(expr, newVarNode); if (specializationState != null) { specializationState.reportRemovedFunction(functionNode, block); } } else { Preconditions.checkState(parent.isObjectLit()); functionNode = node.getFirstChild(); Node assign = parent.getParent(); Node expr = assign.getParent(); Node block = expr.getParent(); node.removeChild(functionNode); parent.removeChild(node); newNameNode.addChildToFront(functionNode); block.addChildAfter(newVarNode, expr); if (specializationState != null) { specializationState.reportRemovedFunction(functionNode, block); } } // add extra argument String self = newMethodName + "$self"; Node argList = functionNode.getFirstChild().getNext(); argList.addChildToFront(IR.name(self) .copyInformationFrom(functionNode)); // rewrite body Node body = functionNode.getLastChild(); replaceReferencesToThis(body, self); // fix type fixFunctionType(functionNode); compiler.reportCodeChange(); } /** * Creates a new JSType based on the original function type by * adding the original this pointer type to the beginning of the * argument type list and replacing the this pointer type with * NO_TYPE. */ private void fixFunctionType(Node functionNode) { FunctionType type = JSType.toMaybeFunctionType(functionNode.getJSType()); if (type != null) { JSTypeRegistry typeRegistry = compiler.getTypeRegistry(); List parameterTypes = Lists.newArrayList(); parameterTypes.add(type.getTypeOfThis()); for (Node param : type.getParameters()) { parameterTypes.add(param.getJSType()); } ObjectType thisType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); JSType returnType = type.getReturnType(); JSType newType = typeRegistry.createFunctionType( thisType, returnType, parameterTypes); functionNode.setJSType(newType); } } /** * Replaces references to "this" with references to name. Do not * traverse function boundaries. */ private void replaceReferencesToThis(Node node, String name) { if (node.isFunction()) { return; } for (Node child : node.children()) { if (child.isThis()) { Node newName = IR.name(name); newName.setJSType(child.getJSType()); node.replaceChild(child, newName); } else { replaceReferencesToThis(child, name); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/VariableNameGenerator.java0000644000175000017500000000231612115204405027676 0ustar apoapo/* * 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.Sets; import java.util.Set; /** * Generates new variables names that would not collide with existing names in * a scope. * * */ class VariableNameGenerator { private final NameGenerator names; private final Scope scope; VariableNameGenerator(Scope scope) { this.scope = scope; Set usedNames = Sets.newHashSet(); names = new NameGenerator(usedNames, "", null); } String getNextNewName() { String name = null; while (scope.isDeclared(name = names.generateNextName(), true)) {} return name; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/NodeIterators.java0000644000175000017500000002076612115204405026274 0ustar apoapo/* * 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.base.Preconditions; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Stack; /** * A package for common iteration patterns. * * All iterators are forward, post-order traversals unless otherwise noted. * * @author nicksantos@google.com (Nick Santos) */ class NodeIterators { private NodeIterators() {} /* all static */ /** * Traverses the local scope, skipping all function nodes. */ static class FunctionlessLocalScope implements Iterator { private final Stack ancestors = new Stack(); /** * @param ancestors The ancestors of the point where iteration will start, * beginning with the deepest ancestor. The start node will not be * exposed in the iteration. */ FunctionlessLocalScope(Node ... ancestors) { Preconditions.checkArgument(ancestors.length > 0); for (Node n : ancestors) { if (n.isFunction()) { break; } this.ancestors.add(0, n); } } @Override public boolean hasNext() { // Check if the current node has any nodes after it. return !(ancestors.size() == 1 && ancestors.peek().getNext() == null); } @Override public Node next() { Node current = ancestors.pop(); if (current.getNext() == null) { current = ancestors.peek(); // If this is a function node, skip it. if (current.isFunction()) { return next(); } } else { current = current.getNext(); ancestors.push(current); // If this is a function node, skip it. if (current.isFunction()) { return next(); } while (current.hasChildren()) { current = current.getFirstChild(); ancestors.push(current); // If this is a function node, skip it. if (current.isFunction()) { return next(); } } } return current; } @Override public void remove() { throw new UnsupportedOperationException("Not implemented"); } /** * Gets the node most recently returned by next(). */ protected Node current() { return ancestors.peek(); } /** * Gets the parent of the node most recently returned by next(). */ protected Node currentParent() { return ancestors.size() >= 2 ? ancestors.get(ancestors.size() - 2) : null; } /** * Gets the ancestors of the current node, with the deepest node first. * Only exposed for testing purposes. */ List currentAncestors() { List list = Lists.newArrayList(ancestors); Collections.reverse(list); return list; } } /** * An iterator to help with variable inlining. Given a variable declaration, * find all the nodes in post-order where the variable is guaranteed to * retain its original value. * * Consider: *
         * var X = 1;
         * var Y = 3; // X is still 1
         * if (Y) {
         *   // X is still 1
         * } else {
         *   X = 5;
         * }
         * // X may not be 1
         * 
      * In the above example, the iterator will iterate past the declaration of * Y and into the first block of the IF branch, and will stop at the * assignment {@code X = 5}. */ static class LocalVarMotion implements Iterator { private final boolean valueHasSideEffects; private final FunctionlessLocalScope iterator; private final String varName; private Node lookAhead; /** * @return Create a LocalVarMotion for use with moving a value assigned * at a variable declaration. */ static LocalVarMotion forVar( Node name, Node var, Node block) { Preconditions.checkArgument(var.isVar()); Preconditions.checkArgument(NodeUtil.isStatement(var)); // The FunctionlessLocalScope must start at "name" as this may be used // before the Normalize pass, and thus the VAR node may define multiple // names and the "name" node may have siblings. The actual assigned // value is skipped as it is a child of name. return new LocalVarMotion( name, new FunctionlessLocalScope(name, var, block)); } /** * @return Create a LocalVarMotion for use with moving a value assigned * as part of a simple assignment expression ("a = b;"). */ static LocalVarMotion forAssign( Node name, Node assign, Node expr, Node block) { Preconditions.checkArgument(assign.isAssign()); Preconditions.checkArgument(expr.isExprResult()); // The FunctionlessLocalScope must start at "assign", to skip the value // assigned to "name" (which would be its sibling). return new LocalVarMotion( name, new FunctionlessLocalScope(assign, expr, block)); } /** * @param iterator The iterator to use while inspecting the node * beginning with the deepest ancestor. */ private LocalVarMotion(Node nameNode, FunctionlessLocalScope iterator) { Preconditions.checkArgument(nameNode.isName()); Node valueNode = NodeUtil.getAssignedValue(nameNode); this.varName = nameNode.getString(); this.valueHasSideEffects = valueNode != null && NodeUtil.mayHaveSideEffects(valueNode); this.iterator = iterator; advanceLookAhead(true); } @Override public boolean hasNext() { return lookAhead != null; } @Override public Node next() { Node next = lookAhead; advanceLookAhead(false); return next; } @Override public void remove() { throw new UnsupportedOperationException("Not implemented"); } private void advanceLookAhead(boolean atStart) { if (!atStart) { if (lookAhead == null) { return; } // Don't advance past a reference to the variable that we're trying // to inline. Node curNode = iterator.current(); if (curNode.isName() && varName.equals(curNode.getString())) { lookAhead = null; return; } } if (!iterator.hasNext()) { lookAhead = null; return; } Node nextNode = iterator.next(); Node nextParent = iterator.currentParent(); int type = nextNode.getType(); if (valueHasSideEffects) { // Reject anything that might read state boolean readsState = false; if (// Any read of a different variable. (nextNode.isName() && !varName.equals(nextNode.getString())) || // Any read of a property. (nextNode.isGetProp() || nextNode.isGetElem())) { // If this is a simple assign, we'll be ok. if (nextParent == null || !NodeUtil.isVarOrSimpleAssignLhs(nextNode, nextParent)) { readsState = true; } } else if (nextNode.isCall() || nextNode.isNew()) { // This isn't really an important case. In most cases when we use // CALL or NEW, we're invoking it on a NAME or a GETPROP. And in the // few cases where we're not, it's because we have an anonymous // function that escapes the variable we're worried about. But we // include this for completeness. readsState = true; } if (readsState) { lookAhead = null; return; } } // Reject anything that might modify relevant state. We assume that // nobody relies on variables being undeclared, which will break // constructions like: // var a = b; // var b = 3; // alert(a); if (NodeUtil.nodeTypeMayHaveSideEffects(nextNode) && type != Token.NAME || type == Token.NAME && nextParent.isCatch()) { lookAhead = null; return; } lookAhead = nextNode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/0000755000175000017500000000000012115204405024136 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/SecureCompiler.java0000644000175000017500000001405312115204405027725 0ustar apoapo/* * 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.jsonml; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerInput; import com.google.javascript.jscomp.CompilerOptions; import com.google.javascript.jscomp.CompilerOptions.Reach; import com.google.javascript.jscomp.JSError; import com.google.javascript.jscomp.JSModule; import com.google.javascript.jscomp.Result; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.jscomp.VariableRenamingPolicy; import java.util.ArrayList; /** * Compilation of JavaScript code which guarantees that all security * capabilities are preserved after the process. In particular, it can be * safely applied to cajoled source. * * JS Compiler is used for code analysis and optimization. It runs a series * of passes which try to improve the code. * * For safety reasons, only a subset of local passes, which are provided by * JS Compiler, are processed. Currently it includes: * - elimination of temporary variables * * Using SecureCompiler is quite straightforward. A user just needs to create * a new instance and call compile() method. Currently the only input which * is supported is JsonML. * * @author dhans@google.com (Daniel Hans) */ public class SecureCompiler { private static final String COMPILATION_UNCOMPLETED_MSG = "No compilation has been completed yet."; private static final String COMPILATION_UNSUCCESSFUL_MSG = "The last compilation was not successful."; private static final String COMPILATION_ALREADY_COMPLETED_MSG = "This instance has already compiled one source."; private Compiler compiler; private CompilerOptions options; private JsonMLAst sourceAst; /** Report from the last compilation */ private Report report; public SecureCompiler() { compiler = new Compiler(); options = getSecureCompilerOptions(); } /** * Returns compiled source in JsonML format. */ public JsonML getJsonML() { Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); Preconditions.checkState(report.success, COMPILATION_UNSUCCESSFUL_MSG); return sourceAst.convertToJsonML(); } /** * Returns compiled source as a JavaScript. */ public String getString() { Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); Preconditions.checkState(report.success, COMPILATION_UNSUCCESSFUL_MSG); return compiler.toSource(); } /** * Returns report from the last compilation. */ public Report getReport() { Preconditions.checkState(report != null, COMPILATION_UNCOMPLETED_MSG); return report; } public void compile(JsonML source) { if (report != null) { throw new IllegalStateException(COMPILATION_ALREADY_COMPLETED_MSG); } sourceAst = new JsonMLAst(source); CompilerInput input = new CompilerInput( sourceAst, "[[jsonmlsource]]", false); JSModule module = new JSModule("[[jsonmlmodule]]"); module.add(input); Result result = compiler.compileModules( ImmutableList.of(), ImmutableList.of(module), options); report = generateReport(result); } /** * Returns compiler options which are safe for compilation of a cajoled * module. The set of options is similar to the one which is used by * CompilationLevel in simple mode. The main difference is that variable * renaming and closurePass options are turned off. */ private CompilerOptions getSecureCompilerOptions() { CompilerOptions options = new CompilerOptions(); options.variableRenaming = VariableRenamingPolicy.OFF; options.setInlineVariables(Reach.LOCAL_ONLY); options.inlineLocalFunctions = true; options.checkGlobalThisLevel = CheckLevel.OFF; options.coalesceVariableNames = true; options.deadAssignmentElimination = true; options.collapseVariableDeclarations = true; options.convertToDottedProperties = true; options.labelRenaming = true; options.removeDeadCode = true; options.optimizeArgumentsArray = true; options.removeUnusedVars = false; options.removeUnusedLocalVars = true; return options; } public void enableFoldConstant() { options.foldConstants = true; } Report generateReport(Result result) { // a report may be generated only after actual compilation is complete if (result == null) { return null; } ArrayList errors = Lists.newArrayList(); for (JSError error : result.errors) { errors.add(JsonMLError.make(error, sourceAst)); } ArrayList warnings = Lists.newArrayList(); for (JSError warning : result.warnings) { warnings.add(JsonMLError.make(warning, sourceAst)); } return new Report( errors.toArray(new JsonMLError[0]), warnings.toArray(new JsonMLError[0])); } public class Report { private final boolean success; private final JsonMLError[] errors; private final JsonMLError[] warnings; private Report(JsonMLError[] errors, JsonMLError[] warnings) { this.success = errors.length == 0; this.errors = errors; this.warnings = warnings; } public boolean isSuccessful() { return success; } public JsonMLError[] getErrors() { return errors; } public JsonMLError[] getWarnings() { return warnings; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/TagAttr.java0000644000175000017500000000315012115204405026346 0ustar apoapo/* * 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.jsonml; import java.util.HashMap; import java.util.Map; /** * List of attributes that a JsonML element may have. * * @author dhans@google.com (Daniel Hans) */ public enum TagAttr { BODY("body"), DIRECTIVE("directive"), END_COLUMN("endColumn"), END_LINE("endLine"), FLAGS("flags"), IS_PREFIX("isPrefix"), LABEL("label"), NAME("name"), OP("op"), OPAQUE_POSITION("opaque_position"), SOURCE("source"), START_COLUMN("startColumn"), START_LINE("startLine"), TYPE("type"), VALUE("value"); private final String name; private static final Map lookup = new HashMap(); static { for (TagAttr t : TagAttr.values()) { lookup.put(t.getName(), t); } } private String getName() { return name; } private TagAttr(String name) { this.name = name; } public static TagAttr get(String name) { return lookup.get(name); } @Override public String toString() { return name; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/JsonMLAst.java0000644000175000017500000001150412115204405026614 0ustar apoapo/* * 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.jsonml; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.AbstractCompiler; import com.google.javascript.jscomp.AstValidator; import com.google.javascript.jscomp.SourceAst; import com.google.javascript.jscomp.SourceFile; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.util.ArrayDeque; import java.util.Deque; /** * Generates an AST from a JsonML source file. * * JsonML format for representation of JavaScript is specified * here. * * @author dhans@google.com (Daniel Hans) * */ public class JsonMLAst implements SourceAst { private static final long serialVersionUID = 1L; private static final String DEFAULT_SOURCE_NAME = "[[jsonmlsource]]"; /* * Root element of JavaScript source which is represented by a JsonML tree. * See JsonML class for more details. */ private JsonML jsonml; /* * Root node of internal JS Compiler AST which represents the same source. * In order to get the tree, getAstRoot() has to be called. */ private Node root; private final SourceFile sourceFile; private final InputId inputId; public JsonMLAst(JsonML jsonml) { this.jsonml = jsonml; this.inputId = new InputId(getSourceName()); this.sourceFile = new SourceFile(getSourceName()); } @Override public void clearAst() { root = null; } /** * Generates AST based on AST representation * @see com.google.javascript.jscomp.SourceAst#getAstRoot(AbstractCompiler) */ @Override public Node getAstRoot(AbstractCompiler compiler) { if (root == null) { createAst(compiler); } return root; } @Override public SourceFile getSourceFile() { return null; } @Override public void setSourceFile(SourceFile file) { throw new UnsupportedOperationException( "JsonMLAst cannot be associated with a SourceFile instance."); } public String getSourceName() { Object obj = jsonml.getAttribute(TagAttr.SOURCE); if (obj instanceof String) { return (String) obj; } else { return DEFAULT_SOURCE_NAME; } } private void createAst(AbstractCompiler compiler) { Reader translator = new Reader(); translator.setRootElement(jsonml); try { root = translator.parse(compiler); root.setInputId(inputId); root.setStaticSourceFile(sourceFile); new AstValidator().validateScript(root); } catch (JsonMLException e) { // compiler should already have JSErrors } } public JsonML convertToJsonML () { if (root != null) { Writer converter = new Writer(); return converter.processAst(root); } return null; } /** * Returns a JsonML element with the specified number from the tree in * pre-order walk. * * @return nth node or null if the node does not exists */ public JsonML getElementPreOrder(int n) { Preconditions.checkState(jsonml != null); if (n == 0) { return jsonml; } Deque stack = new ArrayDeque(); stack.push(new WalkHelper(jsonml, 0)); int i = 0; while (i <= n && !stack.isEmpty()) { WalkHelper current = stack.pop(); JsonML element = current.element; Integer childno = current.childno; // not all the children of this node have been visited if (childno < element.childrenSize()) { stack.push(new WalkHelper(element, childno + 1)); // we visit the next child i++; element = element.getChild(childno); if (i == n) { return element; } // put the next child on the stack to preserve pre-order stack.push(new WalkHelper(element, 0)); } } return null; } /* * Represents a walk step while the JsonML tree is traversed. */ private static class WalkHelper { // JsonML element that corresponds to this step final JsonML element; // number of children of the element which has already been visited final int childno; WalkHelper(JsonML element, int childno) { this.element = element; this.childno = childno; } } @Override public InputId getInputId() { return inputId; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/JsonML.java0000644000175000017500000002027012115204405026144 0ustar apoapo/* * 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.jsonml; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * Class which represents JsonML element according to the specification at * "http://code.google.com/p/es-lab/wiki/JsonMLASTFormat" * * @author dhans@google.com (Daniel Hans) */ public class JsonML { private final TagType type; private Map attributes = new EnumMap(TagAttr.class); private List children = new ArrayList(); /** * Creates a new element with a given type. * @param type */ public JsonML(TagType type) { this.type = type; } /** * Creates a new element. * @param type type of the element * @param children children to append to the element */ public JsonML(TagType type, JsonML... children) { this(type, Arrays.asList(children)); } public JsonML(TagType type, List children) { this(type, Collections.emptyMap(), children); } public JsonML(TagType type, Map attributes) { this(type, attributes, Collections.emptyList()); } public JsonML(TagType type, Map attributes, List children) { this.type = type; this.attributes.putAll(attributes); appendChildren(children); } /** * Inserts the given JsonML element at the given position in the * list of children. * @param index index at which the given element is to be inserted * @param element JsonML element to be inserted */ public void addChild(int index, JsonML element) { children.add(index, element); } /** * Appends a given child element to the list of children. * @param element JsonML element to append */ public void appendChild(JsonML element) { children.add(element); } /** * Appends a collection of children to the back of the list of children. * @param elements collection of JsonML elements to append */ public void appendChildren(Collection elements) { children.addAll(elements); } /** * Returns number of the children. */ public int childrenSize() { return children.size(); } /** * Removes all elements from the list of children. */ public void clearChildren() { setChildren(); } /** * Returns value associated with a given attribute. * @param name name of the attribute * @return associated value or null if the attribute is not present */ public Object getAttribute(TagAttr name) { return attributes.get(name); } /** * Returns a map with attributes and respective values. */ public Map getAttributes() { return attributes; } /** * Returns child at a given position. */ public JsonML getChild(int index) { return children.get(index); } /** * Returns a list of all children. */ public List getChildren() { return children; } /** * Returns the portion of children list between the specified * fromIndex, inclusive, and toIndex, exclusive. * @param fromIndex low endpoint (inclusive) * @param toIndex high endpoint (exclusive) */ public List getChildren(int fromIndex, int toIndex) { return children.subList(fromIndex, toIndex); } /** * Returns type of the JsonML element. */ public TagType getType() { return type; } /** * Returns true if the JsonML element has at least one child. */ public boolean hasChildren() { return !children.isEmpty(); } /** * Sets value for a given attribute. * @param name name of the attribute * @param value value to associate with the attribute */ public void setAttribute(TagAttr name, Object value) { attributes.put(name, value); } /** * Sets attributes of the JsonML element. * @param attributes map with attributes and their values */ public void setAttributes(Map attributes) { this.attributes = attributes; } /** * Replaces the element at the given position in the list of children wit * the given JsonML element. * @param index index of element to replace * @param element JsonML element to append */ public void setChild(int index, JsonML element) { children.set(index, element); } /** * Replaces all elements in the list of children with the given * JsonML elements. * @param children a comma separated list of JsonML elements */ public void setChildren(JsonML... children) { this.children.clear(); this.children.addAll(Arrays.asList(children)); } /** * Replaces all elements in the list of children with the given * list of JsonML elements.. * @param children a list of JsonML elements. */ public void setChildren(List children) { this.children = children; } @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb, true, true); return sb.toString(); } private void toString(StringBuilder sb, boolean printAttributes, boolean printChildren) { sb.append("[\""); escapeStringOnto(type.name(), sb); sb.append('"'); if (printAttributes) { sb.append(", {"); boolean first = true; for (Entry entry : attributes.entrySet()) { if (first) { first = false; } else { sb.append(", "); } sb.append('"'); escapeStringOnto(entry.getKey().toString(), sb); sb.append("\": "); Object value = entry.getValue(); if (value == null) { sb.append("null"); } else if (value instanceof String) { sb.append('"'); escapeStringOnto((String) value, sb); sb.append('"'); } else { sb.append(value); } } sb.append("}"); } if (printChildren) { for (JsonML child : children) { sb.append(", "); sb.append(child.toString()); } } sb.append(']'); } /** * Encodes the specified string and appends it to the given StringBuilder. */ private static void escapeStringOnto(String s, StringBuilder sb) { int pos = 0, n = s.length(); for (int i = 0; i < n; ++i) { char ch = s.charAt(i); switch (ch) { case '\r': case '\n': case '"': case '\\': // these two characters are the exceptions to the general rule // that JSON is a syntactic subset of JavaScript // From JSON's perspective they are considered to be whitespaces, // while ES5 specifies them as line terminators. case '\u2028': case '\u2029': String hex = Integer.toString(ch, 16); sb.append(s, pos, i) .append("\\u").append("0000", hex.length(), 4).append(hex); pos = i + 1; break; } } sb.append(s, pos, n); } /** * Prints a JsonML tree in a human readable format. */ public String toStringTree() { try { StringBuilder s = new StringBuilder(); toStringTreeHelper(this, 0, s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } private static void toStringTreeHelper(JsonML element, int level, StringBuilder sb) throws IOException { for (int i = 0; i < level; ++i) { sb.append(" "); } element.toString(sb, true, false); sb.append("\n"); for (JsonML child : element.getChildren()) { toStringTreeHelper(child, level + 1, sb); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/ErrorLevel.java0000644000175000017500000000152712115204405027067 0ustar apoapo/* * 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.jsonml; /** * Represents possible error levels for JsonML errors. * * @author dhans@google.com (Daniel Hans) */ public enum ErrorLevel { COMPILATION_ERROR, COMPILATION_WARNING, SYNTAX_ERROR, } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/JsonMLException.java0000644000175000017500000000170112115204405030021 0ustar apoapo/* * 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.jsonml; /** * Class used to report internal exceptions which concern JsonML. * * @author dhans@google.com (Daniel Hans) * */ class JsonMLException extends Exception { private static final long serialVersionUID = 1L; JsonMLException() {} JsonMLException(String message) { super(message); } }closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/JsonMLError.java0000644000175000017500000000514612115204405027163 0ustar apoapo/* * 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.jsonml; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.JSError; /** * Class used to represent errors which correspond to JsonML elements. * * @author dhans@google.com (Daniel Hans) */ public class JsonMLError { /** Description of the error */ public final String description; /** Name of the source */ public final String sourceName; /** Node where the warning occurred. */ public final JsonML element; /** Line number of the source */ public final int lineNumber; /** Level */ public final ErrorLevel level; private JsonMLError(DiagnosticType type, String sourceName, JsonML element, int lineNumber, ErrorLevel level, String... arguments) { this.description = type.format.format(arguments); this.sourceName = sourceName; this.element = element; this.lineNumber = lineNumber; this.level = level; } private JsonMLError(String description, DiagnosticType type, String sourceName, JsonML element, int lineNumber, ErrorLevel level) { this.description = description; this.sourceName = sourceName; this.element = element; this.lineNumber = lineNumber; this.level = level; } public static JsonMLError make(DiagnosticType type, String sourceName, JsonML element, int lineNumber, ErrorLevel level, String... arguments) { return new JsonMLError(type, sourceName, element, lineNumber, level, arguments); } public static JsonMLError make(JSError error, JsonMLAst ast) { // try to find the corresponding JsonML element // it is stored as line number of the JSError int n = error.lineNumber; JsonML element = ast.getElementPreOrder(n); ErrorLevel level = error.getDefaultLevel() == CheckLevel.ERROR ? ErrorLevel.COMPILATION_ERROR : ErrorLevel.COMPILATION_WARNING; return new JsonMLError(error.getType(), error.sourceName, element, 0, level, error.description); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/Reader.java0000644000175000017500000012761212115204405026214 0ustar apoapo/* * 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.jsonml; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.javascript.jscomp.AbstractCompiler; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.JSError; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Traverse JsonML source tree and generates AST. * * @author dhans@google.com (Daniel Hans) */ public class Reader { static final DiagnosticType JSONML_SYNTAX = DiagnosticType.error( "JSONML_SYNTAX", "Syntax error: {0}"); /** Root element of JsonML tree which contains JavaScript source. */ private JsonML rootElement; /** Name of JavaScript source file */ private String sourceName; /** Error reporter */ private ErrorReporter errorReporter; /** List of ES5 directives supported by JsonML */ private final Set ALLOWED_DIRECTIVES = Sets.newHashSet("use strict"); /** Number of node in JsonML order which is currently processed */ private int nodeIndex; /** * Inner class which is responsible for passing reader errors * to the JS compiler. */ private class ErrorReporter { private AbstractCompiler compiler; ErrorReporter(AbstractCompiler compiler) { this.compiler = compiler; } private void report(JsonML element, String...arguments) throws JsonMLException { report(JSONML_SYNTAX, element, arguments); } private void report(DiagnosticType type, JsonML element, String... arguments) throws JsonMLException { // nodeIndex is the number of the node in which the error occurred // we will store it in line number int lineno = nodeIndex; int charno = -1; report(JSError.make(sourceName, lineno, charno, type, arguments)); } /** * Reports a new parser error to the compiler and terminates the job. * @param error JSError instance to be passed to the compiler */ private void report(JSError error) throws JsonMLException { report(error, true); } /** * Reports a new parser error to the compiler and terminates the job * if the error is fatal. * @param error JSError instance to be passed to the compiler * @param terminal if true, parsing is terminated by throwing exception */ private void report(JSError error, boolean terminal) throws JsonMLException { compiler.report(error); if (terminal) { throw new JsonMLException(); } } } // TODO(dhans): Maybe this state can be replaced with a simpler check /** * Stores state if EXPR_RESULT node should be inserted. The reason why * we have to keep track on that is JsonML representation does not have this * information. */ private boolean insertExprResultState = true; public void setRootElement(JsonML rootElement) { this.rootElement = rootElement; } /** * Generates AST for a specified JsonML source file. * @return root node of the generated AST * @throws JsonMLException if an error occurs */ public Node parse(AbstractCompiler compiler) throws JsonMLException { if (compiler == null) { // TODO(dhans): Review error handling // maybe throw an exception that compiler is required for errors. return null; } errorReporter = this.new ErrorReporter(compiler); Node root = IR.block(); nodeIndex = -1; Preconditions.checkState(rootElement.getType() == TagType.Program); transformElement(rootElement, root); return root.removeFirstChild(); } /** * Retrieves value of an attribute, but does not throw an exception if * the attribute is not present for a specified JsonML element. * @param type desired type of the attribute * @return value of the attribute or null if it is not specified * @throws JsonMLException i.e. when the value has a wrong type */ private T getOptionalAttribute(JsonML element, TagAttr attr, Class type) throws JsonMLException { return getAttribute(element, attr, type, true); } /** * Retrieves value of an attribute and throws an exception if * the attribute is not present for a specified JsonML element. * @param type desired type of the attribute * @return value of the attribute * @throws JsonMLException i.e. when the attribute does not exist */ private T getAttribute(JsonML element, TagAttr attr, Class type) throws JsonMLException { return getAttribute(element, attr, type, false); } private T getAttribute(JsonML element, TagAttr attr, Class type, boolean optional) throws JsonMLException { Object value = element.getAttribute(attr); if (value == null) { if (type == null || optional) { return null; } throw new JsonMLException( "Missing " + attr.name() + " attribute for " + element.getType().name() + " element."); } // Double type is a special case, as it might be represented by all // Number types or even by certain strings which contain only digit chars if (type.equals(Double.class)) { if (value instanceof Number) { return type.cast(((Number) value).doubleValue()); } if (value instanceof String) { return type.cast(Double.valueOf((String) value)); } throw new JsonMLException( "Wrong type of " + attr.name() + " attribute. " + "Received: " + value.getClass() + ". Expected: " + type.getName()); } if (type.isInstance(value)) { return type.cast(value); } throw new JsonMLException( "Wrong type of " + attr.name() + "attribute. " + "Received: " + value.getClass() + ". Expected: " + type.getName()); } /** * Retrieves an attribute whose type should be Object. */ private Object getObjectAttribute(JsonML element, TagAttr attr) throws JsonMLException { return getAttribute(element, attr, Object.class); } /** * Retrieves an attribute whose type should be String. */ private String getStringAttribute(JsonML element, TagAttr attr) throws JsonMLException { return getAttribute(element, attr, String.class); } private void validate(JsonML element) throws JsonMLException { String errorMessage = Validator.validate(element); if (errorMessage != null) { errorReporter.report(element, errorMessage); } } /** * Recursively transforms JsonML tree into AST. * * @param element JsonML element to transform * @param parent current parent AST node, i.e. when the element is * transformed * to a new AST node, it should be added as a last child to the parent Node. */ private void transformElement(JsonML element, Node parent) throws JsonMLException { // next element is transformed nodeIndex++; // the element has to be validated validate(element); // determine if EXPR_RESULT should be inserted if (insertExprResultState && JsonMLUtil.isExpression(element)) { transformExpr(element, parent); return; } switch (element.getType()) { case ArrayExpr: transformArrayExpr(element, parent); break; case AssignExpr: transformAssignExpr(element, parent); break; case BinaryExpr: transformBinaryExpr(element, parent); break; case BlockStmt: transformBlock(element, parent); break; case BreakStmt: transformBreakStmt(element, parent); break; case CallExpr: transformCallExpr(element, parent); break; case Case: transformCase(element, parent); break; case CatchClause: transformCatchClause(element, parent); break; case ConditionalExpr: transformConditionalExpr(element, parent); break; case ContinueStmt: transformContinueStmt(element, parent); break; case CountExpr: transformCountExpr(element, parent); break; case DataProp: transformDataProp(element, parent); break; case GetterProp: transformGetterProp(element, parent); break; case SetterProp: transformSetterProp(element, parent); break; case DefaultCase: transformDefaultCase(element, parent); break; case DeleteExpr: transformDeleteExpr(element, parent); break; case DoWhileStmt: transformDoWhileStmt(element, parent); break; case Empty: transformEmpty(element, parent); break; case EmptyStmt: transformEmptyStmt(element, parent); break; case EvalExpr: transformEvalExpr(element, parent); break; case ForInStmt: transformForInStmt(element, parent); break; case ForStmt: transformForStmt(element, parent); break; case FunctionDecl: transformFunctionDecl(element, parent); break; case FunctionExpr: transformFunctionExpr(element, parent); break; case IdExpr: transformIdExpr(element, parent); break; case IdPatt: transformIdPatt(element, parent); break; case IfStmt: transformIfStmt(element, parent); break; case InitPatt: transformInitPatt(element, parent); break; case InvokeExpr: transformInvokeExpr(element, parent); break; case LabelledStmt: transformLabelledStmt(element, parent); break; case LiteralExpr: transformLiteralExpr(element, parent); break; case LogicalAndExpr: transformLogicalAndExpr(element, parent); break; case LogicalOrExpr: transformLogicalOrExpr(element, parent); break; case MemberExpr: transformMemberExpr(element, parent); break; case NewExpr: transformNewExpr(element, parent); break; case ObjectExpr: transformObjectExpr(element, parent); break; case ParamDecl: transformParamDecl(element, parent); break; case Program: transformProgram(element, parent); break; case PrologueDecl: transformPrologueDecl(element, parent); break; case RegExpExpr: transformRegExpExpr(element, parent); break; case ReturnStmt: transformReturnStmt(element, parent); break; case SwitchStmt: transformSwitchStmt(element, parent); break; case ThisExpr: transformThisExpr(element, parent); break; case ThrowStmt: transformThrowStmt(element, parent); break; case TryStmt: transformTryStmt(element, parent); break; case TypeofExpr: transformTypeofExpr(element, parent); break; case UnaryExpr: transformUnaryExpr(element, parent); break; case VarDecl: transformVarDecl(element, parent); break; case WhileStmt: transformWhileStmt(element, parent); break; case WithStmt: transformWithStmt(element, parent); break; } } /* * Helper functions. * Usually called by functions which process particular JsonML elements. */ private void transformAllChildren(JsonML element, Node parent, boolean newState) throws JsonMLException { transformElements(element.getChildren(), parent, newState); } private void transformAllChildren(JsonML element, Node parent) throws JsonMLException { transformElements(element.getChildren(), parent); } private void transformAllChildrenFromIndex(JsonML element, Node parent, int fromIndex, boolean newState) throws JsonMLException { transformElements(element.getChildren().subList( fromIndex, element.childrenSize()), parent, newState); } private void transformElements(List elements, Node parent, boolean newState) throws JsonMLException { boolean oldState = insertExprResultState; insertExprResultState = newState; transformElements(elements, parent); insertExprResultState = oldState; } private void transformElements(List elements, Node parent) throws JsonMLException { for (JsonML element : elements) { transformElement(element, parent); } } /** * Responsible for inserting EXPR_RESULT nodes. */ private boolean transformExpr(JsonML element, Node parent) throws JsonMLException { boolean result = false; if (insertExprResultState) { Node node = new Node(Token.EXPR_RESULT); parent.addChildToBack(node); insertExprResultState = false; nodeIndex--; // the same node will be transformed again transformElement(element, node); insertExprResultState = true; result = true; } return result; } /** * Generic function responsible for dealing with JsonML elements describing * for loop (ForStmt and ForInStmt). */ private void transformForLoop(JsonML element, Node parent, int childno) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.FOR, element); parent.addChildToBack(node); JsonML child; for (int i = 0; i < childno; ++i) { child = element.getChild(i); if (child.getType() == TagType.EmptyStmt || child.getType() == TagType.Empty) { nodeIndex++; node.addChildToBack(IR.empty()); } else { transformElement(child, node); } } transformPotentiallyUnwrappedBlock(element.getChild(childno), node); insertExprResultState = true; } /** * Generic function responsible for dealing with the following JsonML * elements: BreakStmt and ContinueStmt. */ private void transformJumpStmt(JsonML element, Node parent, int type) throws JsonMLException { Node node = createNode(type, element); parent.addChildToBack(node); String label = getOptionalAttribute(element, TagAttr.LABEL, String.class); if (label != null) { node.addChildToBack(IR.labelName(label)); } } /** * Generic function responsible for dealing with JsonML elements describing * logical two arguments expressions: LogicalAndExpr and LogicalOrExpr. */ private void transformLogicalExpr(JsonML element, Node parent, int type) throws JsonMLException { transformTwoArgumentExpr(element, parent, type); } /** * Generic function responsible for dealing with all kind of expressions * which are passed exactly two arguments. */ private void transformTwoArgumentExpr(JsonML element, Node parent, int type) throws JsonMLException { Node node = createNode(type, element); parent.addChildToBack(node); transformAllChildren(element, node); } /** * Transforms an element which should be transformed into a BLOCK node, but * may not be represented by BlockStmt. In this case, additional BLOCK node * is created. */ private void transformPotentiallyUnwrappedBlock(JsonML element, Node parent) throws JsonMLException { // in theory it should be always EmptyStmt, but due to possible // compatibility issues Empty element is allowed as well if (element.getType() == TagType.EmptyStmt || element.getType() == TagType.Empty) { nodeIndex++; // Empty elements are only replaced by BLOCK node Node block = IR.block(); parent.addChildToBack(block); block.putBooleanProp(Node.EMPTY_BLOCK, true); } else if (element.getType() != TagType.BlockStmt) { Node block = IR.block(); parent.addChildToBack(block); boolean state = insertExprResultState; insertExprResultState = true; transformElement(element, block); insertExprResultState = state; } else { nodeIndex++; transformBlock(element, parent); } } /* * Main functions. * Functions responsible for handling particular JsonML elements. Depending * on type, transformElement function dispatches actual work to * the corresponding function below. */ private void transformArrayExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.ARRAYLIT, element); parent.addChildToBack(node); // iterate through all the children and look for empty elements for (JsonML child : element.getChildren()) { transformElement(child, node); } } private void transformAssignExpr(JsonML element, Node parent) throws JsonMLException { String op = getStringAttribute(element, TagAttr.OP); int type = Operator.getNodeTypeForAssignOp(op); transformTwoArgumentExpr(element, parent, type); } private void transformBinaryExpr(JsonML element, Node parent) throws JsonMLException { String op = getStringAttribute(element, TagAttr.OP); int type = Operator.getNodeTypeForBinaryOp(op); transformTwoArgumentExpr(element, parent, type); } private void transformBlock(JsonML element, Node parent) throws JsonMLException { transformBlock(element, parent, 0, element.childrenSize()); } private void transformBlock(JsonML element, Node parent, int start) throws JsonMLException { transformBlock(element, parent, start, element.childrenSize()); } private void transformBlock(JsonML element, Node parent, int start, int end) throws JsonMLException { Node node = createNode(Token.BLOCK, element); parent.addChildToBack(node); transformElements(element.getChildren(start, end), node, true); } private void transformBreakStmt(JsonML element, Node parent) throws JsonMLException { transformJumpStmt(element, parent, Token.BREAK); } private void transformCallExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.CALL, element); parent.addChildToBack(node); transformAllChildren(element, node); // Keep track of of the "this" context of a call. A call without an // explicit "this" is a free call. Node first = node.getFirstChild(); if (first.getType() != Token.GETPROP && first.getType() != Token.GETELEM) { node.putBooleanProp(Node.FREE_CALL, true); } } private void transformCase(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.CASE, element); parent.addChildToBack(node); // the first element represents case id JsonML child = element.getChild(0); transformElement(child, node); // always insert an extra BLOCK node Node block = IR.block(); block.setIsSyntheticBlock(true); node.addChildToBack(block); transformAllChildrenFromIndex(element, block, 1, true); } private void transformCatchClause(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.CATCH, element); parent.addChildToBack(node); JsonML child = element.getChild(0); transformElement(child, node); // the second child represents actual block child = element.getChild(1); transformElement(child, node); } private void transformConditionalExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.HOOK, element); parent.addChildToBack(node); transformAllChildren(element, node); } private void transformContinueStmt(JsonML element, Node parent) throws JsonMLException { transformJumpStmt(element, parent, Token.CONTINUE); } /* * CountExpr are both incrementing and decrementing expressions (++x, --x) */ private void transformCountExpr(JsonML element, Node parent) throws JsonMLException { String op = getStringAttribute(element, TagAttr.OP); int type = Operator.getNodeTypeForCountOp(op); Boolean isPrefix = getAttribute(element, TagAttr.IS_PREFIX, Boolean.class); Node node = createNode(type, element); node.putIntProp(Node.INCRDECR_PROP, isPrefix ? 0 : 1); parent.addChildToBack(node); transformElement(element.getChild(0), node); } /* * DataProp is the name for an object property which is initialized * when the object is created by object literal. * For example, in {x: 1, y: 2} each property is represented by its own * DataProp. */ private void transformDataProp(JsonML element, Node parent) throws JsonMLException { Object name = getObjectAttribute(element, TagAttr.NAME); Node node = null; if (name instanceof Number) { node = IR.stringKey(getStringValue(((Number) name).doubleValue())); } else if (name instanceof String) { node = IR.stringKey((String) name); } else { throw new IllegalStateException( "The name of the property has invalid type."); } setPosition(node); parent.addChildToBack(node); transformElement(element.getChild(0), node); } private static String getStringValue(double value) { long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(value); } } /* * GetterProp is a object literal entry for a getter. * For example, {get x() {return 1}} */ private void transformGetterProp(JsonML element, Node parent) throws JsonMLException { transformProp(Token.GETTER_DEF, element, parent); } /* * GetterProp is a object literal entry for a getter. * For example, {set x() {return 1}} */ private void transformSetterProp(JsonML element, Node parent) throws JsonMLException { transformProp(Token.SETTER_DEF, element, parent); } private void transformProp(int tokenType, JsonML element, Node parent) throws JsonMLException { Object name = getObjectAttribute(element, TagAttr.NAME); Node node = null; if (name instanceof Number) { // TODO(johnlenz): convert the number to a quoted string. throw new IllegalStateException( "Not yet supported."); } else if (name instanceof String) { node = Node.newString(tokenType, (String) name); } else { throw new IllegalStateException( "The name of the property has invalid type."); } setPosition(node); parent.addChildToBack(node); transformElement(element.getChild(0), node); } private void transformDefaultCase(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.DEFAULT_CASE, element); parent.addChildToBack(node); // the first child represent body Node block = IR.block(); block.setIsSyntheticBlock(true); node.addChildToBack(block); transformAllChildren(element, block, true); } private void transformDeleteExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.DELPROP, element); parent.addChildToBack(node); transformElement(element.getChild(0), node); } private void transformDoWhileStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.DO, element); parent.addChildToBack(node); // the first child represents body JsonML child = element.getChild(0); transformPotentiallyUnwrappedBlock(child, node); // the second child represents condition child = element.getChild(1); transformElement(child, node); insertExprResultState = true; } private void transformEmpty(JsonML element, Node parent) { switch (parent.getType()) { case Token.ARRAYLIT: parent.addChildToBack(IR.empty()); break; case Token.FUNCTION: parent.addChildToBack(IR.name("")); break; default: throw new IllegalArgumentException("Unexpected Empty element."); } } private void transformEmptyStmt(JsonML element, Node parent) { Preconditions.checkState( parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT); parent.addChildToBack(IR.empty()); } private void transformEvalExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.CALL, element); node.putBooleanProp(Node.FREE_CALL, true); parent.addChildToBack(node); Node child = IR.name("eval"); child.putBooleanProp(Node.DIRECT_EVAL, true); node.addChildToBack(child); transformAllChildren(element, node); } private void transformForInStmt(JsonML element, Node parent) throws JsonMLException { transformForLoop(element, parent, 2); } private void transformForStmt(JsonML element, Node parent) throws JsonMLException { transformForLoop(element, parent, 3); } private void transformFunction(JsonML element, Node parent, boolean needsName) throws JsonMLException { Node node = createNode(Token.FUNCTION, element); parent.addChildToBack(node); JsonML child = element.getChild(0); String name = ""; // it be already validated at this point that a non empty name exists // if it is a function declaration transformElement(element.getChild(0), node); transformElement(element.getChild(1), node); // other children represents function body which should be // wrapped inside a block node transformBlock(element, node, 2); } private void transformFunctionDecl(JsonML element, Node parent) throws JsonMLException { transformFunction(element, parent, true); } private void transformFunctionExpr(JsonML element, Node parent) throws JsonMLException { transformFunction(element, parent, false); } private void transformIdExpr(JsonML element, Node parent) throws JsonMLException { String name = getStringAttribute(element, TagAttr.NAME); Node node = IR.name(name); setPosition(node); parent.addChildToBack(node); } /* * InitPatt represents all variable declarations value initialization. * It has two children: name of the variable and the initial value. */ private void transformInitPatt(JsonML element, Node parent) throws JsonMLException { JsonML child = element.getChild(0); nodeIndex++; Node node = IR.name( getAttribute(child, TagAttr.NAME, String.class)); setPosition(node); parent.addChildToBack(node); child = element.getChild(1); transformElement(child, node); } private void transformIdPatt(JsonML element, Node parent) throws JsonMLException { Node node = IR.name( getStringAttribute(element, TagAttr.NAME)); setPosition(node); parent.addChildToBack(node); } private void transformIfStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.IF, element); parent.addChildToBack(node); // the first child represents condition JsonML child = element.getChild(0); transformElement(child, node); // the second child is required child = element.getChild(1); transformPotentiallyUnwrappedBlock(child, node); // the third child represents else part and is not required by AST child = element.getChild(2); if (child.getType() != TagType.EmptyStmt && child.getType() != TagType.Empty) { transformPotentiallyUnwrappedBlock(child, node); } else { nodeIndex++; } insertExprResultState = true; } private void transformInvokeExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.CALL, element); parent.addChildToBack(node); transformMemberExpr(element, node); transformElements(element.getChildren(2, element.childrenSize()), node); } private void transformLabelledStmt(JsonML element, Node parent) throws JsonMLException { String label = getStringAttribute(element, TagAttr.LABEL); Node node = createNode(Token.LABEL, element); node.addChildToBack(IR.labelName(label)); parent.addChildToBack(node); JsonML child = element.getChild(0); if (child.getType() == TagType.EmptyStmt) { nodeIndex++; node.addChildToBack(IR.empty()); } else { transformElement(element.getChild(0), node); } } private void transformLiteralExpr(JsonML element, Node parent) throws JsonMLException { Node node = null; Type type = Type.get(getStringAttribute(element, TagAttr.TYPE)); switch (type) { case BOOLEAN: { Boolean value = getAttribute(element, TagAttr.VALUE, Boolean.class); if (value) { node = IR.trueNode(); } else { node = IR.falseNode(); } break; } case NULL: { // needed to throw an exception if value is not null getAttribute(element, TagAttr.VALUE, null); node = IR.nullNode(); break; } case NUMBER: { Double value = getAttribute(element, TagAttr.VALUE, Double.class); node = IR.number(value); break; } case STRING: { String value = getStringAttribute(element, TagAttr.VALUE); node = IR.string(value); break; } default: throw new JsonMLException("Unrecognized type attribute."); } setPosition(node); parent.addChildToBack(node); } private void transformLogicalAndExpr(JsonML element, Node parent) throws JsonMLException { transformLogicalExpr(element, parent, Token.AND); } private void transformLogicalOrExpr(JsonML element, Node parent) throws JsonMLException { transformLogicalExpr(element, parent, Token.OR); } private void transformMemberExpr(JsonML element, Node parent) throws JsonMLException { String op = getAttribute(element, TagAttr.OP, String.class); int type; if (op.equals(".")) { type = Token.GETPROP; } else if (op.equals("[]")) { type = Token.GETELEM; } else { throw new JsonMLException("Invalid OP argument: " + op); } Node node = createNode(type, element); parent.addChildToBack(node); transformElement(element.getChild(0), node); transformElement(element.getChild(1), node); } private void transformNewExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.NEW, element); parent.addChildToBack(node); transformAllChildren(element, node); } private void transformObjectExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.OBJECTLIT, element); parent.addChildToBack(node); transformAllChildren(element, node); } private void transformParamDecl(JsonML element, Node parent) throws JsonMLException { // formal arguments should be wrapped by LP node Node node = createNode(Token.PARAM_LIST, element); parent.addChildToBack(node); transformAllChildren(element, node); } private void transformProgram(JsonML element, Node parent) throws JsonMLException { Preconditions.checkNotNull(parent); insertExprResultState = true; Node script = IR.script(); parent.addChildToBack(script); for (JsonML child : element.getChildren()) { transformElement(child, script); } } private void transformPrologueDecl(JsonML element, Node parent) throws JsonMLException { String directive = getStringAttribute(element, TagAttr.DIRECTIVE); if (ALLOWED_DIRECTIVES.contains(directive)) { Set directives = parent.getDirectives(); if (directives == null) { directives = Sets.newHashSet(); } directives.add(directive); parent.setDirectives(directives); } else { // for a directive which is not supported, we create a regular node Node node = IR.exprResult(IR.string(directive)); parent.addChildToBack(node); } } private void transformRegExpExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.REGEXP, element); parent.addChildToBack(node); String body = getStringAttribute(element, TagAttr.BODY); node.addChildToBack(IR.string(body)); String flags = getStringAttribute(element, TagAttr.FLAGS); if (!(flags.equals(""))) { node.addChildToBack(IR.string(flags)); } } private void transformReturnStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); Node node = createNode(Token.RETURN, element); parent.addChildToBack(node); if (element.hasChildren()) { insertExprResultState = false; transformElement(element.getChild(0), node); insertExprResultState = true; } } private void transformSwitchStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.SWITCH, element); parent.addChildToBack(node); // make sure it has at least one child // the first child represents switch param JsonML child = element.getChild(0); transformElement(child, node); // the rest of the children represent cases for (int i = 1; i < element.childrenSize(); ++i) { child = element.getChild(i); // make sure it is case or default transformElement(child, node); } insertExprResultState = true; } /** * @throws JsonMLException */ private void transformThisExpr(JsonML element, Node parent) throws JsonMLException { parent.addChildToBack(createNode(Token.THIS, element)); } private void transformThrowStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); Node node = createNode(Token.THROW, element); parent.addChildToBack(node); insertExprResultState = false; transformElement(element.getChild(0), node); insertExprResultState = true; } private void transformTryStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); Node node = createNode(Token.TRY, element); parent.addChildToBack(node); // the first child represents try body JsonML child = element.getChild(0); transformElement(child, node); // the second child represents catch Node block = IR.block(); node.addChildToBack(block); child = element.getChild(1); if (child.getType() == TagType.CatchClause) { transformElement(child, block); } else { // catch clause is not present, but the element has to be counted nodeIndex++; } // if the third child is present, it represents finally if (element.childrenSize() == 3) { child = element.getChild(2); transformElement(child, node); } } private void transformTypeofExpr(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.TYPEOF, element); parent.addChildToBack(node); transformElement(element.getChild(0), node); } private void transformUnaryExpr(JsonML element, Node parent) throws JsonMLException { String op = getStringAttribute(element, TagAttr.OP); int type = Operator.getNodeTypeForUnaryOp(op); Node node = createNode(type, element); parent.addChildToBack(node); transformAllChildren(element, node); } private void transformVarDecl(JsonML element, Node parent) throws JsonMLException { Node node = createNode(Token.VAR, element); parent.addChildToBack(node); transformAllChildren(element, node, false); } private void transformWhileStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.WHILE, element); parent.addChildToBack(node); // the first child represents loop condition JsonML child = element.getChild(0); transformElement(child, node); // the second child represents loop body child = element.getChild(1); transformPotentiallyUnwrappedBlock(child, node); insertExprResultState = true; } private void transformWithStmt(JsonML element, Node parent) throws JsonMLException { Preconditions.checkState(insertExprResultState == true); insertExprResultState = false; Node node = createNode(Token.WITH, element); parent.addChildToBack(node); // the first child represents object JsonML child = element.getChild(0); transformElement(child, node); // the second child represents body child = element.getChild(1); transformPotentiallyUnwrappedBlock(child, node); insertExprResultState = true; } /** * Creates a node which refers to a particular JsonML element. */ private Node createNode(int type, JsonML element) { return new Node(type, nodeIndex, -1); } /** * Sets position for a node which refers to a particular JsonML element. * The position says which number (in pre-order) has the corresponding * JsonML element in the tree. */ private void setPosition(Node node) { node.setLineno(nodeIndex); } /** * Internal representation for operators which are used by JsonML as * attributes for various elements. */ private enum Operator { // Assign Operators ASSIGN("="), ASSIGN_BITOR("|="), ASSIGN_BITXOR("^="), ASSIGN_BITAND("&="), ASSIGN_LSH("<<="), ASSIGN_RSH(">>="), ASSIGN_URSH(">>>="), ASSIGN_ADD("+="), ASSIGN_SUB("-="), ASSIGN_MUL("*="), ASSIGN_DIV("/="), ASSIGN_MOD("%="), // Binary Operators BITOR("|"), BITXOR("^"), BITAND("&"), EQ("=="), NE("!="), LT("<"), LE("<="), GT(">"), GE(">="), LSH("<<"), RSH(">>"), URSH(">>>"), ADD("+"), SUB("-"), MUL("*"), DIV("/"), MOD("%"), SHEQ("==="), SHNE("!=="), COMMA(","), INSTANCEOF("instanceof"), IN("in"), // Count Operators DEC("--"), INC("++"), // Unary Operators NOT("!"), BITNOT("~"), POS("+_unary"), // "+" would be a duplicate with ADD NEG("-_unary"), // "-" would be a duplicate with SUB VOID("void"); private final String name; private static Map lookup = Maps.newHashMap(); // Maps string representation of operators with corresponding enums static { for (Operator op : Operator.values()) { lookup.put(op.getName(), op); } } private String getName() { return this.name; } private Operator(String name) { this.name = name; } private static Operator get(String name) { return lookup.get(name); } /** * Returns assign operator associated with a specified name. */ private static int getNodeTypeForAssignOp(String name) { Operator op = get(name); if (op == null) { return Token.ERROR; } int type; switch (op) { case ASSIGN: type = Token.ASSIGN; break; case ASSIGN_BITOR: type = Token.ASSIGN_BITOR; break; case ASSIGN_BITXOR: type = Token.ASSIGN_BITXOR; break; case ASSIGN_BITAND: type = Token.ASSIGN_BITAND; break; case ASSIGN_LSH: type = Token.ASSIGN_LSH; break; case ASSIGN_RSH: type = Token.ASSIGN_RSH; break; case ASSIGN_URSH: type = Token.ASSIGN_URSH; break; case ASSIGN_ADD: type = Token.ASSIGN_ADD; break; case ASSIGN_SUB: type = Token.ASSIGN_SUB; break; case ASSIGN_MUL: type = Token.ASSIGN_MUL; break; case ASSIGN_DIV: type = Token.ASSIGN_DIV; break; case ASSIGN_MOD: type = Token.ASSIGN_MOD; break; default: throw new IllegalArgumentException("" + "Invalid type of assign expression."); } return type; } /** * Returns binary operator associated with a specified name. */ private static int getNodeTypeForBinaryOp(String name) { Operator op = get(name); int type; switch (op) { case BITOR: type = Token.BITOR; break; case BITXOR: type = Token.BITXOR; break; case BITAND: type = Token.BITAND; break; case EQ: type = Token.EQ; break; case NE: type = Token.NE; break; case LT: type = Token.LT; break; case LE: type = Token.LE; break; case GT: type = Token.GT; break; case GE: type = Token.GE; break; case LSH: type = Token.LSH; break; case RSH: type = Token.RSH; break; case URSH: type = Token.URSH; break; case ADD: type = Token.ADD; break; case SUB: type = Token.SUB; break; case MUL: type = Token.MUL; break; case DIV: type = Token.DIV; break; case MOD: type = Token.MOD; break; case SHEQ: type = Token.SHEQ; break; case SHNE: type = Token.SHNE; break; case COMMA: type = Token.COMMA; break; case INSTANCEOF: type = Token.INSTANCEOF; break; case IN: type = Token.IN; break; default: throw new IllegalArgumentException("" + "Invalid type of binary expression."); } return type; } /** * Returns count operator(++, --) associated with a specified name. */ private static int getNodeTypeForCountOp(String name) { Operator op = get(name); if (op == null) { return Token.ERROR; } int type; switch (op) { case DEC: type = Token.DEC; break; case INC: type = Token.INC; break; default: throw new IllegalArgumentException("" + "Invalid type of count expression."); } return type; } /** * Returns assign operator associated with a specified name. */ private static int getNodeTypeForUnaryOp(String name) { String realName = new String(name); if (name.equals("+") || name.equals("-")) { realName += "_unary"; } Operator op = get(realName); int type; switch (op) { case NOT: type = Token.NOT; break; case BITNOT: type = Token.BITNOT; break; case POS: type = Token.POS; break; case NEG: type = Token.NEG; break; case VOID: type = Token.VOID; break; default: throw new IllegalArgumentException("" + "Invalid type of unary expression."); } return type; } } /** * Internal representation of possible types of arguments of JsonML. */ private enum Type { BOOLEAN("boolean"), NULL("null"), NUMBER("number"), STRING("string"); private final String name; private static Map lookup = new HashMap(); static { for (Type type : Type.values()) { lookup.put(type.getName(), type); } } private String getName() { return this.name; } private Type(String name) { this.name = name; } private static Type get(String name) { return lookup.get(name); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/NodeUtil.java0000644000175000017500000000453412115204405026532 0ustar apoapo/* * 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.jsonml; import com.google.common.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Non public methods copied from com.google.javascript.jscomp.NodeUtil class. * * @author dhans@google.com (Daniel Hans) */ class NodeUtil { /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.getType() == Token.FOR && n.getChildCount() == 3; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression). */ static boolean isFunctionDeclaration(Node n) { return n.getType() == Token.FUNCTION && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().getType() == Token.SCRIPT || n.getParent().getParent().getType() == Token.FUNCTION); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/Validator.java0000644000175000017500000003501412115204405026731 0ustar apoapo/* * 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.jsonml; import java.util.Arrays; /** * Statically validates JsonML elements. * * It is done in constant time: no subtree is traversed, but the element * is validated based only on its properties. Sometimes, also its children * are taken into account. * * Usually it checks if the specified element has a correct number of children, * and if all require attributes exist. It does not enforce all restrictions * which are implied by ES3 or ES5 specification. * * @author dhans@google.com (Daniel Hans) */ public class Validator { public static final String MISSING_ARGUMENT = "" + "No %s attribute specified for %s."; public static final String NOT_ENOUGH_CHILDREN_FMT = "" + "Not enough children for %s. Expected: %d. Found: %d."; public static final String TOO_MANY_CHILDREN_FMT = "" + "Too many children for %s. Expected: %d. Found: %d."; public static final String WRONG_CHILD_TYPE_FMT = "" + "Wrong type of child number %d for %s. Expected: %s. Found: %s."; // used to check if a JsonML element represents an expression public static TagType[] exprTypes = { TagType.ArrayExpr, TagType.AssignExpr, TagType.BinaryExpr, TagType.CallExpr, TagType.ConditionalExpr, TagType.CountExpr, TagType.DeleteExpr, TagType.EvalExpr, TagType.IdExpr, TagType.InvokeExpr, TagType.LiteralExpr, TagType.LogicalAndExpr, TagType.LogicalOrExpr, TagType.MemberExpr, TagType.NewExpr, TagType.ObjectExpr, TagType.RegExpExpr, TagType.ThisExpr, TagType.TypeofExpr, TagType.UnaryExpr, TagType.FunctionExpr }; private final StringBuilder b; private boolean error; private Validator() { b = new StringBuilder(); error = false; } /** * Validates the specified JsonML element. * @param element JsonML element to validate * @return error message if the element could not be * validated, an empty string otherwise */ public static String validate(JsonML element) { return (new Validator()).doValidate(element); } /** * Validates the specified JsonML element. */ private String doValidate(JsonML element) { String message; switch (element.getType()) { case AssignExpr: validateAssignExpr(element); break; case BinaryExpr: validateBinaryExpr(element); break; case BreakStmt: case ContinueStmt: validateJmpStmt(element); break; case Case: validateCase(element); break; case CatchClause: validateCatchClause(element); break; case ConditionalExpr: validateConditionalExpr(element); break; case CountExpr: validateCountExpr(element); break; case DataProp: validateProp(element); break; case GetterProp: validateProp(element); break; case SetterProp: validateProp(element); break; case DeleteExpr: validateDeleteExpr(element); break; case DoWhileStmt: validateDoWhileStmt(element); break; case EmptyStmt: validateEmptyStmt(element); break; case ForInStmt: validateForInStmt(element); break; case ForStmt: validateForStmt(element); break; case FunctionDecl: validateFunctionDecl(element); break; case FunctionExpr: validateFunctionExpr(element); break; case IdExpr: validateIdExpr(element); break; case IdPatt: validateIdPatt(element); break; case IfStmt: validateIfStmt(element); break; case InvokeExpr: validateInvokeExpr(element); break; case LabelledStmt: validateLabelledStmt(element); break; case LiteralExpr: validateLiteralExpr(element); break; case LogicalAndExpr: case LogicalOrExpr: validateLogicalExpr(element); break; case MemberExpr: validateMemberExpr(element); break; case NewExpr: validateNewExpr(element); break; case ObjectExpr: validateObjectExpr(element); break; case ParamDecl: validateParamDecl(element); break; case RegExpExpr: validateRegExpExpr(element); break; case ReturnStmt: validateReturnStmt(element); break; case SwitchStmt: validateSwitchStmt(element); break; case ThisExpr: validateThisExpr(element); break; case ThrowStmt: validateThrowStmt(element); break; case TryStmt: validateTryStmt(element); break; case TypeofExpr: validateTypeofExpr(element); break; case UnaryExpr: validateUnaryExpr(element); break; case VarDecl: validateVarDecl(element); break; case WhileStmt: validateWhileStmt(element); break; case WithStmt: validateWithStmt(element); break; } return b.length() != 0 ? b.toString() : null; } private void validateAssignExpr(JsonML element) { validateChildrenSize(element, 2); validateArgument(element, TagAttr.OP); } private void validateBinaryExpr(JsonML element) { validateChildrenSize(element, 2); validateArgument(element, TagAttr.OP); } private void validateCase(JsonML element) { validateMinChildrenSize(element, 1); if (!error) { validateIsChildExpression(element, 0); } } private void validateCatchClause(JsonML element) { validateChildrenSize(element, 2); if (!error) { validateChildType(element, TagType.IdPatt , 0); validateChildType(element, TagType.BlockStmt, 1); } } private void validateConditionalExpr(JsonML element) { validateChildrenSize(element, 3); } private void validateCountExpr(JsonML element) { validateChildrenSize(element, 1); validateArgument(element, TagAttr.IS_PREFIX); validateArgument(element, TagAttr.OP); } private void validateProp(JsonML element) { validateChildrenSize(element, 1); if (!error) { validateArgument(element, TagAttr.NAME); } } private void validateDeleteExpr(JsonML element) { validateChildrenSize(element, 1); // TODO(dhans): maybe add that it has to be expression } private void validateDoWhileStmt(JsonML element) { validateChildrenSize(element, 2); // TODO(dhans): maybe add that the second child has to be an exception } private void validateEmptyStmt(JsonML element) { validateChildrenSize(element, 0); } private void validateForInStmt(JsonML element) { validateChildrenSize(element, 3); } private void validateForStmt(JsonML element) { validateChildrenSize(element, 4); } private void validateFunctionDecl(JsonML element) { validateFunction(element, true); } private void validateFunctionExpr(JsonML element) { validateFunction(element, false); } private void validateIdExpr(JsonML element) { validateChildrenSize(element, 0); if (!error) { validateArgument(element, TagAttr.NAME); } } private void validateIdPatt(JsonML element) { validateChildrenSize(element, 0); validateArgument(element, TagAttr.NAME); } private void validateIfStmt(JsonML element) { validateChildrenSize(element, 3); if (!error) { // TODO(dhans): check the first child is condition } } private void validateInvokeExpr(JsonML element) { validateMinChildrenSize(element, 2); validateArgument(element, TagAttr.OP); } private void validateJmpStmt(JsonML element) { // for both BreakStmt and ContinueStmt validateChildrenSize(element, 0); } private void validateLabelledStmt(JsonML element) { validateChildrenSize(element, 1); validateArgument(element, TagAttr.LABEL); } private void validateLiteralExpr(JsonML element) { validateChildrenSize(element, 0); validateArgument(element, TagAttr.TYPE); validateArgument(element, TagAttr.VALUE); } private void validateLogicalExpr(JsonML element) { validateChildrenSize(element, 2); } private void validateMemberExpr(JsonML element) { validateChildrenSize(element, 2); validateArgument(element, TagAttr.OP); } private void validateNewExpr(JsonML element) { validateMinChildrenSize(element, 1); } private void validateObjectExpr(JsonML element) { TagType[] expected = {TagType.DataProp, TagType.GetterProp, TagType.SetterProp}; for (int i = 0; i < element.childrenSize(); ++i) { validateChildType(element, expected, i); } } private void validateParamDecl(JsonML element) { for (int i = 0; i < element.childrenSize(); ++i) { validateChildType(element, TagType.IdPatt, i); } } private void validateRegExpExpr(JsonML element) { validateChildrenSize(element, 0); validateArgument(element, TagAttr.BODY); validateArgument(element, TagAttr.FLAGS); } private void validateReturnStmt(JsonML element) { validateMaxChildrenSize(element, 1); } private void validateSwitchStmt(JsonML element) { validateMinChildrenSize(element, 1); boolean defaultStmt = false; for (int i = 1; i < element.childrenSize(); ++i) { if (!defaultStmt) { validateChildType(element, new TagType[] {TagType.Case, TagType.DefaultCase}, i); } else { validateChildType(element, TagType.Case, i); } if (error) { break; } if (element.getChild(i).getType() == TagType.DefaultCase) { defaultStmt = true; } } } private void validateThisExpr(JsonML element) { validateChildrenSize(element, 0); } private void validateThrowStmt(JsonML element) { validateChildrenSize(element, 1); } private void validateTryStmt(JsonML element) { validateChildrenSize(element, 2, 3); if (error) { return; } validateChildType(element, TagType.BlockStmt, 0); TagType[] types = new TagType[] { TagType.CatchClause, TagType.Empty }; validateChildType(element, types, 1); if (element.childrenSize() > 2) { validateChildType(element, TagType.BlockStmt, 2); } } private void validateFunction(JsonML element, boolean needsName) { validateMinChildrenSize(element, 2); if (error) { return; } if (needsName) { validateChildType( element, new TagType[] { TagType.IdPatt }, 0); } else { validateChildType( element, new TagType[] { TagType.IdPatt, TagType.Empty }, 0); } validateChildType(element, TagType.ParamDecl, 1); } private void validateTypeofExpr(JsonML element) { validateChildrenSize(element, 1); } private void validateUnaryExpr(JsonML element) { validateChildrenSize(element, 1); if (!error) { validateArgument(element, TagAttr.OP); } } private void validateVarDecl(JsonML element) { validateMinChildrenSize(element, 1); TagType[] types = new TagType[] { TagType.InitPatt, TagType.IdPatt }; for (int i = 0; i < element.childrenSize(); ++i) { validateChildType(element, types, i); } } private void validateWhileStmt(JsonML element) { validateChildrenSize(element, 2); //TODO(dhans): check if the first child is expression } private void validateWithStmt(JsonML element) { validateChildrenSize(element, 2); //TODO(dhans): check if the first child is expression } private void validateArgument(JsonML element, TagAttr attr) { Object value = element.getAttribute(attr); if (value == null) { // there is an exceptional situation when the value can be null // {'value': null, 'type': 'null'} String type; if ((type = (String) element.getAttribute(TagAttr.TYPE)) != null && type.equals("null")) { return; } error = true; appendLine(String.format( MISSING_ARGUMENT, attr, element.getType())); } } private void validateChildrenSize(JsonML element, int expected) { validateChildrenSize(element, expected, expected); } private void validateChildrenSize(JsonML element, int min, int max) { validateMinChildrenSize(element, min); if (!error) { validateMaxChildrenSize(element, max); } } private void validateMinChildrenSize(JsonML element, int min) { int size = element.childrenSize(); if (size < min) { appendLine(String.format( NOT_ENOUGH_CHILDREN_FMT, element.getType(), min, size)); error = true; } } private void validateMaxChildrenSize(JsonML element, int max) { int size = element.childrenSize(); if (size > max) { appendLine(String.format( TOO_MANY_CHILDREN_FMT, element.getType().toString(), max, size)); error = true; } } private void validateIsChildExpression(JsonML element, int index) { validateChildType(element, exprTypes, index); } private void validateChildType(JsonML element, TagType expected, int index) { TagType[] types = { expected }; validateChildType(element, types, index); } private void validateChildType(JsonML element, TagType[] expected, int index) { TagType type = element.getChild(index).getType(); if (!Arrays.asList(expected).contains(type)) { appendLine(String.format( WRONG_CHILD_TYPE_FMT, index, element.getType(), printList(expected), type)); error = true; } } private void appendLine(String line) { b.append(String.format("%s", line)); } // public for test purposes only public static String printList(Object[] list) { StringBuilder builder = new StringBuilder(""); if (list.length == 1) { builder.append(list[0].toString()); } else if (list.length > 1) { builder.append('['); for (int i = 0; i < list.length; ++i) { builder.append(list[i].toString()); if (i < list.length - 1) { builder.append(", "); } } builder.append("]"); } return builder.toString(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/TagType.java0000644000175000017500000000315712115204405026364 0ustar apoapo/* * 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.jsonml; /** * List of types allowed for JsonML elements. * * @author dhans@google.com (Daniel Hans) */ public enum TagType { // *Expr types ArrayExpr, AssignExpr, BinaryExpr, CallExpr, ConditionalExpr, CountExpr, DeleteExpr, EvalExpr, FunctionExpr, IdExpr, InvokeExpr, LiteralExpr, LogicalAndExpr, LogicalOrExpr, MemberExpr, NewExpr, ObjectExpr, RegExpExpr, ThisExpr, TypeofExpr, UnaryExpr, // *Stmt types BlockStmt, BreakStmt, ContinueStmt, DebuggerStmt, DoWhileStmt, EmptyStmt, ForInStmt, ForStmt, IfStmt, LabelledStmt, ReturnStmt, SwitchStmt, ThrowStmt, TryStmt, WhileStmt, WithStmt, // *Decl types FunctionDecl, ParamDecl, PrologueDecl, // TODO VarDecl, // *Prop types DataProp, GetterProp, SetterProp, // *Patt types IdPatt, InitPatt, // *Case types Case, DefaultCase, // CatchClause type CatchClause, // Empty type Empty, // Program type (root) Program, }closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/package.html0000644000175000017500000000027312115204405026421 0ustar apoapo Provides the classes to support JsonML format and secure compiler. closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/Writer.java0000644000175000017500000006403612115204405026266 0ustar apoapo/* * 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.jsonml; import com.google.common.base.Preconditions; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Iterator; import java.util.Set; /** * Converts internal AST into JsonML tree. * * @author dhans@google.com (Daniel Hans) */ public class Writer { /** * Creates JsonML tree based on a specified AST. * @param root AST node * @return root of a created JsonML tree */ public JsonML processAst(Node root) { Preconditions.checkNotNull(root); Preconditions.checkArgument( root.getType() == Token.BLOCK || root.getType() == Token.SCRIPT); JsonML rootElement = new JsonML(TagType.BlockStmt); if (root.getType() == Token.SCRIPT) { processNode(root, rootElement); return rootElement.getChild(0); } else { Node child = root.getFirstChild(); while (child != null) { processNode(child, rootElement); child = child.getNext(); } // TODO(johnlenz): Add support for multiple scripts. return rootElement.getChild(0); } } /** * Dispatches an AST node to a function which converts it to JsonML. * @param node AST node to convert into JsonML element. * @param currentParent element to which newly created JsonML element will * be attached as a child */ private void processNode(Node node, JsonML currentParent) { switch (node.getType()) { case Token.RETURN: processReturn(node, currentParent); break; case Token.BITOR: processBinaryExpr(node, currentParent, "|"); break; case Token.BITXOR: processBinaryExpr(node, currentParent, "^"); break; case Token.BITAND: processBinaryExpr(node, currentParent, "&"); break; case Token.EQ: processBinaryExpr(node, currentParent, "=="); break; case Token.NE: processBinaryExpr(node, currentParent, "!="); break; case Token.LT: processBinaryExpr(node, currentParent, "<"); break; case Token.LE: processBinaryExpr(node, currentParent, "<="); break; case Token.GT: processBinaryExpr(node, currentParent, ">"); break; case Token.GE: processBinaryExpr(node, currentParent, ">="); break; case Token.LSH: processBinaryExpr(node, currentParent, "<<"); break; case Token.RSH: processBinaryExpr(node, currentParent, ">>"); break; case Token.URSH: processBinaryExpr(node, currentParent, ">>>"); break; case Token.ADD: processBinaryExpr(node, currentParent, "+"); break; case Token.SUB: processBinaryExpr(node, currentParent, "-"); break; case Token.MUL: processBinaryExpr(node, currentParent, "*"); break; case Token.DIV: processBinaryExpr(node, currentParent, "/"); break; case Token.MOD: processBinaryExpr(node, currentParent, "%"); break; case Token.NOT: processUnaryExpr(node, currentParent, "!"); break; case Token.BITNOT: processUnaryExpr(node, currentParent, "~"); break; case Token.POS: processUnaryExpr(node, currentParent, "+"); break; case Token.NEG: processUnaryExpr(node, currentParent, "-"); break; case Token.NEW: processNew(node, currentParent, TagType.NewExpr); break; case Token.DELPROP: processOneArgExpr(node, currentParent, TagType.DeleteExpr); break; case Token.TYPEOF: processOneArgExpr(node, currentParent, TagType.TypeofExpr); break; case Token.GETPROP: processMemberExpr(node, currentParent, "."); break; case Token.GETELEM: processMemberExpr(node, currentParent, "[]"); break; case Token.CALL: processCall(node, currentParent); break; case Token.NAME: processName(node, currentParent); break; case Token.NUMBER: case Token.STRING: case Token.NULL: case Token.FALSE: case Token.TRUE: processLiteral(node, currentParent); break; case Token.THIS: processThis(node, currentParent); break; case Token.SHEQ: processBinaryExpr(node, currentParent, "==="); break; case Token.SHNE: processBinaryExpr(node, currentParent, "!=="); break; case Token.REGEXP: processRegExp(node, currentParent); break; case Token.THROW: processThrow(node, currentParent); break; case Token.IN: processBinaryExpr(node, currentParent, "in"); break; case Token.INSTANCEOF: processBinaryExpr(node, currentParent, "instanceof"); break; case Token.ARRAYLIT: processArrayLiteral(node, currentParent); break; case Token.OBJECTLIT: processObjectLiteral(node, currentParent); break; case Token.TRY: processTry(node, currentParent); break; case Token.COMMA: processBinaryExpr(node, currentParent, ","); break; case Token.ASSIGN: processAssignExpr(node, currentParent, "="); break; case Token.ASSIGN_BITOR: processAssignExpr(node, currentParent, "|="); break; case Token.ASSIGN_BITXOR: processAssignExpr(node, currentParent, "^="); break; case Token.ASSIGN_BITAND: processAssignExpr(node, currentParent, "&="); break; case Token.ASSIGN_LSH: processAssignExpr(node, currentParent, "<<="); break; case Token.ASSIGN_RSH: processAssignExpr(node, currentParent, ">>="); break; case Token.ASSIGN_URSH: processAssignExpr(node, currentParent, ">>>="); break; case Token.ASSIGN_ADD: processAssignExpr(node, currentParent, "+="); break; case Token.ASSIGN_SUB: processAssignExpr(node, currentParent, "-="); break; case Token.ASSIGN_MUL: processAssignExpr(node, currentParent, "*="); break; case Token.ASSIGN_DIV: processAssignExpr(node, currentParent, "/="); break; case Token.ASSIGN_MOD: processAssignExpr(node, currentParent, "%="); break; case Token.HOOK: processHook(node, currentParent); break; case Token.OR: processLogicalExpr(node, currentParent, "||"); break; case Token.AND: processLogicalExpr(node, currentParent, "&&"); break; case Token.INC: processIncrDecrExpr(node, currentParent, "++"); break; case Token.DEC: processIncrDecrExpr(node, currentParent, "--"); break; case Token.FUNCTION: processFunction(node, currentParent); break; case Token.IF: processIf(node, currentParent); break; case Token.SWITCH: processSwitch(node, currentParent); break; case Token.CASE: processCase(node, currentParent, TagType.Case); break; case Token.DEFAULT_CASE: processCase(node, currentParent, TagType.DefaultCase); break; case Token.WHILE: processLoop(node, currentParent, TagType.WhileStmt); break; case Token.DO: processLoop(node, currentParent, TagType.DoWhileStmt); break; case Token.FOR: processForLoop(node, currentParent); break; case Token.BREAK: processJmp(node, currentParent, TagType.BreakStmt); break; case Token.CONTINUE: processJmp(node, currentParent, TagType.ContinueStmt); break; case Token.VAR: processVar(node, currentParent); break; case Token.WITH: processWith(node, currentParent); break; case Token.CATCH: processCatch(node, currentParent); break; case Token.VOID: processUnaryExpr(node, currentParent, "void"); break; case Token.EMPTY: processEmpty(node, currentParent); break; case Token.BLOCK: processBlock(node, currentParent); break; case Token.LABEL: processLabel(node, currentParent); break; case Token.EXPR_RESULT: processExprResult(node, currentParent); break; case Token.SCRIPT: processScript(node, currentParent); break; } } private void processAssignExpr(Node node, JsonML currentParent, String op) { processTwoArgExpr(node, currentParent, TagType.AssignExpr, op); } private void processArrayLiteral(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.ArrayExpr); currentParent.appendChild(element); Iterator it = node.children().iterator(); while (it.hasNext()) { processNode(it.next(), element); } } private void processBinaryExpr(Node node, JsonML currentParent, String op) { processTwoArgExpr(node, currentParent, TagType.BinaryExpr, op); } private void processBlock(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.BlockStmt); if (currentParent != null) { currentParent.appendChild(element); } processDirectives(node, element); for (Node child : node.children()) { processNode(child, element); } } private void processCall(Node node, JsonML currentParent) { Iterator it = node.children().iterator(); Node child = it.next(); JsonML element; // the first child may indicate that it is invoke expression // or a standard function call switch (child.getType()) { case Token.GETPROP: // a.x() case Token.GETELEM: // a[x]() // we have to process this node here and cannot call processNode(child) // other children of CALL represent arguments, so we need to have // access to them while processing InvokeExpr element = new JsonML(TagType.InvokeExpr); element.setAttribute( TagAttr.OP, child.getType() == Token.GETPROP ? "." : "[]"); currentParent.appendChild(element); // there should be exactly two children Node grandchild = child.getFirstChild(); processNode(grandchild, element); processNode(grandchild.getNext(), element); break; case Token.NAME: // Caja treats calls to eval in a special way if (child.getString().equals("eval")) { element = new JsonML(TagType.EvalExpr); } else { // element representing function name is created element = new JsonML(TagType.IdExpr); element.setAttribute(TagAttr.NAME, child.getString()); // element representing function is created element = new JsonML(TagType.CallExpr, element); } currentParent.appendChild(element); break; default: // it addresses all cases where the first argument evaluates to // another expression element = new JsonML(TagType.CallExpr); currentParent.appendChild(element); processNode(child, element); break; } // there may be arguments applied while (it.hasNext()) { processNode(it.next(), element); } } private void processCase(Node node, JsonML currentParent, TagType type) { JsonML element = new JsonML(type); currentParent.appendChild(element); Node child = node.getFirstChild(); // for case, the first child represents its argument if (type == TagType.Case) { processNode(child, element); child = child.getNext(); } // it should be a BLOCK which is required by rhino for compatibility // the writer skips the node and move on to its children Preconditions.checkNotNull(child); Preconditions.checkState(child.getType() == Token.BLOCK); child = child.getFirstChild(); while (child != null) { processNode(child, element); child = child.getNext(); } } private void processCatch(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.CatchClause); currentParent.appendChild(element); // the first child represents exception's name Node child = node.getFirstChild(); JsonML patt = new JsonML(TagType.IdPatt); patt.setAttribute(TagAttr.NAME, child.getString()); element.appendChild(patt); // the second child represents content child = child.getNext(); processNode(child, element); } private void processEmpty(Node node, JsonML currentParent) { if (currentParent.getType() == TagType.ArrayExpr) { // Empty expression are only found in Array literals currentParent.appendChild(new JsonML(TagType.Empty)); } else { currentParent.appendChild(new JsonML(TagType.EmptyStmt)); } } private void processExprResult(Node node, JsonML currentParent) { // this not interesting to JsonML, so we just need to skip it processNode(node.getFirstChild(), currentParent); } private void processForLoop(Node node, JsonML currentParent) { if (NodeUtil.isForIn(node)) { processLoop(node, currentParent, TagType.ForInStmt); } else { processLoop(node, currentParent, TagType.ForStmt); } } private void processFunction(Node node, JsonML currentParent) { JsonML element; if (NodeUtil.isFunctionDeclaration(node)) { element = new JsonML(TagType.FunctionDecl); } else { // isFunctionExpresion == true element = new JsonML(TagType.FunctionExpr); } currentParent.appendChild(element); // the first child represents function's name Node child = node.getFirstChild(); String name = child.getString(); if (!name.equals("")) { JsonML nameElement = new JsonML(TagType.IdPatt); nameElement.setAttribute(TagAttr.NAME, name); element.appendChild(nameElement); } else { element.appendChild(new JsonML(TagType.Empty)); } // the second child is a wrapper for formal parameters child = child.getNext(); JsonML params = new JsonML(TagType.ParamDecl); element.appendChild(params); Iterator it = child.children().iterator(); while (it.hasNext()) { JsonML param = new JsonML(TagType.IdPatt); Node nameNode = it.next(); param.setAttribute(TagAttr.NAME, nameNode.getString()); params.appendChild(param); } // the third child represents function's body child = child.getNext(); // it can contain some directives processDirectives(child, element); it = child.children().iterator(); while (it.hasNext()) { processNode(it.next(), element); } } private void processHook(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.ConditionalExpr); currentParent.appendChild(element); processChildren(node, element); } private void processIf(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.IfStmt); currentParent.appendChild(element); Iterator it = node.children().iterator(); // there should be at least one child while (it.hasNext()) { processNode(it.next(), element); } // append EmptyStmt for each missing part int childCount = node.getChildCount(); Preconditions.checkState(childCount >= 2); if (childCount < 3) { // no "else" part for sure element.appendChild(new JsonML(TagType.EmptyStmt)); } } private void processIncrDecrExpr(Node node, JsonML currentParent, String op) { JsonML element = new JsonML(TagType.CountExpr); currentParent.appendChild(element); if (op.equals("++")) { element.setAttribute(TagAttr.OP, "++"); } else { // op.equals("--") element.setAttribute(TagAttr.OP, "--"); } if (node.getIntProp(Node.INCRDECR_PROP) == 1) { element.setAttribute(TagAttr.IS_PREFIX, false); } else { // INCRDECR_PROP == 0 element.setAttribute(TagAttr.IS_PREFIX, true); } // there is exactly one child processNode(node.getFirstChild(), element); } private void processJmp(Node node, JsonML currentParent, TagType type) { JsonML element = new JsonML(type); currentParent.appendChild(element); // optional child may point to a label Node child = node.getFirstChild(); if (child != null) { element.setAttribute(TagAttr.LABEL, child.getString()); } } private void processLabel(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.LabelledStmt); currentParent.appendChild(element); // the first child represents label's name Node child = node.getFirstChild(); element.setAttribute(TagAttr.LABEL, child.getString()); // the second child represents labeled content child = child.getNext(); processNode(child, element); } private void processLiteral(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.LiteralExpr); switch (node.getType()) { case Token.NUMBER: element.setAttribute(TagAttr.TYPE, "number"); element.setAttribute(TagAttr.VALUE, node.getDouble()); break; case Token.STRING: element.setAttribute(TagAttr.TYPE, "string"); element.setAttribute(TagAttr.VALUE, node.getString()); break; case Token.NULL: element.setAttribute(TagAttr.TYPE, "null"); element.setAttribute(TagAttr.VALUE, null); break; case Token.TRUE: element.setAttribute(TagAttr.TYPE, "boolean"); element.setAttribute(TagAttr.VALUE, true); break; case Token.FALSE: element.setAttribute(TagAttr.TYPE, "boolean"); element.setAttribute(TagAttr.VALUE, false); break; default: throw new IllegalArgumentException("Illegal type of node."); } currentParent.appendChild(element); } private void processLogicalExpr(Node node, JsonML currentParent, String op) { if (op.equals("||")) { processTwoArgExpr(node, currentParent, TagType.LogicalOrExpr); } else if (op.endsWith("&&")) { processTwoArgExpr(node, currentParent, TagType.LogicalAndExpr); } else { throw new IllegalArgumentException("Unsupported value of op argument."); } } private void processLoop(Node node, JsonML currentParent, TagType type) { JsonML element = new JsonML(type); currentParent.appendChild(element); processChildren(node, element); } private void processMemberExpr(Node node, JsonML currentParent, String op) { JsonML element = new JsonML(TagType.MemberExpr); element.setAttribute(TagAttr.OP, op); currentParent.appendChild(element); // there should be exactly two children Node child = node.getFirstChild(); processNode(child, element); processNode(child.getNext(), element); } private void processName(Node node, JsonML currentParent) { Preconditions.checkState(!node.hasChildren()); JsonML element = new JsonML(TagType.IdExpr); element.setAttribute(TagAttr.NAME, node.getString()); currentParent.appendChild(element); } private void processNew(Node node, JsonML currentParent, TagType type) { JsonML element = new JsonML(type); currentParent.appendChild(element); processChildren(node, element); } private void processObjectLiteral(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.ObjectExpr); currentParent.appendChild(element); for (Node key : node.children()) { Node value = key.getFirstChild(); JsonML item; Object name; switch (key.getType()) { case Token.STRING_KEY: item = new JsonML(TagType.DataProp); name = key.getString(); break; case Token.GETTER_DEF: item = new JsonML(TagType.GetterProp); name = key.getString(); break; case Token.SETTER_DEF: item = new JsonML(TagType.SetterProp); name = key.getString(); break; default: throw new IllegalArgumentException("Illegal type of node."); } item.setAttribute(TagAttr.NAME, name); processNode(value, item); element.appendChild(item); } } private void processRegExp(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.RegExpExpr); currentParent.appendChild(element); // first child represents expression's body Node child = node.getFirstChild(); element.setAttribute(TagAttr.BODY, child.getString()); // optional second child represents flags String flags = ""; child = child.getNext(); if (child != null) { flags = child.getString(); } element.setAttribute(TagAttr.FLAGS, flags); } private void processSwitch(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.SwitchStmt); currentParent.appendChild(element); // the first child represents expression Node child = node.getFirstChild(); processNode(child, element); // next children represent particular cases for (Node c = child.getNext(); c != null; c = c.getNext()) { processNode(c, element); } } private void processThis(Node node, JsonML currentParent) { currentParent.appendChild(new JsonML(TagType.ThisExpr)); } private void processThrow(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.ThrowStmt); currentParent.appendChild(element); // there is exactly one child processNode(node.getFirstChild(), element); } private void processTry(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.TryStmt); currentParent.appendChild(element); // first child represents actual try block Node child = node.getFirstChild(); processNode(child, element); // second child (precisely: child of that child) represents catch block child = child.getNext(); if (child.hasChildren()) { processNode(child.getFirstChild(), element); } else { // catch block is not present element.appendChild(new JsonML(TagType.Empty)); } //optional third child represents finally block child = child.getNext(); if (child != null) { processNode(child, element); } } private void processTwoArgExpr(Node node, JsonML currentParent, TagType type) { processTwoArgExpr(node, currentParent, type, null); } private void processTwoArgExpr(Node node, JsonML currentParent, TagType type, String op) { JsonML element = new JsonML(type); if (op != null) { element.setAttribute(TagAttr.OP, op); } currentParent.appendChild(element); Preconditions.checkState(node.getChildCount() == 2); Node child = node.getFirstChild(); processNode(child, element); processNode(child.getNext(), element); } /** * Process nodes which JsonML represents by UnaryExpr. * @param node node to process * @param op operation for this unary expression - depends on node type */ private void processUnaryExpr(Node node, JsonML currentParent, String op) { JsonML element = new JsonML(TagType.UnaryExpr); element.setAttribute(TagAttr.OP, op); currentParent.appendChild(element); processNode(node.getFirstChild(), element); } private void processVar(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.VarDecl); currentParent.appendChild(element); //there may be many actual declarations Iterator it = node.children().iterator(); while (it.hasNext()) { Node child = it.next(); // this node represents var's id // its own child represents initial value JsonML id = new JsonML(TagType.IdPatt); id.setAttribute(TagAttr.NAME, child.getString()); if (child.hasChildren()) { JsonML patt = new JsonML(TagType.InitPatt); element.appendChild(patt); patt.appendChild(id); processNode(child.getFirstChild(), patt); } else { element.appendChild(id); } } } private void processReturn(Node currentNode, JsonML currentParent) { JsonML element = new JsonML(TagType.ReturnStmt); currentParent.appendChild(element); // there is exactly one child if return statement is not empty if (currentNode.hasChildren()) { processNode(currentNode.getFirstChild(), element); } } private void processScript(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.Program); currentParent.appendChild(element); processDirectives(node, element); processChildren(node, element); } private void processWith(Node node, JsonML currentParent) { JsonML element = new JsonML(TagType.WithStmt); currentParent.appendChild(element); // the first child represent object Node child = node.getFirstChild(); processNode(child, element); // the second one represents content child = child.getNext(); processNode(child, element); } private void processChildren(Node node, JsonML currentParent) { for (Node child : node.children()) { processNode(child, currentParent); } } private void processDirectives(Node node, JsonML currentParent) { Set directives = node.getDirectives(); if (directives == null) { return; } for (String directive : directives) { JsonML element = new JsonML(TagType.PrologueDecl); element.setAttribute(TagAttr.DIRECTIVE, directive); element.setAttribute(TagAttr.VALUE, directive); currentParent.appendChild(element); } } private void processOneArgExpr(Node node, JsonML currentParent, TagType type) { JsonML element = new JsonML(type); currentParent.appendChild(element); // there is only one child node processNode(node.getFirstChild(), element); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/jsonml/JsonMLUtil.java0000644000175000017500000002124112115204405027001 0ustar apoapo/* * 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.jsonml; import org.json.JSONArray; import org.json.JSONObject; import java.util.Iterator; /** * JsonMLUtil contains utilities for the JsonML object. * * @author dhans@google.com (Daniel Hans) */ public class JsonMLUtil { /** * Checks if the specified JsonML element represents an expression. */ public static boolean isExpression(JsonML element) { switch (element.getType()) { case ArrayExpr: case AssignExpr: case BinaryExpr: case CallExpr: case ConditionalExpr: case CountExpr: case DeleteExpr: case EvalExpr: case FunctionExpr: case IdExpr: case InvokeExpr: case LiteralExpr: case LogicalAndExpr: case LogicalOrExpr: case MemberExpr: case NewExpr: case ObjectExpr: case RegExpExpr: case ThisExpr: case TypeofExpr: case UnaryExpr: return true; default: return false; } } /** * Parses JSON string which contains serialized JsonML content. * @param jsonml string representation of JsonML * @return root element of a JsonML tree */ public static JsonML parseString(String jsonml) throws Exception { return parseElement(new JSONArray(jsonml)); } private static JsonML parseElement(JSONArray element) throws Exception { JsonML jsonMLElement = new JsonML(TagType.valueOf(element.getString(0))); // set attributes for the JsonML element JSONObject attrs = element.getJSONObject(1); Iterator it = attrs.keys(); while (it.hasNext()) { String key = (String) it.next(); Object value = attrs.get(key); TagAttr tag = TagAttr.get(key); // an unsupported attribute if (tag == null) { continue; } if (value instanceof Number) { value = ((Number) value).doubleValue(); } switch (tag) { case NAME: case BODY: case FLAGS: case OP: case TYPE: case IS_PREFIX: case LABEL: jsonMLElement.setAttribute(tag, value); break; case VALUE: // we do not want to deal with JSONObject.NULL if (value != null && value.equals(null)) { value = null; } // we want all numbers to be stored as double values if (value instanceof Number) { jsonMLElement.setAttribute(tag, ((Number) value).doubleValue()); } else { jsonMLElement.setAttribute(tag, value); } break; default: } } // recursively set children for the JsonML element for (int i = 2; i < element.length(); ++i) { jsonMLElement.appendChild(parseElement(element.getJSONArray(i))); } return jsonMLElement; } /** * Compares two specified JsonML trees. * * Two JsonML nodes are considered to be equal when the following conditions * are met: * * - have the same type * - have the same attributes from the list of attributes to compare * - have the same number of children * - nodes in each pair of corresponding children are equal * * Two JsonML trees are equal, if their roots are equal. * * When two nodes are compared, only the following attributes are taken * into account: * TagAttr.BODY, TagAttr.FLAGS, TagAttr.IS_PREFIX, TagAttr.LABEL, * TagAttr.NAME, TagAttr.OP, TagAttr.TYPE, TagAttr.VALUE * Generally, the comparator does not care about debugging attributes. * * @return * Returns string describing the inequality in the following format: * * The trees are not equal: * * Tree1: * -- string representation of Tree1 * * Tree2: * -- string representation of Tree2 * * Subtree1: * -- string representation of the subtree of the Tree1 which is not * -- equal to the corresponding subtree of the Tree2 * * Subtree2: * -- see Subtree1 * * If the trees are equal, null is returned. */ public static String compare(JsonML tree1, JsonML tree2) { return (new JsonMLComparator(tree1, tree2)).compare(); } /** * Returns true if the trees are equal, false otherwise. */ static boolean compareSilent(JsonML tree1, JsonML tree2) { return (new JsonMLComparator(tree1, tree2)).compareSilent(); } /** * Helper class which actually compares two given JsonML trees. * */ private static class JsonMLComparator { private static final TagAttr[] ATTRS_TO_COMPARE = { TagAttr.BODY, TagAttr.FLAGS, TagAttr.IS_PREFIX, TagAttr.LABEL, TagAttr.NAME, TagAttr.OP, TagAttr.TYPE, TagAttr.VALUE }; private JsonML treeA; private JsonML treeB; private JsonML mismatchA; private JsonML mismatchB; JsonMLComparator(JsonML treeA, JsonML treeB) { this.treeA = treeA; this.treeB = treeB; if (compareElements(treeA, treeB)) { mismatchA = null; mismatchB = null; } } private boolean setMismatch(JsonML a, JsonML b) { mismatchA = a; mismatchB = b; return false; } /** * Check if two elements are equal (including comparing their children). */ private boolean compareElements(JsonML a, JsonML b) { // the elements are considered to be equal if they are both null if (a == null || b == null) { if (a == null && b == null) { return true; } else { return setMismatch(a, b); } } // the elements themselves have to be equivalent if (!areEquivalent(a, b)) { return setMismatch(a, b); } // they both have to have the same number of children if (a.childrenSize() != b.childrenSize()) { return setMismatch(a, b); } // all the children has to be the same Iterator itA = a.getChildren().listIterator(); Iterator itB = b.getChildren().listIterator(); while (itA.hasNext()) { if (!compareElements(itA.next(), itB.next())) { return false; } } return true; } /** * Checks if two elements are semantically the same. */ private boolean areEquivalent(JsonML a, JsonML b) { // both elements must have the same type if (a.getType() != b.getType()) { return false; } for (TagAttr attr : ATTRS_TO_COMPARE) { if (!compareAttribute(attr, a, b)) { return false; } } return true; } private boolean compareAttribute(TagAttr attr, JsonML a, JsonML b) { Object valueA = a.getAttributes().get(attr); Object valueB = b.getAttributes().get(attr); // none of the elements have the attribute if (valueA == null && valueB == null) { return true; } // only one of the elements has the attribute if (valueA == null || valueB == null) { return false; } // check if corresponding values are equal if (!(valueA.equals(valueB))) { // there is still a chance that both attributes are numbers, but are // represented by different classes Double doubleA = null, doubleB = null; if (valueA instanceof Number) { doubleA = ((Number) valueA).doubleValue(); } else if (valueA instanceof String) { doubleA = Double.valueOf((String) valueA); } else { return false; } if (valueB instanceof Number) { doubleB = ((Number) valueB).doubleValue(); } else if (valueB instanceof String) { doubleB = Double.valueOf((String) valueB); } else { return false; } if (!doubleA.equals(doubleB)) { return false; } } return true; } private boolean compareSilent() { return mismatchA == null && mismatchB == null; } private String compare() { if (compareSilent()) { return null; } return "The trees are not equal: " + "\n\nTree1:\n " + treeA.toStringTree() + "\n\nTree2:\n " + treeB.toStringTree() + "\n\nSubtree1:\n " + mismatchA.toStringTree() + "\n\nSubtree2:\n " + mismatchB.toStringTree(); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/jscomp/Strings.java0000644000175000017500000001105012115204405025125 0ustar apoapo/* * 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; /** * Guava code that will eventually be open-sourced properly. Package-private * until they're able to do that. A lot of these methods are discouraged * anyways. * * @author nicksantos@google.com (Nick Santos) */ class Strings { private Strings() {} // All static /** * If this given string is of length {@code maxLength} or less, it will * be returned as-is. * Otherwise, it will be truncated to {@code maxLength}, regardless of whether * there are any space characters in the String. If an ellipsis is requested * to be appended to the truncated String, the String will be truncated so * that the ellipsis will also fit within maxLength. * If no truncation was necessary, no ellipsis will be added. * @param source the String to truncate if necessary * @param maxLength the maximum number of characters to keep * @param addEllipsis if true, and if the String had to be truncated, * add "..." to the end of the String before returning. Additionally, * the ellipsis will only be added if maxLength is greater than 3. * @return the original string if it's length is less than or equal to * maxLength, otherwise a truncated string as mentioned above */ static String truncateAtMaxLength(String source, int maxLength, boolean addEllipsis) { if (source.length() <= maxLength) { return source; } if (addEllipsis && maxLength > 3) { return unicodePreservingSubstring(source, 0, maxLength - 3) + "..."; } return unicodePreservingSubstring(source, 0, maxLength); } /** * Normalizes {@code index} such that it respects Unicode character * boundaries in {@code str}. * *

      If {@code index} is the low surrogate of a Unicode character, * the method returns {@code index - 1}. Otherwise, {@code index} is * returned. * *

      In the case in which {@code index} falls in an invalid surrogate pair * (e.g. consecutive low surrogates, consecutive high surrogates), or if * if it is not a valid index into {@code str}, the original value of * {@code index} is returned. * * @param str the String * @param index the index to be normalized * @return a normalized index that does not split a Unicode character */ private static int unicodePreservingIndex(String str, int index) { if (index > 0 && index < str.length()) { if (Character.isHighSurrogate(str.charAt(index - 1)) && Character.isLowSurrogate(str.charAt(index))) { return index - 1; } } return index; } /** * Returns a substring of {@code str} that respects Unicode character * boundaries. * *

      The string will never be split between a [high, low] surrogate pair, * as defined by {@link Character#isHighSurrogate} and * {@link Character#isLowSurrogate}. * *

      If {@code begin} or {@code end} are the low surrogate of a Unicode * character, it will be offset by -1. * *

      This behavior guarantees that * {@code str.equals(StringUtil.unicodePreservingSubstring(str, 0, n) + * StringUtil.unicodePreservingSubstring(str, n, str.length())) } is * true for all {@code n}. *

    13. * *

      This means that unlike {@link String#substring(int, int)}, the length of * the returned substring may not necessarily be equivalent to * {@code end - begin}. * * @param str the original String * @param begin the beginning index, inclusive * @param end the ending index, exclusive * @return the specified substring, possibly adjusted in order to not * split Unicode surrogate pairs * @throws IndexOutOfBoundsException if the {@code begin} is negative, * or {@code end} is larger than the length of {@code str}, or * {@code begin} is larger than {@code end} */ private static String unicodePreservingSubstring( String str, int begin, int end) { return str.substring(unicodePreservingIndex(str, begin), unicodePreservingIndex(str, end)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/0000755000175000017500000000000012115204405022460 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/SimpleErrorReporter.java0000644000175000017500000000646412115204405027323 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import java.util.ArrayList; import java.util.List; /** * A simple {@link ErrorReporter} that collects warnings and errors and makes * them accessible via {@link #errors()} and {@link #warnings()}. * */ public class SimpleErrorReporter implements ErrorReporter { private List warnings = null; private List errors = null; @Override public void warning(String message, String sourceName, int line, int lineOffset) { if (warnings == null) { warnings = new ArrayList(); } warnings.add(formatDetailedMessage(message, sourceName, line)); } @Override public void error(String message, String sourceName, int line, int lineOffset) { if (errors == null) { errors = new ArrayList(); } errors.add(formatDetailedMessage(message, sourceName, line)); } /** * Returns the list of errors, or {@code null} if there were none. */ public List errors() { return errors; } /** * Returns the list of warnings, or {@code null} if there were none. */ public List warnings() { return warnings; } private String formatDetailedMessage( String message, String sourceName, int lineNumber) { String details = message; if (sourceName == null || lineNumber <= 0) { return details; } StringBuilder buf = new StringBuilder(details); buf.append(" ("); if (sourceName != null) { buf.append(sourceName); } if (lineNumber > 0) { buf.append('#'); buf.append(lineNumber); } buf.append(')'); return buf.toString(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/0000755000175000017500000000000012115204405023776 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/CanCastToVisitor.java0000644000175000017500000001372312115204405030046 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A "can cast to" relationship visitor. */ class CanCastToVisitor implements RelationshipVisitor { @Override public Boolean caseUnknownType(JSType thisType, JSType thatType) { return true; } @Override public Boolean caseNoType(JSType thatType) { return true; } @Override public Boolean caseNoObjectType(JSType thatType) { return true; // TODO(johnlenz): restrict to objects } @Override public Boolean caseAllType(JSType thatType) { return true; } boolean canCastToUnion(JSType thisType, UnionType unionType) { for (JSType type : unionType.getAlternates()) { if (thisType.visit(this, type)) { return true; } } return false; } boolean canCastToFunction(JSType thisType, FunctionType functionType) { if (thisType.isFunctionType()) { // TODO(johnlenz): visit function parts return true; } else { return thisType.isSubtype(functionType) || functionType.isSubtype(thisType); } } private boolean isInterface(JSType type) { ObjectType objType = type.toObjectType(); if (objType != null) { JSType constructor = objType.getConstructor(); return constructor != null && constructor.isInterface(); } return false; } Boolean castCastToHelper(JSType thisType, JSType thatType) { if (thatType.isUnknownType() || thatType.isAllType() || thatType.isNoObjectType() // TODO(johnlenz): restrict to objects || thatType.isNoType()) { return true; } else if (thisType.isRecordType() || thatType.isRecordType()) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (isInterface(thisType) || isInterface(thatType)) { return true; // TODO(johnlenz): are there any misuses we can catch? } else if (thatType.isEnumElementType()) { return thisType.visit(this, thatType.toMaybeEnumElementType().getPrimitiveType()); } else if (thatType.isUnionType()) { return canCastToUnion(thisType, thatType.toMaybeUnionType()); } else if (thatType.isFunctionType()) { return canCastToFunction(thisType, thatType.toMaybeFunctionType()); } else if (thatType.isTemplatizedType()) { // TODO(johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.visit(this, thatType.toMaybeTemplatizedType().getReferencedTypeInternal()); } return thisType.isSubtype(thatType) || thatType.isSubtype(thisType); } @Override public Boolean caseValueType(ValueType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseObjectType(ObjectType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseFunctionType(FunctionType thisType, JSType thatType) { return castCastToHelper(thisType, thatType); } @Override public Boolean caseUnionType(UnionType thisType, JSType thatType) { boolean visited = false; for (JSType type : thisType.getAlternates()) { if (type.isVoidType() || type.isNullType()) { // Don't allow if the only match between the types is null or void, // otherwise any nullable type would be castable to any other nullable // type and we don't want that. } else { visited = true; if (type.visit(this, thatType)) { return true; } } } // Special case the "null|undefined" union and allow it to be cast // to any cast to any type containing allowing either null|undefined. if (!visited) { JSType NULL_TYPE = thisType.getNativeType(JSTypeNative.NULL_TYPE); JSType VOID_TYPE = thisType.getNativeType(JSTypeNative.VOID_TYPE); return NULL_TYPE.visit(this, thatType) || VOID_TYPE.visit(this, thatType); } return false; } @Override public Boolean caseTemplatizedType( TemplatizedType thisType, JSType thatType) { // TODO(johnlenz): once the templated type work is finished, // restrict the type parameters. return thisType.getReferencedTypeInternal().visit(this, thatType); } @Override public Boolean caseTemplateType(TemplateType thisType, JSType thatType) { return true; } @Override public Boolean caseEnumElementType( EnumElementType typeType, JSType thatType) { return typeType.getPrimitiveType().visit(this, thatType); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/TemplateType.java0000644000175000017500000000513012115204405027255 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ /** * For functions with function(this: T, ...) and T as arguments, type inference * will set the type of this on a function literal argument to the actual type * of T. * */ package com.google.javascript.rhino.jstype; public class TemplateType extends ProxyObjectType { private static final long serialVersionUID = 1L; private final String name; TemplateType(JSTypeRegistry registry, String name) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); this.name = name; } @Override public String getReferenceName() { return name; } @Override String toStringHelper(boolean forAnnotations) { return name; } @Override public TemplateType toMaybeTemplateType() { return this; } @Override public boolean hasAnyTemplateTypesInternal() { return true; } @Override public T visit(Visitor visitor) { return visitor.caseTemplateType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseTemplateType(this, that); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ValueType.java0000644000175000017500000000415012115204405026557 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; /** * Value types (null, void, number, boolean, string). */ abstract class ValueType extends JSType { ValueType(JSTypeRegistry registry) { super(registry); } @Override final JSType resolveInternal(ErrorReporter t, StaticScope scope) { return this; } @Override public boolean hasDisplayName() { return true; } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseValueType(this, that); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/InstanceObjectType.java0000644000175000017500000001224112115204405030376 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Preconditions; import com.google.javascript.rhino.Node; /** * An object type that is an instance of some function constructor. */ class InstanceObjectType extends PrototypeObjectType { private static final long serialVersionUID = 1L; private final FunctionType constructor; InstanceObjectType(JSTypeRegistry registry, FunctionType constructor) { this(registry, constructor, false); } InstanceObjectType(JSTypeRegistry registry, FunctionType constructor, boolean isNativeType) { super(registry, null, null, isNativeType, constructor.getTemplateTypeMap()); Preconditions.checkNotNull(constructor); this.constructor = constructor; } @Override public String getReferenceName() { return getConstructor().getReferenceName(); } @Override public boolean hasReferenceName() { return getConstructor().hasReferenceName(); } @Override public ObjectType getImplicitPrototype() { return getConstructor().getPrototype(); } @Override public FunctionType getConstructor() { return constructor; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { ObjectType proto = getImplicitPrototype(); if (proto != null && proto.hasOwnDeclaredProperty(name)) { return false; } return super.defineProperty(name, type, inferred, propertyNode); } @Override String toStringHelper(boolean forAnnotations) { if (constructor.hasReferenceName()) { return constructor.getReferenceName(); } else { return super.toStringHelper(forAnnotations); } } @Override boolean isTheObjectType() { return getConstructor().isNativeObjectType() && "Object".equals(getReferenceName()); } @Override public boolean isInstanceType() { return true; } @Override public boolean isArrayType() { return getConstructor().isNativeObjectType() && "Array".equals(getReferenceName()); } @Override public boolean isStringObjectType() { return getConstructor().isNativeObjectType() && "String".equals(getReferenceName()); } @Override public boolean isBooleanObjectType() { return getConstructor().isNativeObjectType() && "Boolean".equals(getReferenceName()); } @Override public boolean isNumberObjectType() { return getConstructor().isNativeObjectType() && "Number".equals(getReferenceName()); } @Override public boolean isDateType() { return getConstructor().isNativeObjectType() && "Date".equals(getReferenceName()); } @Override public boolean isRegexpType() { return getConstructor().isNativeObjectType() && "RegExp".equals(getReferenceName()); } @Override public boolean isNominalType() { return hasReferenceName(); } /** * If this is equal to a NamedType object, its hashCode must be equal * to the hashCode of the NamedType object. */ @Override public int hashCode() { if (hasReferenceName()) { return getReferenceName().hashCode(); } else { return super.hashCode(); } } @Override public Iterable getCtorImplementedInterfaces() { return getConstructor().getImplementedInterfaces(); } @Override public Iterable getCtorExtendedInterfaces() { return getConstructor().getExtendedInterfaces(); } // The owner will always be a resolved type, so there's no need to set // the constructor in resolveInternal. // (it would lead to infinite loops if we did). // JSType resolveInternal(ErrorReporter t, StaticScope scope); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ArrowType.java0000644000175000017500000002416412115204405026604 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The arrow type is an internal type that models the functional arrow type * seen in typical functional programming languages. It is used solely for * separating the management of the arrow type from the complex * {@link FunctionType} that models JavaScript's notion of functions. */ final class ArrowType extends JSType { private static final long serialVersionUID = 1L; final Node parameters; JSType returnType; // Whether the return type is inferred. final boolean returnTypeInferred; ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType) { this(registry, parameters, returnType, false); } ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType, boolean returnTypeInferred) { super(registry); this.parameters = parameters == null ? registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) : parameters; this.returnType = returnType == null ? getNativeType(UNKNOWN_TYPE) : returnType; this.returnTypeInferred = returnTypeInferred; } @Override public boolean isSubtype(JSType other) { if (!(other instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) other; // This is described in Draft 2 of the ES4 spec, // Section 3.4.7: Subtyping Function Types. // this.returnType <: that.returnType (covariant) if (!this.returnType.isSubtype(that.returnType)) { return false; } // that.paramType[i] <: this.paramType[i] (contravariant) // // If this.paramType[i] is required, // then that.paramType[i] is required. // // In theory, the "required-ness" should work in the other direction as // well. In other words, if we have // // function f(number, number) {} // function g(number) {} // // Then f *should* not be a subtype of g, and g *should* not be // a subtype of f. But in practice, we do not implement it this way. // We want to support the use case where you can pass g where f is // expected, and pretend that g ignores the second argument. // That way, you can have a single "no-op" function, and you don't have // to create a new no-op function for every possible type signature. // // So, in this case, g < f, but f !< g Node thisParam = parameters.getFirstChild(); Node thatParam = that.parameters.getFirstChild(); while (thisParam != null && thatParam != null) { JSType thisParamType = thisParam.getJSType(); JSType thatParamType = thatParam.getJSType(); if (thisParamType != null) { if (thatParamType == null || !thatParamType.isSubtype(thisParamType)) { return false; } } boolean thisIsVarArgs = thisParam.isVarArgs(); boolean thatIsVarArgs = thatParam.isVarArgs(); boolean thisIsOptional = thisIsVarArgs || thisParam.isOptionalArg(); boolean thatIsOptional = thatIsVarArgs || thatParam.isOptionalArg(); // "that" can't be a supertype, because it's missing a required argument. if (!thisIsOptional && thatIsOptional) { // NOTE(nicksantos): In our type system, we use {function(...?)} and // {function(...NoType)} to to indicate that arity should not be // checked. Strictly speaking, this is not a correct formulation, // because now a sub-function can required arguments that are var_args // in the super-function. So we special-case this. boolean isTopFunction = thatIsVarArgs && (thatParamType == null || thatParamType.isUnknownType() || thatParamType.isNoType()); if (!isTopFunction) { return false; } } // don't advance if we have variable arguments if (!thisIsVarArgs) { thisParam = thisParam.getNext(); } if (!thatIsVarArgs) { thatParam = thatParam.getNext(); } // both var_args indicates the end if (thisIsVarArgs && thatIsVarArgs) { thisParam = null; thatParam = null; } } // "that" can't be a supertype, because it's missing a required argument. if (thisParam != null && !thisParam.isOptionalArg() && !thisParam.isVarArgs() && thatParam == null) { return false; } return true; } /** * @return True if our parameter spec is equal to {@code that}'s parameter * spec. */ boolean hasEqualParameters(ArrowType that, EquivalenceMethod eqMethod) { Node thisParam = parameters.getFirstChild(); Node otherParam = that.parameters.getFirstChild(); while (thisParam != null && otherParam != null) { JSType thisParamType = thisParam.getJSType(); JSType otherParamType = otherParam.getJSType(); if (thisParamType != null) { // Both parameter lists give a type for this param, it should be equal if (otherParamType != null && !thisParamType.checkEquivalenceHelper( otherParamType, eqMethod)) { return false; } } else { if (otherParamType != null) { return false; } } // Check var_args/optionality if (thisParam.isOptionalArg() != otherParam.isOptionalArg()) { return false; } if (thisParam.isVarArgs() != otherParam.isVarArgs()) { return false; } thisParam = thisParam.getNext(); otherParam = otherParam.getNext(); } // One of the parameters is null, so the types are only equal if both // parameter lists are null (they are equal). return thisParam == otherParam; } boolean checkArrowEquivalenceHelper( ArrowType that, EquivalenceMethod eqMethod) { // Please keep this method in sync with the hashCode() method below. if (!returnType.checkEquivalenceHelper(that.returnType, eqMethod)) { return false; } return hasEqualParameters(that, eqMethod); } @Override public int hashCode() { int hashCode = 0; if (returnType != null) { hashCode += returnType.hashCode(); } if (returnTypeInferred) { hashCode += 1; } if (parameters != null) { Node param = parameters.getFirstChild(); while (param != null) { JSType paramType = param.getJSType(); if (paramType != null) { hashCode += paramType.hashCode(); } param = param.getNext(); } } return hashCode; } @Override public JSType getLeastSupertype(JSType that) { throw new UnsupportedOperationException(); } @Override public JSType getGreatestSubtype(JSType that) { throw new UnsupportedOperationException(); } @Override public TernaryValue testForEquality(JSType that) { throw new UnsupportedOperationException(); } @Override public T visit(Visitor visitor) { throw new UnsupportedOperationException(); } @Override T visit(RelationshipVisitor visitor, JSType that) { throw new UnsupportedOperationException(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { returnType = safeResolve(returnType, t, scope); if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); } } return this; } boolean hasUnknownParamsOrReturn() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type == null || type.isUnknownType()) { return true; } } } return returnType == null || returnType.isUnknownType(); } @Override String toStringHelper(boolean forAnnotations) { return "[ArrowType]"; } @Override public boolean hasAnyTemplateTypesInternal() { return returnType.hasAnyTemplateTypes() || hasTemplatedParameterType(); } private boolean hasTemplatedParameterType() { if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { JSType type = paramNode.getJSType(); if (type != null && type.hasAnyTemplateTypes()) { return true; } } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ModificationVisitor.java0000644000175000017500000001465712115204405030643 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.javascript.rhino.Node; import java.util.List; /** * A visitor implementation that enables type substitutions. * * @author johnlenz@google.com (John Lenz) */ public class ModificationVisitor implements Visitor { private final JSTypeRegistry registry; public ModificationVisitor(JSTypeRegistry registry) { this.registry = registry; } @Override public JSType caseNoType() { return getNativeType(JSTypeNative.NO_TYPE); } @Override public JSType caseEnumElementType(EnumElementType type) { return type; } @Override public JSType caseAllType() { return getNativeType(JSTypeNative.ALL_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(JSTypeNative.BOOLEAN_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(JSTypeNative.NO_OBJECT_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { if (isNativeFunctionType(type)) { return type; } // TODO(johnlenz): remove this simplifying assumption... if (!type.isOrdinaryFunction()) { return type; } boolean changed = false; JSType beforeThis = type.getTypeOfThis(); JSType afterThis = coerseToThisType(beforeThis.visit(this)); if (beforeThis != afterThis) { changed = true; } JSType beforeReturn = type.getReturnType(); JSType afterReturn = beforeReturn.visit(this); if (beforeReturn != afterReturn) { changed = true; } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(registry); for (Node paramNode : type.getParameters()) { JSType beforeParamType = paramNode.getJSType(); JSType afterParamType = beforeParamType.visit(this); if (beforeParamType != afterParamType) { changed = true; } if (paramNode.isOptionalArg()) { paramBuilder.addOptionalParams(afterParamType); } else if (paramNode.isVarArgs()) { paramBuilder.addVarArgs(afterParamType); } else { paramBuilder.addRequiredParams(afterParamType); } } if (changed) { // TODO(johnlenz): should we support preserving template keys? FunctionBuilder builder = new FunctionBuilder(registry); builder.withParams(paramBuilder); builder.withReturnType(afterReturn); builder.withTypeOfThis(afterThis); return builder.build(); } return type; } private JSType coerseToThisType(JSType type) { return type != null ? type : registry.getNativeObjectType( JSTypeNative.UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType objType) { return objType; } @Override public JSType caseTemplatizedType(TemplatizedType type) { boolean changed = false; ObjectType beforeBaseType = type.getReferencedType(); ObjectType afterBaseType = ObjectType.cast(beforeBaseType.visit(this)); if (beforeBaseType != afterBaseType) { changed = true; } ImmutableList.Builder builder = ImmutableList.builder(); for (JSType beforeTemplateType : type.getTemplateTypes()) { JSType afterTemplateType = beforeTemplateType.visit(this); if (beforeTemplateType != afterTemplateType) { changed = true; } builder.add(afterTemplateType); } if (changed) { type = registry.createTemplatizedType(afterBaseType, builder.build()); } return type; } @Override public JSType caseUnknownType() { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } @Override public JSType caseNullType() { return getNativeType(JSTypeNative.NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(JSTypeNative.NUMBER_TYPE); } @Override public JSType caseStringType() { return getNativeType(JSTypeNative.STRING_TYPE); } @Override public JSType caseVoidType() { return getNativeType(JSTypeNative.VOID_TYPE); } @Override public JSType caseUnionType(UnionType type) { boolean changed = false; List results = Lists.newArrayList(); for (JSType alternative : type.getAlternates()) { JSType replacement = alternative.visit(this); if (replacement != alternative) { changed = true; } results.add(replacement); } if (changed) { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : results) { builder.addAlternate(alternate); } return builder.build(); // maybe not a union } return type; } @Override public JSType caseTemplateType(TemplateType type) { return type; } private JSType getNativeType(JSTypeNative nativeType) { return registry.getNativeType(nativeType); } private boolean isNativeFunctionType(FunctionType type) { return type.isNativeObjectType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/BooleanLiteralSet.java0000644000175000017500000000572412115204405030221 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A set in the domain {true,false}. * There are four possible sets: {}, {true}, {false}, {true,false}. * */ public enum BooleanLiteralSet { EMPTY, TRUE, FALSE, BOTH; private BooleanLiteralSet fromOrdinal(int ordinal) { switch (ordinal) { case 0: return EMPTY; case 1: return TRUE; case 2: return FALSE; case 3: return BOTH; default: throw new IllegalArgumentException("Ordinal: " + ordinal); } } /** * Computes the intersection of this set and {@code that}. */ public BooleanLiteralSet intersection(BooleanLiteralSet that) { return fromOrdinal(this.ordinal() & that.ordinal()); } /** * Computes the union of this set and {@code that}. */ public BooleanLiteralSet union(BooleanLiteralSet that) { return fromOrdinal(this.ordinal() | that.ordinal()); } /** * Returns whether {@code this} contains the given literal value. */ public boolean contains(boolean literalValue) { switch (this.ordinal()) { case 0: return false; case 1: return literalValue; case 2: return !literalValue; case 3: return true; default: throw new IndexOutOfBoundsException("Ordinal: " + this.ordinal()); } } /** * Returns the singleton set {literalValue}. */ public static BooleanLiteralSet get(boolean literalValue) { return literalValue ? TRUE : FALSE; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/UnionType.java0000644000175000017500000004304612115204405026602 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.javascript.rhino.ErrorReporter; import java.util.Collection; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; /** * The {@code UnionType} implements a common JavaScript idiom in which the * code is specifically designed to work with multiple input types. Because * JavaScript always knows the run-time type of an object value, this is safer * than a C union.

      * * For instance, values of the union type {@code (String,boolean)} can be of * type {@code String} or of type {@code boolean}. The commutativity of the * statement is captured by making {@code (String,boolean)} and * {@code (boolean,String)} equal.

      * * The implementation of this class prevents the creation of nested * unions.

      */ public class UnionType extends JSType { private static final long serialVersionUID = 1L; Collection alternates; private int hashcode; /** * Creates a union type. * * @param alternates the alternates of the union */ UnionType(JSTypeRegistry registry, Collection alternates) { super(registry); this.alternates = alternates; this.hashcode = this.alternates.hashCode(); } /** * Gets the alternate types of this union type. * @return The alternate types of this union type. The returned set is * immutable. */ public Collection getAlternates() { for (JSType t : alternates) { if (t.isUnionType()) { rebuildAlternates(); break; } } return alternates; } /** * Use UnionTypeBuilder to rebuild the list of alternates and hashcode * of the current UnionType. */ private void rebuildAlternates() { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : alternates) { builder.addAlternate(alternate); } alternates = builder.getAlternates(); hashcode = alternates.hashCode(); } /** * This predicate is used to test whether a given type can appear in a * numeric context, such as an operand of a multiply operator. * * @return true if the type can appear in a numeric context. */ @Override public boolean matchesNumberContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternates) { if (t.matchesNumberContext()) { return true; } } return false; } /** * This predicate is used to test whether a given type can appear in a * {@code String} context, such as an operand of a string concat ({@code +}) * operator.

      * * All types have at least the potential for converting to {@code String}. * When we add externally defined types, such as a browser OM, we may choose * to add types that do not automatically convert to {@code String}. * * @return {@code true} if not {@link VoidType} */ @Override public boolean matchesStringContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternates) { if (t.matchesStringContext()) { return true; } } return false; } /** * This predicate is used to test whether a given type can appear in an * {@code Object} context, such as the expression in a {@code with} * statement.

      * * Most types we will encounter, except notably {@code null}, have at least * the potential for converting to {@code Object}. Host defined objects can * get peculiar.

      * * VOID type is included here because while it is not part of the JavaScript * language, functions returning 'void' type can't be used as operands of * any operator or statement.

      * * @return {@code true} if the type is not {@link NullType} or * {@link VoidType} */ @Override public boolean matchesObjectContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternates) { if (t.matchesObjectContext()) { return true; } } return false; } @Override public JSType findPropertyType(String propertyName) { JSType propertyType = null; for (JSType alternate : getAlternates()) { // Filter out the null/undefined type. if (alternate.isNullType() || alternate.isVoidType()) { continue; } JSType altPropertyType = alternate.findPropertyType(propertyName); if (altPropertyType == null) { continue; } if (propertyType == null) { propertyType = altPropertyType; } else { propertyType = propertyType.getLeastSupertype(altPropertyType); } } return propertyType; } @Override public boolean canBeCalled() { for (JSType t : alternates) { if (!t.canBeCalled()) { return false; } } return true; } @Override public JSType autobox() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { restricted.addAlternate(t.autobox()); } return restricted.build(); } @Override public JSType restrictByNotNullOrUndefined() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { restricted.addAlternate(t.restrictByNotNullOrUndefined()); } return restricted.build(); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = null; for (JSType t : alternates) { TernaryValue test = t.testForEquality(that); if (result == null) { result = test; } else if (!result.equals(test)) { return UNKNOWN; } } return result; } /** * This predicate determines whether objects of this type can have the * {@code null} value, and therefore can appear in contexts where * {@code null} is expected. * * @return {@code true} for everything but {@code Number} and * {@code Boolean} types. */ @Override public boolean isNullable() { for (JSType t : alternates) { if (t.isNullable()) { return true; } } return false; } @Override public boolean isUnknownType() { for (JSType t : alternates) { if (t.isUnknownType()) { return true; } } return false; } @Override public boolean isStruct() { for (JSType typ : getAlternates()) { if (typ.isStruct()) { return true; } } return false; } @Override public boolean isDict() { for (JSType typ : getAlternates()) { if (typ.isDict()) { return true; } } return false; } @Override public JSType getLeastSupertype(JSType that) { if (!that.isUnknownType() && !that.isUnionType()) { for (JSType alternate : alternates) { if (!alternate.isUnknownType() && that.isSubtype(alternate)) { return this; } } } return getLeastSupertype(this, that); } JSType meet(JSType that) { UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (JSType alternate : alternates) { if (alternate.isSubtype(that)) { builder.addAlternate(alternate); } } if (that.isUnionType()) { for (JSType otherAlternate : that.toMaybeUnionType().alternates) { if (otherAlternate.isSubtype(this)) { builder.addAlternate(otherAlternate); } } } else if (that.isSubtype(this)) { builder.addAlternate(that); } JSType result = builder.build(); if (!result.isNoType()) { return result; } else if (this.isObject() && (that.isObject() && !that.isNoType())) { return getNativeType(JSTypeNative.NO_OBJECT_TYPE); } else { return getNativeType(JSTypeNative.NO_TYPE); } } /** * Two union types are equal if, after flattening nested union types, * they have the same number of alternates and all alternates are equal. */ boolean checkUnionEquivalenceHelper( UnionType that, EquivalenceMethod eqMethod) { Collection thatAlternates = that.getAlternates(); if (eqMethod == EquivalenceMethod.IDENTITY && getAlternates().size() != thatAlternates.size()) { return false; } for (JSType alternate : thatAlternates) { if (!hasAlternate(alternate, eqMethod)) { return false; } } return true; } private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod) { for (JSType alternate : getAlternates()) { if (alternate.checkEquivalenceHelper(type, eqMethod)) { return true; } } return false; } @Override public boolean hasProperty(String pname) { for (JSType alternate : alternates) { if (alternate.hasProperty(pname)) { return true; } } return false; } @Override public int hashCode() { return this.hashcode; } @Override public UnionType toMaybeUnionType() { return this; } @Override public boolean isObject() { for (JSType alternate : alternates) { if (!alternate.isObject()) { return false; } } return true; } /** * A {@link UnionType} contains a given type (alternate) iff the member * vector contains it. * * @param type The alternate which might be in this union. * * @return {@code true} if the alternate is in the union */ public boolean contains(JSType type) { for (JSType alt : alternates) { if (alt.isEquivalentTo(type)) { return true; } } return false; } /** * Returns a more restricted union type than {@code this} one, in which all * subtypes of {@code type} have been removed.

      * * Examples: *

        *
      • {@code (number,string)} restricted by {@code number} is * {@code string}
      • *
      • {@code (null, EvalError, URIError)} restricted by * {@code Error} is {@code null}
      • *
      * * @param type the supertype of the types to remove from this union type */ public JSType getRestrictedUnion(JSType type) { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternates) { // Keep all unknown/unresolved types. if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) { restricted.addAlternate(t); } } return restricted.build(); } @Override String toStringHelper(boolean forAnnotations) { StringBuilder result = new StringBuilder(); boolean firstAlternate = true; result.append("("); SortedSet sorted = new TreeSet(ALPHA); sorted.addAll(alternates); for (JSType t : sorted) { if (!firstAlternate) { result.append("|"); } result.append(t.toStringHelper(forAnnotations)); firstAlternate = false; } result.append(")"); return result.toString(); } @Override public boolean isSubtype(JSType that) { // unknown if (that.isUnknownType()) { return true; } // all type if (that.isAllType()) { return true; } for (JSType element : alternates) { if (!element.isSubtype(that)) { return false; } } return true; } @Override public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { // gather elements after restriction UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { restricted.addAlternate( element.getRestrictedTypeGivenToBooleanOutcome(outcome)); } return restricted.build(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { BooleanLiteralSet literals = BooleanLiteralSet.EMPTY; for (JSType element : alternates) { literals = literals.union(element.getPossibleToBooleanOutcomes()); if (literals == BooleanLiteralSet.BOTH) { break; } } return literals; } @Override public TypePair getTypesUnderEquality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderEquality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public TypePair getTypesUnderInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public TypePair getTypesUnderShallowInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public T visit(Visitor visitor) { return visitor.caseUnionType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseUnionType(this, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { setResolvedTypeInternal(this); // for circularly defined types. // Just resolve the alternates, but do not update as that breaks some error // reporting cases. for (JSType alternate : alternates) { alternate.resolve(t, scope); } // Ensure the union is in a normalized state. rebuildAlternates(); return this; } @Override public String toDebugHashCodeString() { List hashCodes = Lists.newArrayList(); for (JSType a : alternates) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; } @Override public boolean setValidator(Predicate validator) { for (JSType a : alternates) { a.setValidator(validator); } return true; } @Override public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; for (JSType a : alternates) { if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } ObjectType obj = a.toObjectType(); if (obj == null) { if (currentValue == null && currentCommonSuper == null) { // If obj is not an object, then it must be a value. currentValue = a; } else { // Multiple values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative.ALL_TYPE); } } else if (currentValue != null) { // Values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative.ALL_TYPE); } else if (currentCommonSuper == null) { currentCommonSuper = obj; } else { currentCommonSuper = registry.findCommonSuperObject(currentCommonSuper, obj); } } return currentCommonSuper; } @Override public void matchConstraint(JSType constraint) { for (JSType alternate : alternates) { alternate.matchConstraint(constraint); } } @Override public boolean hasAnyTemplateTypesInternal() { for (JSType alternate : alternates) { if (alternate.hasAnyTemplateTypes()) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/PrototypeObjectType.java0000644000175000017500000003460312115204405030645 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Set; /** * The object type represents instances of JavaScript objects such as * {@code Object}, {@code Date}, {@code Function}.

      * * Objects in JavaScript are unordered collections of properties. * Each property consists of a name, a value and a set of attributes.

      * * Each instance has an implicit prototype property ({@code [[Prototype]]}) * pointing to an object instance, which itself has an implicit property, thus * forming a chain.

      * * A class begins life with no name. Later, a name may be provided once it * can be inferred. Note that the name in this case is strictly for * debugging purposes. Looking up type name references goes through the * {@link JSTypeRegistry}.

      */ class PrototypeObjectType extends ObjectType { private static final long serialVersionUID = 1L; private final String className; private final PropertyMap properties; private final boolean nativeType; // NOTE(nicksantos): The implicit prototype can change over time. // Modeling this is a bear. Always call getImplicitPrototype(), because // some subclasses override this to do special resolution handling. private ObjectType implicitPrototypeFallback; // If this is a function prototype, then this is the owner. // A PrototypeObjectType can only be the prototype of one function. If we try // to do this for multiple functions, then we'll have to create a new one. private FunctionType ownerFunction = null; // Whether the toString representation of this should be pretty-printed, // by printing all properties. private boolean prettyPrint = false; private static final int MAX_PRETTY_PRINTED_PROPERTIES = 4; /** * Creates an object type. * * @param className the name of the class. May be {@code null} to * denote an anonymous class. * * @param implicitPrototype the implicit prototype * (a.k.a. {@code [[Prototype]]}) as defined by ECMA-262. If the * implicit prototype is {@code null} the implicit prototype will be * set to the {@link JSTypeNative#OBJECT_TYPE}. */ PrototypeObjectType(JSTypeRegistry registry, String className, ObjectType implicitPrototype) { this(registry, className, implicitPrototype, false, null); } /** * Creates an object type, allowing specification of the implicit prototype, * whether the object is native, and any templatized types. */ PrototypeObjectType(JSTypeRegistry registry, String className, ObjectType implicitPrototype, boolean nativeType, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); this.properties = new PropertyMap(); this.properties.setParentSource(this); this.className = className; this.nativeType = nativeType; if (nativeType || implicitPrototype != null) { setImplicitPrototype(implicitPrototype); } else { setImplicitPrototype( registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE)); } } @Override PropertyMap getPropertyMap() { return properties; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if (hasOwnDeclaredProperty(name)) { return false; } Property newProp = new Property( name, type, inferred, propertyNode); properties.putProperty(name, newProp); return true; } @Override public boolean removeProperty(String name) { return properties.removeProperty(name); } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { if (info != null) { if (properties.getOwnProperty(propertyName) == null) { // If docInfo was attached, but the type of the property // was not defined anywhere, then we consider this an explicit // declaration of the property. defineInferredProperty(propertyName, getPropertyType(propertyName), null); } // The prototype property is not represented as a normal Property. // We probably don't want to attach any JSDoc to it anyway. Property property = properties.getOwnProperty(propertyName); if (property != null) { property.setJSDocInfo(info); } } } @Override public boolean matchesNumberContext() { return isNumberObjectType() || isDateType() || isBooleanObjectType() || isStringObjectType() || hasOverridenNativeProperty("valueOf"); } @Override public boolean matchesStringContext() { return isTheObjectType() || isStringObjectType() || isDateType() || isRegexpType() || isArrayType() || isNumberObjectType() || isBooleanObjectType() || hasOverridenNativeProperty("toString"); } /** * Given the name of a native object property, checks whether the property is * present on the object and different from the native one. */ private boolean hasOverridenNativeProperty(String propertyName) { if (isNativeObjectType()) { return false; } JSType propertyType = getPropertyType(propertyName); ObjectType nativeType = isFunctionType() ? registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE) : registry.getNativeObjectType(JSTypeNative.OBJECT_PROTOTYPE); JSType nativePropertyType = nativeType.getPropertyType(propertyName); return propertyType != nativePropertyType; } @Override public JSType unboxesTo() { if (isStringObjectType()) { return getNativeType(JSTypeNative.STRING_TYPE); } else if (isBooleanObjectType()) { return getNativeType(JSTypeNative.BOOLEAN_TYPE); } else if (isNumberObjectType()) { return getNativeType(JSTypeNative.NUMBER_TYPE); } else { return super.unboxesTo(); } } @Override public boolean matchesObjectContext() { return true; } @Override public boolean canBeCalled() { return isRegexpType(); } @Override String toStringHelper(boolean forAnnotations) { if (hasReferenceName()) { return getReferenceName(); } else if (prettyPrint) { // Don't pretty print recursively. prettyPrint = false; // Use a tree set so that the properties are sorted. Set propertyNames = Sets.newTreeSet(); for (ObjectType current = this; current != null && !current.isNativeObjectType() && propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES; current = current.getImplicitPrototype()) { propertyNames.addAll(current.getOwnPropertyNames()); } StringBuilder sb = new StringBuilder(); sb.append("{"); int i = 0; for (String property : propertyNames) { if (i > 0) { sb.append(", "); } sb.append(property); sb.append(": "); sb.append(getPropertyType(property).toStringHelper(forAnnotations)); ++i; if (!forAnnotations && i == MAX_PRETTY_PRINTED_PROPERTIES) { sb.append(", ..."); break; } } sb.append("}"); prettyPrint = true; return sb.toString(); } else { return forAnnotations ? "?" : "{...}"; } } void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; } boolean isPrettyPrint() { return prettyPrint; } @Override public FunctionType getConstructor() { return null; } @Override public ObjectType getImplicitPrototype() { return implicitPrototypeFallback; } /** * This should only be reset on the FunctionPrototypeType, only to fix an * incorrectly established prototype chain due to the user having a mismatch * in super class declaration, and only before properties on that type are * processed. */ final void setImplicitPrototype(ObjectType implicitPrototype) { checkState(!hasCachedValues()); this.implicitPrototypeFallback = implicitPrototype; } @Override public String getReferenceName() { if (className != null) { return className; } else if (ownerFunction != null) { return ownerFunction.getReferenceName() + ".prototype"; } else { return null; } } @Override public boolean hasReferenceName() { return className != null || ownerFunction != null; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Union types if (that.isUnionType()) { // The static {@code JSType.isSubtype} check already decomposed // union types, so we don't need to check those again. return false; } // record types if (that.isRecordType()) { return RecordType.isSubtype(this, that.toMaybeRecordType()); } // Interfaces // Find all the interfaces implemented by this class and compare each one // to the interface instance. ObjectType thatObj = that.toObjectType(); FunctionType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (getConstructor() != null && getConstructor().isInterface()) { for (ObjectType thisInterface : getCtorExtendedInterfaces()) { if (thisInterface.isSubtype(that)) { return true; } } } else if (thatCtor != null && thatCtor.isInterface()) { Iterable thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return thatObj != null && isImplicitPrototype(thatObj); } private boolean implicitPrototypeChainIsUnknown() { ObjectType p = getImplicitPrototype(); while (p != null) { if (p.isUnknownType()) { return true; } p = p.getImplicitPrototype(); } return false; } @Override public boolean hasCachedValues() { return super.hasCachedValues(); } /** Whether this is a built-in object. */ @Override public boolean isNativeObjectType() { return nativeType; } @Override void setOwnerFunction(FunctionType type) { Preconditions.checkState(ownerFunction == null || type == null); ownerFunction = type; } @Override public FunctionType getOwnerFunction() { return ownerFunction; } @Override public Iterable getCtorImplementedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getImplementedInterfaces() : ImmutableList.of(); } @Override public Iterable getCtorExtendedInterfaces() { return isFunctionPrototypeType() ? getOwnerFunction().getExtendedInterfaces() : ImmutableList.of(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { setResolvedTypeInternal(this); ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { implicitPrototypeFallback = (ObjectType) implicitPrototype.resolve(t, scope); } for (Property prop : properties.values()) { prop.setType(safeResolve(prop.getType(), t, scope)); } return this; } @Override public void matchConstraint(JSType constraint) { // We only want to match constraints on anonymous types. if (hasReferenceName()) { return; } // Handle the case where the constraint object is a record type. // // param constraint {{prop: (number|undefined)}} // function f(constraint) {} // f({}); // // We want to modify the object literal to match the constraint, by // taking any each property on the record and trying to match // properties on this object. if (constraint.isRecordType()) { matchRecordTypeConstraint(constraint.toObjectType()); } else if (constraint.isUnionType()) { for (JSType alt : constraint.toMaybeUnionType().getAlternates()) { if (alt.isRecordType()) { matchRecordTypeConstraint(alt.toObjectType()); } } } } public void matchRecordTypeConstraint(ObjectType constraintObj) { for (String prop : constraintObj.getOwnPropertyNames()) { JSType propType = constraintObj.getPropertyType(prop); if (!isPropertyTypeDeclared(prop)) { JSType typeToInfer = propType; if (!hasProperty(prop)) { typeToInfer = getNativeType(JSTypeNative.VOID_TYPE) .getLeastSupertype(propType); } defineInferredProperty(prop, typeToInfer, null); } } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StaticSourceFile.java0000644000175000017500000000616012115204405030054 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * The {@code StaticSourceFile} contains information about a compiler input. * * @author nicksantos@google.com (Nick Santos) */ public interface StaticSourceFile { /** * The name of the file. Must be unique across all files in the compilation. */ String getName(); /** * Returns whether this is an externs file. */ boolean isExtern(); /** * Returns the offset of the given line number relative to the file start. * Line number should be 1-based. * * If the source file doesn't have line information, it should return * Integer.MIN_VALUE. The negative offsets will make it more obvious * what happened. * * @param lineNumber the line of the input to get the absolute offset of. * @return the absolute offset of the start of the provided line. * @throws IllegalArgumentException if lineno is less than 1 or greater than * the number of lines in the source. */ int getLineOffset(int lineNumber); /** * Gets the 1-based line number of the given source offset. * * @param offset An absolute file offset. * @return The 1-based line number of that offset. The behavior is * undefined if this offset does not exist in the source file. */ int getLineOfOffset(int offset); /** * Gets the 0-based column number of the given source offset. * * @param offset An absolute file offset. * @return The 0-based column number of that offset. The behavior is * undefined if this offset does not exist in the source file. */ int getColumnOfOffset(int offset); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NoType.java0000644000175000017500000000636512115204405026071 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * Bottom type, representing the subclass of any value or object. * * Although JavaScript programmers can't explicitly denote the bottom type, * it comes up in static analysis. For example, if we have: * * var x = null; * if (x) { * f(x); * } * * We need to be able to assign {@code x} a type within the {@code f(x)} * call. Since it has no possible type, we assign {@code x} the NoType, * so that {@code f(x)} is legal no matter what the type of {@code f}'s * first argument is. * * @see Bottom types */ public class NoType extends NoObjectType { private static final long serialVersionUID = 1L; NoType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoObjectType() { return false; } @Override public boolean isNoType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.EMPTY; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public T visit(Visitor visitor) { return visitor.caseNoType(); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseNoType(that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "None"; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StringType.java0000644000175000017500000000612312115204405026753 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * String type. */ public final class StringType extends ValueType { private static final long serialVersionUID = 1L; StringType(JSTypeRegistry registry) { super(registry); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isStringValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "string"; } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.STRING_OBJECT_TYPE); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public T visit(Visitor visitor) { return visitor.caseStringType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/EquivalenceMethod.java0000644000175000017500000000557112115204405030253 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * Represents different ways for comparing equality among types. * @author nicksantos@google.com (Nick Santos) */ enum EquivalenceMethod { /** * Indicates that the two types should behave exactly the same under * all type operations. * * Thus, {string} != {?} and {Unresolved} != {?} */ IDENTITY, /** * Indicates that the two types are almost exactly the same, and that a * data flow analysis algorithm comparing them should consider them equal. * * In traditional type inference, the types form a finite lattice, and this * ensures that type inference will terminate. * * In our type system, the unknown types do not obey the lattice rules. So * if we continue to perform inference over the unknown types, we may * never terminate. * * By treating all unknown types as equivalent for the purposes of data * flow analysis, we ensure that the algorithm will terminate. * * Thus, {string} != {?} and {Unresolved} ~= {?} */ DATA_FLOW, /** * Indicates that two types are invariant. * * In a type system without unknown types, this would be the same * as IDENTITY. But we always want to consider type A invariant with type B * if B is unknown. * * Thus, {string} ~= {?} and {Unresolved} ~= {?} */ INVARIANT } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NamedType.java0000644000175000017500000003227712115204405026542 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; import java.util.List; /** * A {@code NamedType} is a named reference to some other type. This provides * a convenient mechanism for implementing forward references to types; a * {@code NamedType} can be used as a placeholder until its reference is * resolved. It is also useful for representing type names in JsDoc type * annotations, some of which may never be resolved (as they may refer to * types in host systems not yet supported by JSCompiler, such as the JVM.)

      * * An important distinction: {@code NamedType} is a type name reference, * whereas {@link ObjectType} is a named type object, such as an Enum name. * The Enum itself is typically used only in a dot operator to name one of its * constants, or in a declaration, where its name will appear in a * NamedType.

      * * A {@code NamedType} is not currently a full-fledged typedef, because it * cannot resolve to any JavaScript type. It can only resolve to a named * {@link JSTypeRegistry} type, or to {@link FunctionType} or * {@link EnumType}.

      * * If full typedefs are to be supported, then each method on each type class * needs to be reviewed to make sure that everything works correctly through * typedefs. Alternatively, we would need to walk through the parse tree and * unroll each reference to a {@code NamedType} to its resolved type before * applying the rest of the analysis.

      * * TODO(user): Revisit all of this logic.

      * * The existing typing logic is hacky. Unresolved types should get processed * in a more consistent way, but with the Rhino merge coming, there will be * much that has to be changed.

      * */ class NamedType extends ProxyObjectType { private static final long serialVersionUID = 1L; private final String reference; private final String sourceName; private final int lineno; private final int charno; /** * Validates the type resolution. */ private Predicate validator; /** * Property-defining continuations. */ private List propertyContinuations = null; /** * Create a named type based on the reference. */ NamedType(JSTypeRegistry registry, String reference, String sourceName, int lineno, int charno) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); Preconditions.checkNotNull(reference); this.reference = reference; this.sourceName = sourceName; this.lineno = lineno; this.charno = charno; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (!isResolved()) { // If this is an unresolved object type, we need to save all its // properties and define them when it is resolved. if (propertyContinuations == null) { propertyContinuations = Lists.newArrayList(); } propertyContinuations.add( new PropertyContinuation( propertyName, type, inferred, propertyNode)); return true; } else { return super.defineProperty( propertyName, type, inferred, propertyNode); } } private void finishPropertyContinuations() { ObjectType referencedObjType = getReferencedObjTypeInternal(); if (referencedObjType != null && !referencedObjType.isUnknownType()) { if (propertyContinuations != null) { for (PropertyContinuation c : propertyContinuations) { c.commit(this); } } } propertyContinuations = null; } /** Returns the type to which this refers (which is unknown if unresolved). */ public JSType getReferencedType() { return getReferencedTypeInternal(); } @Override public String getReferenceName() { return reference; } @Override String toStringHelper(boolean forAnnotations) { return reference; } @Override public boolean hasReferenceName() { return true; } @Override boolean isNamedType() { return true; } @Override public boolean isNominalType() { return true; } @Override public int hashCode() { return reference.hashCode(); } /** * Resolve the referenced type within the enclosing scope. */ @Override JSType resolveInternal(ErrorReporter t, StaticScope enclosing) { // TODO(user): Investigate whether it is really necessary to keep two // different mechanisms for resolving named types, and if so, which order // makes more sense. Now, resolution via registry is first in order to // avoid triggering the warnings built into the resolution via properties. boolean resolved = resolveViaRegistry(t); if (detectInheritanceCycle()) { handleTypeCycle(t); } if (resolved) { super.resolveInternal(t, enclosing); finishPropertyContinuations(); return registry.isLastGeneration() ? getReferencedType() : this; } resolveViaProperties(t, enclosing); if (detectInheritanceCycle()) { handleTypeCycle(t); } super.resolveInternal(t, enclosing); if (isResolved()) { finishPropertyContinuations(); } return registry.isLastGeneration() ? getReferencedType() : this; } /** * Resolves a named type by looking it up in the registry. * @return True if we resolved successfully. */ private boolean resolveViaRegistry(ErrorReporter reporter) { JSType type = registry.getType(reference); if (type != null) { setReferencedAndResolvedType(type, reporter); return true; } return false; } /** * Resolves a named type by looking up its first component in the scope, and * subsequent components as properties. The scope must have been fully * parsed and a symbol table constructed. */ private void resolveViaProperties(ErrorReporter reporter, StaticScope enclosing) { JSType value = lookupViaProperties(reporter, enclosing); // last component of the chain if (value != null && value.isFunctionType() && (value.isConstructor() || value.isInterface())) { FunctionType functionType = value.toMaybeFunctionType(); setReferencedAndResolvedType(functionType.getInstanceType(), reporter); } else if (value != null && value.isNoObjectType()) { setReferencedAndResolvedType( registry.getNativeFunctionType( JSTypeNative.NO_OBJECT_TYPE).getInstanceType(), reporter); } else if (value instanceof EnumType) { setReferencedAndResolvedType( ((EnumType) value).getElementsType(), reporter); } else { // We've been running into issues where people forward-declare // non-named types. (This is legitimate...our dependency management // code doubles as our forward-declaration code.) // // So if the type does resolve to an actual value, but it's not named, // then don't respect the forward declaration. handleUnresolvedType(reporter, value == null || value.isUnknownType()); } } /** * Resolves a type by looking up its first component in the scope, and * subsequent components as properties. The scope must have been fully * parsed and a symbol table constructed. * @return The type of the symbol, or null if the type could not be found. */ private JSType lookupViaProperties(ErrorReporter reporter, StaticScope enclosing) { String[] componentNames = reference.split("\\.", -1); if (componentNames[0].length() == 0) { return null; } StaticSlot slot = enclosing.getSlot(componentNames[0]); if (slot == null) { return null; } // If the first component has a type of 'Unknown', then any type // names using it should be regarded as silently 'Unknown' rather than be // noisy about it. JSType slotType = slot.getType(); if (slotType == null || slotType.isAllType() || slotType.isNoType()) { return null; } JSType value = getTypedefType(reporter, slot); if (value == null) { return null; } // resolving component by component for (int i = 1; i < componentNames.length; i++) { ObjectType parentClass = ObjectType.cast(value); if (parentClass == null) { return null; } if (componentNames[i].length() == 0) { return null; } value = parentClass.getPropertyType(componentNames[i]); } return value; } private void setReferencedAndResolvedType( JSType type, ErrorReporter reporter) { if (validator != null) { validator.apply(type); } setReferencedType(type); checkEnumElementCycle(reporter); checkProtoCycle(reporter); setResolvedTypeInternal(getReferencedType()); } private void handleTypeCycle(ErrorReporter t) { setReferencedType( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); t.warning("Cycle detected in inheritance chain of type " + reference, sourceName, lineno, charno); setResolvedTypeInternal(getReferencedType()); } private void checkEnumElementCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType instanceof EnumElementType && ((EnumElementType) referencedType).getPrimitiveType() == this) { handleTypeCycle(t); } } private void checkProtoCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType == this) { handleTypeCycle(t); } } // Warns about this type being unresolved iff it's not a forward-declared // type name. private void handleUnresolvedType( ErrorReporter t, boolean ignoreForwardReferencedTypes) { if (registry.isLastGeneration()) { boolean isForwardDeclared = ignoreForwardReferencedTypes && registry.isForwardDeclaredType(reference); if (!isForwardDeclared && registry.isLastGeneration()) { t.warning("Bad type annotation. Unknown type " + reference, sourceName, lineno, charno); } else { setReferencedType( registry.getNativeObjectType( JSTypeNative.NO_RESOLVED_TYPE)); if (registry.isLastGeneration() && validator != null) { validator.apply(getReferencedType()); } } setResolvedTypeInternal(getReferencedType()); } else { setResolvedTypeInternal(this); } } private JSType getTypedefType(ErrorReporter t, StaticSlot slot) { JSType type = slot.getType(); if (type != null) { return type; } handleUnresolvedType(t, true); return null; } @Override public boolean setValidator(Predicate validator) { // If the type is already resolved, we can validate it now. If // the type has not been resolved yet, we need to wait till its // resolved before we can validate it. if (this.isResolved()) { return super.setValidator(validator); } else { this.validator = validator; return true; } } /** Store enough information to define a property at a later time. */ private static final class PropertyContinuation { private final String propertyName; private final JSType type; private final boolean inferred; private final Node propertyNode; private PropertyContinuation( String propertyName, JSType type, boolean inferred, Node propertyNode) { this.propertyName = propertyName; this.type = type; this.inferred = inferred; this.propertyNode = propertyNode; } void commit(ObjectType target) { target.defineProperty( propertyName, type, inferred, propertyNode); } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java0000644000175000017500000020443212115204405027555 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.ARRAY_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.VOID_TYPE; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.ScriptRuntime; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * The type registry is used to resolve named types. * *

      This class is not thread-safe. * */ public class JSTypeRegistry implements Serializable { private static final long serialVersionUID = 1L; /** * The name associated with the template variable corresponding to the * property key type of the built-in Javascript object. */ public static final String OBJECT_INDEX_TEMPLATE = "Object#Key"; private TemplateType objectIndexTemplateKey; /** * The name associated with the template variable corresponding to the * property value type for Javascript Objects and Arrays. */ public static final String OBJECT_ELEMENT_TEMPLATE = "Object#Element"; private TemplateType objectElementTemplateKey; /** * The UnionTypeBuilder caps the maximum number of alternate types it * remembers and then defaults to "?" (unknown type). By default this max * is 20, but it's very easy for the same property to appear on more than 20 * types. Use larger unions for property checking. 3000 was picked * semi-randomly for use by the Google+ FE project. */ private static final int PROPERTY_CHECKING_UNION_SIZE = 3000; // TODO(user): An instance of this class should be used during // compilation. We also want to make all types' constructors package private // and force usage of this registry instead. This will allow us to evolve the // types without being tied by an open API. private final transient ErrorReporter reporter; // We use an Array instead of an immutable list because this lookup needs // to be very fast. When it was an immutable list, we were spending 5% of // CPU time on bounds checking inside get(). private final JSType[] nativeTypes; private final Map namesToTypes; // Set of namespaces in which types (or other namespaces) exist. private final Set namespaces = new HashSet(); // NOTE(nicksantos): This is a terrible terrible hack. When type expressions // are evaluated, we need to be able to decide whether that type name // resolves to a nullable type or a non-nullable type. Object types are // nullable, but enum types are not. // // Notice that it's not good enough to just declare enum types sooner. // For example, if we have // /** @enum {MyObject} */ var MyEnum = ...; // we won't be to declare "MyEnum" without evaluating the expression // {MyObject}, and following those dependencies starts to lead us into // undecidable territory. Instead, we "pre-declare" enum types and typedefs, // so that the expression resolver can decide whether a given name is // nullable or not. private final Set nonNullableTypeNames = new HashSet(); // Types that have been "forward-declared." // If these types are not declared anywhere in the binary, we shouldn't // try to type-check them at all. private final Set forwardDeclaredTypes = new HashSet(); // A map of properties to the types on which those properties have been // declared. private final Map typesIndexedByProperty = Maps.newHashMap(); // A map of properties to each reference type on which those // properties have been declared. Each type has a unique name used // for de-duping. private final Map> eachRefTypeIndexedByProperty = Maps.newHashMap(); // A map of properties to the greatest subtype on which those properties have // been declared. This is filled lazily from the types declared in // typesIndexedByProperty. private final Map greatestSubtypeByProperty = Maps.newHashMap(); // A map from interface name to types that implement it. private final Multimap interfaceToImplementors = LinkedHashMultimap.create(); // All the unresolved named types. private final Multimap, NamedType> unresolvedNamedTypes = ArrayListMultimap.create(); // All the resolved named types. private final Multimap, NamedType> resolvedNamedTypes = ArrayListMultimap.create(); // NamedType warns about unresolved types in the last generation. private boolean lastGeneration = true; // The template type name. private Map templateTypes = Maps.newHashMap(); // A single empty TemplateTypeMap, which can be safely reused in cases where // there are no template types. private final TemplateTypeMap emptyTemplateTypeMap; private final boolean tolerateUndefinedValues; /** * The type registry has three modes, which control how type ASTs are * converted to types in {@link #createFromTypeNodes}. */ public static enum ResolveMode { /** * Expressions are converted into Unknown blobs that can be * resolved into complex types. */ LAZY_EXPRESSIONS, /** * Expressions are evaluated. If any names in the expression point to * unknown types, then we create a proxy {@code NamedType} structure * until the type can be resolved. * * This is the legacy way of resolving ways, and may not exist in the * future. */ LAZY_NAMES, /** * Expressions and type names are evaluated aggressively. A warning * will be emitted if a type name fails to resolve to a real type. */ IMMEDIATE } private ResolveMode resolveMode = ResolveMode.LAZY_NAMES; /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry(ErrorReporter reporter) { this(reporter, false); } /** * Constructs a new type registry populated with the built-in types. */ public JSTypeRegistry( ErrorReporter reporter, boolean tolerateUndefinedValues) { this.reporter = reporter; this.emptyTemplateTypeMap = new TemplateTypeMap( this, ImmutableList.of(), ImmutableList.of()); nativeTypes = new JSType[JSTypeNative.values().length]; namesToTypes = new HashMap(); resetForTypeCheck(); this.tolerateUndefinedValues = tolerateUndefinedValues; } /** * @return The template variable corresponding to the property value type for * Javascript Objects and Arrays. */ public TemplateType getObjectElementKey() { return this.objectElementTemplateKey; } /** * @return The template variable corresponding to the * property key type of the built-in Javascript object. */ public TemplateType getObjectIndexKey() { Preconditions.checkNotNull(objectIndexTemplateKey); return objectIndexTemplateKey; } /** * Set the current resolving mode of the type registry. * @see ResolveMode */ public void setResolveMode(ResolveMode mode) { this.resolveMode = mode; } ResolveMode getResolveMode() { return resolveMode; } public ErrorReporter getErrorReporter() { return reporter; } public boolean shouldTolerateUndefinedValues() { return tolerateUndefinedValues; } /** * Reset to run the TypeCheck pass. */ public void resetForTypeCheck() { typesIndexedByProperty.clear(); eachRefTypeIndexedByProperty.clear(); initializeBuiltInTypes(); namesToTypes.clear(); namespaces.clear(); initializeRegistry(); } private void initializeBuiltInTypes() { objectIndexTemplateKey = new TemplateType(this, OBJECT_INDEX_TEMPLATE); objectElementTemplateKey = new TemplateType(this, OBJECT_ELEMENT_TEMPLATE); // These locals shouldn't be all caps. BooleanType BOOLEAN_TYPE = new BooleanType(this); registerNativeType(JSTypeNative.BOOLEAN_TYPE, BOOLEAN_TYPE); NullType NULL_TYPE = new NullType(this); registerNativeType(JSTypeNative.NULL_TYPE, NULL_TYPE); NumberType NUMBER_TYPE = new NumberType(this); registerNativeType(JSTypeNative.NUMBER_TYPE, NUMBER_TYPE); StringType STRING_TYPE = new StringType(this); registerNativeType(JSTypeNative.STRING_TYPE, STRING_TYPE); UnknownType UNKNOWN_TYPE = new UnknownType(this, false); registerNativeType(JSTypeNative.UNKNOWN_TYPE, UNKNOWN_TYPE); UnknownType checkedUnknownType = new UnknownType(this, true); registerNativeType( JSTypeNative.CHECKED_UNKNOWN_TYPE, checkedUnknownType); VoidType VOID_TYPE = new VoidType(this); registerNativeType(JSTypeNative.VOID_TYPE, VOID_TYPE); AllType ALL_TYPE = new AllType(this); registerNativeType(JSTypeNative.ALL_TYPE, ALL_TYPE); // Top Level Prototype (the One) // The initializations of TOP_LEVEL_PROTOTYPE and OBJECT_FUNCTION_TYPE // use each other's results, so at least one of them will get null // instead of an actual type; however, this seems to be benign. PrototypeObjectType TOP_LEVEL_PROTOTYPE = new PrototypeObjectType(this, null, null, true, null); registerNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE, TOP_LEVEL_PROTOTYPE); // Object FunctionType OBJECT_FUNCTION_TYPE = new FunctionType(this, "Object", null, createArrowType(createOptionalParameters(ALL_TYPE), UNKNOWN_TYPE), null, createTemplateTypeMap(ImmutableList.of( objectIndexTemplateKey, objectElementTemplateKey), null), true, true); OBJECT_FUNCTION_TYPE.setPrototype(TOP_LEVEL_PROTOTYPE, null); registerNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE, OBJECT_FUNCTION_TYPE); ObjectType OBJECT_TYPE = OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.OBJECT_TYPE, OBJECT_TYPE); ObjectType OBJECT_PROTOTYPE = OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.OBJECT_PROTOTYPE, OBJECT_PROTOTYPE); // Function FunctionType FUNCTION_FUNCTION_TYPE = new FunctionType(this, "Function", null, createArrowType( createParametersWithVarArgs(ALL_TYPE), UNKNOWN_TYPE), null, null, true, true); FUNCTION_FUNCTION_TYPE.setPrototypeBasedOn(OBJECT_TYPE); registerNativeType( JSTypeNative.FUNCTION_FUNCTION_TYPE, FUNCTION_FUNCTION_TYPE); ObjectType FUNCTION_PROTOTYPE = FUNCTION_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.FUNCTION_PROTOTYPE, FUNCTION_PROTOTYPE); NoType NO_TYPE = new NoType(this); registerNativeType(JSTypeNative.NO_TYPE, NO_TYPE); NoObjectType NO_OBJECT_TYPE = new NoObjectType(this); registerNativeType(JSTypeNative.NO_OBJECT_TYPE, NO_OBJECT_TYPE); NoObjectType NO_RESOLVED_TYPE = new NoResolvedType(this); registerNativeType(JSTypeNative.NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); // Array FunctionType ARRAY_FUNCTION_TYPE = new FunctionType(this, "Array", null, createArrowType(createParametersWithVarArgs(ALL_TYPE), null), null, createTemplateTypeMap(ImmutableList.of( objectElementTemplateKey), null), true, true); ARRAY_FUNCTION_TYPE.getInternalArrowType().returnType = ARRAY_FUNCTION_TYPE.getInstanceType(); ObjectType arrayPrototype = ARRAY_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE, ARRAY_FUNCTION_TYPE); ObjectType ARRAY_TYPE = ARRAY_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.ARRAY_TYPE, ARRAY_TYPE); // Boolean FunctionType BOOLEAN_OBJECT_FUNCTION_TYPE = new FunctionType(this, "Boolean", null, createArrowType(createOptionalParameters(ALL_TYPE), BOOLEAN_TYPE), null, null, true, true); ObjectType booleanPrototype = BOOLEAN_OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType( JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE, BOOLEAN_OBJECT_FUNCTION_TYPE); ObjectType BOOLEAN_OBJECT_TYPE = BOOLEAN_OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); // Date FunctionType DATE_FUNCTION_TYPE = new FunctionType(this, "Date", null, createArrowType( createOptionalParameters(UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE), STRING_TYPE), null, null, true, true); ObjectType datePrototype = DATE_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.DATE_FUNCTION_TYPE, DATE_FUNCTION_TYPE); ObjectType DATE_TYPE = DATE_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.DATE_TYPE, DATE_TYPE); // Error FunctionType ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "Error"); registerNativeType(JSTypeNative.ERROR_FUNCTION_TYPE, ERROR_FUNCTION_TYPE); ObjectType ERROR_TYPE = ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.ERROR_TYPE, ERROR_TYPE); // EvalError FunctionType EVAL_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "EvalError"); EVAL_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_FUNCTION_TYPE); ObjectType EVAL_ERROR_TYPE = EVAL_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.EVAL_ERROR_TYPE, EVAL_ERROR_TYPE); // RangeError FunctionType RANGE_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "RangeError"); RANGE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_FUNCTION_TYPE); ObjectType RANGE_ERROR_TYPE = RANGE_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.RANGE_ERROR_TYPE, RANGE_ERROR_TYPE); // ReferenceError FunctionType REFERENCE_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "ReferenceError"); REFERENCE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE, REFERENCE_ERROR_FUNCTION_TYPE); ObjectType REFERENCE_ERROR_TYPE = REFERENCE_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.REFERENCE_ERROR_TYPE, REFERENCE_ERROR_TYPE); // SyntaxError FunctionType SYNTAX_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "SyntaxError"); SYNTAX_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE, SYNTAX_ERROR_FUNCTION_TYPE); ObjectType SYNTAX_ERROR_TYPE = SYNTAX_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.SYNTAX_ERROR_TYPE, SYNTAX_ERROR_TYPE); // TypeError FunctionType TYPE_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "TypeError"); TYPE_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_FUNCTION_TYPE); ObjectType TYPE_ERROR_TYPE = TYPE_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.TYPE_ERROR_TYPE, TYPE_ERROR_TYPE); // URIError FunctionType URI_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "URIError"); URI_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.URI_ERROR_FUNCTION_TYPE, URI_ERROR_FUNCTION_TYPE); ObjectType URI_ERROR_TYPE = URI_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.URI_ERROR_TYPE, URI_ERROR_TYPE); // Number FunctionType NUMBER_OBJECT_FUNCTION_TYPE = new FunctionType(this, "Number", null, createArrowType(createOptionalParameters(ALL_TYPE), NUMBER_TYPE), null, null, true, true); ObjectType numberPrototype = NUMBER_OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType( JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE, NUMBER_OBJECT_FUNCTION_TYPE); ObjectType NUMBER_OBJECT_TYPE = NUMBER_OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.NUMBER_OBJECT_TYPE, NUMBER_OBJECT_TYPE); // RegExp FunctionType REGEXP_FUNCTION_TYPE = new FunctionType(this, "RegExp", null, createArrowType(createOptionalParameters(ALL_TYPE, ALL_TYPE)), null, null, true, true); REGEXP_FUNCTION_TYPE.getInternalArrowType().returnType = REGEXP_FUNCTION_TYPE.getInstanceType(); ObjectType regexpPrototype = REGEXP_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE, REGEXP_FUNCTION_TYPE); ObjectType REGEXP_TYPE = REGEXP_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.REGEXP_TYPE, REGEXP_TYPE); // String FunctionType STRING_OBJECT_FUNCTION_TYPE = new FunctionType(this, "String", null, createArrowType(createOptionalParameters(ALL_TYPE), STRING_TYPE), null, null, true, true); ObjectType stringPrototype = STRING_OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType( JSTypeNative.STRING_OBJECT_FUNCTION_TYPE, STRING_OBJECT_FUNCTION_TYPE); ObjectType STRING_OBJECT_TYPE = STRING_OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType( JSTypeNative.STRING_OBJECT_TYPE, STRING_OBJECT_TYPE); // (null,void) JSType NULL_VOID = createUnionType(NULL_TYPE, VOID_TYPE); registerNativeType(JSTypeNative.NULL_VOID, NULL_VOID); // (Object,string,number) JSType OBJECT_NUMBER_STRING = createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE); registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING, OBJECT_NUMBER_STRING); // (Object,string,number,boolean) JSType OBJECT_NUMBER_STRING_BOOLEAN = createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN, OBJECT_NUMBER_STRING_BOOLEAN); // (string,number,boolean) JSType NUMBER_STRING_BOOLEAN = createUnionType(NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); registerNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN, NUMBER_STRING_BOOLEAN); // (string,number) JSType NUMBER_STRING = createUnionType(NUMBER_TYPE, STRING_TYPE); registerNativeType(JSTypeNative.NUMBER_STRING, NUMBER_STRING); // Native object properties are filled in by externs... // (String, string) JSType STRING_VALUE_OR_OBJECT_TYPE = createUnionType(STRING_OBJECT_TYPE, STRING_TYPE); registerNativeType( JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE, STRING_VALUE_OR_OBJECT_TYPE); // (Number, number) JSType NUMBER_VALUE_OR_OBJECT_TYPE = createUnionType(NUMBER_OBJECT_TYPE, NUMBER_TYPE); registerNativeType( JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE); // unknown function type, i.e. (?...) -> ? FunctionType U2U_FUNCTION_TYPE = createFunctionType(UNKNOWN_TYPE, true, UNKNOWN_TYPE); registerNativeType(JSTypeNative.U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE); // unknown constructor type, i.e. (?...) -> ? with the Unknown type // as instance type FunctionType U2U_CONSTRUCTOR_TYPE = // This is equivalent to // createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but, // in addition, overrides getInstanceType() to return the NoObject type // instead of a new anonymous object. new FunctionType(this, "Function", null, createArrowType( createParametersWithVarArgs(UNKNOWN_TYPE), UNKNOWN_TYPE), UNKNOWN_TYPE, null, true, true) { private static final long serialVersionUID = 1L; @Override public FunctionType getConstructor() { return registry.getNativeFunctionType( JSTypeNative.FUNCTION_FUNCTION_TYPE); } }; // The U2U_CONSTRUCTOR is weird, because it's the supertype of its // own constructor. registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE); registerNativeType( JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE); FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE); U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE); // least function type, i.e. (All...) -> NoType FunctionType LEAST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(NO_TYPE, ALL_TYPE); registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE); // the 'this' object in the global scope FunctionType GLOBAL_THIS_CTOR = new FunctionType(this, "global this", null, createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE), null, null, true, true); ObjectType GLOBAL_THIS = GLOBAL_THIS_CTOR.getInstanceType(); registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS); // greatest function type, i.e. (NoType...) -> All FunctionType GREATEST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(ALL_TYPE, NO_TYPE); registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE, GREATEST_FUNCTION_TYPE); // Register the prototype property. See the comments below in // registerPropertyOnType about the bootstrapping process. registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE); } private void initializeRegistry() { register(getNativeType(JSTypeNative.ARRAY_TYPE)); register(getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE)); register(getNativeType(JSTypeNative.BOOLEAN_TYPE)); register(getNativeType(JSTypeNative.DATE_TYPE)); register(getNativeType(JSTypeNative.NULL_TYPE)); register(getNativeType(JSTypeNative.NULL_TYPE), "Null"); register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE)); register(getNativeType(JSTypeNative.NUMBER_TYPE)); register(getNativeType(JSTypeNative.OBJECT_TYPE)); register(getNativeType(JSTypeNative.ERROR_TYPE)); register(getNativeType(JSTypeNative.URI_ERROR_TYPE)); register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE)); register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE)); register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE)); register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE)); register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE)); register(getNativeType(JSTypeNative.REGEXP_TYPE)); register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE)); register(getNativeType(JSTypeNative.STRING_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined"); register(getNativeType(JSTypeNative.VOID_TYPE), "void"); register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function"); } private void register(JSType type) { register(type, type.toString()); } private void register(JSType type, String name) { Preconditions.checkArgument( !name.contains("<"), "Type names cannot contain template annotations."); namesToTypes.put(name, type); // Add all the namespaces in which this name lives. while (name.indexOf('.') > 0) { name = name.substring(0, name.lastIndexOf('.')); namespaces.add(name); } } private void registerNativeType(JSTypeNative typeId, JSType type) { nativeTypes[typeId.ordinal()] = type; } /** * Tells the type system that {@code owner} may have a property named * {@code propertyName}. This allows the registry to keep track of what * types a property is defined upon. * * This is NOT the same as saying that {@code owner} must have a property * named type. ObjectType#hasProperty attempts to minimize false positives * ("if we're not sure, then don't type check this property"). The type * registry, on the other hand, should attempt to minimize false negatives * ("if this property is assigned anywhere in the program, it must * show up in the type registry"). */ public void registerPropertyOnType(String propertyName, JSType type) { UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName); if (typeSet == null) { typeSet = new UnionTypeBuilder(this, PROPERTY_CHECKING_UNION_SIZE); typesIndexedByProperty.put(propertyName, typeSet); } typeSet.addAlternate(type); addReferenceTypeIndexedByProperty(propertyName, type); // Clear cached values that depend on typesIndexedByProperty. greatestSubtypeByProperty.remove(propertyName); } private void addReferenceTypeIndexedByProperty( String propertyName, JSType type) { if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) { Map typeSet = eachRefTypeIndexedByProperty.get(propertyName); if (typeSet == null) { typeSet = Maps.newHashMap(); eachRefTypeIndexedByProperty.put(propertyName, typeSet); } ObjectType objType = (ObjectType) type; typeSet.put(objType.getReferenceName(), objType); } else if (type instanceof NamedType) { addReferenceTypeIndexedByProperty( propertyName, ((NamedType) type).getReferencedType()); } else if (type.isUnionType()) { for (JSType alternate : type.toMaybeUnionType().getAlternates()) { addReferenceTypeIndexedByProperty(propertyName, alternate); } } } /** * Removes the index's reference to a property on the given type (if it is * currently registered). If the property is not registered on the type yet, * this method will not change internal state. * * @param propertyName the name of the property to unregister * @param type the type to unregister the property on. */ public void unregisterPropertyOnType(String propertyName, JSType type) { // TODO(bashir): typesIndexedByProperty should also be updated! Map typeSet = eachRefTypeIndexedByProperty.get(propertyName); if (typeSet != null) { typeSet.remove(type.toObjectType().getReferenceName()); } } /** * Gets the greatest subtype of the {@code type} that has a property * {@code propertyName} defined on it. */ public JSType getGreatestSubtypeWithProperty( JSType type, String propertyName) { if (greatestSubtypeByProperty.containsKey(propertyName)) { return greatestSubtypeByProperty.get(propertyName) .getGreatestSubtype(type); } if (typesIndexedByProperty.containsKey(propertyName)) { JSType built = typesIndexedByProperty.get(propertyName).build(); greatestSubtypeByProperty.put(propertyName, built); return built.getGreatestSubtype(type); } return getNativeType(NO_TYPE); } /** * Returns whether the given property can possibly be set on the given type. */ public boolean canPropertyBeDefined(JSType type, String propertyName) { if (typesIndexedByProperty.containsKey(propertyName)) { for (JSType alt : typesIndexedByProperty.get(propertyName).getAlternates()) { JSType greatestSubtype = alt.getGreatestSubtype(type); if (!greatestSubtype.isEmptyType()) { // We've found a type with this property. Now we just have to make // sure it's not a type used for internal bookkeeping. RecordType maybeRecordType = greatestSubtype.toMaybeRecordType(); if (maybeRecordType != null && maybeRecordType.isSynthetic()) { continue; } return true; } } } return false; } /** * Returns each type that has a property {@code propertyName} defined on it. * * Like most types in our type system, the collection of types returned * will be collapsed. This means that if a type is defined on * {@code Object} and on {@code Array}, it would be reasonable for this * method to return either {@code [Object, Array]} or just {@code [Object]}. */ public Iterable getTypesWithProperty(String propertyName) { if (typesIndexedByProperty.containsKey(propertyName)) { return typesIndexedByProperty.get(propertyName).getAlternates(); } else { return ImmutableList.of(); } } /** * Returns each reference type that has a property {@code propertyName} * defined on it. * * Unlike most types in our type system, the collection of types returned * will not be collapsed. This means that if a type is defined on * {@code Object} and on {@code Array}, this method must return * {@code [Object, Array]}. It would not be correct to collapse them to * {@code [Object]}. */ public Iterable getEachReferenceTypeWithProperty( String propertyName) { if (eachRefTypeIndexedByProperty.containsKey(propertyName)) { return eachRefTypeIndexedByProperty.get(propertyName).values(); } else { return ImmutableList.of(); } } /** * Finds the common supertype of the two given object types. */ ObjectType findCommonSuperObject(ObjectType a, ObjectType b) { List stackA = getSuperStack(a); List stackB = getSuperStack(b); ObjectType result = getNativeObjectType(JSTypeNative.OBJECT_TYPE); while (!stackA.isEmpty() && !stackB.isEmpty()) { ObjectType currentA = stackA.remove(stackA.size() - 1); ObjectType currentB = stackB.remove(stackB.size() - 1); if (currentA.isEquivalentTo(currentB)) { result = currentA; } else { return result; } } return result; } private static List getSuperStack(ObjectType a) { List stack = Lists.newArrayListWithExpectedSize(5); for (ObjectType current = a; current != null; current = current.getImplicitPrototype()) { stack.add(current); } return stack; } /** * Increments the current generation. Clients must call this in order to * move to the next generation of type resolution, allowing types to attempt * resolution again. */ public void incrementGeneration() { for (NamedType type : resolvedNamedTypes.values()) { type.clearResolved(); } unresolvedNamedTypes.putAll(resolvedNamedTypes); resolvedNamedTypes.clear(); } boolean isLastGeneration() { return lastGeneration; } /** * Sets whether this is the last generation. In the last generation, * {@link NamedType} warns about unresolved types. */ public void setLastGeneration(boolean lastGeneration) { this.lastGeneration = lastGeneration; } /** * Tells the type system that {@code type} implements interface {@code * interfaceInstance}. * {@code inter} must be an ObjectType for the instance of the interface as it * could be a named type and not yet have the constructor. */ void registerTypeImplementingInterface( FunctionType type, ObjectType interfaceInstance) { interfaceToImplementors.put(interfaceInstance.getReferenceName(), type); } /** * Returns a collection of types that directly implement {@code * interfaceInstance}. Subtypes of implementing types are not guaranteed to * be returned. {@code interfaceInstance} must be an ObjectType for the * instance of the interface. */ public Collection getDirectImplementors( ObjectType interfaceInstance) { return interfaceToImplementors.get(interfaceInstance.getReferenceName()); } /** * Records declared global type names. This makes resolution faster * and more robust in the common case. * * @param name The name of the type to be recorded. * @param t The actual type being associated with the name. * @return True if this name is not already defined, false otherwise. */ public boolean declareType(String name, JSType t) { if (namesToTypes.containsKey(name)) { return false; } register(t, name); return true; } /** * Overrides a declared global type name. Throws an exception if this * type name hasn't been declared yet. */ public void overwriteDeclaredType(String name, JSType t) { Preconditions.checkState(namesToTypes.containsKey(name)); register(t, name); } /** * Records a forward-declared type name. We will not emit errors if this * type name never resolves to anything. */ public void forwardDeclareType(String name) { forwardDeclaredTypes.add(name); } /** * Whether this is a forward-declared type name. */ public boolean isForwardDeclaredType(String name) { return forwardDeclaredTypes.contains(name); } /** Determines whether the given JS package exists. */ public boolean hasNamespace(String name) { return namespaces.contains(name); } /** * Looks up a type by name. * * @param jsTypeName The name string. * @return the corresponding JSType object or {@code null} it cannot be found */ public JSType getType(String jsTypeName) { // TODO(user): Push every local type name out of namesToTypes so that // NamedType#resolve is correct. TemplateType templateType = templateTypes.get(jsTypeName); if (templateType != null) { return templateType; } return namesToTypes.get(jsTypeName); } public JSType getNativeType(JSTypeNative typeId) { return nativeTypes[typeId.ordinal()]; } public ObjectType getNativeObjectType(JSTypeNative typeId) { return (ObjectType) getNativeType(typeId); } public FunctionType getNativeFunctionType(JSTypeNative typeId) { return (FunctionType) getNativeType(typeId); } /** * Looks up a type by name. To allow for forward references to types, an * unrecognized string has to be bound to a NamedType object that will be * resolved later. * * @param scope A scope for doing type name resolution. * @param jsTypeName The name string. * @param sourceName The name of the source file where this reference appears. * @param lineno The line number of the reference. * @return a NamedType if the string argument is not one of the known types, * otherwise the corresponding JSType object. */ public JSType getType(StaticScope scope, String jsTypeName, String sourceName, int lineno, int charno) { JSType type = getType(jsTypeName); if (type == null) { // TODO(user): Each instance should support named type creation using // interning. NamedType namedType = new NamedType(this, jsTypeName, sourceName, lineno, charno); unresolvedNamedTypes.put(scope, namedType); type = namedType; } return type; } /** * Flushes out the current resolved and unresolved Named Types from * the type registry. This is intended to be used ONLY before a * compile is run. */ public void clearNamedTypes() { resolvedNamedTypes.clear(); unresolvedNamedTypes.clear(); } /** * Resolve all the unresolved types in the given scope. */ public void resolveTypesInScope(StaticScope scope) { for (NamedType type : unresolvedNamedTypes.get(scope)) { type.resolve(reporter, scope); } resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope)); if (scope != null && scope.getParentScope() == null) { // By default, the global "this" type is just an anonymous object. // If the user has defined a Window type, make the Window the // implicit prototype of "this". PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType( JSTypeNative.GLOBAL_THIS); JSType windowType = getType("Window"); if (globalThis.isUnknownType()) { ObjectType windowObjType = ObjectType.cast(windowType); if (windowObjType != null) { globalThis.setImplicitPrototype(windowObjType); } else { globalThis.setImplicitPrototype( getNativeObjectType(JSTypeNative.OBJECT_TYPE)); } } } } /** * Creates a type representing optional values of the given type. * @return the union of the type and the void type */ public JSType createOptionalType(JSType type) { if (type instanceof UnknownType || type.isAllType()) { return type; } else { return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE)); } } /** * Creates a type representing nullable values of the given type. * @return the union of the type and the Null type */ public JSType createDefaultObjectUnion(JSType type) { if (type.isTemplateType()) { // Template types represent the substituted type exactly and should // not be wrapped. return type; } else { return shouldTolerateUndefinedValues() ? createOptionalNullableType(type) : createNullableType(type); } } /** * Creates a type representing nullable values of the given type. * @return the union of the type and the Null type */ public JSType createNullableType(JSType type) { return createUnionType(type, getNativeType(JSTypeNative.NULL_TYPE)); } /** * Creates a nullable and undefine-able value of the given type. * @return The union of the type and null and undefined. */ public JSType createOptionalNullableType(JSType type) { return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE), getNativeType(JSTypeNative.NULL_TYPE)); } /** * Creates a union type whose variants are the arguments. */ public JSType createUnionType(JSType... variants) { UnionTypeBuilder builder = new UnionTypeBuilder(this); for (JSType type : variants) { builder.addAlternate(type); } return builder.build(); } /** * Creates a union type whose variants are the built-in types specified * by the arguments. */ public JSType createUnionType(JSTypeNative... variants) { UnionTypeBuilder builder = new UnionTypeBuilder(this); for (JSTypeNative typeId : variants) { builder.addAlternate(getNativeType(typeId)); } return builder.build(); } /** * Creates an enum type. */ public EnumType createEnumType( String name, Node source, JSType elementsType) { return new EnumType(this, name, source, elementsType); } /** * Creates an arrow type, an abstract representation of the parameters * and return value of a function. * * @param parametersNode the parameters' types, formatted as a Node with * param names and optionality info. * @param returnType the function's return type */ ArrowType createArrowType(Node parametersNode, JSType returnType) { return new ArrowType(this, parametersNode, returnType); } /** * Creates an arrow type with an unknown return type. * * @param parametersNode the parameters' types, formatted as a Node with * param names and optionality info. */ ArrowType createArrowType(Node parametersNode) { return new ArrowType(this, parametersNode, null); } /** * Creates a function type. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ public FunctionType createFunctionType( JSType returnType, JSType... parameterTypes) { return createFunctionType(returnType, createParameters(parameterTypes)); } /** * Creates a function type. The last parameter type of the function is * considered a variable length argument. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ public FunctionType createFunctionTypeWithVarArgs( JSType returnType, List parameterTypes) { return createFunctionType( returnType, createParametersWithVarArgs(parameterTypes)); } /** * Creates a function type. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ public FunctionType createFunctionType( JSType returnType, List parameterTypes) { return createFunctionType(returnType, createParameters(parameterTypes)); } /** * Creates a function type. The last parameter type of the function is * considered a variable length argument. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ public FunctionType createFunctionTypeWithVarArgs( JSType returnType, JSType... parameterTypes) { return createFunctionType( returnType, createParametersWithVarArgs(parameterTypes)); } /** * Creates a function type. The last parameter type of the function is * considered a variable length argument. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ private FunctionType createNativeFunctionTypeWithVarArgs( JSType returnType, JSType... parameterTypes) { return createNativeFunctionType( returnType, createParametersWithVarArgs(parameterTypes)); } /** * Creates a function type which can act as a constructor. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ public FunctionType createConstructorType( JSType returnType, JSType... parameterTypes) { return createConstructorType( null, null, createParameters(parameterTypes), returnType, null); } /** * Creates a function type which can act as a constructor. The last * parameter type of the constructor is considered a variable length argument. * * @param returnType the function's return type * @param parameterTypes the parameters' types */ private FunctionType createConstructorTypeWithVarArgs( JSType returnType, JSType... parameterTypes) { return createConstructorType( null, null, createParametersWithVarArgs(parameterTypes), returnType, null); } /** * Creates a function type in which {@code this} refers to an object instance. * * @param instanceType the type of {@code this} * @param returnType the function's return type * @param parameterTypes the parameters' types */ public JSType createFunctionType(ObjectType instanceType, JSType returnType, List parameterTypes) { return new FunctionBuilder(this) .withParamsNode(createParameters(parameterTypes)) .withReturnType(returnType) .withTypeOfThis(instanceType) .build(); } /** * Creates a function type in which {@code this} refers to an object instance. * The last parameter type of the function is considered a variable length * argument. * * @param instanceType the type of {@code this} * @param returnType the function's return type * @param parameterTypes the parameters' types */ public JSType createFunctionTypeWithVarArgs(ObjectType instanceType, JSType returnType, List parameterTypes) { return new FunctionBuilder(this) .withParamsNode(createParametersWithVarArgs(parameterTypes)) .withReturnType(returnType) .withTypeOfThis(instanceType) .build(); } /** * Creates a tree hierarchy representing a typed argument list. * * @param parameterTypes the parameter types. * @return a tree hierarchy representing a typed argument list. */ public Node createParameters(List parameterTypes) { return createParameters( parameterTypes.toArray(new JSType[parameterTypes.size()])); } /** * Creates a tree hierarchy representing a typed argument list. The last * parameter type is considered a variable length argument. * * @param parameterTypes the parameter types. The last element of this array * is considered a variable length argument. * @return a tree hierarchy representing a typed argument list. */ public Node createParametersWithVarArgs(List parameterTypes) { return createParametersWithVarArgs( parameterTypes.toArray(new JSType[parameterTypes.size()])); } /** * Creates a tree hierarchy representing a typed argument list. * * @param parameterTypes the parameter types. * @return a tree hierarchy representing a typed argument list. */ public Node createParameters(JSType... parameterTypes) { return createParameters(false, parameterTypes); } /** * Creates a tree hierarchy representing a typed argument list. The last * parameter type is considered a variable length argument. * * @param parameterTypes the parameter types. The last element of this array * is considered a variable length argument. * @return a tree hierarchy representing a typed argument list. */ public Node createParametersWithVarArgs(JSType... parameterTypes) { return createParameters(true, parameterTypes); } /** * Creates a tree hierarchy representing a typed parameter list in which * every parameter is optional. */ public Node createOptionalParameters(JSType... parameterTypes) { FunctionParamBuilder builder = new FunctionParamBuilder(this); builder.addOptionalParams(parameterTypes); return builder.build(); } /** * Creates a tree hierarchy representing a typed argument list. * * @param lastVarArgs whether the last type should considered as a variable * length argument. * @param parameterTypes the parameter types. The last element of this array * is considered a variable length argument is {@code lastVarArgs} is * {@code true}. * @return a tree hierarchy representing a typed argument list */ private Node createParameters(boolean lastVarArgs, JSType... parameterTypes) { FunctionParamBuilder builder = new FunctionParamBuilder(this); int max = parameterTypes.length - 1; for (int i = 0; i <= max; i++) { if (lastVarArgs && i == max) { builder.addVarArgs(parameterTypes[i]); } else { builder.addRequiredParams(parameterTypes[i]); } } return builder.build(); } /** * Creates a function type. * @param returnType the function's return type * @param lastVarArgs whether the last parameter type should be considered as * an extensible var_args parameter * @param parameterTypes the parameters' types */ public FunctionType createFunctionType(JSType returnType, boolean lastVarArgs, JSType... parameterTypes) { if (lastVarArgs) { return createFunctionTypeWithVarArgs(returnType, parameterTypes); } else { return createFunctionType(returnType, parameterTypes); } } /** * Creates a new function type based on an existing function type but * with a new return type. * @param existingFunctionType the existing function type. * @param returnType the new return type. */ public FunctionType createFunctionTypeWithNewReturnType( FunctionType existingFunctionType, JSType returnType) { return new FunctionBuilder(this) .copyFromOtherFunction(existingFunctionType) .withReturnType(returnType) .build(); } /** * Creates a new function type based on an existing function type but * with a new {@code this} type. * @param existingFunctionType the existing function type. * @param thisType the new this type. */ public FunctionType createFunctionTypeWithNewThisType( FunctionType existingFunctionType, ObjectType thisType) { return new FunctionBuilder(this) .copyFromOtherFunction(existingFunctionType) .withTypeOfThis(thisType) .build(); } /** * @param parameters the function's parameters or {@code null} * to indicate that the parameter types are unknown. * @param returnType the function's return type or {@code null} to indicate * that the return type is unknown. */ public FunctionType createFunctionType( JSType returnType, Node parameters) { return new FunctionBuilder(this) .withParamsNode(parameters) .withReturnType(returnType) .build(); } private FunctionType createNativeFunctionType( JSType returnType, Node parameters) { return new FunctionBuilder(this) .withParamsNode(parameters) .withReturnType(returnType) .forNativeType() .build(); } /** * Creates a function type which can act as a constructor. * @param returnType the function's return type * @param lastVarArgs whether the last parameter type should be considered as * an extensible var_args parameter * @param parameterTypes the parameters' types */ public FunctionType createConstructorType(JSType returnType, boolean lastVarArgs, JSType... parameterTypes) { if (lastVarArgs) { return createConstructorTypeWithVarArgs(returnType, parameterTypes); } else { return createConstructorType(returnType, parameterTypes); } } /** * Create an object type. */ public ObjectType createObjectType(ObjectType implicitPrototype) { return createObjectType(null, null, implicitPrototype); } /** * Creates a record type. */ public RecordType createRecordType(Map properties) { return new RecordType(this, properties); } /** * Create an object type. */ public ObjectType createObjectType(String name, Node n, ObjectType implicitPrototype) { return new PrototypeObjectType(this, name, implicitPrototype); } /** * Create an anonymous object type. * @param info Used to mark object literals as structs; can be {@code null} */ public ObjectType createAnonymousObjectType(JSDocInfo info) { PrototypeObjectType type = new PrototypeObjectType(this, null, null); type.setPrettyPrint(true); type.setJSDocInfo(info); return type; } /** * Set the implicit prototype if it's possible to do so. * @return True if we were able to set the implicit prototype successfully, * false if it was not possible to do so for some reason. There are * a few different reasons why this could fail: for example, numbers * can't be implicit prototypes, and we don't want to change the implicit * prototype if other classes have already subclassed this one. */ public boolean resetImplicitPrototype( JSType type, ObjectType newImplicitProto) { if (type instanceof PrototypeObjectType) { PrototypeObjectType poType = (PrototypeObjectType) type; poType.clearCachedValues(); poType.setImplicitPrototype(newImplicitProto); return true; } return false; } /** * Create an anonymous object type for a native type. */ ObjectType createNativeAnonymousObjectType() { PrototypeObjectType type = new PrototypeObjectType(this, null, null, true, null); type.setPrettyPrint(true); return type; } /** * Creates a constructor function type. * @param name the function's name or {@code null} to indicate that the * function is anonymous. * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. * @param parameters the function's parameters or {@code null} * to indicate that the parameter types are unknown. * @param returnType the function's return type or {@code null} to indicate * that the return type is unknown. * @param templateKeyNames the templatized type keys for the class. */ public FunctionType createConstructorType(String name, Node source, Node parameters, JSType returnType, ImmutableList templateKeyNames) { return createConstructorTypeInternal(name, source, parameters, returnType, createTemplateMapKeys(templateKeyNames)); } private FunctionType createConstructorTypeInternal(String name, Node source, Node parameters, JSType returnType, ImmutableList templateKeys) { return new FunctionType(this, name, source, createArrowType(parameters, returnType), null, createTemplateTypeMap(templateKeys, null), true, false); } ImmutableList createTemplateMapKeys(ImmutableList keys) { ImmutableList.Builder builder = ImmutableList.builder(); if (keys != null) { for (String key : keys) { builder.add(new TemplateType(this, key)); } } return builder.build(); } /** * Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. */ public FunctionType createInterfaceType(String name, Node source) { return FunctionType.forInterface(this, name, source); } public TemplateType createTemplateType(String name) { return new TemplateType(this, name); } /** * Creates a template type map from the specified list of template keys and * template value types. */ public TemplateTypeMap createTemplateTypeMap( ImmutableList templateKeys, ImmutableList templateValues) { templateKeys = templateKeys == null ? ImmutableList.of() : templateKeys; templateValues = templateValues == null ? ImmutableList.of() : templateValues; return (templateKeys.isEmpty() && templateValues.isEmpty()) ? emptyTemplateTypeMap : new TemplateTypeMap(this, templateKeys, templateValues); } /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when * more types can be templatized. * @param baseType the type to be templatized. * @param templatizedTypes a list of the template JSTypes. Will be matched by * list order to the template keys on the base type. */ public TemplatizedType createTemplatizedType( ObjectType baseType, ImmutableList templatizedTypes) { // Only ObjectTypes can currently be templatized; extend this logic when // more types can be templatized. return new TemplatizedType(this, baseType, templatizedTypes); } /** * Creates a templatized instance of the specified type. Only ObjectTypes * can currently be templatized; extend the logic in this function when * more types can be templatized. * @param baseType the type to be templatized. * @param templatizedTypes a list of the template JSTypes. Will be matched by * list order to the template keys on the base type. */ public TemplatizedType createTemplatizedType( ObjectType baseType, JSType... templatizedTypes) { return createTemplatizedType( baseType, ImmutableList.copyOf(templatizedTypes)); } /** * Creates a named type. */ @VisibleForTesting public JSType createNamedType(String reference, String sourceName, int lineno, int charno) { return new NamedType(this, reference, sourceName, lineno, charno); } /** * Identifies the name of a typedef or enum before we actually declare it. */ public void identifyNonNullableName(String name) { Preconditions.checkNotNull(name); nonNullableTypeNames.add(name); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope scope) { if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName); } } return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); int nAllowedTypes = namedType.getTemplateTypeMap().numUnfilledTemplateKeys(); if (typeList != null && nAllowedTypes > 0) { // Templatized types. ImmutableList.Builder templateTypes = ImmutableList.builder(); // Special case for Object, where Object. implies Object.. if (n.getString().equals("Object") && typeList.getFirstChild() == typeList.getLastChild()) { templateTypes.add(getNativeType(UNKNOWN_TYPE)); } int templateNodeIndex = 0; for (Node templateNode : typeList.getFirstChild().siblings()) { // Don't parse more templatized type nodes than the type can // accommodate. This is because some existing clients have // template annotations on non-templatized classes, for instance: // goog.structs.Set. // The problem in these cases is that the previously-unparsed // SomeType is not actually a valid type. To prevent these clients // from seeing unknown type errors, we explicitly don't parse // these types. // TODO(user): Address this issue by removing bad template // annotations on non-templatized classes. if (++templateNodeIndex > nAllowedTypes) { break; } templateTypes.add(createFromTypeNodesInternal( templateNode, sourceName, scope)); } namedType = createTemplatizedType( (ObjectType) namedType, templateTypes.build()); Preconditions.checkNotNull(namedType); } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; boolean isConstructor = false; Node current = n.getFirstChild(); if (current.getType() == Token.THIS || current.getType() == Token.NEW) { Node contextNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( contextNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0( current.getType() == Token.THIS ? "msg.jsdoc.function.thisnotobject" : "msg.jsdoc.function.newnotobject"), sourceName, contextNode.getLineno(), contextNode.getCharno()); } isConstructor = current.getType() == Token.NEW; current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.PARAM_LIST) { for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeNames(List keys) { Preconditions.checkNotNull(keys); for (TemplateType key : keys) { templateTypes.put(key.getReferenceName(), key); } } /** * Clears the template type name. */ public void clearTemplateTypeNames() { templateTypes.clear(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/FunctionType.java0000644000175000017500000012055412115204405027277 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Collections; import java.util.List; import java.util.Set; /** * This derived type provides extended information about a function, including * its return type and argument types.

      * * Note: the parameters list is the LP node that is the parent of the * actual NAME node containing the parsed argument list (annotated with * JSDOC_TYPE_PROP's for the compile-time type of each argument. */ public class FunctionType extends PrototypeObjectType { private static final long serialVersionUID = 1L; private enum Kind { ORDINARY, CONSTRUCTOR, INTERFACE } // relevant only for constructors private enum PropAccess { ANY, STRUCT, DICT } /** * {@code [[Call]]} property. */ private ArrowType call; /** * The {@code prototype} property. This field is lazily initialized by * {@code #getPrototype()}. The most important reason for lazily * initializing this field is that there are cycles in the native types * graph, so some prototypes must temporarily be {@code null} during * the construction of the graph. * * If non-null, the type must be a PrototypeObjectType. */ private Property prototypeSlot; /** * Whether a function is a constructor, an interface, or just an ordinary * function. */ private final Kind kind; /** * Whether the instances are structs, dicts, or unrestricted. */ private PropAccess propAccess; /** * The type of {@code this} in the scope of this function. */ private JSType typeOfThis; /** * The function node which this type represents. It may be {@code null}. */ private Node source; /** * The interfaces directly implemented by this function (for constructors) * It is only relevant for constructors. May not be {@code null}. */ private List implementedInterfaces = ImmutableList.of(); /** * The interfaces directly extended by this function (for interfaces) * It is only relevant for constructors. May not be {@code null}. */ private List extendedInterfaces = ImmutableList.of(); /** * The types which are subtypes of this function. It is only relevant for * constructors and may be {@code null}. */ private List subTypes; /** Creates an instance for a function that might be a constructor. */ FunctionType(JSTypeRegistry registry, String name, Node source, ArrowType arrowType, JSType typeOfThis, TemplateTypeMap templateTypeMap, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType, templateTypeMap); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkNotNull(arrowType); this.source = source; if (isConstructor) { this.kind = Kind.CONSTRUCTOR; this.propAccess = PropAccess.ANY; this.typeOfThis = typeOfThis != null ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.kind = Kind.ORDINARY; this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } this.call = arrowType; } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name, source); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. It overrides // getConstructor() appropriately when it's declared. return this == registry.getNativeType(U2U_CONSTRUCTOR_TYPE); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } /** * When a class B inherits from A and A is annotated as a struct, then B * automatically gets the annotation, even if B's constructor is not * explicitly annotated. */ public boolean makesStructs() { if (!isConstructor()) { return false; } if (propAccess == PropAccess.STRUCT) { return true; } FunctionType superc = getSuperClassConstructor(); if (superc != null && superc.makesStructs()) { setStruct(); return true; } return false; } /** * When a class B inherits from A and A is annotated as a dict, then B * automatically gets the annotation, even if B's constructor is not * explicitly annotated. */ public boolean makesDicts() { if (!isConstructor()) { return false; } if (propAccess == PropAccess.DICT) { return true; } FunctionType superc = getSuperClassConstructor(); if (superc != null && superc.makesDicts()) { setDict(); return true; } return false; } public void setStruct() { propAccess = PropAccess.STRUCT; } public void setDict() { propAccess = PropAccess.DICT; } @Override public FunctionType toMaybeFunctionType() { return this; } @Override public boolean canBeCalled() { return true; } public boolean hasImplementedInterfaces() { if (!implementedInterfaces.isEmpty()){ return true; } FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor != null) { return superCtor.hasImplementedInterfaces(); } return false; } public Iterable getParameters() { Node n = getParametersNode(); if (n != null) { return n.children(); } else { return Collections.emptySet(); } } /** Gets an LP node that contains all params. May be null. */ public Node getParametersNode() { return call.parameters; } /** Gets the minimum number of arguments that this function requires. */ public int getMinArguments() { // NOTE(nicksantos): There are some native functions that have optional // parameters before required parameters. This algorithm finds the position // of the last required parameter. int i = 0; int min = 0; for (Node n : getParameters()) { i++; if (!n.isOptionalArg() && !n.isVarArgs()) { min = i; } } return min; } /** * Gets the maximum number of arguments that this function requires, * or Integer.MAX_VALUE if this is a variable argument function. */ public int getMaxArguments() { Node params = getParametersNode(); if (params != null) { Node lastParam = params.getLastChild(); if (lastParam == null || !lastParam.isVarArgs()) { return params.getChildCount(); } } return Integer.MAX_VALUE; } public JSType getReturnType() { return call.returnType; } public boolean isReturnTypeInferred() { return call.returnTypeInferred; } /** Gets the internal arrow type. For use by subclasses only. */ ArrowType getInternalArrowType() { return call; } @Override public Property getSlot(String name) { if ("prototype".equals(name)) { // Lazy initialization of the prototype field. getPrototype(); return prototypeSlot; } else { return super.getSlot(name); } } /** * Includes the prototype iff someone has created it. We do not want * to expose the prototype for ordinary functions. */ @Override public Set getOwnPropertyNames() { if (prototypeSlot == null) { return super.getOwnPropertyNames(); } else { Set names = Sets.newHashSet("prototype"); names.addAll(super.getOwnPropertyNames()); return names; } } /** * Gets the {@code prototype} property of this function type. This is * equivalent to {@code (ObjectType) getPropertyType("prototype")}. */ public ObjectType getPrototype() { // lazy initialization of the prototype field if (prototypeSlot == null) { String refName = getReferenceName(); if (refName == null) { // Someone is trying to access the prototype of a structural function. // We don't want to give real properties to this prototype, because // then it would propagate to all structural functions. setPrototypeNoCheck( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE), null); } else { setPrototype( new PrototypeObjectType( registry, getReferenceName() + ".prototype", registry.getNativeObjectType(OBJECT_TYPE), isNativeObjectType(), null), null); } } return (ObjectType) prototypeSlot.getType(); } /** * Sets the prototype, creating the prototype object from the given * base type. * @param baseType The base type. */ public void setPrototypeBasedOn(ObjectType baseType) { setPrototypeBasedOn(baseType, null); } void setPrototypeBasedOn(ObjectType baseType, Node propertyNode) { // This is a bit weird. We need to successfully handle these // two cases: // Foo.prototype = new Bar(); // and // Foo.prototype = {baz: 3}; // In the first case, we do not want new properties to get // added to Bar. In the second case, we do want new properties // to get added to the type of the anonymous object. // // We handle this by breaking it into two cases: // // In the first case, we create a new PrototypeObjectType and set // its implicit prototype to the type being assigned. This ensures // that Bar will not get any properties of Foo.prototype, but properties // later assigned to Bar will get inherited properly. // // In the second case, we just use the anonymous object as the prototype. if (baseType.hasReferenceName() || isNativeObjectType() || baseType.isFunctionPrototypeType()) { baseType = new PrototypeObjectType( registry, getReferenceName() + ".prototype", baseType); } setPrototype(baseType, propertyNode); } /** * Sets the prototype. * @param prototype the prototype. If this value is {@code null} it will * silently be discarded. */ boolean setPrototype(ObjectType prototype, Node propertyNode) { if (prototype == null) { return false; } // getInstanceType fails if the function is not a constructor if (isConstructor() && prototype == getInstanceType()) { return false; } return setPrototypeNoCheck(prototype, propertyNode); } /** Set the prototype without doing any sanity checks. */ private boolean setPrototypeNoCheck(ObjectType prototype, Node propertyNode) { ObjectType oldPrototype = prototypeSlot == null ? null : (ObjectType) prototypeSlot.getType(); boolean replacedPrototype = oldPrototype != null; this.prototypeSlot = new Property("prototype", prototype, true, propertyNode == null ? source : propertyNode); prototype.setOwnerFunction(this); if (oldPrototype != null) { // Disassociating the old prototype makes this easier to debug-- // we don't have to worry about two prototypes running around. oldPrototype.setOwnerFunction(null); } if (isConstructor() || isInterface()) { FunctionType superClass = getSuperClassConstructor(); if (superClass != null) { superClass.addSubType(this); } if (isInterface()) { for (ObjectType interfaceType : getExtendedInterfaces()) { if (interfaceType.getConstructor() != null) { interfaceType.getConstructor().addSubType(this); } } } } if (replacedPrototype) { clearCachedValues(); } return true; } /** * Returns all interfaces implemented by a class or its superclass and any * superclasses for any of those interfaces. If this is called before all * types are resolved, it may return an incomplete set. */ public Iterable getAllImplementedInterfaces() { // Store them in a linked hash set, so that the compile job is // deterministic. Set interfaces = Sets.newLinkedHashSet(); for (ObjectType type : getImplementedInterfaces()) { addRelatedInterfaces(type, interfaces); } return interfaces; } private void addRelatedInterfaces(ObjectType instance, Set set) { FunctionType constructor = instance.getConstructor(); if (constructor != null) { if (!constructor.isInterface()) { return; } set.add(instance); for (ObjectType interfaceType : instance.getCtorExtendedInterfaces()) { addRelatedInterfaces(interfaceType, set); } } } /** Returns interfaces implemented directly by a class or its superclass. */ public Iterable getImplementedInterfaces() { FunctionType superCtor = isConstructor() ? getSuperClassConstructor() : null; if (superCtor == null) { return implementedInterfaces; } else { return Iterables.concat( implementedInterfaces, superCtor.getImplementedInterfaces()); } } /** Returns interfaces directly implemented by the class. */ public Iterable getOwnImplementedInterfaces() { return implementedInterfaces; } public void setImplementedInterfaces(List implementedInterfaces) { if (isConstructor()) { // Records this type for each implemented interface. for (ObjectType type : implementedInterfaces) { registry.registerTypeImplementingInterface(this, type); } this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); } else { throw new UnsupportedOperationException(); } } /** * Returns all extended interfaces declared by an interfaces or its super- * interfaces. If this is called before all types are resolved, it may return * an incomplete set. */ public Iterable getAllExtendedInterfaces() { // Store them in a linked hash set, so that the compile job is // deterministic. Set extendedInterfaces = Sets.newLinkedHashSet(); for (ObjectType interfaceType : getExtendedInterfaces()) { addRelatedExtendedInterfaces(interfaceType, extendedInterfaces); } return extendedInterfaces; } private void addRelatedExtendedInterfaces(ObjectType instance, Set set) { FunctionType constructor = instance.getConstructor(); if (constructor != null) { set.add(instance); for (ObjectType interfaceType : constructor.getExtendedInterfaces()) { addRelatedExtendedInterfaces(interfaceType, set); } } } /** Returns interfaces directly extended by an interface */ public Iterable getExtendedInterfaces() { return extendedInterfaces; } /** Returns the number of interfaces directly extended by an interface */ public int getExtendedInterfacesCount() { return extendedInterfaces.size(); } public void setExtendedInterfaces(List extendedInterfaces) throws UnsupportedOperationException { if (isInterface()) { this.extendedInterfaces = ImmutableList.copyOf(extendedInterfaces); } else { throw new UnsupportedOperationException(); } } @Override public JSType getPropertyType(String name) { if (!hasOwnProperty(name)) { // Define the "call", "apply", and "bind" functions lazily. boolean isCall = "call".equals(name); boolean isBind = "bind".equals(name); if (isCall || isBind) { defineDeclaredProperty(name, getCallOrBindSignature(isCall), source); } else if ("apply".equals(name)) { // Define the "apply" function lazily. FunctionParamBuilder builder = new FunctionParamBuilder(registry); // ECMA-262 says that apply's second argument must be an Array // or an arguments object. We don't model the arguments object, // so let's just be forgiving for now. // TODO(nicksantos): Model the Arguments object. builder.addOptionalParams( registry.createNullableType(getTypeOfThis()), registry.createNullableType( registry.getNativeType(JSTypeNative.OBJECT_TYPE))); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParams(builder) .withReturnType(getReturnType()) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()) .build(), source); } } return super.getPropertyType(name); } /** * Get the return value of calling "bind" on this function * with the specified number of arguments. * * If -1 is passed, then we will return a result that accepts * any parameters. */ public FunctionType getBindReturnType(int argsToBind) { FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(getReturnType()) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()); if (argsToBind >= 0) { Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) { if (params.getFirstChild().isVarArgs()) { break; } params.removeFirstChild(); } builder.withParamsNode(params); } } return builder.build(); } /** * Notice that "call" and "bind" have the same argument signature, * except that all the arguments of "bind" (except the first) * are optional. */ private FunctionType getCallOrBindSignature(boolean isCall) { boolean isBind = !isCall; FunctionBuilder builder = new FunctionBuilder(registry) .withReturnType(isCall ? getReturnType() : getBindReturnType(-1)) .withTemplateKeys(getTemplateTypeMap().getTemplateKeys()); Node origParams = getParametersNode(); if (origParams != null) { Node params = origParams.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); if (isBind) { // The arguments of bind() are unique in that they are all // optional but not undefinable. for (Node current = thisTypeNode.getNext(); current != null; current = current.getNext()) { current.setOptionalArg(true); } } else if (isCall) { // The first argument of call() is optional iff all the arguments // are optional. It's sufficient to check the first argument. Node firstArg = thisTypeNode.getNext(); if (firstArg == null || firstArg.isOptionalArg() || firstArg.isVarArgs()) { thisTypeNode.setOptionalArg(true); } } builder.withParamsNode(params); } return builder.build(); } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if ("prototype".equals(name)) { ObjectType objType = type.toObjectType(); if (objType != null) { if (prototypeSlot != null && objType.isEquivalentTo(prototypeSlot.getType())) { return true; } setPrototypeBasedOn(objType, propertyNode); return true; } else { return false; } } return super.defineProperty(name, type, inferred, propertyNode); } /** * Computes the supremum or infimum of two functions. * Because sup() and inf() share a lot of logic for functions, we use * a single helper. * @param leastSuper If true, compute the supremum of {@code this} with * {@code that}. Otherwise, compute the infimum. * @return The least supertype or greatest subtype. */ FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) { // NOTE(nicksantos): When we remove the unknown type, the function types // form a lattice with the universal constructor at the top of the lattice, // and the LEAST_FUNCTION_TYPE type at the bottom of the lattice. // // When we introduce the unknown type, it's much more difficult to make // heads or tails of the partial ordering of types, because there's no // clear hierarchy between the different components (parameter types and // return types) in the ArrowType. // // Rather than make the situation more complicated by introducing new // types (like unions of functions), we just fallback on the simpler // approach of getting things right at the top and the bottom of the // lattice. // // If there are unknown parameters or return types making things // ambiguous, then sup(A, B) is always the top function type, and // inf(A, B) is always the bottom function type. Preconditions.checkNotNull(that); if (isEquivalentTo(that)) { return this; } // If these are ordinary functions, then merge them. // Don't do this if any of the params/return // values are unknown, because then there will be cycles in // their local lattice and they will merge in weird ways. if (isOrdinaryFunction() && that.isOrdinaryFunction() && !this.call.hasUnknownParamsOrReturn() && !that.call.hasUnknownParamsOrReturn()) { // Check for the degenerate case, but double check // that there's not a cycle. boolean isSubtypeOfThat = isSubtype(that); boolean isSubtypeOfThis = that.isSubtype(this); if (isSubtypeOfThat && !isSubtypeOfThis) { return leastSuper ? that : this; } else if (isSubtypeOfThis && !isSubtypeOfThat) { return leastSuper ? this : that; } // Merge the two functions component-wise. FunctionType merged = tryMergeFunctionPiecewise(that, leastSuper); if (merged != null) { return merged; } } // The function instance type is a special case // that lives above the rest of the lattice. JSType functionInstance = registry.getNativeType( JSTypeNative.FUNCTION_INSTANCE_TYPE); if (functionInstance.isEquivalentTo(that)) { return leastSuper ? that : this; } else if (functionInstance.isEquivalentTo(this)) { return leastSuper ? this : that; } // In theory, we should be using the GREATEST_FUNCTION_TYPE as the // greatest function. In practice, we don't because it's way too // broad. The greatest function takes var_args None parameters, which // means that all parameters register a type warning. // // Instead, we use the U2U ctor type, which has unknown type args. FunctionType greatestFn = registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); FunctionType leastFn = registry.getNativeFunctionType(JSTypeNative.LEAST_FUNCTION_TYPE); return leastSuper ? greatestFn : leastFn; } /** * Try to get the sup/inf of two functions by looking at the * piecewise components. */ private FunctionType tryMergeFunctionPiecewise( FunctionType other, boolean leastSuper) { Node newParamsNode = null; if (call.hasEqualParameters(other.call, EquivalenceMethod.IDENTITY)) { newParamsNode = call.parameters; } else { // If the parameters are not equal, don't try to merge them. // Someday, we should try to merge the individual params. return null; } JSType newReturnType = leastSuper ? call.returnType.getLeastSupertype(other.call.returnType) : call.returnType.getGreatestSubtype(other.call.returnType); JSType newTypeOfThis = null; if (isEquivalent(typeOfThis, other.typeOfThis)) { newTypeOfThis = typeOfThis; } else { JSType maybeNewTypeOfThis = leastSuper ? typeOfThis.getLeastSupertype(other.typeOfThis) : typeOfThis.getGreatestSubtype(other.typeOfThis); newTypeOfThis = maybeNewTypeOfThis; } boolean newReturnTypeInferred = call.returnTypeInferred || other.call.returnTypeInferred; return new FunctionType( registry, null, null, new ArrowType( registry, newParamsNode, newReturnType, newReturnTypeInferred), newTypeOfThis, null, false, false); } /** * Given a constructor or an interface type, get its superclass constructor * or {@code null} if none exists. */ public FunctionType getSuperClassConstructor() { Preconditions.checkArgument(isConstructor() || isInterface()); ObjectType maybeSuperInstanceType = getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return null; } return maybeSuperInstanceType.getConstructor(); } /** * Given an interface and a property, finds the top-most super interface * that has the property defined (including this interface). */ public static ObjectType getTopDefiningInterface(ObjectType type, String propertyName) { ObjectType foundType = null; if (type.hasProperty(propertyName)) { foundType = type; } for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { foundType = getTopDefiningInterface(interfaceType, propertyName); } } return foundType; } /** * Given a constructor or an interface type and a property, finds the * top-most superclass that has the property defined (including this * constructor). */ public ObjectType getTopMostDefiningType(String propertyName) { Preconditions.checkState(isConstructor() || isInterface()); Preconditions.checkArgument(getInstanceType().hasProperty(propertyName)); FunctionType ctor = this; if (isInterface()) { return getTopDefiningInterface(getInstanceType(), propertyName); } ObjectType topInstanceType = null; do { topInstanceType = ctor.getInstanceType(); ctor = ctor.getSuperClassConstructor(); } while (ctor != null && ctor.getPrototype().hasProperty(propertyName)); return topInstanceType; } /** * Two function types are equal if their signatures match. Since they don't * have signatures, two interfaces are equal if their names match. */ boolean checkFunctionEquivalenceHelper( FunctionType that, EquivalenceMethod eqMethod) { if (isConstructor()) { if (that.isConstructor()) { return this == that; } return false; } if (isInterface()) { if (that.isInterface()) { return getReferenceName().equals(that.getReferenceName()); } return false; } if (that.isInterface()) { return false; } return typeOfThis.checkEquivalenceHelper(that.typeOfThis, eqMethod) && call.checkArrowEquivalenceHelper(that.call, eqMethod); } @Override public int hashCode() { return isInterface() ? getReferenceName().hashCode() : call.hashCode(); } public boolean hasEqualCallType(FunctionType otherType) { return this.call.checkArrowEquivalenceHelper( otherType.call, EquivalenceMethod.IDENTITY); } /** * Informally, a function is represented by * {@code function (params): returnType} where the {@code params} is a comma * separated list of types, the first one being a special * {@code this:T} if the function expects a known type for {@code this}. */ @Override String toStringHelper(boolean forAnnotations) { if (!isPrettyPrint() || this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return "Function"; } setPrettyPrint(false); StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !(typeOfThis instanceof UnknownType); if (hasKnownTypeOfThis) { if (isConstructor()) { b.append("new:"); } else { b.append("this:"); } b.append(typeOfThis.toStringHelper(forAnnotations)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); appendArgString(b, p, forAnnotations); p = p.getNext(); while (p != null) { b.append(", "); appendArgString(b, p, forAnnotations); p = p.getNext(); } } b.append("): "); b.append(call.returnType.toStringHelper(forAnnotations)); setPrettyPrint(true); return b.toString(); } private void appendArgString( StringBuilder b, Node p, boolean forAnnotations) { if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType(), forAnnotations); } else if (p.isOptionalArg()) { appendOptionalArgString(b, p.getJSType(), forAnnotations); } else { b.append(p.getJSType().toStringHelper(forAnnotations)); } } /** Gets the string representation of a var args param. */ private void appendVarArgsString(StringBuilder builder, JSType paramType, boolean forAnnotations) { if (paramType.isUnionType()) { // Remove the optionality from the var arg. paramType = paramType.toMaybeUnionType().getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append("...[").append( paramType.toStringHelper(forAnnotations)).append("]"); } /** Gets the string representation of an optional param. */ private void appendOptionalArgString( StringBuilder builder, JSType paramType, boolean forAnnotations) { if (paramType.isUnionType()) { // Remove the optionality from the var arg. paramType = paramType.toMaybeUnionType().getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append(paramType.toStringHelper(forAnnotations)).append("="); } /** * A function is a subtype of another if their call methods are related via * subtyping and {@code this} is a subtype of {@code that} with regard to * the prototype chain. */ @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } if (that.isFunctionType()) { FunctionType other = that.toMaybeFunctionType(); if (other.isInterface()) { // Any function can be assigned to an interface function. return true; } if (isInterface()) { // An interface function cannot be assigned to anything. return false; } // If functionA is a subtype of functionB, then their "this" types // should be contravariant. However, this causes problems because // of the way we enforce overrides. Because function(this:SubFoo) // is not a subtype of function(this:Foo), our override check treats // this as an error. Let's punt on all this for now. // TODO(nicksantos): fix this. boolean treatThisTypesAsCovariant = // An interface 'this'-type is non-restrictive. // In practical terms, if C implements I, and I has a method m, // then any m doesn't necessarily have to C#m's 'this' // type doesn't need to match I. (other.typeOfThis.toObjectType() != null && other.typeOfThis.toObjectType().getConstructor() != null && other.typeOfThis.toObjectType().getConstructor().isInterface()) || // If one of the 'this' types is covariant of the other, // then we'll treat them as covariant (see comment above). other.typeOfThis.isSubtype(this.typeOfThis) || this.typeOfThis.isSubtype(other.typeOfThis); return treatThisTypesAsCovariant && this.call.isSubtype(other.call); } return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that); } @Override public T visit(Visitor visitor) { return visitor.caseFunctionType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseFunctionType(this, that); } /** * Gets the type of instance of this function. * @throws IllegalStateException if this function is not a constructor * (see {@link #isConstructor()}). */ public ObjectType getInstanceType() { Preconditions.checkState(hasInstanceType()); return typeOfThis.toObjectType(); } /** * Sets the instance type. This should only be used for special * native types. */ void setInstanceType(ObjectType instanceType) { typeOfThis = instanceType; } /** * Returns whether this function type has an instance type. */ public boolean hasInstanceType() { return isConstructor() || isInterface(); } /** * Gets the type of {@code this} in this function. */ @Override public JSType getTypeOfThis() { return typeOfThis.isEmptyType() ? registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis; } /** * Gets the source node or null if this is an unknown function. */ public Node getSource() { return source; } /** * Sets the source node. */ public void setSource(Node source) { if (prototypeSlot != null) { // NOTE(bashir): On one hand when source is null we want to drop any // references to old nodes retained in prototypeSlot. On the other hand // we cannot simply drop prototypeSlot, so we retain all information // except the propertyNode for which we use an approximation! These // details mostly matter in hot-swap passes. if (source == null || prototypeSlot.getNode() == null) { prototypeSlot = new Property(prototypeSlot.getName(), prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source); } } this.source = source; } /** Adds a type to the list of subtypes for this type. */ private void addSubType(FunctionType subType) { if (subTypes == null) { subTypes = Lists.newArrayList(); } subTypes.add(subType); } @Override public void clearCachedValues() { super.clearCachedValues(); if (subTypes != null) { for (FunctionType subType : subTypes) { subType.clearCachedValues(); } } if (!isNativeObjectType()) { if (hasInstanceType()) { getInstanceType().clearCachedValues(); } if (prototypeSlot != null) { ((ObjectType) prototypeSlot.getType()).clearCachedValues(); } } } /** * Returns a list of types that are subtypes of this type. This is only valid * for constructor functions, and may be null. This allows a downward * traversal of the subtype graph. */ public List getSubTypes() { return subTypes; } @Override public boolean hasCachedValues() { return prototypeSlot != null || super.hasCachedValues(); } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { setResolvedTypeInternal(this); call = (ArrowType) safeResolve(call, t, scope); if (prototypeSlot != null) { prototypeSlot.setType( safeResolve(prototypeSlot.getType(), t, scope)); } // Warning about typeOfThis if it doesn't resolve to an ObjectType // is handled further upstream. // // TODO(nicksantos): Handle this correctly if we have a UnionType. // // TODO(nicksantos): In ES3, the run-time coerces "null" to the global // activation object. In ES5, it leaves it as null. Just punt on this // issue for now by coercing out null. This is complicated by the // fact that when most people write @this {Foo}, they really don't // mean "nullable Foo". For certain tags (like @extends) we de-nullify // the name for them. JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope); if (maybeTypeOfThis != null) { maybeTypeOfThis = maybeTypeOfThis.restrictByNotNullOrUndefined(); } if (maybeTypeOfThis instanceof ObjectType) { typeOfThis = maybeTypeOfThis; } boolean changed = false; ImmutableList.Builder resolvedInterfaces = ImmutableList.builder(); for (ObjectType iface : implementedInterfaces) { ObjectType resolvedIface = (ObjectType) iface.resolve(t, scope); resolvedInterfaces.add(resolvedIface); changed |= (resolvedIface != iface); } if (changed) { implementedInterfaces = resolvedInterfaces.build(); } if (subTypes != null) { for (int i = 0; i < subTypes.size(); i++) { subTypes.set( i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope))); } } return super.resolveInternal(t, scope); } @Override public String toDebugHashCodeString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return super.toDebugHashCodeString(); } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); } private String getDebugHashCodeStringOf(JSType type) { if (type == this) { return "me"; } else { return type.toDebugHashCodeString(); } } /** Create a new constructor with the parameters and return type stripped. */ public FunctionType cloneWithoutArrowType() { FunctionType result = new FunctionType( registry, getReferenceName(), source, registry.createArrowType(null, null), getInstanceType(), null, true, false); result.setPrototypeBasedOn(getInstanceType()); return result; } @Override public boolean hasAnyTemplateTypesInternal() { return getTemplateTypeMap().numUnfilledTemplateKeys() > 0 || typeOfThis.hasAnyTemplateTypes() || call.hasAnyTemplateTypes(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NoResolvedType.java0000644000175000017500000000527312115204405027572 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * An unresolved type that was forward declared. So we know it exists, * but that it wasn't pulled into this binary. * * In most cases, it behaves like a bottom type in the type lattice: * no real type should be assigned to a NoResolvedType, but the * NoResolvedType is a subtype of everything. In a few cases, it behaves * like the unknown type: properties of this type are also NoResolved types, * and comparisons to other types always have an unknown result. * * @author nicksantos@google.com (Nick Santos) */ class NoResolvedType extends NoType { private static final long serialVersionUID = 1L; NoResolvedType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoResolvedType() { return true; } @Override public boolean isNoType() { return false; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return !that.isNoType(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoResolvedType"; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java0000644000175000017500000003027212115204405030106 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.CHECKED_UNKNOWN_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.NO_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * A builder for union types. * * @author nicksantos@google.com (Nick Santos) */ class UnionTypeBuilder implements Serializable { private static final long serialVersionUID = 1L; // If the best we can do is say "this object is one of twenty things", // then we should just give up and admit that we have no clue. private static final int DEFAULT_MAX_UNION_SIZE = 20; private final JSTypeRegistry registry; private final List alternates = Lists.newArrayList(); private boolean isAllType = false; private boolean isNativeUnknownType = false; private boolean areAllUnknownsChecked = true; private final int maxUnionSize; // Every UnionType may have at most one structural function in it. // // NOTE(nicksantos): I've read some literature that says that type-inferenced // languages are fundamentally incompatible with union types. I refuse // to believe this. But they do make the type lattice much more complicated. // // For this reason, when we deal with function types, we actually merge some // nodes on the lattice, and treat them as fundamentally equivalent. // For example, we treat // function(): string | function(): number // as equivalent to // function(): (string|number) // and normalize the first type into the second type. // // To perform this normalization, we've modified UnionTypeBuilder to disallow // multiple structural functions in a union. We always delegate to // FunctionType::getLeastSupertype, which either merges the functions into // one structural function, or just bails out and uses the top function type. private int functionTypePosition = -1; // Memoize the result, in case build() is called multiple times. private JSType result = null; UnionTypeBuilder(JSTypeRegistry registry) { this(registry, DEFAULT_MAX_UNION_SIZE); } UnionTypeBuilder(JSTypeRegistry registry, int maxUnionSize) { this.registry = registry; this.maxUnionSize = maxUnionSize; } Collection getAlternates() { JSType specialCaseType = reduceAlternatesWithoutUnion(); if (specialCaseType != null) { return ImmutableList.of(specialCaseType); } return Collections.unmodifiableList(alternates); } /** * Adds an alternate to the union type under construction. Returns this * for easy chaining. */ UnionTypeBuilder addAlternate(JSType alternate) { // build() returns the bottom type by default, so we can // just bail out early here. if (alternate.isNoType()) { return this; } isAllType = isAllType || alternate.isAllType(); boolean isAlternateUnknown = alternate instanceof UnknownType; isNativeUnknownType = isNativeUnknownType || isAlternateUnknown; if (isAlternateUnknown) { areAllUnknownsChecked = areAllUnknownsChecked && alternate.isCheckedUnknownType(); } if (!isAllType && !isNativeUnknownType) { if (alternate.isUnionType()) { UnionType union = alternate.toMaybeUnionType(); for (JSType unionAlt : union.getAlternates()) { addAlternate(unionAlt); } } else { if (alternates.size() > maxUnionSize) { return this; } // Function types are special, because they have their // own bizarre sub-lattice. See the comments on // FunctionType#supAndInf helper and above at functionTypePosition. if (alternate.isFunctionType() && functionTypePosition != -1) { // See the comments on functionTypePosition above. FunctionType other = alternates.get(functionTypePosition).toMaybeFunctionType(); FunctionType supremum = alternate.toMaybeFunctionType().supAndInfHelper(other, true); alternates.set(functionTypePosition, supremum); result = null; return this; } // Look through the alternates we've got so far, // and check if any of them are duplicates of // one another. int currentIndex = 0; Iterator it = alternates.iterator(); while (it.hasNext()) { boolean removeCurrent = false; JSType current = it.next(); // Unknown and NoResolved types may just be names that haven't // been resolved yet. So keep these in the union, and just use // equality checking for simple de-duping. if (alternate.isUnknownType() || current.isUnknownType() || alternate.isNoResolvedType() || current.isNoResolvedType() || alternate.hasAnyTemplateTypes() || current.hasAnyTemplateTypes()) { if (alternate.isEquivalentTo(current)) { // Alternate is unnecessary. return this; } } else { // Because "Foo" and "Foo." are roughly equivalent // templatized types, special care is needed when building the // union. For example: // Object is consider a subtype of Object. // but we want to leave "Object" not "Object." when // building the subtype. // if (alternate.isTemplatizedType() || current.isTemplatizedType()) { // Cases: // 1) alternate:Array. and current:Object ==> Object // 2) alternate:Array. and current:Array ==> Array // 3) alternate:Object. and // current:Array ==> Array|Object. // 4) alternate:Object and current:Array. ==> Object // 5) alternate:Array and current:Array. ==> Array // 6) alternate:Array and // current:Object. ==> Array|Object. // 7) alternate:Array. and // current:Array. ==> Array. // 8) alternate:Array. and // current:Array. ==> Array. // 9) alternate:Array. and // current:Object. ==> Object.|Array. if (!current.isTemplatizedType()) { if (alternate.isSubtype(current)) { // case 1, 2 return this; } // case 3: leave current, add alternate } else if (!alternate.isTemplatizedType()) { if (current.isSubtype(alternate)) { // case 4, 5 removeCurrent = true; } // case 6: leave current, add alternate } else { Preconditions.checkState(current.isTemplatizedType() && alternate.isTemplatizedType()); TemplatizedType templatizedAlternate = alternate.toMaybeTemplatizedType(); TemplatizedType templatizedCurrent = current.toMaybeTemplatizedType(); if (templatizedCurrent.wrapsSameRawType(templatizedAlternate)) { if (alternate.getTemplateTypeMap().checkEquivalenceHelper( current.getTemplateTypeMap(), EquivalenceMethod.IDENTITY)) { // case 8 return this; } else { // TODO(johnlenz): should we leave both types? // case 7: add a merged alternate // We currently merge to the templatized types to "unknown" // which is equivalent to the raw type. JSType merged = templatizedCurrent .getReferencedObjTypeInternal(); return addAlternate(merged); } } // case 9: leave current, add alternate } // Otherwise leave both templatized types. } else if (alternate.isSubtype(current)) { // Alternate is unnecessary. return this; } else if (current.isSubtype(alternate)) { // Alternate makes current obsolete removeCurrent = true; } } if (removeCurrent) { it.remove(); if (currentIndex == functionTypePosition) { functionTypePosition = -1; } else if (currentIndex < functionTypePosition) { functionTypePosition--; currentIndex--; } } currentIndex++; } if (alternate.isFunctionType()) { // See the comments on functionTypePosition above. Preconditions.checkState(functionTypePosition == -1); functionTypePosition = alternates.size(); } alternates.add(alternate); result = null; // invalidate the memoized result } } else { result = null; } return this; } /** * Reduce the alternates into a non-union type. * If the alternates can't be accurately represented with a non-union * type, return null. */ private JSType reduceAlternatesWithoutUnion() { if (isAllType) { return registry.getNativeType(ALL_TYPE); } else if (isNativeUnknownType) { if (areAllUnknownsChecked) { return registry.getNativeType(CHECKED_UNKNOWN_TYPE); } else { return registry.getNativeType(UNKNOWN_TYPE); } } else { int size = alternates.size(); if (size > maxUnionSize) { return registry.getNativeType(UNKNOWN_TYPE); } else if (size > 1) { return null; } else if (size == 1) { return alternates.iterator().next(); } else { return registry.getNativeType(NO_TYPE); } } } /** * Creates a union. * @return A UnionType if it has two or more alternates, the * only alternate if it has one and otherwise {@code NO_TYPE}. */ JSType build() { if (result == null) { result = reduceAlternatesWithoutUnion(); if (result == null) { result = new UnionType(registry, getAlternateListCopy()); } } return result; } private Collection getAlternateListCopy() { return ImmutableList.copyOf(alternates); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/RelationshipVisitor.java0000644000175000017500000000551312115204405030666 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A type relationship visitor.

      * * This code will calculate a specific value of type {@code T} from * two types based on its structure. * * @author johnlenz@google.com (John Lenz) */ interface RelationshipVisitor { /** * Unknown type's case. */ T caseUnknownType(JSType thisType, JSType thatType); /** * Bottom type's case. */ T caseNoType(JSType thatType); /** * Bottom Object type's case. */ T caseNoObjectType(JSType thatType); /** * All type's case. */ T caseAllType(JSType thatType); /** * Value type's case. */ T caseValueType(ValueType thisType, JSType thatType); /** * Object type's case. */ T caseObjectType(ObjectType thisType, JSType thatType); /** * Function type's case. */ T caseFunctionType(FunctionType thisType, JSType thatType); /** * Union type's case. */ T caseUnionType(UnionType thisType, JSType thatType); /** * Templatized type's case. */ T caseTemplatizedType(TemplatizedType thisType, JSType thatType); /** * Template type's case. */ T caseTemplateType(TemplateType thisType, JSType thatType); /** * Enum element type's case. */ T caseEnumElementType(EnumElementType typeType, JSType thatType); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/FunctionParamBuilder.java0000644000175000017500000001131312115204405030715 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * A builder for the Rhino Node representing Function parameters. * @author nicksantos@google.com (Nick Santos) */ public class FunctionParamBuilder { private final JSTypeRegistry registry; private final Node root = new Node(Token.PARAM_LIST); public FunctionParamBuilder(JSTypeRegistry registry) { this.registry = registry; } /** * Add parameters of the given type to the end of the param list. * @return False if this is called after optional params are added. */ public boolean addRequiredParams(JSType ...types) { if (hasOptionalOrVarArgs()) { return false; } for (JSType type : types) { newParameter(type); } return true; } /** * Add optional parameters of the given type to the end of the param list. * @param types Types for each optional parameter. The builder will make them * undefine-able. * @return False if this is called after var args are added. */ public boolean addOptionalParams(JSType ...types) { if (hasVarArgs()) { return false; } for (JSType type : types) { newParameter(registry.createOptionalType(type)).setOptionalArg(true); } return true; } /** * Add variable arguments to the end of the parameter list. * @return False if this is called after var args are added. */ public boolean addVarArgs(JSType type) { if (hasVarArgs()) { return false; } // There are two types of variable argument functions: // 1) Programmer-defined var args // 2) Native bottom types that can accept any argument. // For the first one, "undefined" is a valid value for all arguments. // For the second, we do not want to cast it up to undefined. if (!type.isEmptyType()) { type = registry.createOptionalType(type); } newParameter(type).setVarArgs(true); return true; } /** * Copies the parameter specification from the given node. */ public Node newParameterFromNode(Node n) { Node newParam = newParameter(n.getJSType()); newParam.setVarArgs(n.isVarArgs()); newParam.setOptionalArg(n.isOptionalArg()); return newParam; } /** * Copies the parameter specification from the given node, * but makes sure it's optional. */ public Node newOptionalParameterFromNode(Node n) { Node newParam = newParameterFromNode(n); if (!newParam.isVarArgs() && !newParam.isOptionalArg()) { newParam.setOptionalArg(true); } return newParam; } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, ""); paramNode.setJSType(type); root.addChildToBack(paramNode); return paramNode; } public Node build() { return root; } private boolean hasOptionalOrVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && (lastChild.isOptionalArg() || lastChild.isVarArgs()); } public boolean hasVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && lastChild.isVarArgs(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/SimpleSlot.java0000644000175000017500000000471312115204405026741 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.JSDocInfo; import java.io.Serializable; /** * The minimum implementation of StaticSlot. * * @author nicksantos@google.com (Nick Santos) */ public class SimpleSlot implements StaticSlot, Serializable { private static final long serialVersionUID = 1L; final String name; final JSType type; final boolean inferred; public SimpleSlot(String name, JSType type, boolean inferred) { this.name = name; this.type = type; this.inferred = inferred; } @Override public String getName() { return name; } @Override public JSType getType() { return type; } @Override public boolean isTypeInferred() { return inferred; } @Override public StaticReference getDeclaration() { return null; } @Override public JSDocInfo getJSDocInfo() { return null; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/RecordTypeBuilder.java0000644000175000017500000000701212115204405030230 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.Maps; import com.google.javascript.rhino.Node; import java.util.Collections; import java.util.HashMap; /** * A builder for record types. * */ public class RecordTypeBuilder { private boolean isEmpty = true; private boolean isDeclared = true; private final JSTypeRegistry registry; private final HashMap properties = Maps.newHashMap(); public RecordTypeBuilder(JSTypeRegistry registry) { this.registry = registry; } /** See the comments on RecordType about synthetic types. */ void setSynthesized(boolean synthesized) { isDeclared = !synthesized; } /** * Adds a property with the given name and type to the record type. * @param name the name of the new property * @param type the JSType of the new property * @param propertyNode the node that holds this property definition * @return The builder itself for chaining purposes, or null if there's * a duplicate. */ public RecordTypeBuilder addProperty(String name, JSType type, Node propertyNode) { isEmpty = false; if (properties.containsKey(name)) { return null; } properties.put(name, new RecordProperty(type, propertyNode)); return this; } /** * Creates a record. * @return The record type. */ public JSType build() { // If we have an empty record, simply return the object type. if (isEmpty) { return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } return new RecordType( registry, Collections.unmodifiableMap(properties), isDeclared); } static class RecordProperty { private final JSType type; private final Node propertyNode; RecordProperty(JSType type, Node propertyNode) { this.type = type; this.propertyNode = propertyNode; } public JSType getType() { return type; } public Node getPropertyNode() { return propertyNode; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StaticSymbolTable.java0000644000175000017500000000413112115204405030225 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * Lookup references by the symbols that they refer to. * * @author nicksantos@google.com (Nick Santos) */ public interface StaticSymbolTable , R extends StaticReference> { /** * Returns the references that point to the given symbol. */ Iterable getReferences(S symbol); /** * Returns the scope for a given symbol. */ StaticScope getScope(S symbol); /** * Returns all variables in this symbol table. */ Iterable getAllSymbols(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/EnumType.java0000644000175000017500000001247512115204405026420 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * An enum type representing a branded collection of elements. Each element * is referenced by its name, and has an {@link EnumElementType} type. */ public class EnumType extends PrototypeObjectType { private static final long serialVersionUID = 1L; /** * The object literal or alias which this type represents. * It may be {@code null}. */ private final Node source; // the type of the individual elements private EnumElementType elementsType; // the elements' names (they all have the same type) private final Set elements = new HashSet(); /** * Creates an enum type. * * @param name the enum's name * @param elementsType the base type of the individual elements */ EnumType(JSTypeRegistry registry, String name, Node source, JSType elementsType) { super(registry, "enum{" + name + "}", null); this.source = source; this.elementsType = new EnumElementType(registry, elementsType, name); } /** * Gets the source node or null if this is an unknown enum. */ public Node getSource() { return source; } @Override public EnumType toMaybeEnumType() { return this; } @Override public ObjectType getImplicitPrototype() { return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } /** * Gets the elements defined on this enum. * @return the elements' names defined on this enum. The returned set is * immutable. */ public Set getElements() { return Collections.unmodifiableSet(elements); } /** * Defines a new element on this enum. * @param name the name of the new element * @param definingNode the {@code Node} that defines this new element * @return true iff the new element is added successfully */ public boolean defineElement(String name, Node definingNode) { elements.add(name); return defineDeclaredProperty(name, elementsType, definingNode); } /** * Gets the elements' type. */ public EnumElementType getElementsType() { return elementsType; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } return this.isEquivalentTo(that) ? TRUE : FALSE; } @Override public boolean isSubtype(JSType that) { return that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_TYPE)) || that.isEquivalentTo(getNativeType(JSTypeNative.OBJECT_PROTOTYPE)) || JSType.isSubtypeHelper(this, that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "Object" : getReferenceName(); } @Override public String getDisplayName() { return elementsType.getDisplayName(); } @Override public T visit(Visitor visitor) { return visitor.caseObjectType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseObjectType(this, that); } @Override public FunctionType getConstructor() { return null; } @Override public boolean matchesNumberContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { elementsType = (EnumElementType) elementsType.resolve(t, scope); return super.resolveInternal(t, scope); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/JSTypeNative.java0000644000175000017500000000776312115204405027203 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * Constants corresponding to types that are built into a JavaScript engine * and other types that occur very often in the type system. See * {@link com.google.javascript.rhino.jstype.JSTypeRegistry#getNativeType(JSTypeNative)}. */ public enum JSTypeNative { // Built-in types (please keep alphabetized) ARRAY_TYPE, ARRAY_FUNCTION_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_FUNCTION_TYPE, /** * A checked unknown type is a type that we know something about, * but we're not really sure what we know about it. * * Examples of checked unknown types include: * * if (x) { // x is unknown * alert(x); // x is checked unknown * } * * * * /* @param {SomeForwardDeclaredType} x / * function f(x) { * // x is checked unknown. We know it's some type, but the type * // has not been included in this binary. * } * * * This is useful for missing property warnings, where we don't * want to emit warnings on things that have been checked. */ CHECKED_UNKNOWN_TYPE, DATE_TYPE, DATE_FUNCTION_TYPE, ERROR_FUNCTION_TYPE, ERROR_TYPE, EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_TYPE, FUNCTION_FUNCTION_TYPE, FUNCTION_INSTANCE_TYPE, // equivalent to U2U_CONSTRUCTOR_TYPE FUNCTION_PROTOTYPE, NULL_TYPE, NUMBER_TYPE, NUMBER_OBJECT_TYPE, NUMBER_OBJECT_FUNCTION_TYPE, OBJECT_TYPE, OBJECT_FUNCTION_TYPE, OBJECT_PROTOTYPE, RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_TYPE, REFERENCE_ERROR_FUNCTION_TYPE, REFERENCE_ERROR_TYPE, REGEXP_TYPE, REGEXP_FUNCTION_TYPE, STRING_OBJECT_TYPE, STRING_OBJECT_FUNCTION_TYPE, STRING_TYPE, SYNTAX_ERROR_FUNCTION_TYPE, SYNTAX_ERROR_TYPE, TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_TYPE, UNKNOWN_TYPE, URI_ERROR_FUNCTION_TYPE, URI_ERROR_TYPE, VOID_TYPE, // Commonly used types TOP_LEVEL_PROTOTYPE, STRING_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE, ALL_TYPE, NO_TYPE, NO_OBJECT_TYPE, NO_RESOLVED_TYPE, GLOBAL_THIS, U2U_CONSTRUCTOR_TYPE, U2U_FUNCTION_TYPE, LEAST_FUNCTION_TYPE, GREATEST_FUNCTION_TYPE, /** * (null, void) */ NULL_VOID, /** * (Object,number,string) */ OBJECT_NUMBER_STRING, /** * (Object,number,string,boolean) */ OBJECT_NUMBER_STRING_BOOLEAN, /** * (number,string,boolean) */ NUMBER_STRING_BOOLEAN, /** * (number,string) */ NUMBER_STRING, } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/VoidType.java0000644000175000017500000000613112115204405026405 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Void type whose only element is the {@code undefined} value. */ public class VoidType extends ValueType { private static final long serialVersionUID = 1L; VoidType(JSTypeRegistry registry) { super(registry); } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { if (UNKNOWN.equals(super.testForEquality(that))) { return UNKNOWN; } if (that.isSubtype(this) || that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) { return TRUE; } return FALSE; } @Override public boolean matchesNumberContext() { return false; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public boolean isVoidType() { return true; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "undefined"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public T visit(Visitor visitor) { return visitor.caseVoidType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/AllType.java0000644000175000017500000000572112115204405026220 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.javascript.rhino.ErrorReporter; /** * All type, representing all values. */ public final class AllType extends JSType { private static final long serialVersionUID = 1L; AllType(JSTypeRegistry registry) { super(registry); } @Override public boolean isAllType() { return true; } @Override public boolean matchesStringContext() { // Be lenient. return true; } @Override public boolean matchesObjectContext() { // Be lenient. return true; } @Override public boolean canBeCalled() { return false; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override String toStringHelper(boolean forAnnotations) { return "*"; } @Override public String getDisplayName() { return ""; } @Override public boolean hasDisplayName() { return true; } @Override public T visit(Visitor visitor) { return visitor.caseAllType(); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseAllType(that); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { return this; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/JSType.java0000644000175000017500000013106012115204405026020 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.common.base.Predicate; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.jstype.JSTypeRegistry.ResolveMode; import java.io.Serializable; import java.util.Comparator; /** * Represents JavaScript value types.

      * * Types are split into two separate families: value types and object types. * * A special {@link UnknownType} exists to represent a wildcard type on which * no information can be gathered. In particular, it can assign to everyone, * is a subtype of everyone (and everyone is a subtype of it).

      * * If you remove the {@link UnknownType}, the set of types in the type system * forms a lattice with the {@link #isSubtype} relation defining the partial * order of types. All types are united at the top of the lattice by the * {@link AllType} and at the bottom by the {@link NoType}.

      * */ public abstract class JSType implements Serializable { private static final long serialVersionUID = 1L; private boolean resolved = false; private JSType resolveResult = null; protected final TemplateTypeMap templateTypeMap; private boolean inTemplatedCheckVisit = false; private static final CanCastToVisitor CAN_CAST_TO_VISITOR = new CanCastToVisitor(); public static final String UNKNOWN_NAME = "Unknown class name"; public static final String NOT_A_CLASS = "Not declared as a constructor"; public static final String NOT_A_TYPE = "Not declared as a type name"; public static final String EMPTY_TYPE_COMPONENT = "Named type with empty name component"; /** * Total ordering on types based on their textual representation. * This is used to have a deterministic output of the toString * method of the union type since this output is used in tests. */ static final Comparator ALPHA = new Comparator() { @Override public int compare(JSType t1, JSType t2) { return t1.toString().compareTo(t2.toString()); } }; // A flag set on enum definition tree nodes public static final int ENUMDECL = 1; public static final int NOT_ENUMDECL = 0; final JSTypeRegistry registry; JSType(JSTypeRegistry registry) { this(registry, null); } JSType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) { this.registry = registry; this.templateTypeMap = templateTypeMap == null ? registry.createTemplateTypeMap(null, null) : templateTypeMap; } /** * Utility method for less verbose code. */ JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } /** * Gets the docInfo for this type. By default, documentation cannot be * attached to arbitrary types. This must be overridden for * programmer-defined types. */ public JSDocInfo getJSDocInfo() { return null; } /** * Returns a user meaningful label for the JSType instance. For example, * Functions and Enums will return their declaration name (if they have one). * Some types will not have a meaningful display name. Calls to * hasDisplayName() will return true IFF getDisplayName() will return null * or a zero length string. * * @return the display name of the type, or null if one is not available */ public String getDisplayName() { return null; } /** * @return true if the JSType has a user meaningful label. */ public boolean hasDisplayName() { String displayName = getDisplayName(); return displayName != null && !displayName.isEmpty(); } /** * Checks whether the property is present on the object. * @param pname The property name. */ public boolean hasProperty(String pname) { return false; } public boolean isNoType() { return false; } public boolean isNoResolvedType() { return false; } public boolean isNoObjectType() { return false; } public final boolean isEmptyType() { return isNoType() || isNoObjectType() || isNoResolvedType() || (registry.getNativeFunctionType( JSTypeNative.LEAST_FUNCTION_TYPE) == this); } public boolean isNumberObjectType() { return false; } public boolean isNumberValueType() { return false; } /** Whether this is the prototype of a function. */ public boolean isFunctionPrototypeType() { return false; } public boolean isStringObjectType() { return false; } boolean isTheObjectType() { return false; } public boolean isStringValueType() { return false; } /** * Tests whether the type is a string (value or Object). * @return {@code this <: (String, string)} */ public final boolean isString() { return isSubtype( getNativeType(JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE)); } /** * Tests whether the type is a number (value or Object). * @return {@code this <: (Number, number)} */ public final boolean isNumber() { return isSubtype( getNativeType(JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE)); } public boolean isArrayType() { return false; } public boolean isBooleanObjectType() { return false; } public boolean isBooleanValueType() { return false; } public boolean isRegexpType() { return false; } public boolean isDateType() { return false; } public boolean isNullType() { return false; } public boolean isVoidType() { return false; } public boolean isAllType() { return false; } public boolean isUnknownType() { return false; } public boolean isCheckedUnknownType() { return false; } public final boolean isUnionType() { return toMaybeUnionType() != null; } /** * Returns true iff {@code this} can be a {@code struct}. * UnionType overrides the method, assume {@code this} is not a union here. */ public boolean isStruct() { if (isObject()) { ObjectType objType = toObjectType(); ObjectType iproto = objType.getImplicitPrototype(); // For the case when a @struct constructor is assigned to a function's // prototype property if (iproto != null && iproto.isStruct()) { return true; } FunctionType ctor = objType.getConstructor(); // This test is true for object literals if (ctor == null) { JSDocInfo info = objType.getJSDocInfo(); return info != null && info.makesStructs(); } else { return ctor.makesStructs(); } } return false; } /** * Returns true iff {@code this} can be a {@code dict}. * UnionType overrides the method, assume {@code this} is not a union here. */ public boolean isDict() { if (isObject()) { ObjectType objType = toObjectType(); ObjectType iproto = objType.getImplicitPrototype(); // For the case when a @dict constructor is assigned to a function's // prototype property if (iproto != null && iproto.isDict()) { return true; } FunctionType ctor = objType.getConstructor(); // This test is true for object literals if (ctor == null) { JSDocInfo info = objType.getJSDocInfo(); return info != null && info.makesDicts(); } else { return ctor.makesDicts(); } } return false; } /** * Downcasts this to a UnionType, or returns null if this is not a UnionType. * * Named in honor of Haskell's Maybe type constructor. */ public UnionType toMaybeUnionType() { return null; } /** Returns true if this is a global this type. */ public final boolean isGlobalThisType() { return this == registry.getNativeType(JSTypeNative.GLOBAL_THIS); } /** Returns true if toMaybeFunctionType returns a non-null FunctionType. */ public final boolean isFunctionType() { return toMaybeFunctionType() != null; } /** * Downcasts this to a FunctionType, or returns null if this is not * a function. * * For the purposes of this function, we define a MaybeFunctionType as any * type in the sub-lattice * { x | LEAST_FUNCTION_TYPE <= x <= GREATEST_FUNCTION_TYPE } * This definition excludes bottom types like NoType and NoObjectType. * * This definition is somewhat arbitrary and axiomatic, but this is the * definition that makes the most sense for the most callers. */ public FunctionType toMaybeFunctionType() { return null; } /** * Null-safe version of toMaybeFunctionType(). */ public static FunctionType toMaybeFunctionType(JSType type) { return type == null ? null : type.toMaybeFunctionType(); } public final boolean isEnumElementType() { return toMaybeEnumElementType() != null; } /** * Downcasts this to an EnumElementType, or returns null if this is not an EnumElementType. */ public EnumElementType toMaybeEnumElementType() { return null; } public boolean isEnumType() { return toMaybeEnumType() != null; } /** * Downcasts this to an EnumType, or returns null if this is not an EnumType. */ public EnumType toMaybeEnumType() { return null; } boolean isNamedType() { return false; } public boolean isRecordType() { return toMaybeRecordType() != null; } /** * Downcasts this to a RecordType, or returns null if this is not * a RecordType. */ RecordType toMaybeRecordType() { return null; } public final boolean isTemplatizedType() { return toMaybeTemplatizedType() != null; } /** * Downcasts this to a TemplatizedType, or returns null if this is not * a function. */ public TemplatizedType toMaybeTemplatizedType() { return null; } /** * Null-safe version of toMaybeTemplatizedType(). */ public static TemplatizedType toMaybeTemplatizedType(JSType type) { return type == null ? null : type.toMaybeTemplatizedType(); } public final boolean isTemplateType() { return toMaybeTemplateType() != null; } /** * Downcasts this to a TemplateType, or returns null if this is not * a function. */ public TemplateType toMaybeTemplateType() { return null; } /** * Null-safe version of toMaybeTemplateType(). */ public static TemplateType toMaybeTemplateType(JSType type) { return type == null ? null : type.toMaybeTemplateType(); } public boolean hasAnyTemplateTypes() { if (!this.inTemplatedCheckVisit) { this.inTemplatedCheckVisit = true; boolean result = hasAnyTemplateTypesInternal(); this.inTemplatedCheckVisit = false; return result; } else { // prevent infinite recursion, this is "not yet". return false; } } boolean hasAnyTemplateTypesInternal() { return templateTypeMap.hasAnyTemplateTypesInternal(); } /** * Returns the template type map associated with this type. */ public TemplateTypeMap getTemplateTypeMap() { return templateTypeMap; } /** * Tests whether this type is an {@code Object}, or any subtype thereof. * @return {@code this <: Object} */ public boolean isObject() { return false; } /** * Whether this type is a {@link FunctionType} that is a constructor or a * named type that points to such a type. */ public boolean isConstructor() { return false; } /** * Whether this type is a nominal type (a named instance object or * a named enum). */ public boolean isNominalType() { return false; } /** * Whether this type is the original constructor of a nominal type. * Does not include structural constructors. */ public final boolean isNominalConstructor() { if (isConstructor() || isInterface()) { FunctionType fn = toMaybeFunctionType(); if (fn == null) { return false; } // Programmer-defined constructors will have a link // back to the original function in the source tree. // Structural constructors will not. if (fn.getSource() != null) { return true; } // Native constructors are always nominal. return fn.isNativeObjectType(); } return false; } /** * Whether this type is an Instance object of some constructor. * Does not necessarily mean this is an {@link InstanceObjectType}. */ public boolean isInstanceType() { return false; } /** * Whether this type is a {@link FunctionType} that is an interface or a named * type that points to such a type. */ public boolean isInterface() { return false; } /** * Whether this type is a {@link FunctionType} that is an ordinary function or * a named type that points to such a type. */ public boolean isOrdinaryFunction() { return false; } /** * Checks if two types are equivalent. */ public final boolean isEquivalentTo(JSType that) { return checkEquivalenceHelper(that, EquivalenceMethod.IDENTITY); } /** * Checks if two types are invariant. * @see EquivalenceMethod */ public final boolean isInvariant(JSType that) { return checkEquivalenceHelper(that, EquivalenceMethod.INVARIANT); } /** * Whether this type is meaningfully different from {@code that} type for * the purposes of data flow analysis. * * This is a trickier check than pure equality, because it has to properly * handle unknown types. See {@code EquivalenceMethod} for more info. * * @see Unknown unknowns */ public final boolean differsFrom(JSType that) { return !checkEquivalenceHelper(that, EquivalenceMethod.DATA_FLOW); } /** * An equivalence visitor. */ boolean checkEquivalenceHelper(JSType that, EquivalenceMethod eqMethod) { if (this == that) { return true; } boolean thisUnknown = isUnknownType(); boolean thatUnknown = that.isUnknownType(); if (thisUnknown || thatUnknown) { if (eqMethod == EquivalenceMethod.INVARIANT) { // If we're checking for invariance, the unknown type is invariant // with everyone. return true; } else if (eqMethod == EquivalenceMethod.DATA_FLOW) { // If we're checking data flow, then two types are the same if they're // both unknown. return thisUnknown && thatUnknown; } else if (thisUnknown && thatUnknown && (isNominalType() ^ that.isNominalType())) { // If they're both unknown, but one is a nominal type and the other // is not, then we should fail out immediately. This ensures that // we won't unbox the unknowns further down. return false; } } if (isUnionType() && that.isUnionType()) { return toMaybeUnionType().checkUnionEquivalenceHelper( that.toMaybeUnionType(), eqMethod); } if (isFunctionType() && that.isFunctionType()) { return toMaybeFunctionType().checkFunctionEquivalenceHelper( that.toMaybeFunctionType(), eqMethod); } if (isRecordType() && that.isRecordType()) { return toMaybeRecordType().checkRecordEquivalenceHelper( that.toMaybeRecordType(), eqMethod); } if (!getTemplateTypeMap().checkEquivalenceHelper( that.getTemplateTypeMap(), eqMethod)) { return false; } if (isNominalType() && that.isNominalType()) { return toObjectType().getReferenceName().equals( that.toObjectType().getReferenceName()); } // Unbox other proxies. if (this instanceof ProxyObjectType) { return ((ProxyObjectType) this) .getReferencedTypeInternal().checkEquivalenceHelper( that, eqMethod); } if (that instanceof ProxyObjectType) { return checkEquivalenceHelper( ((ProxyObjectType) that).getReferencedTypeInternal(), eqMethod); } // Relies on the fact that for the base {@link JSType}, only one // instance of each sub-type will ever be created in a given registry, so // there is no need to verify members. If the object pointers are not // identical, then the type member must be different. return this == that; } public static boolean isEquivalent(JSType typeA, JSType typeB) { return (typeA == null || typeB == null) ? typeA == typeB : typeA.isEquivalentTo(typeB); } @Override public boolean equals(Object jsType) { return (jsType instanceof JSType) ? isEquivalentTo((JSType) jsType) : false; } @Override public int hashCode() { return System.identityHashCode(this); } /** * This predicate is used to test whether a given type can appear in a * 'Int32' context. This context includes, for example, the operands of a * bitwise or operator. Since we do not currently support integer types, * this is a synonym for {@code Number}. */ public final boolean matchesInt32Context() { return matchesNumberContext(); } /** * This predicate is used to test whether a given type can appear in a * 'Uint32' context. This context includes the right-hand operand of a shift * operator. */ public final boolean matchesUint32Context() { return matchesNumberContext(); } /** * This predicate is used to test whether a given type can appear in a * numeric context, such as an operand of a multiply operator. */ public boolean matchesNumberContext() { return false; } /** * This predicate is used to test whether a given type can appear in a * {@code String} context, such as an operand of a string concat (+) operator. * * All types have at least the potential for converting to {@code String}. * When we add externally defined types, such as a browser OM, we may choose * to add types that do not automatically convert to {@code String}. */ public boolean matchesStringContext() { return false; } /** * This predicate is used to test whether a given type can appear in an * {@code Object} context, such as the expression in a with statement. * * Most types we will encounter, except notably {@code null}, have at least * the potential for converting to {@code Object}. Host defined objects can * get peculiar. */ public boolean matchesObjectContext() { return false; } /** * Coerces this type to an Object type, then gets the type of the property * whose name is given. * * Unlike {@link ObjectType#getPropertyType}, returns null if the property * is not found. * * @return The property's type. {@code null} if the current type cannot * have properties, or if the type is not found. */ public JSType findPropertyType(String propertyName) { ObjectType autoboxObjType = ObjectType.cast(autoboxesTo()); if (autoboxObjType != null) { return autoboxObjType.findPropertyType(propertyName); } return null; } /** * This predicate is used to test whether a given type can be used as the * 'function' in a function call. * * @return {@code true} if this type might be callable. */ public boolean canBeCalled() { return false; } /** * Tests whether values of {@code this} type can be safely assigned * to values of {@code that} type.

      * * The default implementation verifies that {@code this} is a subtype * of {@code that}.

      */ public boolean canCastTo(JSType that) { return this.visit(CAN_CAST_TO_VISITOR, that); } /** * Turn a scalar type to the corresponding object type. * * @return the auto-boxed type or {@code null} if this type is not a scalar. */ public JSType autoboxesTo() { return null; } /** * Turn an object type to its corresponding scalar type. * * @return the unboxed type or {@code null} if this type does not unbox. */ public JSType unboxesTo() { return null; } /** * Casts this to an ObjectType, or returns null if this is not an ObjectType. * If this is a scalar type, it will *not* be converted to an object type. * If you want to simulate JS autoboxing or dereferencing, you should use * autoboxesTo() or dereference(). */ public ObjectType toObjectType() { return this instanceof ObjectType ? (ObjectType) this : null; } /** * Dereference a type for property access. * * Filters null/undefined and autoboxes the resulting type. * Never returns null. */ public JSType autobox() { JSType restricted = restrictByNotNullOrUndefined(); JSType autobox = restricted.autoboxesTo(); return autobox == null ? restricted : autobox; } /** * Dereference a type for property access. * * Filters null/undefined, autoboxes the resulting type, and returns it * iff it's an object. */ public final ObjectType dereference() { return autobox().toObjectType(); } /** * Tests whether {@code this} and {@code that} are meaningfully * comparable. By meaningfully, we mean compatible types that do not lead * to step 22 of the definition of the Abstract Equality Comparison * Algorithm (11.9.3, page 55–56) of the ECMA-262 specification.

      */ public final boolean canTestForEqualityWith(JSType that) { return testForEquality(that).equals(UNKNOWN); } /** * Compares {@code this} and {@code that}. * @return

        *
      • {@link TernaryValue#TRUE} if the comparison of values of * {@code this} type and {@code that} always succeed (such as * {@code undefined} compared to {@code null})
      • *
      • {@link TernaryValue#FALSE} if the comparison of values of * {@code this} type and {@code that} always fails (such as * {@code undefined} compared to {@code number})
      • *
      • {@link TernaryValue#UNKNOWN} if the comparison can succeed or * fail depending on the concrete values
      • *
      */ public TernaryValue testForEquality(JSType that) { return testForEqualityHelper(this, that); } TernaryValue testForEqualityHelper(JSType aType, JSType bType) { if (bType.isAllType() || bType.isUnknownType() || bType.isNoResolvedType() || aType.isAllType() || aType.isUnknownType() || aType.isNoResolvedType()) { return UNKNOWN; } boolean aIsEmpty = aType.isEmptyType(); boolean bIsEmpty = bType.isEmptyType(); if (aIsEmpty || bIsEmpty) { if (aIsEmpty && bIsEmpty) { return TernaryValue.TRUE; } else { return UNKNOWN; } } if (aType.isFunctionType() || bType.isFunctionType()) { JSType otherType = aType.isFunctionType() ? bType : aType; // In theory, functions are comparable to anything except // null/undefined. For example, on FF3: // function() {} == 'function () {\n}' // In practice, how a function serializes to a string is // implementation-dependent, so it does not really make sense to test // for equality with a string. JSType meet = otherType.getGreatestSubtype( getNativeType(JSTypeNative.OBJECT_TYPE)); if (meet.isNoType() || meet.isNoObjectType()) { return TernaryValue.FALSE; } else { return TernaryValue.UNKNOWN; } } if (bType.isEnumElementType() || bType.isUnionType()) { return bType.testForEquality(aType); } return null; } /** * Tests whether {@code this} and {@code that} are meaningfully * comparable using shallow comparison. By meaningfully, we mean compatible * types that are not rejected by step 1 of the definition of the Strict * Equality Comparison Algorithm (11.9.6, page 56–57) of the * ECMA-262 specification.

      */ public final boolean canTestForShallowEqualityWith(JSType that) { if (isEmptyType() || that.isEmptyType()) { return isSubtype(that) || that.isSubtype(this); } JSType inf = getGreatestSubtype(that); return !inf.isEmptyType() || // Our getGreatestSubtype relation on functions is pretty bad. // Let's just say it's always ok to compare two functions. // Once the TODO in FunctionType is fixed, we should be able to // remove this. inf == registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE); } /** * Tests whether this type is nullable. */ public boolean isNullable() { return isSubtype(getNativeType(JSTypeNative.NULL_TYPE)); } /** * Gets the least supertype of this that's not a union. */ public JSType collapseUnion() { return this; } /** * Gets the least supertype of {@code this} and {@code that}. * The least supertype is the join (∨) or supremum of both types in the * type lattice.

      * Examples: *

        *
      • {@code number ∨ *} = {@code *}
      • *
      • {@code number ∨ Object} = {@code (number, Object)}
      • *
      • {@code Number ∨ Object} = {@code Object}
      • *
      * @return {@code this ∨ that} */ public JSType getLeastSupertype(JSType that) { if (that.isUnionType()) { // Union types have their own implementation of getLeastSupertype. return that.toMaybeUnionType().getLeastSupertype(this); } return getLeastSupertype(this, that); } /** * A generic implementation meant to be used as a helper for common * getLeastSupertype implementations. */ static JSType getLeastSupertype(JSType thisType, JSType thatType) { boolean areEquivalent = thisType.isEquivalentTo(thatType); return areEquivalent ? thisType : filterNoResolvedType( thisType.registry.createUnionType(thisType, thatType)); } /** * Gets the greatest subtype of {@code this} and {@code that}. * The greatest subtype is the meet (∧) or infimum of both types in the * type lattice.

      * Examples *

        *
      • {@code Number ∧ Any} = {@code Any}
      • *
      • {@code number ∧ Object} = {@code Any}
      • *
      • {@code Number ∧ Object} = {@code Number}
      • *
      * @return {@code this ∨ that} */ public JSType getGreatestSubtype(JSType that) { return getGreatestSubtype(this, that); } /** * A generic implementation meant to be used as a helper for common * getGreatestSubtype implementations. */ static JSType getGreatestSubtype(JSType thisType, JSType thatType) { if (thisType.isFunctionType() && thatType.isFunctionType()) { // The FunctionType sub-lattice is not well-defined. i.e., the // proposition // A < B => sup(A, B) == B // does not hold because of unknown parameters and return types. // See the comment in supAndInfHelper for more info on this. return thisType.toMaybeFunctionType().supAndInfHelper( thatType.toMaybeFunctionType(), false); } else if (thisType.isEquivalentTo(thatType)) { return thisType; } else if (thisType.isUnknownType() || thatType.isUnknownType()) { // The greatest subtype with any unknown type is the universal // unknown type, unless the two types are equal. return thisType.isEquivalentTo(thatType) ? thisType : thisType.getNativeType(JSTypeNative.UNKNOWN_TYPE); } else if (thisType.isUnionType()) { return thisType.toMaybeUnionType().meet(thatType); } else if (thatType.isUnionType()) { return thatType.toMaybeUnionType().meet(thisType); } else if (thisType.isTemplatizedType()) { return thisType.toMaybeTemplatizedType().getGreatestSubtypeHelper( thatType); } else if (thatType.isTemplatizedType()) { return thatType.toMaybeTemplatizedType().getGreatestSubtypeHelper( thisType); } else if (thisType.isSubtype(thatType)) { return filterNoResolvedType(thisType); } else if (thatType.isSubtype(thisType)) { return filterNoResolvedType(thatType); } else if (thisType.isRecordType()) { return thisType.toMaybeRecordType().getGreatestSubtypeHelper(thatType); } else if (thatType.isRecordType()) { return thatType.toMaybeRecordType().getGreatestSubtypeHelper(thisType); } if (thisType.isEnumElementType()) { JSType inf = thisType.toMaybeEnumElementType().meet(thatType); if (inf != null) { return inf; } } else if (thatType.isEnumElementType()) { JSType inf = thatType.toMaybeEnumElementType().meet(thisType); if (inf != null) { return inf; } } if (thisType.isObject() && thatType.isObject()) { return thisType.getNativeType(JSTypeNative.NO_OBJECT_TYPE); } return thisType.getNativeType(JSTypeNative.NO_TYPE); } /** * When computing infima, we may get a situation like * inf(Type1, Type2) * where both types are unresolved, so they're technically * subtypes of one another. * * If this happens, filter them down to NoResolvedType. */ static JSType filterNoResolvedType(JSType type) { if (type.isNoResolvedType()) { // inf(UnresolvedType1, UnresolvedType2) needs to resolve // to the base unresolved type, so that the relation is symmetric. return type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE); } else if (type.isUnionType()) { UnionType unionType = type.toMaybeUnionType(); boolean needsFiltering = false; for (JSType alt : unionType.getAlternates()) { if (alt.isNoResolvedType()) { needsFiltering = true; break; } } if (needsFiltering) { UnionTypeBuilder builder = new UnionTypeBuilder(type.registry); builder.addAlternate(type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE)); for (JSType alt : unionType.getAlternates()) { if (!alt.isNoResolvedType()) { builder.addAlternate(alt); } } return builder.build(); } } return type; } /** * Computes the restricted type of this type knowing that the * {@code ToBoolean} predicate has a specific value. For more information * about the {@code ToBoolean} predicate, see * {@link #getPossibleToBooleanOutcomes}. * * @param outcome the value of the {@code ToBoolean} predicate * * @return the restricted type, or the Any Type if the underlying type could * not have yielded this ToBoolean value * * TODO(user): Move this method to the SemanticRAI and use the visit * method of types to get the restricted type. */ public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } BooleanLiteralSet literals = getPossibleToBooleanOutcomes(); if (literals.contains(outcome)) { return this; } else { return getNativeType(JSTypeNative.NO_TYPE); } } /** * Computes the set of possible outcomes of the {@code ToBoolean} predicate * for this type. The {@code ToBoolean} predicate is defined by the ECMA-262 * standard, 3rd edition. Its behavior for simple types can be * summarized by the following table: * * * * * * * * *
      typeresult
      {@code undefined}{false}
      {@code null}{false}
      {@code boolean}{true, false}
      {@code number}{true, false}
      {@code string}{true, false}
      {@code Object}{true}
      * @return the set of boolean literals for this type */ public abstract BooleanLiteralSet getPossibleToBooleanOutcomes(); /** * Computes the subset of {@code this} and {@code that} types if equality * is observed. If a value {@code v1} of type {@code null} is equal to a value * {@code v2} of type {@code (undefined,number)}, we can infer that the * type of {@code v1} is {@code null} and the type of {@code v2} is * {@code undefined}. * * @return a pair containing the restricted type of {@code this} as the first * component and the restricted type of {@code that} as the second * element. The returned pair is never {@code null} even though its * components may be {@code null} */ public TypePair getTypesUnderEquality(JSType that) { // unions types if (that.isUnionType()) { TypePair p = that.toMaybeUnionType().getTypesUnderEquality(this); return new TypePair(p.typeB, p.typeA); } // other types switch (testForEquality(that)) { case FALSE: return new TypePair(null, null); case TRUE: case UNKNOWN: return new TypePair(this, that); } // switch case is exhaustive throw new IllegalStateException(); } /** * Computes the subset of {@code this} and {@code that} types if inequality * is observed. If a value {@code v1} of type {@code number} is not equal to a * value {@code v2} of type {@code (undefined,number)}, we can infer that the * type of {@code v1} is {@code number} and the type of {@code v2} is * {@code number} as well. * * @return a pair containing the restricted type of {@code this} as the first * component and the restricted type of {@code that} as the second * element. The returned pair is never {@code null} even though its * components may be {@code null} */ public TypePair getTypesUnderInequality(JSType that) { // unions types if (that.isUnionType()) { TypePair p = that.toMaybeUnionType().getTypesUnderInequality(this); return new TypePair(p.typeB, p.typeA); } // other types switch (testForEquality(that)) { case TRUE: JSType noType = getNativeType(JSTypeNative.NO_TYPE); return new TypePair(noType, noType); case FALSE: case UNKNOWN: return new TypePair(this, that); } // switch case is exhaustive throw new IllegalStateException(); } /** * Computes the subset of {@code this} and {@code that} types under shallow * equality. * * @return a pair containing the restricted type of {@code this} as the first * component and the restricted type of {@code that} as the second * element. The returned pair is never {@code null} even though its * components may be {@code null}. */ public TypePair getTypesUnderShallowEquality(JSType that) { JSType commonType = getGreatestSubtype(that); return new TypePair(commonType, commonType); } /** * Computes the subset of {@code this} and {@code that} types under * shallow inequality. * * @return A pair containing the restricted type of {@code this} as the first * component and the restricted type of {@code that} as the second * element. The returned pair is never {@code null} even though its * components may be {@code null} */ public TypePair getTypesUnderShallowInequality(JSType that) { // union types if (that.isUnionType()) { TypePair p = that.toMaybeUnionType().getTypesUnderShallowInequality(this); return new TypePair(p.typeB, p.typeA); } // Other types. // There are only two types whose shallow inequality is deterministically // true -- null and undefined. We can just enumerate them. if (isNullType() && that.isNullType() || isVoidType() && that.isVoidType()) { return new TypePair(null, null); } else { return new TypePair(this, that); } } /** * If this is a union type, returns a union type that does not include * the null or undefined type. */ public JSType restrictByNotNullOrUndefined() { return this; } /** * Checks whether {@code this} is a subtype of {@code that}.

      * * Subtyping rules: *

        *
      • (unknown) — every type is a subtype of the Unknown type.
      • *
      • (no) — the No type is a subtype of every type.
      • *
      • (no-object) — the NoObject type is a subtype of every object * type (i.e. subtypes of the Object type).
      • *
      • (ref) — a type is a subtype of itself.
      • *
      • (union-l) — A union type is a subtype of a type U if all the * union type's constituents are a subtype of U. Formally
        * {@code (T1, …, Tn) <: U} if and only * {@code Tk <: U} for all {@code k ∈ 1..n}.
      • *
      • (union-r) — A type U is a subtype of a union type if it is a * subtype of one of the union type's constituents. Formally
        * {@code U <: (T1, …, Tn)} if and only * if {@code U <: Tk} for some index {@code k}.
      • *
      • (objects) — an Object {@code O1} is a subtype * of an object {@code O2} if it has more properties * than {@code O2} and all common properties are * pairwise subtypes.
      • *
      * * @return {@code this <: that} */ public boolean isSubtype(JSType that) { return isSubtypeHelper(this, that); } /** * A generic implementation meant to be used as a helper for common subtyping * cases. */ static boolean isSubtypeHelper(JSType thisType, JSType thatType) { // unknown if (thatType.isUnknownType()) { return true; } // all type if (thatType.isAllType()) { return true; } // equality if (thisType.isEquivalentTo(thatType)) { return true; } // unions if (thatType.isUnionType()) { UnionType union = thatType.toMaybeUnionType(); for (JSType element : union.alternates) { if (thisType.isSubtype(element)) { return true; } } return false; } // templatized types. if (thisType.isTemplatizedType()) { return !areIncompatibleArrays(thisType, thatType) && thisType.toMaybeTemplatizedType().getReferencedType().isSubtype( thatType); } if (thatType.isTemplatizedType()) { if (!isExemptFromTemplateTypeInvariance(thatType) && !thisType.getTemplateTypeMap().checkEquivalenceHelper( thatType.getTemplateTypeMap(), EquivalenceMethod.IDENTITY)) { return false; } } // proxy types if (thatType instanceof ProxyObjectType) { return thisType.isSubtype( ((ProxyObjectType) thatType).getReferencedTypeInternal()); } return false; } /** * Determines if two types are incompatible Arrays, meaning that their element * template types are not subtypes of one another. */ private static boolean areIncompatibleArrays(JSType type1, JSType type2) { ObjectType type1Obj = type1.toObjectType(); ObjectType type2Obj = type2.toObjectType(); if (type1Obj == null || type2Obj == null) { return false; } if (!"Array".equals(type1Obj.getReferenceName()) || !"Array".equals(type2Obj.getReferenceName())) { return false; } TemplateType templateKey = type1.registry.getObjectElementKey(); JSType elemType1 = type1.getTemplateTypeMap().getTemplateType(templateKey); JSType elemType2 = type2.getTemplateTypeMap().getTemplateType(templateKey); return !elemType1.isSubtype(elemType2) && !elemType2.isSubtype(elemType1); } /** * Determines if the specified type is exempt from standard invariant * templatized typing rules. */ static boolean isExemptFromTemplateTypeInvariance(JSType type) { ObjectType objType = type.toObjectType(); return objType == null || "Array".equals(objType.getReferenceName()) || "Object".equals(objType.getReferenceName()); } /** * Visit this type with the given visitor. * @see com.google.javascript.rhino.jstype.Visitor * @return the value returned by the visitor */ public abstract T visit(Visitor visitor); /** * Visit the types with the given visitor. * @see com.google.javascript.rhino.jstype.RelationshipVisitor * @return the value returned by the visitor */ abstract T visit(RelationshipVisitor visitor, JSType that); /** * Force this type to resolve, even if the registry is in a lazy * resolving mode. * @see #resolve */ public final JSType forceResolve(ErrorReporter t, StaticScope scope) { ResolveMode oldResolveMode = registry.getResolveMode(); registry.setResolveMode(ResolveMode.IMMEDIATE); JSType result = resolve(t, scope); registry.setResolveMode(oldResolveMode); return result; } /** * Resolve this type in the given scope. * * The returned value must be equal to {@code this}, as defined by * {@link #isEquivalentTo}. It may or may not be the same object. This method * may modify the internal state of {@code this}, as long as it does * so in a way that preserves Object equality. * * For efficiency, we should only resolve a type once per compilation job. * For incremental compilations, one compilation job may need the * artifacts from a previous generation, so we will eventually need * a generational flag instead of a boolean one. */ public final JSType resolve(ErrorReporter t, StaticScope scope) { if (resolved) { // TODO(nicksantos): Check to see if resolve() looped back on itself. // Preconditions.checkNotNull(resolveResult); if (resolveResult == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return resolveResult; } resolved = true; resolveResult = resolveInternal(t, scope); resolveResult.setResolvedTypeInternal(resolveResult); return resolveResult; } /** * @see #resolve */ abstract JSType resolveInternal(ErrorReporter t, StaticScope scope); void setResolvedTypeInternal(JSType type) { resolveResult = type; resolved = true; } /** Whether the type has been resolved. */ public final boolean isResolved() { return resolved; } /** Clears the resolved field. */ public final void clearResolved() { resolved = false; resolveResult = null; } /** * A null-safe resolve. * @see #resolve */ static final JSType safeResolve( JSType type, ErrorReporter t, StaticScope scope) { return type == null ? null : type.resolve(t, scope); } /** * Certain types have constraints on them at resolution-time. * For example, a type in an {@code @extends} annotation must be an * object. Clients should inject a validator that emits a warning * if the type does not validate, and return false. */ public boolean setValidator(Predicate validator) { return validator.apply(this); } public static class TypePair { public final JSType typeA; public final JSType typeB; public TypePair(JSType typeA, JSType typeB) { this.typeA = typeA; this.typeB = typeB; } } /** * A string representation of this type, suitable for printing * in warnings. */ @Override public String toString() { return toStringHelper(false); } /** * A hash code function for diagnosing complicated issues * around type-identity. */ public String toDebugHashCodeString() { return "{" + hashCode() + "}"; } /** * A string representation of this type, suitable for printing * in type annotations at code generation time. */ public final String toAnnotationString() { return toStringHelper(true); } /** * @param forAnnotations Whether this is for use in code generator * annotations. Otherwise, it's for warnings. */ abstract String toStringHelper(boolean forAnnotations); /** * Modify this type so that it matches the specified type. * * This is useful for reverse type-inference, where we want to * infer that an object literal matches its constraint (much like * how the java compiler does reverse-inference to figure out generics). * @param constraint */ public void matchConstraint(JSType constraint) {} } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/EnumElementType.java0000644000175000017500000001514512115204405027727 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The type of individual elements of an enum type * (see {@link EnumType}). */ public class EnumElementType extends ObjectType { private static final long serialVersionUID = 1L; /** * The primitive type this enum element type wraps. For instance, in * the following code defining the {@code LOCAL_CODES} enum *
      var LOCAL_CODES = {A: 3, B: 9, C: 8}
      * the primitive type of the the constants is {@code number}. */ private JSType primitiveType; // The primitive type, if it is an object. private ObjectType primitiveObjectType; private final String name; EnumElementType(JSTypeRegistry registry, JSType elementType, String name) { super(registry); this.primitiveType = elementType; this.primitiveObjectType = elementType.toObjectType(); this.name = name; } @Override public PropertyMap getPropertyMap() { return primitiveObjectType == null ? PropertyMap.immutableEmptyMap() : primitiveObjectType.getPropertyMap(); } @Override public EnumElementType toMaybeEnumElementType() { return this; } @Override public boolean matchesNumberContext() { return primitiveType.matchesNumberContext(); } @Override public boolean matchesStringContext() { return primitiveType.matchesStringContext(); } @Override public boolean matchesObjectContext() { return primitiveType.matchesObjectContext(); } @Override public boolean canBeCalled() { return primitiveType.canBeCalled(); } @Override public boolean isObject() { return primitiveType.isObject(); } @Override public TernaryValue testForEquality(JSType that) { return primitiveType.testForEquality(that); } /** * This predicate determines whether objects of this type can have the null * value, and therefore can appear in contexts where null is expected. * * @return true for everything but Number and Boolean types. */ @Override public boolean isNullable() { return primitiveType.isNullable(); } @Override public boolean isNominalType() { return hasReferenceName(); } /** * If this is equal to a NamedType object, its hashCode must be equal * to the hashCode of the NamedType object. */ @Override public int hashCode() { if (hasReferenceName()) { return getReferenceName().hashCode(); } else { return super.hashCode(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? primitiveType.toString() : (getReferenceName() + ".<" + primitiveType + ">"); } @Override public String getReferenceName() { return name; } @Override public boolean hasReferenceName() { return true; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return primitiveType.isSubtype(that); } } @Override public T visit(Visitor visitor) { return visitor.caseEnumElementType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseEnumElementType(this, that); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public JSType findPropertyType(String propertyName) { return primitiveType.findPropertyType(propertyName); } @Override public FunctionType getConstructor() { return primitiveObjectType == null ? null : primitiveObjectType.getConstructor(); } @Override public JSType autoboxesTo() { return primitiveType.autoboxesTo(); } /** * Gets the primitive type of this enum element. */ public JSType getPrimitiveType() { return primitiveType; } /** * Returns the infimum of a enum element type and another type, or null * if the infimum is empty. * * This can be a little bit weird. For example, suppose you have an enum * of {(string|number)}, and you want the greatest subtype of the enum * and a {number}. * * The infimum is non-empty. But at the same time, we don't really have * a name for this infimum. It's equivalent to "elements of this enum that * are numbers". * * The best we can do is make up a new type. This is similar to what * we do in UnionType#meet, which kind-of-sort-of makes sense, because * an EnumElementType is a union of instances of a type. */ JSType meet(JSType that) { JSType meetPrimitive = primitiveType.getGreatestSubtype(that); if (meetPrimitive.isEmptyType()) { return null; } else { return new EnumElementType(registry, meetPrimitive, name); } } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { primitiveType = primitiveType.resolve(t, scope); primitiveObjectType = ObjectType.cast(primitiveType); return this; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/TernaryValue.java0000644000175000017500000001231212115204405027261 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** *

      An enum for ternary logic. The {@link #TRUE} and {@link #FALSE} values * are equivalent to typical booleans, and the {@link #UNKNOWN} value plays the * role of a placeholder, which can be either {@link #TRUE} or * {@link #FALSE}.

      * *

      A ternary value expression evaluates to {@link #TRUE} or * {@link #FALSE} only if all replacements of {@link #UNKNOWN} in this * expression yield the same result. Therefore, the ternary logic coincides * with typical Boolean logic if the {@link #UNKNOWN} value is not * present in an expression.

      * * @see Ternary Logic */ public enum TernaryValue { /** * {@code false} */ FALSE { @Override public TernaryValue and(TernaryValue that) { return FALSE; } @Override public TernaryValue not() { return TRUE; } @Override public TernaryValue or(TernaryValue that) { return that; } @Override public TernaryValue xor(TernaryValue that) { return that; } @Override public boolean toBoolean(boolean unknown) { return false; } @Override public String toString() { return "false"; } }, /** * {@code true} */ TRUE { @Override public TernaryValue and(TernaryValue that) { return that; } @Override public TernaryValue not() { return FALSE; } @Override public TernaryValue or(TernaryValue that) { return TRUE; } @Override public TernaryValue xor(TernaryValue that) { return that.not(); } @Override public boolean toBoolean(boolean unknown) { return true; } @Override public String toString() { return "true"; } }, /** * {@code unknown}, it represents lack of knowledge about whether this value * is {@code true} or {@code false}. */ UNKNOWN { @Override public TernaryValue and(TernaryValue that) { return (FALSE.equals(that)) ? FALSE : UNKNOWN; } @Override public TernaryValue not() { return UNKNOWN; } @Override public TernaryValue or(TernaryValue that) { return (TRUE.equals(that)) ? TRUE : UNKNOWN; } @Override public TernaryValue xor(TernaryValue that) { return UNKNOWN; } @Override public boolean toBoolean(boolean unknown) { return unknown; } @Override public String toString() { return "unknown"; } }; /** * Gets the {@code and} of {@code this} and {@code that}. */ public abstract TernaryValue and(TernaryValue that); /** * Gets the {@code not} of {@code this}. */ public abstract TernaryValue not(); /** * Gets the {@code or} of {@code this} and {@code that}. */ public abstract TernaryValue or(TernaryValue that); /** * Gets the {@code xor} of {@code this} and {@code that}. */ public abstract TernaryValue xor(TernaryValue that); /** * Converts {@code this} ternary value to boolean. The {@code #TRUE} and * {@code #FALSE} values are simply converted to {@code true} and * {@code false} respectively, whilst the {@link #UNKNOWN} is converted * to the specified {@code unknown} value. * * @param unknown the boolean value to which the {@link #UNKNOWN} value is * converted * @return
      return
         *     this == TRUE ? true :
         *     this == FALSE ? false :
         *     unknown
      */ public abstract boolean toBoolean(boolean unknown); /** * Gets the TernaryValue for the given boolean. */ public static TernaryValue forBoolean(boolean val) { return val ? TernaryValue.TRUE : TernaryValue.FALSE; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/Property.java0000644000175000017500000000701512115204405026470 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.io.Serializable; /** * A property slot of an object. * @author nicksantos@google.com (Nick Santos) */ public final class Property implements Serializable, StaticSlot, StaticReference { private static final long serialVersionUID = 1L; /** * Property's name. */ private final String name; /** * Property's type. */ private JSType type; /** * Whether the property's type is inferred. */ private final boolean inferred; /** * The node corresponding to this property, e.g., a GETPROP node that * declares this property. */ private Node propertyNode; /** The JSDocInfo for this property. */ private JSDocInfo docInfo = null; Property(String name, JSType type, boolean inferred, Node propertyNode) { this.name = name; this.type = type; this.inferred = inferred; this.propertyNode = propertyNode; } @Override public String getName() { return name; } @Override public Node getNode() { return propertyNode; } @Override public StaticSourceFile getSourceFile() { return propertyNode == null ? null : propertyNode.getStaticSourceFile(); } @Override public Property getSymbol() { return this; } @Override public Property getDeclaration() { return propertyNode == null ? null : this; } @Override public JSType getType() { return type; } @Override public boolean isTypeInferred() { return inferred; } boolean isFromExterns() { return propertyNode == null ? false : propertyNode.isFromExterns(); } void setType(JSType type) { this.type = type; } @Override public JSDocInfo getJSDocInfo() { return this.docInfo; } void setJSDocInfo(JSDocInfo info) { this.docInfo = info; } public void setNode(Node n) { this.propertyNode = n; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NullType.java0000644000175000017500000000624412115204405026423 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.TRUE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Null type. */ public final class NullType extends ValueType { private static final long serialVersionUID = 1L; NullType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullType() { return true; } @Override public boolean isNullable() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return false; } @Override public boolean matchesStringContext() { return true; } @Override public JSType restrictByNotNullOrUndefined() { return registry.getNativeType(JSTypeNative.NO_TYPE); } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isNullType() || that.isVoidType()) { return TRUE; } if (that.isUnknownType() || that.isNullable()) { return UNKNOWN; } return FALSE; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "null"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.FALSE; } @Override public T visit(Visitor visitor) { return visitor.caseNullType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/PropertyMap.java0000644000175000017500000001373712115204405027136 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.Serializable; import java.util.Map; import java.util.Set; /** * Representation for a collection of properties on an object. * @author nicksantos@google.com (Nick Santos) */ class PropertyMap implements Serializable { private static final long serialVersionUID = 1L; private static final PropertyMap EMPTY_MAP = new PropertyMap( ImmutableMap.of()); private static final Function PROP_MAP_FROM_TYPE = new Function() { @Override public PropertyMap apply(ObjectType t) { return t.getPropertyMap(); } }; // A place to get the inheritance structure. // Because the extended interfaces are resolved dynamically, this gets // messy :(. If type-resolution was more well-defined, we could // just reference primary parents and secondary parents directly. private ObjectType parentSource = null; // The map of our own properties. private final Map properties; PropertyMap() { this(Maps.newTreeMap()); } private PropertyMap(Map underlyingMap) { this.properties = underlyingMap; } static PropertyMap immutableEmptyMap() { return EMPTY_MAP; } void setParentSource(ObjectType ownerType) { if (this != EMPTY_MAP) { this.parentSource = ownerType; } } /** Returns the direct parent of this property map. */ PropertyMap getPrimaryParent() { if (parentSource == null) { return null; } ObjectType iProto = parentSource.getImplicitPrototype(); return iProto == null ? null : iProto.getPropertyMap(); } /** * Returns the secondary parents of this property map, for interfaces that * need multiple inheritance. */ Iterable getSecondaryParents() { if (parentSource == null) { return ImmutableList.of(); } Iterable extendedInterfaces = parentSource.getCtorExtendedInterfaces(); // Most of the time, this will be empty. if (Iterables.isEmpty(extendedInterfaces)) { return ImmutableList.of(); } return Iterables.transform(extendedInterfaces, PROP_MAP_FROM_TYPE); } Property getSlot(String name) { if (properties.containsKey(name)) { return properties.get(name); } PropertyMap primaryParent = getPrimaryParent(); if (primaryParent != null) { Property prop = primaryParent.getSlot(name); if (prop != null) { return prop; } } for (PropertyMap p : getSecondaryParents()) { if (p != null) { Property prop = p.getSlot(name); if (prop != null) { return prop; } } } return null; } Property getOwnProperty(String propertyName) { return properties.get(propertyName); } int getPropertiesCount() { PropertyMap primaryParent = getPrimaryParent(); if (primaryParent == null) { return this.properties.size(); } Set props = Sets.newHashSet(); collectPropertyNames(props); return props.size(); } boolean hasOwnProperty(String propertyName) { return properties.get(propertyName) != null; } boolean hasProperty(String propertyName) { return getSlot(propertyName) != null; } Set getOwnPropertyNames() { return properties.keySet(); } void collectPropertyNames(Set props) { for (String prop : properties.keySet()) { props.add(prop); } PropertyMap primaryParent = getPrimaryParent(); if (primaryParent != null) { primaryParent.collectPropertyNames(props); } for (PropertyMap p : getSecondaryParents()) { if (p != null) { p.collectPropertyNames(props); } } } boolean removeProperty(String name) { return properties.remove(name) != null; } void putProperty(String name, Property newProp) { Property oldProp = properties.get(name); if (oldProp != null) { // This is to keep previously inferred JsDoc info, e.g., in a // replaceScript scenario. newProp.setJSDocInfo(oldProp.getJSDocInfo()); } properties.put(name, newProp); } Iterable values() { return properties.values(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ErrorFunctionType.java0000644000175000017500000000553112115204405030306 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.JSTypeNative.ALL_TYPE; /** * This type is for built-in error constructors. */ class ErrorFunctionType extends FunctionType { private static final long serialVersionUID = 1L; ErrorFunctionType(JSTypeRegistry registry, String name) { super( registry, name, null, registry.createArrowType( registry.createOptionalParameters( registry.getNativeType(ALL_TYPE), registry.getNativeType(ALL_TYPE), registry.getNativeType(ALL_TYPE)), null), null, null, true, true); // NOTE(nicksantos): Errors have the weird behavior in that they can // be called as functions, and they will return instances of themselves. // Error('x') instanceof Error => true // // In user-defined types, we would deal with this case by creating // a NamedType with the name "Error" and then resolve it later. // // For native types, we don't really want the native types to // depend on type-resolution. So we just set the return type manually // at the end of construction. // // There's similar logic in JSTypeRegistry for Array and RegExp. getInternalArrowType().returnType = getInstanceType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StaticSlot.java0000644000175000017500000000455112115204405026737 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.JSDocInfo; /** * The {@code StaticSlot} interface must be implemented by variables that can * appear as members of a {@code StaticScope}. * * @param The type of information stored about the slot */ public interface StaticSlot { /** * Gets the name of the slot. */ String getName(); /** * Returns the type information, if any, for this slot. * @return The type or {@code null} if no type is declared for it. */ T getType(); /** * Returns whether the type has been inferred (as opposed to declared). */ boolean isTypeInferred(); /** Gets the declaration of this symbol. May not exist. */ StaticReference getDeclaration(); /** Gets the JSDoc for this slot. */ JSDocInfo getJSDocInfo(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/SimpleReference.java0000644000175000017500000000470112115204405027713 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; /** * A simple immutable reference. * @author nicksantos@google.com (Nick Santos) */ public class SimpleReference> implements StaticReference { private final T symbol; private final Node node; public SimpleReference(T symbol, Node node) { this.symbol = symbol; this.node = node; } @Override final public T getSymbol() { return symbol; } @Override final public Node getNode() { return node; } @Override final public StaticSourceFile getSourceFile() { return node.getStaticSourceFile(); } @Override public String toString() { String sourceName = node == null ? null : node.getSourceFileName(); int lineNo = node == null ? -1 : node.getLineno(); return node.getQualifiedName() + "@" + sourceName + ":" + lineNo; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/TemplatizedType.java0000644000175000017500000001233112115204405027765 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; /** * An object type with declared template types, such as * Array.. * */ public final class TemplatizedType extends ProxyObjectType { private static final long serialVersionUID = 1L; final ImmutableList templateTypes; TemplatizedType( JSTypeRegistry registry, ObjectType objectType, ImmutableList templateTypes) { super(registry, objectType, objectType.getTemplateTypeMap().extendValues( templateTypes)); // Cache which template keys were filled, and what JSTypes they were filled // with. ImmutableList filledTemplateKeys = objectType.getTemplateTypeMap().getUnfilledTemplateKeys(); ImmutableList.Builder builder = ImmutableList.builder(); for (TemplateType filledTemplateKey : filledTemplateKeys) { builder.add(getTemplateTypeMap().getTemplateType(filledTemplateKey)); } this.templateTypes = builder.build(); } @Override String toStringHelper(boolean forAnnotations) { String typeString = super.toStringHelper(forAnnotations); if (!templateTypes.isEmpty()) { typeString += ".<" + Joiner.on(",").join(templateTypes) + ">"; } return typeString; } @Override public T visit(Visitor visitor) { return visitor.caseTemplatizedType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseTemplatizedType(this, that); } @Override public TemplatizedType toMaybeTemplatizedType() { return this; } @Override public ImmutableList getTemplateTypes() { return templateTypes; } @Override public boolean isSubtype(JSType that) { return isSubtypeHelper(this, that); } boolean wrapsSameRawType(JSType that) { return that.isTemplatizedType() && this.getReferencedTypeInternal() .isEquivalentTo( that.toMaybeTemplatizedType().getReferencedTypeInternal()); } boolean wrapsRawType(JSType that) { return this.getReferencedTypeInternal().isEquivalentTo(that); } /** * Computes the greatest subtype of two related templatized types. * @return The greatest subtype. */ JSType getGreatestSubtypeHelper(JSType rawThat) { Preconditions.checkNotNull(rawThat); if (!wrapsSameRawType(rawThat)) { if (!rawThat.isTemplatizedType()) { if (this.isSubtype(rawThat)) { return this; } else if (rawThat.isSubtype(this)) { return filterNoResolvedType(rawThat); } } if (this.isObject() && rawThat.isObject()) { return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE); } return this.getNativeType(JSTypeNative.NO_TYPE); } TemplatizedType that = rawThat.toMaybeTemplatizedType(); Preconditions.checkNotNull(that); if (getTemplateTypeMap().checkEquivalenceHelper( that.getTemplateTypeMap(), EquivalenceMethod.INVARIANT)) { return this; } // For types that have the same raw type but different type parameters, // we simply create a type has a "unknown" type parameter. This is // equivalent to the raw type. return getReferencedObjTypeInternal(); } @Override public TemplateTypeMap getTemplateTypeMap() { return templateTypeMap; } @Override public boolean hasAnyTemplateTypesInternal() { return templateTypeMap.hasAnyTemplateTypesInternal(); } /** * @return The referenced ObjectType wrapped by this TemplatizedType. */ public ObjectType getReferencedType() { return getReferencedObjTypeInternal(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/TemplateTypeMap.java0000644000175000017500000001612412115204405027720 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.io.Serializable; /** * Manages a mapping from TemplateType to its resolved JSType. Provides utility * methods for cloning/extending the map. * * @author izaakr@google.com (Izaak Rubin) */ public class TemplateTypeMap implements Serializable { private final ImmutableList templateKeys; private final ImmutableList templateValues; final JSTypeRegistry registry; TemplateTypeMap(JSTypeRegistry registry, ImmutableList templateKeys, ImmutableList templateValues) { Preconditions.checkNotNull(templateKeys); Preconditions.checkNotNull(templateValues); this.registry = registry; this.templateKeys = templateKeys; int nKeys = templateKeys.size(); this.templateValues = templateValues.size() > nKeys ? templateValues.subList(0, nKeys) : templateValues; } /** * Returns a list of all template keys. */ public ImmutableList getTemplateKeys() { return templateKeys; } /** * Returns true if this map contains the specified template key, false * otherwise. */ public boolean hasTemplateKey(TemplateType templateKey) { // Note: match by identity, not equality for (TemplateType entry : templateKeys) { if (entry == templateKey) { return true; } } return false; } /** * Returns the number of template keys in this map that do not have a * corresponding JSType value. */ int numUnfilledTemplateKeys() { return templateKeys.size() - templateValues.size(); } /** * Returns a list of template keys in this map that do not have corresponding * JSType values. */ ImmutableList getUnfilledTemplateKeys() { return templateKeys.subList(templateValues.size(), templateKeys.size()); } /** * Returns true if there is a JSType value associated with the specified * template key; false otherwise. */ public boolean hasTemplateType(TemplateType key) { return getTemplateTypeInternal(key) != null; } /** * Returns the JSType value associated with the specified template key. If no * JSType value is associated, returns UNKNOWN_TYPE. */ public JSType getTemplateType(TemplateType key) { JSType templateType = getTemplateTypeInternal(key); return (templateType == null) ? registry.getNativeType(JSTypeNative.UNKNOWN_TYPE) : templateType; } public TemplateType getTemplateTypeKeyByName(String keyName) { for (TemplateType key : templateKeys) { if (key.getReferenceName().equals(keyName)) { return key; } } return null; } /** * Returns the JSType value associated with the specified template key. If no * JSType value is associated, returns null. */ private JSType getTemplateTypeInternal(TemplateType key) { int index = 0; for (TemplateType item : templateKeys) { // Note: match by identity. if (item == key) { break; } index++; } if (index < 0 || index >= templateValues.size()) { return null; } return templateValues.get(index); } /** * Determines if this map and the specified map have equivalent template * types. */ public boolean checkEquivalenceHelper( TemplateTypeMap that, EquivalenceMethod eqMethod) { int thisNumKeys = templateKeys.size(); int thatNumKeys = that.getTemplateKeys().size(); for (int i = 0; i < Math.min(thisNumKeys, thatNumKeys); i++) { JSType thisTemplateType = getTemplateType(templateKeys.get(i)); JSType thatTemplateType = that.getTemplateType( that.getTemplateKeys().get(i)); if (!thisTemplateType.checkEquivalenceHelper( thatTemplateType, eqMethod)) { return false; } } return thisNumKeys == thatNumKeys || eqMethod == EquivalenceMethod.INVARIANT; } /** * Returns a new TemplateTypeMap whose keys have been extended with the * specified list. */ TemplateTypeMap extendKeys(ImmutableList newKeys) { return registry.createTemplateTypeMap( concatImmutableLists(templateKeys, newKeys), templateValues); } /** * Returns a new TemplateTypeMap whose values have been extended with the * specified list. */ TemplateTypeMap extendValues(ImmutableList newValues) { // Ignore any new template values that will not align with an existing // template key. int numUnfilledKeys = numUnfilledTemplateKeys(); if (numUnfilledKeys < newValues.size()) { newValues = newValues.subList(0, numUnfilledKeys); } return registry.createTemplateTypeMap( templateKeys, concatImmutableLists(templateValues, newValues)); } /** * Concatenates two ImmutableList instances. If either input is empty, the * other is returned; otherwise, a new ImmutableList instance is created that * contains the contents of both arguments. */ private ImmutableList concatImmutableLists( ImmutableList first, ImmutableList second) { if (first.isEmpty()) { return second; } if (second.isEmpty()) { return first; } ImmutableList.Builder builder = ImmutableList.builder(); builder.addAll(first); builder.addAll(second); return builder.build(); } boolean hasAnyTemplateTypesInternal() { for (JSType templateValue : templateValues) { if (templateValue.hasAnyTemplateTypes()) { return true; } } return false; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/SimpleSourceFile.java0000644000175000017500000000475212115204405030063 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A simple implementation of {@code StaticSourceFile} for testing. * * @author nicksantos@google.com (Nick Santos) */ public final class SimpleSourceFile implements StaticSourceFile { private final String name; private final boolean extern; public SimpleSourceFile(String name, boolean extern) { this.name = name; this.extern = extern; } @Override public String getName() { return name; } @Override public boolean isExtern() { return extern; } @Override public int getColumnOfOffset(int offset) { return 0; } @Override public int getLineOfOffset(int offset) { return 1; } @Override public int getLineOffset(int line) { if (line < 1) { throw new IllegalStateException( "Should not call getLineOffset with line number " + line); } return Integer.MIN_VALUE; } @Override public String toString() { return name; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ObjectType.java0000644000175000017500000004706012115204405026720 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Set; /** * Object type. * * In JavaScript, all object types have properties, and each of those * properties has a type. Property types may be DECLARED, INFERRED, or * UNKNOWN. * * DECLARED properties have an explicit type annotation, as in: * * /xx @type {number} x/ * Foo.prototype.bar = 1; * * This property may only hold number values, and an assignment to any * other type of value is an error. * * INFERRED properties do not have an explicit type annotation. Rather, * we try to find all the possible types that this property can hold. * * Foo.prototype.bar = 1; * * If the programmer assigns other types of values to this property, * the property will take on the union of all these types. * * UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN * type has all properties, but we do not know whether they are * declared or inferred. * */ public abstract class ObjectType extends JSType implements StaticScope { private boolean visited; private JSDocInfo docInfo = null; private boolean unknown = true; ObjectType(JSTypeRegistry registry) { super(registry); } ObjectType(JSTypeRegistry registry, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); } @Override public Node getRootNode() { return null; } @Override public ObjectType getParentScope() { return getImplicitPrototype(); } /** * Returns the property map that manages the set of properties for an object. */ PropertyMap getPropertyMap() { return PropertyMap.immutableEmptyMap(); } /** * Default getSlot implementation. This gets overridden by FunctionType * for lazily-resolved prototypes. */ @Override public Property getSlot(String name) { return getPropertyMap().getSlot(name); } @Override public Property getOwnSlot(String name) { return getPropertyMap().getOwnProperty(name); } @Override public JSType getTypeOfThis() { return null; } /** * Gets the declared default element type. * @see TemplatizedType */ public ImmutableList getTemplateTypes() { return null; } /** * Gets the docInfo for this type. */ @Override public JSDocInfo getJSDocInfo() { return docInfo; } /** * Sets the docInfo for this type from the given * {@link JSDocInfo}. The {@code JSDocInfo} may be {@code null}. */ public void setJSDocInfo(JSDocInfo info) { docInfo = info; } /** * Detects a cycle in the implicit prototype chain. This method accesses * the {@link #getImplicitPrototype()} method and must therefore be * invoked only after the object is sufficiently initialized to respond to * calls to this method.

      * * @return True iff an implicit prototype cycle was detected. */ final boolean detectImplicitPrototypeCycle() { // detecting cycle this.visited = true; ObjectType p = getImplicitPrototype(); while (p != null) { if (p.visited) { return true; } else { p.visited = true; } p = p.getImplicitPrototype(); } // clean up p = this; do { p.visited = false; p = p.getImplicitPrototype(); } while (p != null); return false; } /** * Detects cycles in either the implicit prototype chain, or the implemented/extended * interfaces.

      * * @return True iff a cycle was detected. */ final boolean detectInheritanceCycle() { // TODO(user): This should get moved to preventing cycles in FunctionTypeBuilder // rather than removing them here after they have been created. // Also, this doesn't do the right thing for extended interfaces, though that is // masked by another bug. return detectImplicitPrototypeCycle() || Iterables.contains(this.getCtorImplementedInterfaces(), this) || Iterables.contains(this.getCtorExtendedInterfaces(), this); } /** * Gets the reference name for this object. This includes named types * like constructors, prototypes, and enums. It notably does not include * literal types like strings and booleans and structural types. * @return the object's name or {@code null} if this is an anonymous * object */ public abstract String getReferenceName(); /** * Due to the complexity of some of our internal type systems, sometimes * we have different types constructed by the same constructor. * In other parts of the type system, these are called delegates. * We construct these types by appending suffixes to the constructor name. * * The normalized reference name does not have these suffixes, and as such, * recollapses these implicit types back to their real type. */ public String getNormalizedReferenceName() { String name = getReferenceName(); if (name != null) { int pos = name.indexOf("("); if (pos != -1) { return name.substring(0, pos); } } return name; } @Override public String getDisplayName() { return getNormalizedReferenceName(); } /** * Creates a suffix for a proxy delegate. * @see #getNormalizedReferenceName */ public static String createDelegateSuffix(String suffix) { return "(" + suffix + ")"; } /** * Returns true if the object is named. * @return true if the object is named, false if it is anonymous */ public boolean hasReferenceName() { return false; } @Override public TernaryValue testForEquality(JSType that) { // super TernaryValue result = super.testForEquality(that); if (result != null) { return result; } // objects are comparable to everything but null/undefined if (that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } else { return FALSE; } } /** * Gets this object's constructor. * @return this object's constructor or {@code null} if it is a native * object (constructed natively v.s. by instantiation of a function) */ public abstract FunctionType getConstructor(); /** * Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property). */ public abstract ObjectType getImplicitPrototype(); /** * Defines a property whose type is explicitly declared by the programmer. * @param propertyName the property's name * @param type the type * @param propertyNode the node corresponding to the declaration of property * which might later be accessed using {@code getPropertyNode}. */ public final boolean defineDeclaredProperty(String propertyName, JSType type, Node propertyNode) { boolean result = defineProperty(propertyName, type, false, propertyNode); // All property definitions go through this method // or defineInferredProperty. Because the properties defined an an // object can affect subtyping, it's slightly more efficient // to register this after defining the property. registry.registerPropertyOnType(propertyName, this); return result; } /** * Defines a property whose type is on a synthesized object. These objects * don't actually exist in the user's program. They're just used for * bookkeeping in the type system. */ public final boolean defineSynthesizedProperty(String propertyName, JSType type, Node propertyNode) { return defineProperty(propertyName, type, false, propertyNode); } /** * Defines a property whose type is inferred. * @param propertyName the property's name * @param type the type * @param propertyNode the node corresponding to the inferred definition of * property that might later be accessed using {@code getPropertyNode}. */ public final boolean defineInferredProperty(String propertyName, JSType type, Node propertyNode) { StaticSlot originalSlot = getSlot(propertyName); if (hasProperty(propertyName)) { if (isPropertyTypeDeclared(propertyName)) { // We never want to hide a declared property with an inferred property. return true; } JSType originalType = getPropertyType(propertyName); type = originalType == null ? type : originalType.getLeastSupertype(type); } boolean result = defineProperty(propertyName, type, true, propertyNode); // All property definitions go through this method // or defineDeclaredProperty. Because the properties defined an an // object can affect subtyping, it's slightly more efficient // to register this after defining the property. registry.registerPropertyOnType(propertyName, this); return result; } /** * Defines a property.

      * * For clarity, callers should prefer {@link #defineDeclaredProperty} and * {@link #defineInferredProperty}. * * @param propertyName the property's name * @param type the type * @param inferred {@code true} if this property's type is inferred * @param propertyNode the node that represents the definition of property. * Depending on the actual sub-type the node type might be different. * The general idea is to have an estimate of where in the source code * this property is defined. * @return True if the property was registered successfully, false if this * conflicts with a previous property type declaration. */ abstract boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode); /** * Removes the declared or inferred property from this ObjectType. * * @param propertyName the property's name * @return true if the property was removed successfully. False if the * property did not exist, or could not be removed. */ public boolean removeProperty(String propertyName) { return false; } /** * Gets the node corresponding to the definition of the specified property. * This could be the node corresponding to declaration of the property or the * node corresponding to the first reference to this property, e.g., * "this.propertyName" in a constructor. Note this is mainly intended to be * an estimate of where in the source code a property is defined. Sometime * the returned node is not even part of the global AST but in the AST of the * JsDoc that defines a type. * * @param propertyName the name of the property * @return the {@code Node} corresponding to the property or null. */ public Node getPropertyNode(String propertyName) { Property p = getSlot(propertyName); return p == null ? null : p.getNode(); } /** * Gets the docInfo on the specified property on this type. This should not * be implemented recursively, as you generally need to know exactly on * which type in the prototype chain the JSDocInfo exists. */ public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { Property p = getOwnSlot(propertyName); return p == null ? null : p.getJSDocInfo(); } /** * Sets the docInfo for the specified property from the * {@link JSDocInfo} on its definition. * @param info {@code JSDocInfo} for the property definition. May be * {@code null}. */ public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { // by default, do nothing } @Override public JSType findPropertyType(String propertyName) { return hasProperty(propertyName) ? getPropertyType(propertyName) : null; } /** * Gets the property type of the property whose name is given. If the * underlying object does not have this property, the Unknown type is * returned to indicate that no information is available on this property. * * This gets overridden by FunctionType for lazily-resolved call() and * bind() functions. * * @return the property's type or {@link UnknownType}. This method never * returns {@code null}. */ public JSType getPropertyType(String propertyName) { StaticSlot slot = getSlot(propertyName); if (slot == null) { if (isNoResolvedType() || isCheckedUnknownType()) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } else if (isEmptyType()) { return getNativeType(JSTypeNative.NO_TYPE); } return getNativeType(JSTypeNative.UNKNOWN_TYPE); } return slot.getType(); } @Override public boolean hasProperty(String propertyName) { // Unknown types have all properties. return isEmptyType() || isUnknownType() || getSlot(propertyName) != null; } /** * Checks whether the property whose name is given is present directly on * the object. Returns false even if it is declared on a supertype. */ public boolean hasOwnProperty(String propertyName) { return getOwnSlot(propertyName) != null; } /** * Returns the names of all the properties directly on this type. * * Overridden by FunctionType to add "prototype". */ public Set getOwnPropertyNames() { return getPropertyMap().getOwnPropertyNames(); } /** * Checks whether the property's type is inferred. */ public boolean isPropertyTypeInferred(String propertyName) { StaticSlot slot = getSlot(propertyName); return slot == null ? false : slot.isTypeInferred(); } /** * Checks whether the property's type is declared. */ public boolean isPropertyTypeDeclared(String propertyName) { StaticSlot slot = getSlot(propertyName); return slot == null ? false : !slot.isTypeInferred(); } /** * Whether the given property is declared on this object. */ final boolean hasOwnDeclaredProperty(String name) { return hasOwnProperty(name) && isPropertyTypeDeclared(name); } /** Checks whether the property was defined in the externs. */ public boolean isPropertyInExterns(String propertyName) { Property p = getSlot(propertyName); return p == null ? false : p.isFromExterns(); } /** * Gets the number of properties of this object. */ public int getPropertiesCount() { return getPropertyMap().getPropertiesCount(); } /** * Returns a list of properties defined or inferred on this type and any of * its supertypes. */ public Set getPropertyNames() { Set props = Sets.newTreeSet(); collectPropertyNames(props); return props; } /** * Adds any properties defined on this type or its supertypes to the set. */ final void collectPropertyNames(Set props) { getPropertyMap().collectPropertyNames(props); } @Override public T visit(Visitor visitor) { return visitor.caseObjectType(this); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseObjectType(this, that); } /** * Checks that the prototype is an implicit prototype of this object. Since * each object has an implicit prototype, an implicit prototype's * implicit prototype is also this implicit prototype's. * * @param prototype any prototype based object * * @return {@code true} if {@code prototype} is {@code equal} to any * object in this object's implicit prototype chain. */ final boolean isImplicitPrototype(ObjectType prototype) { for (ObjectType current = this; current != null; current = current.getImplicitPrototype()) { if (current.isEquivalentTo(prototype)) { return true; } } return false; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } /** * We treat this as the unknown type if any of its implicit prototype * properties is unknown. */ @Override public boolean isUnknownType() { // If the object is unknown now, check the supertype again, // because it might have been resolved since the last check. if (unknown) { ObjectType implicitProto = getImplicitPrototype(); if (implicitProto == null || implicitProto.isNativeObjectType()) { unknown = false; for (ObjectType interfaceType : getCtorExtendedInterfaces()) { if (interfaceType.isUnknownType()) { unknown = true; break; } } } else { unknown = implicitProto.isUnknownType(); } } return unknown; } @Override public boolean isObject() { return true; } /** * Returns true if any cached values have been set for this type. If true, * then the prototype chain should not be changed, as it might invalidate the * cached values. */ public boolean hasCachedValues() { return !unknown; } /** * Clear cached values. Should be called before making changes to a prototype * that may have been changed since creation. */ public void clearCachedValues() { unknown = true; } /** Whether this is a built-in object. */ public boolean isNativeObjectType() { return false; } /** * A null-safe version of JSType#toObjectType. */ public static ObjectType cast(JSType type) { return type == null ? null : type.toObjectType(); } @Override public final boolean isFunctionPrototypeType() { return getOwnerFunction() != null; } /** Gets the owner of this if it's a function prototype. */ public FunctionType getOwnerFunction() { return null; } /** Sets the owner function. By default, does nothing. */ void setOwnerFunction(FunctionType type) {} /** * Gets the interfaces implemented by the ctor associated with this type. * Intended to be overridden by subclasses. */ public Iterable getCtorImplementedInterfaces() { return ImmutableSet.of(); } /** * Gets the interfaces extended by the interface associated with this type. * Intended to be overridden by subclasses. */ public Iterable getCtorExtendedInterfaces() { return ImmutableSet.of(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/ProxyObjectType.java0000644000175000017500000002232412115204405027756 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import java.util.Collections; /** * An object type which uses composition to delegate all calls. * * @see NamedType * @see TemplatizedType * */ class ProxyObjectType extends ObjectType { private static final long serialVersionUID = 1L; private JSType referencedType; private ObjectType referencedObjType; ProxyObjectType(JSTypeRegistry registry, JSType referencedType) { this(registry, referencedType, null); } ProxyObjectType(JSTypeRegistry registry, JSType referencedType, TemplateTypeMap templateTypeMap) { super(registry, templateTypeMap); setReferencedType(referencedType); } @Override PropertyMap getPropertyMap() { return referencedObjType == null ? PropertyMap.immutableEmptyMap() : referencedObjType.getPropertyMap(); } JSType getReferencedTypeInternal() { return referencedType; } ObjectType getReferencedObjTypeInternal() { return referencedObjType; } void setReferencedType(JSType referencedType) { this.referencedType = referencedType; if (referencedType instanceof ObjectType) { this.referencedObjType = (ObjectType) referencedType; } else { this.referencedObjType = null; } } @Override public String getReferenceName() { return referencedObjType == null ? "" : referencedObjType.getReferenceName(); } @Override public boolean hasReferenceName() { return referencedObjType == null ? null : referencedObjType.hasReferenceName(); } @Override public boolean matchesNumberContext() { return referencedType.matchesNumberContext(); } @Override public boolean matchesStringContext() { return referencedType.matchesStringContext(); } @Override public boolean matchesObjectContext() { return referencedType.matchesObjectContext(); } @Override public boolean canBeCalled() { return referencedType.canBeCalled(); } @Override public boolean isNoType() { return referencedType.isNoType(); } @Override public boolean isNoObjectType() { return referencedType.isNoObjectType(); } @Override public boolean isNoResolvedType() { return referencedType.isNoResolvedType(); } @Override public boolean isUnknownType() { return referencedType.isUnknownType(); } @Override public boolean isCheckedUnknownType() { return referencedType.isCheckedUnknownType(); } @Override public boolean isNullable() { return referencedType.isNullable(); } @Override public EnumType toMaybeEnumType() { return referencedType.toMaybeEnumType(); } @Override public boolean isConstructor() { return referencedType.isConstructor(); } @Override public boolean isNominalType() { return referencedType.isNominalType(); } @Override public boolean isInstanceType() { return referencedType.isInstanceType(); } @Override public boolean isInterface() { return referencedType.isInterface(); } @Override public boolean isOrdinaryFunction() { return referencedType.isOrdinaryFunction(); } @Override public boolean isAllType() { return referencedType.isAllType(); } @Override public boolean isStruct() { return referencedType.isStruct(); } @Override public boolean isDict() { return referencedType.isDict(); } @Override public boolean isNativeObjectType() { return referencedObjType == null ? false : referencedObjType.isNativeObjectType(); } @Override RecordType toMaybeRecordType() { return referencedType.toMaybeRecordType(); } @Override public UnionType toMaybeUnionType() { return referencedType.toMaybeUnionType(); } @Override public FunctionType toMaybeFunctionType() { return referencedType.toMaybeFunctionType(); } @Override public EnumElementType toMaybeEnumElementType() { return referencedType.toMaybeEnumElementType(); } @Override public TernaryValue testForEquality(JSType that) { return referencedType.testForEquality(that); } @Override public boolean isSubtype(JSType that) { return referencedType.isSubtype(that); } @Override public FunctionType getOwnerFunction() { return referencedObjType == null ? null : referencedObjType.getOwnerFunction(); } @Override public Iterable getCtorImplementedInterfaces() { return referencedObjType == null ? Collections.emptyList() : referencedObjType.getCtorImplementedInterfaces(); } @Override public int hashCode() { return referencedType.hashCode(); } @Override String toStringHelper(boolean forAnnotations) { return referencedType.toStringHelper(forAnnotations); } @Override public ObjectType getImplicitPrototype() { return referencedObjType == null ? null : referencedObjType.getImplicitPrototype(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { return referencedObjType == null ? true : referencedObjType.defineProperty( propertyName, type, inferred, propertyNode); } @Override public boolean removeProperty(String name) { return referencedObjType == null ? false : referencedObjType.removeProperty(name); } @Override public JSType findPropertyType(String propertyName) { return referencedType.findPropertyType(propertyName); } @Override public JSDocInfo getJSDocInfo() { return referencedType.getJSDocInfo(); } @Override public void setJSDocInfo(JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setJSDocInfo(info); } } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setPropertyJSDocInfo(propertyName, info); } } @Override public FunctionType getConstructor() { return referencedObjType == null ? null : referencedObjType.getConstructor(); } @Override public ImmutableList getTemplateTypes() { return referencedObjType == null ? null : referencedObjType.getTemplateTypes(); } @Override public T visit(Visitor visitor) { return referencedType.visit(visitor); } @Override T visit(RelationshipVisitor visitor, JSType that) { return referencedType.visit(visitor, that); } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { setReferencedType(referencedType.resolve(t, scope)); return this; } @Override public String toDebugHashCodeString() { return "{proxy:" + referencedType.toDebugHashCodeString() + "}"; } @Override public JSType getTypeOfThis() { if (referencedObjType != null) { return referencedObjType.getTypeOfThis(); } return super.getTypeOfThis(); } @Override public JSType collapseUnion() { if (referencedType.isUnionType()) { return referencedType.collapseUnion(); } return this; } @Override public void matchConstraint(JSType constraint) { referencedType.matchConstraint(constraint); } @Override public TemplatizedType toMaybeTemplatizedType() { return referencedType.toMaybeTemplatizedType(); } @Override public TemplateType toMaybeTemplateType() { return referencedType.toMaybeTemplateType(); } @Override public boolean hasAnyTemplateTypesInternal() { return referencedType.hasAnyTemplateTypes(); } @Override public TemplateTypeMap getTemplateTypeMap() { return referencedType.getTemplateTypeMap(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/package.html0000644000175000017500000000052312115204405026257 0ustar apoapo Provides abstractions to represent types in JavaScript. Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users. closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StaticReference.java0000644000175000017500000000410312115204405027705 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; /** * The {@code StaticReference} tells us all the ways that a {@code StaticSlot} * is used in a program. * * @author nicksantos@google.com (Nick Santos) */ public interface StaticReference { /** * The variable that this reference points to. */ StaticSlot getSymbol(); /** * The node where the reference lives. */ Node getNode(); /** * The source file where the reference lives. */ StaticSourceFile getSourceFile(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/StaticScope.java0000644000175000017500000000542312115204405027066 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; /** * The {@code StaticScope} interface must be implemented by any object that * defines variables for the purposes of static analysis. It is distinguished * from the {@code Scriptable} class that Rhino normally uses to represent a * run-time scope. * * @param The type of information stored about the slot */ public interface StaticScope { /** * Returns the root node associated with this scope. May be null. */ Node getRootNode(); /** Returns the scope enclosing this one or null if none. */ StaticScope getParentScope(); /** * Returns any defined slot within this scope for this name. This call * continues searching through parent scopes if a slot with this name is not * found in the current scope. * @param name The name of the variable slot to look up. * @return The defined slot for the variable, or {@code null} if no * definition exists. */ StaticSlot getSlot(String name); /** Like {@code getSlot} but does not recurse into parent scopes. */ StaticSlot getOwnSlot(String name); /** Returns the expected type of {@code this} in the current scope. */ T getTypeOfThis(); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NumberType.java0000644000175000017500000000622012115204405026733 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Number type. */ public class NumberType extends ValueType { private static final long serialVersionUID = 1L; NumberType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; } @Override public boolean isNumberValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "number"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public T visit(Visitor visitor) { return visitor.caseNumberType(); } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/BooleanType.java0000644000175000017500000000625312115204405027070 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.FALSE; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; /** * Boolean type. */ public class BooleanType extends ValueType { private static final long serialVersionUID = 1L; BooleanType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNullable() { return false; } @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype( getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) || that.isObject()) { return UNKNOWN; } return FALSE; } @Override public boolean isBooleanValueType() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public boolean matchesObjectContext() { // TODO(user): Revisit this for ES4, which is stricter. return true; } @Override public JSType autoboxesTo() { return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE); } @Override String toStringHelper(boolean forAnnotations) { return getDisplayName(); } @Override public String getDisplayName() { return "boolean"; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override public T visit(Visitor visitor) { return visitor.caseBooleanType(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/UnknownType.java0000644000175000017500000000751112115204405027146 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * The {@code Unknown} type. */ public class UnknownType extends ObjectType { private static final long serialVersionUID = 1L; // See the explanation of checked unknown types in JSTypeNative. private final boolean isChecked; UnknownType(JSTypeRegistry registry, boolean isChecked) { super(registry); this.isChecked = isChecked; } @Override public boolean isUnknownType() { return true; } @Override public boolean isCheckedUnknownType() { return isChecked; } @Override public boolean canBeCalled() { return true; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public TernaryValue testForEquality(JSType that) { return UNKNOWN; } @Override public boolean isNullable() { return true; } @Override public boolean isSubtype(JSType that) { return true; } @Override public T visit(Visitor visitor) { return visitor.caseUnknownType(); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseUnknownType(this, that); } @Override String toStringHelper(boolean forAnnotations) { return getReferenceName(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing to define return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public FunctionType getConstructor() { return null; } @Override public String getReferenceName() { return isChecked ? "??" : "?"; } @Override public String getDisplayName() { return "Unknown"; } @Override public boolean hasDisplayName() { return true; } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.BOTH; } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { return this; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/FunctionBuilder.java0000644000175000017500000001243612115204405027743 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.Node; /** * A builder class for function and arrow types. * * If you need to build an interface constructor, * use {@link JSTypeRegistry#createInterfaceType}. * * @author nicksantos@google.com (Nick Santos) */ public final class FunctionBuilder { private final JSTypeRegistry registry; private String name = null; private Node sourceNode = null; private Node parametersNode = null; private JSType returnType = null; private JSType typeOfThis = null; private TemplateTypeMap templateTypeMap = null; private boolean inferredReturnType = false; private boolean isConstructor = false; private boolean isNativeType = false; public FunctionBuilder(JSTypeRegistry registry) { this.registry = registry; } /** Set the name of the function type. */ public FunctionBuilder withName(String name) { this.name = name; return this; } /** Set the source node of the function type. */ public FunctionBuilder withSourceNode(Node sourceNode) { this.sourceNode = sourceNode; return this; } /** Set the parameters of the function type from a FunctionParamBuilder. */ public FunctionBuilder withParams(FunctionParamBuilder params) { this.parametersNode = params.build(); return this; } /** * Set the parameters of the function type with a specially-formatted node. */ public FunctionBuilder withParamsNode(Node parametersNode) { this.parametersNode = parametersNode; return this; } /** Set the return type. */ public FunctionBuilder withReturnType(JSType returnType) { this.returnType = returnType; return this; } /** Set the return type and whether it's inferred. */ public FunctionBuilder withReturnType(JSType returnType, boolean inferred) { this.returnType = returnType; this.inferredReturnType = inferred; return this; } /** Sets an inferred return type. */ public FunctionBuilder withInferredReturnType(JSType returnType) { this.returnType = returnType; this.inferredReturnType = true; return this; } /** Set the "this" type. */ public FunctionBuilder withTypeOfThis(JSType typeOfThis) { this.typeOfThis = typeOfThis; return this; } /** Set the template name. */ public FunctionBuilder withTemplateKeys( ImmutableList templateKeys) { this.templateTypeMap = registry.createTemplateTypeMap(templateKeys, null); return this; } /** Make this a constructor. */ public FunctionBuilder forConstructor() { this.isConstructor = true; return this; } /** Set whether this is a constructor. */ public FunctionBuilder setIsConstructor(boolean isConstructor) { this.isConstructor = isConstructor; return this; } /** Make this a native type. */ FunctionBuilder forNativeType() { this.isNativeType = true; return this; } /** Copies all the information from another function type. */ public FunctionBuilder copyFromOtherFunction(FunctionType otherType) { this.name = otherType.getReferenceName(); this.sourceNode = otherType.getSource(); this.parametersNode = otherType.getParametersNode(); this.returnType = otherType.getReturnType(); this.typeOfThis = otherType.getTypeOfThis(); this.templateTypeMap = otherType.getTemplateTypeMap(); this.isConstructor = otherType.isConstructor(); this.isNativeType = otherType.isNativeObjectType(); return this; } /** Construct a new function type. */ public FunctionType build() { return new FunctionType(registry, name, sourceNode, new ArrowType(registry, parametersNode, returnType, inferredReturnType), typeOfThis, templateTypeMap, isConstructor, isNativeType); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/RecordType.java0000644000175000017500000002302012115204405026716 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.RecordTypeBuilder.RecordProperty; import java.util.Map; import java.util.Set; /** * A record (structural) type. * * Subtyping: The subtyping of a record type is defined via structural * comparison of a record type's properties. For example, a record * type of the form { a : TYPE_1 } is a supertype of a record type * of the form { b : TYPE_2, a : TYPE_1 } because B can be assigned to * A and matches all constraints. Similarly, a defined type can be assigned * to a record type so long as that defined type matches all property * constraints of the record type. A record type of the form { a : A, b : B } * can be assigned to a record of type { a : A }. * */ class RecordType extends PrototypeObjectType { private static final long serialVersionUID = 1L; private final boolean declared; private boolean isFrozen = false; RecordType(JSTypeRegistry registry, Map properties) { this(registry, properties, true); } /** * Creates a record type. * * @param registry The type registry under which this type lives. * @param properties A map of all the properties of this record type. * @param declared Whether this is a declared or synthesized type. * A synthesized record type is just used for bookkeeping * in the type system. A declared record type was actually used in the * user's program. * @throws IllegalStateException if the {@code RecordProperty} associated * with a property is null. */ RecordType(JSTypeRegistry registry, Map properties, boolean declared) { super(registry, null, null); setPrettyPrint(true); this.declared = declared; for (String property : properties.keySet()) { RecordProperty prop = properties.get(property); if (prop == null) { throw new IllegalStateException( "RecordProperty associated with a property should not be null!"); } if (declared) { defineDeclaredProperty( property, prop.getType(), prop.getPropertyNode()); } else { defineSynthesizedProperty( property, prop.getType(), prop.getPropertyNode()); } } // Freeze the record type. isFrozen = true; } /** @return Is this synthesized for internal bookkeeping? */ boolean isSynthetic() { return !declared; } boolean checkRecordEquivalenceHelper( RecordType otherRecord, EquivalenceMethod eqMethod) { Set keySet = getOwnPropertyNames(); Set otherKeySet = otherRecord.getOwnPropertyNames(); if (!otherKeySet.equals(keySet)) { return false; } for (String key : keySet) { if (!otherRecord.getPropertyType(key).checkEquivalenceHelper( getPropertyType(key), eqMethod)) { return false; } } return true; } @Override public ObjectType getImplicitPrototype() { return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (isFrozen) { return false; } return super.defineProperty(propertyName, type, inferred, propertyNode); } JSType getGreatestSubtypeHelper(JSType that) { if (that.isRecordType()) { RecordType thatRecord = that.toMaybeRecordType(); RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.setSynthesized(true); // The greatest subtype consists of those *unique* properties of both // record types. If any property conflicts, then the NO_TYPE type // is returned. for (String property : getOwnPropertyNames()) { if (thatRecord.hasProperty(property) && !thatRecord.getPropertyType(property).isInvariant( getPropertyType(property))) { return registry.getNativeObjectType(JSTypeNative.NO_TYPE); } builder.addProperty(property, getPropertyType(property), getPropertyNode(property)); } for (String property : thatRecord.getOwnPropertyNames()) { if (!hasProperty(property)) { builder.addProperty(property, thatRecord.getPropertyType(property), thatRecord.getPropertyNode(property)); } } return builder.build(); } JSType greatestSubtype = registry.getNativeType( JSTypeNative.NO_OBJECT_TYPE); JSType thatRestrictedToObj = registry.getNativeType(JSTypeNative.OBJECT_TYPE) .getGreatestSubtype(that); if (!thatRestrictedToObj.isEmptyType()) { // In this branch, the other type is some object type. We find // the greatest subtype with the following algorithm: // 1) For each property "x" of this record type, take the union // of all classes with a property "x" with a compatible property type. // and which are a subtype of {@code that}. // 2) Take the intersection of all of these unions. for (String propName : getOwnPropertyNames()) { JSType propType = getPropertyType(propName); UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (ObjectType alt : registry.getEachReferenceTypeWithProperty(propName)) { JSType altPropType = alt.getPropertyType(propName); if (altPropType != null && !alt.isEquivalentTo(this) && alt.isSubtype(that) && propType.isInvariant(altPropType)) { builder.addAlternate(alt); } } greatestSubtype = greatestSubtype.getLeastSupertype(builder.build()); } } return greatestSubtype; } @Override RecordType toMaybeRecordType() { return this; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Top of the record types is the empty record, or OBJECT_TYPE. if (registry.getNativeObjectType( JSTypeNative.OBJECT_TYPE).isSubtype(that)) { return true; } // A type is a subtype of a record type if it itself is a record // type and it has at least the same members as the parent record type // with the same types. if (!that.isRecordType()) { return false; } return RecordType.isSubtype(this, that.toMaybeRecordType()); } /** Determines if typeA is a subtype of typeB */ static boolean isSubtype(ObjectType typeA, RecordType typeB) { // typeA is a subtype of record type typeB iff: // 1) typeA has all the properties declared in typeB. // 2) And for each property of typeB, // 2a) if the property of typeA is declared, it must be equal // to the type of the property of typeB, // 2b) otherwise, it must be a subtype of the property of typeB. // // To figure out why this is true, consider the following pseudo-code: // /** @type {{a: (Object,null)}} */ var x; // /** @type {{a: !Object}} */ var y; // var z = {a: {}}; // x.a = null; // // y cannot be assigned to x, because line 4 would violate y's declared // properties. But z can be assigned to x. Even though z and y are the // same type, the properties of z are inferred--and so an assignment // to the property of z would not violate any restrictions on it. for (String property : typeB.getOwnPropertyNames()) { if (!typeA.hasProperty(property)) { return false; } JSType propA = typeA.getPropertyType(property); JSType propB = typeB.getPropertyType(property); if (typeA.isPropertyTypeDeclared(property)) { // If one declared property isn't invariant, // then the whole record isn't covariant. if (!propA.isInvariant(propB)) { return false; } } else { // If one inferred property isn't a subtype, // then the whole record isn't covariant. if (!propA.isSubtype(propB)) { return false; } } } return true; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/Visitor.java0000644000175000017500000000563512115204405026311 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; /** * A type visitor.

      * * This code will calculate a specific value of type {@code T} from a type * based on its structure: * *

      JSType type = …;
       * T value = type.visit(new Visitor<T>() {
       *   …
       * });
      * */ public interface Visitor { /** * Bottom type's case. */ T caseNoType(); /** * Enum element type's case. */ T caseEnumElementType(EnumElementType type); /** * All type's case. */ T caseAllType(); /** * Boolean value type's case. */ T caseBooleanType(); /** * Bottom Object type's case. */ T caseNoObjectType(); /** * Function type's case. */ T caseFunctionType(FunctionType type); /** * Object type's case. */ T caseObjectType(ObjectType type); /** * Unknown type's case. */ T caseUnknownType(); /** * Null type's case. */ T caseNullType(); /** * Number value type's case. */ T caseNumberType(); /** * String value type's case. */ T caseStringType(); /** * Void type's case. */ T caseVoidType(); /** * Union type's case. */ T caseUnionType(UnionType type); /** * Templatized type's case. */ T caseTemplatizedType(TemplatizedType type); /** * Template type's case. */ T caseTemplateType(TemplateType templateType); } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/UnresolvedTypeExpression.java0000644000175000017500000000565612115204405031725 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.common.base.Preconditions; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.Node; /** * An {@code UnresolvedType} is a reference to some type expression. * This provides a convenient mechanism for implementing forward * references to types; a {@code UnresolvedType} can be used as a * placeholder until its reference is resolved. * * The {@code UnresolvedType} will behave like an opaque unknown type. * When its {@code #resolve} method is called, it will return the underlying * type. The underlying type can resolve to any JS type.

      * * @author nicksantos@google.com (Nick Santos) */ class UnresolvedTypeExpression extends UnknownType { private static final long serialVersionUID = 1L; private final Node typeExpr; private final String sourceName; /** * Create a named type based on the reference. */ UnresolvedTypeExpression(JSTypeRegistry registry, Node typeExpr, String sourceName) { super(registry, false); Preconditions.checkNotNull(typeExpr); this.typeExpr = typeExpr; this.sourceName = sourceName; } /** * Resolve the referenced type within the enclosing scope. */ @Override JSType resolveInternal(ErrorReporter t, StaticScope enclosing) { return registry.createFromTypeNodes(typeExpr, sourceName, enclosing); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/jstype/NoObjectType.java0000644000175000017500000001057012115204405027211 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; /** * The bottom Object type, representing the subclass of all objects. * * Although JavaScript programmers can't explicitly denote the bottom * Object type, it comes up in static analysis. For example, if we have: * * var x = function() {}; * if (x instanceof Array) { * f(x); * } * * We need to be able to assign {@code x} a type within the {@code f(x)} * call. It has no possible type, but {@code x} would not be legal if f * expected a string. So we assign it the {@code NoObjectType}. * * @see Bottom types */ public class NoObjectType extends FunctionType { private static final long serialVersionUID = 1L; NoObjectType(JSTypeRegistry registry) { super(registry, null, null, registry.createArrowType(null, null), null, null, true, true); getInternalArrowType().returnType = this; this.setInstanceType(this); } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return that.isObject() && !that.isNoType() && !that.isNoResolvedType(); } } @Override public FunctionType toMaybeFunctionType() { return null; } @Override public boolean isNoObjectType() { return true; } @Override public ObjectType getImplicitPrototype() { return null; } @Override public String getReferenceName() { return null; } @Override public boolean matchesNumberContext() { return true; } @Override public boolean matchesObjectContext() { return true; } @Override public boolean matchesStringContext() { return true; } @Override public int hashCode() { return System.identityHashCode(this); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing, all properties are defined return true; } @Override public boolean removeProperty(String name) { return false; } @Override public void setPropertyJSDocInfo(String propertyName, JSDocInfo info) { // Do nothing, specific properties do not have JSDocInfo. } @Override public T visit(Visitor visitor) { return visitor.caseNoObjectType(); } @Override T visit(RelationshipVisitor visitor, JSType that) { return visitor.caseNoObjectType(that); } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoObject"; } @Override public FunctionType getConstructor() { return null; } @Override JSType resolveInternal(ErrorReporter t, StaticScope scope) { return this; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/JSDocInfoBuilder.java0000644000175000017500000007312212115204405026415 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.util.List; import java.util.Set; /** * A builder for {@link JSDocInfo} objects. This builder abstracts the * construction process of {@link JSDocInfo} objects whilst minimizing the * number of instances of {@link JSDocInfo} objects. It provides early * incompatibility detection among properties stored on the {@code JSDocInfo} * object being created. * */ final public class JSDocInfoBuilder { // the current JSDoc which is being populated private JSDocInfo currentInfo; // whether the current JSDocInfo has valuable information private boolean populated = false; // whether to include the documentation itself when parsing the JsDoc private boolean parseDocumentation = false; // the current marker, if any. private JSDocInfo.Marker currentMarker = null; public JSDocInfoBuilder(boolean parseDocumentation) { this.currentInfo = new JSDocInfo(parseDocumentation); this.parseDocumentation = parseDocumentation; } /** * Sets the original JSDoc comment string. This is a no-op if the builder * isn't configured to record documentation. */ public void recordOriginalCommentString(String sourceComment) { if (parseDocumentation) { currentInfo.setOriginalCommentString(sourceComment); } } public boolean shouldParseDocumentation() { return parseDocumentation; } /** * Returns whether this builder is populated with information that can be * used to {@link #build} a {@link JSDocInfo} object. */ public boolean isPopulated() { return populated; } /** * Returns whether this builder is populated with information that can be * used to {@link #build} a {@link JSDocInfo} object that has a * fileoverview tag. */ public boolean isPopulatedWithFileOverview() { return isPopulated() && (currentInfo.hasFileOverview() || currentInfo.isExterns() || currentInfo.isNoCompile()); } /** * Returns whether this builder recorded a description. */ public boolean isDescriptionRecorded() { return currentInfo.getDescription() != null; } /** * Builds a {@link JSDocInfo} object based on the populated information and * returns it. Once this method is called, the builder can be reused to build * another {@link JSDocInfo} object. * * @param associatedNode The source node containing the JSDoc. * @return a {@link JSDocInfo} object populated with the values given to this * builder. If no value was populated, this method simply returns * {@code null} */ public JSDocInfo build(Node associatedNode) { if (populated) { JSDocInfo built = currentInfo; built.setAssociatedNode(associatedNode); populateDefaults(built); populated = false; currentInfo = new JSDocInfo(this.parseDocumentation); return built; } else { return null; } } /** Generate defaults when certain parameters are not specified. */ private static void populateDefaults(JSDocInfo info) { if (info.getVisibility() == null) { info.setVisibility(Visibility.INHERITED); } } /** * Adds a marker to the current JSDocInfo and populates the marker with the * annotation information. */ public void markAnnotation(String annotation, int lineno, int charno) { JSDocInfo.Marker marker = currentInfo.addMarker(); if (marker != null) { JSDocInfo.TrimmedStringPosition position = new JSDocInfo.TrimmedStringPosition(); position.setItem(annotation); position.setPositionInformation(lineno, charno, lineno, charno + annotation.length()); marker.setAnnotation(position); populated = true; } currentMarker = marker; } /** * Adds a textual block to the current marker. */ public void markText(String text, int startLineno, int startCharno, int endLineno, int endCharno) { if (currentMarker != null) { JSDocInfo.StringPosition position = new JSDocInfo.StringPosition(); position.setItem(text); position.setPositionInformation(startLineno, startCharno, endLineno, endCharno); currentMarker.setDescription(position); } } /** * Adds a type declaration to the current marker. */ public void markTypeNode(Node typeNode, int lineno, int startCharno, int endLineno, int endCharno, boolean hasLC) { if (currentMarker != null) { JSDocInfo.TypePosition position = new JSDocInfo.TypePosition(); position.setItem(typeNode); position.setHasBrackets(hasLC); position.setPositionInformation(lineno, startCharno, endLineno, endCharno); currentMarker.setType(position); } } /** * Adds a name declaration to the current marker. * @deprecated Use #markName(String, StaticSourceFile, int, int) */ @Deprecated public void markName(String name, int lineno, int charno) { markName(name, null, lineno, charno); } /** * Adds a name declaration to the current marker. */ public void markName(String name, StaticSourceFile file, int lineno, int charno) { if (currentMarker != null) { // Record the name as both a SourcePosition and a // SourcePosition. The form is deprecated, // because is more consistent with how other name // references are handled (see #markTypeNode) // // TODO(nicksantos): Remove all uses of the Name position // and replace them with the NameNode position. JSDocInfo.TrimmedStringPosition position = new JSDocInfo.TrimmedStringPosition(); position.setItem(name); position.setPositionInformation(lineno, charno, lineno, charno + name.length()); currentMarker.setName(position); SourcePosition nodePos = new JSDocInfo.NamePosition(); Node node = Node.newString(Token.NAME, name, lineno, charno); node.setLength(name.length()); node.setStaticSourceFile(file); nodePos.setItem(node); nodePos.setPositionInformation(lineno, charno, lineno, charno + name.length()); currentMarker.setNameNode(nodePos); } } /** * Records a block-level description. * * @return {@code true} if the description was recorded. */ public boolean recordBlockDescription(String description) { populated = true; return currentInfo.documentBlock(description); } /** * Records a visibility. * * @return {@code true} if the visibility was recorded and {@code false} * if it was already defined */ public boolean recordVisibility(Visibility visibility) { if (currentInfo.getVisibility() == null) { populated = true; currentInfo.setVisibility(visibility); return true; } else { return false; } } /** * Records a typed parameter. * * @return {@code true} if the typed parameter was recorded and * {@code false} if a parameter with the same name was already defined */ public boolean recordParameter(String parameterName, JSTypeExpression type) { if (!hasAnySingletonTypeTags() && currentInfo.declareParam(type, parameterName)) { populated = true; return true; } else { return false; } } /** * Records a parameter's description. * * @return {@code true} if the parameter's description was recorded and * {@code false} if a parameter with the same name was already defined */ public boolean recordParameterDescription( String parameterName, String description) { if (currentInfo.documentParam(parameterName, description)) { populated = true; return true; } else { return false; } } /** * Records a template type name. * * @return {@code true} if the template type name was recorded and * {@code false} if a template type name was already defined. */ public boolean recordTemplateTypeNames(List names) { if (currentInfo.declareTemplateTypeNames(names)) { populated = true; return true; } else { return false; } } /** * Records a template type name. * * @return {@code true} if the template type name was recorded and * {@code false} if a template type name was already defined. */ public boolean recordClassTemplateTypeNames(List names) { if (currentInfo.declareClassTemplateTypeNames(names)) { populated = true; return true; } else { return false; } } /** * Records a thrown type. */ public boolean recordThrowType(JSTypeExpression type) { if (!hasAnySingletonTypeTags()) { currentInfo.declareThrows(type); populated = true; return true; } return false; } /** * Records a throw type's description. * * @return {@code true} if the type's description was recorded and * {@code false} if a description with the same type was already defined */ public boolean recordThrowDescription( JSTypeExpression type, String description) { if (currentInfo.documentThrows(type, description)) { populated = true; return true; } else { return false; } } /** * Adds an author to the current information. */ public boolean addAuthor(String author) { if (currentInfo.documentAuthor(author)) { populated = true; return true; } else { return false; } } /** * Adds a reference ("@see") to the current information. */ public boolean addReference(String reference) { if (currentInfo.documentReference(reference)) { populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isConsistentIdGenerator()} flag set to * {@code true}. * * @return {@code true} if the consistentIdGenerator flag was recorded and * {@code false} if it was already recorded */ public boolean recordConsistentIdGenerator() { if (!currentInfo.isConsistentIdGenerator()) { currentInfo.setConsistentIdGenerator(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its {@link * JSDocInfo#isStableIdGenerator()} flag set to {@code true}. * * @return {@code true} if the stableIdGenerator flag was recorded and {@code false} if it was * already recorded. */ public boolean recordStableIdGenerator() { if (!currentInfo.isStableIdGenerator()) { currentInfo.setStableIdGenerator(true); populated = true; return true; } else { return false; } } /** * Records the version. */ public boolean recordVersion(String version) { if (currentInfo.documentVersion(version)) { populated = true; return true; } else { return false; } } /** * Records the deprecation reason. */ public boolean recordDeprecationReason(String reason) { if (currentInfo.setDeprecationReason(reason)) { populated = true; return true; } else { return false; } } /** * Records the list of suppressed warnings. */ public boolean recordSuppressions(Set suppressions) { if (currentInfo.setSuppressions(suppressions)) { populated = true; return true; } else { return false; } } /** * Records the list of modifies warnings. */ public boolean recordModifies(Set modifies) { if (!hasAnySingletonSideEffectTags() && currentInfo.setModifies(modifies)) { populated = true; return true; } else { return false; } } /** * Records a type. * * @return {@code true} if the type was recorded and {@code false} if * it is invalid or was already defined */ public boolean recordType(JSTypeExpression type) { if (type != null && !hasAnyTypeRelatedTags()) { currentInfo.setType(type); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should be populated * with a {@code typedef}'d type. */ public boolean recordTypedef(JSTypeExpression type) { if (type != null && !hasAnyTypeRelatedTags()) { currentInfo.setTypedefType(type); populated = true; return true; } return false; } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isIdGenerator()} flag set to * {@code true}. * * @return {@code true} if the idGenerator flag was recorded and {@code false} * if it was already recorded */ public boolean recordIdGenerator() { if (!currentInfo.isIdGenerator()) { currentInfo.setIdGenerator(true); populated = true; return true; } else { return false; } } /** * Records a return type. * * @return {@code true} if the return type was recorded and {@code false} if * it is invalid or was already defined */ public boolean recordReturnType(JSTypeExpression jsType) { if (jsType != null && currentInfo.getReturnType() == null && !hasAnySingletonTypeTags()) { currentInfo.setReturnType(jsType); populated = true; return true; } else { return false; } } /** * Records a return description * * @return {@code true} if the return description was recorded and * {@code false} if it is invalid or was already defined */ public boolean recordReturnDescription(String description) { if (currentInfo.documentReturn(description)) { populated = true; return true; } else { return false; } } /** * Records the type of a define. * * 'Define' values are special constants that may be manipulated by * the compiler. They are designed to mimic the #define command in * the C preprocessor. */ public boolean recordDefineType(JSTypeExpression type) { if (type != null && !currentInfo.isConstant() && !currentInfo.isDefine() && recordType(type)) { currentInfo.setDefine(true); populated = true; return true; } else { return false; } } /** * Records a parameter type to an enum. * * @return {@code true} if the enum's parameter type was recorded and * {@code false} if it was invalid or already defined */ public boolean recordEnumParameterType(JSTypeExpression type) { if (type != null && !hasAnyTypeRelatedTags()) { currentInfo.setEnumParameterType(type); populated = true; return true; } else { return false; } } /** * Records a type for {@code @this} annotation. * * @return {@code true} if the type was recorded and * {@code false} if it is invalid or if it collided with {@code @enum} or * {@code @type} annotations */ public boolean recordThisType(JSTypeExpression type) { if (type != null && !hasAnySingletonTypeTags() && !currentInfo.hasThisType()) { currentInfo.setThisType(type); populated = true; return true; } else { return false; } } /** * Records a base type. * * @return {@code true} if the base type was recorded and {@code false} * if it was already defined */ public boolean recordBaseType(JSTypeExpression jsType) { if (jsType != null && !hasAnySingletonTypeTags() && !currentInfo.hasBaseType()) { currentInfo.setBaseType(jsType); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isConstant()} flag set to {@code true}. * * @return {@code true} if the constancy was recorded and {@code false} * if it was already defined */ public boolean recordConstancy() { if (!currentInfo.isConstant()) { currentInfo.setConstant(true); populated = true; return true; } else { return false; } } /** * Records a description giving context for translation (i18n). * * @return {@code true} if the description was recorded and {@code false} * if the description was invalid or was already defined */ public boolean recordDescription(String description) { if (description != null && currentInfo.getDescription() == null) { currentInfo.setDescription(description); populated = true; return true; } else { return false; } } /** * Records a meaning giving context for translation (i18n). Different * meanings will result in different translations. * * @return {@code true} If the meaning was successfully updated. */ public boolean recordMeaning(String meaning) { if (meaning != null && currentInfo.getMeaning() == null) { currentInfo.setMeaning(meaning); populated = true; return true; } else { return false; } } /** * Records a fileoverview description. * * @return {@code true} if the description was recorded and {@code false} * if the description was invalid or was already defined. */ public boolean recordFileOverview(String description) { if (currentInfo.documentFileOverview(description)) { populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isHidden()} flag set to {@code true}. * * @return {@code true} if the hiddenness was recorded and {@code false} * if it was already defined */ public boolean recordHiddenness() { if (!currentInfo.isHidden()) { currentInfo.setHidden(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isNoCompile()} flag set to {@code true}. * * @return {@code true} if the no compile flag was recorded and {@code false} * if it was already recorded */ public boolean recordNoCompile() { if (!currentInfo.isNoCompile()) { currentInfo.setNoCompile(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isNoTypeCheck()} flag set to {@code true}. * * @return {@code true} if the no check flag was recorded and {@code false} * if it was already recorded */ public boolean recordNoTypeCheck() { if (!currentInfo.isNoTypeCheck()) { currentInfo.setNoCheck(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isConstructor()} flag set to {@code true}. * * @return {@code true} if the constructor was recorded and {@code false} * if it was already defined or it was incompatible with the existing * flags */ public boolean recordConstructor() { if (!hasAnySingletonTypeTags() && !currentInfo.isConstructor() && !currentInfo.isInterface()) { currentInfo.setConstructor(true); populated = true; return true; } else { return false; } } /** * Whether the {@link JSDocInfo} being built will have its * {@link JSDocInfo#isConstructor()} flag set to {@code true}. */ public boolean isConstructorRecorded() { return currentInfo.isConstructor(); } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#makesStructs()} flag set to {@code true}. * * @return {@code true} if the struct was recorded and {@code false} * if it was already defined or it was incompatible with the existing flags */ public boolean recordStruct() { if (hasAnySingletonTypeTags() || currentInfo.isInterface() || currentInfo.makesDicts() || currentInfo.makesStructs()) { return false; } currentInfo.setStruct(); populated = true; return true; } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#makesDicts()} flag set to {@code true}. * * @return {@code true} if the dict was recorded and {@code false} * if it was already defined or it was incompatible with the existing flags */ public boolean recordDict() { if (hasAnySingletonTypeTags() || currentInfo.isInterface() || currentInfo.makesDicts() || currentInfo.makesStructs()) { return false; } currentInfo.setDict(); populated = true; return true; } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isJavaDispatch()} flag set to {@code true}. * * @return {@code true} if the javadispatch was recorded and {@code false} * if it was already defined or it was incompatible with the existing * flags */ public boolean recordJavaDispatch() { if (!currentInfo.isJavaDispatch()) { currentInfo.setJavaDispatch(true); populated = true; return true; } else { return false; } } /** * Whether the {@link JSDocInfo} being built will have its * {@link JSDocInfo#isJavaDispatch()} flag set to {@code true}. */ public boolean isJavaDispatch() { return currentInfo.isJavaDispatch(); } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#shouldPreserveTry()} flag set to {@code true}. */ public boolean recordPreserveTry() { if (!currentInfo.shouldPreserveTry()) { currentInfo.setShouldPreserveTry(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isOverride()} flag set to {@code true}. */ public boolean recordOverride() { if (!currentInfo.isOverride()) { currentInfo.setOverride(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isNoAlias()} flag set to {@code true}. */ public boolean recordNoAlias() { if (!currentInfo.isNoAlias()) { currentInfo.setNoAlias(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isDeprecated()} flag set to {@code true}. */ public boolean recordDeprecated() { if (!currentInfo.isDeprecated()) { currentInfo.setDeprecated(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isInterface()} flag set to {@code true}. * * @return {@code true} if the flag was recorded and {@code false} * if it was already defined or it was incompatible with the existing flags */ public boolean recordInterface() { if (hasAnySingletonTypeTags() || currentInfo.makesStructs() || currentInfo.makesDicts() || currentInfo.isConstructor() || currentInfo.isInterface()) { return false; } currentInfo.setInterface(true); populated = true; return true; } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isExport()} flag set to {@code true}. */ public boolean recordExport() { if (!currentInfo.isExport()) { currentInfo.setExport(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isExpose()} flag set to {@code true}. */ public boolean recordExpose() { if (!currentInfo.isExpose()) { currentInfo.setExpose(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isNoShadow()} flag set to {@code true}. */ public boolean recordNoShadow() { if (!currentInfo.isNoShadow()) { currentInfo.setNoShadow(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isImplicitCast()} flag set to {@code true}. */ public boolean recordImplicitCast() { if (!currentInfo.isImplicitCast()) { currentInfo.setImplicitCast(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isNoSideEffects()} flag set to {@code true}. */ public boolean recordNoSideEffects() { if (!hasAnySingletonSideEffectTags() && !currentInfo.isNoSideEffects()) { currentInfo.setNoSideEffects(true); populated = true; return true; } else { return false; } } /** * Records that the {@link JSDocInfo} being built should have its * {@link JSDocInfo#isExterns()} flag set to {@code true}. */ public boolean recordExterns() { if (!currentInfo.isExterns()) { currentInfo.setExterns(true); populated = true; return true; } else { return false; } } /** * Whether the {@link JSDocInfo} being built will have its * {@link JSDocInfo#isInterface()} flag set to {@code true}. */ public boolean isInterfaceRecorded() { return currentInfo.isInterface(); } /** * @return Whether a parameter of the given name has already been recorded. */ public boolean hasParameter(String name) { return currentInfo.hasParameter(name); } /** * Records an implemented interface. */ public boolean recordImplementedInterface(JSTypeExpression interfaceName) { if (currentInfo.addImplementedInterface(interfaceName)) { populated = true; return true; } else { return false; } } /** * Records an extended interface type. */ public boolean recordExtendedInterface(JSTypeExpression interfaceType) { if (currentInfo.addExtendedInterface(interfaceType)) { populated = true; return true; } else { return false; } } /** * Records that we're lending to another name. */ public boolean recordLends(String name) { if (!hasAnyTypeRelatedTags()) { currentInfo.setLendsName(name); populated = true; return true; } else { return false; } } /** * Returns whether current JSDoc is annotated with {@code @ngInject}. */ public boolean isNgInjectRecorded() { return currentInfo.isNgInject(); } /** * Records that we'd like to add {@code $inject} property inferred from * parameters. */ public boolean recordNgInject(boolean ngInject) { if (!isNgInjectRecorded()) { currentInfo.setNgInject(ngInject); populated = true; return true; } else { return false; } } /** * Whether the current doc info has other type tags, like * {@code @param} or {@code @return} or {@code @type} or etc. */ private boolean hasAnyTypeRelatedTags() { return currentInfo.isConstructor() || currentInfo.isInterface() || currentInfo.getParameterCount() > 0 || currentInfo.hasReturnType() || currentInfo.hasBaseType() || currentInfo.getExtendedInterfacesCount() > 0 || currentInfo.getLendsName() != null || currentInfo.hasThisType() || hasAnySingletonTypeTags(); } /** * Whether the current doc info has any of the singleton type * tags that may not appear with other type tags, like * {@code @type} or {@code @typedef}. */ private boolean hasAnySingletonTypeTags() { return currentInfo.hasType() || currentInfo.hasTypedefType() || currentInfo.hasEnumParameterType(); } /** * Whether the current doc info has any of the singleton type * tags that may not appear with other type tags, like * {@code @type} or {@code @typedef}. */ private boolean hasAnySingletonSideEffectTags() { return currentInfo.isNoSideEffects() || currentInfo.hasModifies(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/JSTypeExpression.java0000644000175000017500000000732312115204405026566 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.StaticScope; import java.io.Serializable; /** * Represents a type expression as a miniature Rhino AST, so that the * type expression can be evaluated later. * * @author nicksantos@google.com (Nick Santos) */ public final class JSTypeExpression implements Serializable { private static final long serialVersionUID = 1L; /** The root of the AST. */ private final Node root; /** The source name where the type expression appears. */ private final String sourceName; public JSTypeExpression(Node root, String sourceName) { this.root = root; this.sourceName = sourceName; } /** * Make the given type expression into an optional type expression, * if possible. */ public static JSTypeExpression makeOptionalArg(JSTypeExpression expr) { if (expr.isOptionalArg() || expr.isVarArgs()) { return expr; } else { return new JSTypeExpression( new Node(Token.EQUALS, expr.root), expr.sourceName); } } /** * @return Whether this expression denotes an optional {@code @param}. */ public boolean isOptionalArg() { return root.getType() == Token.EQUALS; } /** * @return Whether this expression denotes a rest args {@code @param}. */ public boolean isVarArgs() { return root.getType() == Token.ELLIPSIS; } /** * Evaluates the type expression into a {@code JSType} object. */ public JSType evaluate(StaticScope scope, JSTypeRegistry registry) { JSType type = registry.createFromTypeNodes(root, sourceName, scope); root.setJSType(type); return type; } @Override public boolean equals(Object other) { return other instanceof JSTypeExpression && ((JSTypeExpression) other).root.isEquivalentTo(root); } @Override public int hashCode() { return root.toStringTree().hashCode(); } /** * @return The source for this type expression. Note that it will not * contain an expression if there's an @override tag. */ public Node getRoot() { return root; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/Node.java0000644000175000017500000017737612115204405024235 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Roger Lawrence * Mike McCabe * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.SimpleSourceFile; import com.google.javascript.rhino.jstype.StaticSourceFile; import java.io.IOException; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; /** * This class implements the root of the intermediate representation. * */ public class Node implements Cloneable, Serializable { private static final long serialVersionUID = 1L; public static final int JSDOC_INFO_PROP = 29, // contains a TokenStream.JSDocInfo object VAR_ARGS_NAME = 30, // the name node is a variable length // argument placeholder. INCRDECR_PROP = 32, // pre or post type of increment/decrement QUOTED_PROP = 36, // set to indicate a quoted object lit key OPT_ARG_NAME = 37, // The name node is an optional argument. SYNTHETIC_BLOCK_PROP = 38, // A synthetic block. Used to make // processing simpler, and does not // represent a real block in the source. EMPTY_BLOCK = 39, // Used to indicate BLOCK that replaced // EMPTY nodes. ORIGINALNAME_PROP = 40, // The original name of the node, before // renaming. SIDE_EFFECT_FLAGS = 42, // Function or constructor call side effect // flags // Coding convention props IS_CONSTANT_NAME = 43, // The variable or property is constant. IS_NAMESPACE = 46, // The variable creates a namespace. IS_DISPATCHER = 47, // The function is a dispatcher function, // probably generated from Java code, and // should be resolved to the proper // overload if possible. DIRECTIVES = 48, // The ES5 directives on this node. DIRECT_EVAL = 49, // ES5 distinguishes between direct and // indirect calls to eval. FREE_CALL = 50, // A CALL without an explicit "this" value. STATIC_SOURCE_FILE = 51, // A StaticSourceFile indicating the file // where this node lives. LENGTH = 52, // The length of the code represented by // this node. INPUT_ID = 53, // The id of the input associated with this // node. SLASH_V = 54, // Whether a STRING node contains a \v // vertical tab escape. This is a total hack. // See comments in IRFactory about this. INFERRED_FUNCTION = 55, // Marks a function whose parameter types // have been inferred. LAST_PROP = 55; public static final int // flags for INCRDECR_PROP DECR_FLAG = 0x1, POST_FLAG = 0x2; private static final String propToString(int propType) { switch (propType) { case VAR_ARGS_NAME: return "var_args_name"; case JSDOC_INFO_PROP: return "jsdoc_info"; case INCRDECR_PROP: return "incrdecr"; case QUOTED_PROP: return "quoted"; case OPT_ARG_NAME: return "opt_arg"; case SYNTHETIC_BLOCK_PROP: return "synthetic"; case EMPTY_BLOCK: return "empty_block"; case ORIGINALNAME_PROP: return "originalname"; case SIDE_EFFECT_FLAGS: return "side_effect_flags"; case IS_CONSTANT_NAME: return "is_constant_name"; case IS_NAMESPACE: return "is_namespace"; case IS_DISPATCHER: return "is_dispatcher"; case DIRECTIVES: return "directives"; case DIRECT_EVAL: return "direct_eval"; case FREE_CALL: return "free_call"; case STATIC_SOURCE_FILE: return "source_file"; case INPUT_ID: return "input_id"; case LENGTH: return "length"; case SLASH_V: return "slash_v"; case INFERRED_FUNCTION: return "inferred"; default: throw new IllegalStateException("unexpect prop id " + propType); } } private static class NumberNode extends Node { private static final long serialVersionUID = 1L; NumberNode(double number) { super(Token.NUMBER); this.number = number; } public NumberNode(double number, int lineno, int charno) { super(Token.NUMBER, lineno, charno); this.number = number; } @Override public double getDouble() { return this.number; } @Override public void setDouble(double d) { this.number = d; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { boolean equivalent = super.isEquivalentTo(node, compareJsType, recurse); if (equivalent) { double thisValue = getDouble(); double thatValue = ((NumberNode) node).getDouble(); if (thisValue == thatValue) { // detect the difference between 0.0 and -0.0. return (thisValue != 0.0) || (1/thisValue == 1/thatValue); } } return false; } private double number; } private static class StringNode extends Node { private static final long serialVersionUID = 1L; StringNode(int type, String str) { super(type); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } StringNode(int type, String str, int lineno, int charno) { super(type, lineno, charno); if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } /** * returns the string content. * @return non null. */ @Override public String getString() { return this.str; } /** * sets the string content. * @param str the new value. Non null. */ @Override public void setString(String str) { if (null == str) { throw new IllegalArgumentException("StringNode: str is null"); } this.str = str; } @Override boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { return (super.isEquivalentTo(node, compareJsType, recurse) && this.str.equals(((StringNode) node).str)); } /** * If the property is not defined, this was not a quoted key. The * QUOTED_PROP int property is only assigned to STRING tokens used as * object lit keys. * @return true if this was a quoted string key in an object literal. */ @Override public boolean isQuotedString() { return getBooleanProp(QUOTED_PROP); } /** * This should only be called for STRING nodes created in object lits. */ @Override public void setQuotedString() { putBooleanProp(QUOTED_PROP, true); } private String str; } // PropListItems must be immutable so that they can be shared. private interface PropListItem { int getType(); PropListItem getNext(); PropListItem chain(PropListItem next); Object getObjectValue(); int getIntValue(); } private static abstract class AbstractPropListItem implements PropListItem, Serializable { private static final long serialVersionUID = 1L; private final PropListItem next; private final int propType; AbstractPropListItem(int propType, PropListItem next) { this.propType = propType; this.next = next; } @Override public int getType() { return propType; } @Override public PropListItem getNext() { return next; } @Override public abstract PropListItem chain(PropListItem next); } // A base class for Object storing props private static class ObjectPropListItem extends AbstractPropListItem { private static final long serialVersionUID = 1L; private final Object objectValue; ObjectPropListItem(int propType, Object objectValue, PropListItem next) { super(propType, next); this.objectValue = objectValue; } @Override public int getIntValue() { throw new UnsupportedOperationException(); } @Override public Object getObjectValue() { return objectValue; } @Override public String toString() { return objectValue == null ? "null" : objectValue.toString(); } @Override public PropListItem chain(PropListItem next) { return new ObjectPropListItem(getType(), objectValue, next); } } // A base class for int storing props private static class IntPropListItem extends AbstractPropListItem { private static final long serialVersionUID = 1L; final int intValue; IntPropListItem(int propType, int intValue, PropListItem next) { super(propType, next); this.intValue = intValue; } @Override public int getIntValue() { return intValue; } @Override public Object getObjectValue() { throw new UnsupportedOperationException(); } @Override public String toString() { return String.valueOf(intValue); } @Override public PropListItem chain(PropListItem next) { return new IntPropListItem(getType(), intValue, next); } } public Node(int nodeType) { type = nodeType; parent = null; sourcePosition = -1; } public Node(int nodeType, Node child) { Preconditions.checkArgument(child.parent == null, "new child has existing parent"); Preconditions.checkArgument(child.next == null, "new child has existing sibling"); type = nodeType; parent = null; first = last = child; child.next = null; child.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node right) { Preconditions.checkArgument(left.parent == null, "first new child has existing parent"); Preconditions.checkArgument(left.next == null, "first new child has existing sibling"); Preconditions.checkArgument(right.parent == null, "second new child has existing parent"); Preconditions.checkArgument(right.next == null, "second new child has existing sibling"); type = nodeType; parent = null; first = left; last = right; left.next = right; left.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = right; mid.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, Node left, Node mid, Node mid2, Node right) { Preconditions.checkArgument(left.parent == null); Preconditions.checkArgument(left.next == null); Preconditions.checkArgument(mid.parent == null); Preconditions.checkArgument(mid.next == null); Preconditions.checkArgument(mid2.parent == null); Preconditions.checkArgument(mid2.next == null); Preconditions.checkArgument(right.parent == null); Preconditions.checkArgument(right.next == null); type = nodeType; parent = null; first = left; last = right; left.next = mid; left.parent = this; mid.next = mid2; mid.parent = this; mid2.next = right; mid2.parent = this; right.next = null; right.parent = this; sourcePosition = -1; } public Node(int nodeType, int lineno, int charno) { type = nodeType; parent = null; sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node child, int lineno, int charno) { this(nodeType, child); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node right, int lineno, int charno) { this(nodeType, left, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node right, int lineno, int charno) { this(nodeType, left, mid, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node left, Node mid, Node mid2, Node right, int lineno, int charno) { this(nodeType, left, mid, mid2, right); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children, int lineno, int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument(children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) { return null; } Node n = first; while (n.next != child) { n = n.next; if (n == null) { throw new RuntimeException("node is not a child"); } } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public int getIndexOfChild(Node child) { Node n = first; int i = 0; while (n != null) { if (child == n) { return i; } n = n.next; i++; } return -1; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) { last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = null; if (last == null) { first = last = child; return; } last.next = child; last = child; } public void addChildrenToFront(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSib = children.getLastSibling(); lastSib.next = first; first = children; if (last == null) { last = lastSib; } } public void addChildrenToBack(Node children) { addChildrenAfter(children, getLastChild()); } /** * Add 'child' before 'node'. */ public void addChildBefore(Node newChild, Node node) { Preconditions.checkArgument(node != null && node.parent == this, "The existing child node of the parent should not be null."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); if (first == node) { newChild.parent = this; newChild.next = first; first = newChild; return; } Node prev = getChildBefore(node); addChildAfter(newChild, prev); } /** * Add 'child' after 'node'. */ public void addChildAfter(Node newChild, Node node) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); addChildrenAfter(newChild, node); } /** * Add all children after 'node'. */ public void addChildrenAfter(Node children, Node node) { Preconditions.checkArgument(node == null || node.parent == this); for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSibling = children.getLastSibling(); if (node != null) { Node oldNext = node.next; node.next = children; lastSibling.next = oldNext; if (node == last) { last = lastSibling; } } else { // Append to the beginning. if (first != null) { lastSibling.next = first; } else { last = lastSibling; } first = children; } } /** * Detach a child from its parent and siblings. */ public void removeChild(Node child) { Node prev = getChildBefore(child); if (prev == null) first = first.next; else prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; } /** * Detaches child from Node and replaces it with newChild. */ public void replaceChild(Node child, Node newChild) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(child); newChild.next = child.next; newChild.parent = this; if (child == first) { first = newChild; } else { Node prev = getChildBefore(child); prev.next = newChild; } if (child == last) last = newChild; child.next = null; child.parent = null; } public void replaceChildAfter(Node prevChild, Node newChild) { Preconditions.checkArgument(prevChild.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(prevChild); Node child = prevChild.next; newChild.next = child.next; newChild.parent = this; prevChild.next = newChild; if (child == last) last = newChild; child.next = null; child.parent = null; } @VisibleForTesting PropListItem lookupProperty(int propType) { PropListItem x = propListHead; while (x != null && propType != x.getType()) { x = x.getNext(); } return x; } /** * Clone the properties from the provided node without copying * the property object. The receiving node may not have any * existing properties. * @param other The node to clone properties from. * @return this node. */ public Node clonePropsFrom(Node other) { Preconditions.checkState(this.propListHead == null, "Node has existing properties."); this.propListHead = other.propListHead; return this; } public void removeProp(int propType) { PropListItem result = removeProp(propListHead, propType); if (result != propListHead) { propListHead = result; } } /** * @param item The item to inspect * @param propType The property to look for * @return The replacement list if the property was removed, or * 'item' otherwise. */ private PropListItem removeProp(PropListItem item, int propType) { if (item == null) { return null; } else if (item.getType() == propType) { return item.getNext(); } else { PropListItem result = removeProp(item.getNext(), propType); if (result != item.getNext()) { return item.chain(result); } else { return item; } } } public Object getProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return null; } return item.getObjectValue(); } public boolean getBooleanProp(int propType) { return getIntProp(propType) != 0; } /** * Returns the integer value for the property, or 0 if the property * is not defined. */ public int getIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { return 0; } return item.getIntValue(); } public int getExistingIntProp(int propType) { PropListItem item = lookupProperty(propType); if (item == null) { throw new IllegalStateException("missing prop: " + propType); } return item.getIntValue(); } public void putProp(int propType, Object value) { removeProp(propType); if (value != null) { propListHead = createProp(propType, value, propListHead); } } public void putBooleanProp(int propType, boolean value) { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = createProp(propType, value, propListHead); } } PropListItem createProp(int propType, Object value, PropListItem next) { return new ObjectPropListItem(propType, value, next); } PropListItem createProp(int propType, int value, PropListItem next) { return new IntPropListItem(propType, value, next); } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count--; keys[count] = x.getType(); } Arrays.sort(keys); return keys; } /** Can only be called when getType() == TokenStream.NUMBER */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** * Can only be called when getType() == Token.NUMBER * @param value value to set. */ public void setDouble(double value) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** * Can only be called for a Token.STRING or Token.NAME. * @param value the value to set. */ public void setString(String value) throws UnsupportedOperationException { if (this.getType() == Token.STRING || this.getType() == Token.NAME) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append(""); } else { sb.append(first.getString()); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { default: value = x.toString(); break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append(" : "); sb.append(jsTypeString); } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuilder s = new StringBuilder(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Appendable appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the maximum column number that can * be represented. JSCompiler's modifications to Rhino cause all * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. */ public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1; /** * COLUMN_MASK stores a value where bits storing the column number * are set, and bits storing the line are not set. It's handy for * separating column number from line number. */ public static final int COLUMN_MASK = MAX_COLUMN_NUMBER; /** * Source position of this node. The position is encoded with the * column number in the low 12 bits of the integer, and the line * number in the rest. Create some handy constants so we can change this * size if we want. */ private int sourcePosition; private JSType jsType; private Node parent; //========================================================================== // Source position management public void setStaticSourceFile(StaticSourceFile file) { this.putProp(STATIC_SOURCE_FILE, file); } /** Sets the source file to a non-extern file of the given name. */ public void setSourceFileForTesting(String name) { this.putProp(STATIC_SOURCE_FILE, new SimpleSourceFile(name, false)); } public String getSourceFileName() { StaticSourceFile file = getStaticSourceFile(); return file == null ? null : file.getName(); } /** Returns the source file associated with this input. May be null */ public StaticSourceFile getStaticSourceFile() { return ((StaticSourceFile) this.getProp(STATIC_SOURCE_FILE)); } /** * @param inputId */ public void setInputId(InputId inputId) { this.putProp(INPUT_ID, inputId); } /** * @return The Id of the CompilerInput associated with this Node. */ public InputId getInputId() { return ((InputId) this.getProp(INPUT_ID)); } public boolean isFromExterns() { StaticSourceFile file = getStaticSourceFile(); return file == null ? false : file.isExtern(); } public int getLength() { return getIntProp(LENGTH); } public void setLength(int length) { putIntProp(LENGTH, length); } public int getLineno() { return extractLineno(sourcePosition); } public int getCharno() { return extractCharno(sourcePosition); } public int getSourceOffset() { StaticSourceFile file = getStaticSourceFile(); if (file == null) { return -1; } int lineno = getLineno(); if (lineno == -1) { return -1; } return file.getLineOffset(lineno) + getCharno(); } public int getSourcePosition() { return sourcePosition; } public void setLineno(int lineno) { int charno = getCharno(); if (charno == -1) { charno = 0; } sourcePosition = mergeLineCharNo(lineno, charno); } public void setCharno(int charno) { sourcePosition = mergeLineCharNo(getLineno(), charno); } public void setSourceEncodedPosition(int sourcePosition) { this.sourcePosition = sourcePosition; } public void setSourceEncodedPositionForTree(int sourcePosition) { this.sourcePosition = sourcePosition; for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.setSourceEncodedPositionForTree(sourcePosition); } } /** * Merges the line number and character number in one integer. The Character * number takes the first 12 bits and the line number takes the rest. If * the character number is greater than 212-1 it is * adjusted to 212-1. */ protected static int mergeLineCharNo(int lineno, int charno) { if (lineno < 0 || charno < 0) { return -1; } else if ((charno & ~COLUMN_MASK) != 0) { return lineno << COLUMN_BITS | COLUMN_MASK; } else { return lineno << COLUMN_BITS | (charno & COLUMN_MASK); } } /** * Extracts the line number and character number from a merged line char * number (see {@link #mergeLineCharNo(int, int)}). */ protected static int extractLineno(int lineCharNo) { if (lineCharNo == -1) { return -1; } else { return lineCharNo >>> COLUMN_BITS; } } /** * Extracts the character number and character number from a merged line * char number (see {@link #mergeLineCharNo(int, int)}). */ protected static int extractCharno(int lineCharNo) { if (lineCharNo == -1) { return -1; } else { return lineCharNo & COLUMN_MASK; } } //========================================================================== // Iteration /** *

      Return an iterable object that iterates over this node's children. * The iterator does not support the optional operation * {@link Iterator#remove()}.

      * *

      To iterate over a node's siblings, one can write

      *
      Node n = ...;
         * for (Node child : n.children()) { ...
      */ public Iterable children() { if (first == null) { return Collections.emptySet(); } else { return new SiblingNodeIterable(first); } } /** *

      Return an iterable object that iterates over this node's siblings. * The iterator does not support the optional operation * {@link Iterator#remove()}.

      * *

      To iterate over a node's siblings, one can write

      *
      Node n = ...;
         * for (Node sibling : n.siblings()) { ...
      */ public Iterable siblings() { return new SiblingNodeIterable(this); } /** * @see Node#siblings() */ private static final class SiblingNodeIterable implements Iterable, Iterator { private final Node start; private Node current; private boolean used; SiblingNodeIterable(Node start) { this.start = start; this.current = start; this.used = false; } @Override public Iterator iterator() { if (!used) { used = true; return this; } else { // We have already used the current object as an iterator; // we must create a new SiblingNodeIterable based on this // iterable's start node. // // Since the primary use case for Node.children is in for // loops, this branch is extremely unlikely. return (new SiblingNodeIterable(start)).iterator(); } } @Override public boolean hasNext() { return current != null; } @Override public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } @Override public void remove() { throw new UnsupportedOperationException(); } } // ========================================================================== // Accessors PropListItem getPropListHeadForTesting() { return propListHead; } public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. * * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while (node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return cur != null; } @Override public Node next() { if (!hasNext()) throw new NoSuchElementException(); Node n = cur; cur = cur.getParent(); return n; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * * @return Whether the node has exactly one child. */ public boolean hasOneChild() { return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * * @return Whether the node more than one child. */ public boolean hasMoreThanOneChild() { return first != null && first != last; } public int getChildCount() { int c = 0; for (Node n = first; n != null; n = n.next) c++; return c; } // Intended for testing and verification only. public boolean hasChild(Node child) { for (Node n = first; n != null; n = n.getNext()) { if (child == n) { return true; } } return false; } /** * Checks if the subtree under this node is the same as another subtree. * Returns null if it's equal, or a message describing the differences. */ public String checkTreeEquals(Node node2) { NodeMismatch diff = checkTreeEqualsImpl(node2); if (diff != null) { return "Node tree inequality:" + "\nTree1:\n" + toStringTree() + "\n\nTree2:\n" + node2.toStringTree() + "\n\nSubtree1: " + diff.nodeA.toStringTree() + "\n\nSubtree2: " + diff.nodeB.toStringTree(); } return null; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeEqualsImpl(Node node2) { if (!isEquivalentTo(node2, false, false)) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { if (node2 == null) { throw new IllegalStateException(); } res = n.checkTreeEqualsImpl(n2); if (res != null) { return res; } } return res; } /** * Compare this node to node2 recursively and return the first pair of nodes * that differs doing a preorder depth-first traversal. Package private for * testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { // Do a non-recursive equivalents check. if (!isEquivalentTo(node2, true, false)) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { return isEquivalentTo(node, false, true); } /** * Returns true if this node is equivalent semantically to another and * the types are equivalent. */ public boolean isEquivalentToTyped(Node node) { return isEquivalentTo(node, true, true); } /** * @param compareJsType Whether to compare the JSTypes of the nodes. * @param recurse Whether to compare the children of the current node, if * not only the the count of the children are compared. * @return Whether this node is equivalent semantically to the provided node. */ boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) { if (type != node.getType() || getChildCount() != node.getChildCount() || this.getClass() != node.getClass()) { return false; } if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) { return false; } if (type == Token.INC || type == Token.DEC) { int post1 = this.getIntProp(INCRDECR_PROP); int post2 = node.getIntProp(INCRDECR_PROP); if (post1 != post2) { return false; } } else if (type == Token.STRING || type == Token.STRING_KEY) { if (type == Token.STRING_KEY) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } } int slashV1 = this.getIntProp(SLASH_V); int slashV2 = node.getIntProp(SLASH_V); if (slashV1 != slashV2) { return false; } } else if (type == Token.CALL) { if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) { return false; } } if (recurse) { Node n, n2; for (n = first, n2 = node.first; n != null; n = n.next, n2 = n2.next) { if (!n.isEquivalentTo(n2, compareJsType, true)) { return false; } } } return true; } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { String name = getString(); return name.isEmpty() ? null : name; } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * x or a.b.c or this.a. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: return getString().isEmpty() ? false : true; case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as a.b.c, but not this.a * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return getString().isEmpty() ? false : true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its children. */ public Node cloneNode() { Node result; try { result = (Node) super.clone(); // PropListItem lists are immutable and can be shared so there is no // need to clone them here. result.next = null; result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public Node cloneTree() { Node result = cloneNode(); for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { Node n2clone = n2.cloneTree(); n2clone.parent = result; if (result.last != null) { result.last.next = n2clone; } if (result.first == null) { result.first = n2clone; } result.last = n2clone; } return result; } /** * Copies source file and name information from the other * node given to the current node. Used for maintaining * debug information across node append and remove operations. * @return this */ // TODO(nicksantos): The semantics of this method are ill-defined. Delete it. public Node copyInformationFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(STATIC_SOURCE_FILE) == null) { putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); sourcePosition = other.sourcePosition; } return this; } /** * Copies source file and name information from the other node to the * entire tree rooted at this node. * @return this */ // TODO(nicksantos): The semantics of this method are ill-defined. Delete it. public Node copyInformationFromForTree(Node other) { copyInformationFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } return this; } /** * Overwrite all the source information in this node with * that of {@code other}. */ public Node useSourceInfoFrom(Node other) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); sourcePosition = other.sourcePosition; return this; } public Node srcref(Node other) { return useSourceInfoFrom(other); } /** * Overwrite all the source information in this node and its subtree with * that of {@code other}. */ public Node useSourceInfoFromForTree(Node other) { useSourceInfoFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.useSourceInfoFromForTree(other); } return this; } public Node srcrefTree(Node other) { return useSourceInfoFromForTree(other); } /** * Overwrite all the source information in this node with * that of {@code other} iff the source info is missing. */ public Node useSourceInfoIfMissingFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(STATIC_SOURCE_FILE) == null) { putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); sourcePosition = other.sourcePosition; } return this; } /** * Overwrite all the source information in this node and its subtree with * that of {@code other} iff the source info is missing. */ public Node useSourceInfoIfMissingFromForTree(Node other) { useSourceInfoIfMissingFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.useSourceInfoIfMissingFromForTree(other); } return this; } //========================================================================== // Custom annotations public JSType getJSType() { return jsType; } public void setJSType(JSType jsType) { this.jsType = jsType; } public FileLevelJsDocBuilder getJsDocBuilderForNode() { return new FileLevelJsDocBuilder(); } /** * An inner class that provides back-door access to the license * property of the JSDocInfo property for this node. This is only * meant to be used for top-level script nodes where the * {@link com.google.javascript.jscomp.parsing.JsDocInfoParser} needs to * be able to append directly to the top-level node, not just the * current node. */ public class FileLevelJsDocBuilder { public void append(String fileLevelComment) { JSDocInfo jsDocInfo = getJSDocInfo(); if (jsDocInfo == null) { // TODO(user): Is there a way to determine whether to // parse the JsDoc documentation from here? jsDocInfo = new JSDocInfo(false); } String license = jsDocInfo.getLicense(); if (license == null) { license = ""; } jsDocInfo.setLicense(license + fileLevelComment); setJSDocInfo(jsDocInfo); } } /** * Get the {@link JSDocInfo} attached to this node. * @return the information or {@code null} if no JSDoc is attached to this * node */ public JSDocInfo getJSDocInfo() { return (JSDocInfo) getProp(JSDOC_INFO_PROP); } /** * Sets the {@link JSDocInfo} attached to this node. */ public Node setJSDocInfo(JSDocInfo info) { putProp(JSDOC_INFO_PROP, info); return this; } /** * Sets whether this node is a variable length argument node. This * method is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public void setVarArgs(boolean varArgs) { putBooleanProp(VAR_ARGS_NAME, varArgs); } /** * Returns whether this node is a variable length argument node. This * method's return value is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public boolean isVarArgs() { return getBooleanProp(VAR_ARGS_NAME); } /** * Sets whether this node is an optional argument node. This * method is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public void setOptionalArg(boolean optionalArg) { putBooleanProp(OPT_ARG_NAME, optionalArg); } /** * Returns whether this node is an optional argument node. This * method's return value is meaningful only on {@link Token#NAME} nodes * used to define a {@link Token#FUNCTION}'s argument list. */ public boolean isOptionalArg() { return getBooleanProp(OPT_ARG_NAME); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setIsSyntheticBlock(boolean val) { putBooleanProp(SYNTHETIC_BLOCK_PROP, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean isSyntheticBlock() { return getBooleanProp(SYNTHETIC_BLOCK_PROP); } /** * Sets the ES5 directives on this node. */ public void setDirectives(Set val) { putProp(DIRECTIVES, val); } /** * Returns the set of ES5 directives for this node. */ @SuppressWarnings("unchecked") public Set getDirectives() { return (Set) getProp(DIRECTIVES); } /** * Adds a warning to be suppressed. This is indistinguishable * from having a {@code @suppress} tag in the code. */ public void addSuppression(String warning) { if (getJSDocInfo() == null) { setJSDocInfo(new JSDocInfo(false)); } getJSDocInfo().addSuppression(warning); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setWasEmptyNode(boolean val) { putBooleanProp(EMPTY_BLOCK, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean wasEmptyNode() { return getBooleanProp(EMPTY_BLOCK); } // There are four values of interest: // global state changes // this state changes // arguments state changes // whether the call throws an exception // locality of the result // We want a value of 0 to mean "global state changes and // unknown locality of result". final public static int FLAG_GLOBAL_STATE_UNMODIFIED = 1; final public static int FLAG_THIS_UNMODIFIED = 2; final public static int FLAG_ARGUMENTS_UNMODIFIED = 4; final public static int FLAG_NO_THROWS = 8; final public static int FLAG_LOCAL_RESULTS = 16; final public static int SIDE_EFFECTS_FLAGS_MASK = 31; final public static int SIDE_EFFECTS_ALL = 0; final public static int NO_SIDE_EFFECTS = FLAG_GLOBAL_STATE_UNMODIFIED | FLAG_THIS_UNMODIFIED | FLAG_ARGUMENTS_UNMODIFIED | FLAG_NO_THROWS; /** * Marks this function or constructor call's side effect flags. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setSideEffectFlags(int flags) { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putIntProp(SIDE_EFFECT_FLAGS, flags); } public void setSideEffectFlags(SideEffectFlags flags) { setSideEffectFlags(flags.valueOf()); } /** * Returns the side effects flags for this node. */ public int getSideEffectFlags() { return getIntProp(SIDE_EFFECT_FLAGS); } /** * A helper class for getting and setting the side-effect flags. * @author johnlenz@google.com (John Lenz) */ public static class SideEffectFlags { private int value = Node.SIDE_EFFECTS_ALL; public SideEffectFlags() { } public SideEffectFlags(int value) { this.value = value; } public int valueOf() { return value; } /** All side-effect occur and the returned results are non-local. */ public void setAllFlags() { value = Node.SIDE_EFFECTS_ALL; } /** No side-effects occur and the returned results are local. */ public void clearAllFlags() { value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS; } public boolean areAllFlagsSet() { return value == Node.SIDE_EFFECTS_ALL; } /** * Preserve the return result flag, but clear the others: * no global state change, no throws, no this change, no arguments change */ public void clearSideEffectFlags() { value |= Node.NO_SIDE_EFFECTS; } public void setMutatesGlobalState() { // Modify global means everything must be assumed to be modified. removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED); removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); removeFlag(Node.FLAG_THIS_UNMODIFIED); } public void setThrows() { removeFlag(Node.FLAG_NO_THROWS); } public void setMutatesThis() { removeFlag(Node.FLAG_THIS_UNMODIFIED); } public void setMutatesArguments() { removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED); } public void setReturnsTainted() { removeFlag(Node.FLAG_LOCAL_RESULTS); } private void removeFlag(int flag) { value &= ~flag; } } /** * @return Whether the only side-effect is "modifies this" */ public boolean isOnlyModifiesThisCall() { return areBitFlagsSet( getSideEffectFlags() & Node.NO_SIDE_EFFECTS, Node.FLAG_GLOBAL_STATE_UNMODIFIED | Node.FLAG_ARGUMENTS_UNMODIFIED | Node.FLAG_NO_THROWS); } /** * Returns true if this node is a function or constructor call that * has no side effects. */ public boolean isNoSideEffectsCall() { return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS); } /** * Returns true if this node is a function or constructor call that * returns a primitive or a local object (an object that has no other * references). */ public boolean isLocalResultCall() { return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS); } /** * returns true if all the flags are set in value. */ private boolean areBitFlagsSet(int value, int flags) { return (value & flags) == flags; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public boolean isQuotedString() { return false; } /** * This should only be called for STRING nodes children of OBJECTLIT. */ public void setQuotedString() { throw new IllegalStateException("not a StringNode"); } static class NodeMismatch { final Node nodeA; final Node nodeB; NodeMismatch(Node nodeA, Node nodeB) { this.nodeA = nodeA; this.nodeB = nodeB; } @Override public boolean equals(Object object) { if (object instanceof NodeMismatch) { NodeMismatch that = (NodeMismatch) object; return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB); } return false; } @Override public int hashCode() { return Objects.hashCode(nodeA, nodeB); } } /*** AST type check methods ***/ public boolean isAdd() { return this.getType() == Token.ADD; } public boolean isAnd() { return this.getType() == Token.AND; } public boolean isArrayLit() { return this.getType() == Token.ARRAYLIT; } public boolean isAssign() { return this.getType() == Token.ASSIGN; } public boolean isAssignAdd() { return this.getType() == Token.ASSIGN_ADD; } public boolean isBlock() { return this.getType() == Token.BLOCK; } public boolean isBreak() { return this.getType() == Token.BREAK; } public boolean isCall() { return this.getType() == Token.CALL; } public boolean isCase() { return this.getType() == Token.CASE; } public boolean isCast() { return this.getType() == Token.CAST; } public boolean isCatch() { return this.getType() == Token.CATCH; } public boolean isComma() { return this.getType() == Token.COMMA; } public boolean isContinue() { return this.getType() == Token.CONTINUE; } public boolean isDebugger() { return this.getType() == Token.DEBUGGER; } public boolean isDec() { return this.getType() == Token.DEC; } public boolean isDefaultCase() { return this.getType() == Token.DEFAULT_CASE; } public boolean isDelProp() { return this.getType() == Token.DELPROP; } public boolean isDo() { return this.getType() == Token.DO; } public boolean isEmpty() { return this.getType() == Token.EMPTY; } public boolean isExprResult() { return this.getType() == Token.EXPR_RESULT; } public boolean isFalse() { return this.getType() == Token.FALSE; } public boolean isFor() { return this.getType() == Token.FOR; } public boolean isFunction() { return this.getType() == Token.FUNCTION; } public boolean isGetterDef() { return this.getType() == Token.GETTER_DEF; } public boolean isGetElem() { return this.getType() == Token.GETELEM; } public boolean isGetProp() { return this.getType() == Token.GETPROP; } public boolean isHook() { return this.getType() == Token.HOOK; } public boolean isIf() { return this.getType() == Token.IF; } public boolean isIn() { return this.getType() == Token.IN; } public boolean isInc() { return this.getType() == Token.INC; } public boolean isInstanceOf() { return this.getType() == Token.INSTANCEOF; } public boolean isLabel() { return this.getType() == Token.LABEL; } public boolean isLabelName() { return this.getType() == Token.LABEL_NAME; } public boolean isName() { return this.getType() == Token.NAME; } public boolean isNE() { return this.getType() == Token.NE; } public boolean isNew() { return this.getType() == Token.NEW; } public boolean isNot() { return this.getType() == Token.NOT; } public boolean isNull() { return this.getType() == Token.NULL; } public boolean isNumber() { return this.getType() == Token.NUMBER; } public boolean isObjectLit() { return this.getType() == Token.OBJECTLIT; } public boolean isOr() { return this.getType() == Token.OR; } public boolean isParamList() { return this.getType() == Token.PARAM_LIST; } public boolean isRegExp() { return this.getType() == Token.REGEXP; } public boolean isReturn() { return this.getType() == Token.RETURN; } public boolean isScript() { return this.getType() == Token.SCRIPT; } public boolean isSetterDef() { return this.getType() == Token.SETTER_DEF; } public boolean isString() { return this.getType() == Token.STRING; } public boolean isStringKey() { return this.getType() == Token.STRING_KEY; } public boolean isSwitch() { return this.getType() == Token.SWITCH; } public boolean isThis() { return this.getType() == Token.THIS; } public boolean isThrow() { return this.getType() == Token.THROW; } public boolean isTrue() { return this.getType() == Token.TRUE; } public boolean isTry() { return this.getType() == Token.TRY; } public boolean isTypeOf() { return this.getType() == Token.TYPEOF; } public boolean isVar() { return this.getType() == Token.VAR; } public boolean isVoid() { return this.getType() == Token.VOID; } public boolean isWhile() { return this.getType() == Token.WHILE; } public boolean isWith() { return this.getType() == Token.WITH; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/IR.java0000644000175000017500000004240112115204405023636 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.base.Preconditions; import java.util.List; /** * An AST construction helper class * @author johnlenz@google.com (John Lenz) */ public class IR { private IR() {} public static Node empty() { return new Node(Token.EMPTY); } public static Node function(Node name, Node params, Node body) { Preconditions.checkState(name.isName()); Preconditions.checkState(params.isParamList()); Preconditions.checkState(body.isBlock()); return new Node(Token.FUNCTION, name, params, body); } public static Node paramList() { return new Node(Token.PARAM_LIST); } public static Node paramList(Node param) { Preconditions.checkState(param.isName()); return new Node(Token.PARAM_LIST, param); } public static Node paramList(Node ... params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node paramList(List params) { Node paramList = paramList(); for (Node param : params) { Preconditions.checkState(param.isName()); paramList.addChildToBack(param); } return paramList; } public static Node block() { Node block = new Node(Token.BLOCK); return block; } public static Node block(Node stmt) { Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.BLOCK, stmt); return block; } public static Node block(Node ... stmts) { Node block = block(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); block.addChildToBack(stmt); } return block; } public static Node block(List stmts) { Node paramList = block(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatement(stmt)); paramList.addChildToBack(stmt); } return paramList; } private static Node blockUnchecked(Node stmt) { return new Node(Token.BLOCK, stmt); } public static Node script() { // TODO(johnlenz): finish setting up the SCRIPT node Node block = new Node(Token.SCRIPT); return block; } public static Node script(Node ... stmts) { Node block = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt)); block.addChildToBack(stmt); } return block; } public static Node script(List stmts) { Node paramList = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt)); paramList.addChildToBack(stmt); } return paramList; } public static Node var(Node name, Node value) { Preconditions.checkState(name.isName() && !name.hasChildren()); Preconditions.checkState(mayBeExpression(value)); name.addChildToFront(value); return var(name); } public static Node var(Node name) { Preconditions.checkState(name.isName()); return new Node(Token.VAR, name); } public static Node returnNode() { return new Node(Token.RETURN); } public static Node returnNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.RETURN, expr); } public static Node throwNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.THROW, expr); } public static Node exprResult(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.EXPR_RESULT, expr); } public static Node ifNode(Node cond, Node then) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); return new Node(Token.IF, cond, then); } public static Node ifNode(Node cond, Node then, Node elseNode) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); Preconditions.checkState(elseNode.isBlock()); return new Node(Token.IF, cond, then, elseNode); } public static Node doNode(Node body, Node cond) { Preconditions.checkState(body.isBlock()); Preconditions.checkState(mayBeExpression(cond)); return new Node(Token.DO, body, cond); } public static Node forIn(Node target, Node cond, Node body) { Preconditions.checkState(target.isVar() || mayBeExpression(target)); Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, target, cond, body); } public static Node forNode(Node init, Node cond, Node incr, Node body) { Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init)); Preconditions.checkState(mayBeExpressionOrEmpty(cond)); Preconditions.checkState(mayBeExpressionOrEmpty(incr)); Preconditions.checkState(body.isBlock()); return new Node(Token.FOR, init, cond, incr, body); } public static Node switchNode(Node cond, Node ... cases) { Preconditions.checkState(mayBeExpression(cond)); Node switchNode = new Node(Token.SWITCH, cond); for (Node caseNode : cases) { Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase()); switchNode.addChildToBack(caseNode); } return switchNode; } public static Node caseNode(Node expr, Node body) { Preconditions.checkState(mayBeExpression(expr)); Preconditions.checkState(body.isBlock()); body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.CASE, expr, body); } public static Node defaultCase(Node body) { Preconditions.checkState(body.isBlock()); body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); return new Node(Token.DEFAULT_CASE, body); } public static Node label(Node name, Node stmt) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); Preconditions.checkState(mayBeStatement(stmt)); Node block = new Node(Token.LABEL, name, stmt); return block; } public static Node labelName(String name) { Preconditions.checkState(!name.isEmpty()); return Node.newString(Token.LABEL_NAME, name); } public static Node tryFinally(Node tryBody, Node finallyBody) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(finallyBody.isBlock()); Node catchBody = block().copyInformationFrom(tryBody); return new Node(Token.TRY, tryBody, catchBody, finallyBody); } public static Node tryCatch(Node tryBody, Node catchNode) { Preconditions.checkState(tryBody.isBlock()); Preconditions.checkState(catchNode.isCatch()); Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode); return new Node(Token.TRY, tryBody, catchBody); } public static Node tryCatchFinally( Node tryBody, Node catchNode, Node finallyBody) { Preconditions.checkState(finallyBody.isBlock()); Node tryNode = tryCatch(tryBody, catchNode); tryNode.addChildToBack(finallyBody); return tryNode; } public static Node catchNode(Node expr, Node body) { Preconditions.checkState(expr.isName()); Preconditions.checkState(body.isBlock()); return new Node(Token.CATCH, expr, body); } public static Node breakNode() { return new Node(Token.BREAK); } public static Node breakNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.BREAK, name); } public static Node continueNode() { return new Node(Token.CONTINUE); } public static Node continueNode(Node name) { // TODO(johnlenz): additional validation here. Preconditions.checkState(name.isLabelName()); return new Node(Token.CONTINUE, name); } // public static Node call(Node target, Node ... args) { Node call = new Node(Token.CALL, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); call.addChildToBack(arg); } return call; } public static Node newNode(Node target, Node ... args) { Node newcall = new Node(Token.NEW, target); for (Node arg : args) { Preconditions.checkState(mayBeExpression(arg)); newcall.addChildToBack(arg); } return newcall; } public static Node name(String name) { return Node.newString(Token.NAME, name); } public static Node getprop(Node target, Node prop) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(prop.isString()); return new Node(Token.GETPROP, target, prop); } public static Node getelem(Node target, Node elem) { Preconditions.checkState(mayBeExpression(target)); Preconditions.checkState(mayBeExpression(elem)); return new Node(Token.GETELEM, target, elem); } public static Node assign(Node target, Node expr) { Preconditions.checkState(isAssignmentTarget(target)); Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.ASSIGN, target, expr); } public static Node hook(Node cond, Node trueval, Node falseval) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(mayBeExpression(trueval)); Preconditions.checkState(mayBeExpression(falseval)); return new Node(Token.HOOK, cond, trueval, falseval); } public static Node comma(Node expr1, Node expr2) { return binaryOp(Token.COMMA, expr1, expr2); } public static Node and(Node expr1, Node expr2) { return binaryOp(Token.AND, expr1, expr2); } public static Node or(Node expr1, Node expr2) { return binaryOp(Token.OR, expr1, expr2); } public static Node not(Node expr1) { return unaryOp(Token.NOT, expr1); } /** * "==" */ public static Node eq(Node expr1, Node expr2) { return binaryOp(Token.EQ, expr1, expr2); } /** * "===" */ public static Node sheq(Node expr1, Node expr2) { return binaryOp(Token.SHEQ, expr1, expr2); } public static Node voidNode(Node expr1) { return unaryOp(Token.VOID, expr1); } public static Node neg(Node expr1) { return unaryOp(Token.NEG, expr1); } public static Node pos(Node expr1) { return unaryOp(Token.POS, expr1); } public static Node cast(Node expr1) { return unaryOp(Token.CAST, expr1); } public static Node add(Node expr1, Node expr2) { return binaryOp(Token.ADD, expr1, expr2); } public static Node sub(Node expr1, Node expr2) { return binaryOp(Token.SUB, expr1, expr2); } // TODO(johnlenz): the rest of the ops // literals public static Node objectlit(Node ... propdefs) { Node objectlit = new Node(Token.OBJECTLIT); for (Node propdef : propdefs) { Preconditions.checkState( propdef.isStringKey() || propdef.isGetterDef() || propdef.isSetterDef()); Preconditions.checkState(propdef.hasOneChild()); objectlit.addChildToBack(propdef); } return objectlit; } // TODO(johnlenz): quoted props public static Node propdef(Node string, Node value) { Preconditions.checkState(string.isStringKey()); Preconditions.checkState(!string.hasChildren()); Preconditions.checkState(mayBeExpression(value)); string.addChildToFront(value); return string; } public static Node arraylit(Node ... exprs) { Node arraylit = new Node(Token.ARRAYLIT); for (Node expr : exprs) { Preconditions.checkState(mayBeExpressionOrEmpty(expr)); arraylit.addChildToBack(expr); } return arraylit; } public static Node regexp(Node expr) { Preconditions.checkState(expr.isString()); return new Node(Token.REGEXP, expr); } public static Node regexp(Node expr, Node flags) { Preconditions.checkState(expr.isString()); Preconditions.checkState(flags.isString()); return new Node(Token.REGEXP, expr, flags); } public static Node string(String s) { return Node.newString(s); } public static Node stringKey(String s) { return Node.newString(Token.STRING_KEY, s); } public static Node number(double d) { return Node.newNumber(d); } public static Node thisNode() { return new Node(Token.THIS); } public static Node trueNode() { return new Node(Token.TRUE); } public static Node falseNode() { return new Node(Token.FALSE); } public static Node nullNode() { return new Node(Token.NULL); } // helper methods private static Node binaryOp(int token, Node expr1, Node expr2) { Preconditions.checkState(mayBeExpression(expr1)); Preconditions.checkState(mayBeExpression(expr2)); return new Node(token, expr1, expr2); } private static Node unaryOp(int token, Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(token, expr); } private static boolean mayBeExpressionOrEmpty(Node n) { return n.isEmpty() || mayBeExpression(n); } private static boolean isAssignmentTarget(Node n) { return n.isName() || n.isGetProp() || n.isGetElem(); } // NOTE: some nodes are neither statements nor expression nodes: // SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH // GETTER_DEF, SETTER_DEF /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeStatementNoReturn(Node n) { switch (n.getType()) { case Token.EMPTY: case Token.FUNCTION: // EMPTY and FUNCTION are used both in expression and statement // contexts return true; case Token.BLOCK: case Token.BREAK: case Token.CONST: case Token.CONTINUE: case Token.DEBUGGER: case Token.DO: case Token.EXPR_RESULT: case Token.FOR: case Token.IF: case Token.LABEL: case Token.SWITCH: case Token.THROW: case Token.TRY: case Token.VAR: case Token.WHILE: case Token.WITH: return true; default: return false; } } /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeStatement(Node n) { if (!mayBeStatementNoReturn(n)) { return n.isReturn(); } return true; } /** * It isn't possible to always determine if a detached node is a expression, * so make a best guess. */ private static boolean mayBeExpression(Node n) { switch (n.getType()) { case Token.FUNCTION: // FUNCTION is used both in expression and statement // contexts. return true; case Token.ADD: case Token.AND: case Token.ARRAYLIT: case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.BITAND: case Token.BITOR: case Token.BITNOT: case Token.BITXOR: case Token.CALL: case Token.CAST: case Token.COMMA: case Token.DEC: case Token.DELPROP: case Token.DIV: case Token.EQ: case Token.FALSE: case Token.GE: case Token.GETPROP: case Token.GETELEM: case Token.GT: case Token.HOOK: case Token.IN: case Token.INC: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NAME: case Token.NE: case Token.NEG: case Token.NEW: case Token.NOT: case Token.NUMBER: case Token.NULL: case Token.OBJECTLIT: case Token.OR: case Token.POS: case Token.REGEXP: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.STRING: case Token.SUB: case Token.THIS: case Token.TYPEOF: case Token.TRUE: case Token.URSH: case Token.VOID: return true; default: return false; } } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/JSDocInfo.java0000644000175000017500000012301612115204405025104 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** *

      JSDoc information describing JavaScript code. JSDoc is represented as a * unified object with fields for each JSDoc annotation, even though some * combinations are incorrect. For instance, if a JSDoc describes an enum, * it cannot have information about a return type. This implementation * takes advantage of such incompatibilities to reuse fields for multiple * purposes, reducing memory consumption.

      * *

      Constructing {@link JSDocInfo} objects is simplified by * {@link JSDocInfoBuilder} which provides early incompatibility detection.

      * */ public class JSDocInfo implements Serializable { private static final long serialVersionUID = 1L; /** * Visibility categories. The {@link Visibility#ordinal()} can be used as a * numerical indicator of privacy, where 0 is the most private. This means * that the {@link Visibility#compareTo} method can be used to * determine if a visibility is more permissive than another. */ public enum Visibility { PRIVATE, PROTECTED, PUBLIC, // If visibility is not specified, we just assume that visibility // is inherited from the super class. INHERITED } private static final class LazilyInitializedInfo implements Serializable { private static final long serialVersionUID = 1L; // Function information JSTypeExpression baseType = null; List extendedInterfaces = null; List implementedInterfaces = null; Map parameters = null; List thrownTypes = null; ImmutableList templateTypeNames = null; ImmutableList classTemplateTypeNames = null; // Other information String description = null; String meaning = null; String deprecated = null; String license = null; Set suppressions = null; Set modifies = null; String lendsName = null; boolean ngInject = false; } private static final class LazilyInitializedDocumentation { String sourceComment = null; List markers = null; Map parameters = null; Map throwsDescriptions = null; String blockDescription = null; String fileOverview = null; String returnDescription = null; String version = null; List authors = null; List sees = null; } /** * A piece of information (found in a marker) which contains a position * with a string. */ public static class StringPosition extends SourcePosition { } /** * A piece of information (found in a marker) which contains a position * with a string that has no leading or trailing whitespace. */ static class TrimmedStringPosition extends StringPosition { @Override public void setItem(String item) { Preconditions.checkArgument( item.charAt(0) != ' ' && item.charAt(item.length() - 1) != ' ', "String has leading or trailing whitespace"); super.setItem(item); } } /** * A piece of information (found in a marker) which contains a position * with a name node. */ public static class NamePosition extends SourcePosition {} /** * A piece of information (found in a marker) which contains a position * with a type expression syntax tree. */ public static class TypePosition extends SourcePosition { private boolean brackets = false; /** Returns whether the type has curly braces around it. */ public boolean hasBrackets() { return brackets; } void setHasBrackets(boolean newVal) { brackets = newVal; } } /** * Defines a class for containing the parsing information * for this JSDocInfo. For each annotation found in the * JsDoc, a marker will be created indicating the annotation * itself, the name of the annotation (if any; for example, * a @param has a name, but a @return does not), the * textual description found on that annotation and, if applicable, * the type declaration. All this information is only collected * if documentation collection is turned on. */ public static final class Marker { private TrimmedStringPosition annotation = null; private TrimmedStringPosition name = null; private SourcePosition nameNode = null; private StringPosition description = null; private TypePosition type = null; /** * Gets the position information for the annotation name. (e.g., "param") */ public StringPosition getAnnotation() { return annotation; } void setAnnotation(TrimmedStringPosition p) { annotation = p; } /** * Gets the position information for the name found * in a @param tag. * @deprecated Use #getNameNode */ @Deprecated public StringPosition getName() { return name; } void setName(TrimmedStringPosition p) { name = p; } /** * Gets the position information for the name found * in an @param tag. */ public SourcePosition getNameNode() { return nameNode; } void setNameNode(SourcePosition p) { nameNode = p; } /** * Gets the position information for the description found * in a block tag. */ public StringPosition getDescription() { return description; } void setDescription(StringPosition p) { description = p; } /** * Gets the position information for the type expression found * in some block tags, like "@param" and "@return". */ public TypePosition getType() { return type; } void setType(TypePosition p) { type = p; } } private LazilyInitializedInfo info = null; private LazilyInitializedDocumentation documentation = null; // The Node this JSDoc is associated with. private Node associatedNode = null; private Visibility visibility = null; /** * The {@link #isConstant()}, {@link #isConstructor()}, {@link #isInterface}, * {@link #isHidden()} and {@link #shouldPreserveTry()} flags as well as * whether the {@link #type} field stores a value for {@link #getType()}, * {@link #getReturnType()} or {@link #getEnumParameterType()}. * * @see #setFlag(boolean, int) * @see #getFlag(int) * @see #setType(JSTypeExpression, int) * @see #getType(int) */ private int bitset = 0x00; /** * The type for {@link #getType()}, {@link #getReturnType()} or * {@link #getEnumParameterType()}. The knowledge of which one is recorded is * stored in the {@link #bitset} field. * * @see #setType(JSTypeExpression, int) * @see #getType(int) */ private JSTypeExpression type = null; /** * The type for {@link #getThisType()}. */ private JSTypeExpression thisType = null; /** * Whether to include documentation. * * @see JSDocInfo.LazilyInitializedDocumentation */ private boolean includeDocumentation = false; // We use a bit map to represent whether or not the JSDoc contains // one of the "boolean" annotation types (annotations like @constructor, // for which the presence of the annotation alone is significant). // Mask all the boolean annotation types private static final int MASK_FLAGS = 0x3FFFFFFF; private static final int MASK_CONSTANT = 0x00000001; // @const private static final int MASK_CONSTRUCTOR = 0x00000002; // @constructor private static final int MASK_DEFINE = 0x00000004; // @define private static final int MASK_HIDDEN = 0x00000008; // @hidden private static final int MASK_PRESERVETRY = 0x00000010; // @preserveTry private static final int MASK_NOCHECK = 0x00000020; // @notypecheck private static final int MASK_OVERRIDE = 0x00000040; // @override private static final int MASK_NOALIAS = 0x00000080; // @noalias private static final int MASK_DEPRECATED = 0x00000100; // @deprecated private static final int MASK_INTERFACE = 0x00000200; // @interface private static final int MASK_EXPORT = 0x00000400; // @export private static final int MASK_NOSHADOW = 0x00000800; // @noshadow private static final int MASK_FILEOVERVIEW = 0x00001000; // @fileoverview private static final int MASK_IMPLICITCAST = 0x00002000; // @implicitCast private static final int MASK_NOSIDEEFFECTS = 0x00004000; // @nosideeffects private static final int MASK_EXTERNS = 0x00008000; // @externs private static final int MASK_JAVADISPATCH = 0x00010000; // @javadispatch private static final int MASK_NOCOMPILE = 0x00020000; // @nocompile private static final int MASK_CONSISTIDGEN = 0x00040000; // @consistentIdGenerator private static final int MASK_IDGEN = 0x00080000; // @idGenerator private static final int MASK_EXPOSE = 0x00100000; // @expose private static final int MASK_STRUCT = 0x00200000; // @struct private static final int MASK_DICT = 0x00400000; // @dict private static final int MASK_STALBEIDGEN = 0x00800000; // @stableIdGenerator // 3 bit type field stored in the top 3 bits of the most significant // nibble. private static final int MASK_TYPEFIELD = 0xE0000000; // 1110... private static final int TYPEFIELD_TYPE = 0x20000000; // 0010... private static final int TYPEFIELD_RETURN = 0x40000000; // 0100... private static final int TYPEFIELD_ENUM = 0x60000000; // 0110... private static final int TYPEFIELD_TYPEDEF = 0x80000000; // 1000... /** * Creates a {@link JSDocInfo} object. This object should be created using * a {@link JSDocInfoBuilder}. */ JSDocInfo(boolean includeDocumentation) { this.includeDocumentation = includeDocumentation; } // Visible for testing. public JSDocInfo() {} void setConsistentIdGenerator(boolean value) { setFlag(value, MASK_CONSISTIDGEN); } void setStableIdGenerator(boolean value) { setFlag(value, MASK_STALBEIDGEN); } void setConstant(boolean value) { setFlag(value, MASK_CONSTANT); } void setConstructor(boolean value) { setFlag(value, MASK_CONSTRUCTOR); } void setStruct() { setFlag(true, MASK_STRUCT); } void setDict() { setFlag(true, MASK_DICT); } void setDefine(boolean value) { setFlag(value, MASK_DEFINE); } void setHidden(boolean value) { setFlag(value, MASK_HIDDEN); } void setNoCheck(boolean value) { setFlag(value, MASK_NOCHECK); } void setShouldPreserveTry(boolean value) { setFlag(value, MASK_PRESERVETRY); } void setOverride(boolean value) { setFlag(value, MASK_OVERRIDE); } void setNoAlias(boolean value) { setFlag(value, MASK_NOALIAS); } // Visible for testing. public void setDeprecated(boolean value) { setFlag(value, MASK_DEPRECATED); } void setInterface(boolean value) { setFlag(value, MASK_INTERFACE); } void setExport(boolean value) { setFlag(value, MASK_EXPORT); } void setExpose(boolean value) { setFlag(value, MASK_EXPOSE); } void setNoShadow(boolean value) { setFlag(value, MASK_NOSHADOW); } void setIdGenerator(boolean value) { setFlag(value, MASK_IDGEN); } void setImplicitCast(boolean value) { setFlag(value, MASK_IMPLICITCAST); } void setNoSideEffects(boolean value) { setFlag(value, MASK_NOSIDEEFFECTS); } void setExterns(boolean value) { setFlag(value, MASK_EXTERNS); } void setJavaDispatch(boolean value) { setFlag(value, MASK_JAVADISPATCH); } void setNoCompile(boolean value) { setFlag(value, MASK_NOCOMPILE); } private void setFlag(boolean value, int mask) { if (value) { bitset |= mask; } else { bitset &= ~mask; } } /** * @return whether the {@code @consistentIdGenerator} is present on * this {@link JSDocInfo} */ public boolean isConsistentIdGenerator() { return getFlag(MASK_CONSISTIDGEN); } /** * @return whether the {@code @stableIdGenerator} is present on this {@link JSDocInfo}. */ public boolean isStableIdGenerator() { return getFlag(MASK_STALBEIDGEN); } /** * Returns whether the {@code @const} annotation is present on this * {@link JSDocInfo}. */ public boolean isConstant() { return getFlag(MASK_CONSTANT) || isDefine(); } /** * Returns whether the {@code @constructor} annotation is present on this * {@link JSDocInfo}. */ public boolean isConstructor() { return getFlag(MASK_CONSTRUCTOR); } /** * Returns whether the {@code @struct} annotation is present on this * {@link JSDocInfo}. */ public boolean makesStructs() { return getFlag(MASK_STRUCT); } /** * Returns whether the {@code @dict} annotation is present on this * {@link JSDocInfo}. */ public boolean makesDicts() { return getFlag(MASK_DICT); } /** * Returns whether the {@code @define} annotation is present on this * {@link JSDocInfo}. If this annotation is present, then the * {@link #getType()} method will retrieve the define type. */ public boolean isDefine() { return getFlag(MASK_DEFINE); } /** * Returns whether the {@code @hidden} annotation is present on this * {@link JSDocInfo}. */ public boolean isHidden() { return getFlag(MASK_HIDDEN); } /** * Returns whether the {@code @nocheck} annotation is present on this * {@link JSDocInfo}. */ public boolean isNoTypeCheck() { return getFlag(MASK_NOCHECK); } /** * Returns whether the {@code @preserveTry} annotation is present on this * {@link JSDocInfo}. */ public boolean shouldPreserveTry() { return getFlag(MASK_PRESERVETRY); } /** * Returns whether the {@code @override} annotation is present on this * {@link JSDocInfo}. */ public boolean isOverride() { return getFlag(MASK_OVERRIDE); } /** * Returns whether the {@code @noalias} annotation is present on this * {@link JSDocInfo}. */ public boolean isNoAlias() { return getFlag(MASK_NOALIAS); } /** * Returns whether the {@code @deprecated} annotation is present on this * {@link JSDocInfo}. */ public boolean isDeprecated() { return getFlag(MASK_DEPRECATED); } /** * Returns whether the {@code @interface} annotation is present on this * {@link JSDocInfo}. */ public boolean isInterface() { return getFlag(MASK_INTERFACE); } /** * Returns whether the {@code @export} annotation is present on this * {@link JSDocInfo}. */ public boolean isExport() { return getFlag(MASK_EXPORT); } /** * Returns whether the {@code @expose} annotation is present on this * {@link JSDocInfo}. */ public boolean isExpose() { return getFlag(MASK_EXPOSE); } /** * Returns whether the {@code @noshadow} annotation is present on this * {@link JSDocInfo}. */ public boolean isNoShadow() { return getFlag(MASK_NOSHADOW); } /** * @return whether the {@code @idGenerator} is present on * this {@link JSDocInfo} */ public boolean isIdGenerator() { return getFlag(MASK_IDGEN); } /** * Returns whether the {@code @implicitCast} annotation is present on this * {@link JSDocInfo}. */ public boolean isImplicitCast() { return getFlag(MASK_IMPLICITCAST); } /** * Returns whether the {@code @nosideeffects} annotation is present on this * {@link JSDocInfo}. */ public boolean isNoSideEffects() { return getFlag(MASK_NOSIDEEFFECTS); } /** * Returns whether the {@code @externs} annotation is present on this * {@link JSDocInfo}. */ public boolean isExterns() { return getFlag(MASK_EXTERNS); } /** * Returns whether the {@code @javadispatch} annotation is present on this * {@link JSDocInfo}. */ public boolean isJavaDispatch() { return getFlag(MASK_JAVADISPATCH); } /** * Returns whether the {@code @nocompile} annotation is present on this * {@link JSDocInfo}. */ public boolean isNoCompile() { return getFlag(MASK_NOCOMPILE); } /** * @return Whether there is declaration present on this {@link JSDocInfo}. */ public boolean containsDeclaration() { return (hasType() || hasReturnType() || hasEnumParameterType() || hasTypedefType() || hasThisType() || getParameterCount() > 0 || getFlag(MASK_CONSTANT | MASK_CONSTRUCTOR | MASK_DEFINE | MASK_OVERRIDE | MASK_NOALIAS | MASK_DEPRECATED | MASK_INTERFACE | MASK_NOSHADOW | MASK_IMPLICITCAST | MASK_NOSIDEEFFECTS)); } private boolean getFlag(int mask) { return (bitset & mask) != 0x00; } // Visible for testing. public void setVisibility(Visibility visibility) { this.visibility = visibility; } private void lazyInitInfo() { if (info == null) { info = new LazilyInitializedInfo(); } } /** * Lazily initializes the documentation information object, but only * if the JSDocInfo was told to keep such information around. */ private boolean lazyInitDocumentation() { if (!includeDocumentation) { return false; } if (documentation == null) { documentation = new LazilyInitializedDocumentation(); } return true; } /** * Adds a marker to the documentation (if it exists) and * returns the marker. Returns null otherwise. */ Marker addMarker() { if (!lazyInitDocumentation()) { return null; } if (documentation.markers == null) { documentation.markers = Lists.newArrayList(); } Marker marker = new Marker(); documentation.markers.add(marker); return marker; } /** * Sets the deprecation reason. * * @param reason The deprecation reason */ boolean setDeprecationReason(String reason) { lazyInitInfo(); if (info.deprecated != null) { return false; } info.deprecated = reason; return true; } /** * Add a suppressed warning. */ public void addSuppression(String suppression) { lazyInitInfo(); if (info.suppressions == null) { info.suppressions = Sets.newHashSet(); } info.suppressions.add(suppression); } /** * Sets suppressed warnings. * @param suppressions A list of suppressed warning types. */ boolean setSuppressions(Set suppressions) { lazyInitInfo(); if (info.suppressions != null) { return false; } info.suppressions = suppressions; return true; } /** * Add modifies values. */ void addModifies(String modifies) { lazyInitInfo(); if (info.modifies == null) { info.modifies = Sets.newHashSet(); } info.modifies.add(modifies); } /** * Sets modifies values. * @param modifies A list of modifies types. */ boolean setModifies(Set modifies) { lazyInitInfo(); if (info.modifies != null) { return false; } info.modifies = modifies; return true; } /** * Documents the version. */ boolean documentVersion(String version) { if (!lazyInitDocumentation()) { return true; } if (documentation.version != null) { return false; } documentation.version = version; return true; } /** * Documents a reference (i.e. adds a "see" reference to the list). */ boolean documentReference(String reference) { if (!lazyInitDocumentation()) { return true; } if (documentation.sees == null) { documentation.sees = Lists.newArrayList(); } documentation.sees.add(reference); return true; } /** * Documents the author (i.e. adds it to the author list). */ boolean documentAuthor(String author) { if (!lazyInitDocumentation()) { return true; } if (documentation.authors == null) { documentation.authors = Lists.newArrayList(); } documentation.authors.add(author); return true; } /** * Documents the throws (i.e. adds it to the throws list). */ boolean documentThrows(JSTypeExpression type, String throwsDescription) { if (!lazyInitDocumentation()) { return true; } if (documentation.throwsDescriptions == null) { documentation.throwsDescriptions = new LinkedHashMap(); } if (!documentation.throwsDescriptions.containsKey(type)) { documentation.throwsDescriptions.put(type, throwsDescription); return true; } return false; } /** * Documents a parameter. Parameters are described using the {@code @param} * annotation. * * @param parameter the parameter's name * @param description the parameter's description */ boolean documentParam(String parameter, String description) { if (!lazyInitDocumentation()) { return true; } if (documentation.parameters == null) { documentation.parameters = new LinkedHashMap(); } if (!documentation.parameters.containsKey(parameter)) { documentation.parameters.put(parameter, description); return true; } else { return false; } } /** * Documents the block-level comment/description. * * @param description the description */ boolean documentBlock(String description) { if (!lazyInitDocumentation()) { return true; } if (documentation.blockDescription != null) { return false; } documentation.blockDescription = description; return true; } /** * Documents the fileoverview comment/description. * * @param description the description */ boolean documentFileOverview(String description) { setFlag(true, MASK_FILEOVERVIEW); if (!lazyInitDocumentation()) { return true; } if (documentation.fileOverview != null) { return false; } documentation.fileOverview = description; return true; } /** * Documents the return value. Return value is described using the * {@code @return} annotation. * * @param description the return value's description */ boolean documentReturn(String description) { if (!lazyInitDocumentation()) { return true; } if (documentation.returnDescription != null) { return false; } documentation.returnDescription = description; return true; } /** * Declares a parameter. Parameters are described using the {@code @param} * annotation. * * @param jsType the parameter's type, it may be {@code null} when the * {@code @param} annotation did not specify a type. * @param parameter the parameter's name */ boolean declareParam(JSTypeExpression jsType, String parameter) { lazyInitInfo(); if (info.parameters == null) { info.parameters = new LinkedHashMap(); } if (!info.parameters.containsKey(parameter)) { info.parameters.put(parameter, jsType); return true; } else { return false; } } /** * Declares a template type name. Template type names are described using the * {@code @template} annotation. * * @param templateTypeNames the template type name. */ boolean declareTemplateTypeNames(List templateTypeNames) { lazyInitInfo(); if (info.templateTypeNames != null) { return false; } info.templateTypeNames = ImmutableList.copyOf(templateTypeNames); return true; } /** * Declares a template type name. Template type names are described using the * {@code @template} annotation. * * @param templateTypeNames the template type name. */ boolean declareClassTemplateTypeNames(List templateTypeNames) { lazyInitInfo(); if (info.classTemplateTypeNames != null) { return false; } info.classTemplateTypeNames = ImmutableList.copyOf(templateTypeNames); return true; } /** * Declares that the method throws a given type. * * @param jsType The type that can be thrown by the method. */ boolean declareThrows(JSTypeExpression jsType) { lazyInitInfo(); if (info.thrownTypes == null) { info.thrownTypes = Lists.newArrayList(); } info.thrownTypes.add(jsType); return true; } /** * Gets the visibility specified by {@code @private}, {@code @protected} or * {@code @public} annotation. If no visibility is specified, visibility * is inherited from the base class. */ public Visibility getVisibility() { return visibility; } /** * Gets the parameter type. * @param parameter the parameter's name * @return the parameter's type or {@code null} if this parameter is not * defined or has a {@code null} type */ public JSTypeExpression getParameterType(String parameter) { if (info == null || info.parameters == null) { return null; } return info.parameters.get(parameter); } /** * Returns whether the parameter is defined. */ public boolean hasParameter(String parameter) { if (info == null || info.parameters == null) { return false; } return info.parameters.containsKey(parameter); } /** * Returns whether the parameter has an attached type. * * @return {@code true} if the parameter has an attached type, {@code false} * if the parameter has no attached type or does not exist. */ public boolean hasParameterType(String parameter) { return getParameterType(parameter) != null; } /** * Returns the set of names of the defined parameters. The iteration order * of the returned set is not the order in which parameters are defined. * * @return the set of names of the defined parameters. The returned set is * immutable. */ public Set getParameterNames() { if (info == null || info.parameters == null) { return ImmutableSet.of(); } return ImmutableSet.copyOf(info.parameters.keySet()); } /** * Gets the number of parameters defined. */ public int getParameterCount() { if (info == null || info.parameters == null) { return 0; } return info.parameters.size(); } void setType(JSTypeExpression type) { setType(type, TYPEFIELD_TYPE); } void setReturnType(JSTypeExpression type) { setType(type, TYPEFIELD_RETURN); } void setEnumParameterType(JSTypeExpression type) { setType(type, TYPEFIELD_ENUM); } void setTypedefType(JSTypeExpression type) { setType(type, TYPEFIELD_TYPEDEF); } private void setType(JSTypeExpression type, int mask) { if ((bitset & MASK_TYPEFIELD) != 0) { throw new IllegalStateException( "API tried to add two incompatible type tags. " + "This should have been blocked and emitted a warning."); } this.bitset = (bitset & MASK_FLAGS) | mask; this.type = type; } /** * Returns the list of thrown types. */ public List getThrownTypes() { if (info == null || info.thrownTypes == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.thrownTypes); } /** * Returns whether a type, specified using the {@code @type} annotation, is * present on this JSDoc. */ public boolean hasType() { return hasType(TYPEFIELD_TYPE); } /** * Returns whether an enum parameter type, specified using the {@code @enum} * annotation, is present on this JSDoc. */ public boolean hasEnumParameterType() { return hasType(TYPEFIELD_ENUM); } /** * Returns whether a typedef parameter type, specified using the * {@code @typedef} annotation, is present on this JSDoc. */ public boolean hasTypedefType() { return hasType(TYPEFIELD_TYPEDEF); } /** * Returns whether this {@link JSDocInfo} contains a type for {@code @return} * annotation. */ public boolean hasReturnType() { return hasType(TYPEFIELD_RETURN); } private boolean hasType(int mask) { return (bitset & MASK_TYPEFIELD) == mask; } /** * Gets the type specified by the {@code @type} annotation. */ public JSTypeExpression getType() { return getType(TYPEFIELD_TYPE); } /** * Gets the return type specified by the {@code @return} annotation. */ public JSTypeExpression getReturnType() { return getType(TYPEFIELD_RETURN); } /** * Gets the enum parameter type specified by the {@code @enum} annotation. */ public JSTypeExpression getEnumParameterType() { return getType(TYPEFIELD_ENUM); } /** * Gets the typedef type specified by the {@code @type} annotation. */ public JSTypeExpression getTypedefType() { return getType(TYPEFIELD_TYPEDEF); } private JSTypeExpression getType(int typefield) { if ((MASK_TYPEFIELD & bitset) == typefield) { return type; } else { return null; } } /** * Gets the type specified by the {@code @this} annotation. */ public JSTypeExpression getThisType() { return thisType; } /** * Sets the type specified by the {@code @this} annotation. */ void setThisType(JSTypeExpression type) { this.thisType = type; } /** * Returns whether this {@link JSDocInfo} contains a type for {@code @this} * annotation. */ public boolean hasThisType() { return thisType != null; } void setBaseType(JSTypeExpression type) { lazyInitInfo(); info.baseType = type; } /** * Gets the base type specified by the {@code @extends} annotation. */ public JSTypeExpression getBaseType() { return (info == null) ? null : info.baseType; } /** * Gets the description specified by the {@code @desc} annotation. */ public String getDescription() { return (info == null) ? null : info.description; } void setDescription(String desc) { lazyInitInfo(); info.description = desc; } /** * Gets the meaning specified by the {@code @meaning} annotation. * * In localization systems, two messages with the same content but * different "meanings" may be translated differently. By default, we * use the name of the variable that the message is initialized to as * the "meaning" of the message. * * But some code generators (like Closure Templates) inject their own * meaning with the jsdoc {@code @meaning} annotation. */ public String getMeaning() { return (info == null) ? null : info.meaning; } void setMeaning(String meaning) { lazyInitInfo(); info.meaning = meaning; } /** * Gets the name we're lending to in a {@code @lends} annotation. * * In many reflection APIs, you pass an anonymous object to a function, * and that function mixes the anonymous object into another object. * The {@code @lends} annotation allows the type system to track * those property assignments. */ public String getLendsName() { return (info == null) ? null : info.lendsName; } void setLendsName(String name) { lazyInitInfo(); info.lendsName = name; } /** * Returns whether JSDoc is annotated with {@code @ngInject} annotation. */ public boolean isNgInject() { return (info == null) ? false : info.ngInject; } void setNgInject(boolean ngInject) { lazyInitInfo(); info.ngInject = ngInject; } /** * Gets the description specified by the {@code @license} annotation. */ public String getLicense() { return (info == null) ? null : info.license; } /** License directives can appear in multiple comments, and always * apply to the entire file. Break protection and allow outsiders to * update the license string so that we can attach the license text even * when the JSDocInfo has been created and tagged with other information. * @param license String containing new license text. */ public void setLicense(String license) { lazyInitInfo(); info.license = license; } @Override public String toString() { return "JSDocInfo"; } /** * Returns whether this {@link JSDocInfo} contains a type for {@code @extends} * annotation. */ public boolean hasBaseType() { return getBaseType() != null; } /** * Adds an implemented interface. Returns whether the interface was added. If * the interface was already present in the list, it won't get added again. */ boolean addImplementedInterface(JSTypeExpression interfaceName) { lazyInitInfo(); if (info.implementedInterfaces == null) { info.implementedInterfaces = Lists.newArrayListWithCapacity(2); } if (info.implementedInterfaces.contains(interfaceName)) { return false; } info.implementedInterfaces.add(interfaceName); return true; } /** * Returns the types specified by the {@code @implements} annotation. * * @return An immutable list of JSTypeExpression objects that can * be resolved to types. */ public List getImplementedInterfaces() { if (info == null || info.implementedInterfaces == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.implementedInterfaces); } /** * Gets the number of interfaces specified by the {@code @implements} * annotation. */ public int getImplementedInterfaceCount() { if (info == null || info.implementedInterfaces == null) { return 0; } return info.implementedInterfaces.size(); } /** * Adds an extended interface (for interface only). * Returns whether the type was added. * if the type was already present in the list, it won't get added again. */ boolean addExtendedInterface(JSTypeExpression type) { lazyInitInfo(); if (info.extendedInterfaces == null) { info.extendedInterfaces = Lists.newArrayListWithCapacity(2); } if (info.extendedInterfaces.contains(type)) { return false; } info.extendedInterfaces.add(type); return true; } /** * Returns the interfaces extended by an interface * * @return An immutable list of JSTypeExpression objects that can * be resolved to types. */ public List getExtendedInterfaces() { if (info == null || info.extendedInterfaces == null) { return ImmutableList.of(); } return Collections.unmodifiableList(info.extendedInterfaces); } /** * Gets the number of extended interfaces specified */ public int getExtendedInterfacesCount() { if (info == null || info.extendedInterfaces == null) { return 0; } return info.extendedInterfaces.size(); } /** * Returns the deprecation reason or null if none specified. */ public String getDeprecationReason() { return info == null ? null : info.deprecated; } /** * Returns the set of suppressed warnings. */ public Set getSuppressions() { Set suppressions = info == null ? null : info.suppressions; return suppressions == null ? Collections.emptySet() : suppressions; } /** * Returns the set of sideeffect notations. */ public Set getModifies() { Set modifies = info == null ? null : info.modifies; return modifies == null ? Collections.emptySet() : modifies; } /** * Returns whether a description exists for the parameter with the specified * name. */ public boolean hasDescriptionForParameter(String name) { if (documentation == null || documentation.parameters == null) { return false; } return documentation.parameters.containsKey(name); } /** * Returns the description for the parameter with the given name, if its * exists. */ public String getDescriptionForParameter(String name) { if (documentation == null || documentation.parameters == null) { return null; } return documentation.parameters.get(name); } /** * Returns the list of authors or null if none. */ public Collection getAuthors() { return documentation == null ? null : documentation.authors; } /** * Returns the list of references or null if none. */ public Collection getReferences() { return documentation == null ? null : documentation.sees; } /** * Returns the version or null if none. */ public String getVersion() { return documentation == null ? null : documentation.version; } /** * Returns the description of the returned object or null if none specified. */ public String getReturnDescription() { return documentation == null ? null : documentation.returnDescription; } /** * Returns the block-level description or null if none specified. */ public String getBlockDescription() { return documentation == null ? null : documentation.blockDescription; } /** * Returns whether this has a fileoverview flag. */ public boolean hasFileOverview() { return getFlag(MASK_FILEOVERVIEW); } /** * Returns the file overview or null if none specified. */ public String getFileOverview() { return documentation == null ? null : documentation.fileOverview; } public Node getAssociatedNode() { return this.associatedNode; } /** * Sets the node associated with this JSDoc. * Notice that many nodes may have pointer to the same JSDocInfo * object (because we propagate it across the type graph). But there * is only one canonical "owner" node of the JSDocInfo, which corresponds * to its original place in the syntax tree. */ public void setAssociatedNode(Node node) { this.associatedNode = node; } /** Gets the name of the source file that contains this JSDoc. */ public String getSourceName() { return this.associatedNode != null ? this.associatedNode.getSourceFileName() : null; } /** Gets the list of all markers for the documentation in this JSDoc. */ public Collection getMarkers() { return (documentation == null || documentation.markers == null) ? ImmutableList.of() : documentation.markers; } /** Gets the template type name. */ public ImmutableList getTemplateTypeNames() { if (info == null || info.templateTypeNames == null) { return ImmutableList.of(); } return info.templateTypeNames; } public ImmutableList getClassTemplateTypeNames() { if (info == null || info.classTemplateTypeNames == null) { return ImmutableList.of(); } return info.classTemplateTypeNames; } /** * Returns a collection of all type nodes that are a part of this JSDocInfo. * This includes @type, @this, @extends, @implements, @param, @throws, * and @return. Any future type specific JSDoc should make sure to add the * appropriate nodes here. * @return collection of all type nodes */ public Collection getTypeNodes() { List nodes = Lists.newArrayList(); if (type != null) { nodes.add(type.getRoot()); } if (thisType != null) { nodes.add(thisType.getRoot()); } if (info != null) { if (info.baseType != null) { nodes.add(info.baseType.getRoot()); } if (info.extendedInterfaces != null) { for (JSTypeExpression interfaceType : info.extendedInterfaces) { nodes.add(interfaceType.getRoot()); } } if (info.implementedInterfaces != null) { for (JSTypeExpression interfaceType : info.implementedInterfaces) { nodes.add(interfaceType.getRoot()); } } if (info.parameters != null) { for (JSTypeExpression parameterType : info.parameters.values()) { if (parameterType != null) { nodes.add(parameterType.getRoot()); } } } if (info.thrownTypes != null) { for (JSTypeExpression thrownType : info.thrownTypes) { if (thrownType != null) { nodes.add(thrownType.getRoot()); } } } } return nodes; } public boolean hasModifies() { return info != null && info.modifies != null; } /** * Returns the original JSDoc comment string. Returns null unless * parseJsDocDocumentation is enabled via the ParserConfig. */ public String getOriginalCommentString() { return documentation == null ? null : documentation.sourceComment; } void setOriginalCommentString(String sourceComment) { if (!lazyInitDocumentation()) { return; } documentation.sourceComment = sourceComment; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/Messages.properties0000644000175000017500000001521012115204405026344 0ustar apoapo# # Default JavaScript messages file. # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (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.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Rhino code, released # May 6, 1999. # # The Initial Developer of the Original Code is # Netscape Communications Corporation. # Portions created by the Initial Developer are Copyright (C) 1997-1999 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Norris Boyd # Bob Jervis # Pascal-Louis Perez # # Alternatively, the contents of this file may be used under the terms of # the GNU General Public License Version 2 or later (the "GPL"), in which # case the provisions of the GPL are applicable instead of those above. If # you wish to allow use of your version of this file only under the terms of # the GPL and not to allow others to use your version of this file under the # MPL, indicate your decision by deleting the provisions above and replacing # them with the notice and other provisions required by the GPL. If you do # not delete the provisions above, a recipient may use your version of this # file under either the MPL or the GPL. # # ***** END LICENSE BLOCK ***** # This is replaced during jar assembly from property string # and should not be translated implementation.version = @IMPLEMENTATION.VERSION@ # # To add JavaScript error messages for a particular locale, create a # new Messages_[locale].properties file, where [locale] is the Java # string abbreviation for that locale. For example, JavaScript # messages for the Polish locale should be located in # Messages_pl.properties, and messages for the Italian Swiss locale # should be located in Messages_it_CH.properties. Message properties # files should be accessible through the classpath under # org.mozilla.javascript.resources # # See: # java.util.ResourceBundle # java.text.MessageFormat # # SomeJavaClassWhereUsed # Codegen msg.dup.parms =\ Duplicate parameter name "{0}". msg.unexpected.eof =\ Unexpected end of file msg.extra.trailing.comma =\ Trailing comma is not legal in an ECMA-262 object initializer msg.end.annotation.expected =\ expected end of line or comment msg.bad.jsdoc.tag =\ illegal use of unknown JSDoc tag "{0}"; ignoring it msg.missing.variable.name =\ expecting a variable name in a @param tag msg.dup.variable.name =\ duplicate variable name "{0}" msg.jsdoc.incompat.type =\ type annotation incompatible with other annotations msg.jsdoc.type.syntax =\ type not recognized due to syntax error msg.jsdoc.override =\ extra @override/@inheritDoc tag msg.jsdoc.preservertry =\ extra @preserveTry tag msg.jsdoc.visibility.private =\ extra @private tag msg.jsdoc.visibility.protected =\ extra @protected tag msg.jsdoc.visibility.public =\ extra @public tag msg.jsdoc.idgen =\ extra @idGenerator tag msg.jsdoc.hidden =\ extra @hidden tag msg.jsdoc.nocheck =\ extra @notypecheck tag msg.jsdoc.consistidgen =\ extra @consistentIdGenerator tag msg.jsdoc.const =\ conflicting @const tag msg.jsdoc.desc.extra =\ extra @desc tag msg.jsdoc.meaning.extra =\ extra @meaning tag msg.jsdoc.fileoverview.extra =\ extra @fileoverview tag msg.jsdoc.lends.incompatible =\ @lends tag incompatible with other annotations msg.jsdoc.lends.missing =\ missing object name in @lends tag msg.jsdoc.preserve.nobuilder =\ @preserve or @license annotation without file to associate it with msg.jsdoc.missing.lp =\ missing opening ( msg.jsdoc.missing.lb =\ missing opening [ msg.jsdoc.missing.rc =\ expected closing } msg.jsdoc.missing.rp =\ missing closing ) msg.jsdoc.missing.gt =\ missing closing > msg.jsdoc.missing.rb =\ missing closing ] msg.jsdoc.missing.colon =\ expecting colon after this msg.jsdoc.function.this =\ expecting this but {0} found msg.jsdoc.function.thisnotobject =\ this type must be an object type msg.jsdoc.function.newnotobject =\ constructed type must be an object type msg.jsdoc.function.varargs =\ variable length argument must be last msg.jsdoc.type.union =\ union type element with bad syntax msg.jsdoc.enum =\ conflicting @enum tag msg.jsdoc.constructor =\ conflicting @constructor tag msg.jsdoc.deprecated =\ extra @deprecated tag msg.jsdoc.interface =\ extra @interface tag msg.jsdoc.interface.constructor =\ cannot be both an interface and a constructor msg.jsdoc.implements.duplicate =\ duplicate @implements tag msg.jsdoc.noalias =\ extra @noalias tag msg.jsdoc.noshadow =\ extra @noshadow tag msg.jsdoc.nosideeffects =\ conflicting @nosideeffects tag msg.jsdoc.implicitcast =\ extra @implicitCast tag msg.jsdoc.this =\ conflicting @this tag msg.jsdoc.this.object =\ @this must specify an object type msg.jsdoc.type =\ conflicting @type tag msg.jsdoc.define =\ conflicting @define tag msg.jsdoc.define.badtype =\ @define tag only permits literal types msg.jsdoc.extends =\ conflicting @extends tag msg.jsdoc.export =\ extra @export tag msg.jsdoc.expose =\ extra @expose tag msg.jsdoc.externs =\ extra @externs tag msg.jsdoc.javadispatch =\ extra @javadispatch tag msg.jsdoc.nocompile =\ extra @nocompile tag msg.jsdoc.seemissing =\ @see tag missing description msg.jsdoc.authormissing =\ @author tag missing author msg.jsdoc.versionmissing =\ @version tag missing version information msg.jsdoc.extraversion =\ conflicting @version tag msg.jsdoc.suppress =\ malformed @suppress tag msg.jsdoc.suppress.duplicate =\ duplicate @suppress tag msg.jsdoc.suppress.unknown =\ unknown @suppress parameter: {0} msg.jsdoc.modifies =\ malformed @modifies tag msg.jsdoc.modifies.duplicate =\ conflicting @modifies tag msg.jsdoc.modifies.unknown =\ unknown @modifies parameter: {0} msg.jsdoc.stableidgen =\ extra @stableIdGenerator tag msg.jsdoc.templatemissing =\ @template tag missing type name msg.jsdoc.template.at.most.once =\ @template tag at most once msg.jsdoc.classtemplate.missing.type.name =\ @classTemplate tag missing type name msg.jsdoc.classtemplate.at.most.once =\ @classTemplate tag at most once msg.jsdoc.nginject.extra =\ extra @ngInject tag msg.no.type.name =\ expecting a type name closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/TokenStream.java0000644000175000017500000002100712115204405025557 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Roger Lawrence * Mike McCabe * Igor Bukanov * Ethan Hugg * Bob Jervis * Terry Lucas * Milen Nankov * Pascal-Louis Perez * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; /** * This class implements the JavaScript scanner. * * It is based on the C source files jsscan.c and jsscan.h * in the jsref package. * */ public class TokenStream { public static boolean isKeyword(String name) { boolean id = false; String s = name; complete: { String X = null; int c; partial: switch (s.length()) { case 2: c=s.charAt(1); if (c=='f') { if (s.charAt(0)=='i') {id=true; break complete;} } else if (c=='n') { if (s.charAt(0)=='i') {id=true; break complete;} } else if (c=='o') { if (s.charAt(0)=='d') {id=true; break complete;} } break partial; case 3: switch (s.charAt(0)) { case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') { id=true; break complete; } break partial; case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') { id=true; break complete; } break partial; case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') { id=true; break complete; } break partial; case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') { id=true; break complete; } break partial; case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') { id=true; break complete; } break partial; } break partial; case 4: switch (s.charAt(0)) { case 'b': X="byte";id=true; break partial; case 'c': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') { id=true; break complete;} } else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') { id=true; break complete; } } break partial; case 'e': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') { id=true; break complete;} } else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') { id=true; break complete;} } break partial; case 'g': X="goto";id=true; break partial; case 'l': X="long";id=true; break partial; case 'n': X="null";id=true; break partial; case 't': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') { id=true; break complete;} } else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') { id=true; break complete;} } break partial; case 'v': X="void";id=true; break partial; case 'w': X="with";id=true; break partial; } break partial; case 5: switch (s.charAt(2)) { case 'a': X="class";id=true; break partial; case 'e': X="break";id=true; break partial; case 'i': X="while";id=true; break partial; case 'l': X="false";id=true; break partial; case 'n': c=s.charAt(0); if (c=='c') { X="const";id=true; } else if (c=='f') { X="final";id=true; } break partial; case 'o': c=s.charAt(0); if (c=='f') { X="float";id=true; } else if (c=='s') { X="short";id=true; } break partial; case 'p': X="super";id=true; break partial; case 'r': X="throw";id=true; break partial; case 't': X="catch";id=true; break partial; } break partial; case 6: switch (s.charAt(1)) { case 'a': X="native";id=true; break partial; case 'e': c=s.charAt(0); if (c=='d') { X="delete";id=true; } else if (c=='r') { X="return";id=true; } break partial; case 'h': X="throws";id=true; break partial; case 'm': X="import";id=true; break partial; case 'o': X="double";id=true; break partial; case 't': X="static";id=true; break partial; case 'u': X="public";id=true; break partial; case 'w': X="switch";id=true; break partial; case 'x': X="export";id=true; break partial; case 'y': X="typeof";id=true; break partial; } break partial; case 7: switch (s.charAt(1)) { case 'a': X="package";id=true; break partial; case 'e': X="default";id=true; break partial; case 'i': X="finally";id=true; break partial; case 'o': X="boolean";id=true; break partial; case 'r': X="private";id=true; break partial; case 'x': X="extends";id=true; break partial; } break partial; case 8: switch (s.charAt(0)) { case 'a': X="abstract";id=true; break partial; case 'c': X="continue";id=true; break partial; case 'd': X="debugger";id=true; break partial; case 'f': X="function";id=true; break partial; case 'v': X="volatile";id=true; break partial; } break partial; case 9: c=s.charAt(0); if (c=='i') { X="interface";id=true; } else if (c=='p') { X="protected";id=true; } else if (c=='t') { X="transient";id=true; } break partial; case 10: c=s.charAt(1); if (c=='m') { X="implements";id=true; } else if (c=='n') { X="instanceof";id=true; } break partial; case 12: X="synchronized";id=true; break partial; } // partial match validate the entire string the one possibility if (X!=null && X!=s && !X.equals(s)) return false; } return id; } public static boolean isJSIdentifier(String s) { int length = s.length(); if (length == 0 || Character.isIdentifierIgnorable(s.charAt(0)) || !Character.isJavaIdentifierStart(s.charAt(0))) { return false; } for (int i = 1; i < length; i++) { if (Character.isIdentifierIgnorable(s.charAt(i)) || !Character.isJavaIdentifierPart(s.charAt(i))) { return false; } } return true; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/SourcePosition.java0000644000175000017500000000741012115204405026312 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; /** * Represents a position in some piece of source code, with an associated * item of type T found at that position. * */ public abstract class SourcePosition { /** * The (well typed) item found at the source position. */ private T item = null; /** * The starting line number. */ private int startLineno = 0; /** * The character position on the starting line. */ private int startCharno = 0; /** * The ending line number. */ private int endLineno = 0; /** * The character position on the ending line. */ private int endCharno = 0; /** * Sets the item that this source position references. */ public void setItem(T item) { this.item = item; } /** * Sets the position information contained in this source position. */ public void setPositionInformation(int startLineno, int startCharno, int endLineno, int endCharno) { if (startLineno == endLineno) { if (startCharno >= endCharno) { throw new IllegalStateException( "Recorded bad position information\n" + "start-char: " + startCharno + "\n" + "end-char: " + endCharno); } } else { if (startLineno > endLineno) { throw new IllegalStateException( "Recorded bad position information\n" + "start-line: " + startLineno + "\n" + "end-line: " + endLineno); } } this.startLineno = startLineno; this.startCharno = startCharno; this.endLineno = endLineno; this.endCharno = endCharno; } /** * Returns the item found at this source position. */ public T getItem() { return item; } /** * Returns the starting line number of this position. */ public int getStartLine() { return startLineno; } /** * Returns the character position on the starting line. */ public int getPositionOnStartLine() { return startCharno; } /** * Returns the ending line number of this position. */ public int getEndLine() { return endLineno; } /** * Returns the character position on the ending line. */ public int getPositionOnEndLine() { return endCharno; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/ScriptRuntime.java0000644000175000017500000003434412115204405026143 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Patrick Beard * Norris Boyd * Igor Bukanov * Ethan Hugg * Roger Lawrence * Terry Lucas * Frank Mitchell * Milen Nankov * Andrew Wason * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import java.text.MessageFormat; import java.util.Locale; import java.util.ResourceBundle; /** * This is the class that implements the run-time. * */ public class ScriptRuntime { /** * No instances should be created. */ protected ScriptRuntime() { } // It is public so NativeRegExp can access it . public static boolean isJSLineTerminator(int c) { // Optimization for faster check for EOL character: // they do not have 0xDFD0 bits set if ((c & 0xDFD0) != 0) { return false; } return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; } // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM, // versions 2.01 and 3.0P1, that causes some uses (returns at least) of // Double.NaN to be converted to 1.0. // So we use ScriptRuntime.NaN instead of Double.NaN. public static final double NaN = Double.longBitsToDouble(0x7ff8000000000000L); // A similar problem exists for negative zero. public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L); /* * Helper function for toNumber, parseInt, and TokenStream.getToken. */ @SuppressWarnings("fallthrough") static double stringToNumber(String s, int start, int radix) { char digitMax = '9'; char lowerCaseBound = 'a'; char upperCaseBound = 'A'; int len = s.length(); if (radix < 10) { digitMax = (char) ('0' + radix - 1); } if (radix > 10) { lowerCaseBound = (char) ('a' + radix - 10); upperCaseBound = (char) ('A' + radix - 10); } int end; double sum = 0.0; for (end=start; end < len; end++) { char c = s.charAt(end); int newDigit; if ('0' <= c && c <= digitMax) newDigit = c - '0'; else if ('a' <= c && c < lowerCaseBound) newDigit = c - 'a' + 10; else if ('A' <= c && c < upperCaseBound) newDigit = c - 'A' + 10; else break; sum = sum*radix + newDigit; } if (start == end) { return NaN; } if (sum >= 9007199254740992.0) { if (radix == 10) { /* If we're accumulating a decimal number and the number * is >= 2^53, then the result from the repeated multiply-add * above may be inaccurate. Call Java to get the correct * answer. */ try { return Double.valueOf(s.substring(start, end)).doubleValue(); } catch (NumberFormatException nfe) { return NaN; } } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { /* The number may also be inaccurate for one of these bases. * This happens if the addition in value*radix + digit causes * a round-down to an even least significant mantissa bit * when the first dropped bit is a one. If any of the * following digits in the number (which haven't been added * in yet) are nonzero then the correct action would have * been to round up instead of down. An example of this * occurs when reading the number 0x1000000000000081, which * rounds to 0x1000000000000000 instead of 0x1000000000000100. */ int bitShiftInChar = 1; int digit = 0; final int SKIP_LEADING_ZEROS = 0; final int FIRST_EXACT_53_BITS = 1; final int AFTER_BIT_53 = 2; final int ZEROS_AFTER_54 = 3; final int MIXED_AFTER_54 = 4; int state = SKIP_LEADING_ZEROS; int exactBitsLimit = 53; double factor = 0.0; boolean bit53 = false; // bit54 is the 54th bit (the first dropped from the mantissa) boolean bit54 = false; for (;;) { if (bitShiftInChar == 1) { if (start == end) break; digit = s.charAt(start++); if ('0' <= digit && digit <= '9') digit -= '0'; else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10; else digit -= 'A' - 10; bitShiftInChar = radix; } bitShiftInChar >>= 1; boolean bit = (digit & bitShiftInChar) != 0; switch (state) { case SKIP_LEADING_ZEROS: if (bit) { --exactBitsLimit; sum = 1.0; state = FIRST_EXACT_53_BITS; } break; case FIRST_EXACT_53_BITS: sum *= 2.0; if (bit) sum += 1.0; --exactBitsLimit; if (exactBitsLimit == 0) { bit53 = bit; state = AFTER_BIT_53; } break; case AFTER_BIT_53: bit54 = bit; factor = 2.0; state = ZEROS_AFTER_54; break; case ZEROS_AFTER_54: if (bit) { state = MIXED_AFTER_54; } // fallthrough case MIXED_AFTER_54: factor *= 2; break; } } switch (state) { case SKIP_LEADING_ZEROS: sum = 0.0; break; case FIRST_EXACT_53_BITS: case AFTER_BIT_53: // do nothing break; case ZEROS_AFTER_54: // x1.1 -> x1 + 1 (round up) // x0.1 -> x0 (round down) if (bit54 & bit53) sum += 1.0; sum *= factor; break; case MIXED_AFTER_54: // x.100...1.. -> x + 1 (round up) // x.0anything -> x (round down) if (bit54) sum += 1.0; sum *= factor; break; } } /* We don't worry about inaccurate numbers for any other base. */ } return sum; } public static String escapeString(String s) { return escapeString(s, '"'); } /** * For escaping strings printed by object and array literals; not quite * the same as 'escape.' */ public static String escapeString(String s, char escapeQuote) { if (!(escapeQuote == '"' || escapeQuote == '\'')) { throw new IllegalStateException("unexpected quote char:" + escapeQuote); } StringBuffer sb = null; for(int i = 0, L = s.length(); i != L; ++i) { int c = s.charAt(i); if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { // an ordinary print character (like C isprint()) and not " // or \ . if (sb != null) { sb.append((char)c); } continue; } if (sb == null) { sb = new StringBuffer(L + 3); sb.append(s); sb.setLength(i); } int escape = -1; switch (c) { case '\b': escape = 'b'; break; case '\f': escape = 'f'; break; case '\n': escape = 'n'; break; case '\r': escape = 'r'; break; case '\t': escape = 't'; break; case 0xb: escape = 'v'; break; // Java lacks \v. case ' ': escape = ' '; break; case '\\': escape = '\\'; break; } if (escape >= 0) { // an \escaped sort of character sb.append('\\'); sb.append((char)escape); } else if (c == escapeQuote) { sb.append('\\'); sb.append(escapeQuote); } else { int hexSize; if (c < 256) { // 2-digit hex sb.append("\\x"); hexSize = 2; } else { // Unicode. sb.append("\\u"); hexSize = 4; } // append hexadecimal form of c left-padded with 0 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { int digit = 0xf & (c >> shift); int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; sb.append((char)hc); } } } return (sb == null) ? s : sb.toString(); } static boolean isValidIdentifierName(String s) { int L = s.length(); if (L == 0) return false; if (!Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i != L; ++i) { if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; } return !TokenStream.isKeyword(s); } /** * If str is a decimal presentation of Uint32 value, return it as long. * Otherwise, return -1L; */ public static long testUint32String(String str) { // The length of the decimal string representation of // UINT32_MAX_VALUE, 4294967296 final int MAX_VALUE_LENGTH = 10; int len = str.length(); if (1 <= len && len <= MAX_VALUE_LENGTH) { int c = str.charAt(0); c -= '0'; if (c == 0) { // Note that 00,01 etc. are not valid Uint32 presentations return (len == 1) ? 0L : -1L; } if (1 <= c && c <= 9) { long v = c; for (int i = 1; i != len; ++i) { c = str.charAt(i) - '0'; if (!(0 <= c && c <= 9)) { return -1; } v = 10 * v + c; } // Check for overflow if ((v >>> 32) == 0) { return v; } } } return -1; } static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } // ------------------ // Statements // ------------------ public static String getMessage0(String messageId) { return getMessage(messageId, null); } public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } /* OPT there's a noticeable delay for the first error! Maybe it'd * make sense to use a ListResourceBundle instead of a properties * file to avoid (synchronized) text parsing. */ public static String getMessage(String messageId, Object[] arguments) { final String defaultResource = "rhino_ast.java.com.google.javascript.rhino.Messages"; Locale locale = Locale.getDefault(); // ResourceBundle does caching. ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); String formatString; try { formatString = rb.getString(messageId); } catch (java.util.MissingResourceException mre) { throw new RuntimeException ("no message resource found for message property "+ messageId); } /* * It's OK to format the string, even if 'arguments' is null; * we need to format it anyway, to make double ''s collapse to * single 's. */ // TODO: MessageFormat is not available on pJava MessageFormat formatter = new MessageFormat(formatString); return formatter.format(arguments); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/0000755000175000017500000000000012115204405024135 5ustar apoapoclosure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/BaseJSTypeTestCase.java0000644000175000017500000006203112115204405030407 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.testing; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.FunctionBuilder; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.RecordTypeBuilder; import com.google.javascript.rhino.jstype.TemplatizedType; import junit.framework.TestCase; public abstract class BaseJSTypeTestCase extends TestCase { protected JSTypeRegistry registry; protected TestErrorReporter errorReporter; protected JSType ALL_TYPE; protected ObjectType NO_OBJECT_TYPE; protected ObjectType NO_TYPE; protected ObjectType NO_RESOLVED_TYPE; protected FunctionType ARRAY_FUNCTION_TYPE; protected ObjectType ARRAY_TYPE; protected JSType BOOLEAN_OBJECT_FUNCTION_TYPE; protected ObjectType BOOLEAN_OBJECT_TYPE; protected JSType BOOLEAN_TYPE; protected ObjectType CHECKED_UNKNOWN_TYPE; protected JSType DATE_FUNCTION_TYPE; protected ObjectType DATE_TYPE; protected JSType ERROR_FUNCTION_TYPE; protected ObjectType ERROR_TYPE; protected JSType EVAL_ERROR_FUNCTION_TYPE; protected ObjectType EVAL_ERROR_TYPE; protected FunctionType FUNCTION_FUNCTION_TYPE; protected FunctionType FUNCTION_INSTANCE_TYPE; protected ObjectType FUNCTION_PROTOTYPE; protected JSType GREATEST_FUNCTION_TYPE; protected JSType LEAST_FUNCTION_TYPE; protected JSType MATH_TYPE; protected JSType NULL_TYPE; protected JSType NUMBER_OBJECT_FUNCTION_TYPE; protected ObjectType NUMBER_OBJECT_TYPE; protected JSType NUMBER_STRING_BOOLEAN; protected JSType NUMBER_TYPE; protected FunctionType OBJECT_FUNCTION_TYPE; protected JSType NULL_VOID; protected JSType OBJECT_NUMBER_STRING; protected JSType OBJECT_NUMBER_STRING_BOOLEAN; protected JSType OBJECT_PROTOTYPE; protected ObjectType OBJECT_TYPE; protected JSType RANGE_ERROR_FUNCTION_TYPE; protected ObjectType RANGE_ERROR_TYPE; protected JSType REFERENCE_ERROR_FUNCTION_TYPE; protected ObjectType REFERENCE_ERROR_TYPE; protected JSType REGEXP_FUNCTION_TYPE; protected ObjectType REGEXP_TYPE; protected JSType STRING_OBJECT_FUNCTION_TYPE; protected ObjectType STRING_OBJECT_TYPE; protected JSType STRING_TYPE; protected JSType SYNTAX_ERROR_FUNCTION_TYPE; protected ObjectType SYNTAX_ERROR_TYPE; protected JSType TYPE_ERROR_FUNCTION_TYPE; protected ObjectType TYPE_ERROR_TYPE; protected FunctionType U2U_CONSTRUCTOR_TYPE; protected FunctionType U2U_FUNCTION_TYPE; protected ObjectType UNKNOWN_TYPE; protected JSType URI_ERROR_FUNCTION_TYPE; protected ObjectType URI_ERROR_TYPE; protected JSType VOID_TYPE; protected int NATIVE_PROPERTIES_COUNT; @Override protected void setUp() throws Exception { super.setUp(); errorReporter = new TestErrorReporter(null, null); registry = new JSTypeRegistry(errorReporter); initTypes(); } protected void initTypes() { ALL_TYPE = registry.getNativeType(JSTypeNative.ALL_TYPE); NO_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); NO_TYPE = registry.getNativeObjectType(JSTypeNative.NO_TYPE); NO_RESOLVED_TYPE = registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE); ARRAY_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE); ARRAY_TYPE = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); BOOLEAN_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE); BOOLEAN_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); BOOLEAN_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); CHECKED_UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.CHECKED_UNKNOWN_TYPE); DATE_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE); DATE_TYPE = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE); ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.ERROR_TYPE); EVAL_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE); EVAL_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.EVAL_ERROR_TYPE); FUNCTION_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_FUNCTION_TYPE); FUNCTION_INSTANCE_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_INSTANCE_TYPE); FUNCTION_PROTOTYPE = registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE); GREATEST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE); LEAST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE); NULL_TYPE = registry.getNativeType(JSTypeNative.NULL_TYPE); NUMBER_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE); NUMBER_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); NUMBER_STRING_BOOLEAN = registry.getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN); NUMBER_TYPE = registry.getNativeType(JSTypeNative.NUMBER_TYPE); OBJECT_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE); NULL_VOID = registry.getNativeType(JSTypeNative.NULL_VOID); OBJECT_NUMBER_STRING = registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING); OBJECT_NUMBER_STRING_BOOLEAN = registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN); OBJECT_PROTOTYPE = registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE); OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); RANGE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.RANGE_ERROR_FUNCTION_TYPE); RANGE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.RANGE_ERROR_TYPE); REFERENCE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE); REFERENCE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.REFERENCE_ERROR_TYPE); REGEXP_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE); REGEXP_TYPE = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); STRING_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE); STRING_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); STRING_TYPE = registry.getNativeType(JSTypeNative.STRING_TYPE); SYNTAX_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE); SYNTAX_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE); TYPE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE); TYPE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE); U2U_CONSTRUCTOR_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); U2U_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE); UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); URI_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE); URI_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE); VOID_TYPE = registry.getNativeType(JSTypeNative.VOID_TYPE); addNativeProperties(registry); NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount(); } /** Adds a basic set of properties to the native types. */ public static void addNativeProperties(JSTypeRegistry registry) { JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE); JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE); JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); ObjectType objectType = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); ObjectType arrayType = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); ObjectType dateType = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ObjectType regexpType = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); ObjectType booleanObjectType = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); ObjectType numberObjectType = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); ObjectType stringObjectType = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); ObjectType objectPrototype = registry .getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE) .getPrototype(); addMethod(registry, objectPrototype, "constructor", objectType); addMethod(registry, objectPrototype, "toString", stringType); addMethod(registry, objectPrototype, "toLocaleString", stringType); addMethod(registry, objectPrototype, "valueOf", unknownType); addMethod(registry, objectPrototype, "hasOwnProperty", booleanType); addMethod(registry, objectPrototype, "isPrototypeOf", booleanType); addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType); ObjectType arrayPrototype = registry .getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE) .getPrototype(); addMethod(registry, arrayPrototype, "constructor", arrayType); addMethod(registry, arrayPrototype, "toString", stringType); addMethod(registry, arrayPrototype, "toLocaleString", stringType); addMethod(registry, arrayPrototype, "concat", arrayType); addMethod(registry, arrayPrototype, "join", stringType); addMethod(registry, arrayPrototype, "pop", unknownType); addMethod(registry, arrayPrototype, "push", numberType); addMethod(registry, arrayPrototype, "reverse", arrayType); addMethod(registry, arrayPrototype, "shift", unknownType); addMethod(registry, arrayPrototype, "slice", arrayType); addMethod(registry, arrayPrototype, "sort", arrayType); addMethod(registry, arrayPrototype, "splice", arrayType); addMethod(registry, arrayPrototype, "unshift", numberType); arrayType.defineDeclaredProperty("length", numberType, null); ObjectType booleanPrototype = registry .getNativeFunctionType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE) .getPrototype(); addMethod(registry, booleanPrototype, "constructor", booleanObjectType); addMethod(registry, booleanPrototype, "toString", stringType); addMethod(registry, booleanPrototype, "valueOf", booleanType); ObjectType datePrototype = registry .getNativeFunctionType(JSTypeNative.DATE_FUNCTION_TYPE) .getPrototype(); addMethod(registry, datePrototype, "constructor", dateType); addMethod(registry, datePrototype, "toString", stringType); addMethod(registry, datePrototype, "toDateString", stringType); addMethod(registry, datePrototype, "toTimeString", stringType); addMethod(registry, datePrototype, "toLocaleString", stringType); addMethod(registry, datePrototype, "toLocaleDateString", stringType); addMethod(registry, datePrototype, "toLocaleTimeString", stringType); addMethod(registry, datePrototype, "valueOf", numberType); addMethod(registry, datePrototype, "getTime", numberType); addMethod(registry, datePrototype, "getFullYear", numberType); addMethod(registry, datePrototype, "getUTCFullYear", numberType); addMethod(registry, datePrototype, "getMonth", numberType); addMethod(registry, datePrototype, "getUTCMonth", numberType); addMethod(registry, datePrototype, "getDate", numberType); addMethod(registry, datePrototype, "getUTCDate", numberType); addMethod(registry, datePrototype, "getDay", numberType); addMethod(registry, datePrototype, "getUTCDay", numberType); addMethod(registry, datePrototype, "getHours", numberType); addMethod(registry, datePrototype, "getUTCHours", numberType); addMethod(registry, datePrototype, "getMinutes", numberType); addMethod(registry, datePrototype, "getUTCMinutes", numberType); addMethod(registry, datePrototype, "getSeconds", numberType); addMethod(registry, datePrototype, "getUTCSeconds", numberType); addMethod(registry, datePrototype, "getMilliseconds", numberType); addMethod(registry, datePrototype, "getUTCMilliseconds", numberType); addMethod(registry, datePrototype, "getTimezoneOffset", numberType); addMethod(registry, datePrototype, "setTime", numberType); addMethod(registry, datePrototype, "setMilliseconds", numberType); addMethod(registry, datePrototype, "setUTCMilliseconds", numberType); addMethod(registry, datePrototype, "setSeconds", numberType); addMethod(registry, datePrototype, "setUTCSeconds", numberType); addMethod(registry, datePrototype, "setMinutes", numberType); addMethod(registry, datePrototype, "setUTCMinutes", numberType); addMethod(registry, datePrototype, "setHours", numberType); addMethod(registry, datePrototype, "setUTCHours", numberType); addMethod(registry, datePrototype, "setDate", numberType); addMethod(registry, datePrototype, "setUTCDate", numberType); addMethod(registry, datePrototype, "setMonth", numberType); addMethod(registry, datePrototype, "setUTCMonth", numberType); addMethod(registry, datePrototype, "setFullYear", numberType); addMethod(registry, datePrototype, "setUTCFullYear", numberType); addMethod(registry, datePrototype, "toUTCString", stringType); addMethod(registry, datePrototype, "toGMTString", stringType); ObjectType numberPrototype = registry .getNativeFunctionType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE) .getPrototype(); addMethod(registry, numberPrototype, "constructor", numberObjectType); addMethod(registry, numberPrototype, "toString", stringType); addMethod(registry, numberPrototype, "toLocaleString", stringType); addMethod(registry, numberPrototype, "valueOf", numberType); addMethod(registry, numberPrototype, "toFixed", stringType); addMethod(registry, numberPrototype, "toExponential", stringType); addMethod(registry, numberPrototype, "toPrecision", stringType); ObjectType regexpPrototype = registry .getNativeFunctionType(JSTypeNative.REGEXP_FUNCTION_TYPE) .getPrototype(); addMethod(registry, regexpPrototype, "constructor", regexpType); addMethod(registry, regexpPrototype, "exec", registry.createNullableType(arrayType)); addMethod(registry, regexpPrototype, "test", booleanType); addMethod(registry, regexpPrototype, "toString", stringType); regexpType.defineDeclaredProperty("source", stringType, null); regexpType.defineDeclaredProperty("global", booleanType, null); regexpType.defineDeclaredProperty("ignoreCase", booleanType, null); regexpType.defineDeclaredProperty("multiline", booleanType, null); regexpType.defineDeclaredProperty("lastIndex", numberType, null); ObjectType stringPrototype = registry .getNativeFunctionType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE) .getPrototype(); addMethod(registry, stringPrototype, "constructor", stringObjectType); addMethod(registry, stringPrototype, "toString", stringType); addMethod(registry, stringPrototype, "valueOf", stringType); addMethod(registry, stringPrototype, "charAt", stringType); addMethod(registry, stringPrototype, "charCodeAt", numberType); addMethod(registry, stringPrototype, "concat", stringType); addMethod(registry, stringPrototype, "indexOf", numberType); addMethod(registry, stringPrototype, "lastIndexOf", numberType); addMethod(registry, stringPrototype, "localeCompare", numberType); addMethod(registry, stringPrototype, "match", registry.createNullableType(arrayType)); addMethod(registry, stringPrototype, "replace", stringType); addMethod(registry, stringPrototype, "search", numberType); addMethod(registry, stringPrototype, "slice", stringType); addMethod(registry, stringPrototype, "split", arrayType); addMethod(registry, stringPrototype, "substring", stringType); addMethod(registry, stringPrototype, "toLowerCase", stringType); addMethod(registry, stringPrototype, "toLocaleLowerCase", stringType); addMethod(registry, stringPrototype, "toUpperCase", stringType); addMethod(registry, stringPrototype, "toLocaleUpperCase", stringType); stringObjectType.defineDeclaredProperty("length", numberType, null); } private static void addMethod( JSTypeRegistry registry, ObjectType receivingType, String methodName, JSType returnType) { receivingType.defineDeclaredProperty(methodName, new FunctionBuilder(registry).withReturnType(returnType).build(), null); } protected JSType createUnionType(JSType... variants) { return registry.createUnionType(variants); } protected RecordTypeBuilder createRecordTypeBuilder() { return new RecordTypeBuilder(registry); } protected JSType createNullableType(JSType type) { return registry.createNullableType(type); } protected JSType createOptionalType(JSType type) { return registry.createOptionalType(type); } protected TemplatizedType createTemplatizedType( ObjectType baseType, ImmutableList templatizedTypes) { return registry.createTemplatizedType(baseType, templatizedTypes); } protected TemplatizedType createTemplatizedType( ObjectType baseType, JSType... templatizedType) { return createTemplatizedType( baseType, ImmutableList.copyOf(templatizedType)); } /** * Asserts that a Node representing a type expression resolves to the * correct {@code JSType}. */ protected void assertTypeEquals(JSType expected, Node actual) { assertTypeEquals(expected, new JSTypeExpression(actual, "")); } /** * Asserts that a a type expression resolves to the correct {@code JSType}. */ protected void assertTypeEquals(JSType expected, JSTypeExpression actual) { assertEquals(expected, resolve(actual)); } /** * Resolves a type expression, expecting the given warnings. */ protected JSType resolve(JSTypeExpression n, String... warnings) { errorReporter.setWarnings(warnings); return n.evaluate(null, registry); } /** * A definition of all extern types. This should be kept in sync with * javascript/externs/es3.js. This is used to check that the built-in types * declared in {@link JSTypeRegistry} have the same type as that in the * externs. It can also be used for any tests that want to use built-in types * in their externs. */ public static final String ALL_NATIVE_EXTERN_TYPES = "/**\n" + " * @constructor\n" + " * @param {*=} opt_value\n" + " */\n" + "function Object(opt_value) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {...*} var_args\n" + " */\n" + "\n" + "function Function(var_args) {}\n" + "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {...*} var_args\n" + " * @return {!Array}\n" + " */\n" + "function Array(var_args) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {*=} opt_value\n" + " * @return {boolean}\n" + " */\n" + "function Boolean(opt_value) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {*=} opt_value\n" + " * @return {number}\n" + " */\n" + "function Number(opt_value) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {?=} opt_yr_num\n" + " * @param {?=} opt_mo_num\n" + " * @param {?=} opt_day_num\n" + " * @param {?=} opt_hr_num\n" + " * @param {?=} opt_min_num\n" + " * @param {?=} opt_sec_num\n" + " * @param {?=} opt_ms_num\n" + " * @return {string}\n" + " */\n" + "function Date(opt_yr_num, opt_mo_num, opt_day_num, opt_hr_num," + " opt_min_num, opt_sec_num, opt_ms_num) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Object}\n" + " * @param {*=} opt_str\n" + " * @return {string}\n" + " */\n" + "function String(opt_str) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {*=} opt_pattern\n" + " * @param {*=} opt_flags\n" + " * @return {!RegExp}\n" + " */\n" + "function RegExp(opt_pattern, opt_flags) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!Error}\n" + " */\n" + "function Error(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!EvalError}\n" + " */\n" + "function EvalError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!RangeError}\n" + " */\n" + "function RangeError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!ReferenceError}\n" + " */\n" + "function ReferenceError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!SyntaxError}\n" + " */\n" + "function SyntaxError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!TypeError}\n" + " */\n" + "function TypeError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @constructor\n" + " * @extends {Error}\n" + " * @param {*=} opt_message\n" + " * @param {*=} opt_file\n" + " * @param {*=} opt_line\n" + " * @return {!URIError}\n" + " */\n" + "function URIError(opt_message, opt_file, opt_line) {}\n" + "\n" + "/**\n" + " * @param {string} progId\n" + " * @param {string=} opt_location\n" + " * @constructor\n" + " */\n" + "function ActiveXObject(progId, opt_location) {}\n"; protected final void assertTypeEquals(JSType a, JSType b) { Asserts.assertTypeEquals(a, b); } protected final void assertTypeEquals(String msg, JSType a, JSType b) { Asserts.assertTypeEquals(msg, a, b); } protected final void assertTypeNotEquals(JSType a, JSType b) { Asserts.assertTypeNotEquals(a, b); } protected final void assertTypeNotEquals(String msg, JSType a, JSType b) { Asserts.assertTypeNotEquals(msg, a, b); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/AbstractStaticScope.java0000644000175000017500000000440312115204405030706 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.testing; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.jstype.StaticScope; import com.google.javascript.rhino.jstype.StaticSlot; /** * A scope that just returns null for everything. * @author nicksantos@google.com (Nick Santos) */ public abstract class AbstractStaticScope implements StaticScope { @Override public Node getRootNode() { return null; } @Override public StaticScope getParentScope() { return null; } @Override public abstract StaticSlot getSlot(String name); @Override public StaticSlot getOwnSlot(String name) { return getSlot(name); } @Override public T getTypeOfThis() { return null; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/Asserts.java0000644000175000017500000001244212115204405026427 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.testing; import com.google.common.collect.Iterables; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.StaticScope; import junit.framework.Assert; import java.util.Iterator; /** * Helper methods for making assertions about the validity of types. * @author nicksantos@google.com (Nick Santos) */ public class Asserts { private Asserts() {} // all static public static JSType assertResolvesToSame(JSType type) { Assert.assertSame(type, assertValidResolve(type)); return type; } /** @return The resolved type */ public static JSType assertValidResolve(JSType type) { return assertValidResolve(type, MapBasedScope.emptyScope()); } /** @return The resolved type */ public static JSType assertValidResolve( JSType type, StaticScope scope) { ErrorReporter t = TestErrorReporter.forNoExpectedReports(); JSType resolvedType = type.resolve(t, scope); assertTypeEquals("JSType#resolve should not affect object equality", type, resolvedType); return resolvedType; } public static void assertTypeNotEquals(JSType a, JSType b) { assertTypeNotEquals("", a, b); } public static void assertTypeNotEquals(String message, JSType a, JSType b) { Assert.assertFalse( message + (message.isEmpty() ? "" : "\n") + "Type: " + b + "\n", a.isEquivalentTo(b)); Assert.assertFalse( message + " Equals is not symmetric.\n" + "Type: " + b + "\n", b.isEquivalentTo(a)); } public static void assertTypeEquals(JSType a, JSType b) { assertTypeEquals("", a, b); } public static void assertTypeEquals(String message, JSType a, JSType b) { Assert.assertTrue( "Both types must be null, or both must be non-null " + a + "," + b, (a == null) == (b == null)); if (a == null) { return; } Assert.assertTrue( message + (message.isEmpty() ? "" : "\n") + "Expected: " + a + "\n" + "Actual : " + b, a.isEquivalentTo(b)); Assert.assertTrue( message + " Equals is not symmetric.\n" + "Expected: " + b + "\n" + "Actual : " + a, b.isEquivalentTo(a)); } public static void assertTypeCollectionEquals(Iterable a, Iterable b) { Assert.assertEquals(Iterables.size(a), Iterables.size(b)); Iterator aIterator = a.iterator(); Iterator bIterator = b.iterator(); while (aIterator.hasNext()) { assertTypeEquals(aIterator.next(), bIterator.next()); } } /** * For the given equivalent types, run all type operations that * should have trivial solutions (getGreatestSubtype, isEquivalentTo, etc) */ public static void assertEquivalenceOperations(JSType a, JSType b) { Assert.assertTrue(a.isEquivalentTo(b)); Assert.assertTrue(a.isEquivalentTo(a)); Assert.assertTrue(b.isEquivalentTo(b)); Assert.assertTrue(b.isEquivalentTo(a)); Assert.assertTrue(a.isSubtype(b)); Assert.assertTrue(a.isSubtype(a)); Assert.assertTrue(b.isSubtype(b)); Assert.assertTrue(b.isSubtype(a)); assertTypeEquals(a, a.getGreatestSubtype(b)); assertTypeEquals(a, a.getGreatestSubtype(a)); assertTypeEquals(a, b.getGreatestSubtype(b)); assertTypeEquals(a, b.getGreatestSubtype(a)); assertTypeEquals(a, a.getLeastSupertype(b)); assertTypeEquals(a, a.getLeastSupertype(a)); assertTypeEquals(a, b.getLeastSupertype(b)); assertTypeEquals(a, b.getLeastSupertype(a)); Assert.assertTrue(a.canCastTo(b)); Assert.assertTrue(a.canCastTo(a)); Assert.assertTrue(b.canCastTo(b)); Assert.assertTrue(b.canCastTo(a)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/MapBasedScope.java0000644000175000017500000000504612115204405027453 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Nick Santos * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.testing; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.SimpleSlot; import com.google.javascript.rhino.jstype.StaticSlot; import java.util.Map; /** * A scope based on a simple hashmap. * @author nicksantos@google.com (Nick Santos) */ public class MapBasedScope extends AbstractStaticScope { private final Map> slots = Maps.newHashMap(); public MapBasedScope(Map namesToTypes) { for (Map.Entry entry : namesToTypes.entrySet()) { slots.put( entry.getKey(), new SimpleSlot(entry.getKey(), entry.getValue(), false)); } } public static MapBasedScope emptyScope() { return new MapBasedScope(ImmutableMap.of()); } @Override public StaticSlot getSlot(String name) { return slots.get(name); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/testing/TestErrorReporter.java0000644000175000017500000000713012115204405030455 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.testing; import com.google.javascript.rhino.ErrorReporter; import junit.framework.Assert; /** *

      An error reporter for testing that verifies that messages reported to the * reporter are expected.

      * *

      Sample use

      *
       * TestErrorReporter e =
       *   new TestErrorReporter(null, new String[] { "first warning" });
       * ...
       * assertTrue(e.hasEncounteredAllWarnings());
       * 
      * */ public final class TestErrorReporter extends Assert implements ErrorReporter { private String[] errors; private String[] warnings; private int errorsIndex = 0; private int warningsIndex = 0; public TestErrorReporter(String[] errors, String[] warnings) { this.errors = errors; this.warnings = warnings; } public static TestErrorReporter forNoExpectedReports() { return new TestErrorReporter(null, null); } public void setErrors(String[] errors) { this.errors = errors; errorsIndex = 0; } public void setWarnings(String[] warnings) { this.warnings = warnings; warningsIndex = 0; } @Override public void error(String message, String sourceName, int line, int lineOffset) { if (errors != null && errorsIndex < errors.length) { assertEquals(errors[errorsIndex++], message); } else { fail("extra error: " + message); } } @Override public void warning(String message, String sourceName, int line, int lineOffset) { if (warnings != null && warningsIndex < warnings.length) { assertEquals(warnings[warningsIndex++], message); } else { fail("extra warning: " + message); } } /** * Returns whether all warnings were reported to this reporter. */ public boolean hasEncounteredAllWarnings() { return (warnings == null) ? warningsIndex == 0 : warnings.length == warningsIndex; } /** * Returns whether all errors were reported to this reporter. */ public boolean hasEncounteredAllErrors() { return (errors == null) ? errorsIndex == 0 : errors.length == errorsIndex; } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/InputId.java0000644000175000017500000000443412115204405024704 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import java.io.Serializable; /** * An id used uniquely identify a CompilerInput * @author johnlenz@google.com (John Lenz) */ public class InputId implements Serializable { public static final long serialVersionUID = 1L; private final String id; public InputId(String id) { this.id = id; } public String getIdName() { return id; } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; return id.equals(((InputId) obj).id); } @Override public String toString() { return "InputId: " + getIdName(); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/package.html0000644000175000017500000000054012115204405024740 0ustar apoapo The core AST from Rhino. Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users. closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/Token.java0000644000175000017500000002551512115204405024413 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Roger Lawrence * Mike McCabe * Igor Bukanov * Milen Nankov * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; /** * This class implements the JavaScript scanner. * * It is based on the C source files jsscan.c and jsscan.h * in the jsref package. * */ public class Token { /** * Token types. These values correspond to JSTokenType values in * jsscan.c. */ public final static int ERROR = -1, RETURN = 4, BITOR = 9, BITXOR = 10, BITAND = 11, EQ = 12, NE = 13, LT = 14, LE = 15, GT = 16, GE = 17, LSH = 18, RSH = 19, URSH = 20, ADD = 21, SUB = 22, MUL = 23, DIV = 24, MOD = 25, NOT = 26, BITNOT = 27, POS = 28, NEG = 29, NEW = 30, DELPROP = 31, TYPEOF = 32, GETPROP = 33, GETELEM = 35, CALL = 37, NAME = 38, NUMBER = 39, STRING = 40, NULL = 41, THIS = 42, FALSE = 43, TRUE = 44, SHEQ = 45, // shallow equality (===) SHNE = 46, // shallow inequality (!==) REGEXP = 47, THROW = 49, IN = 51, INSTANCEOF = 52, ARRAYLIT = 63, // array literal OBJECTLIT = 64, // object literal TRY = 77, PARAM_LIST = 83, COMMA = 85, // comma operator ASSIGN = 86, // simple assignment (=) ASSIGN_BITOR = 87, // |= ASSIGN_BITXOR = 88, // ^= ASSIGN_BITAND = 89, // &= ASSIGN_LSH = 90, // <<= ASSIGN_RSH = 91, // >>= ASSIGN_URSH = 92, // >>>= ASSIGN_ADD = 93, // += ASSIGN_SUB = 94, // -= ASSIGN_MUL = 95, // *= ASSIGN_DIV = 96, // /= ASSIGN_MOD = 97, // %= HOOK = 98, // conditional (?:) OR = 100, // logical or (||) AND = 101, // logical and (&&) INC = 102, // increment (++) DEC = 103, // decrement (--) FUNCTION = 105, // function keyword IF = 108, // if keyword SWITCH = 110, // switch keyword CASE = 111, // case keyword DEFAULT_CASE = 112, // default keyword WHILE = 113, // while keyword DO = 114, // do keyword FOR = 115, // for keyword BREAK = 116, // break keyword CONTINUE = 117, // continue keyword VAR = 118, // var keyword WITH = 119, // with keyword CATCH = 120, // catch keyword VOID = 122, // void keyword EMPTY = 124, BLOCK = 125, // statement block LABEL = 126, // label EXPR_RESULT = 130, // expression statement in scripts SCRIPT = 132, // top-level node for entire script GETTER_DEF = 147, SETTER_DEF = 148, CONST = 149, // JS 1.5 const keyword DEBUGGER = 152, // JSCompiler introduced tokens LABEL_NAME = 153, STRING_KEY = 154, // object literal key CAST = 155, // JSDoc-only tokens ANNOTATION = 300, PIPE = 301, STAR = 302, EOC = 303, QMARK = 304, ELLIPSIS = 305, BANG = 306, EQUALS = 307, LB = 308, // left brackets LC = 309, // left curly braces COLON = 310; // Transitional definitions // TODO(johnlenz): remove these public final static int DEFAULT = DEFAULT_CASE, GET = GETTER_DEF, LP = PARAM_LIST, SET = SETTER_DEF; public static String name(int token) { switch (token) { case ERROR: return "ERROR"; case RETURN: return "RETURN"; case BITOR: return "BITOR"; case BITXOR: return "BITXOR"; case BITAND: return "BITAND"; case EQ: return "EQ"; case NE: return "NE"; case LT: return "LT"; case LE: return "LE"; case GT: return "GT"; case GE: return "GE"; case LSH: return "LSH"; case RSH: return "RSH"; case URSH: return "URSH"; case ADD: return "ADD"; case SUB: return "SUB"; case MUL: return "MUL"; case DIV: return "DIV"; case MOD: return "MOD"; case NOT: return "NOT"; case BITNOT: return "BITNOT"; case POS: return "POS"; case NEG: return "NEG"; case NEW: return "NEW"; case DELPROP: return "DELPROP"; case TYPEOF: return "TYPEOF"; case GETPROP: return "GETPROP"; case GETELEM: return "GETELEM"; case CALL: return "CALL"; case NAME: return "NAME"; case LABEL_NAME: return "LABEL_NAME"; case NUMBER: return "NUMBER"; case STRING: return "STRING"; case STRING_KEY: return "STRING_KEY"; case NULL: return "NULL"; case THIS: return "THIS"; case FALSE: return "FALSE"; case TRUE: return "TRUE"; case SHEQ: return "SHEQ"; case SHNE: return "SHNE"; case REGEXP: return "REGEXP"; case THROW: return "THROW"; case IN: return "IN"; case INSTANCEOF: return "INSTANCEOF"; case ARRAYLIT: return "ARRAYLIT"; case OBJECTLIT: return "OBJECTLIT"; case TRY: return "TRY"; case PARAM_LIST: return "PARAM_LIST"; case COMMA: return "COMMA"; case ASSIGN: return "ASSIGN"; case ASSIGN_BITOR: return "ASSIGN_BITOR"; case ASSIGN_BITXOR: return "ASSIGN_BITXOR"; case ASSIGN_BITAND: return "ASSIGN_BITAND"; case ASSIGN_LSH: return "ASSIGN_LSH"; case ASSIGN_RSH: return "ASSIGN_RSH"; case ASSIGN_URSH: return "ASSIGN_URSH"; case ASSIGN_ADD: return "ASSIGN_ADD"; case ASSIGN_SUB: return "ASSIGN_SUB"; case ASSIGN_MUL: return "ASSIGN_MUL"; case ASSIGN_DIV: return "ASSIGN_DIV"; case ASSIGN_MOD: return "ASSIGN_MOD"; case HOOK: return "HOOK"; case OR: return "OR"; case AND: return "AND"; case INC: return "INC"; case DEC: return "DEC"; case FUNCTION: return "FUNCTION"; case IF: return "IF"; case SWITCH: return "SWITCH"; case CASE: return "CASE"; case DEFAULT_CASE: return "DEFAULT_CASE"; case WHILE: return "WHILE"; case DO: return "DO"; case FOR: return "FOR"; case BREAK: return "BREAK"; case CONTINUE: return "CONTINUE"; case VAR: return "VAR"; case WITH: return "WITH"; case CATCH: return "CATCH"; case EMPTY: return "EMPTY"; case BLOCK: return "BLOCK"; case LABEL: return "LABEL"; case EXPR_RESULT: return "EXPR_RESULT"; case SCRIPT: return "SCRIPT"; case GETTER_DEF: return "GETTER_DEF"; case SETTER_DEF: return "SETTER_DEF"; case CONST: return "CONST"; case DEBUGGER: return "DEBUGGER"; case CAST: return "CAST"; case ANNOTATION: return "ANNOTATION"; case PIPE: return "PIPE"; case STAR: return "STAR"; case EOC: return "EOC"; case QMARK: return "QMARK"; case ELLIPSIS: return "ELLIPSIS"; case BANG: return "BANG"; case VOID: return "VOID"; case EQUALS: return "EQUALS"; case LB: return "LB"; case LC: return "LC"; case COLON: return "COLON"; } // Token without name throw new IllegalStateException(String.valueOf(token)); } } closure-compiler-20130227+dfsg1/src/com/google/javascript/rhino/ErrorReporter.java0000644000175000017500000000601112115204405026135 0ustar apoapo/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ // API class package com.google.javascript.rhino; /** * This is interface defines a protocol for the reporting of * errors during JavaScript translation or execution. * */ public interface ErrorReporter { /** * Report a warning. * * The implementing class may choose to ignore the warning * if it desires. * * @param message a String describing the warning * @param sourceName a String describing the JavaScript source * where the warning occurred; typically a filename or URL * @param line the line number associated with the warning * @param lineOffset the offset into lineSource where problem was detected */ void warning(String message, String sourceName, int line, int lineOffset); /** * Report an error. * * The implementing class is free to throw an exception if * it desires. * * If execution has not yet begun, the JavaScript engine is * free to find additional errors rather than terminating * the translation. It will not execute a script that had * errors, however. * * @param message a String describing the error * @param sourceName a String describing the JavaScript source * where the error occurred; typically a filename or URL * @param line the line number associated with the error * @param lineOffset the offset into lineSource where problem was detected */ void error(String message, String sourceName, int line, int lineOffset); } closure-compiler-20130227+dfsg1/COPYING0000644000175000017500000002613612115204405015415 0ustar apoapo Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. closure-compiler-20130227+dfsg1/closure-compiler.pom0000644000175000017500000001137512115204405020362 0ustar apoapo 4.0.0 com.google.javascript closure-compiler jar Closure Compiler r@build.svnVersion@ http://code.google.com/p/closure-compiler/ Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs. 2009 scm:svn:http://closure-compiler.googlecode.com/svn/trunk scm:svn:https://closure-compiler.googlecode.com/svn/trunk http://code.google.com/p/closure-compiler/source/browse/#svn/trunk code.google.com http://code.google.com/p/closure-compiler/issues Google http://www.google.com The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.html repo central Maven Repository Switchboard default http://repo1.maven.org/maven2 false caja http://google-caja.googlecode.com/svn/maven johnlenz John Lenz concavelenz@gmail.com nicksantos Nick Santos nicholas.j.santos@gmail.com acleung Alan Leung acleung@gmail.com mbolin Michael Bolin mbolin@alum.mit.edu plindner Paul Lindner lindner@inuus.com args4j args4j 2.0.16 com.google.guava guava 14.0 com.google.protobuf protobuf-java 2.4.1 org.json json 20090211 org.apache.ant ant 1.8.2 compile com.google.code.findbugs jsr305 1.3.9 com.googlecode.jarjar jarjar 1.1 compile junit junit 4.10 test caja caja r4939 test closure-compiler-20130227+dfsg1/externs/0000755000175000017500000000000012115204405016042 5ustar apoapoclosure-compiler-20130227+dfsg1/externs/w3c_dom2.js0000644000175000017500000016366712115204405020040 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's DOM Level 2 specification. * This file depends on w3c_dom1.js. * The whole file has been fully type annotated. * Created from * http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html * * @externs */ // All the provided definitions have been type annotated. /** * @constructor * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75708506 */ function HTMLCollection() {} /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40057551 */ HTMLCollection.prototype.length; /** * @param {number} index * @return {Node} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33262535 * @nosideeffects */ HTMLCollection.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-HTML/html.html#HTMLOptionsCollection */ function HTMLOptionsCollection() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-HTML/html.html#HTMLOptionsCollection-length */ HTMLOptionsCollection.prototype.length; /** * @param {number} index * @return {Node} * @see http://www.w3.org/TR/DOM-Level-2-HTML/html.html#HTMLOptionsCollection-item * @nosideeffects */ HTMLOptionsCollection.prototype.item = function(index) {}; /** * @constructor * @extends {Document} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26809268 */ function HTMLDocument() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18446827 */ HTMLDocument.prototype.title; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95229140 */ HTMLDocument.prototype.referrer; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-2250147 */ HTMLDocument.prototype.domain; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46183437 */ HTMLDocument.prototype.URL; /** * @type {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56360201 */ HTMLDocument.prototype.body; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90379117 */ HTMLDocument.prototype.images; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85113862 */ HTMLDocument.prototype.applets; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7068919 */ HTMLDocument.prototype.links; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1689064 */ HTMLDocument.prototype.forms; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7577272 */ HTMLDocument.prototype.anchors; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8747038 */ HTMLDocument.prototype.cookie; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72161170 * @override */ HTMLDocument.prototype.open = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98948567 * @override */ HTMLDocument.prototype.close = function() {}; /** * @param {string} text * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75233634 * @override */ HTMLDocument.prototype.write = function(text) {}; /** * @param {string} text * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35318390 * @override */ HTMLDocument.prototype.writeln = function(text) {}; /** * @param {string} elementName * @return {!NodeList} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71555259 * @override * @nosideeffects */ HTMLDocument.prototype.getElementsByName = function(elementName) {}; /** * @constructor * @extends {Element} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58190037 */ function HTMLElement() {} /** * @implicitCast * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63534901 */ HTMLElement.prototype.id; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78276800 */ HTMLElement.prototype.title; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59132807 */ HTMLElement.prototype.lang; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52460740 */ HTMLElement.prototype.dir; /** * @implicitCast * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95362176 */ HTMLElement.prototype.className; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33759296 */ function HTMLHtmlElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9383775 */ HTMLHtmlElement.prototype.version; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77253168 */ function HTMLHeadElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96921909 */ HTMLHeadElement.prototype.profile; /** * @constructor * @extends {HTMLElement} * @implements {LinkStyle} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35143001 */ function HTMLLinkElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87355129 */ HTMLLinkElement.prototype.disabled; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63954491 */ HTMLLinkElement.prototype.charset; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33532588 */ HTMLLinkElement.prototype.href; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85145682 */ HTMLLinkElement.prototype.hreflang; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75813125 */ HTMLLinkElement.prototype.media; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41369587 */ HTMLLinkElement.prototype.rel; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40715461 */ HTMLLinkElement.prototype.rev; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84183095 */ HTMLLinkElement.prototype.target; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32498296 */ HTMLLinkElement.prototype.type; /** @override */ HTMLLinkElement.prototype.sheet; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79243169 */ function HTMLTitleElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77500413 */ HTMLTitleElement.prototype.text; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37041454 */ function HTMLMetaElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87670826 */ HTMLMetaElement.prototype.content; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77289449 */ HTMLMetaElement.prototype.httpEquiv; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31037081 */ HTMLMetaElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35993789 */ HTMLMetaElement.prototype.scheme; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73629039 */ function HTMLBaseElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65382887 */ HTMLBaseElement.prototype.href; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73844298 */ HTMLBaseElement.prototype.target; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85283003 */ function HTMLIsIndexElement() {} /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87069980 */ HTMLIsIndexElement.prototype.form; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33589862 */ HTMLIsIndexElement.prototype.prompt; /** * @constructor * @extends {HTMLElement} * @implements {LinkStyle} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16428977 */ function HTMLStyleElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-51162010 */ HTMLStyleElement.prototype.disabled; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76412738 */ HTMLStyleElement.prototype.media; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22472002 */ HTMLStyleElement.prototype.type; /** @override */ HTMLStyleElement.prototype.sheet; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62018039 */ function HTMLBodyElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59424581 */ HTMLBodyElement.prototype.aLink; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37574810 */ HTMLBodyElement.prototype.background; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-24940084 */ HTMLBodyElement.prototype.bgColor; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7662206 */ HTMLBodyElement.prototype.link; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73714763 */ HTMLBodyElement.prototype.text; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83224305 */ HTMLBodyElement.prototype.vLink; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40002357 */ function HTMLFormElement() {} /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76728479 */ HTMLFormElement.prototype.elements; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#HTML-HTMLFormElement-length */ HTMLFormElement.prototype.length; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22051454 */ HTMLFormElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19661795 */ HTMLFormElement.prototype.acceptCharset; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74049184 */ HTMLFormElement.prototype.action; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84227810 */ HTMLFormElement.prototype.enctype; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82545539 */ HTMLFormElement.prototype.method; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6512890 */ HTMLFormElement.prototype.target; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76767676 */ HTMLFormElement.prototype.submit = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76767677 */ HTMLFormElement.prototype.reset = function() {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-94282980 */ function HTMLSelectElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58783172 */ HTMLSelectElement.prototype.type; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85676760 */ HTMLSelectElement.prototype.selectedIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59351919 */ HTMLSelectElement.prototype.value; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-5933486 */ HTMLSelectElement.prototype.length; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20489458 */ HTMLSelectElement.prototype.form; /** * @type {HTMLOptionsCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30606413 */ HTMLSelectElement.prototype.options; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79102918 */ HTMLSelectElement.prototype.disabled; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13246613 */ HTMLSelectElement.prototype.multiple; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41636323 */ HTMLSelectElement.prototype.name; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18293826 */ HTMLSelectElement.prototype.size; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40676705 */ HTMLSelectElement.prototype.tabIndex; /** * @param {HTMLElement} element * @param {HTMLElement} before * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14493106 */ HTMLSelectElement.prototype.add = function(element, before) {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-28216144 * @override */ HTMLSelectElement.prototype.blur = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32130014 * @override */ HTMLSelectElement.prototype.focus = function() {}; /** * @param {number} index * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-33404570 */ HTMLSelectElement.prototype.remove = function(index) {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38450247 */ function HTMLOptGroupElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15518803 */ HTMLOptGroupElement.prototype.disabled; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95806054 */ HTMLOptGroupElement.prototype.label; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70901257 */ function HTMLOptionElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-37770574 */ HTMLOptionElement.prototype.defaultSelected; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23482473 */ HTMLOptionElement.prototype.disabled; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17116503 */ HTMLOptionElement.prototype.form; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14038413 */ HTMLOptionElement.prototype.index; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40736115 */ HTMLOptionElement.prototype.label; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70874476 */ HTMLOptionElement.prototype.selected; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48154426 */ HTMLOptionElement.prototype.text; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6185554 */ HTMLOptionElement.prototype.value; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6043025 */ function HTMLInputElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15328520 */ HTMLInputElement.prototype.accept; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59914154 */ HTMLInputElement.prototype.accessKey; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96991182 */ HTMLInputElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92701314 */ HTMLInputElement.prototype.alt; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30233917 */ HTMLInputElement.prototype.checked; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20509171 */ HTMLInputElement.prototype.defaultChecked; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26091157 */ HTMLInputElement.prototype.defaultValue; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50886781 */ HTMLInputElement.prototype.disabled; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63239895 */ HTMLInputElement.prototype.form; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-54719353 */ HTMLInputElement.prototype.maxLength; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89658498 */ HTMLInputElement.prototype.name; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88461592 */ HTMLInputElement.prototype.readOnly; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79659438 */ HTMLInputElement.prototype.size; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-97320704 */ HTMLInputElement.prototype.src; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62176355 */ HTMLInputElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62883744 */ HTMLInputElement.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32463706 */ HTMLInputElement.prototype.useMap; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-49531485 */ HTMLInputElement.prototype.value; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26838235 * @override */ HTMLInputElement.prototype.blur = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-2651361 * @override */ HTMLInputElement.prototype.click = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65996295 * @override */ HTMLInputElement.prototype.focus = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34677168 */ HTMLInputElement.prototype.select = function() {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-24874179 */ function HTMLTextAreaElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93102991 */ HTMLTextAreaElement.prototype.accessKey; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-51387225 */ HTMLTextAreaElement.prototype.cols; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36152213 */ HTMLTextAreaElement.prototype.defaultValue; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98725443 */ HTMLTextAreaElement.prototype.disabled; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18911464 */ HTMLTextAreaElement.prototype.form; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70715578 */ HTMLTextAreaElement.prototype.name; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39131423 */ HTMLTextAreaElement.prototype.readOnly; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46975887 */ HTMLTextAreaElement.prototype.rows; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-60363303 */ HTMLTextAreaElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#HTML-HTMLTextAreaElement-type */ HTMLTextAreaElement.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70715579 */ HTMLTextAreaElement.prototype.value; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6750689 * @override */ HTMLTextAreaElement.prototype.blur = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39055426 * @override */ HTMLTextAreaElement.prototype.focus = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48880622 */ HTMLTextAreaElement.prototype.select = function() {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34812697 */ function HTMLButtonElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-73169431 */ HTMLButtonElement.prototype.accessKey; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92757155 */ HTMLButtonElement.prototype.disabled; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71254493 */ HTMLButtonElement.prototype.form; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11029910 */ HTMLButtonElement.prototype.name; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39190908 */ HTMLButtonElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27430092 */ HTMLButtonElement.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72856782 */ HTMLButtonElement.prototype.value; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13691394 */ function HTMLLabelElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43589892 */ HTMLLabelElement.prototype.accessKey; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32480901 */ HTMLLabelElement.prototype.form; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96509813 */ HTMLLabelElement.prototype.htmlFor; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7365882 */ function HTMLFieldSetElement() {} /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75392630 */ HTMLFieldSetElement.prototype.form; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-21482039 */ function HTMLLegendElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11297832 */ HTMLLegendElement.prototype.accessKey; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79538067 */ HTMLLegendElement.prototype.align; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-29594519 */ HTMLLegendElement.prototype.form; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-86834457 */ function HTMLUListElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39864178 */ HTMLUListElement.prototype.compact; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96874670 */ HTMLUListElement.prototype.type; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58056027 */ function HTMLOListElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76448506 */ HTMLOListElement.prototype.compact; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14793325 */ HTMLOListElement.prototype.start; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40971103 */ HTMLOListElement.prototype.type; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52368974 */ function HTMLDListElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-21738539 */ HTMLDListElement.prototype.compact; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71600284 */ function HTMLDirectoryElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75317739 */ HTMLDirectoryElement.prototype.compact; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-72509186 */ function HTMLMenuElement() {} /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68436464 */ HTMLMenuElement.prototype.compact; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74680021 */ function HTMLLIElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52387668 */ HTMLLIElement.prototype.type; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-45496263 */ HTMLLIElement.prototype.value; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22445964 */ function HTMLDivElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70908791 */ HTMLDivElement.prototype.align; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84675076 */ function HTMLParagraphElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53465507 */ HTMLParagraphElement.prototype.align; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43345119 */ function HTMLHeadingElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6796462 */ HTMLHeadingElement.prototype.align; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70319763 */ function HTMLQuoteElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53895598 */ HTMLQuoteElement.prototype.cite; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11383425 */ function HTMLPreElement() {} /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13894083 */ HTMLPreElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56836063 */ function HTMLBRElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82703081 */ HTMLBRElement.prototype.clear; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32774408 */ function HTMLBaseFontElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87502302 */ HTMLBaseFontElement.prototype.color; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88128969 */ HTMLBaseFontElement.prototype.face; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38930424 */ HTMLBaseFontElement.prototype.size; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43943847 */ function HTMLFontElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53532975 */ HTMLFontElement.prototype.color; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-55715655 */ HTMLFontElement.prototype.face; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90127284 */ HTMLFontElement.prototype.size; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68228811 */ function HTMLHRElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-15235012 */ HTMLHRElement.prototype.align; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79813978 */ HTMLHRElement.prototype.noShade; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77612587 */ HTMLHRElement.prototype.size; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87744198 */ HTMLHRElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79359609 */ function HTMLModElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75101708 */ HTMLModElement.prototype.cite; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88432678 */ HTMLModElement.prototype.dateTime; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48250443 */ function HTMLAnchorElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89647724 */ HTMLAnchorElement.prototype.accessKey; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67619266 */ HTMLAnchorElement.prototype.charset; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-92079539 */ HTMLAnchorElement.prototype.coords; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88517319 */ HTMLAnchorElement.prototype.href; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87358513 */ HTMLAnchorElement.prototype.hreflang; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-32783304 */ HTMLAnchorElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-3815891 */ HTMLAnchorElement.prototype.rel; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58259771 */ HTMLAnchorElement.prototype.rev; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-49899808 */ HTMLAnchorElement.prototype.shape; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-41586466 */ HTMLAnchorElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6414197 */ HTMLAnchorElement.prototype.target; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63938221 */ HTMLAnchorElement.prototype.type; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-65068939 * @override */ HTMLAnchorElement.prototype.blur = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47150313 * @override */ HTMLAnchorElement.prototype.focus = function() {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17701901 */ function HTMLImageElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-3211094 */ HTMLImageElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-95636861 */ HTMLImageElement.prototype.alt; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-136671 */ HTMLImageElement.prototype.border; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91561496 */ HTMLImageElement.prototype.height; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53675471 */ HTMLImageElement.prototype.hspace; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58983880 */ HTMLImageElement.prototype.isMap; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77376969 */ HTMLImageElement.prototype.longDesc; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91256910 */ HTMLImageElement.prototype.lowSrc; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47534097 */ HTMLImageElement.prototype.name; /** * @type {string} * @implicitCast * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-87762984 */ HTMLImageElement.prototype.src; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35981181 */ HTMLImageElement.prototype.useMap; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85374897 */ HTMLImageElement.prototype.vspace; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13839076 */ HTMLImageElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9893177 */ function HTMLObjectElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16962097 */ HTMLObjectElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-47783837 */ HTMLObjectElement.prototype.archive; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82818419 */ HTMLObjectElement.prototype.border; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75241146 */ HTMLObjectElement.prototype.code; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25709136 */ HTMLObjectElement.prototype.codeBase; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19945008 */ HTMLObjectElement.prototype.codeType; /** * @type {Document} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38538621 */ HTMLObjectElement.prototype.contentDocument; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-81766986 */ HTMLObjectElement.prototype.data; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-942770 */ HTMLObjectElement.prototype.declare; /** * @type {HTMLFormElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46094773 */ HTMLObjectElement.prototype.form; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88925838 */ HTMLObjectElement.prototype.height; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-17085376 */ HTMLObjectElement.prototype.hspace; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20110362 */ HTMLObjectElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25039673 */ HTMLObjectElement.prototype.standby; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27083787 */ HTMLObjectElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91665621 */ HTMLObjectElement.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6649772 */ HTMLObjectElement.prototype.useMap; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8682483 */ HTMLObjectElement.prototype.vspace; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38538620 */ HTMLObjectElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64077273 */ function HTMLParamElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59871447 */ HTMLParamElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18179888 */ HTMLParamElement.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77971357 */ HTMLParamElement.prototype.value; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23931872 */ HTMLParamElement.prototype.valueType; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31006348 */ function HTMLAppletElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8049912 */ HTMLAppletElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58610064 */ HTMLAppletElement.prototype.alt; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14476360 */ HTMLAppletElement.prototype.archive; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-61509645 */ HTMLAppletElement.prototype.code; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6581160 */ HTMLAppletElement.prototype.codeBase; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90184867 */ HTMLAppletElement.prototype.height; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1567197 */ HTMLAppletElement.prototype.hspace; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39843695 */ HTMLAppletElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93681523 */ HTMLAppletElement.prototype.object; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22637173 */ HTMLAppletElement.prototype.vspace; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16526327 */ HTMLAppletElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-94109203 */ function HTMLMapElement() {} /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-71838730 */ HTMLMapElement.prototype.areas; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52696514 */ HTMLMapElement.prototype.name; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26019118 */ function HTMLAreaElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-57944457 */ HTMLAreaElement.prototype.accessKey; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39775416 */ HTMLAreaElement.prototype.alt; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66021476 */ HTMLAreaElement.prototype.coords; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-34672936 */ HTMLAreaElement.prototype.href; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-61826871 */ HTMLAreaElement.prototype.noHref; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-85683271 */ HTMLAreaElement.prototype.shape; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8722121 */ HTMLAreaElement.prototype.tabIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46054682 */ HTMLAreaElement.prototype.target; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-81598695 */ function HTMLScriptElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-35305677 */ HTMLScriptElement.prototype.charset; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93788534 */ HTMLScriptElement.prototype.defer; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-56700403 */ HTMLScriptElement.prototype.event; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66979266 */ HTMLScriptElement.prototype.htmlFor; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-75147231 */ HTMLScriptElement.prototype.src; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-46872999 */ HTMLScriptElement.prototype.text; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30534818 */ HTMLScriptElement.prototype.type; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64060425 */ function HTMLTableElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-23180977 */ HTMLTableElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83532985 */ HTMLTableElement.prototype.bgColor; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50969400 */ HTMLTableElement.prototype.border; /** * @type {HTMLTableCaptionElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-14594520 */ HTMLTableElement.prototype.caption; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-59162158 */ HTMLTableElement.prototype.cellPadding; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68907883 */ HTMLTableElement.prototype.cellSpacing; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64808476 */ HTMLTableElement.prototype.frame; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6156016 */ HTMLTableElement.prototype.rows; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-26347553 */ HTMLTableElement.prototype.rules; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-44998528 */ HTMLTableElement.prototype.summary; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-63206416 */ HTMLTableElement.prototype.tBodies; /** * @type {HTMLTableSectionElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-64197097 */ HTMLTableElement.prototype.tFoot; /** * @type {HTMLTableSectionElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9530944 */ HTMLTableElement.prototype.tHead; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-77447361 */ HTMLTableElement.prototype.width; /** * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96920263 */ HTMLTableElement.prototype.createCaption = function() {}; /** * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8453710 */ HTMLTableElement.prototype.createTFoot = function() {}; /** * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70313345 */ HTMLTableElement.prototype.createTHead = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22930071 */ HTMLTableElement.prototype.deleteCaption = function() {}; /** * @param {number} index * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-13114938 */ HTMLTableElement.prototype.deleteRow = function(index) {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78363258 */ HTMLTableElement.prototype.deleteTFoot = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-38310198 */ HTMLTableElement.prototype.deleteTHead = function() {}; /** * @param {number} index * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-39872903 */ HTMLTableElement.prototype.insertRow = function(index) {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-12035137 */ function HTMLTableCaptionElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79875068 */ HTMLTableCaptionElement.prototype.align; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84150186 */ function HTMLTableColElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-31128447 */ HTMLTableColElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-9447412 */ HTMLTableColElement.prototype.ch; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-57779225 */ HTMLTableColElement.prototype.chOff; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96511335 */ HTMLTableColElement.prototype.span; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83291710 */ HTMLTableColElement.prototype.vAlign; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-25196799 */ HTMLTableColElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67417573 */ function HTMLTableSectionElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-40530119 */ HTMLTableSectionElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83470012 */ HTMLTableSectionElement.prototype.ch; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-53459732 */ HTMLTableSectionElement.prototype.chOff; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-52092650 */ HTMLTableSectionElement.prototype.rows; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-4379116 */ HTMLTableSectionElement.prototype.vAlign; /** * @param {number} index * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-5625626 */ HTMLTableSectionElement.prototype.deleteRow = function(index) {}; /** * @param {number} index * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-93995626 */ HTMLTableSectionElement.prototype.insertRow = function(index) {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-6986576 */ function HTMLTableRowElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74098257 */ HTMLTableRowElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-18161327 */ HTMLTableRowElement.prototype.bgColor; /** * @type {HTMLCollection} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67349879 */ HTMLTableRowElement.prototype.cells; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-16230502 */ HTMLTableRowElement.prototype.ch; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68207461 */ HTMLTableRowElement.prototype.chOff; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67347567 */ HTMLTableRowElement.prototype.rowIndex; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-79105901 */ HTMLTableRowElement.prototype.sectionRowIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-90000058 */ HTMLTableRowElement.prototype.vAlign; /** * @param {number} index * @return {undefined} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11738598 */ HTMLTableRowElement.prototype.deleteCell = function(index) {}; /** * @param {number} index * @return {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-68927016 */ HTMLTableRowElement.prototype.insertCell = function(index) {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-82915075 */ function HTMLTableCellElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-74444037 */ HTMLTableCellElement.prototype.abbr; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98433879 */ HTMLTableCellElement.prototype.align; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-76554418 */ HTMLTableCellElement.prototype.axis; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-88135431 */ HTMLTableCellElement.prototype.bgColor; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-80748363 */ HTMLTableCellElement.prototype.cellIndex; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-30914780 */ HTMLTableCellElement.prototype.ch; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-20144310 */ HTMLTableCellElement.prototype.chOff; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-84645244 */ HTMLTableCellElement.prototype.colSpan; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-89104817 */ HTMLTableCellElement.prototype.headers; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-83679212 */ HTMLTableCellElement.prototype.height; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-62922045 */ HTMLTableCellElement.prototype.noWrap; /** * @type {number} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-48237625 */ HTMLTableCellElement.prototype.rowSpan; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36139952 */ HTMLTableCellElement.prototype.scope; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-58284221 */ HTMLTableCellElement.prototype.vAlign; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-27480795 */ HTMLTableCellElement.prototype.width; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43829095 */ function HTMLFrameSetElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-98869594 */ HTMLFrameSetElement.prototype.cols; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-19739247 */ HTMLFrameSetElement.prototype.rows; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-97790553 */ function HTMLFrameElement() {} /** * @type {Document} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78799536 */ HTMLFrameElement.prototype.contentDocument; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11858633 */ HTMLFrameElement.prototype.frameBorder; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-7836998 */ HTMLFrameElement.prototype.longDesc; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-55569778 */ HTMLFrameElement.prototype.marginHeight; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-8369969 */ HTMLFrameElement.prototype.marginWidth; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91128709 */ HTMLFrameElement.prototype.name; /** * @type {boolean} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-80766578 */ HTMLFrameElement.prototype.noResize; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-45411424 */ HTMLFrameElement.prototype.scrolling; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-78799535 */ HTMLFrameElement.prototype.src; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-50708718 */ function HTMLIFrameElement() {} /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-11309947 */ HTMLIFrameElement.prototype.align; /** * @type {Document} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67133006 */ HTMLIFrameElement.prototype.contentDocument; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-22463410 */ HTMLIFrameElement.prototype.frameBorder; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-1678118 */ HTMLIFrameElement.prototype.height; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-70472105 */ HTMLIFrameElement.prototype.longDesc; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-91371294 */ HTMLIFrameElement.prototype.marginHeight; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-66486595 */ HTMLIFrameElement.prototype.marginWidth; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-96819659 */ HTMLIFrameElement.prototype.name; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-36369822 */ HTMLIFrameElement.prototype.scrolling; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-43933957 */ HTMLIFrameElement.prototype.src; /** * @type {string} * @see http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html#ID-67133005 */ HTMLIFrameElement.prototype.width; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF */ DOMException.INVALID_STATE_ERR = 11; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF */ DOMException.SYNTAX_ERR = 12; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF */ DOMException.INVALID_MODIFICATION_ERR = 13; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF */ DOMException.NAMESPACE_ERR = 14; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-258A00AF */ DOMException.INVALID_ACCESS_ERR = 15; closure-compiler-20130227+dfsg1/externs/ie_vml.js0000644000175000017500000000355212115204405017660 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for IE's vector markup language, or VML. * * @externs * @author robbyw@google.com (Robby Walker) */ /** * @type {Object|string} * @see http://msdn.microsoft.com/en-us/library/bb263836(VS.85).aspx */ Element.prototype.coordorigin; /** * @type {Object|string} * @see http://msdn.microsoft.com/en-us/library/bb263837(VS.85).aspx */ Element.prototype.coordsize; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/bb263839(VS.85).aspx */ Element.prototype.fillcolor; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/bb263840(VS.85).aspx */ Element.prototype.filled; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/bb263871(VS.85).aspx */ Element.prototype.path; /** * @type {number|string} * @see http://msdn.microsoft.com/en-us/library/bb263877(VS.85).aspx */ Element.prototype.rotation; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/bb263881(VS.85).aspx */ Element.prototype.strokecolor; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/bb263882(VS.85).aspx */ Element.prototype.stroked; /** * @type {number|string} * @see http://msdn.microsoft.com/en-us/library/bb263883(VS.85).aspx */ Element.prototype.strokeweight; closure-compiler-20130227+dfsg1/externs/w3c_event3.js0000644000175000017500000000332412115204405020362 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Definitions for W3C's event Level 3 specification. * This file depends on w3c_event.js. * The whole file has been partially type annotated. * Created from * http://www.w3.org/TR/DOM-Level-3-Events/#ecma-script-binding-ecma-binding * * @externs */ /** * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {Window} viewArg * @param {string} keyIdentifierArg * @param {number} keyLocationArg * @param {string} modifiersList */ KeyboardEvent.prototype.initKeyboardEvent = function(typeArg, canBubbleArg, cancelableArg, viewArg, keyIdentifierArg, keyLocationArg, modifiersList) {}; /** @type {string} */ KeyboardEvent.prototype['char']; /** @type {string} */ KeyboardEvent.prototype.key; /** @type {number} */ KeyboardEvent.prototype.location; /** @type {boolean} */ KeyboardEvent.prototype.repeat; /** @type {string} */ KeyboardEvent.prototype.locale; /** @type {boolean} */ Event.prototype.defaultPrevented; /** @type {string} */ Event.prototype.namespaceURI; /** @return {undefined} */ Event.prototype.stopImmediatePropagation = function() {}; closure-compiler-20130227+dfsg1/externs/webkit_dom.js0000644000175000017500000001267712115204405020541 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over W3C's DOM * specification by WebKit. This file depends on w3c_dom2.js. * All the provided definitions has been type annotated * * @externs */ /** * @constructor * @see http://trac.webkit.org/browser/trunk/Source/WebCore/page/MemoryInfo.idl * @see http://trac.webkit.org/browser/trunk/Source/WebCore/page/MemoryInfo.cpp */ function MemoryInfo() {}; /** @type {number} */ MemoryInfo.prototype.totalJSHeapSize; /** @type {number} */ MemoryInfo.prototype.usedJSHeapSize; /** @type {number} */ MemoryInfo.prototype.jsHeapSizeLimit; /** * @constructor * @see http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/ScriptProfileNode.idl */ function ScriptProfileNode() {}; /** @type {string} */ ScriptProfileNode.prototype.functionName; /** @type {string} */ ScriptProfileNode.prototype.url; /** @type {number} */ ScriptProfileNode.prototype.lineNumber; /** @type {number} */ ScriptProfileNode.prototype.totalTime; /** @type {number} */ ScriptProfileNode.prototype.selfTime; /** @type {number} */ ScriptProfileNode.prototype.numberOfCalls; /** @type {Array.} */ ScriptProfileNode.prototype.children; /** @type {boolean} */ ScriptProfileNode.prototype.visible; /** @type {number} */ ScriptProfileNode.prototype.callUID; /** * @constructor * @see http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/ScriptProfile.idl */ function ScriptProfile() {}; /** @type {string} */ ScriptProfile.prototype.title; /** @type {number} */ ScriptProfile.prototype.uid; /** @type {ScriptProfileNode} */ ScriptProfile.prototype.head; /** * @constructor * @see http://trac.webkit.org/browser/trunk/Source/WebCore/page/Console.idl * @see http://trac.webkit.org/browser/trunk/Source/WebCore/page/Console.cpp */ function Console() {}; /** * @param {*} condition * @param {...*} var_args */ Console.prototype.assert = function(condition, var_args) {}; /** * @param {...*} var_args */ Console.prototype.error = function(var_args) {}; /** * @param {...*} var_args */ Console.prototype.info = function(var_args) {}; /** * @param {...*} var_args */ Console.prototype.log = function(var_args) {}; /** * @param {...*} var_args */ Console.prototype.warn = function(var_args) {}; /** * @param {...*} var_args */ Console.prototype.debug = function(var_args) {}; /** * @param {*} value */ Console.prototype.dir = function(value) {}; /** * @param {...*} var_args */ Console.prototype.dirxml = function(var_args) {}; /** * @return {undefined} */ Console.prototype.trace = function() {}; /** * @param {*} value */ Console.prototype.count = function(value) {}; /** * @param {*} value */ Console.prototype.markTimeline = function(value) {}; /** * @param {string=} opt_title */ Console.prototype.profile = function(opt_title) {}; /** @type {Array.} */ Console.prototype.profiles; Console.prototype.profileEnd = function() {}; /** * @param {string} name */ Console.prototype.time = function(name) {}; /** * @param {string} name */ Console.prototype.timeEnd = function(name) {}; /** * @param {*} value */ Console.prototype.timeStamp = function(value) {}; /** * @param {...*} var_args */ Console.prototype.group = function(var_args) {}; Console.prototype.groupEnd = function() {}; /** @type {MemoryInfo} */ Console.prototype.memory; /** @type {Console} */ Window.prototype.console; /** * @type {number} * @see http://developer.android.com/reference/android/webkit/WebView.html */ Window.prototype.devicePixelRatio; /** @type {Node} */ Selection.prototype.baseNode; /** @type {number} */ Selection.prototype.baseOffset; /** @type {Node} */ Selection.prototype.extentNode; /** @type {number} */ Selection.prototype.extentOffset; /** @type {string} */ Selection.prototype.type; /** * @return {undefined} */ Selection.prototype.empty = function() {}; /** * @param {Node} baseNode * @param {number} baseOffset * @param {Node} extentNode * @param {number} extentOffset * @return {undefined} */ Selection.prototype.setBaseAndExtent = function(baseNode, baseOffset, extentNode, extentOffset) {}; /** * @param {string} alter * @param {string} direction * @param {string} granularity * @return {undefined} */ Selection.prototype.modify = function(alter, direction, granularity) {}; /** * @param {Element} element * @param {string} pseudoElement * @param {boolean=} opt_authorOnly * @return {CSSRuleList} * @nosideeffects */ ViewCSS.prototype.getMatchedCSSRules = function(element, pseudoElement, opt_authorOnly) {}; /** * @param {string} contextId * @param {string} name * @param {number} width * @param {number} height * @nosideeffects */ Document.prototype.getCSSCanvasContext = function(contextId, name, width, height) {}; /** * @type {string} * @see http://code.google.com/chrome/whitepapers/pagevisibility.html */ Document.prototype.webkitVisibilityState; closure-compiler-20130227+dfsg1/externs/w3c_dom1.js0000644000175000017500000006122712115204405020024 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's DOM Level 1 specification. * The whole file has been fully type annotated. Created from * http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html * * @externs */ /** * @constructor * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-17189187 */ function DOMException() {} /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.INDEX_SIZE_ERR = 1; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.DOMSTRING_SIZE_ERR = 2; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.HIERARCHY_REQUEST_ERR = 3; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.WRONG_DOCUMENT_ERR = 4; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.INVALID_CHARACTER_ERR = 5; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.NO_DATA_ALLOWED_ERR = 6; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.NO_MODIFICATION_ALLOWED_ERR = 7; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.NOT_FOUND_ERR = 8; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.NOT_SUPPORTED_ERR = 9; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ DOMException.INUSE_ATTRIBUTE_ERR = 10; /** * @constructor * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-258A00AF */ function ExceptionCode() {} /** * @constructor * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-102161490 */ function DOMImplementation() {} /** * @param {string} feature * @param {string} version * @return {boolean} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-5CED94D7 * @nosideeffects */ DOMImplementation.prototype.hasFeature = function(feature, version) {}; /** * @constructor * @implements {EventTarget} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ function Node() {} /** @override */ Node.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ Node.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ Node.prototype.dispatchEvent = function(evt) {}; /** * @type {NamedNodeMap} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-attributes */ Node.prototype.attributes; /** * @type {!NodeList} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-childNodes */ Node.prototype.childNodes; /** * @type {Node?} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-firstChild */ Node.prototype.firstChild; /** * @type {Node?} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-lastChild */ Node.prototype.lastChild; /** * @type {Node?} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nextSibling */ Node.prototype.nextSibling; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeName */ Node.prototype.nodeName; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeValue */ Node.prototype.nodeValue; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-nodeType */ Node.prototype.nodeType; /** * @type {Document} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-ownerDocument */ Node.prototype.ownerDocument; /** * @type {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-parentNode */ Node.prototype.parentNode; /** * @type {Node?} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-previousSibling */ Node.prototype.previousSibling; /** * @param {Node} newChild * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-appendChild */ Node.prototype.appendChild = function(newChild) {}; /** * @param {boolean} deep * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-cloneNode * @nosideeffects */ Node.prototype.cloneNode = function(deep) {}; /** * @return {boolean} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-hasChildNodes * @nosideeffects */ Node.prototype.hasChildNodes = function() {}; /** * @param {Node} newChild * @param {Node?} refChild * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-insertBefore */ Node.prototype.insertBefore = function(newChild, refChild) {}; /** * @param {Node} oldChild * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeChild */ Node.prototype.removeChild = function(oldChild) {}; /** * @param {Node} newChild * @param {Node} oldChild * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-replaceChild */ Node.prototype.replaceChild = function(newChild, oldChild) {}; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.ATTRIBUTE_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.CDATA_SECTION_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.COMMENT_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.DOCUMENT_FRAGMENT_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.DOCUMENT_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.DOCUMENT_TYPE_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.ELEMENT_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.ENTITY_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.ENTITY_REFERENCE_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.PROCESSING_INSTRUCTION_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.TEXT_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.XPATH_NAMESPACE_NODE; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1950641247 */ Node.NOTATION_NODE; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-B63ED1A3 */ function DocumentFragment() {} /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#i-Document */ function Document() {} /** * @type {DocumentType} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-doctype */ Document.prototype.doctype; /** * @type {!Element} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-documentElement */ Document.prototype.documentElement; /** * @type {DOMImplementation} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-implementation */ Document.prototype.implementation; /** * @param {string} name * @return {!Attr} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createAttribute * @nosideeffects */ Document.prototype.createAttribute = function(name) {}; /** * @param {string} data * @return {!Comment} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createComment * @nosideeffects */ Document.prototype.createComment = function(data) {}; /** * @param {string} data * @return {!CDATASection} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createCDATASection * @nosideeffects */ Document.prototype.createCDATASection = function(data) {}; /** * @return {!DocumentFragment} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createDocumentFragment * @nosideeffects */ Document.prototype.createDocumentFragment = function() {}; /** * Create a DOM element. Surprisingly, this has side-effects on IE * (creating and element with a custom tag boots up a sub-system that * handles custom tags). * @param {string} tagName * @return {!Element} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createElement */ Document.prototype.createElement = function(tagName) {}; /** * @param {string} name * @return {!EntityReference} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createEntityReference * @nosideeffects */ Document.prototype.createEntityReference = function(name) {}; /** * @param {string} target * @param {string} data * @return {!ProcessingInstruction} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createProcessingInstruction * @nosideeffects */ Document.prototype.createProcessingInstruction = function(target, data) {}; /** * @param {number|string} data * @return {!Text} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-createTextNode * @nosideeffects */ Document.prototype.createTextNode = function(data) {}; /** * @param {string} tagname * @return {!NodeList} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-A6C9094 * @nosideeffects */ Document.prototype.getElementsByTagName = function(tagname) {}; /** * @constructor * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-536297177 */ function NodeList() {} /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-203510337 */ NodeList.prototype.length; /** * @param {number} index * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-844377136 */ NodeList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1780488922 */ function NamedNodeMap() {} /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6D0FB19E */ NamedNodeMap.prototype.length; /** * @param {string} name * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1074577549 * @nosideeffects */ NamedNodeMap.prototype.getNamedItem = function(name) {}; /** * @param {number} index * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-349467F9 * @nosideeffects */ NamedNodeMap.prototype.item = function(index) {}; /** * @param {string} name * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D58B193 */ NamedNodeMap.prototype.removeNamedItem = function(name) {}; /** * @param {Node} arg * @return {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1025163788 */ NamedNodeMap.prototype.setNamedItem = function(arg) {}; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-FF21A306 */ function CharacterData() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-72AB8359 */ CharacterData.prototype.data; /** * @type {number} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-7D61178C */ CharacterData.prototype.length; /** * @param {string} arg * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-32791A2F */ CharacterData.prototype.appendData = function(arg) {}; /** * @param {number} offset * @param {number} count * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-7C603781 */ CharacterData.prototype.deleteData = function(offset, count) {}; /** * @param {number} offset * @param {string} arg * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-3EDB695F */ CharacterData.prototype.insertData = function(offset, arg) {}; /** * @param {number} offset * @param {number} count * @param {string} arg * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-E5CBA7FB */ CharacterData.prototype.replaceData = function(offset, count, arg) {}; /** * @param {number} offset * @param {number} count * @return {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6531BCCF * @nosideeffects */ CharacterData.prototype.substringData = function(offset, count) {}; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-637646024 */ function Attr() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1112119403 */ Attr.prototype.name; /** * @type {boolean} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-862529273 */ Attr.prototype.specified; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-221662474 */ Attr.prototype.value; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-745549614 */ function Element() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#attribute-tagName */ Element.prototype.tagName; /** * @param {string} name * @param {number?=} opt_flags * @return {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-getAttribute * @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx * @nosideeffects */ Element.prototype.getAttribute = function(name, opt_flags) {}; /** * @param {string} name * @return {Attr} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-getAttributeNode * @nosideeffects */ Element.prototype.getAttributeNode = function(name) {}; /** * @param {string} tagname * @return {!NodeList} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1938918D * @nosideeffects */ Element.prototype.getElementsByTagName = function(tagname) {}; /** * @param {string} name * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeAttribute */ Element.prototype.removeAttribute = function(name) {}; /** * @param {Attr} oldAttr * @return {?Attr} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-removeAttributeNode */ Element.prototype.removeAttributeNode = function(oldAttr) {}; /** * @param {string} name * @param {string|number|boolean} value Values are converted to strings with * ToString, so we accept number and boolean since both convert easily to * strings. * @return {undefined} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-setAttribute */ Element.prototype.setAttribute = function(name, value) {}; /** * @param {Attr} newAttr * @return {?Attr} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#method-setAttributeNode */ Element.prototype.setAttributeNode = function(newAttr) {}; // Event handlers // The DOM level 3 spec has a good index of these // http://www.w3.org/TR/DOM-Level-3-Events/#event-types /** @type {?function (Event)} */ Element.prototype.onabort; /** @type {?function (Event)} */ Element.prototype.onbeforeunload; /** @type {?function (Event)} */ Element.prototype.onblur; /** @type {?function (Event)} */ Element.prototype.onchange; /** @type {?function (Event)} */ Element.prototype.onclick; /** @type {?function (Event)} */ Element.prototype.oncompositionstart; /** @type {?function (Event)} */ Element.prototype.oncompositionupdate; /** @type {?function (Event)} */ Element.prototype.oncompositionend; /** @type {?function (Event)} */ Element.prototype.oncontextmenu; /** @type {?function (Event)} */ Element.prototype.oncopy; /** @type {?function (Event)} */ Element.prototype.oncut; /** @type {?function (Event)} */ Element.prototype.ondblclick; /** @type {?function (Event)} */ Element.prototype.onerror; /** @type {?function (Event)} */ Element.prototype.onfocus; /** @type {?function (Event)} */ Element.prototype.onfocusin; /** @type {?function (Event)} */ Element.prototype.onfocusout; /** @type {?function (Event)} */ Element.prototype.onkeydown; /** @type {?function (Event)} */ Element.prototype.onkeypress; /** @type {?function (Event)} */ Element.prototype.onkeyup; /** @type {?function (Event)} */ Element.prototype.onload; /** @type {?function (Event)} */ Element.prototype.onunload; /** @type {?function (Event)} */ Element.prototype.onmousedown; /** @type {?function (Event)} */ Element.prototype.onmousemove; /** @type {?function (Event)} */ Element.prototype.onmouseout; /** @type {?function (Event)} */ Element.prototype.onmouseover; /** @type {?function (Event)} */ Element.prototype.onmouseup; /** @type {?function (Event)} */ Element.prototype.onmousewheel; /** @type {?function (Event)} */ Element.prototype.onpaste; /** @type {?function (Event)} */ Element.prototype.onreset; /** @type {?function (Event)} */ Element.prototype.onresize; /** @type {?function (Event)} */ Element.prototype.onscroll; /** @type {?function (Event)} */ Element.prototype.onselect; /** @type {?function (Event=)} */ Element.prototype.onsubmit; /** @type {?function (Event)} */ Element.prototype.ontextinput; /** @type {?function (Event)} */ Element.prototype.onwheel; /** * @constructor * @extends {CharacterData} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1312295772 */ function Text() {} /** * @param {number} offset * @return {Text} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-38853C1D */ Text.prototype.splitText = function(offset) {}; /** * @constructor * @extends {CharacterData} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1728279322 */ function Comment() {} /** * @constructor * @extends {Text} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-667469212 */ function CDATASection() {} /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-412266927 */ function DocumentType() {} /** * @type {NamedNodeMap} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1788794630 */ DocumentType.prototype.entities; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1844763134 */ DocumentType.prototype.name; /** * @type {NamedNodeMap} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D46829EF */ DocumentType.prototype.notations; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-5431D1B9 */ function Notation() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-54F2B4D0 */ Notation.prototype.publicId; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-E8AAB1D0 */ Notation.prototype.systemId; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-527DCFF2 */ function Entity() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D7303025 */ Entity.prototype.publicId; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-D7C29F3E */ Entity.prototype.systemId; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-6ABAEB38 */ Entity.prototype.notationName; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-11C98490 */ function EntityReference() {} /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1004215813 */ function ProcessingInstruction() {} /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-837822393 */ ProcessingInstruction.prototype.data; /** * @type {string} * @see http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core.html#ID-1478689192 */ ProcessingInstruction.prototype.target; /** * @constructor * @implements {EventTarget} */ function Window() {} /** @override */ Window.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ Window.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ Window.prototype.dispatchEvent = function(evt) {}; /** @type {?function (Event)} */ Window.prototype.onabort; /** @type {?function (Event)} */ Window.prototype.onbeforeunload; /** @type {?function (Event)} */ Window.prototype.onblur; /** @type {?function (Event)} */ Window.prototype.onchange; /** @type {?function (Event)} */ Window.prototype.onclick; /** @type {?function (Event)} */ Window.prototype.onclose; /** @type {?function (Event)} */ Window.prototype.oncontextmenu; /** @type {?function (Event)} */ Window.prototype.ondblclick; /** @type {?function (Event)} */ Window.prototype.ondragdrop; // onerror has a special signature. // See https://developer.mozilla.org/en/DOM/window.onerror // and http://msdn.microsoft.com/en-us/library/cc197053(VS.85).aspx /** @type {?function (string, string, number)} */ Window.prototype.onerror; /** @type {?function (Event)} */ Window.prototype.onfocus; /** @type {?function (Event)} */ Window.prototype.onhashchange; /** @type {?function (Event)} */ Window.prototype.onkeydown; /** @type {?function (Event)} */ Window.prototype.onkeypress; /** @type {?function (Event)} */ Window.prototype.onkeyup; /** @type {?function (Event)} */ Window.prototype.onload; /** @type {?function (Event)} */ Window.prototype.onmousedown; /** @type {?function (Event)} */ Window.prototype.onmousemove; /** @type {?function (Event)} */ Window.prototype.onmouseout; /** @type {?function (Event)} */ Window.prototype.onmouseover; /** @type {?function (Event)} */ Window.prototype.onmouseup; /** @type {?function (Event)} */ Window.prototype.onmousewheel; /** @type {?function (Event)} */ Window.prototype.onpaint; /** @type {?function (Event)} */ Window.prototype.onpopstate; /** @type {?function (Event)} */ Window.prototype.onreset; /** @type {?function (Event)} */ Window.prototype.onresize; /** @type {?function (Event)} */ Window.prototype.onscroll; /** @type {?function (Event)} */ Window.prototype.onselect; /** @type {?function (Event=)} */ Window.prototype.onsubmit; /** @type {?function (Event)} */ Window.prototype.onunload; /** @type {?function (Event)} */ Window.prototype.onwheel; closure-compiler-20130227+dfsg1/externs/ie_event.js0000644000175000017500000001466112115204405020206 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over the * W3C's event specification by IE in JScript. This file depends on * w3c_event.js. * * @see http://msdn.microsoft.com/en-us/library/ms535863.aspx * @externs */ /** @type {string} */ Event.prototype.Abstract; /** @type {boolean} */ Event.prototype.altKey; /** @type {boolean} */ Event.prototype.altLeft; /** @type {string} */ Event.prototype.Banner; /** @type {number} */ Event.prototype.button; /** @type {boolean} */ Event.prototype.cancelBubble; /** @type {number} */ Event.prototype.clientX; /** @type {number} */ Event.prototype.clientY; /** * @see http://msdn.microsoft.com/en-us/library/ms535220.aspx * @type {(ClipboardData|undefined)} */ Event.prototype.clipboardData; /** @type {boolean} */ Event.prototype.contentOverflow; /** @type {boolean} */ Event.prototype.ctrlKey; /** @type {boolean} */ Event.prototype.ctrlLeft; /** @type {string} */ Event.prototype.data; /** @type {string} */ Event.prototype.dataFld; Event.prototype.domain; /** @type {Element} */ Event.prototype.fromElement; /** @type {number} */ Event.prototype.keyCode; /** @type {string} */ Event.prototype.MoreInfo; /** @type {string} */ Event.prototype.nextPage; /** @type {number} */ Event.prototype.offsetX; /** @type {number} */ Event.prototype.offsetY; /** @type {string} */ Event.prototype.propertyName; /** @type {string} */ Event.prototype.qualifier; /** @type {number} */ Event.prototype.reason; /** @type {Object.<*,*>} */ Event.prototype.recordset; /** @type {boolean} */ Event.prototype.repeat; /** @type {(boolean|string|undefined)} */ Event.prototype.returnValue; /** @type {string} */ Event.prototype.saveType; Event.prototype.scheme; /** @type {number} */ Event.prototype.screenX; /** @type {number} */ Event.prototype.screenY; /** @type {boolean} */ Event.prototype.shiftKey; /** @type {boolean} */ Event.prototype.shiftLeft; /** @type {Window} */ Event.prototype.source; /** @type {Element} */ Event.prototype.srcElement; Event.prototype.srcFilter; /** @type {string} */ Event.prototype.srcUrn; /** @type {Element} */ Event.prototype.toElement; Event.prototype.userName; /** @type {number} */ Event.prototype.wheelDelta; /** @type {number} */ Event.prototype.x; /** @type {number} */ Event.prototype.y; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441257.aspx */ function MSPointerPoint() {} /** @type {number} */ MSPointerPoint.prototype.pointerId; /** @type {number} */ MSPointerPoint.prototype.pointerType; /** * @constructor * @extends {Event} * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441233.aspx */ function MSPointerEvent() {} /** @type {number} */ MSPointerEvent.MSPOINTER_TYPE_MOUSE; /** @type {number} */ MSPointerEvent.MSPOINTER_TYPE_PEN; /** @type {number} */ MSPointerEvent.MSPOINTER_TYPE_TOUCH; /** @type {number} */ MSPointerEvent.prototype.pointerId; /** @type {number} */ MSPointerEvent.prototype.pointerType; /** * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {Window} viewArg * @param {number} detailArg * @param {number} screenXArg * @param {number} screenYArg * @param {number} clientXArg * @param {number} clientYArg * @param {boolean} ctrlKeyArg * @param {boolean} altKeyArg * @param {boolean} shiftKeyArg * @param {boolean} metaKeyArg * @param {number} buttonArg * @param {Element} relatedTargetArg * @param {number} offsetXArg * @param {number} offsetYArg * @param {number} widthArg * @param {number} heightArg * @param {number} pressure * @param {number} rotation * @param {number} tiltX * @param {number} tiltY * @param {number} pointerIdArg * @param {number} pointerType * @param {number} hwTimestampArg * @param {boolean} isPrimary * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441246.aspx */ MSPointerEvent.prototype.initPointerEvent; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/ie/hh968249(v=vs.85).aspx */ function MSGesture() {} /** * @type {Element} */ MSGesture.prototype.target; /** * @param {number} pointerId */ MSGesture.prototype.addPointer = function(pointerId) {}; MSGesture.prototype.stop = function() {}; /** * @constructor * @extends {Event} * @see http://msdn.microsoft.com/en-us/library/ie/hh772076(v=vs.85).aspx */ function MSGestureEvent() {} /** @type {number} */ MSGestureEvent.prototype.expansion; /** @type {!MSGesture} */ MSGestureEvent.prototype.gestureObject; /** @type {number} */ MSGestureEvent.prototype.hwTimestamp; /** @type {number} */ MSGestureEvent.prototype.rotation; /** @type {number} */ MSGestureEvent.prototype.scale; /** @type {number} */ MSGestureEvent.prototype.translationX; /** @type {number} */ MSGestureEvent.prototype.translationY; /** @type {number} */ MSGestureEvent.prototype.velocityAngular; /** @type {number} */ MSGestureEvent.prototype.velocityExpansion; /** @type {number} */ MSGestureEvent.prototype.velocityX; /** @type {number} */ MSGestureEvent.prototype.velocityY; /** * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {Window} viewArg * @param {number} detailArg * @param {number} screenXArg * @param {number} screenYArg * @param {number} clientXArg * @param {number} clientYArg * @param {number} offsetXArg * @param {number} offsetYArg * @param {number} translationXArg * @param {number} translationYArg * @param {number} scaleArg * @param {number} expansionArg * @param {number} rotationArg * @param {number} velocityXArg * @param {number} velocityYArg * @param {number} velocityExpansionArg * @param {number} velocityAngularArg * @param {number} hwTimestampArg * @param {EventTarget} relatedTargetArg * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh441187.aspx */ MSGestureEvent.prototype.initGestureEvent; closure-compiler-20130227+dfsg1/externs/w3c_geolocation.js0000644000175000017500000000673312115204405021470 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for W3C's Geolocation specification * http://www.w3.org/TR/geolocation-API/ * @externs */ /** * @constructor * @see http://www.w3.org/TR/geolocation-API/#geolocation */ function Geolocation() {} /** * @param {function(GeolocationPosition)} successCallback * @param {(function(GeolocationPositionError)|null)=} opt_errorCallback * @param {GeolocationPositionOptions=} opt_options */ Geolocation.prototype.getCurrentPosition = function(successCallback, opt_errorCallback, opt_options) {}; /** * @param {function(GeolocationPosition)} successCallback * @param {(function(GeolocationPositionError)|null)=} opt_errorCallback * @param {GeolocationPositionOptions=} opt_options * @return {number} */ Geolocation.prototype.watchPosition = function(successCallback, opt_errorCallback, opt_options) {}; /** @param {number} watchId */ Geolocation.prototype.clearWatch = function(watchId) {}; /** * @constructor * @see http://www.w3.org/TR/geolocation-API/#coordinates */ function GeolocationCoordinates() {} /** @type {number} */ GeolocationCoordinates.prototype.latitude; /** @type {number} */ GeolocationCoordinates.prototype.longitude; /** @type {number} */ GeolocationCoordinates.prototype.accuracy; /** @type {number} */ GeolocationCoordinates.prototype.altitude; /** @type {number} */ GeolocationCoordinates.prototype.altitudeAccuracy; /** @type {number} */ GeolocationCoordinates.prototype.heading; /** @type {number} */ GeolocationCoordinates.prototype.speed; /** * @constructor * @see http://www.w3.org/TR/geolocation-API/#position */ function GeolocationPosition() {} /** @type {GeolocationCoordinates} */ GeolocationPosition.prototype.coords; /** @type {Date} */ GeolocationPosition.prototype.timestamp; /** * @constructor * @see http://www.w3.org/TR/geolocation-API/#position-options */ function GeolocationPositionOptions() {} /** @type {boolean} */ GeolocationPositionOptions.prototype.enableHighAccuracy; /** @type {number} */ GeolocationPositionOptions.prototype.maximumAge; /** @type {number} */ GeolocationPositionOptions.prototype.timeout; /** * @constructor * @see http://www.w3.org/TR/geolocation-API/#position-error */ function GeolocationPositionError() {} /** @type {number} */ GeolocationPositionError.prototype.code; /** @type {string} */ GeolocationPositionError.prototype.message; /** @type {number} */ GeolocationPositionError.prototype.UNKNOWN_ERROR; /** @type {number} */ GeolocationPositionError.prototype.PERMISSION_DENIED; /** @type {number} */ GeolocationPositionError.prototype.POSITION_UNAVAILABLE; /** @type {number} */ GeolocationPositionError.prototype.TIMEOUT; /** @type {Geolocation} */ Navigator.prototype.geolocation; closure-compiler-20130227+dfsg1/externs/ie_dom.js0000644000175000017500000010172412115204405017641 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over the * W3C's DOM specification by IE in JScript. This file depends on * w3c_dom2.js. The whole file has NOT been fully type annotated. * * When a non-standard extension appears in both Gecko and IE, we put * it in gecko_dom.js * * @externs */ // TODO(nicksantos): Rewrite all the DOM interfaces as interfaces, instead // of kludging them as an inheritance hierarchy. /** * @constructor * @extends {Document} * @see http://msdn.microsoft.com/en-us/library/ms757878(VS.85).aspx */ function XMLDOMDocument() {} /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms761398(VS.85).aspx */ XMLDOMDocument.prototype.async; /** * @type {!Function} * @see http://msdn.microsoft.com/en-us/library/ms762647(VS.85).aspx */ XMLDOMDocument.prototype.ondataavailable; /** * @type {!Function} * @see http://msdn.microsoft.com/en-us/library/ms764640(VS.85).aspx */ XMLDOMDocument.prototype.onreadystatechange; /** * @type {!Function} * @see http://msdn.microsoft.com/en-us/library/ms753795(VS.85).aspx */ XMLDOMDocument.prototype.ontransformnode; /** * @type {Object} * @see http://msdn.microsoft.com/en-us/library/ms756041(VS.85).aspx */ XMLDOMDocument.prototype.parseError; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms761353(VS.85).aspx */ XMLDOMDocument.prototype.preserveWhiteSpace; /** * @type {number} * @see http://msdn.microsoft.com/en-us/library/ms753702(VS.85).aspx */ XMLDOMDocument.prototype.readyState; /** * @see http://msdn.microsoft.com/en-us/library/ms762283(VS.85).aspx * @type {boolean} */ XMLDOMDocument.prototype.resolveExternals; /** * @see http://msdn.microsoft.com/en-us/library/ms760290(v=vs.85).aspx * @param {string} name * @param {*} value */ XMLDOMDocument.prototype.setProperty = function(name, value) {}; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms767669(VS.85).aspx */ XMLDOMDocument.prototype.url; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms762791(VS.85).aspx */ XMLDOMDocument.prototype.validateOnParse; /** * @see http://msdn.microsoft.com/en-us/library/ms763830(VS.85).aspx */ XMLDOMDocument.prototype.abort = function() {}; /** * @param {*} type * @param {string} name * @param {string} namespaceURI * @return {Node} * @see http://msdn.microsoft.com/en-us/library/ms757901(VS.85).aspx * @nosideeffects */ XMLDOMDocument.prototype.createNode = function(type, name, namespaceURI) {}; /** * @param {string} xmlSource * @return {boolean} * @see http://msdn.microsoft.com/en-us/library/ms762722(VS.85).aspx * @override */ XMLDOMDocument.prototype.load = function(xmlSource) {}; /** * @param {string} xmlString * @return {boolean} * @see http://msdn.microsoft.com/en-us/library/ms754585(VS.85).aspx * @override */ XMLDOMDocument.prototype.loadXML = function(xmlString) {}; /** * @param {string} id * @return {Node} * @see http://msdn.microsoft.com/en-us/library/ms766397(VS.85).aspx */ XMLDOMDocument.prototype.nodeFromID = function(id) {}; //============================================================================== // XMLNode methods and properties // In a real DOM hierarchy, XMLDOMDocument inherits from XMLNode and Document. // Since we can't express that in our type system, we put XMLNode properties // on Node. /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms767570(VS.85).aspx */ Node.prototype.baseName; /** * @type {?string} * @see http://msdn.microsoft.com/en-us/library/ms762763(VS.85).aspx */ Node.prototype.dataType; /** * @type {Node} * @see http://msdn.microsoft.com/en-us/library/ms764733(VS.85).aspx */ Node.prototype.definition; /** * IE5 used document instead of ownerDocument. * Old versions of WebKit used document instead of contentDocument. * @type {Document} */ Node.prototype.document; /** * Inserts the given HTML text into the element at the location. * @param {string} sWhere Where to insert the HTML text, one of 'beforeBegin', * 'afterBegin', 'beforeEnd', 'afterEnd'. * @param {string} sText HTML text to insert. * @see http://msdn.microsoft.com/en-us/library/ms536452(VS.85).aspx */ Node.prototype.insertAdjacentHTML = function(sWhere, sText) {}; /** * @type {*} * @see http://msdn.microsoft.com/en-us/library/ms762308(VS.85).aspx */ Node.prototype.nodeTypedValue; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms757895(VS.85).aspx */ Node.prototype.nodeTypeString; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms762237(VS.85).aspx */ Node.prototype.parsed; /** * @type {Element} * @see http://msdn.microsoft.com/en-us/library/ms534327(VS.85).aspx */ Node.prototype.parentElement; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms753816(VS.85).aspx */ Node.prototype.specified; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms762687(VS.85).aspx */ Node.prototype.text; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms755989(VS.85).aspx */ Node.prototype.xml; /** * @param {string} expression An XPath expression. * @return {NodeList} * @see http://msdn.microsoft.com/en-us/library/ms754523(VS.85).aspx * @nosideeffects */ Node.prototype.selectNodes = function(expression) {}; /** * @param {string} expression An XPath expression. * @return {Node} * @see http://msdn.microsoft.com/en-us/library/ms757846(VS.85).aspx * @nosideeffects */ Node.prototype.selectSingleNode = function(expression) {}; /** * @param {Node} stylesheet XSLT stylesheet. * @return {string} * @see http://msdn.microsoft.com/en-us/library/ms761399(VS.85).aspx * @nosideeffects */ Node.prototype.transformNode = function(stylesheet) {}; /** * @param {Node} stylesheet XSLT stylesheet. * @param {Object} outputObject * @see http://msdn.microsoft.com/en-us/library/ms766561(VS.85).aspx */ Node.prototype.transformNodeToObject = function(stylesheet, outputObject) {}; //============================================================================== // Node methods /** * @param {boolean=} opt_bRemoveChildren Whether to remove the entire sub-tree. * Defaults to false. * @return {Node} The object that was removed. * @see http://msdn.microsoft.com/en-us/library/ms536708(VS.85).aspx */ Node.prototype.removeNode = function(opt_bRemoveChildren) {}; /** * @constructor */ function ClipboardData() {} /** * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx * @param {string} type Type of clipboard data to clear. 'Text' or * 'URL' or 'File' or 'HTML' or 'Image'. */ ClipboardData.prototype.clearData = function(type) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx * @param {string} type Type of clipboard data to set ('Text' or 'URL'). * @param {string} data Data to set * @return {boolean} Whether the data were set correctly. */ ClipboardData.prototype.setData = function(type, data) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx * @param {string} type Type of clipboard data to get ('Text' or 'URL'). * @return {string} The current data */ ClipboardData.prototype.getData = function(type) { }; /** @type {function(new:ActiveXObject, string, string=)} */ Window.prototype.ActiveXObject; /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window */ var window; /** * @param {number|undefined|null} immediateID * @see https://developer.mozilla.org/en-US/docs/DOM/window.clearImmediate * @see http://msdn.microsoft.com/en-us/library/ie/hh924825(v=vs.85).aspx */ Window.prototype.clearImmediate = function(immediateID) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms535220(VS.85).aspx * @type ClipboardData */ Window.prototype.clipboardData; /** * @see http://msdn.microsoft.com/en-us/library/ms533724(VS.85).aspx */ Window.prototype.dialogHeight; /** * @see http://msdn.microsoft.com/en-us/library/ms533725(VS.85).aspx */ Window.prototype.dialogLeft; /** * @see http://msdn.microsoft.com/en-us/library/ms533726(VS.85).aspx */ Window.prototype.dialogTop; /** * @see http://msdn.microsoft.com/en-us/library/ms533727(VS.85).aspx */ Window.prototype.dialogWidth; /** * @see http://msdn.microsoft.com/en-us/library/ms535863(VS.85).aspx */ Window.prototype.event; /** * @see http://msdn.microsoft.com/en-us/library/cc197012(VS.85).aspx */ Window.prototype.maxConnectionsPer1_0Server; /** * @see http://msdn.microsoft.com/en-us/library/cc197013(VS.85).aspx */ Window.prototype.maxConnectionsPerServer; /** * @see http://msdn.microsoft.com/en-us/library/ms534198(VS.85).aspx */ Window.prototype.offscreenBuffering; /** * @see http://msdn.microsoft.com/en-us/library/ms534389(VS.85).aspx */ Window.prototype.screenLeft; /** * @see http://msdn.microsoft.com/en-us/library/ms534389(VS.85).aspx */ Window.prototype.screenTop; /** * @type {function(new:XDomainRequest)} * @see http://msdn.microsoft.com/en-us/library/cc287985(VS.85).aspx */ Window.prototype.XDomainRequest; /** * @type {function(new:XMLHttpRequest)} * @see http://msdn.microsoft.com/en-us/library/ms535157(VS.85).aspx */ Window.prototype.XMLHttpRequest; // Functions /** * @param {string} event * @param {Function} handler * @see http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx */ Window.prototype.attachEvent; /** * @see http://msdn.microsoft.com/en-us/library/ms536392(VS.85).aspx */ Window.prototype.createPopup; /** * @see http://msdn.microsoft.com/en-us/library/ms536411(VS.85).aspx */ Window.prototype.detachEvent; /** * @see http://msdn.microsoft.com/en-us/library/ms536420(VS.85).aspx */ Window.prototype.execScript; /** * @see http://msdn.microsoft.com/en-us/library/ms536425(VS.85).aspx */ Window.prototype.focus; /** * @param {number} x * @param {number} y * @see http://msdn.microsoft.com/en-us/library/ms536618(VS.85).aspx */ Window.prototype.moveBy = function(x, y) {}; /** * @param {number} x * @param {number} y * @see http://msdn.microsoft.com/en-us/library/ms536626(VS.85).aspx */ Window.prototype.moveTo = function(x, y) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536638(VS.85).aspx */ Window.prototype.navigate; /** * @param {*=} opt_url * @param {string=} opt_windowName * @param {string=} opt_windowFeatures * @param {boolean=} opt_replace * @return {Window} * @see http://msdn.microsoft.com/en-us/library/ms536651(VS.85).aspx */ Window.prototype.open = function(opt_url, opt_windowName, opt_windowFeatures, opt_replace) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536672(VS.85).aspx */ Window.prototype.print = function() {}; /** * @param {string} message * @param {string=} opt_defValue * @return {?string} * @see http://msdn.microsoft.com/en-us/library/ms536673(VS.85).aspx */ Window.prototype.prompt = function(message, opt_defValue) {}; /** * @param {number} width * @param {number} height * @see http://msdn.microsoft.com/en-us/library/ms536722(VS.85).aspx */ Window.prototype.resizeBy = function(width, height) {}; /** * @param {number} width * @param {number} height * @see http://msdn.microsoft.com/en-us/library/ms536723(VS.85).aspx */ Window.prototype.resizeTo = function(width, height) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536738(VS.85).aspx */ Window.prototype.setActive; /** * @param {function()} callback * @return {number} * @see https://developer.mozilla.org/en-US/docs/DOM/window.setImmediate * @see http://msdn.microsoft.com/en-us/library/ie/hh773176(v=vs.85).aspx */ Window.prototype.setImmediate = function(callback) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536758(VS.85).aspx */ Window.prototype.showHelp; /** * @see http://msdn.microsoft.com/en-us/library/ms536761(VS.85).aspx */ Window.prototype.showModelessDialog; /** * @see http://msdn.microsoft.com/en-us/library/ms535246%28v=vs.85%29.aspx * @const */ Window.prototype.external; /** * @constructor */ function History() { }; /** * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx * @param {number|string} delta The number of entries to go back, or * the URL to which to go back. (URL form is supported only in IE) */ History.prototype.go = function(delta) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx * @param {number=} opt_distance The number of entries to go back * (Mozilla doesn't support distance -- use #go instead) */ History.prototype.back = function(opt_distance) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx * @type {number} */ History.prototype.length; /** * @see http://msdn.microsoft.com/en-us/library/ms535864(VS.85).aspx */ History.prototype.forward = function() {}; /** * @type {boolean} * @implicitCast * @see http://msdn.microsoft.com/en-us/library/ie/ms533072(v=vs.85).aspx */ HTMLFrameElement.prototype.allowTransparency; /** * @see http://msdn.microsoft.com/en-us/library/ms533692(VS.85).aspx */ HTMLFrameElement.prototype.contentWindow; /** * @type {boolean} * @implicitCast * @see http://msdn.microsoft.com/en-us/library/ie/ms533072(v=vs.85).aspx */ HTMLIFrameElement.prototype.allowTransparency; /** * @see http://msdn.microsoft.com/en-us/library/ms533692(VS.85).aspx */ HTMLIFrameElement.prototype.contentWindow; /** * @see http://msdn.microsoft.com/en-us/library/ms536385(VS.85).aspx */ HTMLBodyElement.prototype.createControlRange; /** * @constructor */ function ControlRange() {} ControlRange.prototype.add; ControlRange.prototype.addElement; ControlRange.prototype.execCommand; ControlRange.prototype.item; ControlRange.prototype.queryCommandEnabled; ControlRange.prototype.queryCommandIndeterm; ControlRange.prototype.queryCommandState; ControlRange.prototype.queryCommandSupported; ControlRange.prototype.queryCommandValue; ControlRange.prototype.remove; ControlRange.prototype.scrollIntoView; ControlRange.prototype.select; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/ms535872.aspx */ function TextRange() {} /** * @see http://msdn.microsoft.com/en-us/library/ms533538(VS.85).aspx */ TextRange.prototype.boundingHeight; /** * @see http://msdn.microsoft.com/en-us/library/ms533539(VS.85).aspx */ TextRange.prototype.boundingLeft; /** * @see http://msdn.microsoft.com/en-us/library/ms533540(VS.85).aspx */ TextRange.prototype.boundingTop; /** * @see http://msdn.microsoft.com/en-us/library/ms533541(VS.85).aspx */ TextRange.prototype.boundingWidth; /** * @see http://msdn.microsoft.com/en-us/library/ms533874(VS.85).aspx */ TextRange.prototype.htmlText; /** * @see http://msdn.microsoft.com/en-us/library/ms534200(VS.85).aspx */ TextRange.prototype.offsetLeft; /** * @see http://msdn.microsoft.com/en-us/library/ms534303(VS.85).aspx */ TextRange.prototype.offsetTop; /** * @see http://msdn.microsoft.com/en-us/library/ms534676(VS.85).aspx */ TextRange.prototype.text; /** * @see http://msdn.microsoft.com/en-us/library/ms536371(VS.85).aspx */ TextRange.prototype.collapse; /** * @see http://msdn.microsoft.com/en-us/library/ms536373(VS.85).aspx */ TextRange.prototype.compareEndPoints; /** * @see http://msdn.microsoft.com/en-us/library/ms536416(VS.85).aspx */ TextRange.prototype.duplicate; /** * @see http://msdn.microsoft.com/en-us/library/ms536419(VS.85).aspx */ TextRange.prototype.execCommand; /** * @see http://msdn.microsoft.com/en-us/library/ms536421(VS.85).aspx */ TextRange.prototype.expand; /** * @see http://msdn.microsoft.com/en-us/library/ms536422(VS.85).aspx */ TextRange.prototype.findText; /** * @see http://msdn.microsoft.com/en-us/library/ms536432(VS.85).aspx */ TextRange.prototype.getBookmark; /** * @see http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx */ TextRange.prototype.getBoundingClientRect; /** * @see http://msdn.microsoft.com/en-us/library/ms536435(VS.85).aspx */ TextRange.prototype.getClientRects; /** * @see http://msdn.microsoft.com/en-us/library/ms536450(VS.85).aspx */ TextRange.prototype.inRange; /** * @see http://msdn.microsoft.com/en-us/library/ms536458(VS.85).aspx */ TextRange.prototype.isEqual; /** * @see http://msdn.microsoft.com/en-us/library/ms536616(VS.85).aspx */ TextRange.prototype.move; /** * @see http://msdn.microsoft.com/en-us/library/ms536620(VS.85).aspx */ TextRange.prototype.moveEnd; /** * @see http://msdn.microsoft.com/en-us/library/ms536623(VS.85).aspx */ TextRange.prototype.moveStart; /** * @see http://msdn.microsoft.com/en-us/library/ms536628(VS.85).aspx */ TextRange.prototype.moveToBookmark; /** * @see http://msdn.microsoft.com/en-us/library/ms536630(VS.85).aspx */ TextRange.prototype.moveToElementText; /** * @see http://msdn.microsoft.com/en-us/library/ms536632(VS.85).aspx */ TextRange.prototype.moveToPoint; /** * @see http://msdn.microsoft.com/en-us/library/ms536654(VS.85).aspx */ TextRange.prototype.parentElement; /** * @see http://msdn.microsoft.com/en-us/library/ms536656(VS.85).aspx */ TextRange.prototype.pasteHTML; /** * @see http://msdn.microsoft.com/en-us/library/ms536676(VS.85).aspx */ TextRange.prototype.queryCommandEnabled; /** * @see http://msdn.microsoft.com/en-us/library/ms536678(VS.85).aspx */ TextRange.prototype.queryCommandIndeterm; /** * @see http://msdn.microsoft.com/en-us/library/ms536679(VS.85).aspx */ TextRange.prototype.queryCommandState; /** * @see http://msdn.microsoft.com/en-us/library/ms536681(VS.85).aspx */ TextRange.prototype.queryCommandSupported; /** * @see http://msdn.microsoft.com/en-us/library/ms536683(VS.85).aspx */ TextRange.prototype.queryCommandValue; /** * @see http://msdn.microsoft.com/en-us/library/ms536730(VS.85).aspx */ TextRange.prototype.scrollIntoView; /** * @see http://msdn.microsoft.com/en-us/library/ms536735(VS.85).aspx */ TextRange.prototype.select; /** * @see http://msdn.microsoft.com/en-us/library/ms536745(VS.85).aspx */ TextRange.prototype.setEndPoint; /** * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/ms536418(VS.85).aspx */ Selection.prototype.clear = function() {}; /** * @return {TextRange|ControlRange} * @see http://msdn.microsoft.com/en-us/library/ms536394(VS.85).aspx */ Selection.prototype.createRange = function() {}; /** * @return {Array.} * @see http://msdn.microsoft.com/en-us/library/ms536396(VS.85).aspx */ Selection.prototype.createRangeCollection = function() {}; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/ms537447(VS.85).aspx */ function controlRange() {} Document.prototype.loadXML; // http://msdn.microsoft.com/en-us/library/ms531073(VS.85).aspx /** * @see http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx */ Document.prototype.activeElement; /** * @see http://msdn.microsoft.com/en-us/library/ms533553(VS.85).aspx */ Document.prototype.charset; /** * @see http://msdn.microsoft.com/en-us/library/ms533693(VS.85).aspx */ Document.prototype.cookie; /** * @see http://msdn.microsoft.com/en-us/library/ms533714(VS.85).aspx */ Document.prototype.defaultCharset; /** * @see http://msdn.microsoft.com/en-us/library/ms533731(VS.85).aspx */ Document.prototype.dir; /** * @see http://msdn.microsoft.com/en-us/library/cc196988(VS.85).aspx */ Document.prototype.documentMode; /** * @see http://msdn.microsoft.com/en-us/library/ms533747(VS.85).aspx */ Document.prototype.expando; /** * @see http://msdn.microsoft.com/en-us/library/ms533750(VS.85).aspx */ Document.prototype.fileCreatedDate; /** * @see http://msdn.microsoft.com/en-us/library/ms533751(VS.85).aspx */ Document.prototype.fileModifiedDate; /** * @see http://msdn.microsoft.com/en-us/library/ms533752(VS.85).aspx */ Document.prototype.fileSize; /** * @see http://msdn.microsoft.com/en-us/library/ms534331(VS.85).aspx */ Document.prototype.parentWindow; /** * @see http://msdn.microsoft.com/en-us/library/ms534353(VS.85).aspx */ Document.prototype.protocol; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx */ Document.prototype.readyState; /** * @type {Selection} * @see http://msdn.microsoft.com/en-us/library/ms535869(VS.85).aspx */ Document.prototype.selection; /** * @see http://msdn.microsoft.com/en-us/library/ms534704(VS.85).aspx */ Document.prototype.uniqueID; /** * @see http://msdn.microsoft.com/en-us/library/ms534708(VS.85).aspx */ Document.prototype.URL; /** * @see http://msdn.microsoft.com/en-us/library/ms534709(VS.85).aspx */ Document.prototype.URLUnencoded; /** * @see http://msdn.microsoft.com/en-us/library/ms535155(VS.85).aspx */ Document.prototype.XMLDocument; /** * @see http://msdn.microsoft.com/en-us/library/ms535163(VS.85).aspx */ Document.prototype.XSLDocument; // functions /** * @param {string} event * @param {Function} handler * @see http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx */ Document.prototype.attachEvent; /** * @see http://msdn.microsoft.com/en-us/library/ms536390(VS.85).aspx */ Document.prototype.createEventObject; /** * @see http://msdn.microsoft.com/en-us/library/ms531194(VS.85).aspx */ Document.prototype.createStyleSheet; /** * @see http://msdn.microsoft.com/en-us/library/ms536411(VS.85).aspx */ Document.prototype.detachEvent; /** * @see http://msdn.microsoft.com/en-us/library/ms536425(VS.85).aspx */ Document.prototype.focus; /** * @see http://msdn.microsoft.com/en-us/library/ms536447(VS.85).aspx * @return {boolean} */ Document.prototype.hasFocus = function() {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536614(VS.85).aspx */ Document.prototype.mergeAttributes; /** * @see http://msdn.microsoft.com/en-us/library/ms536685(VS.85).aspx */ Document.prototype.recalc; /** * @see http://msdn.microsoft.com/en-us/library/ms536689(VS.85).aspx */ Document.prototype.releaseCapture; /** * @see http://msdn.microsoft.com/en-us/library/ms536738(VS.85).aspx */ Document.prototype.setActive; // collections /** * @see http://msdn.microsoft.com/en-us/library/ms537434(VS.85).aspx */ Document.prototype.all; /** * @see http://msdn.microsoft.com/en-us/library/ms537445(VS.85).aspx */ Document.prototype.childNodes; /** * @see http://msdn.microsoft.com/en-us/library/ms537459(VS.85).aspx */ Document.prototype.frames; /** * @see http://msdn.microsoft.com/en-us/library/ms537461(VS.85).aspx */ Document.prototype.images; /** * @see http://msdn.microsoft.com/en-us/library/ms537470(VS.85).aspx */ Document.prototype.namespaces; /** * @see http://msdn.microsoft.com/en-us/library/ms537487(VS.85).aspx */ Document.prototype.scripts; /** * @param {string} sUrl * @return {number} * @see http://msdn.microsoft.com/en-us/library/ms535922(VS.85).aspx */ Element.prototype.addBehavior = function(sUrl) {}; /** * @param {string} event * @param {Function} handler * @see http://msdn.microsoft.com/en-us/library/mm536343(v=vs.85).aspx */ Element.prototype.attachEvent; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms533546(VS.85).aspx */ Element.prototype.canHaveChildren; /** * @param {number} iCoordX Integer that specifies the client window coordinate * of x. * @param {number} iCoordY Integer that specifies the client window coordinate * of y. * @return {string} The component of an element located at the specified * coordinates. * @see http://msdn.microsoft.com/en-us/library/ms536375(VS.85).aspx * @nosideeffects */ Element.prototype.componentFromPoint = function(iCoordX, iCoordY) {}; /** * @param {Element} el The element to check * @return {boolean} If the element is contained within this one. * @see http://msdn.microsoft.com/en-us/library/ms536377(VS.85).aspx * @nosideeffects */ Element.prototype.contains = function(el) {}; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms533690(VS.85).aspx */ Element.prototype.contentEditable; /** * @return {TextRange} * @see http://msdn.microsoft.com/en-us/library/ms536401(VS.85).aspx */ Element.prototype.createTextRange; /** * @see http://msdn.microsoft.com/en-us/library/ms535231(VS.85).aspx */ Element.prototype.currentStyle; /** * @param {string=} opt_action * @see http://msdn.microsoft.com/en-us/library/ms536414%28VS.85%29.aspx */ Element.prototype.doScroll = function(opt_action) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms536423(VS.85).aspx */ Element.prototype.fireEvent; /** * @type {boolean} * @see http://msdn.microsoft.com/en-us/library/ms533783(VS.85).aspx */ Element.prototype.hideFocus; /** * @see http://msdn.microsoft.com/en-us/library/ms533899.aspx */ Element.prototype.innerText; /** * @see http://msdn.microsoft.com/en-us/library/ms537838(VS.85).aspx */ Element.prototype.isContentEditable; /** * @param {number} pointerId Id of the pointer that is assign to the element. * @see http://msdn.microsoft.com/en-us/library/ie/hh771882(v=vs.85).aspx */ Element.prototype.msSetPointerCapture = function(pointerId) {}; /** * @param {number} pointerId * @see http://msdn.microsoft.com/en-us/library/ie/hh771880.aspx */ Element.prototype.msReleasePointerCapture = function(pointerId) {}; /** * @type {?function(Event)} * @see http://msdn.microsoft.com/en-us/library/ms536903(v=vs.85).aspx */ Element.prototype.onbeforedeactivate; /** * @type {?function(Event)} * @see http://msdn.microsoft.com/en-us/library/ms536945(VS.85).aspx */ Element.prototype.onmouseenter; /** * @type {?function(Event)} * @see http://msdn.microsoft.com/en-us/library/ms536946(VS.85).aspx */ Element.prototype.onmouseleave; /** * @type {?function(Event)} * @see http://msdn.microsoft.com/en-us/library/ms536969(VS.85).aspx */ Element.prototype.onselectstart; /** * @see http://msdn.microsoft.com/en-us/library/aa752326(VS.85).aspx */ Element.prototype.outerHTML; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms534359(VS.85).aspx */ Element.prototype.readyState; /** * @see http://msdn.microsoft.com/en-us/library/ms536689(VS.85).aspx */ Element.prototype.releaseCapture = function() {}; /** * @param {number} iID * @return {boolean} * @see http://msdn.microsoft.com/en-us/library/ms536700(VS.85).aspx */ Element.prototype.removeBehavior = function(iID) {}; /** * @see http://msdn.microsoft.com/en-us/library/aa703996(VS.85).aspx */ Element.prototype.runtimeStyle; /** * @param {boolean=} opt_bContainerCapture Events originating in a container are * captured by the container. Defaults to true. * @see http://msdn.microsoft.com/en-us/library/ms536742(VS.85).aspx */ Element.prototype.setCapture = function(opt_bContainerCapture) {}; /** * @see http://msdn.microsoft.com/en-us/library/ms534635(VS.85).aspx */ Element.prototype.sourceIndex; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms537840.aspx */ Element.prototype.unselectable; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/aa752462(v=vs.85).aspx */ function HTMLFiltersCollection() {} /** * @see http://msdn.microsoft.com/en-us/library/aa752463(v=vs.85).aspx * @type {number} */ HTMLFiltersCollection.prototype.length; /** * @see http://msdn.microsoft.com/en-us/library/ms537452(v=vs.85).aspx * @type {HTMLFiltersCollection} */ Element.prototype.filters; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/ms532853(v=vs.85).aspx */ function HTMLFilter() {} /** * @see http://msdn.microsoft.com/en-us/library/ms532954(v=vs.85).aspx */ HTMLFilter.prototype.apply = function() {}; /** * @constructor * @extends {HTMLFilter} * @see http://msdn.microsoft.com/en-us/library/ms532967(v=vs.85).aspx */ function AlphaFilter() {} /** * @see http://msdn.microsoft.com/en-us/library/ms532910(v=vs.85).aspx * @type {number} */ AlphaFilter.prototype.Opacity; /** * @constructor * @extends {HTMLFilter} * @see http://msdn.microsoft.com/en-us/library/ms532969(v=vs.85).aspx */ function AlphaImageLoaderFilter() {} /** * @see http://msdn.microsoft.com/en-us/library/ms532920(v=vs.85).aspx * @type {string} */ AlphaImageLoaderFilter.prototype.sizingMethod; /** * @constructor * @see http://msdn.microsoft.com/en-us/library/ms535866(VS.85).aspx */ function Location() {} /** * @see http://trac.webkit.org/changeset/113945 * @type {DOMStringList} */ Location.prototype.ancestorOrigins; /** * @see http://msdn.microsoft.com/en-us/library/ms533775(VS.85).aspx * @type {string} */ Location.prototype.hash; /** * @see http://msdn.microsoft.com/en-us/library/ms533784(VS.85).aspx * @type {string} */ Location.prototype.host; /** * @see http://msdn.microsoft.com/en-us/library/ms533785(VS.85).aspx * @type {string} */ Location.prototype.hostname; /** * @see http://msdn.microsoft.com/en-us/library/ms533867(VS.85).aspx * @type {string} */ Location.prototype.href; /** * @see https://docs.google.com/document/view?id=1r_VTFKApVOaNIkocrg0z-t7lZgzisTuGTXkdzAk4gLU&hl=en * @type {string} */ Location.prototype.origin; /** * @see http://msdn.microsoft.com/en-us/library/ms534332(VS.85).aspx * @type {string} */ Location.prototype.pathname; /** * @see http://msdn.microsoft.com/en-us/library/ms534342(VS.85).aspx */ Location.prototype.port; /** * @see http://msdn.microsoft.com/en-us/library/ms534353(VS.85).aspx * @type {string} */ Location.prototype.protocol; /** * @see http://msdn.microsoft.com/en-us/library/ms534620(VS.85).aspx * @type {string} */ Location.prototype.search; /** * @see http://msdn.microsoft.com/en-us/library/ms536342(VS.85).aspx * @param {string} url */ Location.prototype.assign = function(url) {}; /** * @param {boolean=} opt_forceReload If true, reloads the page from * the server. Defaults to false. * @see http://msdn.microsoft.com/en-us/library/ms536691(VS.85).aspx */ Location.prototype.reload = function(opt_forceReload) {}; /** * @param {string} url * @see http://msdn.microsoft.com/en-us/library/ms536712(VS.85).aspx */ Location.prototype.replace = function(url) {}; // For IE, returns an object representing key-value pairs for all the global // variables prefixed with str, e.g. test* /** @param {*=} opt_str */ function RuntimeObject(opt_str) {} /** * @type {StyleSheet} * @see http://msdn.microsoft.com/en-us/library/dd347030(VS.85).aspx */ HTMLStyleElement.prototype.styleSheet; /** * IE implements Cross Origin Resource Sharing (cross-domain XMLHttpRequests) * via the XDomainRequest object. * * @constructor * @see http://msdn.microsoft.com/en-us/library/cc288060(v=vs.85).aspx * @see http://www.w3.org/TR/cors/ */ function XDomainRequest() {} /** * Aborts the request. * @see http://msdn.microsoft.com/en-us/library/cc288129(v=vs.85).aspx */ XDomainRequest.prototype.abort = function() {}; /** * Sets the method and URL for the request. * @param {string} bstrMethod Either "GET" or "POST" * @param {string} bstrUrl The target URL * @see http://msdn.microsoft.com/en-us/library/cc288168(v=vs.85).aspx */ XDomainRequest.prototype.open = function(bstrMethod, bstrUrl) {}; /** * Sends the request. * @param {string=} varBody The POST body to send to the server. If omitted, * the behavior is identical to sending an empty string. * @see http://msdn.microsoft.com/en-us/library/cc288207(v=vs.85).aspx */ XDomainRequest.prototype.send = function(varBody) {}; /** * Called if the request could not be completed. Note that error information is * not available. * @see http://msdn.microsoft.com/en-us/library/ms536930%28v=VS.85%29.aspx * @type {?function()} */ XDomainRequest.prototype.onerror; /** * Called when the response has finished. * @see http://msdn.microsoft.com/en-us/library/ms536942%28v=VS.85%29.aspx * @type {?function()} */ XDomainRequest.prototype.onload; /** * Called every time part of the response has been received. * @see http://msdn.microsoft.com/en-us/library/cc197058%28v=VS.85%29.aspx * @type {?function()} */ XDomainRequest.prototype.onprogress; /** * Called if the timeout period has elapsed. * @see http://msdn.microsoft.com/en-us/library/cc197061%28v=VS.85%29.aspx * @type {?function()} */ XDomainRequest.prototype.ontimeout; /** * The current response body. * @see http://msdn.microsoft.com/en-us/library/cc287956%28v=VS.85%29.aspx * @type {string} */ XDomainRequest.prototype.responseText; /** * The timeout (in milliseconds) for the request. * @type {number} */ XDomainRequest.prototype.timeout; /** * The Content-Type of the response, or an empty string. * @type {string} */ XDomainRequest.prototype.contentType; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms533542(v=vs.85).aspx */ Navigator.prototype.browserLanguage; /** * @type {boolean} * @see http://blogs.msdn.com/b/ie/archive/2011/09/20/touch-input-for-ie10-and-metro-style-apps.aspx */ Navigator.prototype.msPointerEnabled; /** * @type {number} * @see http://msdn.microsoft.com/en-us/library/ms533721(v=vs.85).aspx */ Screen.prototype.deviceXDPI; closure-compiler-20130227+dfsg1/externs/w3c_anim_timing.js0000644000175000017500000001102012115204405021441 0ustar apoapo/* * Copyright 2011 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. */ /** * @fileoverview Definitions for timing control for script base animations. The * whole file has been fully type annotated. * * @see http://www.w3.org/TR/animation-timing/ * @see http://webstuff.nfshost.com/anim-timing/Overview.html * @externs */ /** * @param {function(number)} callback * @param {Element=} opt_element In early versions of this API, the callback * was invoked only if the element was visible. * @return {number} */ function requestAnimationFrame(callback, opt_element) {}; /** * @param {number} handle */ function cancelRequestAnimationFrame(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ function webkitRequestAnimationFrame(callback, opt_element) {}; /** * @param {number} handle */ function webkitCancelRequestAnimationFrame(handle) {}; /** * @param {number} handle */ function webkitCancelAnimationFrame(handle) {}; /** * @param {?function(number)} callback It's legitimate to pass a null * callback and listen on the MozBeforePaint event instead. * @param {Element=} opt_element * @return {number} */ function mozRequestAnimationFrame(callback, opt_element) {}; /** * @param {number} handle */ function mozCancelRequestAnimationFrame(handle) {}; /** * @param {number} handle */ function mozCancelAnimationFrame(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ function msRequestAnimationFrame(callback, opt_element) {}; /** * @param {number} handle */ function msCancelRequestAnimationFrame(handle) {}; /** * @param {number} handle */ function msCancelAnimationFrame(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ function oRequestAnimationFrame(callback, opt_element) {}; /** * @param {number} handle */ function oCancelRequestAnimationFrame(handle) {}; /** * @param {number} handle */ function oCancelAnimationFrame(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ Window.prototype.requestAnimationFrame = function(callback, opt_element) {}; /** * @param {number} handle */ Window.prototype.cancelRequestAnimationFrame = function(handle) {}; /** * @param {number} handle */ Window.prototype.cancelAnimationFrame = function(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ Window.prototype.webkitRequestAnimationFrame = function(callback, opt_element) {}; /** * @param {number} handle */ Window.prototype.webkitCancelRequestAnimationFrame = function(handle) {}; /** * @param {number} handle */ Window.prototype.webkitCancelAnimationFrame = function(handle) {}; /** * @param {?function(number)} callback It's legitimate to pass a null * callback and listen on the MozBeforePaint event instead. * @param {Element=} opt_element * @return {number} */ Window.prototype.mozRequestAnimationFrame = function(callback, opt_element) {}; /** * @param {number} handle */ Window.prototype.mozCancelRequestAnimationFrame = function(handle) {}; /** * @param {number} handle */ Window.prototype.mozCancelAnimationFrame = function(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ Window.prototype.msRequestAnimationFrame = function(callback, opt_element) {}; /** * @param {number} handle */ Window.prototype.msCancelRequestAnimationFrame = function(handle) {}; /** * @param {number} handle */ Window.prototype.msCancelAnimationFrame = function(handle) {}; /** * @param {function(number)} callback * @param {Element=} opt_element * @return {number} */ Window.prototype.oRequestAnimationFrame = function(callback, opt_element) {}; /** * @param {number} handle */ Window.prototype.oCancelRequestAnimationFrame = function(handle) {}; /** * @param {number} handle */ Window.prototype.oCancelAnimationFrame = function(handle) {}; closure-compiler-20130227+dfsg1/externs/deprecated.js0000644000175000017500000000264412115204405020506 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview JavaScript Built-Ins that are not * part of any specifications but are * still needed in some project's build. * @externs * */ // Do we need an opera.js? var opera; Window.prototype.opera; Window.prototype.opera.postError; /** @constructor */ function XSLTProcessor() {} /** @constructor */ function NodeFilter() {} /** * @param {*=} opt_text * @param {*=} opt_value * @param {*=} opt_defaultSelected * @param {*=} opt_selected * @constructor * @extends {Element} */ function Option(opt_text, opt_value, opt_defaultSelected, opt_selected) {} // The "methods" object is a place to hang arbitrary external // properties. It is a throwback to pre-typed days, and should // not be used for any new definitions; it exists only to bridge // the gap between the old way and the new way. var methods = {}; closure-compiler-20130227+dfsg1/externs/webstorage.js0000644000175000017500000000604512115204405020547 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for W3C's WebStorage specification. * This file depends on html5.js. * @externs */ /** * @interface * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-interface */ function Storage() {} /** * @type {number} * @const */ Storage.prototype.length; /** * @param {number} index * @return {?string} */ Storage.prototype.key = function(index) {}; /** * @param {string} key * @return {?string} */ Storage.prototype.getItem = function(key) {}; /** * @param {string} key * @param {string} data * @return {void} */ Storage.prototype.setItem = function(key, data) {}; /** * @param {string} key * @return {void} */ Storage.prototype.removeItem = function(key) {}; /** * @return {void} */ Storage.prototype.clear = function() {}; /** * @interface * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-sessionstorage-attribute */ function WindowSessionStorage() {} /** * @type {Storage} */ WindowSessionStorage.prototype.sessionStorage; /** * Window implements WindowSessionStorage * * @type {Storage} */ Window.prototype.sessionStorage; /** * @interface * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-localstorage-attribute */ function WindowLocalStorage() {} /** * @type {Storage} */ WindowLocalStorage.prototype.localStorage; /** * Window implements WindowLocalStorage * * @type {Storage} */ Window.prototype.localStorage; /** * This is the storage event interface. * @see http://www.w3.org/TR/2011/CR-webstorage-20111208/#the-storage-event * @extends {Event} * @constructor */ function StorageEvent() {} /** * @type {string} */ StorageEvent.prototype.key; /** * @type {?string} */ StorageEvent.prototype.oldValue; /** * @type {?string} */ StorageEvent.prototype.newValue; /** * @type {string} */ StorageEvent.prototype.url; /** * @type {?Storage} */ StorageEvent.prototype.storageArea; /** * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {string} keyArg * @param {?string} oldValueArg * @param {?string} newValueArg * @param {string} urlArg * @param {?Storage} storageAreaArg * @return {void} */ StorageEvent.prototype.initStorageEvent = function(typeArg, canBubbleArg, cancelableArg, keyArg, oldValueArg, newValueArg, urlArg, storageAreaArg) {}; closure-compiler-20130227+dfsg1/externs/gears_types.js0000644000175000017500000004042412115204405020731 0ustar apoapo/* * Copyright 2007 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. */ /** * @fileoverview Extern types introduced by the Gears extension. * @externs */ /** @constructor */ function GearsFactory() {} /** * @param {*=} opt_classVersion * @return {Object} */ GearsFactory.prototype.create = function(className, opt_classVersion) {}; /** @return {string} */ GearsFactory.prototype.getBuildInfo = function() {}; /** * @param {*=} opt_siteName * @param {*=} opt_imageUrl * @param {*=} opt_extraMessage * @return {boolean} */ GearsFactory.prototype.getPermission = function(opt_siteName, opt_imageUrl, opt_extraMessage) {}; /** @type {boolean} */ GearsFactory.prototype.hasPermission = false; /** @param {Object} globalObject */ GearsFactory.prototype.privateSetGlobalObject = function(globalObject) {}; /** @type {string} */ GearsFactory.prototype.version; /** @constructor */ function GearsBlob() {} /** @type {number} */ GearsBlob.prototype.length = 0; /** @return {GearsBlob} */ GearsBlob.prototype.slice = function(offset, length) {}; /** @constructor */ function GearsBlobBuilder() {} /** @param {string|GearsBlob} appendee */ GearsBlobBuilder.prototype.append = function(appendee) {}; /** @return {GearsBlob} */ GearsBlobBuilder.prototype.getAsBlob = function() {}; /** @constructor */ function GearsDatabase() {} /** @param {string=} opt_name */ GearsDatabase.prototype.open = function(opt_name) {}; /** * @param {*=} opt_argArray * @return {GearsResultSet} */ GearsDatabase.prototype.execute = function(sqlStatement, opt_argArray) {}; GearsDatabase.prototype.close = function() {}; GearsDatabase.prototype.remove = function() {}; /** @type {number} */ GearsDatabase.prototype.lastInsertRowId = 0; /** @type {number} */ GearsDatabase.prototype.rowsAffected = 0; /** @constructor */ function GearsResultSet() {} /** @return {boolean} */ GearsResultSet.prototype.isValidRow = function() {}; GearsResultSet.prototype.next = function() {}; GearsResultSet.prototype.close = function() {}; /** @return {number} */ GearsResultSet.prototype.fieldCount = function() {}; /** @return {string} */ GearsResultSet.prototype.fieldName = function(fieldIndex) {}; GearsResultSet.prototype.field = function(fieldIndex) {}; GearsResultSet.prototype.fieldByName = function(fieldName) {}; /** @constructor */ function GearsDesktop() {} /** * @param {string} name The name of the shortcut. * @param {string} url The URL for the shortcut. * @param {Object} icons The icon set to use for the shortcut. * @param {string=} opt_description The description of the shortcut. */ GearsDesktop.prototype.createShortcut = function(name, url, icons, opt_description) {}; /** * @param {GearsBlob} blob The blob to get metadata for. * @return {Object} The metadata object. */ GearsDesktop.prototype.extractMetaData = function(blob) { return null; }; /** * @param {Object} event The event object we're calling from. * @param {string} flavor The type of object to get. * @return {Object} The drag data. */ GearsDesktop.prototype.getDragData = function(event, flavor) { return null; }; /** * @param {function(Array.)} callback The callback after opening. * @param {GearsOpenFileOptions=} opt_options The options to use for opening. */ GearsDesktop.prototype.openFiles = function(callback, opt_options) {}; /** * @param {Object} event * @param {string} dropEffect */ GearsDesktop.prototype.setDropEffect = function(event, dropEffect) {}; /** @constructor */ function GearsFile() {} /** @type {string} */ GearsFile.prototype.name = ''; /** @type {GearsBlob} */ GearsFile.prototype.blob; /** @constructor */ function GearsOpenFileOptions() {} /** @type {boolean} */ GearsOpenFileOptions.prototype.singleFile = false; /** @type {Array.} */ GearsOpenFileOptions.prototype.filter = []; /** @constructor */ function GearsGeolocation() {} /** @const @type {GearsPosition} */ GearsGeolocation.prototype.lastPosition; /** * @param {function(GearsPosition)} successCallback * @param {(function(GearsPositionError)|null)=} opt_errorCallback * @param {GearsPositionOptions=} opt_options */ GearsGeolocation.prototype.getCurrentPosition = function(successCallback, opt_errorCallback, opt_options) {}; /** * @param {function(GearsPosition)} successCallback * @param {(function(GearsPositionError)|null)=} opt_errorCallback * @param {GearsPositionOptions=} opt_options * @return {number} */ GearsGeolocation.prototype.watchPosition = function(successCallback, opt_errorCallback, opt_options) {}; /** @param {number} watchId */ GearsGeolocation.prototype.clearWatch = function(watchId) {}; /** * @param {string=} opt_siteName * @param {string=} opt_imageUrl * @param {string=} opt_extraMessage * @return {boolean} */ GearsGeolocation.prototype.getPermission = function(opt_siteName, opt_imageUrl, opt_extraMessage) {}; /** @constructor */ function GearsCoords() {} /** @const @type {number} */ GearsCoords.prototype.latitude; /** @const @type {number} */ GearsCoords.prototype.longitude; /** @const @type {number} */ GearsCoords.prototype.accuracy; /** @const @type {number} */ GearsCoords.prototype.altitude; /** @const @type {number} */ GearsCoords.prototype.altitudeAccuracy; /** @constructor */ function GearsPosition() {} /** @const @type {number} */ GearsPosition.prototype.latitude; /** @const @type {number} */ GearsPosition.prototype.longitude; /** @const @type {number} */ GearsPosition.prototype.accuracy; /** @const @type {number} */ GearsPosition.prototype.altitude; /** @const @type {number} */ GearsPosition.prototype.altitudeAccuracy; /** @const @type {GearsCoords} */ GearsPosition.prototype.coords; /** @const @type {Date} */ GearsPosition.prototype.timestamp; /** @const @type {GearsAddress} */ GearsPosition.prototype.gearsAddress; /** @constructor */ function GearsPositionOptions() {} /** @type {boolean} */ GearsPositionOptions.prototype.enableHighAccuracy; /** @type {boolean} */ GearsPositionOptions.prototype.gearsRequestAddress; /** @type {string} */ GearsPositionOptions.prototype.gearsAddressLanguage = ''; /** @type {Array.} */ GearsPositionOptions.prototype.gearsLocationProviderUrls; /** @type {number} */ GearsPositionOptions.prototype.maximumAge; /** @type {number} */ GearsPositionOptions.prototype.timeout; /** @constructor */ function GearsPositionError() {} /** @const @type {number} */ GearsPositionError.prototype.code = 0; /** @const @type {string} */ GearsPositionError.prototype.message = ''; /** @const @type {number} */ GearsPositionError.prototype.UNKNOWN_ERROR; /** @const @type {number} */ GearsPositionError.prototype.PERMISSION_DENIED; /** @const @type {number} */ GearsPositionError.prototype.POSITION_UNAVAILABLE; /** @const @type {number} */ GearsPositionError.prototype.TIMEOUT; /** @constructor */ function GearsAddress() {} /** @const @type {string} */ GearsAddress.prototype.streetNumber = ''; /** @const @type {string} */ GearsAddress.prototype.street = ''; /** @const @type {string} */ GearsAddress.prototype.premises = ''; /** @const @type {string} */ GearsAddress.prototype.city = ''; /** @const @type {string} */ GearsAddress.prototype.county = ''; /** @const @type {string} */ GearsAddress.prototype.region = ''; /** @const @type {string} */ GearsAddress.prototype.country = ''; /** @const @type {string} */ GearsAddress.prototype.countryCode = ''; /** @const @type {string} */ GearsAddress.prototype.postalCode = ''; /** @constructor */ function GearsHttpRequest() {} GearsHttpRequest.prototype.open = function(method, url, opt_async) {}; GearsHttpRequest.prototype.setRequestHeader = function(name, value) {}; /** * @param {(string|GearsBlob|null)=} opt_postData */ GearsHttpRequest.prototype.send = function(opt_postData) {}; GearsHttpRequest.prototype.abort = function() {}; /** @return {string} */ GearsHttpRequest.prototype.getResponseHeader = function(name) {}; /** @return {string} */ GearsHttpRequest.prototype.getAllResponseHeaders = function() {}; /** @type {function(GearsProgressEvent)|null} */ GearsHttpRequest.prototype.onprogress = function(progressEvent) {}; GearsHttpRequest.prototype.onreadystatechange = function() {}; /** @type {number} */ GearsHttpRequest.prototype.readyState = 0; /** @type {GearsBlob} */ GearsHttpRequest.prototype.responseBlob; /** @type {string} */ GearsHttpRequest.prototype.responseText = ''; /** @type {number} */ GearsHttpRequest.prototype.status = 0; /** @type {string} */ GearsHttpRequest.prototype.statusText = ''; /** @type {GearsHttpRequestUpload} */ GearsHttpRequest.prototype.upload; /** @constructor */ function GearsHttpRequestUpload() {} /** @type {function(GearsProgressEvent)|null} */ GearsHttpRequestUpload.prototype.onprogress = function(progressEvent) {}; /** @constructor */ function GearsProgressEvent() {} /** @type {number} */ GearsProgressEvent.prototype.total = 0; /** @type {number} */ GearsProgressEvent.prototype.loaded = 0; /** @type {boolean} */ GearsProgressEvent.prototype.lengthComputable; /** @constructor */ function GearsLocalServer() {} /** @return {boolean} */ GearsLocalServer.prototype.canServeLocally = function(url) {}; /** * @param {*=} opt_requiredCookie * @return {GearsResourceStore} */ GearsLocalServer.prototype.createStore = function(name, opt_requiredCookie) {}; /** * @param {*=} opt_requiredCookie * @return {GearsResourceStore} */ GearsLocalServer.prototype.openStore = function(name, opt_requiredCookie) {}; /** * @param {*=} opt_requiredCookie */ GearsLocalServer.prototype.removeStore = function(name, opt_requiredCookie) {}; /** * @param {*=} opt_requiredCookie * @return {GearsManagedResourceStore} */ GearsLocalServer.prototype.createManagedStore = function(name, opt_requiredCookie) {}; /** * @param {*=} opt_requiredCookie * @return {GearsManagedResourceStore} */ GearsLocalServer.prototype.openManagedStore = function(name, opt_requiredCookie) {}; /** * @param {*=} opt_requiredCookie */ GearsLocalServer.prototype.removeManagedStore = function(name, opt_requiredCookie) {}; /** @constructor */ function GearsManagedResourceStore() {} /** @type {string} */ GearsManagedResourceStore.prototype.name = ''; /** @type {string} */ GearsManagedResourceStore.prototype.requiredCookie = ''; /** @type {boolean} */ GearsManagedResourceStore.prototype.enabled = false; /** @type {string} */ GearsManagedResourceStore.prototype.manifestUrl = ''; /** @type {number} */ GearsManagedResourceStore.prototype.lastUpdateCheckTime = 0; /** @type {number} */ GearsManagedResourceStore.prototype.updateStatus = 0; /** @type {string} */ GearsManagedResourceStore.prototype.lastErrorMessage = ''; /** @type {string} */ GearsManagedResourceStore.prototype.currentVersion = ''; /** @type {function(GearsCompleteObject)|null} */ GearsManagedResourceStore.prototype.oncomplete = function(details) {}; /** @type {function(GearsErrorObject)|null} */ GearsManagedResourceStore.prototype.onerror = function(error) {}; /** @type {function(GearsProgressObject)|null} */ GearsManagedResourceStore.prototype.onprogress = function(details) {}; GearsManagedResourceStore.prototype.checkForUpdate = function() {}; /** @constructor */ function GearsResourceStore() {} /** @type {string} */ GearsResourceStore.prototype.name = ''; /** @type {string} */ GearsResourceStore.prototype.requiredCookie = ''; /** @type {boolean} */ GearsResourceStore.prototype.enabled = false; /** @return {number} */ GearsResourceStore.prototype.capture = function(urlOrUrlArray, callback) {}; /** * @param {GearsBlob} blob * @param {string} url * @param {string=} opt_contentType */ GearsResourceStore.prototype.captureBlob = function(blob, url, opt_contentType) {}; GearsResourceStore.prototype.abortCapture = function(captureId) {}; GearsResourceStore.prototype.remove = function(url) {}; GearsResourceStore.prototype.rename = function(srcUrl, destUrl) {}; GearsResourceStore.prototype.copy = function(srcUrl, destUrl) {}; /** @return {boolean} */ GearsResourceStore.prototype.isCaptured = function(url) {}; GearsResourceStore.prototype.captureFile = function(fileInputElement, url) {}; /** @return {string} */ GearsResourceStore.prototype.getCapturedFileName = function(url) {}; /** @return {string} */ GearsResourceStore.prototype.getHeader = function(url, name) {}; /** @return {string} */ GearsResourceStore.prototype.getAllHeaders = function(url) {}; /** * @param {string} url * @return {GearsBlob} */ GearsResourceStore.prototype.getAsBlob = function(url) {}; /** @return {GearsFileSubmitter} */ GearsResourceStore.prototype.createFileSubmitter = function() {}; /** @constructor */ function GearsFileSubmitter() {} GearsFileSubmitter.prototype.setFileInputElement = function(htmlElement, url) {}; /** @constructor */ function GearsTimer() {} /** @return {number} */ GearsTimer.prototype.setTimeout = function(fullScriptOrCallback, msecDelay) {}; /** @return {number} */ GearsTimer.prototype.setInterval = function(fullScript, msecDelay) {}; GearsTimer.prototype.clearTimeout = function(timerId) {}; GearsTimer.prototype.clearInterval = function(timerId) {}; /** @constructor */ function GearsWorkerPool() {} /** * @param {string} messageText The message contents * (Deprecated, use messageObject.text) * @param {number} senderId ID of the source worker. * (Deprecated, use messageObject.sender) * @param {GearsMessageObject} messageObject An object containing * all information about a message. */ GearsWorkerPool.prototype.onmessage = function(messageText, senderId, messageObject) {}; /** * @param {!GearsErrorObject} errorObject An object that explains * the problem. * @return {boolean|undefined} Return true to indicate that the error was * handled, which prevents it from bubbling up to the parent. */ GearsWorkerPool.prototype.onerror = function(errorObject) {}; /** @return {number} */ GearsWorkerPool.prototype.createWorker = function(scriptText) {}; /** @return {number} */ GearsWorkerPool.prototype.createWorkerFromUrl = function(scriptUrl) {}; /** * @param {*} message * @param {number} destWorkerId */ GearsWorkerPool.prototype.sendMessage = function(message, destWorkerId) {}; GearsWorkerPool.prototype.allowCrossOrigin = function() {}; /** @constructor */ function GearsMessageObject() {} /** @type {string} */ GearsMessageObject.prototype.text = ''; /** @type {string} */ GearsMessageObject.prototype.sender = ''; /** @type {string} */ GearsMessageObject.prototype.origin = ''; /** @type {*} */ GearsMessageObject.prototype.body; /** @constructor */ function GearsCompleteObject() {} /** @type {string} */ GearsCompleteObject.prototype.newVersion = ''; /** @constructor */ function GearsErrorObject() {} /** @type {string} */ GearsErrorObject.prototype.message = ''; /** @type {number} */ GearsErrorObject.prototype.lineNumber; /** @constructor */ function GearsProgressObject() {} /** @type {number} */ GearsProgressObject.prototype.filesComplete = 0; /** @type {number} */ GearsProgressObject.prototype.filesTotal = 0; closure-compiler-20130227+dfsg1/externs/iphone.js0000644000175000017500000002573612115204405017677 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for all iPhone extensions. Created from: * http://developer.apple.com/library/safari/navigation/ * * @externs * @author agrieve@google.com (Andrew Grieve) */ /** * The Touch class represents a single touch on the surface. A touch is the * presence or movement of a finger that is part of a unique multi-touch * sequence. * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchClassReference/Touch/Touch.html * @constructor */ function Touch() {} /** * The x-coordinate of the touch's location relative to the window's viewport. * @type {number} */ Touch.prototype.clientX; /** * The y-coordinate of the touch's location relative to the window's viewport. * @type {number} */ Touch.prototype.clientY; /** * The unique identifier for this touch object. * @type {number} */ Touch.prototype.identifier; /** * The x-coordinate of the touch's location in page coordinates. * @type {number} */ Touch.prototype.pageX; /** * The y-coordinate of the touch's location in page coordinates. * @type {number} */ Touch.prototype.pageY; /** * The x-coordinate of the touch's location in screen coordinates. * @type {number} */ Touch.prototype.screenX; /** * The y-coordinate of the touch's location in screen coordinates. * @type {number} */ Touch.prototype.screenY; /** * The target of this touch. * @type {EventTarget} */ Touch.prototype.target; /** * Creates a new Touch object. * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html#//apple_ref/javascript/instm/Document/createTouch * @param {Window} view * @param {EventTarget} target * @param {number} identifier * @param {number} pageX * @param {number} pageY * @param {number} screenX * @param {number} screenY * @return {Touch} */ Document.prototype.createTouch = function(view, target, identifier, pageX, pageY, screenX, screenY) {}; /** * The TouchList class is used to represent a collection of Touch objects. * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchListClassReference/TouchList/TouchList.html * @constructor */ function TouchList() {} /** * The number of Touch objects in this TouchList object. * @type {number} */ TouchList.prototype.length; /** * Returns the Touch object at the given index. * @param {number} index * @return {!Touch} */ TouchList.prototype.item = function(index) {}; /** * Creates a new TouchList object. * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/DocumentAdditionsReference/DocumentAdditions/DocumentAdditions.html#//apple_ref/javascript/instm/Document/createTouchList * @param {Array.} touches * @return {TouchList} */ Document.prototype.createTouchList = function(touches) {}; /** * The TouchEvent class encapsulates information about a touch event. * *

      The system continually sends TouchEvent objects to an application as * fingers touch and move across a surface. A touch event provides a snapshot of * all touches during a multi-touch sequence, most importantly the touches that * are new or have changed for a particular target. A multi-touch sequence * begins when a finger first touches the surface. Other fingers may * subsequently touch the surface, and all fingers may move across the surface. * The sequence ends when the last of these fingers is lifted from the surface. * An application receives touch event objects during each phase of any touch. *

      * *

      The different types of TouchEvent objects that can occur are: *

        *
      • touchstart - Sent when a finger for a given event touches the surface. *
      • touchmove - Sent when a given event moves on the surface. *
      • touchend - Sent when a given event lifts from the surface. *
      • touchcancel - Sent when the system cancels tracking for the touch. *
      * TouchEvent objects are combined together to form high-level GestureEvent * objects that are also sent during a multi-touch sequence.

      * * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html * @extends {UIEvent} * @constructor */ function TouchEvent() {} /** * A collection of Touch objects representing all touches associated with this * target. * @type {TouchList} */ TouchEvent.prototype.touches; /** * A collection of Touch objects representing all touches associated with this * target. * @type {TouchList} */ TouchEvent.prototype.targetTouches; /** * A collection of Touch objects representing all touches that changed in this event. * @type {TouchList} */ TouchEvent.prototype.changedTouches; /** * The distance between two fingers since the start of an event as a multiplier * of the initial distance. The initial value is 1.0. If less than 1.0, the * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is * pinch open (to zoom in). * @type {number} */ TouchEvent.prototype.scale; /** * The delta rotation since the start of an event, in degrees, where clockwise * is positive and counter-clockwise is negative. The initial value is 0.0. * @type {number} */ TouchEvent.prototype.rotation; /** * Initializes a newly created TouchEvent object. * @param {string} type * @param {boolean} canBubble * @param {boolean} cancelable * @param {Window} view * @param {number} detail * @param {number} screenX * @param {number} screenY * @param {number} clientX * @param {number} clientY * @param {boolean} ctrlKey * @param {boolean} altKey * @param {boolean} shiftKey * @param {boolean} metaKey * @param {TouchList} touches * @param {TouchList} targetTouches * @param {TouchList} changedTouches * @param {number} scale * @param {number} rotation */ TouchEvent.prototype.initTouchEvent = function(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, touches, targetTouches, changedTouches, scale, rotation) {}; /** * The GestureEvent class encapsulates information about a multi-touch gesture. * * GestureEvent objects are high-level events that encapsulate the low-level * TouchEvent objects. Both GestureEvent and TouchEvent events are sent during * a multi-touch sequence. Gesture events contain scaling and rotation * information allowing gestures to be combined, if supported by the platform. * If not supported, one gesture ends before another starts. Listen for * GestureEvent events if you want to respond to gestures only, not process * the low-level TouchEvent objects. * * @see http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html * @extends {UIEvent} * @constructor */ function GestureEvent() {} /** * The distance between two fingers since the start of an event as a multiplier * of the initial distance. The initial value is 1.0. If less than 1.0, the * gesture is pinch close (to zoom out). If greater than 1.0, the gesture is * pinch open (to zoom in). * @type {number} */ GestureEvent.prototype.scale; /** * The delta rotation since the start of an event, in degrees, where clockwise * is positive and counter-clockwise is negative. The initial value is 0.0. * @type {number} */ GestureEvent.prototype.rotation; /** * The target of this gesture. * @type {EventTarget} */ GestureEvent.prototype.target; /** * Initializes a newly created GestureEvent object. * @param {string} type * @param {boolean} canBubble * @param {boolean} cancelable * @param {Window} view * @param {number} detail * @param {number} screenX * @param {number} screenY * @param {number} clientX * @param {number} clientY * @param {boolean} ctrlKey * @param {boolean} altKey * @param {boolean} shiftKey * @param {boolean} metaKey * @param {EventTarget} target * @param {number} scale * @param {number} rotation */ GestureEvent.prototype.initGestureEvent = function(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, target, scale, rotation) {}; /** * Specifies the JavaScript method to invoke when the system cancels tracking * for the touch. * @type {?function(!TouchEvent)} */ Element.prototype.ontouchcancel = function(e) {}; /** * Specifies the JavaScript method to invoke when a given event lifts from the * surface. * @type {?function(!TouchEvent)} */ Element.prototype.ontouchend = function(e) {}; /** * Specifies the JavaScript method to invoke when a finger for a given event * moves on the surface. * @type {?function(!TouchEvent)} */ Element.prototype.ontouchmove = function(e) {}; /** * Specifies the JavaScript method to invoke when a finger for a given event * touches the surface. * @type {?function(!TouchEvent)} */ Element.prototype.ontouchstart = function(e) {}; /** * Specifies the JavaScript method to invoke when a gesture is started by * two or more fingers touching the surface. * @type {?function(!GestureEvent)} */ Element.prototype.ongesturestart = function(e) {}; /** * Specifies the JavaScript method to invoke when fingers are moved during a * gesture. * @type {?function(!GestureEvent)} */ Element.prototype.ongesturechange = function(e) {}; /** * Specifies the JavaScript method to invoke when a gesture ends (when there are * 0 or 1 fingers touching the surface). * @type {?function(!GestureEvent)} */ Element.prototype.ongestureend = function(e) {}; /** * Specifies the JavaScript method to invoke when the browser device's * orientation changes, i.e.the device is rotated. * @type {?function(!Event)} * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html */ Window.prototype.onorientationchange = function(e) {}; /** * Returns the orientation of the browser's device, one of [-90, 0, 90, 180]. * @type {number} * @see http://developer.apple.com/library/IOS/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html */ Window.prototype.orientation; /** * @implicitCast * @type {boolean} */ HTMLInputElement.prototype.autocorrect; /** * @implicitCast * @type {boolean} */ HTMLInputElement.prototype.autocapitalize; /** * @implicitCast * @type {boolean} */ HTMLTextAreaElement.prototype.autocorrect; /** * @implicitCast * @type {boolean} */ HTMLTextAreaElement.prototype.autocapitalize; closure-compiler-20130227+dfsg1/externs/w3c_dom3.js0000644000175000017500000005273412115204405020031 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's DOM Level 3 specification. * This file depends on w3c_dom2.js. * The whole file has been fully type annotated. * Created from * http://www.w3.org/TR/DOM-Level-3-Core/ecma-script-binding.html * * @externs */ /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF */ DOMException.prototype.code; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF */ DOMException.VALIDATION_ERR = 16; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-258A00AF */ DOMException.TYPE_MISMATCH_ERR = 17; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList */ function DOMStringList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-length */ DOMStringList.prototype.length; /** * @param {string} str * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-contains */ DOMStringList.prototype.contains = function(str) {}; /** * @param {number} index * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList-item */ DOMStringList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList */ function NameList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-length */ NameList.prototype.length; /** * @param {string} str * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-contains * @nosideeffects */ NameList.prototype.contains = function(str) {}; /** * @param {string} namespaceURI * @param {string} name * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-containsNS * @nosideeffects */ NameList.prototype.containsNS = function(namespaceURI, name) {}; /** * @param {number} index * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-getName * @nosideeffects */ NameList.prototype.getName = function(index) {}; /** * @param {number} index * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#NameList-getNamespaceURI * @nosideeffects */ NameList.prototype.getNamespaceURI = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList */ function DOMImplementationList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList-length */ DOMImplementationList.prototype.length; /** * @param {number} index * @return {DOMImplementation} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationList-item * @nosideeffects */ DOMImplementationList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementationSource */ function DOMImplementationSource() {} /** * @param {string} namespaceURI * @param {string} publicId * @param {DocumentType} doctype * @return {Document} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocument * @nosideeffects */ DOMImplementation.prototype.createDocument = function(namespaceURI, publicId, doctype) {}; /** * @param {string} qualifiedName * @param {string} publicId * @param {string} systemId * @return {DocumentType} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocType * @nosideeffects */ DOMImplementation.prototype.createDocumentType = function(qualifiedName, publicId, systemId) {}; /** * @param {string} features * @return {DOMImplementation} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-getDOMImpl * @nosideeffects */ DOMImplementationSource.prototype.getDOMImplementation = function(features) {}; /** * @param {string} features * @return {DOMImplementationList} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-getDOMImpls * @nosideeffects */ DOMImplementationSource.prototype.getDOMImplementationList = function(features) {}; /** * @param {string} feature * @param {string} version * @return {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMImplementation3-getFeature * @nosideeffects */ DOMImplementation.prototype.getFeature = function(feature, version) {}; /** * @param {Node} externalNode * @return {Node} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-adoptNode */ Document.prototype.adoptNode = function(externalNode) {}; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-documentURI */ Document.prototype.documentURI; /** * @type {DOMConfiguration} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-domConfig */ Document.prototype.domConfig; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-inputEncoding */ Document.prototype.inputEncoding; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-strictErrorChecking */ Document.prototype.strictErrorChecking; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-encoding */ Document.prototype.xmlEncoding; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-standalone */ Document.prototype.xmlStandalone; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-version */ Document.prototype.xmlVersion; /** * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-normalizeDocument */ Document.prototype.normalizeDocument = function() {}; /** * @param {Node} n * @param {string} namespaceURI * @param {string} qualifiedName * @return {Node} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode */ Document.prototype.renameNode = function(n, namespaceURI, qualifiedName) {}; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-baseURI */ Node.prototype.baseURI; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSLocalN */ Node.prototype.localName; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSname */ Node.prototype.namespaceURI; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSPrefix */ Node.prototype.prefix; /** * @type {string} * @implicitCast * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-textContent */ Node.prototype.textContent; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_DISCONNECTED */ Node.DOCUMENT_POSITION_DISCONNECTED = 0x01; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_PRECEDING */ Node.DOCUMENT_POSITION_PRECEDING = 0x02; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_FOLLOWING */ Node.DOCUMENT_POSITION_FOLLOWING = 0x04; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_CONTAINS */ Node.DOCUMENT_POSITION_CONTAINS = 0x08; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_CONTAINED_BY */ Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node-DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC */ Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; /** * @param {Node} other * @return {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition * @nosideeffects */ Node.prototype.compareDocumentPosition = function(other) {}; /** * @param {string} feature * @param {string} version * @return {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-getFeature * @nosideeffects */ Node.prototype.getFeature = function(feature, version) {}; /** * @param {string} key * @return {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-getUserData * @nosideeffects */ Node.prototype.getUserData = function(key) {}; /** * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeHasAttrs * @nosideeffects */ Node.prototype.hasAttributes = function() {}; /** * @param {string} namespaceURI * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace * @nosideeffects */ Node.prototype.isDefaultNamespace = function(namespaceURI) {}; /** * @param {Node} arg * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isEqualNode * @nosideeffects */ Node.prototype.isEqualNode = function(arg) {}; /** * @param {Node} other * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isSameNode * @nosideeffects */ Node.prototype.isSameNode = function(other) {}; /** * @param {string} feature * @param {string} version * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-Node-supports * @nosideeffects */ Node.prototype.isSupported = function(feature, version) {}; /** * @param {string} prefix * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI * @nosideeffects */ Node.prototype.lookupNamespaceURI = function(prefix) {}; /** * @param {string} namespaceURI * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix * @nosideeffects */ Node.prototype.lookupPrefix = function(namespaceURI) {}; /** * @return undefined * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-normalize */ Node.prototype.normalize = function() {}; /** * @param {Object} key * @param {Object} data * @param {UserDataHandler} handler * @return {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-setUserData' */ Node.prototype.setUserData = function(key, data, handler) {}; /** * @param {string} query * @return {Node} * @see http://www.w3.org/TR/selectors-api/#queryselector * @nosideeffects */ Node.prototype.querySelector = function(query) {}; /** * @param {string} query * @return {!NodeList} * @see http://www.w3.org/TR/selectors-api/#queryselectorall * @nosideeffects */ Node.prototype.querySelectorAll = function(query) {}; /** * @type {Element} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-ownerElement */ Attr.prototype.ownerElement; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-isId */ Attr.prototype.isId; /** * @type {TypeInfo} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-schemaTypeInfo */ Attr.prototype.schemaTypeInfo; /** * @type {TypeInfo} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Element-schemaTypeInfo */ Element.prototype.schemaTypeInfo; /** * @param {string} namespaceURI * @param {string} localName * @return {Attr} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElGetAtNodeNS * @nosideeffects */ Element.prototype.getAttributeNodeNS = function(namespaceURI, localName) {}; /** * @param {string} namespaceURI * @param {string} localName * @return {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElGetAttrNS * @nosideeffects */ Element.prototype.getAttributeNS = function(namespaceURI, localName) {}; /** * @param {string} namespaceURI * @param {string} localName * @return {!NodeList} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-A6C90942 * @nosideeffects */ Element.prototype.getElementsByTagNameNS = function(namespaceURI, localName) {}; /** * @param {string} name * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttr * @nosideeffects */ Element.prototype.hasAttribute = function(name) {}; /** * @param {string} namespaceURI * @param {string} localName * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttrNS * @nosideeffects */ Element.prototype.hasAttributeNS = function(namespaceURI, localName) {}; /** * @param {string} namespaceURI * @param {string} localName * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElRemAtNS */ Element.prototype.removeAttributeNS = function(namespaceURI, localName) {}; /** * @param {Attr} newAttr * @return {Attr} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetAtNodeNS */ Element.prototype.setAttributeNodeNS = function(newAttr) {}; /** * @param {string} namespaceURI * @param {string} qualifiedName * @param {string|number|boolean} value Values are converted to strings with * ToString, so we accept number and boolean since both convert easily to * strings. * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetAttrNS */ Element.prototype.setAttributeNS = function(namespaceURI, qualifiedName, value) {}; /** * @param {string} name * @param {boolean} isId * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttr */ Element.prototype.setIdAttribute = function(name, isId) {}; /** * @param {Attr} idAttr * @param {boolean} isId * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttrNode */ Element.prototype.setIdAttributeNode = function(idAttr, isId) {}; /** * @param {string} namespaceURI * @param {string} localName * @param {boolean} isId * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElSetIdAttrNS */ Element.prototype.setIdAttributeNS = function(namespaceURI, localName, isId) {}; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Text3-isElementContentWhitespace * @nosideeffects */ Text.prototype.isElementContentWhitespace; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Text3-wholeText */ Text.prototype.wholeText; /** * @param {string} newText * @return {Text} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Text3-replaceWholeText */ Text.prototype.replaceWholeText = function(newText) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo */ function TypeInfo() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_EXTENSION */ TypeInfo.prototype.DERIVATION_EXTENSION; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_LIST */ TypeInfo.prototype.DERIVATION_LIST; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_RESTRICTION */ TypeInfo.prototype.DERIVATION_RESTRICTION; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-DERIVATION_UNION */ TypeInfo.prototype.DERIVATION_UNION; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-typeName */ TypeInfo.prototype.typeName; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-typeNamespace */ TypeInfo.prototype.typeNamespace; /** * @param {string} typeNamespaceArg * @param {string} typeNameArg * @param {number} derivationMethod * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom * @nosideeffects */ TypeInfo.prototype.isDerivedFrom = function(typeNamespaceArg, typeNameArg, derivationMethod) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler */ function UserDataHandler() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-CLONED */ UserDataHandler.prototype.NODE_CLONED = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-IMPORTED */ UserDataHandler.prototype.NODE_IMPORTED = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-DELETED */ UserDataHandler.prototype.NODE_DELETED = 3; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-RENAMED */ UserDataHandler.prototype.NODE_RENAMED = 4; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#UserDataHandler-ADOPTED */ UserDataHandler.prototype.NODE_ADOPTED = 5; /** * @param {number} operation * @param {string} key * @param {*=} opt_data * @param {?Node=} opt_src * @param {?Node=} opt_dst * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-handleUserDataEvent */ UserDataHandler.prototype.handle = function(operation, key, opt_data, opt_src, opt_dst) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-Interfaces-DOMError */ function DOMError() {} /** * @type {DOMLocator} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-location */ DOMError.prototype.location; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-message */ DOMError.prototype.message; /** * @type {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-relatedData */ DOMError.prototype.relatedData; /** * @type {Object} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-relatedException */ DOMError.prototype.relatedException; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-warning */ DOMError.SEVERITY_WARNING = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-error */ DOMError.SEVERITY_ERROR = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity-fatal-error */ DOMError.SEVERITY_FATAL_ERROR = 3; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-severity */ DOMError.prototype.severity; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-DOMError-type */ DOMError.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/dom/#domerror */ DOMError.prototype.name; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ERROR-Interfaces-DOMErrorHandler */ function DOMErrorHandler() {} /** * @param {DOMError} error * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ERRORS-DOMErrorHandler-handleError */ DOMErrorHandler.prototype.handleError = function(error) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Interfaces-DOMLocator */ function DOMLocator() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-byteOffset */ DOMLocator.prototype.byteOffset; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-column-number */ DOMLocator.prototype.columnNumber; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-line-number */ DOMLocator.prototype.lineNumber; /** * @type {Node} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-node */ DOMLocator.prototype.relatedNode; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-uri */ DOMLocator.prototype.uri; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMLocator-utf16Offset */ DOMLocator.prototype.utf16Offset; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration */ function DOMConfiguration() {} /** * @type {DOMStringList} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-parameterNames */ DOMConfiguration.prototype.parameterNames; /** * @param {string} name * @return {boolean} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-canSetParameter * @nosideeffects */ DOMConfiguration.prototype.canSetParameter = function(name) {}; /** * @param {string} name * @return {*} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-getParameter * @nosideeffects */ DOMConfiguration.prototype.getParameter = function(name) {}; /** * @param {string} name * @param {*} value * @return {*} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMConfiguration-property */ DOMConfiguration.prototype.setParameter = function(name, value) {}; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-internalSubset */ DocumentType.prototype.internalSubset; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-publicId */ DocumentType.prototype.publicId; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-Core-DocType-systemId */ DocumentType.prototype.systemId; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Entity3-inputEncoding */ Entity.prototype.inputEncoding; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Entity3-encoding */ Entity.prototype.xmlEncoding; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-3-Core/core.html#Entity3-version */ Entity.prototype.xmlVersion; closure-compiler-20130227+dfsg1/externs/gears_symbols.js0000644000175000017500000000177512115204405021263 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Global symbols injected by Google Gears. * @externs * @author nicksantos@google.com (Nick Santos) */ /** * Suppresses the compiler warning when multiple externs files declare the * google namespace. * @suppress {duplicate} * @noalias */ var google = {}; google.gears = {}; /** @type {GearsFactory} */ google.gears.factory; /** @type {GearsWorkerPool} */ google.gears.workerPool; closure-compiler-20130227+dfsg1/externs/webkit_notifications.js0000644000175000017500000001026112115204405022616 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Definitions for W3C's Notifications specification. * @externs */ /** * @param {string} title * @param {Object=} opt_options * @constructor * @implements {EventTarget} * @see http://notifications.spec.whatwg.org/#notification */ function Notification(title, opt_options) {} /** * @type {string} */ Notification.permission; /** * @param {NotificationPermissionCallback=} opt_callback */ Notification.requestPermission = function(opt_callback) {}; /** @override */ Notification.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ Notification.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ Notification.prototype.dispatchEvent = function(evt) {}; /** * The ID used by clients to uniquely identify notifications to eliminate * duplicate notifications. * @type {string} * @deprecated Use NotificationOptions.tag instead. */ Notification.prototype.replaceId; /** * The string used by clients to specify the directionality (rtl/ltr) of the * notification. * @type {string} * @deprecated Use NotificationOptions.titleDir and bodyDir instead. */ Notification.prototype.dir; /** * Displays the notification. */ Notification.prototype.show = function() {}; /** * Prevents the notification from being displayed, or closes it if it is already * displayed. */ Notification.prototype.cancel = function() {}; /** * Prevents the notification from being displayed, or closes it if it is already * displayed. */ Notification.prototype.close = function() {}; /** * An event handler called when notification is closed. * @type {?function(Event)} */ Notification.prototype.onclose; /** * An event handler called if the notification could not be displayed due to * an error (i.e. resource could not be loaded). * @type {?function(Event)} */ Notification.prototype.onerror; /** * An event handler called when the notification has become visible. * @type {?function(Event)} * @deprecated Use onshow instead. */ Notification.prototype.ondisplay; /** * An event handler called when the notification has become visible. * @type {?function(Event)} */ Notification.prototype.onshow; /** * An event handler called when the notification has been clicked on. * @type {?function(Event)} */ Notification.prototype.onclick; /** * @typedef {function(string)} * @see http://notifications.spec.whatwg.org/#notificationpermissioncallback */ var NotificationPermissionCallback; /** * @constructor * @see http://dev.w3.org/2006/webapi/WebNotifications/publish/#dialog-if * @deprecated Use Notification instead. */ function NotificationCenter() {} /** * Creates a text+icon notification and displays it to the user. * @param {string} iconUrl * @param {string} title * @param {string} body * @return {Notification} */ NotificationCenter.prototype.createNotification = function(iconUrl, title, body) {}; /** * Creates an HTML notification and displays it to the user. * @param {string} url * @return {Notification} */ NotificationCenter.prototype.createHTMLNotification = function(url) {}; /** * Checks if the user has permission to display notifications. * @return {number} */ NotificationCenter.prototype.checkPermission = function() {}; /** * Requests permission from the user to display notifications. * @param {Function=} opt_callback * @return {void} */ NotificationCenter.prototype.requestPermission = function(opt_callback) {}; /** * WebKit browsers expose the NotificationCenter API through * window.webkitNotifications. * @type {NotificationCenter} */ Window.prototype.webkitNotifications; closure-compiler-20130227+dfsg1/externs/gecko_dom.js0000644000175000017500000006377212115204405020346 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over * W3C's DOM specification by Gecko. This file depends on * w3c_dom2.js. * * When a non-standard extension appears in both Gecko and IE, we put * it in gecko_dom.js * * @externs */ // TODO: Almost all of it has not been annotated with types. // Gecko DOM; /** * Mozilla only??? * @constructor * @extends {HTMLElement} */ function HTMLSpanElement() {} /** * @see https://developer.mozilla.org/en/Components_object */ Window.prototype.Components; /** * @type Window * @see https://developer.mozilla.org/en/DOM/window.content */ Window.prototype.content; /** * @type {boolean} * @see https://developer.mozilla.org/en/DOM/window.closed */ Window.prototype.closed; /** @see https://developer.mozilla.org/en/DOM/window.controllers */ Window.prototype.controllers; /** @see https://developer.mozilla.org/en/DOM/window.crypto */ Window.prototype.crypto; /** * Gets/sets the status bar text for the given window. * @type {string} * @see https://developer.mozilla.org/en/DOM/window.defaultStatus */ Window.prototype.defaultStatus; /** @see https://developer.mozilla.org/en/DOM/window.dialogArguments */ Window.prototype.dialogArguments; /** @see https://developer.mozilla.org/en/DOM/window.directories */ Window.prototype.directories; /** * @type {!Document} * @see https://developer.mozilla.org/en/DOM/window.document */ Window.prototype.document; Window.prototype.eval; /** * @type {HTMLObjectElement|HTMLIFrameElement|null} * @see https://developer.mozilla.org/en/DOM/window.frameElement */ Window.prototype.frameElement; /** * Allows lookup of frames by index or by name. * @type {?Object} * @see https://developer.mozilla.org/en/DOM/window.frames */ Window.prototype.frames; /** * @type {boolean} * @see https://developer.mozilla.org/en/DOM/window.fullScreen */ Window.prototype.fullScreen; /** * @see https://developer.mozilla.org/en/DOM/Storage#globalStorage */ Window.prototype.globalStorage; /** * @type {!History} * @see https://developer.mozilla.org/en/DOM/window.history */ Window.prototype.history; /** * Returns the number of frames (either frame or iframe elements) in the * window. * * @type {number} * @see https://developer.mozilla.org/en/DOM/window.length */ Window.prototype.length; /** * @type {!Location} * @implicitCast * @see https://developer.mozilla.org/en/DOM/window.location */ Window.prototype.location; /** * @see https://developer.mozilla.org/en/DOM/window.locationbar */ Window.prototype.locationbar; /** * @see https://developer.mozilla.org/en/DOM/window.menubar */ Window.prototype.menubar; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/window.name */ Window.prototype.name; /** * @type {Navigator} * @see https://developer.mozilla.org/en/DOM/window.navigator */ Window.prototype.navigator; /** * @type {?Window} * @see https://developer.mozilla.org/en/DOM/window.opener */ Window.prototype.opener; /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window.parent */ Window.prototype.parent; /** @see https://developer.mozilla.org/en/DOM/window.personalbar */ Window.prototype.personalbar; /** @see https://developer.mozilla.org/en/DOM/window.pkcs11 */ Window.prototype.pkcs11; /** @see https://developer.mozilla.org/en/DOM/window */ Window.prototype.returnValue; /** @see https://developer.mozilla.org/en/DOM/window.scrollbars */ Window.prototype.scrollbars; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.scrollMaxX */ Window.prototype.scrollMaxX; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.scrollMaxY */ Window.prototype.scrollMaxY; /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window.self */ Window.prototype.self; /** @see https://developer.mozilla.org/en/DOM/Storage#sessionStorage */ Window.prototype.sessionStorage; /** @see https://developer.mozilla.org/en/DOM/window.sidebar */ Window.prototype.sidebar; /** * @type {?string} * @see https://developer.mozilla.org/en/DOM/window.status */ Window.prototype.status; /** @see https://developer.mozilla.org/en/DOM/window.statusbar */ Window.prototype.statusbar; /** @see https://developer.mozilla.org/en/DOM/window.toolbar */ Window.prototype.toolbar; /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window.self */ Window.prototype.top; /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window.self */ Window.prototype.window; /** * @param {*} message * @see https://developer.mozilla.org/en/DOM/window.alert */ Window.prototype.alert = function(message) {}; /** * Decodes a string of data which has been encoded using base-64 encoding. * * @param {string} encodedData * @see https://developer.mozilla.org/en/DOM/window.atob * @nosideeffects */ Window.prototype.atob = function(encodedData) {}; /** @see https://developer.mozilla.org/en/DOM/window.back */ Window.prototype.back = function() {}; /** @see https://developer.mozilla.org/en/DOM/window.blur */ Window.prototype.blur = function() {}; /** * @param {string} stringToEncode * @return {string} * @see https://developer.mozilla.org/en/DOM/window.btoa * @nosideeffects */ Window.prototype.btoa = function(stringToEncode) {}; /** @deprecated */ Window.prototype.captureEvents; /** * @param {number|undefined?} intervalID * @see https://developer.mozilla.org/en/DOM/window.clearInterval */ Window.prototype.clearInterval = function(intervalID) {}; /** * @param {number|undefined?} timeoutID * @see https://developer.mozilla.org/en/DOM/window.clearTimeout */ Window.prototype.clearTimeout = function(timeoutID) {}; /** @see https://developer.mozilla.org/en/DOM/window.close */ Window.prototype.close = function() {}; /** * @param {*} message * @return {boolean} */ Window.prototype.confirm = function(message) {}; /** * @param {string} regular * @return {string} * @see https://developer.mozilla.org/en/DOM/window.escape * @nosideeffects */ Window.prototype.escape = function(regular) {}; /** @see https://developer.mozilla.org/en/DOM/window.find */ Window.prototype.find; /** @see https://developer.mozilla.org/en/DOM/window.focus */ Window.prototype.focus = function() {}; /** @see https://developer.mozilla.org/en/DOM/window.forward */ Window.prototype.forward = function() {}; /** @see https://developer.mozilla.org/en/DOM/window.getAttention */ Window.prototype.getAttention = function() {}; /** * @param {Element} element * @param {?string} pseudoElt * @return {CSSStyleDeclaration} * @nosideeffects */ Window.prototype.getComputedStyle = function(element, pseudoElt) {}; /** * @return {Selection} * @see https://developer.mozilla.org/en/DOM/window.getSelection * @nosideeffects */ Window.prototype.getSelection = function() {}; /** @see https://developer.mozilla.org/en/DOM/window.home */ Window.prototype.home = function() {}; Window.prototype.openDialog; Window.prototype.releaseEvents; Window.prototype.scrollByLines; Window.prototype.scrollByPages; /** * @param {Function|string} callback * @param {number} delay * @param {...*} var_args * @return {number} */ Window.prototype.setInterval; /** * @param {Function|string} callback * @param {number} delay * @param {...*} var_args * @return {number} */ Window.prototype.setTimeout = function(callback, delay, var_args) {}; /** * @param {string} uri * @param {?=} opt_arguments * @param {string=} opt_options * @see https://developer.mozilla.org/en/DOM/window.showModalDialog */ Window.prototype.showModalDialog; Window.prototype.sizeToContent; /** * @see http://msdn.microsoft.com/en-us/library/ms536769(VS.85).aspx */ Window.prototype.stop = function() {}; /** * @param {string} escaped * @return {string} * @see https://developer.mozilla.org/en/DOM/window.unescape * @nosideeffects */ Window.prototype.unescape = function(escaped) {}; Window.prototype.updateCommands; // properties of Document /** * @see https://developer.mozilla.org/en/DOM/document.alinkColor * @type {string} */ Document.prototype.alinkColor; /** * @see https://developer.mozilla.org/en/DOM/document.anchors * @type {HTMLCollection} */ Document.prototype.anchors; /** * @see https://developer.mozilla.org/en/DOM/document.applets * @type {HTMLCollection} */ Document.prototype.applets; /** @type {boolean} */ Document.prototype.async; /** @type {string?} */ Document.prototype.baseURI; Document.prototype.baseURIObject; /** * @see https://developer.mozilla.org/en/DOM/document.bgColor * @type {string} */ Document.prototype.bgColor; /** @type {HTMLBodyElement} */ Document.prototype.body; Document.prototype.characterSet; /** * @see https://developer.mozilla.org/en/DOM/document.compatMode * @type {string} */ Document.prototype.compatMode; Document.prototype.contentType; /** @type {string} */ Document.prototype.cookie; Document.prototype.defaultView; /** * @see https://developer.mozilla.org/en/DOM/document.designMode * @type {string} */ Document.prototype.designMode; Document.prototype.documentURIObject; /** * @see https://developer.mozilla.org/en/DOM/document.domain * @type {string} */ Document.prototype.domain; /** * @see https://developer.mozilla.org/en/DOM/document.embeds * @type {HTMLCollection} */ Document.prototype.embeds; /** * @see https://developer.mozilla.org/en/DOM/document.fgColor * @type {string} */ Document.prototype.fgColor; /** @type {Element} */ Document.prototype.firstChild; /** * @see https://developer.mozilla.org/en/DOM/document.forms * @type {HTMLCollection} */ Document.prototype.forms; /** @type {number} */ Document.prototype.height; /** @type {Array} */ Document.prototype.images; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/document.lastModified */ Document.prototype.lastModified; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/document.linkColor */ Document.prototype.linkColor; /** * @see https://developer.mozilla.org/en/DOM/document.links * @type {HTMLCollection} */ Document.prototype.links; /** * @type {!Location} * @implicitCast */ Document.prototype.location; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/Using_the_Page_Visibility_API */ Document.prototype.mozVisibilityState; Document.prototype.namespaceURI; Document.prototype.nodePrincipal; Document.prototype.plugins; Document.prototype.popupNode; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/document.referrer */ Document.prototype.referrer; /** * @see https://developer.mozilla.org/en/DOM/document.styleSheets */ Document.prototype.styleSheets; /** @type {?string} */ Document.prototype.title; Document.prototype.tooltipNode; /** @type {string} */ Document.prototype.URL; /** * @type {string} * @see https://developer.mozilla.org/en/DOM/document.vlinkColor */ Document.prototype.vlinkColor; /** @type {number} */ Document.prototype.width; // Methods of Document /** * @see https://developer.mozilla.org/en/DOM/document.clear */ Document.prototype.clear = function() {}; /** * @see https://developer.mozilla.org/en/DOM/document.close */ Document.prototype.close; /** * @see https://developer.mozilla.org/en/DOM/document.createElementNS * @param {string} namespaceURI * @param {string} qualifiedName * @return {!Element} */ Document.prototype.createElementNS = function(namespaceURI, qualifiedName) {}; /** * @param {string} type * @return {Event} */ Document.prototype.createEvent = function(type) {}; Document.prototype.createNSResolver; /** @return {Range} */ Document.prototype.createRange = function() {}; Document.prototype.createTreeWalker; Document.prototype.evaluate; /** * @param {string} commandName * @param {?boolean=} opt_showUi * @param {*=} opt_value * @see https://developer.mozilla.org/en/Rich-Text_Editing_in_Mozilla#Executing_Commands */ Document.prototype.execCommand; /** * @param {string} s id. * @return {HTMLElement} * @nosideeffects * @see https://developer.mozilla.org/en/DOM/document.getElementById */ Document.prototype.getElementById = function(s) {}; /** * @param {string} name * @return {!NodeList} * @nosideeffects * @see https://developer.mozilla.org/en/DOM/document.getElementsByClassName */ Document.prototype.getElementsByClassName = function(name) {}; /** * @param {string} name * @return {!NodeList} * @nosideeffects * @see https://developer.mozilla.org/en/DOM/document.getElementsByName */ Document.prototype.getElementsByName = function(name) {}; /** * @param {string} namespace * @param {string} name * @return {!NodeList} * @nosideeffects * @see https://developer.mozilla.org/en/DOM/document.getElementsByTagNameNS */ Document.prototype.getElementsByTagNameNS = function(namespace, name) {}; /** * @param {Node} externalNode * @param {boolean} deep * @return {Node} */ Document.prototype.importNode = function(externalNode, deep) {}; /** @param {string} uri */ Document.prototype.load = function(uri) {}; Document.prototype.loadOverlay; /** * @see https://developer.mozilla.org/en/DOM/document.open */ Document.prototype.open; /** * @see https://developer.mozilla.org/en/Midas * @see http://msdn.microsoft.com/en-us/library/ms536676(VS.85).aspx */ Document.prototype.queryCommandEnabled; /** * @see https://developer.mozilla.org/en/Midas * @see http://msdn.microsoft.com/en-us/library/ms536678(VS.85).aspx */ Document.prototype.queryCommandIndeterm; /** * @see https://developer.mozilla.org/en/Midas * @see http://msdn.microsoft.com/en-us/library/ms536679(VS.85).aspx */ Document.prototype.queryCommandState; /** * @see https://developer.mozilla.org/en/DOM/document.queryCommandSupported * @see http://msdn.microsoft.com/en-us/library/ms536681(VS.85).aspx * @param {string} command * @return {?} Implementation-specific. */ Document.prototype.queryCommandSupported; /** * @see https://developer.mozilla.org/en/Midas * @see http://msdn.microsoft.com/en-us/library/ms536683(VS.85).aspx */ Document.prototype.queryCommandValue; /** * @see https://developer.mozilla.org/en/DOM/document.write * @param {string} text */ Document.prototype.write = function(text) {}; /** * @see https://developer.mozilla.org/en/DOM/document.writeln * @param {string} text */ Document.prototype.writeln = function(text) {}; Document.prototype.ononline; Document.prototype.onoffline; // XUL /** * @see http://developer.mozilla.org/en/DOM/document.getBoxObjectFor * @return {BoxObject} * @nosideeffects */ Document.prototype.getBoxObjectFor = function(element) {}; // From: // http://lxr.mozilla.org/mozilla1.8/source/dom/public/idl/range/nsIDOMNSRange.idl /** * @param {string} tag * @return {DocumentFragment} */ Range.prototype.createContextualFragment; /** * @param {Node} parent * @param {number} offset * @return {boolean} * @nosideeffects */ Range.prototype.isPointInRange; /** * @param {Node} parent * @param {number} offset * @return {number} * @nosideeffects */ Range.prototype.comparePoint; /** * @param {Node} n * @return {boolean} * @nosideeffects */ Range.prototype.intersectsNode; /** * @param {Node} n * @return {number} * @nosideeffects */ Range.prototype.compareNode; /** @constructor */ function Selection() {} /** * @type {Node} * @see https://developer.mozilla.org/en/DOM/Selection/anchorNode */ Selection.prototype.anchorNode; /** * @type {number} * @see https://developer.mozilla.org/en/DOM/Selection/anchorOffset */ Selection.prototype.anchorOffset; /** * @type {Node} * @see https://developer.mozilla.org/en/DOM/Selection/focusNode */ Selection.prototype.focusNode; /** * @type {number} * @see https://developer.mozilla.org/en/DOM/Selection/focusOffset */ Selection.prototype.focusOffset; /** * @type {boolean} * @see https://developer.mozilla.org/en/DOM/Selection/isCollapsed */ Selection.prototype.isCollapsed; /** * @type {number} * @see https://developer.mozilla.org/en/DOM/Selection/rangeCount */ Selection.prototype.rangeCount; /** * @param {Range} range * @return {undefined} * @see https://developer.mozilla.org/en/DOM/Selection/addRange */ Selection.prototype.addRange = function(range) {}; /** * @param {number} index * @return {Range} * @see https://developer.mozilla.org/en/DOM/Selection/getRangeAt * @nosideeffects */ Selection.prototype.getRangeAt = function(index) {}; /** * @param {Node} node * @param {number} index * @return {undefined} * @see https://developer.mozilla.org/en/DOM/Selection/collapse */ Selection.prototype.collapse = function(node, index) {}; /** * @return {undefined} * @see https://developer.mozilla.org/en/DOM/Selection/collapseToEnd */ Selection.prototype.collapseToEnd = function() {}; /** * @return {undefined} * @see https://developer.mozilla.org/en/DOM/Selection/collapseToStart */ Selection.prototype.collapseToStart = function() {}; /** * @param {Node} node * @param {boolean} partlyContained * @return {boolean} * @see https://developer.mozilla.org/en/DOM/Selection/containsNode * @nosideeffects */ Selection.prototype.containsNode = function(node, partlyContained) {}; /** * @see https://developer.mozilla.org/en/DOM/Selection/deleteFromDocument */ Selection.prototype.deleteFromDocument = function() {}; /** * @param {Node} parentNode * @param {number} offset * @see https://developer.mozilla.org/en/DOM/Selection/extend */ Selection.prototype.extend = function(parentNode, offset) {}; /** * @see https://developer.mozilla.org/en/DOM/Selection/removeAllRanges */ Selection.prototype.removeAllRanges = function() {}; /** * @param {Range} range * @see https://developer.mozilla.org/en/DOM/Selection/removeRange */ Selection.prototype.removeRange = function(range) {}; /** * @param {Node} parentNode * @see https://developer.mozilla.org/en/DOM/Selection/selectAllChildren */ Selection.prototype.selectAllChildren; /** * @see https://developer.mozilla.org/en/DOM/Selection/selectionLanguageChange */ Selection.prototype.selectionLanguageChange; /** @type {NamedNodeMap} */ Element.prototype.attributes; Element.prototype.baseURIObject; /** @type {!NodeList} */ Element.prototype.childNodes; /** * @type {!NodeList} * @see https://developer.mozilla.org/en/DOM/element.children */ Element.prototype.children; /** * @type {string} * @implicitCast */ Element.prototype.className; /** @type {string} */ Element.prototype.dir; /** * Firebug sets this property on elements it is inserting into the DOM. * @type {boolean} */ Element.prototype.firebugIgnore; /** @type {Node} */ Element.prototype.firstChild; /** * @type {string} * @implicitCast */ Element.prototype.id; /** * @type {string} * @implicitCast */ Element.prototype.innerHTML; /** @type {string} */ Element.prototype.lang; /** @type {Node} */ Element.prototype.lastChild; Element.prototype.localName; Element.prototype.name; Element.prototype.namespaceURI; /** @type {Node} */ Element.prototype.nextSibling; Element.prototype.nodeName; Element.prototype.nodePrincipal; /** @type {number} */ Element.prototype.nodeType; Element.prototype.nodeValue; /** @type {Document} */ Element.prototype.ownerDocument; /** @type {Node} */ Element.prototype.parentNode; Element.prototype.prefix; /** @type {Node} */ Element.prototype.previousSibling; /** @type {CSSStyleDeclaration} */ Element.prototype.style; /** * @type {number} * @implicitCast */ Element.prototype.tabIndex; /** * @type {string} * @implicitCast */ Element.prototype.textContent; /** @type {string} */ Element.prototype.title; /** * @param {Node} child * @return {Node} appendedElement. * @override */ Element.prototype.appendChild = function(child) {}; /** * @override * @return {Element} */ Element.prototype.cloneNode = function(deep) {}; /** @override */ Element.prototype.dispatchEvent = function(event) {}; /** @return {undefined} */ Element.prototype.blur = function() {}; /** @return {undefined} */ Element.prototype.click = function() {}; /** @return {undefined} */ Element.prototype.focus = function() {}; /** * @return {boolean} * @override * @nosideeffects */ Element.prototype.hasAttributes = function() {}; /** * @return {boolean} * @override * @nosideeffects */ Element.prototype.hasChildNodes = function() {}; /** @override */ Element.prototype.insertBefore = function(insertedNode, adjacentNode) {}; /** * @return {undefined} * @override */ Element.prototype.normalize = function() {}; /** * @param {Node} removedNode * @return {Node} * @override */ Element.prototype.removeChild = function(removedNode) {}; /** @override */ Element.prototype.removeEventListener = function(type, handler, useCapture) {}; /** @override */ Element.prototype.replaceChild = function(insertedNode, replacedNode) {}; /** @type {number} */ HTMLInputElement.prototype.selectionStart; /** @type {number} */ HTMLInputElement.prototype.selectionEnd; /** * @param {number} selectionStart * @param {number} selectionEnd * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#dom-textarea/input-setselectionrange */ HTMLInputElement.prototype.setSelectionRange = function(selectionStart, selectionEnd) {}; /** @type {number} */ HTMLTextAreaElement.prototype.selectionStart; /** @type {number} */ HTMLTextAreaElement.prototype.selectionEnd; /** * @param {number} selectionStart * @param {number} selectionEnd * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#dom-textarea/input-setselectionrange */ HTMLTextAreaElement.prototype.setSelectionRange = function(selectionStart, selectionEnd) {}; /** @constructor */ function Navigator() {} /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.appCodeName */ Navigator.prototype.appCodeName; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.appVersion */ Navigator.prototype.appName; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.appVersion */ Navigator.prototype.appVersion; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.buildID */ Navigator.prototype.buildID; /** * @type {boolean} * @see https://developer.mozilla.org/en/Navigator.cookieEnabled */ Navigator.prototype.cookieEnabled; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.language */ Navigator.prototype.language; /** * @type {MimeTypeArray} * @see https://developer.mozilla.org/en/Navigator.mimeTypes */ Navigator.prototype.mimeTypes; /** * @type {boolean} * @see https://developer.mozilla.org/en/Navigator.onLine */ Navigator.prototype.onLine; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.oscpu */ Navigator.prototype.oscpu; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.platform */ Navigator.prototype.platform; /** * @type {PluginArray} * @see https://developer.mozilla.org/en/Navigator.plugins */ Navigator.prototype.plugins; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.product */ Navigator.prototype.product; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.productSub */ Navigator.prototype.productSub; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.securityPolicy */ Navigator.prototype.securityPolicy; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.userAgent */ Navigator.prototype.userAgent; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.vendor */ Navigator.prototype.vendor; /** * @type {string} * @see https://developer.mozilla.org/en/Navigator.vendorSub */ Navigator.prototype.vendorSub; /** * @type {function(): boolean} * @see https://developer.mozilla.org/en/Navigator.javaEnabled * @nosideeffects */ Navigator.prototype.javaEnabled = function() {}; /** * @constructor * @see https://developer.mozilla.org/en/DOM/PluginArray */ function PluginArray() {} /** @type {number} */ PluginArray.prototype.length; /** * @param {number} index * @return {Plugin} */ PluginArray.prototype.item = function(index) {}; /** * @param {string} name * @return {Plugin} */ PluginArray.prototype.namedItem = function(name) {}; /** @param {boolean=} reloadDocuments */ PluginArray.prototype.refresh = function(reloadDocuments) {}; /** @constructor */ function MimeTypeArray() {} /** * @param {number} index * @return {MimeType} */ MimeTypeArray.prototype.item = function(index) {}; /** * @type {number} * @see https://developer.mozilla.org/en/DOM/window.navigator.mimeTypes */ MimeTypeArray.prototype.length; /** * @param {string} name * @return {MimeType} */ MimeTypeArray.prototype.namedItem = function(name) {}; /** @constructor */ function MimeType() {} /** @type {string} */ MimeType.prototype.description; /** @type {Plugin} */ MimeType.prototype.enabledPlugin; /** @type {string} */ MimeType.prototype.suffixes; /** @type {string} */ MimeType.prototype.type; /** @constructor */ function Plugin() {} /** @type {string} */ Plugin.prototype.description; /** @type {string} */ Plugin.prototype.filename; /** @type {number} */ Plugin.prototype.length; /** @type {string} */ Plugin.prototype.name; /** @constructor */ function BoxObject() {} /** @type {Element} */ BoxObject.prototype.element; /** @type {number} */ BoxObject.prototype.screenX; /** @type {number} */ BoxObject.prototype.screenY; /** @type {number} */ BoxObject.prototype.x; /** @type {number} */ BoxObject.prototype.y; /** @type {number} */ BoxObject.prototype.width; /** * @type {number} * @see http://www.google.com/codesearch/p?hl=en#eksvcKKj5Ng/mozilla/dom/public/idl/html/nsIDOMNSHTMLImageElement.idl&q=naturalWidth */ HTMLImageElement.prototype.naturalWidth; /** * @type {number} * @see http://www.google.com/codesearch/p?hl=en#eksvcKKj5Ng/mozilla/dom/public/idl/html/nsIDOMNSHTMLImageElement.idl&q=naturalHeight */ HTMLImageElement.prototype.naturalHeight; closure-compiler-20130227+dfsg1/externs/w3c_range.js0000644000175000017500000001573212115204405020260 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's range specification. * This file depends on w3c_dom2.js. * The whole file has been fully type annotated. * Created from * http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html * * @externs */ /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Interface */ function Range() {} /** * @type {Node} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-startParent */ Range.prototype.startContainer; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-startOffset */ Range.prototype.startOffset; /** * @type {Node} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-endParent */ Range.prototype.endContainer; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-endOffset */ Range.prototype.endOffset; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-collapsed */ Range.prototype.collapsed; /** * @type {Node} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-attr-commonParent */ Range.prototype.commonAncestorContainer; /** * @param {Node} refNode * @param {number} offset * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setStart */ Range.prototype.setStart = function(refNode, offset) {}; /** * @param {Node} refNode * @param {number} offset * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEnd */ Range.prototype.setEnd = function(refNode, offset) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-setStartBefore */ Range.prototype.setStartBefore = function(refNode) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setStartAfter */ Range.prototype.setStartAfter = function(refNode) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEndBefore */ Range.prototype.setEndBefore = function(refNode) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-setEndAfter */ Range.prototype.setEndAfter = function(refNode) {}; /** * @param {boolean} toStart * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-collapse */ Range.prototype.collapse = function(toStart) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-selectNode */ Range.prototype.selectNode = function(refNode) {}; /** * @param {Node} refNode * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-selectNodeContents */ Range.prototype.selectNodeContents = function(refNode) {}; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow */ Range.prototype.START_TO_START = 0; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow */ Range.prototype.START_TO_END = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow */ Range.prototype.END_TO_END = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-compareHow */ Range.prototype.END_TO_START = 3; /** * @param {number} how * @param {Range} sourceRange * @return {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-compareBoundaryPoints */ Range.prototype.compareBoundaryPoints = function(how, sourceRange) {}; /** * @return {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-deleteContents */ Range.prototype.deleteContents = function() {}; /** * @return {DocumentFragment} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-extractContents */ Range.prototype.extractContents = function() {}; /** * @return {DocumentFragment} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-cloneContents */ Range.prototype.cloneContents = function() {}; /** * @param {Node} newNode * @return {DocumentFragment} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-insertNode */ Range.prototype.insertNode = function(newNode) {}; /** * @param {Node} newParent * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-surroundContents */ Range.prototype.surroundContents = function(newParent) {}; /** * @return {Range} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-clone */ Range.prototype.cloneRange = function() {}; /** * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-Range-method-detach */ Range.prototype.detach = function() {}; // Introduced in DOM Level 2: /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-DocumentRange-idl */ function DocumentRange() {} /** * @return {Range} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level2-DocumentRange-method-createRange */ DocumentRange.prototype.createRange = function() {}; // Introduced in DOM Level 2: /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeException */ function RangeException() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode */ RangeException.prototype.code; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode */ RangeException.prototype.BAD_BOUNDARYPOINTS_ERR = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#RangeExceptionCode */ RangeException.prototype.INVALID_NODE_TYPE_ERR = 2; closure-compiler-20130227+dfsg1/externs/webgl.js0000644000175000017500000020663412115204405017513 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Definitions for WebGL functions as described at * http://www.khronos.org/registry/webgl/specs/latest/ * * This file is current up to the WebGL 1.0.1 spec, including extensions. * * This relies on html5.js being included for Canvas and Typed Array support. * * @externs */ /** * @constructor * @noalias */ function WebGLRenderingContext() {} /** @type {number} */ WebGLRenderingContext.DEPTH_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.STENCIL_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.COLOR_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.POINTS; /** @type {number} */ WebGLRenderingContext.LINES; /** @type {number} */ WebGLRenderingContext.LINE_LOOP; /** @type {number} */ WebGLRenderingContext.LINE_STRIP; /** @type {number} */ WebGLRenderingContext.TRIANGLES; /** @type {number} */ WebGLRenderingContext.TRIANGLE_STRIP; /** @type {number} */ WebGLRenderingContext.TRIANGLE_FAN; /** @type {number} */ WebGLRenderingContext.ZERO; /** @type {number} */ WebGLRenderingContext.ONE; /** @type {number} */ WebGLRenderingContext.SRC_COLOR; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_SRC_COLOR; /** @type {number} */ WebGLRenderingContext.SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.DST_ALPHA; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_DST_ALPHA; /** @type {number} */ WebGLRenderingContext.DST_COLOR; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_DST_COLOR; /** @type {number} */ WebGLRenderingContext.SRC_ALPHA_SATURATE; /** @type {number} */ WebGLRenderingContext.FUNC_ADD; /** @type {number} */ WebGLRenderingContext.BLEND_EQUATION; /** @type {number} */ WebGLRenderingContext.BLEND_EQUATION_RGB; /** @type {number} */ WebGLRenderingContext.BLEND_EQUATION_ALPHA; /** @type {number} */ WebGLRenderingContext.FUNC_SUBTRACT; /** @type {number} */ WebGLRenderingContext.FUNC_REVERSE_SUBTRACT; /** @type {number} */ WebGLRenderingContext.BLEND_DST_RGB; /** @type {number} */ WebGLRenderingContext.BLEND_SRC_RGB; /** @type {number} */ WebGLRenderingContext.BLEND_DST_ALPHA; /** @type {number} */ WebGLRenderingContext.BLEND_SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.CONSTANT_COLOR; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_CONSTANT_COLOR; /** @type {number} */ WebGLRenderingContext.CONSTANT_ALPHA; /** @type {number} */ WebGLRenderingContext.ONE_MINUS_CONSTANT_ALPHA; /** @type {number} */ WebGLRenderingContext.BLEND_COLOR; /** @type {number} */ WebGLRenderingContext.ARRAY_BUFFER; /** @type {number} */ WebGLRenderingContext.ELEMENT_ARRAY_BUFFER; /** @type {number} */ WebGLRenderingContext.ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.ELEMENT_ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.STREAM_DRAW; /** @type {number} */ WebGLRenderingContext.STATIC_DRAW; /** @type {number} */ WebGLRenderingContext.DYNAMIC_DRAW; /** @type {number} */ WebGLRenderingContext.BUFFER_SIZE; /** @type {number} */ WebGLRenderingContext.BUFFER_USAGE; /** @type {number} */ WebGLRenderingContext.CURRENT_VERTEX_ATTRIB; /** @type {number} */ WebGLRenderingContext.FRONT; /** @type {number} */ WebGLRenderingContext.BACK; /** @type {number} */ WebGLRenderingContext.FRONT_AND_BACK; /** @type {number} */ WebGLRenderingContext.CULL_FACE; /** @type {number} */ WebGLRenderingContext.BLEND; /** @type {number} */ WebGLRenderingContext.DITHER; /** @type {number} */ WebGLRenderingContext.STENCIL_TEST; /** @type {number} */ WebGLRenderingContext.DEPTH_TEST; /** @type {number} */ WebGLRenderingContext.SCISSOR_TEST; /** @type {number} */ WebGLRenderingContext.POLYGON_OFFSET_FILL; /** @type {number} */ WebGLRenderingContext.SAMPLE_ALPHA_TO_COVERAGE; /** @type {number} */ WebGLRenderingContext.SAMPLE_COVERAGE; /** @type {number} */ WebGLRenderingContext.NO_ERROR; /** @type {number} */ WebGLRenderingContext.INVALID_ENUM; /** @type {number} */ WebGLRenderingContext.INVALID_VALUE; /** @type {number} */ WebGLRenderingContext.INVALID_OPERATION; /** @type {number} */ WebGLRenderingContext.OUT_OF_MEMORY; /** @type {number} */ WebGLRenderingContext.CW; /** @type {number} */ WebGLRenderingContext.CCW; /** @type {number} */ WebGLRenderingContext.LINE_WIDTH; /** @type {number} */ WebGLRenderingContext.ALIASED_POINT_SIZE_RANGE; /** @type {number} */ WebGLRenderingContext.ALIASED_LINE_WIDTH_RANGE; /** @type {number} */ WebGLRenderingContext.CULL_FACE_MODE; /** @type {number} */ WebGLRenderingContext.FRONT_FACE; /** @type {number} */ WebGLRenderingContext.DEPTH_RANGE; /** @type {number} */ WebGLRenderingContext.DEPTH_WRITEMASK; /** @type {number} */ WebGLRenderingContext.DEPTH_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.DEPTH_FUNC; /** @type {number} */ WebGLRenderingContext.STENCIL_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.STENCIL_FUNC; /** @type {number} */ WebGLRenderingContext.STENCIL_FAIL; /** @type {number} */ WebGLRenderingContext.STENCIL_PASS_DEPTH_FAIL; /** @type {number} */ WebGLRenderingContext.STENCIL_PASS_DEPTH_PASS; /** @type {number} */ WebGLRenderingContext.STENCIL_REF; /** @type {number} */ WebGLRenderingContext.STENCIL_VALUE_MASK; /** @type {number} */ WebGLRenderingContext.STENCIL_WRITEMASK; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_FUNC; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_FAIL; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_PASS_DEPTH_FAIL; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_PASS_DEPTH_PASS; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_REF; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_VALUE_MASK; /** @type {number} */ WebGLRenderingContext.STENCIL_BACK_WRITEMASK; /** @type {number} */ WebGLRenderingContext.VIEWPORT; /** @type {number} */ WebGLRenderingContext.SCISSOR_BOX; /** @type {number} */ WebGLRenderingContext.COLOR_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.COLOR_WRITEMASK; /** @type {number} */ WebGLRenderingContext.UNPACK_ALIGNMENT; /** @type {number} */ WebGLRenderingContext.PACK_ALIGNMENT; /** @type {number} */ WebGLRenderingContext.MAX_TEXTURE_SIZE; /** @type {number} */ WebGLRenderingContext.MAX_VIEWPORT_DIMS; /** @type {number} */ WebGLRenderingContext.SUBPIXEL_BITS; /** @type {number} */ WebGLRenderingContext.RED_BITS; /** @type {number} */ WebGLRenderingContext.GREEN_BITS; /** @type {number} */ WebGLRenderingContext.BLUE_BITS; /** @type {number} */ WebGLRenderingContext.ALPHA_BITS; /** @type {number} */ WebGLRenderingContext.DEPTH_BITS; /** @type {number} */ WebGLRenderingContext.STENCIL_BITS; /** @type {number} */ WebGLRenderingContext.POLYGON_OFFSET_UNITS; /** @type {number} */ WebGLRenderingContext.POLYGON_OFFSET_FACTOR; /** @type {number} */ WebGLRenderingContext.TEXTURE_BINDING_2D; /** @type {number} */ WebGLRenderingContext.SAMPLE_BUFFERS; /** @type {number} */ WebGLRenderingContext.SAMPLES; /** @type {number} */ WebGLRenderingContext.SAMPLE_COVERAGE_VALUE; /** @type {number} */ WebGLRenderingContext.SAMPLE_COVERAGE_INVERT; /** @type {number} */ WebGLRenderingContext.COMPRESSED_TEXTURE_FORMATS; /** @type {number} */ WebGLRenderingContext.DONT_CARE; /** @type {number} */ WebGLRenderingContext.FASTEST; /** @type {number} */ WebGLRenderingContext.NICEST; /** @type {number} */ WebGLRenderingContext.GENERATE_MIPMAP_HINT; /** @type {number} */ WebGLRenderingContext.BYTE; /** @type {number} */ WebGLRenderingContext.UNSIGNED_BYTE; /** @type {number} */ WebGLRenderingContext.SHORT; /** @type {number} */ WebGLRenderingContext.UNSIGNED_SHORT; /** @type {number} */ WebGLRenderingContext.INT; /** @type {number} */ WebGLRenderingContext.UNSIGNED_INT; /** @type {number} */ WebGLRenderingContext.FLOAT; /** @type {number} */ WebGLRenderingContext.DEPTH_COMPONENT; /** @type {number} */ WebGLRenderingContext.ALPHA; /** @type {number} */ WebGLRenderingContext.RGB; /** @type {number} */ WebGLRenderingContext.RGBA; /** @type {number} */ WebGLRenderingContext.LUMINANCE; /** @type {number} */ WebGLRenderingContext.LUMINANCE_ALPHA; /** @type {number} */ WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4; /** @type {number} */ WebGLRenderingContext.UNSIGNED_SHORT_5_5_5_1; /** @type {number} */ WebGLRenderingContext.UNSIGNED_SHORT_5_6_5; /** @type {number} */ WebGLRenderingContext.FRAGMENT_SHADER; /** @type {number} */ WebGLRenderingContext.VERTEX_SHADER; /** @type {number} */ WebGLRenderingContext.MAX_VERTEX_ATTRIBS; /** @type {number} */ WebGLRenderingContext.MAX_VERTEX_UNIFORM_VECTORS; /** @type {number} */ WebGLRenderingContext.MAX_VARYING_VECTORS; /** @type {number} */ WebGLRenderingContext.MAX_COMBINED_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.MAX_VERTEX_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.MAX_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.MAX_FRAGMENT_UNIFORM_VECTORS; /** @type {number} */ WebGLRenderingContext.SHADER_TYPE; /** @type {number} */ WebGLRenderingContext.DELETE_STATUS; /** @type {number} */ WebGLRenderingContext.LINK_STATUS; /** @type {number} */ WebGLRenderingContext.VALIDATE_STATUS; /** @type {number} */ WebGLRenderingContext.ATTACHED_SHADERS; /** @type {number} */ WebGLRenderingContext.ACTIVE_UNIFORMS; /** @type {number} */ WebGLRenderingContext.ACTIVE_ATTRIBUTES; /** @type {number} */ WebGLRenderingContext.SHADING_LANGUAGE_VERSION; /** @type {number} */ WebGLRenderingContext.CURRENT_PROGRAM; /** @type {number} */ WebGLRenderingContext.NEVER; /** @type {number} */ WebGLRenderingContext.LESS; /** @type {number} */ WebGLRenderingContext.EQUAL; /** @type {number} */ WebGLRenderingContext.LEQUAL; /** @type {number} */ WebGLRenderingContext.GREATER; /** @type {number} */ WebGLRenderingContext.NOTEQUAL; /** @type {number} */ WebGLRenderingContext.GEQUAL; /** @type {number} */ WebGLRenderingContext.ALWAYS; /** @type {number} */ WebGLRenderingContext.KEEP; /** @type {number} */ WebGLRenderingContext.REPLACE; /** @type {number} */ WebGLRenderingContext.INCR; /** @type {number} */ WebGLRenderingContext.DECR; /** @type {number} */ WebGLRenderingContext.INVERT; /** @type {number} */ WebGLRenderingContext.INCR_WRAP; /** @type {number} */ WebGLRenderingContext.DECR_WRAP; /** @type {number} */ WebGLRenderingContext.VENDOR; /** @type {number} */ WebGLRenderingContext.RENDERER; /** @type {number} */ WebGLRenderingContext.VERSION; /** @type {number} */ WebGLRenderingContext.NEAREST; /** @type {number} */ WebGLRenderingContext.LINEAR; /** @type {number} */ WebGLRenderingContext.NEAREST_MIPMAP_NEAREST; /** @type {number} */ WebGLRenderingContext.LINEAR_MIPMAP_NEAREST; /** @type {number} */ WebGLRenderingContext.NEAREST_MIPMAP_LINEAR; /** @type {number} */ WebGLRenderingContext.LINEAR_MIPMAP_LINEAR; /** @type {number} */ WebGLRenderingContext.TEXTURE_MAG_FILTER; /** @type {number} */ WebGLRenderingContext.TEXTURE_MIN_FILTER; /** @type {number} */ WebGLRenderingContext.TEXTURE_WRAP_S; /** @type {number} */ WebGLRenderingContext.TEXTURE_WRAP_T; /** @type {number} */ WebGLRenderingContext.TEXTURE_2D; /** @type {number} */ WebGLRenderingContext.TEXTURE; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP; /** @type {number} */ WebGLRenderingContext.TEXTURE_BINDING_CUBE_MAP; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_X; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_X; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Y; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Y; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Z; /** @type {number} */ WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Z; /** @type {number} */ WebGLRenderingContext.MAX_CUBE_MAP_TEXTURE_SIZE; /** @type {number} */ WebGLRenderingContext.TEXTURE0; /** @type {number} */ WebGLRenderingContext.TEXTURE1; /** @type {number} */ WebGLRenderingContext.TEXTURE2; /** @type {number} */ WebGLRenderingContext.TEXTURE3; /** @type {number} */ WebGLRenderingContext.TEXTURE4; /** @type {number} */ WebGLRenderingContext.TEXTURE5; /** @type {number} */ WebGLRenderingContext.TEXTURE6; /** @type {number} */ WebGLRenderingContext.TEXTURE7; /** @type {number} */ WebGLRenderingContext.TEXTURE8; /** @type {number} */ WebGLRenderingContext.TEXTURE9; /** @type {number} */ WebGLRenderingContext.TEXTURE10; /** @type {number} */ WebGLRenderingContext.TEXTURE11; /** @type {number} */ WebGLRenderingContext.TEXTURE12; /** @type {number} */ WebGLRenderingContext.TEXTURE13; /** @type {number} */ WebGLRenderingContext.TEXTURE14; /** @type {number} */ WebGLRenderingContext.TEXTURE15; /** @type {number} */ WebGLRenderingContext.TEXTURE16; /** @type {number} */ WebGLRenderingContext.TEXTURE17; /** @type {number} */ WebGLRenderingContext.TEXTURE18; /** @type {number} */ WebGLRenderingContext.TEXTURE19; /** @type {number} */ WebGLRenderingContext.TEXTURE20; /** @type {number} */ WebGLRenderingContext.TEXTURE21; /** @type {number} */ WebGLRenderingContext.TEXTURE22; /** @type {number} */ WebGLRenderingContext.TEXTURE23; /** @type {number} */ WebGLRenderingContext.TEXTURE24; /** @type {number} */ WebGLRenderingContext.TEXTURE25; /** @type {number} */ WebGLRenderingContext.TEXTURE26; /** @type {number} */ WebGLRenderingContext.TEXTURE27; /** @type {number} */ WebGLRenderingContext.TEXTURE28; /** @type {number} */ WebGLRenderingContext.TEXTURE29; /** @type {number} */ WebGLRenderingContext.TEXTURE30; /** @type {number} */ WebGLRenderingContext.TEXTURE31; /** @type {number} */ WebGLRenderingContext.ACTIVE_TEXTURE; /** @type {number} */ WebGLRenderingContext.REPEAT; /** @type {number} */ WebGLRenderingContext.CLAMP_TO_EDGE; /** @type {number} */ WebGLRenderingContext.MIRRORED_REPEAT; /** @type {number} */ WebGLRenderingContext.FLOAT_VEC2; /** @type {number} */ WebGLRenderingContext.FLOAT_VEC3; /** @type {number} */ WebGLRenderingContext.FLOAT_VEC4; /** @type {number} */ WebGLRenderingContext.INT_VEC2; /** @type {number} */ WebGLRenderingContext.INT_VEC3; /** @type {number} */ WebGLRenderingContext.INT_VEC4; /** @type {number} */ WebGLRenderingContext.BOOL; /** @type {number} */ WebGLRenderingContext.BOOL_VEC2; /** @type {number} */ WebGLRenderingContext.BOOL_VEC3; /** @type {number} */ WebGLRenderingContext.BOOL_VEC4; /** @type {number} */ WebGLRenderingContext.FLOAT_MAT2; /** @type {number} */ WebGLRenderingContext.FLOAT_MAT3; /** @type {number} */ WebGLRenderingContext.FLOAT_MAT4; /** @type {number} */ WebGLRenderingContext.SAMPLER_2D; /** @type {number} */ WebGLRenderingContext.SAMPLER_CUBE; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_ENABLED; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_SIZE; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_STRIDE; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_TYPE; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_NORMALIZED; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_POINTER; /** @type {number} */ WebGLRenderingContext.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.COMPILE_STATUS; /** @type {number} */ WebGLRenderingContext.LOW_FLOAT; /** @type {number} */ WebGLRenderingContext.MEDIUM_FLOAT; /** @type {number} */ WebGLRenderingContext.HIGH_FLOAT; /** @type {number} */ WebGLRenderingContext.LOW_INT; /** @type {number} */ WebGLRenderingContext.MEDIUM_INT; /** @type {number} */ WebGLRenderingContext.HIGH_INT; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER; /** @type {number} */ WebGLRenderingContext.RGBA4; /** @type {number} */ WebGLRenderingContext.RGB5_A1; /** @type {number} */ WebGLRenderingContext.RGB565; /** @type {number} */ WebGLRenderingContext.DEPTH_COMPONENT16; /** @type {number} */ WebGLRenderingContext.STENCIL_INDEX; /** @type {number} */ WebGLRenderingContext.STENCIL_INDEX8; /** @type {number} */ WebGLRenderingContext.DEPTH_STENCIL; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_WIDTH; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_HEIGHT; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_INTERNAL_FORMAT; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_RED_SIZE; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_GREEN_SIZE; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_BLUE_SIZE; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_ALPHA_SIZE; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_DEPTH_SIZE; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_STENCIL_SIZE; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; /** @type {number} */ WebGLRenderingContext.COLOR_ATTACHMENT0; /** @type {number} */ WebGLRenderingContext.DEPTH_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.STENCIL_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.DEPTH_STENCIL_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.NONE; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_COMPLETE; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_INCOMPLETE_DIMENSIONS; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_UNSUPPORTED; /** @type {number} */ WebGLRenderingContext.FRAMEBUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.RENDERBUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.MAX_RENDERBUFFER_SIZE; /** @type {number} */ WebGLRenderingContext.INVALID_FRAMEBUFFER_OPERATION; /** @type {number} */ WebGLRenderingContext.UNPACK_FLIP_Y_WEBGL; /** @type {number} */ WebGLRenderingContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL; /** @type {number} */ WebGLRenderingContext.CONTEXT_LOST_WEBGL; /** @type {number} */ WebGLRenderingContext.UNPACK_COLORSPACE_CONVERSION_WEBGL; /** @type {number} */ WebGLRenderingContext.BROWSER_DEFAULT_WEBGL; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.prototype.COLOR_BUFFER_BIT; /** @type {number} */ WebGLRenderingContext.prototype.POINTS; /** @type {number} */ WebGLRenderingContext.prototype.LINES; /** @type {number} */ WebGLRenderingContext.prototype.LINE_LOOP; /** @type {number} */ WebGLRenderingContext.prototype.LINE_STRIP; /** @type {number} */ WebGLRenderingContext.prototype.TRIANGLES; /** @type {number} */ WebGLRenderingContext.prototype.TRIANGLE_STRIP; /** @type {number} */ WebGLRenderingContext.prototype.TRIANGLE_FAN; /** @type {number} */ WebGLRenderingContext.prototype.ZERO; /** @type {number} */ WebGLRenderingContext.prototype.ONE; /** @type {number} */ WebGLRenderingContext.prototype.SRC_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_SRC_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.DST_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_DST_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.DST_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_DST_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.SRC_ALPHA_SATURATE; /** @type {number} */ WebGLRenderingContext.prototype.FUNC_ADD; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_EQUATION; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_EQUATION_RGB; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_EQUATION_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.FUNC_SUBTRACT; /** @type {number} */ WebGLRenderingContext.prototype.FUNC_REVERSE_SUBTRACT; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_DST_RGB; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_SRC_RGB; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_DST_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_SRC_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.CONSTANT_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_CONSTANT_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.CONSTANT_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.ONE_MINUS_CONSTANT_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.BLEND_COLOR; /** @type {number} */ WebGLRenderingContext.prototype.ARRAY_BUFFER; /** @type {number} */ WebGLRenderingContext.prototype.ELEMENT_ARRAY_BUFFER; /** @type {number} */ WebGLRenderingContext.prototype.ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.prototype.ELEMENT_ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.prototype.STREAM_DRAW; /** @type {number} */ WebGLRenderingContext.prototype.STATIC_DRAW; /** @type {number} */ WebGLRenderingContext.prototype.DYNAMIC_DRAW; /** @type {number} */ WebGLRenderingContext.prototype.BUFFER_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.BUFFER_USAGE; /** @type {number} */ WebGLRenderingContext.prototype.CURRENT_VERTEX_ATTRIB; /** @type {number} */ WebGLRenderingContext.prototype.FRONT; /** @type {number} */ WebGLRenderingContext.prototype.BACK; /** @type {number} */ WebGLRenderingContext.prototype.FRONT_AND_BACK; /** @type {number} */ WebGLRenderingContext.prototype.CULL_FACE; /** @type {number} */ WebGLRenderingContext.prototype.BLEND; /** @type {number} */ WebGLRenderingContext.prototype.DITHER; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_TEST; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_TEST; /** @type {number} */ WebGLRenderingContext.prototype.SCISSOR_TEST; /** @type {number} */ WebGLRenderingContext.prototype.POLYGON_OFFSET_FILL; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLE_ALPHA_TO_COVERAGE; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLE_COVERAGE; /** @type {number} */ WebGLRenderingContext.prototype.NO_ERROR; /** @type {number} */ WebGLRenderingContext.prototype.INVALID_ENUM; /** @type {number} */ WebGLRenderingContext.prototype.INVALID_VALUE; /** @type {number} */ WebGLRenderingContext.prototype.INVALID_OPERATION; /** @type {number} */ WebGLRenderingContext.prototype.OUT_OF_MEMORY; /** @type {number} */ WebGLRenderingContext.prototype.CW; /** @type {number} */ WebGLRenderingContext.prototype.CCW; /** @type {number} */ WebGLRenderingContext.prototype.LINE_WIDTH; /** @type {number} */ WebGLRenderingContext.prototype.ALIASED_POINT_SIZE_RANGE; /** @type {number} */ WebGLRenderingContext.prototype.ALIASED_LINE_WIDTH_RANGE; /** @type {number} */ WebGLRenderingContext.prototype.CULL_FACE_MODE; /** @type {number} */ WebGLRenderingContext.prototype.FRONT_FACE; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_RANGE; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_WRITEMASK; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_FUNC; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_FUNC; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_FAIL; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_PASS_DEPTH_FAIL; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_PASS_DEPTH_PASS; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_REF; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_VALUE_MASK; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_WRITEMASK; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_FUNC; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_FAIL; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_PASS_DEPTH_FAIL; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_PASS_DEPTH_PASS; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_REF; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_VALUE_MASK; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BACK_WRITEMASK; /** @type {number} */ WebGLRenderingContext.prototype.VIEWPORT; /** @type {number} */ WebGLRenderingContext.prototype.SCISSOR_BOX; /** @type {number} */ WebGLRenderingContext.prototype.COLOR_CLEAR_VALUE; /** @type {number} */ WebGLRenderingContext.prototype.COLOR_WRITEMASK; /** @type {number} */ WebGLRenderingContext.prototype.UNPACK_ALIGNMENT; /** @type {number} */ WebGLRenderingContext.prototype.PACK_ALIGNMENT; /** @type {number} */ WebGLRenderingContext.prototype.MAX_TEXTURE_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.MAX_VIEWPORT_DIMS; /** @type {number} */ WebGLRenderingContext.prototype.SUBPIXEL_BITS; /** @type {number} */ WebGLRenderingContext.prototype.RED_BITS; /** @type {number} */ WebGLRenderingContext.prototype.GREEN_BITS; /** @type {number} */ WebGLRenderingContext.prototype.BLUE_BITS; /** @type {number} */ WebGLRenderingContext.prototype.ALPHA_BITS; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_BITS; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_BITS; /** @type {number} */ WebGLRenderingContext.prototype.POLYGON_OFFSET_UNITS; /** @type {number} */ WebGLRenderingContext.prototype.POLYGON_OFFSET_FACTOR; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_BINDING_2D; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLE_BUFFERS; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLES; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLE_COVERAGE_VALUE; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLE_COVERAGE_INVERT; /** @type {number} */ WebGLRenderingContext.prototype.COMPRESSED_TEXTURE_FORMATS; /** @type {number} */ WebGLRenderingContext.prototype.DONT_CARE; /** @type {number} */ WebGLRenderingContext.prototype.FASTEST; /** @type {number} */ WebGLRenderingContext.prototype.NICEST; /** @type {number} */ WebGLRenderingContext.prototype.GENERATE_MIPMAP_HINT; /** @type {number} */ WebGLRenderingContext.prototype.BYTE; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_BYTE; /** @type {number} */ WebGLRenderingContext.prototype.SHORT; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_SHORT; /** @type {number} */ WebGLRenderingContext.prototype.INT; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_INT; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_COMPONENT; /** @type {number} */ WebGLRenderingContext.prototype.ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.RGB; /** @type {number} */ WebGLRenderingContext.prototype.RGBA; /** @type {number} */ WebGLRenderingContext.prototype.LUMINANCE; /** @type {number} */ WebGLRenderingContext.prototype.LUMINANCE_ALPHA; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_SHORT_4_4_4_4; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_SHORT_5_5_5_1; /** @type {number} */ WebGLRenderingContext.prototype.UNSIGNED_SHORT_5_6_5; /** @type {number} */ WebGLRenderingContext.prototype.FRAGMENT_SHADER; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_SHADER; /** @type {number} */ WebGLRenderingContext.prototype.MAX_VERTEX_ATTRIBS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_VERTEX_UNIFORM_VECTORS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_VARYING_VECTORS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_COMBINED_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_VERTEX_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_TEXTURE_IMAGE_UNITS; /** @type {number} */ WebGLRenderingContext.prototype.MAX_FRAGMENT_UNIFORM_VECTORS; /** @type {number} */ WebGLRenderingContext.prototype.SHADER_TYPE; /** @type {number} */ WebGLRenderingContext.prototype.DELETE_STATUS; /** @type {number} */ WebGLRenderingContext.prototype.LINK_STATUS; /** @type {number} */ WebGLRenderingContext.prototype.VALIDATE_STATUS; /** @type {number} */ WebGLRenderingContext.prototype.ATTACHED_SHADERS; /** @type {number} */ WebGLRenderingContext.prototype.ACTIVE_UNIFORMS; /** @type {number} */ WebGLRenderingContext.prototype.ACTIVE_ATTRIBUTES; /** @type {number} */ WebGLRenderingContext.prototype.SHADING_LANGUAGE_VERSION; /** @type {number} */ WebGLRenderingContext.prototype.CURRENT_PROGRAM; /** @type {number} */ WebGLRenderingContext.prototype.NEVER; /** @type {number} */ WebGLRenderingContext.prototype.LESS; /** @type {number} */ WebGLRenderingContext.prototype.EQUAL; /** @type {number} */ WebGLRenderingContext.prototype.LEQUAL; /** @type {number} */ WebGLRenderingContext.prototype.GREATER; /** @type {number} */ WebGLRenderingContext.prototype.NOTEQUAL; /** @type {number} */ WebGLRenderingContext.prototype.GEQUAL; /** @type {number} */ WebGLRenderingContext.prototype.ALWAYS; /** @type {number} */ WebGLRenderingContext.prototype.KEEP; /** @type {number} */ WebGLRenderingContext.prototype.REPLACE; /** @type {number} */ WebGLRenderingContext.prototype.INCR; /** @type {number} */ WebGLRenderingContext.prototype.DECR; /** @type {number} */ WebGLRenderingContext.prototype.INVERT; /** @type {number} */ WebGLRenderingContext.prototype.INCR_WRAP; /** @type {number} */ WebGLRenderingContext.prototype.DECR_WRAP; /** @type {number} */ WebGLRenderingContext.prototype.VENDOR; /** @type {number} */ WebGLRenderingContext.prototype.RENDERER; /** @type {number} */ WebGLRenderingContext.prototype.VERSION; /** @type {number} */ WebGLRenderingContext.prototype.NEAREST; /** @type {number} */ WebGLRenderingContext.prototype.LINEAR; /** @type {number} */ WebGLRenderingContext.prototype.NEAREST_MIPMAP_NEAREST; /** @type {number} */ WebGLRenderingContext.prototype.LINEAR_MIPMAP_NEAREST; /** @type {number} */ WebGLRenderingContext.prototype.NEAREST_MIPMAP_LINEAR; /** @type {number} */ WebGLRenderingContext.prototype.LINEAR_MIPMAP_LINEAR; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_MAG_FILTER; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_MIN_FILTER; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_WRAP_S; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_WRAP_T; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_2D; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_BINDING_CUBE_MAP; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_X; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_X; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_Y; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_Y; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_POSITIVE_Z; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE_CUBE_MAP_NEGATIVE_Z; /** @type {number} */ WebGLRenderingContext.prototype.MAX_CUBE_MAP_TEXTURE_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE0; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE1; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE2; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE3; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE4; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE5; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE6; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE7; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE8; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE9; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE10; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE11; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE12; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE13; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE14; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE15; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE16; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE17; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE18; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE19; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE20; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE21; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE22; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE23; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE24; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE25; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE26; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE27; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE28; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE29; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE30; /** @type {number} */ WebGLRenderingContext.prototype.TEXTURE31; /** @type {number} */ WebGLRenderingContext.prototype.ACTIVE_TEXTURE; /** @type {number} */ WebGLRenderingContext.prototype.REPEAT; /** @type {number} */ WebGLRenderingContext.prototype.CLAMP_TO_EDGE; /** @type {number} */ WebGLRenderingContext.prototype.MIRRORED_REPEAT; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_VEC2; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_VEC3; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_VEC4; /** @type {number} */ WebGLRenderingContext.prototype.INT_VEC2; /** @type {number} */ WebGLRenderingContext.prototype.INT_VEC3; /** @type {number} */ WebGLRenderingContext.prototype.INT_VEC4; /** @type {number} */ WebGLRenderingContext.prototype.BOOL; /** @type {number} */ WebGLRenderingContext.prototype.BOOL_VEC2; /** @type {number} */ WebGLRenderingContext.prototype.BOOL_VEC3; /** @type {number} */ WebGLRenderingContext.prototype.BOOL_VEC4; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_MAT2; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_MAT3; /** @type {number} */ WebGLRenderingContext.prototype.FLOAT_MAT4; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLER_2D; /** @type {number} */ WebGLRenderingContext.prototype.SAMPLER_CUBE; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_ENABLED; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_STRIDE; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_TYPE; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_NORMALIZED; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_POINTER; /** @type {number} */ WebGLRenderingContext.prototype.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.prototype.COMPILE_STATUS; /** @type {number} */ WebGLRenderingContext.prototype.LOW_FLOAT; /** @type {number} */ WebGLRenderingContext.prototype.MEDIUM_FLOAT; /** @type {number} */ WebGLRenderingContext.prototype.HIGH_FLOAT; /** @type {number} */ WebGLRenderingContext.prototype.LOW_INT; /** @type {number} */ WebGLRenderingContext.prototype.MEDIUM_INT; /** @type {number} */ WebGLRenderingContext.prototype.HIGH_INT; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER; /** @type {number} */ WebGLRenderingContext.prototype.RGBA4; /** @type {number} */ WebGLRenderingContext.prototype.RGB5_A1; /** @type {number} */ WebGLRenderingContext.prototype.RGB565; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_COMPONENT16; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_INDEX; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_INDEX8; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_STENCIL; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_WIDTH; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_HEIGHT; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_INTERNAL_FORMAT; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_RED_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_GREEN_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_BLUE_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_ALPHA_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_DEPTH_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_STENCIL_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE; /** @type {number} */ WebGLRenderingContext.prototype.COLOR_ATTACHMENT0; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.prototype.STENCIL_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.prototype.DEPTH_STENCIL_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.prototype.NONE; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_COMPLETE; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_INCOMPLETE_DIMENSIONS; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_UNSUPPORTED; /** @type {number} */ WebGLRenderingContext.prototype.FRAMEBUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.prototype.RENDERBUFFER_BINDING; /** @type {number} */ WebGLRenderingContext.prototype.MAX_RENDERBUFFER_SIZE; /** @type {number} */ WebGLRenderingContext.prototype.INVALID_FRAMEBUFFER_OPERATION; /** @type {number} */ WebGLRenderingContext.prototype.UNPACK_FLIP_Y_WEBGL; /** @type {number} */ WebGLRenderingContext.prototype.UNPACK_PREMULTIPLY_ALPHA_WEBGL; /** @type {number} */ WebGLRenderingContext.prototype.CONTEXT_LOST_WEBGL; /** @type {number} */ WebGLRenderingContext.prototype.UNPACK_COLORSPACE_CONVERSION_WEBGL; /** @type {number} */ WebGLRenderingContext.prototype.BROWSER_DEFAULT_WEBGL; /** * @type {!HTMLCanvasElement} */ WebGLRenderingContext.prototype.canvas; /** * @type {number} */ WebGLRenderingContext.prototype.drawingBufferWidth; /** * @type {number} */ WebGLRenderingContext.prototype.drawingBufferHeight; /** * @return {!WebGLContextAttributes} * @nosideeffects */ WebGLRenderingContext.prototype.getContextAttributes = function() {}; /** * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isContextLost = function() {}; /** * @return {!Array.} * @nosideeffects */ WebGLRenderingContext.prototype.getSupportedExtensions = function() {}; /** * Note that this has side effects by enabling the extension even if the * result is not used. * @param {string} name * @return {Object} */ WebGLRenderingContext.prototype.getExtension = function(name) {}; /** * @param {number} texture */ WebGLRenderingContext.prototype.activeTexture = function(texture) {}; /** * @param {WebGLProgram} program * @param {WebGLShader} shader */ WebGLRenderingContext.prototype.attachShader = function(program, shader) {}; /** * @param {WebGLProgram} program * @param {number} index * @param {string} name */ WebGLRenderingContext.prototype.bindAttribLocation = function( program, index, name) {}; /** * @param {number} target * @param {WebGLBuffer} buffer */ WebGLRenderingContext.prototype.bindBuffer = function(target, buffer) {}; /** * @param {number} target * @param {WebGLFramebuffer} buffer */ WebGLRenderingContext.prototype.bindFramebuffer = function(target, buffer) {}; /** * @param {number} target * @param {WebGLRenderbuffer} buffer */ WebGLRenderingContext.prototype.bindRenderbuffer = function(target, buffer) {}; /** * @param {number} target * @param {WebGLTexture} texture */ WebGLRenderingContext.prototype.bindTexture = function(target, texture) {}; /** * @param {number} red * @param {number} green * @param {number} blue * @param {number} alpha */ WebGLRenderingContext.prototype.blendColor = function( red, blue, green, alpha) {}; /** * @param {number} mode */ WebGLRenderingContext.prototype.blendEquation = function(mode) {}; /** * @param {number} modeRGB * @param {number} modeAlpha */ WebGLRenderingContext.prototype.blendEquationSeparate = function( modeRGB, modeAlpha) {}; /** * @param {number} sfactor * @param {number} dfactor */ WebGLRenderingContext.prototype.blendFunc = function(sfactor, dfactor) {}; /** * @param {number} srcRGB * @param {number} dstRGB * @param {number} srcAlpha * @param {number} dstAlpha */ WebGLRenderingContext.prototype.blendFuncSeparate = function( srcRGB, dstRGB, srcAlpha, dstAlpha) {}; /** * @param {number} target * @param {ArrayBufferView|ArrayBuffer|number} data * @param {number} usage */ WebGLRenderingContext.prototype.bufferData = function(target, data, usage) {}; /** * @param {number} target * @param {number} offset * @param {ArrayBufferView|ArrayBuffer} data */ WebGLRenderingContext.prototype.bufferSubData = function( target, offset, data) {}; /** * @param {number} target * @return {number} */ WebGLRenderingContext.prototype.checkFramebufferStatus = function(target) {}; /** * @param {number} mask */ WebGLRenderingContext.prototype.clear = function(mask) {}; /** * @param {number} red * @param {number} green * @param {number} blue * @param {number} alpha */ WebGLRenderingContext.prototype.clearColor = function( red, green, blue, alpha) {}; /** * @param {number} depth */ WebGLRenderingContext.prototype.clearDepth = function(depth) {}; /** * @param {number} s */ WebGLRenderingContext.prototype.clearStencil = function(s) {}; /** * @param {boolean} red * @param {boolean} green * @param {boolean} blue * @param {boolean} alpha */ WebGLRenderingContext.prototype.colorMask = function( red, green, blue, alpha) {}; /** * @param {WebGLShader} shader */ WebGLRenderingContext.prototype.compileShader = function(shader) {}; /** * @param {number} target * @param {number} level * @param {number} internalformat * @param {number} width * @param {number} height * @param {number} border * @param {ArrayBufferView} data */ WebGLRenderingContext.prototype.compressedTexImage2D = function( target, level, internalformat, width, height, border, data) {}; /** * @param {number} target * @param {number} level * @param {number} xoffset * @param {number} yoffset * @param {number} width * @param {number} height * @param {number} format * @param {ArrayBufferView} data */ WebGLRenderingContext.prototype.compressedTexSubImage2D = function( target, level, xoffset, yoffset, width, height, format, data) {}; /** * @param {number} target * @param {number} level * @param {number} format * @param {number} x * @param {number} y * @param {number} width * @param {number} height * @param {number} border */ WebGLRenderingContext.prototype.copyTexImage2D = function( target, level, format, x, y, width, height, border) {}; /** * @param {number} target * @param {number} level * @param {number} xoffset * @param {number} yoffset * @param {number} x * @param {number} y * @param {number} width * @param {number} height */ WebGLRenderingContext.prototype.copyTexSubImage2D = function( target, level, xoffset, yoffset, x, y, width, height) {}; /** * @return {!WebGLBuffer} * @nosideeffects */ WebGLRenderingContext.prototype.createBuffer = function() {}; /** * @return {!WebGLFramebuffer} * @nosideeffects */ WebGLRenderingContext.prototype.createFramebuffer = function() {}; /** * @return {!WebGLProgram} * @nosideeffects */ WebGLRenderingContext.prototype.createProgram = function() {}; /** * @return {!WebGLRenderbuffer} * @nosideeffects */ WebGLRenderingContext.prototype.createRenderbuffer = function() {}; /** * @param {number} type * @return {!WebGLShader} * @nosideeffects */ WebGLRenderingContext.prototype.createShader = function(type) {}; /** * @return {!WebGLTexture} * @nosideeffects */ WebGLRenderingContext.prototype.createTexture = function() {}; /** * @param {number} mode */ WebGLRenderingContext.prototype.cullFace = function(mode) {}; /** * @param {WebGLBuffer} buffer */ WebGLRenderingContext.prototype.deleteBuffer = function(buffer) {}; /** * @param {WebGLFramebuffer} buffer */ WebGLRenderingContext.prototype.deleteFramebuffer = function(buffer) {}; /** * @param {WebGLProgram} program */ WebGLRenderingContext.prototype.deleteProgram = function(program) {}; /** * @param {WebGLRenderbuffer} buffer */ WebGLRenderingContext.prototype.deleteRenderbuffer = function(buffer) {}; /** * @param {WebGLShader} shader */ WebGLRenderingContext.prototype.deleteShader = function(shader) {}; /** * @param {WebGLTexture} texture */ WebGLRenderingContext.prototype.deleteTexture = function(texture) {}; /** * @param {number} func */ WebGLRenderingContext.prototype.depthFunc = function(func) {}; /** * @param {boolean} flag */ WebGLRenderingContext.prototype.depthMask = function(flag) {}; /** * @param {number} nearVal * @param {number} farVal */ WebGLRenderingContext.prototype.depthRange = function(nearVal, farVal) {}; /** * @param {WebGLProgram} program * @param {WebGLShader} shader */ WebGLRenderingContext.prototype.detachShader = function(program, shader) {}; /** * @param {number} flags */ WebGLRenderingContext.prototype.disable = function(flags) {}; /** * @param {number} index */ WebGLRenderingContext.prototype.disableVertexAttribArray = function( index) {}; /** * @param {number} mode * @param {number} first * @param {number} count */ WebGLRenderingContext.prototype.drawArrays = function(mode, first, count) {}; /** * @param {number} mode * @param {number} count * @param {number} type * @param {number} offset */ WebGLRenderingContext.prototype.drawElements = function( mode, count, type, offset) {}; /** * @param {number} cap */ WebGLRenderingContext.prototype.enable = function(cap) {}; /** * @param {number} index */ WebGLRenderingContext.prototype.enableVertexAttribArray = function( index) {}; WebGLRenderingContext.prototype.finish = function() {}; WebGLRenderingContext.prototype.flush = function() {}; /** * @param {number} target * @param {number} attachment * @param {number} renderbuffertarget * @param {WebGLRenderbuffer} renderbuffer */ WebGLRenderingContext.prototype.framebufferRenderbuffer = function( target, attachment, renderbuffertarget, renderbuffer) {}; /** * @param {number} target * @param {number} attachment * @param {number} textarget * @param {WebGLTexture} texture * @param {number} level */ WebGLRenderingContext.prototype.framebufferTexture2D = function( target, attachment, textarget, texture, level) {}; /** * @param {number} mode */ WebGLRenderingContext.prototype.frontFace = function(mode) {}; /** * @param {number} target */ WebGLRenderingContext.prototype.generateMipmap = function(target) {}; /** * @param {WebGLProgram} program * @param {number} index * @return {WebGLActiveInfo} * @nosideeffects */ WebGLRenderingContext.prototype.getActiveAttrib = function(program, index) {}; /** * @param {WebGLProgram} program * @param {number} index * @return {WebGLActiveInfo} * @nosideeffects */ WebGLRenderingContext.prototype.getActiveUniform = function(program, index) {}; /** * @param {WebGLProgram} program * @return {!Array.} * @nosideeffects */ WebGLRenderingContext.prototype.getAttachedShaders = function(program) {}; /** * @param {WebGLProgram} program * @param {string} name * @return {number} * @nosideeffects */ WebGLRenderingContext.prototype.getAttribLocation = function(program, name) {}; /** * @param {number} target * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getBufferParameter = function(target, pname) {}; /** * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getParameter = function(pname) {}; /** * @return {number} * @nosideeffects */ WebGLRenderingContext.prototype.getError = function() {}; /** * @param {number} target * @param {number} attachment * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getFramebufferAttachmentParameter = function( target, attachment, pname) {}; /** * @param {WebGLProgram} program * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getProgramParameter = function( program, pname) {}; /** * @param {WebGLProgram} program * @return {string} * @nosideeffects */ WebGLRenderingContext.prototype.getProgramInfoLog = function(program) {}; /** * @param {number} target * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getRenderbufferParameter = function( target, pname) {}; /** * @param {WebGLShader} shader * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getShaderParameter = function(shader, pname) {}; /** * @param {number} shadertype * @param {number} precisiontype * @return {WebGLShaderPrecisionFormat} * @nosideeffects */ WebGLRenderingContext.prototype.getShaderPrecisionFormat = function(shadertype, precisiontype) {}; /** * @param {WebGLShader} shader * @return {string} * @nosideeffects */ WebGLRenderingContext.prototype.getShaderInfoLog = function(shader) {}; /** * @param {WebGLShader} shader * @return {string} * @nosideeffects */ WebGLRenderingContext.prototype.getShaderSource = function(shader) {}; /** * @param {number} target * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getTexParameter = function(target, pname) {}; /** * @param {WebGLProgram} program * @param {WebGLUniformLocation} location * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getUniform = function(program, location) {}; /** * @param {WebGLProgram} program * @param {string} name * @return {WebGLUniformLocation} * @nosideeffects */ WebGLRenderingContext.prototype.getUniformLocation = function(program, name) {}; /** * @param {number} index * @param {number} pname * @return {*} * @nosideeffects */ WebGLRenderingContext.prototype.getVertexAttrib = function(index, pname) {}; /** * @param {number} index * @param {number} pname * @return {number} * @nosideeffects */ WebGLRenderingContext.prototype.getVertexAttribOffset = function( index, pname) {}; /** * @param {number} target * @param {number} mode */ WebGLRenderingContext.prototype.hint = function(target, mode) {}; /** * @param {WebGLObject} buffer * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isBuffer = function(buffer) {}; /** * @param {number} cap * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isEnabled = function(cap) {}; /** * @param {WebGLObject} framebuffer * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isFramebuffer = function(framebuffer) {}; /** * @param {WebGLObject} program * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isProgram = function(program) {}; /** * @param {WebGLObject} renderbuffer * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isRenderbuffer = function(renderbuffer) {}; /** * @param {WebGLObject} shader * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isShader = function(shader) {}; /** * @param {WebGLObject} texture * @return {boolean} * @nosideeffects */ WebGLRenderingContext.prototype.isTexture = function(texture) {}; /** * @param {number} width */ WebGLRenderingContext.prototype.lineWidth = function(width) {}; /** * @param {WebGLProgram} program */ WebGLRenderingContext.prototype.linkProgram = function(program) {}; /** * @param {number} pname * @param {number} param */ WebGLRenderingContext.prototype.pixelStorei = function(pname, param) {}; /** * @param {number} factor * @param {number} units */ WebGLRenderingContext.prototype.polygonOffset = function(factor, units) {}; /** * @param {number} x * @param {number} y * @param {number} width * @param {number} height * @param {number} format * @param {number} type * @param {ArrayBufferView} pixels */ WebGLRenderingContext.prototype.readPixels = function( x, y, width, height, format, type, pixels) {}; /** * @param {number} target * @param {number} internalformat * @param {number} width * @param {number} height */ WebGLRenderingContext.prototype.renderbufferStorage = function( target, internalformat, width, height) {}; /** * @param {number} coverage * @param {boolean} invert */ WebGLRenderingContext.prototype.sampleCoverage = function(coverage, invert) {}; /** * @param {number} x * @param {number} y * @param {number} width * @param {number} height */ WebGLRenderingContext.prototype.scissor = function(x, y, width, height) {}; /** * @param {WebGLShader} shader * @param {string} source */ WebGLRenderingContext.prototype.shaderSource = function(shader, source) {}; /** * @param {number} func * @param {number} ref * @param {number} mask */ WebGLRenderingContext.prototype.stencilFunc = function(func, ref, mask) {}; /** * @param {number} face * @param {number} func * @param {number} ref * @param {number} mask */ WebGLRenderingContext.prototype.stencilFuncSeparate = function( face, func, ref, mask) {}; /** * @param {number} mask */ WebGLRenderingContext.prototype.stencilMask = function(mask) {}; /** * @param {number} face * @param {number} mask */ WebGLRenderingContext.prototype.stencilMaskSeparate = function(face, mask) {}; /** * @param {number} fail * @param {number} zfail * @param {number} zpass */ WebGLRenderingContext.prototype.stencilOp = function(fail, zfail, zpass) {}; /** * @param {number} face * @param {number} fail * @param {number} zfail * @param {number} zpass */ WebGLRenderingContext.prototype.stencilOpSeparate = function( face, fail, zfail, zpass) {}; /** * @param {number} target * @param {number} level * @param {number} internalformat * @param {number} format or width * @param {number} type or height * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement| * number} img or border * @param {number=} opt_format * @param {number=} opt_type * @param {ArrayBufferView=} opt_pixels */ WebGLRenderingContext.prototype.texImage2D = function( target, level, internalformat, format, type, img, opt_format, opt_type, opt_pixels) {}; /** * @param {number} target * @param {number} pname * @param {number} param */ WebGLRenderingContext.prototype.texParameterf = function( target, pname, param) {}; /** * @param {number} target * @param {number} pname * @param {number} param */ WebGLRenderingContext.prototype.texParameteri = function( target, pname, param) {}; /** * @param {number} target * @param {number} level * @param {number} xoffset * @param {number} yoffset * @param {number} format or width * @param {number} type or height * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement| * number} data or format * @param {number=} opt_type * @param {ArrayBufferView=} opt_pixels */ WebGLRenderingContext.prototype.texSubImage2D = function( target, level, xoffset, yoffset, format, type, data, opt_type, opt_pixels) {}; /** * @param {WebGLUniformLocation} location * @param {number} value */ WebGLRenderingContext.prototype.uniform1f = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {Float32Array|Array.} value */ WebGLRenderingContext.prototype.uniform1fv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value */ WebGLRenderingContext.prototype.uniform1i = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {Int32Array|Array.} value */ WebGLRenderingContext.prototype.uniform1iv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 */ WebGLRenderingContext.prototype.uniform2f = function( location, value1, value2) {}; /** * @param {WebGLUniformLocation} location * @param {Float32Array|Array.} value */ WebGLRenderingContext.prototype.uniform2fv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 */ WebGLRenderingContext.prototype.uniform2i = function( location, value1, value2) {}; /** * @param {WebGLUniformLocation} location * @param {Int32Array|Array.} value */ WebGLRenderingContext.prototype.uniform2iv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 * @param {number} value3 */ WebGLRenderingContext.prototype.uniform3f = function( location, value1, value2, value3) {}; /** * @param {WebGLUniformLocation} location * @param {Float32Array|Array.} value */ WebGLRenderingContext.prototype.uniform3fv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 * @param {number} value3 */ WebGLRenderingContext.prototype.uniform3i = function( location, value1, value2, value3) {}; /** * @param {WebGLUniformLocation} location * @param {Int32Array|Array.} value */ WebGLRenderingContext.prototype.uniform3iv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 * @param {number} value3 * @param {number} value4 */ WebGLRenderingContext.prototype.uniform4f = function( location, value1, value2, value3, value4) {}; /** * @param {WebGLUniformLocation} location * @param {Float32Array|Array.} value */ WebGLRenderingContext.prototype.uniform4fv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {number} value1 * @param {number} value2 * @param {number} value3 * @param {number} value4 */ WebGLRenderingContext.prototype.uniform4i = function( location, value1, value2, value3, value4) {}; /** * @param {WebGLUniformLocation} location * @param {Int32Array|Array.} value */ WebGLRenderingContext.prototype.uniform4iv = function(location, value) {}; /** * @param {WebGLUniformLocation} location * @param {boolean} transpose * @param {Float32Array|Array.} data */ WebGLRenderingContext.prototype.uniformMatrix2fv = function( location, transpose, data) {}; /** * @param {WebGLUniformLocation} location * @param {boolean} transpose * @param {Float32Array|Array.} data */ WebGLRenderingContext.prototype.uniformMatrix3fv = function( location, transpose, data) {}; /** * @param {WebGLUniformLocation} location * @param {boolean} transpose * @param {Float32Array|Array.} data */ WebGLRenderingContext.prototype.uniformMatrix4fv = function( location, transpose, data) {}; /** * @param {WebGLProgram} program */ WebGLRenderingContext.prototype.useProgram = function(program) {}; /** * @param {WebGLProgram} program */ WebGLRenderingContext.prototype.validateProgram = function(program) {}; /** * @param {number} indx * @param {number} x */ WebGLRenderingContext.prototype.vertexAttrib1f = function(indx, x) {}; /** * @param {number} indx * @param {Float32Array|Array.} values */ WebGLRenderingContext.prototype.vertexAttrib1fv = function(indx, values) {}; /** * @param {number} indx * @param {number} x * @param {number} y */ WebGLRenderingContext.prototype.vertexAttrib2f = function( indx, x, y) {}; /** * @param {number} indx * @param {Float32Array|Array.} values */ WebGLRenderingContext.prototype.vertexAttrib2fv = function( indx, values) {}; /** * @param {number} indx * @param {number} x * @param {number} y * @param {number} z */ WebGLRenderingContext.prototype.vertexAttrib3f = function( indx, x, y, z) {}; /** * @param {number} indx * @param {Float32Array|Array.} values */ WebGLRenderingContext.prototype.vertexAttrib3fv = function(indx, values) {}; /** * @param {number} indx * @param {number} x * @param {number} y * @param {number} z * @param {number} w */ WebGLRenderingContext.prototype.vertexAttrib4f = function( indx, x, y, z, w) {}; /** * @param {number} indx * @param {Float32Array|Array.} values */ WebGLRenderingContext.prototype.vertexAttrib4fv = function(indx, values) {}; /** * @param {number} indx * @param {number} size * @param {number} type * @param {boolean} normalized * @param {number} stride * @param {number} offset */ WebGLRenderingContext.prototype.vertexAttribPointer = function( indx, size, type, normalized, stride, offset) {}; /** * @param {number} x * @param {number} y * @param {number} width * @param {number} height */ WebGLRenderingContext.prototype.viewport = function(x, y, width, height) {}; /** * @constructor * @noalias */ function WebGLContextAttributes() {} /** * @type {boolean} */ WebGLContextAttributes.prototype.alpha; /** * @type {boolean} */ WebGLContextAttributes.prototype.depth; /** * @type {boolean} */ WebGLContextAttributes.prototype.stencil; /** * @type {boolean} */ WebGLContextAttributes.prototype.antialias; /** * @type {boolean} */ WebGLContextAttributes.prototype.premultipliedAlpha; /** * @type {boolean} */ WebGLContextAttributes.prototype.preserveDrawingBuffer; /** * @constructor * @noalias * @extends {Event} */ function WebGLContextEvent() {} /** * @type {string} */ WebGLContextEvent.prototype.statusMessage; /** * @constructor * @noalias */ function WebGLShaderPrecisionFormat() {} /** * @type {number} */ WebGLShaderPrecisionFormat.prototype.rangeMin; /** * @type {number} */ WebGLShaderPrecisionFormat.prototype.rangeMax; /** * @type {number} */ WebGLShaderPrecisionFormat.prototype.precision; /** * @constructor * @noalias */ function WebGLObject() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLBuffer() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLFramebuffer() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLProgram() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLRenderbuffer() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLShader() {} /** * @constructor * @noalias * @extends {WebGLObject} */ function WebGLTexture() {} /** * @constructor * @noalias */ function WebGLActiveInfo() {} /** @type {number} */ WebGLActiveInfo.prototype.size; /** @type {number} */ WebGLActiveInfo.prototype.type; /** @type {string} */ WebGLActiveInfo.prototype.name; /** * @constructor * @noalias */ function WebGLUniformLocation() {} /** * @see http://www.khronos.org/registry/webgl/extensions/OES_texture_float/ * @constructor * @noalias */ function OES_texture_float() {} /** * @see http://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/ * @constructor * @noalias */ function OES_texture_half_float() {} /** @type {number} */ OES_texture_half_float.prototype.HALF_FLOAT_OES; /** * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_lose_context/ * @constructor * @noalias */ function WEBGL_lose_context() {} WEBGL_lose_context.prototype.loseContext = function() {}; WEBGL_lose_context.prototype.restoreContext = function() {}; /** * @see http://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/ * @constructor * @noalias */ function OES_standard_derivatives() {} /** @type {number} */ OES_standard_derivatives.prototype.FRAGMENT_SHADER_DERIVATIVE_HINT_OES; /** * @see http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ * @constructor * @noalias * @extends {WebGLObject} */ function WebGLVertexArrayObjectOES() {} /** * @see http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ * @constructor * @noalias */ function OES_vertex_array_object() {} /** @type {number} */ OES_vertex_array_object.prototype.VERTEX_ARRAY_BINDING_OES; /** * @return {WebGLVertexArrayObjectOES} * @nosideeffects */ OES_vertex_array_object.prototype.createVertexArrayOES = function() {}; /** * @param {WebGLVertexArrayObjectOES} arrayObject */ OES_vertex_array_object.prototype.deleteVertexArrayOES = function(arrayObject) {}; /** * @param {WebGLVertexArrayObjectOES} arrayObject * @return {boolean} * @nosideeffects */ OES_vertex_array_object.prototype.isVertexArrayOES = function(arrayObject) {}; /** * @param {WebGLVertexArrayObjectOES} arrayObject */ OES_vertex_array_object.prototype.bindVertexArrayOES = function(arrayObject) {}; /** * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/ * @constructor * @noalias */ function WEBGL_debug_renderer_info() {} /** @type {number} */ WEBGL_debug_renderer_info.prototype.UNMASKED_VENDOR_WEBGL; /** @type {number} */ WEBGL_debug_renderer_info.prototype.UNMASKED_RENDERER_WEBGL; /** * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_shaders/ * @constructor * @noalias */ function WEBGL_debug_shaders() {} /** * @param {WebGLShader} shader * @return {string} * @nosideeffects */ WEBGL_debug_shaders.prototype.getTranslatedShaderSource = function(shader) {}; /** * @see http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ * @constructor * @noalias */ function WEBGL_compressed_texture_s3tc() {} /** @type {number} */ WEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGB_S3TC_DXT1_EXT; /** @type {number} */ WEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT1_EXT; /** @type {number} */ WEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT3_EXT; /** @type {number} */ WEBGL_compressed_texture_s3tc.prototype.COMPRESSED_RGBA_S3TC_DXT5_EXT; /** * @see http://www.khronos.org/registry/webgl/extensions/OES_depth_texture/ * @constructor * @noalias */ function OES_depth_texture() {} /** * @see http://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/ * @constructor * @noalias */ function OES_element_index_uint() {} /** * @see http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ * @constructor * @noalias */ function EXT_texture_filter_anisotropic() {} /** @type {number} */ EXT_texture_filter_anisotropic.prototype.TEXTURE_MAX_ANISOTROPY_EXT; /** @type {number} */ EXT_texture_filter_anisotropic.prototype.MAX_TEXTURE_MAX_ANISOTROPY_EXT; closure-compiler-20130227+dfsg1/externs/w3c_event.js0000644000175000017500000001321112115204405020273 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's event specification. * The whole file has been fully type annotated. * Created from * http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html * * @externs */ /** * @interface */ function EventTarget() {} /** * @param {string} type * @param {EventListener|function(Event):(boolean|undefined)} listener * @param {boolean} useCapture * @return {undefined} */ EventTarget.prototype.addEventListener = function(type, listener, useCapture) {}; /** * @param {string} type * @param {EventListener|function(Event):(boolean|undefined)} listener * @param {boolean} useCapture * @return {undefined} */ EventTarget.prototype.removeEventListener = function(type, listener, useCapture) {}; /** * @param {Event} evt * @return {boolean} */ EventTarget.prototype.dispatchEvent = function(evt) {}; /** * @interface */ function EventListener() {} /** * @param {Event} evt * @return {undefined} */ EventListener.prototype.handleEvent = function(evt) {}; /** * @constructor */ function Event() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html */ Event.AT_TARGET; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html */ Event.BUBBLING_PHASE; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Events/ecma-script-binding.html */ Event.CAPTURING_PHASE; /** @type {string} */ Event.prototype.type; /** @type {EventTarget} */ Event.prototype.target; /** @type {EventTarget} */ Event.prototype.currentTarget; /** @type {number} */ Event.prototype.eventPhase; /** @type {boolean} */ Event.prototype.bubbles; /** @type {boolean} */ Event.prototype.cancelable; /** @type {number} */ Event.prototype.timeStamp; /** @type {Object} */ Event.prototype.state; /** * @return {undefined} */ Event.prototype.stopPropagation = function() {}; /** * @return {undefined} */ Event.prototype.preventDefault = function() {}; /** * @param {string} eventTypeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @return {undefined} */ Event.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {}; /** * @constructor * @extends {Event} * @see http://www.w3.org/TR/DOM-Level-3-Events/#interface-CustomEvent */ function CustomEvent() {} /** * @param {string} eventType * @param {boolean} bubbles * @param {boolean} cancelable * @param {*} detail */ CustomEvent.prototype.initCustomEvent = function( eventType, bubbles, cancelable, detail) {}; /** * @type {*} */ CustomEvent.prototype.detail; /** * @constructor */ function DocumentEvent() {} /** * @param {string} eventType * @return {Event} */ DocumentEvent.prototype.createEvent = function(eventType) {}; /** * @constructor * @extends {Event} */ function UIEvent() {} /** @type {number} */ UIEvent.prototype.detail; ///** // * @param {string} typeArg // * @param {boolean} canBubbleArg // * @param {boolean} cancelableArg // * @param {AbstractView} viewArg // * @param {number} detailArg // * @return undefined // */ //UIEvent.prototype.initUIEvent = function (typeArg, canBubbleArg, cancelableArg, viewArg, detailArg) {}; /** * @constructor * @extends {UIEvent} */ function MouseEvent() {} /** @type {number} */ MouseEvent.prototype.screenX; /** @type {number} */ MouseEvent.prototype.screenY; /** @type {number} */ MouseEvent.prototype.clientX; /** @type {number} */ MouseEvent.prototype.clientY; /** @type {boolean} */ MouseEvent.prototype.ctrlKey; /** @type {boolean} */ MouseEvent.prototype.shiftKey; /** @type {boolean} */ MouseEvent.prototype.altKey; /** @type {boolean} */ MouseEvent.prototype.metaKey; /** @type {number} */ MouseEvent.prototype.button; /** @type {EventTarget} */ MouseEvent.prototype.relatedTarget; /** * @constructor * @extends {Event} */ function MutationEvent() {} /** @type {Node} */ MutationEvent.prototype.relatedNode; /** @type {string} */ MutationEvent.prototype.prevValue; /** @type {string} */ MutationEvent.prototype.newValue; /** @type {string} */ MutationEvent.prototype.attrName; /** @type {number} */ MutationEvent.prototype.attrChange; /** * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {Node} relatedNodeArg * @param {string} prevValueArg * @param {string} newValueArg * @param {string} attrNameArg * @param {number} attrChangeArg * @return {undefined} */ MutationEvent.prototype.initMutationEvent = function(typeArg, canBubbleArg, cancelableArg, relatedNodeArg, prevValueArg, newValueArg, attrNameArg, attrChangeArg) {}; // DOM3 /** * @constructor * @extends {UIEvent} */ function KeyboardEvent() {} /** @type {string} */ KeyboardEvent.prototype.keyIdentifier; /** @type {boolean} */ KeyboardEvent.prototype.ctrlKey; /** @type {boolean} */ KeyboardEvent.prototype.shiftKey; /** @type {boolean} */ KeyboardEvent.prototype.altKey; /** @type {boolean} */ KeyboardEvent.prototype.metaKey; /** * @param {string} keyIdentifierArg * @return {boolean} */ KeyboardEvent.prototype.getModifierState = function(keyIdentifierArg) {}; closure-compiler-20130227+dfsg1/externs/w3c_xml.js0000644000175000017500000002503312115204405017757 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's XML related specifications. * This file depends on w3c_dom2.js. * The whole file has been fully type annotated. * * Provides the XML standards from W3C. * Includes: * XPath - Fully type annotated * XMLHttpRequest - Fully type annotated * * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html * @see http://www.w3.org/TR/XMLHttpRequest/ * @see http://www.w3.org/TR/XMLHttpRequest2/ * * @externs */ /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathException */ function XPathException() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#INVALID_EXPRESSION_ERR */ XPathException.INVALID_EXPRESSION_ERR = 52; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#TYPE_ERR */ XPathException.TYPE_ERR = 52; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html# */ XPathException.prototype.code; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator */ function XPathEvaluator() {} /** * @param {string} expr * @param {?XPathNSResolver=} opt_resolver * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-createExpression * @throws XPathException * @throws DOMException */ XPathEvaluator.prototype.createExpression = function(expr, opt_resolver) {}; /** * @param {Node} nodeResolver * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-createNSResolver */ XPathEvaluator.prototype.createNSResolver = function(nodeResolver) {}; /** * @param {string} expr * @param {Node} contextNode * @param {?XPathNSResolver=} opt_resolver * @param {?number=} opt_type * @param {*=} opt_result * @return {XPathResult} * @throws XPathException * @throws DOMException * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate */ XPathEvaluator.prototype.evaluate = function(expr, contextNode, opt_resolver, opt_type, opt_result) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathExpression */ function XPathExpression() {} /** * @param {Node} contextNode * @param {number=} opt_type * @param {*=} opt_result * @return {*} * @throws XPathException * @throws DOMException * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathExpression-evaluate */ XPathExpression.prototype.evaluate = function(contextNode, opt_type, opt_result) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNSResolver */ function XPathNSResolver() {} /** * @param {string} prefix * @return {?string} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNSResolver-lookupNamespaceURI */ XPathNSResolver.prototype.lookupNamespaceURI = function(prefix) {}; /** * From http://www.w3.org/TR/xpath * * XPath is a language for addressing parts of an XML document, designed to be * used by both XSLT and XPointer. * * @noalias * @constructor * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult */ function XPathResult() {} /** * @type {boolean} {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-booleanValue */ XPathResult.prototype.booleanValue; /** * @type {boolean} {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-invalid-iterator-state */ XPathResult.prototype.invalidInteratorState; /** * @type {number} * @throws XPathException {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-numberValue */ XPathResult.prototype.numberValue; /** * @type {number} * @throws XPathException {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-resultType */ XPathResult.prototype.resultType; /** * @type {Node} * @throws XPathException {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-singleNodeValue */ XPathResult.prototype.singleNodeValue; /** * @type {number} * @throws XPathException {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-snapshot-length */ XPathResult.prototype.snapshotLength; /** * @type {string} * @throws XPathException {@see XPathException.TYPE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-stringValue */ XPathResult.prototype.stringValue; /** * @return {Node} * @throws XPathException {@see XPathException.TYPE_ERR} * @throws DOMException {@see DOMException.INVALID_STATE_ERR} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-iterateNext */ XPathResult.prototype.iterateNext = function() {}; /** * @param {number} index * @return {Node} * @throws XPathException * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-snapshotItem */ XPathResult.prototype.snapshotItem = function(index) {}; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ANY-TYPE */ XPathResult.ANY_TYPE = 0; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-NUMBER-TYPE */ XPathResult.NUMBER_TYPE = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-STRING-TYPE */ XPathResult.STRING_TYPE = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-BOOLEAN-TYPE */ XPathResult.BOOLEAN_TYPE = 3; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-UNORDERED-NODE-ITERATOR-TYPE */ XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ORDERED-NODE-ITERATOR-TYPE */ XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-UNORDERED-NODE-SNAPSHOT-TYPE */ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE = 6; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ORDERED-NODE-SNAPSHOT-TYPE */ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-ANY-UNORDERED-NODE-TYPE */ XPathResult.ANY_UNORDERED_NODE_TYPE = 8; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathResult-FIRST-ORDERED-NODE-TYPE */ XPathResult.FIRST_ORDERED_NODE_TYPE = 9; /** * @constructor * @extends {Node} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNamespace */ function XPathNamespace() {} /** * @type {Element} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNamespace-ownerElement */ XPathNamespace.prototype.ownerElement; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPATH_NAMESPACE_NODE */ XPathNamespace.XPATH_NAMESPACE_NODE = 13; /** * From http://www.w3.org/TR/XMLHttpRequest/ * * (Draft) * * The XMLHttpRequest Object specification defines an API that provides * scripted client functionality for transferring data between a client and a * server. * * @constructor * @implements {EventTarget} * @see http://www.w3.org/TR/XMLHttpRequest/#xmlhttprequest-object */ function XMLHttpRequest() {} /** @override */ XMLHttpRequest.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ XMLHttpRequest.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ XMLHttpRequest.prototype.dispatchEvent = function(evt) {}; /** * @param {string} method * @param {string} url * @param {?boolean=} opt_async * @param {?string=} opt_user * @param {?string=} opt_password * @return {undefined} * @see http://www.w3.org/TR/XMLHttpRequest/#open */ XMLHttpRequest.prototype.open = function(method, url, opt_async, opt_user, opt_password) {}; /** * @param {string} header * @param {string} value * @return {undefined} * @see http://www.w3.org/TR/XMLHttpRequest/#setrequestheader */ XMLHttpRequest.prototype.setRequestHeader = function(header, value) {}; /** * @param {ArrayBuffer|Blob|Document|FormData|string=} opt_data * @return {undefined} * @see http://www.w3.org/TR/XMLHttpRequest/#send * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-send-method */ XMLHttpRequest.prototype.send = function(opt_data) {}; /** * @return {undefined} * @see http://www.w3.org/TR/XMLHttpRequest/#abort */ XMLHttpRequest.prototype.abort = function() {}; /** * @return {string} * @see http://www.w3.org/TR/XMLHttpRequest/#getallresponseheaders */ XMLHttpRequest.prototype.getAllResponseHeaders = function() {}; /** * @param {string} header * @return {string} * @see http://www.w3.org/TR/XMLHttpRequest/#getresponseheader */ XMLHttpRequest.prototype.getResponseHeader = function(header) {}; /** * @type {string} * @see http://www.w3.org/TR/XMLHttpRequest/#responsetext */ XMLHttpRequest.prototype.responseText; /** * @type {Document} * @see http://www.w3.org/TR/XMLHttpRequest/#responsexml */ XMLHttpRequest.prototype.responseXML; /** * @type {number} * @see http://www.w3.org/TR/XMLHttpRequest/#readystate */ XMLHttpRequest.prototype.readyState; /** * @type {number} * @see http://www.w3.org/TR/XMLHttpRequest/#status */ XMLHttpRequest.prototype.status; /** * @type {string} * @see http://www.w3.org/TR/XMLHttpRequest/#statustext */ XMLHttpRequest.prototype.statusText; /** * @type {Function} * @see http://www.w3.org/TR/XMLHttpRequest/#onreadystatechange */ XMLHttpRequest.prototype.onreadystatechange; /** * The FormData object represents an ordered collection of entries. Each entry * has a name and value. * * @param {?Element=} opt_form An optional form to use for constructing the form * data set. * @constructor * @see http://www.w3.org/TR/XMLHttpRequest2/#the-formdata-interface */ function FormData(opt_form) {} /** * @param {string} name * @param {Blob|string} value */ FormData.prototype.append = function(name, value) {}; closure-compiler-20130227+dfsg1/externs/gecko_css.js0000644000175000017500000001375312115204405020351 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for Gecko's custom CSS properties. Copied from: * http://mxr.mozilla.org/mozilla2.0/source/dom/interfaces/css/nsIDOMCSS2Properties.idl * * @externs * @author nicksantos@google.com (Nick Santos) */ /** @type {string} */ CSSProperties.prototype.MozAppearance; /** @type {string} */ CSSProperties.prototype.MozBackfaceVisibility; /** @type {string} */ CSSProperties.prototype.MozBackgroundClip; /** @type {string} */ CSSProperties.prototype.MozBackgroundInlinePolicy; /** @type {string} */ CSSProperties.prototype.MozBackgroundOrigin; /** @type {string} */ CSSProperties.prototype.MozBinding; /** @type {string} */ CSSProperties.prototype.MozBorderBottomColors; /** @type {string} */ CSSProperties.prototype.MozBorderEnd; /** @type {string} */ CSSProperties.prototype.MozBorderEndColor; /** @type {string} */ CSSProperties.prototype.MozBorderEndStyle; /** @type {string} */ CSSProperties.prototype.MozBorderEndWidth; /** @type {string} */ CSSProperties.prototype.MozBorderImage; /** @type {string} */ CSSProperties.prototype.MozBorderLeftColors; /** @type {string} */ CSSProperties.prototype.MozBorderRadius; /** @type {string} */ CSSProperties.prototype.MozBorderRadiusTopleft; /** @type {string} */ CSSProperties.prototype.MozBorderRadiusTopright; /** @type {string} */ CSSProperties.prototype.MozBorderRadiusBottomleft; /** @type {string} */ CSSProperties.prototype.MozBorderRadiusBottomright; /** @type {string} */ CSSProperties.prototype.MozBorderRightColors; /** @type {string} */ CSSProperties.prototype.MozBorderStart; /** @type {string} */ CSSProperties.prototype.MozBorderStartColor; /** @type {string} */ CSSProperties.prototype.MozBorderStartStyle; /** @type {string} */ CSSProperties.prototype.MozBorderStartWidth; /** @type {string} */ CSSProperties.prototype.MozBorderTopColors; /** @type {string} */ CSSProperties.prototype.MozBoxAlign; /** @type {string} */ CSSProperties.prototype.MozBoxDirection; /** @type {string} */ CSSProperties.prototype.MozBoxFlex; /** @type {string} */ CSSProperties.prototype.MozBoxOrdinalGroup; /** @type {string} */ CSSProperties.prototype.MozBoxOrient; /** @type {string} */ CSSProperties.prototype.MozBoxPack; /** @type {string} */ CSSProperties.prototype.MozBoxSizing; /** @type {string} */ CSSProperties.prototype.MozColumnCount; /** @type {string} */ CSSProperties.prototype.MozColumnGap; /** @type {string} */ CSSProperties.prototype.MozColumnRule; /** @type {string} */ CSSProperties.prototype.MozColumnRuleColor; /** @type {string} */ CSSProperties.prototype.MozColumnRuleStyle; /** @type {string} */ CSSProperties.prototype.MozColumnRuleWidth; /** @type {string} */ CSSProperties.prototype.MozColumnWidth; /** @type {string} */ CSSProperties.prototype.MozFloatEdge; /** @type {string} */ CSSProperties.prototype.MozFontFeatureSettings; /** @type {string} */ CSSProperties.prototype.MozFontLanguageOverride; /** @type {string} */ CSSProperties.prototype.MozForceBrokenImageIcon; /** @type {string} */ CSSProperties.prototype.MozImageRegion; /** @type {string} */ CSSProperties.prototype.MozMarginEnd; /** @type {string} */ CSSProperties.prototype.MozMarginStart; /** @type {number|string} */ CSSProperties.prototype.MozOpacity; /** @type {string} */ CSSProperties.prototype.MozOutline; /** @type {string} */ CSSProperties.prototype.MozOutlineColor; /** @type {string} */ CSSProperties.prototype.MozOutlineOffset; /** @type {string} */ CSSProperties.prototype.MozOutlineRadius; /** @type {string} */ CSSProperties.prototype.MozOutlineRadiusBottomleft; /** @type {string} */ CSSProperties.prototype.MozOutlineRadiusBottomright; /** @type {string} */ CSSProperties.prototype.MozOutlineRadiusTopleft; /** @type {string} */ CSSProperties.prototype.MozOutlineRadiusTopright; /** @type {string} */ CSSProperties.prototype.MozOutlineStyle; /** @type {string} */ CSSProperties.prototype.MozOutlineWidth; /** @type {string} */ CSSProperties.prototype.MozPaddingEnd; /** @type {string} */ CSSProperties.prototype.MozPaddingStart; /** @type {string} */ CSSProperties.prototype.MozPerspective; /** @type {string} */ CSSProperties.prototype.MozStackSizing; /** @type {string} */ CSSProperties.prototype.MozTabSize; /** @type {string} */ CSSProperties.prototype.MozTransform; /** @type {string} */ CSSProperties.prototype.MozTransformOrigin; /** @type {string} */ CSSProperties.prototype.MozTransition; /** @type {string} */ CSSProperties.prototype.MozTransitionDelay; /** @type {string} */ CSSProperties.prototype.MozTransitionDuration; /** @type {string} */ CSSProperties.prototype.MozTransitionProperty; /** @type {string} */ CSSProperties.prototype.MozTransitionTimingFunction; /** @type {string} */ CSSProperties.prototype.MozUserFocus; /** @type {string} */ CSSProperties.prototype.MozUserInput; /** @type {string} */ CSSProperties.prototype.MozUserModify; /** @type {string} */ CSSProperties.prototype.MozUserSelect; /** @type {string} */ CSSProperties.prototype.MozWindowShadow; // These are non-standard Gecko CSSOM properties on Window.prototype.screen. /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.screen.availTop */ Screen.prototype.availTop; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.screen.availLeft */ Screen.prototype.availLeft; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.screen.left */ Screen.prototype.left; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/window.screen.top */ Screen.prototype.top; closure-compiler-20130227+dfsg1/externs/w3c_indexeddb.js0000644000175000017500000004342712115204405021114 0ustar apoapo/* * Copyright 2011 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. */ /** * @fileoverview Definitions for W3C's IndexedDB API. In Chrome all the * IndexedDB classes are prefixed with 'webkit'. In order to access constants * and static methods of these classes they must be duplicated with the * prefix here. * @see http://www.w3.org/TR/IndexedDB/ * * @externs * @author guido.tapia@picnet.com.au (Guido Tapia) */ /** @type {IDBFactory} */ Window.prototype.moz_indexedDB; /** @type {IDBFactory} */ Window.prototype.mozIndexedDB; /** @type {IDBFactory} */ Window.prototype.webkitIndexedDB; /** @type {IDBFactory} */ Window.prototype.msIndexedDB; /** @type {IDBFactory} */ Window.prototype.indexedDB; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBFactory */ function IDBFactory() {} /** * @param {string} name The name of the database to open. * @param {number=} opt_version The version at which to open the database. * @return {!IDBOpenDBRequest} The IDBRequest object. */ IDBFactory.prototype.open = function(name, opt_version) {}; /** * @param {string} name The name of the database to delete. * @return {!IDBOpenDBRequest} The IDBRequest object. */ IDBFactory.prototype.deleteDatabase = function(name) {}; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabaseException */ function IDBDatabaseException() {} /** * @constructor * @extends {IDBDatabaseException} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabaseException */ function webkitIDBDatabaseException() {} /** * @const * @type {number} */ IDBDatabaseException.UNKNOWN_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.UNKNOWN_ERR; /** * @const * @type {number} */ IDBDatabaseException.NON_TRANSIENT_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.NON_TRANSIENT_ERR; /** * @const * @type {number} */ IDBDatabaseException.NOT_FOUND_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.NOT_FOUND_ERR; /** * @const * @type {number} */ IDBDatabaseException.CONSTRAINT_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.CONSTRAINT_ERR; /** * @const * @type {number} */ IDBDatabaseException.DATA_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.DATA_ERR; /** * @const * @type {number} */ IDBDatabaseException.NOT_ALLOWED_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.NOT_ALLOWED_ERR; /** * @const * @type {number} */ IDBDatabaseException.TRANSACTION_INACTIVE_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.TRANSACTION_INACTIVE_ERR; /** * @const * @type {number} */ IDBDatabaseException.ABORT_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.ABORT_ERR; /** * @const * @type {number} */ IDBDatabaseException.READ_ONLY_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.READ_ONLY_ERR; /** * @const * @type {number} */ IDBDatabaseException.TIMEOUT_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.TIMEOUT_ERR; /** * @const * @type {number} */ IDBDatabaseException.QUOTA_ERR; /** * @const * @type {number} */ webkitIDBDatabaseException.QUOTA_ERR; /** * @const * @type {number} */ IDBDatabaseException.code; /** * @const * @type {number} */ webkitIDBDatabaseException.code; /** * @const * @type {string} */ IDBDatabaseException.message; /** * @const * @type {string} */ webkitIDBDatabaseException.message; /** @type {function(new:IDBRequest)} */ Window.prototype.IDBRequest; /** @type {function(new:IDBRequest)} */ Window.prototype.webkitIDBRequest; /** * @constructor * @implements {EventTarget} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest */ function IDBRequest() {} /** @override */ IDBRequest.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ IDBRequest.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ IDBRequest.prototype.dispatchEvent = function(evt) {}; /** * @constructor * @extends {IDBRequest} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBRequest */ function webkitIDBRequest() {} /** * @type {number} * @const */ IDBRequest.LOADING; /** * @type {number} * @const */ webkitIDBRequest.LOADING; /** * @type {number} * @const */ IDBRequest.DONE; /** * @type {number} * @const */ webkitIDBRequest.DONE; /** * @type {number} * @const */ IDBRequest.prototype.readyState; /** * @type {function(!Event)} */ IDBRequest.prototype.onsuccess = function(e) {}; /** * @type {function(!Event)} */ IDBRequest.prototype.onerror = function(e) {}; /** * @type {*} * @const */ IDBRequest.prototype.result; /** * @type {number} * @const */ IDBRequest.prototype.errorCode; /** * @type {Object} * @const */ IDBRequest.prototype.source; /** * @type {IDBTransaction} * @const */ IDBRequest.prototype.transaction; /** @type {function(new:IDBOpenDBRequest)} */ Window.prototype.IDBOpenDBRequest; /** * @constructor * @extends {IDBRequest} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBOpenDBRequest */ function IDBOpenDBRequest() {} /** * @type {function(!IDBVersionChangeEvent)} */ IDBOpenDBRequest.prototype.onblocked = function(e) {}; /** * @type {function(!IDBVersionChangeEvent)} */ IDBOpenDBRequest.prototype.onupgradeneeded = function(e) {}; /** * @constructor * @implements {EventTarget} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBDatabase */ function IDBDatabase() {} /** * @type {string} * @const */ IDBDatabase.prototype.name; /** * @type {string} * @const */ IDBDatabase.prototype.description; /** * @type {string} * @const */ IDBDatabase.prototype.version; /** * @type {DOMStringList} * @const */ IDBDatabase.prototype.objectStoreNames; /** * @param {string} name The name of the object store. * @param {Object=} opt_parameters Parameters to be passed * creating the object store. * @return {!IDBObjectStore} The created/open object store. */ IDBDatabase.prototype.createObjectStore = function(name, opt_parameters) {}; /** * @param {string} name The name of the object store to remove. */ IDBDatabase.prototype.deleteObjectStore = function(name) {}; /** * @param {string} version The new version of the database. * @return {!IDBRequest} The IDBRequest object. */ IDBDatabase.prototype.setVersion = function(version) {}; /** * @param {Array.} storeNames The stores to open in this transaction. * @param {(number|string)=} mode The mode for opening the object stores. * @return {!IDBTransaction} The IDBRequest object. */ IDBDatabase.prototype.transaction = function(storeNames, mode) {}; /** * Closes the database connection. */ IDBDatabase.prototype.close = function() {}; /** * @type {Function} */ IDBDatabase.prototype.onabort = function() {}; /** * @type {Function} */ IDBDatabase.prototype.onerror = function() {}; /** * @type {Function} */ IDBDatabase.prototype.onversionchange = function() {}; /** @override */ IDBDatabase.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ IDBDatabase.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ IDBDatabase.prototype.dispatchEvent = function(evt) {}; /** * Typedef for valid key types according to the w3 specification. Note that this * is slightly wider than what is actually allowed, as all Array elements must * have a valid key type. * @see http://www.w3.org/TR/IndexedDB/#key-construct * @typedef {number|string|!Date|!Array} */ var IDBKeyType; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBObjectStore */ function IDBObjectStore() {} /** * @type {string} */ IDBObjectStore.prototype.name; /** * @type {string} */ IDBObjectStore.prototype.keyPath; /** * @type {DOMStringList} */ IDBObjectStore.prototype.indexNames; /** @type {IDBTransaction} */ IDBObjectStore.prototype.transaction; /** @type {boolean} */ IDBObjectStore.prototype.autoIncrement; /** * @param {*} value The value to put into the object store. * @param {IDBKeyType=} key The key of this value. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.put = function(value, key) {}; /** * @param {*} value The value to add into the object store. * @param {IDBKeyType=} key The key of this value. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.add = function(value, key) {}; /** * @param {IDBKeyType} key The key of this value. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype['delete'] = function(key) {}; /** * @param {IDBKeyType} key The key of the document to retrieve. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.get = function(key) {}; /** * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.clear = function() {}; /** * @param {IDBKeyRange=} range The range of the cursor. * @param {(number|string)=} direction The direction of cursor enumeration. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.openCursor = function(range, direction) {}; /** * @param {string} name The name of the index. * @param {string} keyPath The path to the index key. * @param {Object=} opt_paramters Optional parameters * for the created index. * @return {!IDBIndex} The IDBIndex object. */ IDBObjectStore.prototype.createIndex = function(name, keyPath, opt_paramters) {}; /** * @param {string} name The name of the index to retrieve. * @return {!IDBIndex} The IDBIndex object. */ IDBObjectStore.prototype.index = function(name) {}; /** * @param {string} indexName The name of the index to remove. */ IDBObjectStore.prototype.deleteIndex = function(indexName) {}; /** * @param {IDBKeyType=} key The key of this value. * @return {!IDBRequest} The IDBRequest object. */ IDBObjectStore.prototype.count = function(key) {}; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBIndex */ function IDBIndex() {} /** * @type {string} * @const */ IDBIndex.prototype.name; /** * @type {!IDBObjectStore} * @const */ IDBIndex.prototype.objectStore; /** * @type {string} * @const */ IDBIndex.prototype.keyPath; /** * @type {boolean} * @const */ IDBIndex.prototype.unique; /** * @param {IDBKeyRange=} range The range of the cursor. * @param {(number|string)=} direction The direction of cursor enumeration. * @return {!IDBRequest} The IDBRequest object. */ IDBIndex.prototype.openCursor = function(range, direction) {}; /** * @param {IDBKeyRange=} range The range of the cursor. * @param {(number|string)=} direction The direction of cursor enumeration. * @return {!IDBRequest} The IDBRequest object. */ IDBIndex.prototype.openKeyCursor = function(range, direction) {}; /** * @param {IDBKeyType} key The id of the object to retrieve. * @return {!IDBRequest} The IDBRequest object. */ IDBIndex.prototype.get = function(key) {}; /** * @param {IDBKeyType} key The id of the object to retrieve. * @return {!IDBRequest} The IDBRequest object. */ IDBIndex.prototype.getKey = function(key) {}; /** @type {function(new:IDBCursor)} */ Window.prototype.IDBCursor; /** @type {function(new:IDBCursor)} */ Window.prototype.webkitIDBCursor; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursor */ function IDBCursor() {} /** * @constructor * @extends {IDBCursor} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBCursor */ function webkitIDBCursor() {} /** * @const * @type {number} */ IDBCursor.NEXT; /** * @const * @type {number} */ webkitIDBCursor.NEXT; /** * @const * @type {number} */ IDBCursor.NEXT_NO_DUPLICATE; /** * @const * @type {number} */ webkitIDBCursor.NEXT_NO_DUPLICATE; /** * @const * @type {number} */ IDBCursor.PREV; /** * @const * @type {number} */ webkitIDBCursor.PREV; /** * @const * @type {number} */ IDBCursor.PREV_NO_DUPLICATE; /** * @const * @type {number} */ webkitIDBCursor.PREV_NO_DUPLICATE; /** * @type {*} * @const */ IDBCursor.prototype.source; /** * @type {number} * @const */ IDBCursor.prototype.direction; /** * @type {IDBKeyType} * @const */ IDBCursor.prototype.key; /** * @type {number} * @const */ IDBCursor.prototype.primaryKey; /** * @param {*} value The new value for the current object in the cursor. * @return {!IDBRequest} The IDBRequest object. */ IDBCursor.prototype.update = function(value) {}; /** * Note: Must be quoted to avoid parse error. * @param {IDBKeyType=} key Continue enumerating the cursor from the specified * key (or next). */ IDBCursor.prototype['continue'] = function(key) {}; /** * @param {number} count Number of times to iterate the cursor. */ IDBCursor.prototype.advance = function(count) {}; /** * Note: Must be quoted to avoid parse error. * @return {!IDBRequest} The IDBRequest object. */ IDBCursor.prototype['delete'] = function() {}; /** @type {function(new:IDBTransaction)} */ Window.prototype.IDBTransaction; /** @type {function(new:IDBTransaction)} */ Window.prototype.webkitIDBTransaction; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction */ function IDBTransaction() {} /** * @constructor * @extends {IDBTransaction} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBTransaction */ function webkitIDBTransaction() {} /** * @const * @type {number} */ IDBTransaction.READ_WRITE; /** * @const * @type {number} */ webkitIDBTransaction.READ_WRITE; /** * @const * @type {number} */ IDBTransaction.READ_ONLY; /** * @const * @type {number} */ webkitIDBTransaction.READ_ONLY; /** * @const * @type {number} */ IDBTransaction.VERSION_CHANGE; /** * @const * @type {number} */ webkitIDBTransaction.VERSION_CHANGE; /** * @type {number|string} * @const */ IDBTransaction.prototype.mode; /** * @type {IDBDatabase} * @const */ IDBTransaction.prototype.db; /** * @param {string} name The name of the object store to retrieve. * @return {!IDBObjectStore} The object store. */ IDBTransaction.prototype.objectStore = function(name) {}; /** * Aborts the transaction. */ IDBTransaction.prototype.abort = function() {}; /** * @type {Function} */ IDBTransaction.prototype.onabort = function() {}; /** * @type {Function} */ IDBTransaction.prototype.oncomplete = function() {}; /** * @type {Function} */ IDBTransaction.prototype.onerror = function() {}; /** @type {function(new:IDBKeyRange)} */ Window.prototype.IDBKeyRange; /** @type {function(new:IDBKeyRange)} */ Window.prototype.webkitIDBKeyRange; /** * @constructor * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange */ function IDBKeyRange() {} /** * @constructor * @extends {IDBKeyRange} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange */ function webkitIDBKeyRange() {} /** * @type {*} * @const */ IDBKeyRange.prototype.lower; /** * @type {*} * @const */ IDBKeyRange.prototype.upper; /** * @type {*} * @const */ IDBKeyRange.prototype.lowerOpen; /** * @type {*} * @const */ IDBKeyRange.prototype.upperOpen; /** * @param {IDBKeyType} value The single key value of this range. * @return {!IDBKeyRange} The key range. */ IDBKeyRange.only = function(value) {}; /** * @param {IDBKeyType} value The single key value of this range. * @return {!IDBKeyRange} The key range. */ webkitIDBKeyRange.only = function(value) {}; /** * @param {IDBKeyType} bound Creates a lower bound key range. * @param {boolean=} open Open the key range. * @return {!IDBKeyRange} The key range. */ IDBKeyRange.lowerBound = function(bound, open) {}; /** * @param {IDBKeyType} bound Creates a lower bound key range. * @param {boolean=} open Open the key range. * @return {!IDBKeyRange} The key range. */ webkitIDBKeyRange.lowerBound = function(bound, open) {}; /** * @param {IDBKeyType} bound Creates an upper bound key range. * @param {boolean=} open Open the key range. * @return {!IDBKeyRange} The key range. */ IDBKeyRange.upperBound = function(bound, open) {}; /** * @param {IDBKeyType} bound Creates an upper bound key range. * @param {boolean=} open Open the key range. * @return {!IDBKeyRange} The key range. */ webkitIDBKeyRange.upperBound = function(bound, open) {}; /** * @param {IDBKeyType} left The left bound value. * @param {IDBKeyType} right The right bound value. * @param {boolean=} openLeft Whether the left bound value should be excluded. * @param {boolean=} openRight Whether the right bound value should be excluded. * @return {!IDBKeyRange} The key range. */ IDBKeyRange.bound = function(left, right, openLeft, openRight) {}; /** * @param {IDBKeyType} left The left bound value. * @param {IDBKeyType} right The right bound value. * @param {boolean=} openLeft Whether the left bound value should be excluded. * @param {boolean=} openRight Whether the right bound value should be excluded. * @return {!IDBKeyRange} The key range. */ webkitIDBKeyRange.bound = function(left, right, openLeft, openRight) {}; /** * @constructor * @extends {Event} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBVersionChangeEvent */ function IDBVersionChangeEvent() {} /** * @type {number} * @const */ IDBVersionChangeEvent.prototype.oldVersion; /** * @type {?number} * @const */ IDBVersionChangeEvent.prototype.newVersion; /** * @constructor * @extends {IDBVersionChangeEvent} * @see http://www.w3.org/TR/IndexedDB/#idl-def-IDBVersionChangeEvent */ function webkitIDBVersionChangeEvent() {} /** * @type {string} * @const */ webkitIDBVersionChangeEvent.prototype.version; closure-compiler-20130227+dfsg1/externs/es5.js0000644000175000017500000001533412115204405017102 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for ECMAScript 5. * @see http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf * @externs */ /** * @param {Object} selfObj Specifies the object to which |this| should point * when the function is run. If the value is null or undefined, it will * default to the global object. * @param {...*} var_args Additional arguments that are partially * applied to fn. * @return {!Function} A partially-applied form of the Function on which * bind() was invoked as a method. * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind */ Function.prototype.bind = function(selfObj, var_args) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim */ String.prototype.trim = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimLeft */ String.prototype.trimLeft = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimRight */ String.prototype.trimRight = function() {}; /** * A object property descriptor used by Object.create, Object.defineProperty, * Object.defineProperties, Object.getOwnPropertyDescriptor. * * Note: not a real constructor. * @constructor */ var ObjectPropertyDescriptor = function(){}; /** @type {*} */ ObjectPropertyDescriptor.prototype.value; /** @type {(function():?)||undefined} */ ObjectPropertyDescriptor.prototype.get; /** @type {(function(?):void)||undefined} */ ObjectPropertyDescriptor.prototype.set; /** @type {boolean|undefined} */ ObjectPropertyDescriptor.prototype.writable; /** @type {boolean|undefined} */ ObjectPropertyDescriptor.prototype.enumerable; /** @type {boolean|undefined} */ ObjectPropertyDescriptor.prototype.configurable; /** * @param {Object} proto * @param {Object=} opt_properties A map of ObjectPropertyDescriptors. * @return {!Object} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create */ Object.create = function(proto, opt_properties) {}; /** * @param {!Object} obj * @param {string} prop * @param {!Object} descriptor A ObjectPropertyDescriptor. * @return {!Object} * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty */ Object.defineProperty = function(obj, prop, descriptor) {}; /** * @param {!Object} obj * @param {!Object} props A map of ObjectPropertyDescriptors. * @return {!Object} * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties */ Object.defineProperties = function(obj, props) {}; /** * @param {!Object} obj * @param {string} prop * @return {Object.} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor */ Object.getOwnPropertyDescriptor = function(obj, prop) {}; /** * @param {!Object} obj * @return {Array.} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys */ Object.keys = function(obj) {}; /** * @param {!Object} obj * @return {Array.} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames */ Object.getOwnPropertyNames = function(obj) {}; /** * @param {!Object} obj * @return {Object} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf */ Object.getPrototypeOf = function(obj) {}; /** * @param {!T} obj * @return {!T} * @template T * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/preventExtensions */ Object.preventExtensions = function(obj) {}; /** * @param {!T} obj * @return {!T} * @template T * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/seal */ Object.seal = function(obj) {}; /** * @param {!T} obj * @return {!T} * @template T * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze */ Object.freeze = function(obj) {}; /** * @param {!Object} obj * @return {boolean} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isExtensible */ Object.isExtensible = function(obj) {}; /** * @param {!Object} obj * @return {boolean} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isSealed */ Object.isSealed = function(obj) {}; /** * @param {!Object} obj * @return {boolean} * @nosideeffects * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isFrozen */ Object.isFrozen = function(obj) {}; /** * As per ECMAScript 5, 15.12.3. * @param {string=} opt_key The JSON key for this object. * @return {*} The serializable representation of this object. Note that this * need not be a string. See http://goo.gl/PEUvs. */ Object.prototype.toJSON = function(opt_key) {}; /** * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString * @return {string} */ Date.prototype.toISOString = function() {}; /** * @param {*=} opt_ignoredKey * @return {string} * @override */ Date.prototype.toJSON = function(opt_ignoredKey) {}; /** * A fake type to model the JSON object. * @constructor */ var JSONType = function() {}; /** * @param {string} jsonStr The string to parse. * @param {(function(string, *) : *)=} opt_reviver * @return {*} The JSON object. * @throws {Error} * @nosideeffects */ JSONType.prototype.parse = function(jsonStr, opt_reviver) {}; /** * @param {*} jsonObj Input object. * @param {(Array.|(function(string, *) : *)|null)=} opt_replacer * @param {(number|string)=} opt_space * @return {string} JSON string which represents jsonObj. * @throws {Error} * @nosideeffects */ JSONType.prototype.stringify = function(jsonObj, opt_replacer, opt_space) {}; /** * @type {!JSONType} * @suppress {duplicate} */ var JSON; closure-compiler-20130227+dfsg1/externs/webkit_event.js0000644000175000017500000000247112115204405021072 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for all the extensions over W3C's * event specification by WebKit. This file depends on w3c_event.js. * All the provided definitions have been type annotated * * @externs */ /** @type {number} */ Event.prototype.wheelDeltaX; /** @type {number} */ Event.prototype.wheelDeltaY; /** * @constructor * @extends {Event} * @see http://developer.apple.com/library/safari/documentation/AudioVideo/Reference/WebKitAnimationEventClassReference/WebKitAnimationEvent/WebKitAnimationEvent.html */ function WebKitAnimationEvent() {} /** * @type {string} * @const */ WebKitAnimationEvent.prototype.animationName; /** * @type {number} * @const */ WebKitAnimationEvent.prototype.elapsedTime;closure-compiler-20130227+dfsg1/externs/fileapi.js0000644000175000017500000005615512115204405020025 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Definitions for objects in the File API, File Writer API, and * File System API. Details of the API are at: * http://www.w3.org/TR/FileAPI/ * http://www.w3.org/TR/file-writer-api/ * http://www.w3.org/TR/file-system-api/ * * @externs * @author dbk@google.com (David Barrett-Kahn) * @author mpd@google.com (Michael Davidson) */ /** * @see http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob * @param {Array.=} opt_blobParts * @param {Object=} opt_options * @constructor * @nosideeffects */ function Blob(opt_blobParts, opt_options) {} /** * @see http://www.w3.org/TR/FileAPI/#dfn-size * @type {number} */ Blob.prototype.size; /** * @see http://www.w3.org/TR/FileAPI/#dfn-type * @type {string} */ Blob.prototype.type; /** * @see http://www.w3.org/TR/FileAPI/#dfn-slice * @param {number} start * @param {number} length * @return {!Blob} * @nosideeffects */ Blob.prototype.slice = function(start, length) {}; /** * This replaces Blob.slice in Chrome since WebKit revision 84005. * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html * @param {number} start * @param {number} end * @return {!Blob} * @nosideeffects */ Blob.prototype.webkitSlice = function(start, end) {}; /** * This replaces Blob.slice in Firefox. * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html * @param {number} start * @param {number} end * @return {!Blob} * @nosideeffects */ Blob.prototype.mozSlice = function(start, end) {}; /** * @see http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob * @type {function(new:Blob, Array.=, Object=)} */ Window.prototype.Blob; /** * @see http://www.w3.org/TR/file-writer-api/#the-blobbuilder-interface * @constructor */ function BlobBuilder() {} /** * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append0 * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append1 * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append2 * @param {string|Blob|ArrayBuffer} data * @param {string=} endings */ BlobBuilder.prototype.append = function(data, endings) {}; /** * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-getBlob * @param {string=} contentType * @return {!Blob} */ BlobBuilder.prototype.getBlob = function(contentType) {}; /** * @see http://www.w3.org/TR/file-writer-api/#the-blobbuilder-interface * @type {function(new:BlobBuilder)} */ Window.prototype.BlobBuilder; /** * This has replaced BlobBuilder in Chrome since WebKit revision 84008. * @see http://lists.w3.org/Archives/Public/public-webapps/2011AprJun/0222.html * @constructor */ function WebKitBlobBuilder() {} /** * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append0 * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append1 * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-append2 * @param {string|Blob|ArrayBuffer} data * @param {string=} endings */ WebKitBlobBuilder.prototype.append = function(data, endings) {}; /** * @see http://www.w3.org/TR/file-writer-api/#widl-BlobBuilder-getBlob * @param {string=} contentType * @return {!Blob} */ WebKitBlobBuilder.prototype.getBlob = function(contentType) {}; /** * @see http://www.w3.org/TR/file-writer-api/#the-blobbuilder-interface * @type {function(new:WebKitBlobBuilder)} */ Window.prototype.WebKitBlobBuilder; /** * @see http://www.w3.org/TR/file-system-api/#the-directoryentry-interface * @constructor * @extends {Entry} */ function DirectoryEntry() {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-createReader * @return {!DirectoryReader} */ DirectoryEntry.prototype.createReader = function() {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-getFile * @param {string} path * @param {Object=} options * @param {function(!FileEntry)=} successCallback * @param {function(!FileError)=} errorCallback */ DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-getDirectory * @param {string} path * @param {Object=} options * @param {function(!DirectoryEntry)=} successCallback * @param {function(!FileError)=} errorCallback */ DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryEntry-removeRecursively * @param {function()} successCallback * @param {function(!FileError)=} errorCallback */ DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#the-directoryreader-interface * @constructor */ function DirectoryReader() {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-DirectoryReader-readEntries * @param {function(!Array.)} successCallback * @param {function(!FileError)=} errorCallback */ DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#the-entry-interface * @constructor */ function Entry() {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-isFile * @type {boolean} */ Entry.prototype.isFile; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-isDirectory * @type {boolean} */ Entry.prototype.isDirectory; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-name * @type {string} */ Entry.prototype.name; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-fullPath * @type {string} */ Entry.prototype.fullPath; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-filesystem * @type {!FileSystem} */ Entry.prototype.filesystem; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-moveTo * @param {!DirectoryEntry} parent * @param {string=} newName * @param {function(!Entry)=} successCallback * @param {function(!FileError)=} errorCallback */ Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-copyTo * @param {!DirectoryEntry} parent * @param {string=} newName * @param {function(!Entry)=} successCallback * @param {function(!FileError)=} errorCallback */ Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-toURL * @param {string=} mimeType * @return {string} */ Entry.prototype.toURL = function(mimeType) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-remove * @param {function()} successCallback * @param {function(!FileError)=} errorCallback */ Entry.prototype.remove = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-getMetadata * @param {function(!Metadata)} successCallback * @param {function(!FileError)=} errorCallback */ Entry.prototype.getMetadata = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-Entry-getParent * @param {function(!Entry)} successCallback * @param {function(!FileError)=} errorCallback */ Entry.prototype.getParent = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-file * @constructor * @extends {Blob} */ function File() {} /** * Chrome uses this instead of name. * @deprecated Use name instead. * @type {string} */ File.prototype.fileName; /** * Chrome uses this instead of size. * @deprecated Use size instead. * @type {string} */ File.prototype.fileSize; /** * @see http://www.w3.org/TR/FileAPI/#dfn-name * @type {string} */ File.prototype.name; /** * @see http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate * @type {Date} */ File.prototype.lastModifiedDate; /** * @see http://www.w3.org/TR/file-system-api/#the-fileentry-interface * @constructor * @extends {Entry} */ function FileEntry() {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileEntry-createWriter * @param {function(!FileWriter)} successCallback * @param {function(!FileError)=} errorCallback */ FileEntry.prototype.createWriter = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileEntry-file * @param {function(!File)} successCallback * @param {function(!FileError)=} errorCallback */ FileEntry.prototype.file = function(successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/FileAPI/#FileErrorInterface * @constructor */ function FileError() {} /** * @see http://www.w3.org/TR/FileAPI/#dfn-NOT_FOUND_ERR * @type {number} */ FileError.prototype.NOT_FOUND_ERR = 1; /** @type {number} */ FileError.NOT_FOUND_ERR = 1; /** * @see http://www.w3.org/TR/FileAPI/#dfn-SECURITY_ERR * @type {number} */ FileError.prototype.SECURITY_ERR = 2; /** @type {number} */ FileError.SECURITY_ERR = 2; /** * @see http://www.w3.org/TR/FileAPI/#dfn-ABORT_ERR * @type {number} */ FileError.prototype.ABORT_ERR = 3; /** @type {number} */ FileError.ABORT_ERR = 3; /** * @see http://www.w3.org/TR/FileAPI/#dfn-NOT_READABLE_ERR * @type {number} */ FileError.prototype.NOT_READABLE_ERR = 4; /** @type {number} */ FileError.NOT_READABLE_ERR = 4; /** * @see http://www.w3.org/TR/FileAPI/#dfn-ENCODING_ERR * @type {number} */ FileError.prototype.ENCODING_ERR = 5; /** @type {number} */ FileError.ENCODING_ERR = 5; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileError-NO_MODIFICATION_ALLOWED_ERR * @type {number} */ FileError.prototype.NO_MODIFICATION_ALLOWED_ERR = 6; /** @type {number} */ FileError.NO_MODIFICATION_ALLOWED_ERR = 6; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileException-INVALID_STATE_ERR * @type {number} */ FileError.prototype.INVALID_STATE_ERR = 7; /** @type {number} */ FileError.INVALID_STATE_ERR = 7; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileException-SYNTAX_ERR * @type {number} */ FileError.prototype.SYNTAX_ERR = 8; /** @type {number} */ FileError.SYNTAX_ERR = 8; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileError-INVALID_MODIFICATION_ERR * @type {number} */ FileError.prototype.INVALID_MODIFICATION_ERR = 9; /** @type {number} */ FileError.INVALID_MODIFICATION_ERR = 9; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileError-QUOTA_EXCEEDED_ERR * @type {number} */ FileError.prototype.QUOTA_EXCEEDED_ERR = 10; /** @type {number} */ FileError.QUOTA_EXCEEDED_ERR = 10; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileException-TYPE_MISMATCH_ERR * @type {number} */ FileError.prototype.TYPE_MISMATCH_ERR = 11; /** @type {number} */ FileError.TYPE_MISMATCH_ERR = 11; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileException-PATH_EXISTS_ERR * @type {number} */ FileError.prototype.PATH_EXISTS_ERR = 12; /** @type {number} */ FileError.PATH_EXISTS_ERR = 12; /** * @see http://www.w3.org/TR/FileAPI/#dfn-code-exception * @type {number} */ FileError.prototype.code; /** * @see http://www.w3.org/TR/FileAPI/#dfn-filereader * @constructor * @implements {EventTarget} */ function FileReader() {} /** @override */ FileReader.prototype.addEventListener = function(type, listener, useCapture) {}; /** @override */ FileReader.prototype.removeEventListener = function(type, listener, useCapture) {}; /** @override */ FileReader.prototype.dispatchEvent = function(evt) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-readAsArrayBuffer * @param {!Blob} blob */ FileReader.prototype.readAsArrayBuffer = function(blob) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-readAsBinaryStringAsync * @param {!Blob} blob */ FileReader.prototype.readAsBinaryString = function(blob) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-readAsText * @param {!Blob} blob * @param {string=} encoding */ FileReader.prototype.readAsText = function(blob, encoding) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-readAsDataURL * @param {!Blob} blob */ FileReader.prototype.readAsDataURL = function(blob) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-abort */ FileReader.prototype.abort = function() {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-empty * @type {number} */ FileReader.prototype.EMPTY = 0; /** * @see http://www.w3.org/TR/FileAPI/#dfn-loading * @type {number} */ FileReader.prototype.LOADING = 1; /** * @see http://www.w3.org/TR/FileAPI/#dfn-done * @type {number} */ FileReader.prototype.DONE = 2; /** * @see http://www.w3.org/TR/FileAPI/#dfn-readystate * @type {number} */ FileReader.prototype.readyState; /** * @see http://www.w3.org/TR/FileAPI/#dfn-result * @type {string|Blob|ArrayBuffer} */ FileReader.prototype.result; /** * @see http://www.w3.org/TR/FileAPI/#dfn-error * @type {FileError} */ FileReader.prototype.error; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onloadstart * @type {?function(!ProgressEvent)} */ FileReader.prototype.onloadstart; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onprogress * @type {?function(!ProgressEvent)} */ FileReader.prototype.onprogress; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onload * @type {?function(!ProgressEvent)} */ FileReader.prototype.onload; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onabort * @type {?function(!ProgressEvent)} */ FileReader.prototype.onabort; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onerror * @type {?function(!ProgressEvent)} */ FileReader.prototype.onerror; /** * @see http://www.w3.org/TR/FileAPI/#dfn-onloadend * @type {?function(!ProgressEvent)} */ FileReader.prototype.onloadend; /** * @see http://www.w3.org/TR/file-writer-api/#idl-def-FileSaver * @constructor */ function FileSaver() {}; /** @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-abort */ FileSaver.prototype.abort = function() {}; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-INIT * @type {number} */ FileSaver.prototype.INIT = 0; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-WRITING * @type {number} */ FileSaver.prototype.WRITING = 1; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-DONE * @type {number} */ FileSaver.prototype.DONE = 2; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-readyState * @type {number} */ FileSaver.prototype.readyState; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-error * @type {FileError} */ FileSaver.prototype.error; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwritestart * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onwritestart; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onprogress * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onprogress; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwrite * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onwrite; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onabort * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onabort; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onerror * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onerror; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileSaver-onwriteend * @type {?function(!ProgressEvent)} */ FileSaver.prototype.onwriteend; /** * @see http://www.w3.org/TR/file-system-api/#the-filesystem-interface * @constructor */ function FileSystem() {} /** * @see http://www.w3.org/TR/file-system-api/#widl-FileSystem-name * @type {string} */ FileSystem.prototype.name; /** * @see http://www.w3.org/TR/file-system-api/#widl-FileSystem-root * @type {!DirectoryEntry} */ FileSystem.prototype.root; /** * @see http://www.w3.org/TR/file-writer-api/#idl-def-FileWriter * @constructor * @extends {FileSaver} */ function FileWriter() {} /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-position * @type {number} */ FileWriter.prototype.position; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-length * @type {number} */ FileWriter.prototype.length; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-write * @param {!Blob} blob */ FileWriter.prototype.write = function(blob) {}; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-seek * @param {number} offset */ FileWriter.prototype.seek = function(offset) {}; /** * @see http://www.w3.org/TR/file-writer-api/#widl-FileWriter-truncate * @param {number} size */ FileWriter.prototype.truncate = function(size) {}; /** * LocalFileSystem interface, implemented by Window and WorkerGlobalScope. * @see http://www.w3.org/TR/file-system-api/#idl-def-LocalFileSystem * @constructor */ function LocalFileSystem() {} /** * Metadata interface. * @see http://www.w3.org/TR/file-system-api/#idl-def-Metadata * @constructor */ function Metadata() {} /** * @see http://www.w3.org/TR/file-system-api/#widl-Metadata-modificationTime * @type {!Date} */ Metadata.prototype.modificationTime; /** * @see http://www.w3.org/TR/file-system-api/#widl-Metadata-size * @type {number} */ Metadata.prototype.size; /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-TEMPORARY * @type {number} */ Window.prototype.TEMPORARY = 0; /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-PERSISTENT * @type {number} */ Window.prototype.PERSISTENT = 1; /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem * @param {number} type * @param {number} size * @param {function(!FileSystem)} successCallback * @param {function(!FileError)=} errorCallback */ function requestFileSystem(type, size, successCallback, errorCallback) {} /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem * @param {number} type * @param {number} size * @param {function(!FileSystem)} successCallback * @param {function(!FileError)=} errorCallback */ Window.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {}; /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI * @param {string} uri * @param {function(!Entry)} successCallback * @param {function(!FileError)=} errorCallback */ function resolveLocalFileSystemURI(uri, successCallback, errorCallback) {} /** * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI * @param {string} uri * @param {function(!Entry)} successCallback * @param {function(!FileError)=} errorCallback */ Window.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {} /** * This has replaced requestFileSystem in Chrome since WebKit revision 84224. * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem * @param {number} type * @param {number} size * @param {function(!FileSystem)} successCallback * @param {function(!FileError)=} errorCallback */ function webkitRequestFileSystem(type, size, successCallback, errorCallback) {} /** * This has replaced requestFileSystem in Chrome since WebKit revision 84224. * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-requestFileSystem * @param {number} type * @param {number} size * @param {function(!FileSystem)} successCallback * @param {function(!FileError)=} errorCallback */ Window.prototype.webkitRequestFileSystem = function(type, size, successCallback, errorCallback) {}; /** * This has replaced resolveLocalFileSystemURI in Chrome since WebKit revision * 84224. * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI * @param {string} uri * @param {function(!Entry)} successCallback * @param {function(!FileError)=} errorCallback */ function webkitResolveLocalFileSystemURI(uri, successCallback, errorCallback) {} /** * This has replaced resolveLocalFileSystemURI in Chrome since WebKit revision * 84224. * @see http://www.w3.org/TR/file-system-api/#widl-LocalFileSystem-resolveLocalFileSystemURI * @param {string} uri * @param {function(!Entry)} successCallback * @param {function(!FileError)=} errorCallback */ Window.prototype.webkitResolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {} // WindowBlobURIMethods interface, implemented by Window and WorkerGlobalScope. // There are three APIs for this: the old specced API, the new specced API, and // the webkit-prefixed API. // @see http://www.w3.org/TR/FileAPI/#creating-revoking /** * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL * @param {!Object} obj * @return {string} */ function createObjectURL(obj) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL * @param {!Object} obj * @return {string} */ Window.prototype.createObjectURL = function(obj) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL * @param {string} url */ function revokeObjectURL(url) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL * @param {string} url */ Window.prototype.revokeObjectURL = function(url) {}; /** * @see http://www.w3.org/TR/FileAPI/#URL-object * @constructor */ function DOMURL() {} /** * @see http://www.w3.org/TR/FileAPI/# * @type {!DOMURL} */ Window.prototype.URL; /** * This has replaced URL in Chrome since WebKit revision 75739. * @see http://www.w3.org/TR/FileAPI/# * @type {!DOMURL} */ Window.prototype.webkitURL; /** * @see http://www.w3.org/TR/FileAPI/#dfn-createObjectURL * @param {!Object} obj * @return {string} */ DOMURL.prototype.createObjectURL = function(obj) {}; /** * @see http://www.w3.org/TR/FileAPI/#dfn-revokeObjectURL * @param {string} url */ DOMURL.prototype.revokeObjectURL = function(url) {}; /** * @see https://developers.google.com/chrome/whitepapers/storage * @constructor */ function StorageInfo() {} /** * @see https://developers.google.com/chrome/whitepapers/storage * @type {number} * */ StorageInfo.prototype.TEMPORARY = 0; /** * @see https://developers.google.com/chrome/whitepapers/storage * @type {number} */ StorageInfo.prototype.PERSISTENT = 1; /** * @see https://developers.google.com/chrome/whitepapers/storage#requestQuota * @param {number} type * @param {number} size * @param {function(number)} successCallback * @param {function(!DOMException)=} errorCallback */ StorageInfo.prototype.requestQuota = function(type, size, successCallback, errorCallback) {}; /** * @see https://developers.google.com/chrome/whitepapers/storage#queryUsageAndQuota * @param {number} type * @param {function(number, number)} successCallback * @param {function(!DOMException)=} errorCallback */ StorageInfo.prototype.queryUsageAndQuota = function(type, successCallback, errorCallback) {}; /** * @see https://developers.google.com/chrome/whitepapers/storage * @type {!StorageInfo} */ Window.prototype.webkitStorageInfo; closure-compiler-20130227+dfsg1/externs/webkit_css.js0000644000175000017500000007032312115204405020542 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for WebKit's custom CSS properties. Copied from: * {@link * http://trac.webkit.org/browser/trunk/Source/WebCore/css/CSSPropertyNames.in} * * If you make changes to this file, notice that every property appears * twice: once as an uppercase name and once as a lowercase name. * WebKit allows both. The uppercase version is preferred. * * @externs * @author nicksantos@google.com (Nick Santos) * @author mastepien@google.com (Marek Stepien) */ /** @type {string} */ CSSProperties.prototype.WebkitAlignContent; /** @type {string} */ CSSProperties.prototype.WebkitAlignItems; /** @type {string} */ CSSProperties.prototype.WebkitAlignSelf; /** @type {string} */ CSSProperties.prototype.WebkitAnimation; /** @type {string} */ CSSProperties.prototype.WebkitAnimationDelay; /** @type {string} */ CSSProperties.prototype.WebkitAnimationDirection; /** @type {string} */ CSSProperties.prototype.WebkitAnimationDuration; /** @type {string} */ CSSProperties.prototype.WebkitAnimationFillMode; /** @type {string} */ CSSProperties.prototype.WebkitAnimationIterationCount; /** @type {string} */ CSSProperties.prototype.WebkitAnimationName; /** @type {string} */ CSSProperties.prototype.WebkitAnimationPlayState; /** @type {string} */ CSSProperties.prototype.WebkitAnimationTimingFunction; /** @type {string} */ CSSProperties.prototype.WebkitAppearance; /** @type {string} */ CSSProperties.prototype.WebkitAppRegion; /** @type {string} */ CSSProperties.prototype.WebkitAspectRatio; /** @type {string} */ CSSProperties.prototype.WebkitBackfaceVisibility; /** @type {string} */ CSSProperties.prototype.WebkitBackgroundClip; /** @type {string} */ CSSProperties.prototype.WebkitBackgroundComposite; /** @type {string} */ CSSProperties.prototype.WebkitBackgroundOrigin; /** @type {string} */ CSSProperties.prototype.WebkitBackgroundSize; /** @type {string} */ CSSProperties.prototype.WebkitBinding; /** @type {string} */ CSSProperties.prototype.WebkitBlendMode; /** @type {string} */ CSSProperties.prototype.WebkitBorderAfter; /** @type {string} */ CSSProperties.prototype.WebkitBorderAfterColor; /** @type {string} */ CSSProperties.prototype.WebkitBorderAfterStyle; /** @type {string} */ CSSProperties.prototype.WebkitBorderAfterWidth; /** @type {string} */ CSSProperties.prototype.WebkitBorderBefore; /** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeColor; /** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeStyle; /** @type {string} */ CSSProperties.prototype.WebkitBorderBeforeWidth; /** @type {string} */ CSSProperties.prototype.WebkitBorderBottomLeftRadius; /** @type {string} */ CSSProperties.prototype.WebkitBorderBottomRightRadius; /** @type {string} */ CSSProperties.prototype.WebkitBorderEnd; /** @type {string} */ CSSProperties.prototype.WebkitBorderEndColor; /** @type {string} */ CSSProperties.prototype.WebkitBorderEndStyle; /** @type {string} */ CSSProperties.prototype.WebkitBorderEndWidth; /** @type {string} */ CSSProperties.prototype.WebkitBorderFit; /** @type {string} */ CSSProperties.prototype.WebkitBorderHorizontalSpacing; /** @type {string} */ CSSProperties.prototype.WebkitBorderImage; /** @type {string} */ CSSProperties.prototype.WebkitBorderRadius; /** @type {string} */ CSSProperties.prototype.WebkitBorderStart; /** @type {string} */ CSSProperties.prototype.WebkitBorderStartColor; /** @type {string} */ CSSProperties.prototype.WebkitBorderStartStyle; /** @type {string} */ CSSProperties.prototype.WebkitBorderStartWidth; /** @type {string} */ CSSProperties.prototype.WebkitBorderTopLeftRadius; /** @type {string} */ CSSProperties.prototype.WebkitBorderTopRightRadius; /** @type {string} */ CSSProperties.prototype.WebkitBorderVerticalSpacing; /** @type {string} */ CSSProperties.prototype.WebkitBoxAlign; /** @type {string} */ CSSProperties.prototype.WebkitBoxDecorationBreak; /** @type {string} */ CSSProperties.prototype.WebkitBoxDirection; /** @type {string} */ CSSProperties.prototype.WebkitBoxFlex; /** @type {string} */ CSSProperties.prototype.WebkitBoxFlexGroup; /** @type {string} */ CSSProperties.prototype.WebkitBoxLines; /** @type {string} */ CSSProperties.prototype.WebkitBoxOrdinalGroup; /** @type {string} */ CSSProperties.prototype.WebkitBoxOrient; /** @type {string} */ CSSProperties.prototype.WebkitBoxPack; /** @type {string} */ CSSProperties.prototype.WebkitBoxReflect; /** @type {string} */ CSSProperties.prototype.WebkitBoxShadow; /** @type {string} */ CSSProperties.prototype.WebkitBoxSizing; /** @type {string} */ CSSProperties.prototype.WebkitColorCorrection; /** @type {string} */ CSSProperties.prototype.WebkitColumnAxis; /** @type {string} */ CSSProperties.prototype.WebkitColumnBreakAfter; /** @type {string} */ CSSProperties.prototype.WebkitColumnBreakBefore; /** @type {string} */ CSSProperties.prototype.WebkitColumnBreakInside; /** @type {string} */ CSSProperties.prototype.WebkitColumnCount; /** @type {string} */ CSSProperties.prototype.WebkitColumnGap; /** @type {string} */ CSSProperties.prototype.WebkitColumnProgression; /** @type {string} */ CSSProperties.prototype.WebkitColumnRule; /** @type {string} */ CSSProperties.prototype.WebkitColumnRuleColor; /** @type {string} */ CSSProperties.prototype.WebkitColumnRuleStyle; /** @type {string} */ CSSProperties.prototype.WebkitColumnRuleWidth; /** @type {string} */ CSSProperties.prototype.WebkitColumns; /** @type {string} */ CSSProperties.prototype.WebkitColumnSpan; /** @type {string} */ CSSProperties.prototype.WebkitColumnWidth; /** @type {string} */ CSSProperties.prototype.WebkitDashboardRegion; /** @type {string} */ CSSProperties.prototype.WebkitFilter; /** @type {string} */ CSSProperties.prototype.WebkitFlex; /** @type {string} */ CSSProperties.prototype.WebkitFlexBasis; /** @type {string} */ CSSProperties.prototype.WebkitFlexDirection; /** @type {string} */ CSSProperties.prototype.WebkitFlexFlow; /** @type {string} */ CSSProperties.prototype.WebkitFlexGrow; /** @type {string} */ CSSProperties.prototype.WebkitFlexShrink; /** @type {string} */ CSSProperties.prototype.WebkitFlexWrap; /** @type {string} */ CSSProperties.prototype.WebkitFlowFrom; /** @type {string} */ CSSProperties.prototype.WebkitFlowInto; /** @type {string} */ CSSProperties.prototype.WebkitFontSizeDelta; /** @type {string} */ CSSProperties.prototype.WebkitFontSmoothing; /** @type {string} */ CSSProperties.prototype.WebkitGridColumn; /** @type {string} */ CSSProperties.prototype.WebkitGridColumns; /** @type {string} */ CSSProperties.prototype.WebkitGridRow; /** @type {string} */ CSSProperties.prototype.WebkitGridRows; /** @type {string} */ CSSProperties.prototype.WebkitHighlight; /** @type {string} */ CSSProperties.prototype.WebkitHyphenateCharacter; /** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitAfter; /** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitBefore; /** @type {string} */ CSSProperties.prototype.WebkitHyphenateLimitLines; /** @type {string} */ CSSProperties.prototype.WebkitHyphens; /** @type {string} */ CSSProperties.prototype.WebkitJustifyContent; /** @type {string} */ CSSProperties.prototype.WebkitLineAlign; /** @type {string} */ CSSProperties.prototype.WebkitLineBoxContain; /** @type {string} */ CSSProperties.prototype.WebkitLineBreak; /** @type {string} */ CSSProperties.prototype.WebkitLineClamp; /** @type {string} */ CSSProperties.prototype.WebkitLineGrid; /** @type {string} */ CSSProperties.prototype.WebkitLineSnap; /** @type {string} */ CSSProperties.prototype.WebkitLocale; /** @type {string} */ CSSProperties.prototype.WebkitLogicalHeight; /** @type {string} */ CSSProperties.prototype.WebkitLogicalWidth; /** @type {string} */ CSSProperties.prototype.WebkitMarginAfter; /** @type {string} */ CSSProperties.prototype.WebkitMarginAfterCollapse; /** @type {string} */ CSSProperties.prototype.WebkitMarginBefore; /** @type {string} */ CSSProperties.prototype.WebkitMarginBeforeCollapse; /** @type {string} */ CSSProperties.prototype.WebkitMarginBottomCollapse; /** @type {string} */ CSSProperties.prototype.WebkitMarginCollapse; /** @type {string} */ CSSProperties.prototype.WebkitMarginEnd; /** @type {string} */ CSSProperties.prototype.WebkitMarginStart; /** @type {string} */ CSSProperties.prototype.WebkitMarginTopCollapse; /** @type {string} */ CSSProperties.prototype.WebkitMarquee; /** @type {string} */ CSSProperties.prototype.WebkitMarqueeDirection; /** @type {string} */ CSSProperties.prototype.WebkitMarqueeIncrement; /** @type {string} */ CSSProperties.prototype.WebkitMarqueeRepetition; /** @type {string} */ CSSProperties.prototype.WebkitMarqueeSpeed; /** @type {string} */ CSSProperties.prototype.WebkitMarqueeStyle; /** @type {string} */ CSSProperties.prototype.WebkitMask; /** @type {string} */ CSSProperties.prototype.WebkitMaskAttachment; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImage; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageOutset; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageRepeat; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageSlice; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageSource; /** @type {string} */ CSSProperties.prototype.WebkitMaskBoxImageWidth; /** @type {string} */ CSSProperties.prototype.WebkitMaskClip; /** @type {string} */ CSSProperties.prototype.WebkitMaskComposite; /** @type {string} */ CSSProperties.prototype.WebkitMaskImage; /** @type {string} */ CSSProperties.prototype.WebkitMaskOrigin; /** @type {string} */ CSSProperties.prototype.WebkitMaskPosition; /** @type {string} */ CSSProperties.prototype.WebkitMaskPositionX; /** @type {string} */ CSSProperties.prototype.WebkitMaskPositionY; /** @type {string} */ CSSProperties.prototype.WebkitMaskRepeat; /** @type {string} */ CSSProperties.prototype.WebkitMaskRepeatX; /** @type {string} */ CSSProperties.prototype.WebkitMaskRepeatY; /** @type {string} */ CSSProperties.prototype.WebkitMaskSize; /** @type {string} */ CSSProperties.prototype.WebkitMatchNearestMailBlockquoteColor; /** @type {string} */ CSSProperties.prototype.WebkitMaxLogicalHeight; /** @type {string} */ CSSProperties.prototype.WebkitMaxLogicalWidth; /** @type {string} */ CSSProperties.prototype.WebkitMinLogicalHeight; /** @type {string} */ CSSProperties.prototype.WebkitMinLogicalWidth; /** @type {string} */ CSSProperties.prototype.WebkitNbspMode; /** @type {string} */ CSSProperties.prototype.WebkitOrder; /** @type {string} */ CSSProperties.prototype.WebkitOverflowScrolling; /** @type {string} */ CSSProperties.prototype.WebkitPaddingAfter; /** @type {string} */ CSSProperties.prototype.WebkitPaddingBefore; /** @type {string} */ CSSProperties.prototype.WebkitPaddingEnd; /** @type {string} */ CSSProperties.prototype.WebkitPaddingStart; /** @type {string} */ CSSProperties.prototype.WebkitPerspective; /** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOrigin; /** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOriginX; /** @type {string} */ CSSProperties.prototype.WebkitPerspectiveOriginY; /** @type {string} */ CSSProperties.prototype.WebkitPrintColorAdjust; /** @type {string} */ CSSProperties.prototype.WebkitRegionBreakAfter; /** @type {string} */ CSSProperties.prototype.WebkitRegionBreakBefore; /** @type {string} */ CSSProperties.prototype.WebkitRegionBreakInside; /** @type {string} */ CSSProperties.prototype.WebkitRegionOverflow; /** @type {string} */ CSSProperties.prototype.WebkitRtlOrdering; /** @type {string} */ CSSProperties.prototype.WebkitRubyPosition; /** @type {string} */ CSSProperties.prototype.WebkitShapeInside; /** @type {string} */ CSSProperties.prototype.WebkitShapeMargin; /** @type {string} */ CSSProperties.prototype.WebkitShapeOutside; /** @type {string} */ CSSProperties.prototype.WebkitShapePadding; /** @type {string} */ CSSProperties.prototype.WebkitTapHighlightColor; /** @type {string} */ CSSProperties.prototype.WebkitTextAlignLast; /** @type {string} */ CSSProperties.prototype.WebkitTextCombine; /** @type {string} */ CSSProperties.prototype.WebkitTextDecorationLine; /** @type {string} */ CSSProperties.prototype.WebkitTextDecorationsInEffect; /** @type {string} */ CSSProperties.prototype.WebkitTextDecorationStyle; /** @type {string} */ CSSProperties.prototype.WebkitTextEmphasis; /** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisColor; /** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisPosition; /** @type {string} */ CSSProperties.prototype.WebkitTextEmphasisStyle; /** @type {string} */ CSSProperties.prototype.WebkitTextFillColor; /** @type {string} */ CSSProperties.prototype.WebkitTextOrientation; /** @type {string} */ CSSProperties.prototype.WebkitTextSecurity; /** @type {string} */ CSSProperties.prototype.WebkitTextSizeAdjust; /** @type {string} */ CSSProperties.prototype.WebkitTextStroke; /** @type {string} */ CSSProperties.prototype.WebkitTextStrokeColor; /** @type {string} */ CSSProperties.prototype.WebkitTextStrokeWidth; /** @type {string} */ CSSProperties.prototype.WebkitTransform; /** @type {string} */ CSSProperties.prototype.WebkitTransformOrigin; /** @type {string} */ CSSProperties.prototype.WebkitTransformOriginX; /** @type {string} */ CSSProperties.prototype.WebkitTransformOriginY; /** @type {string} */ CSSProperties.prototype.WebkitTransformOriginZ; /** @type {string} */ CSSProperties.prototype.WebkitTransformStyle; /** @type {string} */ CSSProperties.prototype.WebkitTransition; /** @type {string} */ CSSProperties.prototype.WebkitTransitionDelay; /** @type {string} */ CSSProperties.prototype.WebkitTransitionDuration; /** @type {string} */ CSSProperties.prototype.WebkitTransitionProperty; /** @type {string} */ CSSProperties.prototype.WebkitTransitionRepeatCount; /** @type {string} */ CSSProperties.prototype.WebkitTransitionTimingFunction; /** @type {string} */ CSSProperties.prototype.WebkitUserDrag; /** @type {string} */ CSSProperties.prototype.WebkitUserModify; /** @type {string} */ CSSProperties.prototype.WebkitUserSelect; /** @type {string} */ CSSProperties.prototype.WebkitWrap; /** @type {string} */ CSSProperties.prototype.WebkitWrapFlow; /** @type {string} */ CSSProperties.prototype.WebkitWrapThrough; /** @type {string} */ CSSProperties.prototype.WebkitWritingMode; // WebKit also adds bindings for the lowercase versions of these properties. // The uppercase version is preferred. /** @type {string} */ CSSProperties.prototype.webkitAlignContent; /** @type {string} */ CSSProperties.prototype.webkitAlignItems; /** @type {string} */ CSSProperties.prototype.webkitAlignSelf; /** @type {string} */ CSSProperties.prototype.webkitAnimation; /** @type {string} */ CSSProperties.prototype.webkitAnimationDelay; /** @type {string} */ CSSProperties.prototype.webkitAnimationDirection; /** @type {string} */ CSSProperties.prototype.webkitAnimationDuration; /** @type {string} */ CSSProperties.prototype.webkitAnimationFillMode; /** @type {string} */ CSSProperties.prototype.webkitAnimationIterationCount; /** @type {string} */ CSSProperties.prototype.webkitAnimationName; /** @type {string} */ CSSProperties.prototype.webkitAnimationPlayState; /** @type {string} */ CSSProperties.prototype.webkitAnimationTimingFunction; /** @type {string} */ CSSProperties.prototype.webkitAppearance; /** @type {string} */ CSSProperties.prototype.webkitAppRegion; /** @type {string} */ CSSProperties.prototype.webkitAspectRatio; /** @type {string} */ CSSProperties.prototype.webkitBackfaceVisibility; /** @type {string} */ CSSProperties.prototype.webkitBackgroundClip; /** @type {string} */ CSSProperties.prototype.webkitBackgroundComposite; /** @type {string} */ CSSProperties.prototype.webkitBackgroundOrigin; /** @type {string} */ CSSProperties.prototype.webkitBackgroundSize; /** @type {string} */ CSSProperties.prototype.webkitBinding; /** @type {string} */ CSSProperties.prototype.webkitBlendMode; /** @type {string} */ CSSProperties.prototype.webkitBorderAfter; /** @type {string} */ CSSProperties.prototype.webkitBorderAfterColor; /** @type {string} */ CSSProperties.prototype.webkitBorderAfterStyle; /** @type {string} */ CSSProperties.prototype.webkitBorderAfterWidth; /** @type {string} */ CSSProperties.prototype.webkitBorderBefore; /** @type {string} */ CSSProperties.prototype.webkitBorderBeforeColor; /** @type {string} */ CSSProperties.prototype.webkitBorderBeforeStyle; /** @type {string} */ CSSProperties.prototype.webkitBorderBeforeWidth; /** @type {string} */ CSSProperties.prototype.webkitBorderBottomLeftRadius; /** @type {string} */ CSSProperties.prototype.webkitBorderBottomRightRadius; /** @type {string} */ CSSProperties.prototype.webkitBorderEnd; /** @type {string} */ CSSProperties.prototype.webkitBorderEndColor; /** @type {string} */ CSSProperties.prototype.webkitBorderEndStyle; /** @type {string} */ CSSProperties.prototype.webkitBorderEndWidth; /** @type {string} */ CSSProperties.prototype.webkitBorderFit; /** @type {string} */ CSSProperties.prototype.webkitBorderHorizontalSpacing; /** @type {string} */ CSSProperties.prototype.webkitBorderImage; /** @type {string} */ CSSProperties.prototype.webkitBorderRadius; /** @type {string} */ CSSProperties.prototype.webkitBorderStart; /** @type {string} */ CSSProperties.prototype.webkitBorderStartColor; /** @type {string} */ CSSProperties.prototype.webkitBorderStartStyle; /** @type {string} */ CSSProperties.prototype.webkitBorderStartWidth; /** @type {string} */ CSSProperties.prototype.webkitBorderTopLeftRadius; /** @type {string} */ CSSProperties.prototype.webkitBorderTopRightRadius; /** @type {string} */ CSSProperties.prototype.webkitBorderVerticalSpacing; /** @type {string} */ CSSProperties.prototype.webkitBoxAlign; /** @type {string} */ CSSProperties.prototype.webkitBoxDecorationBreak; /** @type {string} */ CSSProperties.prototype.webkitBoxDirection; /** @type {string} */ CSSProperties.prototype.webkitBoxFlex; /** @type {string} */ CSSProperties.prototype.webkitBoxFlexGroup; /** @type {string} */ CSSProperties.prototype.webkitBoxLines; /** @type {string} */ CSSProperties.prototype.webkitBoxOrdinalGroup; /** @type {string} */ CSSProperties.prototype.webkitBoxOrient; /** @type {string} */ CSSProperties.prototype.webkitBoxPack; /** @type {string} */ CSSProperties.prototype.webkitBoxReflect; /** @type {string} */ CSSProperties.prototype.webkitBoxShadow; /** @type {string} */ CSSProperties.prototype.webkitBoxSizing; /** @type {string} */ CSSProperties.prototype.webkitColorCorrection; /** @type {string} */ CSSProperties.prototype.webkitColumnAxis; /** @type {string} */ CSSProperties.prototype.webkitColumnBreakAfter; /** @type {string} */ CSSProperties.prototype.webkitColumnBreakBefore; /** @type {string} */ CSSProperties.prototype.webkitColumnBreakInside; /** @type {string} */ CSSProperties.prototype.webkitColumnCount; /** @type {string} */ CSSProperties.prototype.webkitColumnGap; /** @type {string} */ CSSProperties.prototype.webkitColumnProgression; /** @type {string} */ CSSProperties.prototype.webkitColumnRule; /** @type {string} */ CSSProperties.prototype.webkitColumnRuleColor; /** @type {string} */ CSSProperties.prototype.webkitColumnRuleStyle; /** @type {string} */ CSSProperties.prototype.webkitColumnRuleWidth; /** @type {string} */ CSSProperties.prototype.webkitColumns; /** @type {string} */ CSSProperties.prototype.webkitColumnSpan; /** @type {string} */ CSSProperties.prototype.webkitColumnWidth; /** @type {string} */ CSSProperties.prototype.webkitDashboardRegion; /** @type {string} */ CSSProperties.prototype.webkitFilter; /** @type {string} */ CSSProperties.prototype.webkitFlex; /** @type {string} */ CSSProperties.prototype.webkitFlexBasis; /** @type {string} */ CSSProperties.prototype.webkitFlexDirection; /** @type {string} */ CSSProperties.prototype.webkitFlexFlow; /** @type {string} */ CSSProperties.prototype.webkitFlexGrow; /** @type {string} */ CSSProperties.prototype.webkitFlexShrink; /** @type {string} */ CSSProperties.prototype.webkitFlexWrap; /** @type {string} */ CSSProperties.prototype.webkitFlowFrom; /** @type {string} */ CSSProperties.prototype.webkitFlowInto; /** @type {string} */ CSSProperties.prototype.webkitFontSizeDelta; /** @type {string} */ CSSProperties.prototype.webkitFontSmoothing; /** @type {string} */ CSSProperties.prototype.webkitGridColumn; /** @type {string} */ CSSProperties.prototype.webkitGridColumns; /** @type {string} */ CSSProperties.prototype.webkitGridRow; /** @type {string} */ CSSProperties.prototype.webkitGridRows; /** @type {string} */ CSSProperties.prototype.webkitHighlight; /** @type {string} */ CSSProperties.prototype.webkitHyphenateCharacter; /** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitAfter; /** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitBefore; /** @type {string} */ CSSProperties.prototype.webkitHyphenateLimitLines; /** @type {string} */ CSSProperties.prototype.webkitHyphens; /** @type {string} */ CSSProperties.prototype.webkitJustifyContent; /** @type {string} */ CSSProperties.prototype.webkitLineAlign; /** @type {string} */ CSSProperties.prototype.webkitLineBoxContain; /** @type {string} */ CSSProperties.prototype.webkitLineBreak; /** @type {string} */ CSSProperties.prototype.webkitLineClamp; /** @type {string} */ CSSProperties.prototype.webkitLineGrid; /** @type {string} */ CSSProperties.prototype.webkitLineSnap; /** @type {string} */ CSSProperties.prototype.webkitLocale; /** @type {string} */ CSSProperties.prototype.webkitLogicalHeight; /** @type {string} */ CSSProperties.prototype.webkitLogicalWidth; /** @type {string} */ CSSProperties.prototype.webkitMarginAfter; /** @type {string} */ CSSProperties.prototype.webkitMarginAfterCollapse; /** @type {string} */ CSSProperties.prototype.webkitMarginBefore; /** @type {string} */ CSSProperties.prototype.webkitMarginBeforeCollapse; /** @type {string} */ CSSProperties.prototype.webkitMarginBottomCollapse; /** @type {string} */ CSSProperties.prototype.webkitMarginCollapse; /** @type {string} */ CSSProperties.prototype.webkitMarginEnd; /** @type {string} */ CSSProperties.prototype.webkitMarginStart; /** @type {string} */ CSSProperties.prototype.webkitMarginTopCollapse; /** @type {string} */ CSSProperties.prototype.webkitMarquee; /** @type {string} */ CSSProperties.prototype.webkitMarqueeDirection; /** @type {string} */ CSSProperties.prototype.webkitMarqueeIncrement; /** @type {string} */ CSSProperties.prototype.webkitMarqueeRepetition; /** @type {string} */ CSSProperties.prototype.webkitMarqueeSpeed; /** @type {string} */ CSSProperties.prototype.webkitMarqueeStyle; /** @type {string} */ CSSProperties.prototype.webkitMask; /** @type {string} */ CSSProperties.prototype.webkitMaskAttachment; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImage; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageOutset; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageRepeat; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageSlice; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageSource; /** @type {string} */ CSSProperties.prototype.webkitMaskBoxImageWidth; /** @type {string} */ CSSProperties.prototype.webkitMaskClip; /** @type {string} */ CSSProperties.prototype.webkitMaskComposite; /** @type {string} */ CSSProperties.prototype.webkitMaskImage; /** @type {string} */ CSSProperties.prototype.webkitMaskOrigin; /** @type {string} */ CSSProperties.prototype.webkitMaskPosition; /** @type {string} */ CSSProperties.prototype.webkitMaskPositionX; /** @type {string} */ CSSProperties.prototype.webkitMaskPositionY; /** @type {string} */ CSSProperties.prototype.webkitMaskRepeat; /** @type {string} */ CSSProperties.prototype.webkitMaskRepeatX; /** @type {string} */ CSSProperties.prototype.webkitMaskRepeatY; /** @type {string} */ CSSProperties.prototype.webkitMaskSize; /** @type {string} */ CSSProperties.prototype.webkitMatchNearestMailBlockquoteColor; /** @type {string} */ CSSProperties.prototype.webkitMaxLogicalHeight; /** @type {string} */ CSSProperties.prototype.webkitMaxLogicalWidth; /** @type {string} */ CSSProperties.prototype.webkitMinLogicalHeight; /** @type {string} */ CSSProperties.prototype.webkitMinLogicalWidth; /** @type {string} */ CSSProperties.prototype.webkitNbspMode; /** @type {string} */ CSSProperties.prototype.webkitOrder; /** @type {string} */ CSSProperties.prototype.webkitOverflowScrolling; /** @type {string} */ CSSProperties.prototype.webkitPaddingAfter; /** @type {string} */ CSSProperties.prototype.webkitPaddingBefore; /** @type {string} */ CSSProperties.prototype.webkitPaddingEnd; /** @type {string} */ CSSProperties.prototype.webkitPaddingStart; /** @type {string} */ CSSProperties.prototype.webkitPerspective; /** @type {string} */ CSSProperties.prototype.webkitPerspectiveOrigin; /** @type {string} */ CSSProperties.prototype.webkitPerspectiveOriginX; /** @type {string} */ CSSProperties.prototype.webkitPerspectiveOriginY; /** @type {string} */ CSSProperties.prototype.webkitPrintColorAdjust; /** @type {string} */ CSSProperties.prototype.webkitRegionBreakAfter; /** @type {string} */ CSSProperties.prototype.webkitRegionBreakBefore; /** @type {string} */ CSSProperties.prototype.webkitRegionBreakInside; /** @type {string} */ CSSProperties.prototype.webkitRegionOverflow; /** @type {string} */ CSSProperties.prototype.webkitRtlOrdering; /** @type {string} */ CSSProperties.prototype.webkitRubyPosition; /** @type {string} */ CSSProperties.prototype.webkitShapeInside; /** @type {string} */ CSSProperties.prototype.webkitShapeMargin; /** @type {string} */ CSSProperties.prototype.webkitShapeOutside; /** @type {string} */ CSSProperties.prototype.webkitShapePadding; /** @type {string} */ CSSProperties.prototype.webkitTapHighlightColor; /** @type {string} */ CSSProperties.prototype.webkitTextAlignLast; /** @type {string} */ CSSProperties.prototype.webkitTextCombine; /** @type {string} */ CSSProperties.prototype.webkitTextDecorationLine; /** @type {string} */ CSSProperties.prototype.webkitTextDecorationsInEffect; /** @type {string} */ CSSProperties.prototype.webkitTextDecorationStyle; /** @type {string} */ CSSProperties.prototype.webkitTextEmphasis; /** @type {string} */ CSSProperties.prototype.webkitTextEmphasisColor; /** @type {string} */ CSSProperties.prototype.webkitTextEmphasisPosition; /** @type {string} */ CSSProperties.prototype.webkitTextEmphasisStyle; /** @type {string} */ CSSProperties.prototype.webkitTextFillColor; /** @type {string} */ CSSProperties.prototype.webkitTextOrientation; /** @type {string} */ CSSProperties.prototype.webkitTextSecurity; /** @type {string} */ CSSProperties.prototype.webkitTextSizeAdjust; /** @type {string} */ CSSProperties.prototype.webkitTextStroke; /** @type {string} */ CSSProperties.prototype.webkitTextStrokeColor; /** @type {string} */ CSSProperties.prototype.webkitTextStrokeWidth; /** @type {string} */ CSSProperties.prototype.webkitTransform; /** @type {string} */ CSSProperties.prototype.webkitTransformOrigin; /** @type {string} */ CSSProperties.prototype.webkitTransformOriginX; /** @type {string} */ CSSProperties.prototype.webkitTransformOriginY; /** @type {string} */ CSSProperties.prototype.webkitTransformOriginZ; /** @type {string} */ CSSProperties.prototype.webkitTransformStyle; /** @type {string} */ CSSProperties.prototype.webkitTransition; /** @type {string} */ CSSProperties.prototype.webkitTransitionDelay; /** @type {string} */ CSSProperties.prototype.webkitTransitionDuration; /** @type {string} */ CSSProperties.prototype.webkitTransitionProperty; /** @type {string} */ CSSProperties.prototype.webkitTransitionRepeatCount; /** @type {string} */ CSSProperties.prototype.webkitTransitionTimingFunction; /** @type {string} */ CSSProperties.prototype.webkitUserDrag; /** @type {string} */ CSSProperties.prototype.webkitUserModify; /** @type {string} */ CSSProperties.prototype.webkitUserSelect; /** @type {string} */ CSSProperties.prototype.webkitWrap; /** @type {string} */ CSSProperties.prototype.webkitWrapFlow; /** @type {string} */ CSSProperties.prototype.webkitWrapThrough; /** @type {string} */ CSSProperties.prototype.webkitWritingMode; /** * @constructor * @param {number} x * @param {number} y */ function WebKitPoint(x, y) {} /** @type {number} */ WebKitPoint.prototype.x; /** @type {number} */ WebKitPoint.prototype.y; closure-compiler-20130227+dfsg1/externs/w3c_elementtraversal.js0000644000175000017500000000310112115204405022524 0ustar apoapo/* * Copyright 2009 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. */ /** * @fileoverview Definitions for DOM Element Traversal interface. * This file depends on w3c_dom1.js. * The whole file has been fully type annotated. * Created from: * http://www.w3.org/TR/ElementTraversal/#ecmascript-bindings * * @externs * @author arv@google.com (Erik Arvidsson) */ /** * @type {Element} * @see https://developer.mozilla.org/En/DOM/Element.firstElementChild */ Element.prototype.firstElementChild; /** * @type {Element} * @see https://developer.mozilla.org/En/DOM/Element.lastElementChild */ Element.prototype.lastElementChild; /** * @type {Element} * @see https://developer.mozilla.org/En/DOM/Element.previousElementSibling */ Element.prototype.previousElementSibling; /** * @type {Element} * @see https://developer.mozilla.org/En/DOM/Element.nextElementSibling */ Element.prototype.nextElementSibling; /** * @type {number} * @see https://developer.mozilla.org/En/DOM/Element.childElementCount */ Element.prototype.childElementCount; closure-compiler-20130227+dfsg1/externs/google.js0000644000175000017500000000171212115204405017655 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Declaration of the type level google namespace. * @externs * @author nicksantos@google.com (Nick Santos) */ /** * Suppresses the compiler warning when multiple externs files declare the * google namespace. * @suppress {duplicate} * @noalias */ // TODO(nicksantos): Consolidate to one google namespace declaration. var google = {}; closure-compiler-20130227+dfsg1/externs/ie_css.js0000644000175000017500000001367712115204405017663 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for IE's custom CSS properties, as defined here: * http://msdn.microsoft.com/en-us/library/aa768661(VS.85).aspx * * This page is also useful for the IDL definitions: * http://source.winehq.org/source/include/mshtml.idl * * @externs * @author nicksantos@google.com */ /** @type {Element} */ StyleSheet.prototype.owningElement; /** @type {boolean} */ StyleSheet.prototype.readOnly; /** @type {StyleSheetList} */ StyleSheet.prototype.imports; /** @type {string} */ StyleSheet.prototype.id; /** * @param {string} bstrURL * @param {number} lIndex * @return {number} */ StyleSheet.prototype.addImport; /** * @param {string} bstrSelector * @param {string} bstrStyle * @param {number=} opt_iIndex * @return {number} * @see http://msdn.microsoft.com/en-us/library/aa358796%28v=vs.85%29.aspx */ StyleSheet.prototype.addRule; /** * @param {number} lIndex */ StyleSheet.prototype.removeImport; /** * @param {number} lIndex */ StyleSheet.prototype.removeRule; /** @type {string} */ StyleSheet.prototype.cssText; /** @type {CSSRuleList} */ StyleSheet.prototype.rules; // StyleSheet methods /** * @param {string} propName * @return {string} * @see http://msdn.microsoft.com/en-us/library/aa358797(VS.85).aspx */ StyleSheet.prototype.getExpression; /** * @param {string} name * @param {string} expression * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/ms531196(VS.85).aspx */ StyleSheet.prototype.setExpression; /** * @param {string} expression * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/aa358798(VS.85).aspx */ StyleSheet.prototype.removeExpression; // IE-only CSS style names. /** @type {string} */ CSSProperties.prototype.backgroundPositionX; /** @type {string} */ CSSProperties.prototype.backgroundPositionY; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms533883.aspx */ CSSProperties.prototype.imeMode; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms534176(VS.85).aspx */ CSSProperties.prototype.msInterpolationMode; /** @type {string} */ CSSProperties.prototype.overflowX; /** @type {string} */ CSSProperties.prototype.overflowY; /** @type {number} */ CSSProperties.prototype.pixelWidth; /** @type {number} */ CSSProperties.prototype.pixelHeight; /** @type {number} */ CSSProperties.prototype.pixelLeft; /** @type {number} */ CSSProperties.prototype.pixelTop; /** @type {string} */ CSSProperties.prototype.styleFloat; /** * @type {string|number} * @see http://msdn.microsoft.com/en-us/library/ms535169(VS.85).aspx */ CSSProperties.prototype.zoom; /** * @type {string} * @see http://msdn.microsoft.com/en-us/library/ms535153(VS.85).aspx */ CSSProperties.prototype.writingMode; /** * IE-specific extensions. * @see http://blogs.msdn.com/b/ie/archive/2008/09/08/microsoft-css-vendor-extensions.aspx */ /** @type {string} */ CSSProperties.prototype.MsAccelerator; /** @type {string} */ CSSProperties.prototype.MsBackgroundPositionX; /** @type {string} */ CSSProperties.prototype.MsBackgroundPositionY; /** @type {string} */ CSSProperties.prototype.MsBehavior; /** @type {string} */ CSSProperties.prototype.MsBlockProgression; /** @type {string} */ CSSProperties.prototype.MsFilter; /** @type {string} */ CSSProperties.prototype.MsImeMode; /** @type {string} */ CSSProperties.prototype.MsLayoutGrid; /** @type {string} */ CSSProperties.prototype.MsLayoutGridChar; /** @type {string} */ CSSProperties.prototype.MsLayoutGridLine; /** @type {string} */ CSSProperties.prototype.MsLayoutGridMode; /** @type {string} */ CSSProperties.prototype.MsLayoutGridType; /** @type {string} */ CSSProperties.prototype.MsLineBreak; /** @type {string} */ CSSProperties.prototype.MsLineGridMode; /** @type {string} */ CSSProperties.prototype.MsInterpolationMode; /** @type {string} */ CSSProperties.prototype.MsOverflowX; /** @type {string} */ CSSProperties.prototype.MsOverflowY; /** @type {string} */ CSSProperties.prototype.MsScrollbar3dlightColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarArrowColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarBaseColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarDarkshadowColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarFaceColor; CSSProperties.prototype.MsScrollbarHighlightColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarShadowColor; /** @type {string} */ CSSProperties.prototype.MsScrollbarTrackColor; /** @type {string} */ CSSProperties.prototype.MsTextAlignLast; /** @type {string} */ CSSProperties.prototype.MsTextAutospace; /** @type {string} */ CSSProperties.prototype.MsTextJustify; /** @type {string} */ CSSProperties.prototype.MsTextKashidaSpace; /** @type {string} */ CSSProperties.prototype.MsTextOverflow; /** @type {string} */ CSSProperties.prototype.MsTextUnderlinePosition; /** @type {string} */ CSSProperties.prototype.MsWordBreak; /** @type {string} */ CSSProperties.prototype.MsWordWrap; /** @type {string} */ CSSProperties.prototype.MsWritingMode; /** @type {string} */ CSSProperties.prototype.MsZoom; // See: http://msdn.microsoft.com/en-us/library/windows/apps/Hh702466.aspx /** @type {string} */ CSSProperties.prototype.msContentZooming; /** @type {string} */ CSSProperties.prototype.msTouchAction; /** @type {string} */ CSSProperties.prototype.msTransform; /** @type {string} */ CSSProperties.prototype.msTransition; closure-compiler-20130227+dfsg1/externs/html5.js0000644000175000017500000017430412115204405017442 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over the * W3C's DOM3 specification in HTML5. This file depends on * w3c_dom3.js. The whole file has been fully type annotated. * * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/index.html * @see http://dev.w3.org/html5/spec/Overview.html * * This also includes Typed Array definitions from * http://www.khronos.org/registry/typedarray/specs/latest/ * * This relies on w3c_event.js being included first. * * @externs */ /* * JSON API. */ /** * @see https://developer.mozilla.org/En/Using_native_JSON * @type {!JSONType} */ Window.prototype.JSON; /** * @constructor * @extends {HTMLElement} */ function HTMLCanvasElement() {} /** @type {number} */ HTMLCanvasElement.prototype.width; /** @type {number} */ HTMLCanvasElement.prototype.height; /** * @param {string=} opt_type * @return {string} * @throws {Error} * @nosideeffects */ HTMLCanvasElement.prototype.toDataURL = function(opt_type) {}; /** * @param {string} contextId * @param {Object=} opt_args * @return {Object} */ HTMLCanvasElement.prototype.getContext = function(contextId, opt_args) {}; /** * @constructor */ function CanvasRenderingContext2D() {} /** @type {HTMLCanvasElement} */ CanvasRenderingContext2D.prototype.canvas; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.save = function() {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.restore = function() {}; /** * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.scale = function(x, y) {}; /** * @param {number} angle * @return {undefined} */ CanvasRenderingContext2D.prototype.rotate = function(angle) {}; /** * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.translate = function(x, y) {}; /** * @param {number} m11 * @param {number} m12 * @param {number} m21 * @param {number} m22 * @param {number} dx * @param {number} dy * @return {undefined} */ CanvasRenderingContext2D.prototype.transform = function( m11, m12, m21, m22, dx, dy) {}; /** * @param {number} m11 * @param {number} m12 * @param {number} m21 * @param {number} m22 * @param {number} dx * @param {number} dy * @return {undefined} */ CanvasRenderingContext2D.prototype.setTransform = function( m11, m12, m21, m22, dx, dy) {}; /** * @param {number} x0 * @param {number} y0 * @param {number} x1 * @param {number} y1 * @return {CanvasGradient} * @throws {Error} * @nosideeffects */ CanvasRenderingContext2D.prototype.createLinearGradient = function( x0, y0, x1, y1) {}; /** * @param {number} x0 * @param {number} y0 * @param {number} r0 * @param {number} x1 * @param {number} y1 * @param {number} r1 * @return {CanvasGradient} * @throws {Error} * @nosideeffects */ CanvasRenderingContext2D.prototype.createRadialGradient = function( x0, y0, r0, x1, y1, r1) {}; /** * @param {HTMLImageElement|HTMLCanvasElement} image * @param {string} repetition * @return {CanvasPattern} * @throws {Error} * @nosideeffects */ CanvasRenderingContext2D.prototype.createPattern = function( image, repetition) {}; /** * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @return {undefined} */ CanvasRenderingContext2D.prototype.clearRect = function(x, y, w, h) {}; /** * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @return {undefined} */ CanvasRenderingContext2D.prototype.fillRect = function(x, y, w, h) {}; /** * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @return {undefined} */ CanvasRenderingContext2D.prototype.strokeRect = function(x, y, w, h) {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.beginPath = function() {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.closePath = function() {}; /** * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.moveTo = function(x, y) {}; /** * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.lineTo = function(x, y) {}; /** * @param {number} cpx * @param {number} cpy * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.quadraticCurveTo = function( cpx, cpy, x, y) {}; /** * @param {number} cp1x * @param {number} cp1y * @param {number} cp2x * @param {number} cp2y * @param {number} x * @param {number} y * @return {undefined} */ CanvasRenderingContext2D.prototype.bezierCurveTo = function( cp1x, cp1y, cp2x, cp2y, x, y) {}; /** * @param {number} x1 * @param {number} y1 * @param {number} x2 * @param {number} y2 * @param {number} radius * @return {undefined} */ CanvasRenderingContext2D.prototype.arcTo = function(x1, y1, x2, y2, radius) {}; /** * @param {number} x * @param {number} y * @param {number} w * @param {number} h * @return {undefined} */ CanvasRenderingContext2D.prototype.rect = function(x, y, w, h) {}; /** * @param {number} x * @param {number} y * @param {number} radius * @param {number} startAngle * @param {number} endAngle * @param {boolean} anticlockwise * @return {undefined} */ CanvasRenderingContext2D.prototype.arc = function( x, y, radius, startAngle, endAngle, anticlockwise) {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.fill = function() {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.stroke = function() {}; /** * @return {undefined} */ CanvasRenderingContext2D.prototype.clip = function() {}; /** * @param {number} x * @param {number} y * @return {boolean} * @nosideeffects */ CanvasRenderingContext2D.prototype.isPointInPath = function(x, y) {}; /** * @param {string} text * @param {number} x * @param {number} y * @param {number=} opt_maxWidth * @return {undefined} */ CanvasRenderingContext2D.prototype.fillText = function( text, x, y, opt_maxWidth) {}; /** * @param {string} text * @param {number} x * @param {number} y * @param {number=} opt_maxWidth * @return {undefined} */ CanvasRenderingContext2D.prototype.strokeText = function( text, x, y, opt_maxWidth) {}; /** * @param {string} text * @return {TextMetrics} * @nosideeffects */ CanvasRenderingContext2D.prototype.measureText = function(text) {}; /** * @param {HTMLImageElement|HTMLCanvasElement|Image|HTMLVideoElement} image * @param {number} dx Destination x coordinate. * @param {number} dy Destination y coordinate. * @param {number=} opt_dw Destination box width. Defaults to the image width. * @param {number=} opt_dh Destination box height. Defaults to the image height. * @param {number=} opt_sx Source box x coordinate. Used to select a portion of * the source image to draw. Defaults to 0. * @param {number=} opt_sy Source box y coordinate. Used to select a portion of * the source image to draw. Defaults to 0. * @param {number=} opt_sw Source box width. Used to select a portion of * the source image to draw. Defaults to the full image width. * @param {number=} opt_sh Source box height. Used to select a portion of * the source image to draw. Defaults to the full image height. * @return {undefined} */ CanvasRenderingContext2D.prototype.drawImage = function( image, dx, dy, opt_dw, opt_dh, opt_sx, opt_sy, opt_sw, opt_sh) {}; /** * @param {number} sw * @param {number} sh * @return {ImageData} * @nosideeffects */ CanvasRenderingContext2D.prototype.createImageData = function(sw, sh) {}; /** * @param {number} sx * @param {number} sy * @param {number} sw * @param {number} sh * @return {ImageData} * @throws {Error} * @nosideeffects */ CanvasRenderingContext2D.prototype.getImageData = function(sx, sy, sw, sh) {}; /** * @param {ImageData} imagedata * @param {number} dx * @param {number} dy * @param {number=} opt_dirtyX * @param {number=} opt_dirtyY * @param {number=} opt_dirtyWidth * @param {number=} opt_dirtyHeight * @return {undefined} */ CanvasRenderingContext2D.prototype.putImageData = function(imagedata, dx, dy, opt_dirtyX, opt_dirtyY, opt_dirtyWidth, opt_dirtyHeight) {}; /** * Note: WebKit only * @param {number|string=} opt_a * @param {number=} opt_b * @param {number=} opt_c * @param {number=} opt_d * @param {number=} opt_e * @see http://developer.apple.com/library/safari/#documentation/appleapplications/reference/WebKitDOMRef/CanvasRenderingContext2D_idl/Classes/CanvasRenderingContext2D/index.html * @return {undefined} */ CanvasRenderingContext2D.prototype.setFillColor; /** * Note: WebKit only * @param {number|string=} opt_a * @param {number=} opt_b * @param {number=} opt_c * @param {number=} opt_d * @param {number=} opt_e * @see http://developer.apple.com/library/safari/#documentation/appleapplications/reference/WebKitDOMRef/CanvasRenderingContext2D_idl/Classes/CanvasRenderingContext2D/index.html * @return {undefined} */ CanvasRenderingContext2D.prototype.setStrokeColor; /** * @return {Array.} */ CanvasRenderingContext2D.prototype.getLineDash; /** * @param {Array.} segments * @return {undefined} */ CanvasRenderingContext2D.prototype.setLineDash; /** @type {string} */ CanvasRenderingContext2D.prototype.fillColor; /** * @type {string} * @implicitCast */ CanvasRenderingContext2D.prototype.fillStyle; /** @type {string} */ CanvasRenderingContext2D.prototype.font; /** @type {number} */ CanvasRenderingContext2D.prototype.globalAlpha; /** @type {string} */ CanvasRenderingContext2D.prototype.globalCompositeOperation; /** @type {number} */ CanvasRenderingContext2D.prototype.lineWidth; /** @type {string} */ CanvasRenderingContext2D.prototype.lineCap; /** @type {string} */ CanvasRenderingContext2D.prototype.lineJoin; /** @type {number} */ CanvasRenderingContext2D.prototype.miterLimit; /** @type {number} */ CanvasRenderingContext2D.prototype.shadowBlur; /** @type {string} */ CanvasRenderingContext2D.prototype.shadowColor; /** @type {number} */ CanvasRenderingContext2D.prototype.shadowOffsetX; /** @type {number} */ CanvasRenderingContext2D.prototype.shadowOffsetY; /** * @type {string} * @implicitCast */ CanvasRenderingContext2D.prototype.strokeStyle; /** @type {string} */ CanvasRenderingContext2D.prototype.strokeColor; /** @type {string} */ CanvasRenderingContext2D.prototype.textAlign; /** @type {string} */ CanvasRenderingContext2D.prototype.textBaseline; /** * @constructor */ function CanvasGradient() {} /** * @param {number} offset * @param {string} color * @return {undefined} */ CanvasGradient.prototype.addColorStop = function(offset, color) {}; /** * @constructor */ function CanvasPattern() {} /** * @constructor */ function TextMetrics() {} /** @type {number} */ TextMetrics.prototype.width; /** * @constructor */ function ImageData() {} /** @type {Uint8ClampedArray} */ ImageData.prototype.data; /** @type {number} */ ImageData.prototype.width; /** @type {number} */ ImageData.prototype.height; /** * @constructor */ function ClientInformation() {} /** @type {boolean} */ ClientInformation.prototype.onLine; /** * @param {string} protocol * @param {string} uri * @param {string} title * @return {undefined} */ ClientInformation.prototype.registerProtocolHandler = function( protocol, uri, title) {}; /** * @param {string} mimeType * @param {string} uri * @param {string} title * @return {undefined} */ ClientInformation.prototype.registerContentHandler = function( mimeType, uri, title) {}; // HTML5 Database objects /** * @constructor */ function Database() {} /** * @type {string} */ Database.prototype.version; /** * @param {function(!SQLTransaction) : void} callback * @param {(function(!SQLError) : void)=} opt_errorCallback * @param {Function=} opt_Callback */ Database.prototype.transaction = function( callback, opt_errorCallback, opt_Callback) {}; /** * @param {function(!SQLTransaction) : void} callback * @param {(function(!SQLError) : void)=} opt_errorCallback * @param {Function=} opt_Callback */ Database.prototype.readTransaction = function( callback, opt_errorCallback, opt_Callback) {}; /** * @param {string} oldVersion * @param {string} newVersion * @param {function(!SQLTransaction) : void} callback * @param {function(!SQLError) : void} errorCallback * @param {Function} successCallback */ Database.prototype.changeVersion = function( oldVersion, newVersion, callback, errorCallback, successCallback) {}; /** * @interface */ function DatabaseCallback() {} /** * @param {!Database} db * @return {undefined} */ DatabaseCallback.prototype.handleEvent = function(db) {}; /** * @constructor */ function SQLError() {} /** * @type {number} */ SQLError.prototype.code; /** * @type {string} */ SQLError.prototype.message; /** * @constructor */ function SQLTransaction() {} /** * @param {string} sqlStatement * @param {Array.<*>=} opt_queryArgs * @param {SQLStatementCallback=} opt_callback * @param {(function(!SQLTransaction, !SQLError) : (boolean|void))=} * opt_errorCallback */ SQLTransaction.prototype.executeSql = function( sqlStatement, opt_queryArgs, opt_callback, opt_errorCallback) {}; /** * @typedef {(function(!SQLTransaction, !SQLResultSet) : void)} */ var SQLStatementCallback; /** * @constructor */ function SQLResultSet() {} /** * @type {number} */ SQLResultSet.prototype.insertId; /** * @type {number} */ SQLResultSet.prototype.rowsAffected; /** * @type {SQLResultSetRowList} */ SQLResultSet.prototype.rows; /** * @constructor */ function SQLResultSetRowList() {} /** * @type {number} */ SQLResultSetRowList.prototype.length; /** * @param {number} index * @return {Object} * @nosideeffects */ SQLResultSetRowList.prototype.item = function(index) {}; /** * @param {string} name * @param {string} version * @param {string} description * @param {number} size * @param {(DatabaseCallback|function(Database))=} opt_callback * @return {Database} */ function openDatabase(name, version, description, size, opt_callback) {} /** * @param {string} name * @param {string} version * @param {string} description * @param {number} size * @param {(DatabaseCallback|function(Database))=} opt_callback * @return {Database} */ Window.prototype.openDatabase = function(name, version, description, size, opt_callback) {}; /** * @type {boolean} */ HTMLImageElement.prototype.complete; /** * @type {string} * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-crossorigin */ HTMLImageElement.prototype.crossOrigin; /** * The postMessage method (as defined by HTML5 spec and implemented in FF3). * @param {*} message * @param {string|Array} targetOrigin The target origin in the 2-argument * version of this function. WebKit seems to have implemented this * function wrong in the 3-argument version so that ports is the * second argument. * @param {string|Array=} ports An optional array of ports or the target * origin. WebKit seems to have implemented this * function wrong in the 3-argument version so that targetOrigin is the * third argument. * @see http://dev.w3.org/html5/postmsg/#dom-window-postmessage */ Window.prototype.postMessage = function(message, targetOrigin, ports) {}; /** * The postMessage method (as implemented in Opera). * @param {string} message */ Document.prototype.postMessage = function(message) {}; /** * Document head accessor. * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#the-head-element-0 * @type {HTMLHeadElement} */ Document.prototype.head; /** * @see https://developer.apple.com/webapps/docs/documentation/AppleApplications/Reference/SafariJSRef/DOMApplicationCache/DOMApplicationCache.html * @constructor * @implements {EventTarget} */ function DOMApplicationCache() {} /** @override */ DOMApplicationCache.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ DOMApplicationCache.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ DOMApplicationCache.prototype.dispatchEvent = function(evt) {}; /** * The object isn't associated with an application cache. This can occur if the * update process fails and there is no previous cache to revert to, or if there * is no manifest file. * @type {number} */ DOMApplicationCache.prototype.UNCACHED = 0; /** * The cache is idle. * @type {number} */ DOMApplicationCache.prototype.IDLE = 1; /** * The update has started but the resources are not downloaded yet - for * example, this can happen when the manifest file is fetched. * @type {number} */ DOMApplicationCache.prototype.CHECKING = 2; /** * The resources are being downloaded into the cache. * @type {number} */ DOMApplicationCache.prototype.DOWNLOADING = 3; /** * Resources have finished downloading and the new cache is ready to be used. * @type {number} */ DOMApplicationCache.prototype.UPDATEREADY = 4; /** * The cache is obsolete. * @type {number} */ DOMApplicationCache.prototype.OBSOLETE = 5; /** * The current status of the application cache. * @type {number} */ DOMApplicationCache.prototype.status; /** * Sent when the update process finishes for the first time; that is, the first * time an application cache is saved. * @type {?function(!Event)} */ DOMApplicationCache.prototype.oncached; /** * Sent when the cache update process begins. * @type {?function(!Event)} */ DOMApplicationCache.prototype.onchecking; /** * Sent when the update process begins downloading resources in the manifest * file. * @type {?function(!Event)} */ DOMApplicationCache.prototype.ondownloading; /** * Sent when an error occurs. * @type {?function(!Event)} */ DOMApplicationCache.prototype.onerror; /** * Sent when the update process finishes but the manifest file does not change. * @type {?function(!Event)} */ DOMApplicationCache.prototype.onnoupdate; /** * Sent when each resource in the manifest file begins to download. * @type {?function(!Event)} */ DOMApplicationCache.prototype.onprogress; /** * Sent when there is an existing application cache, the update process * finishes, and there is a new application cache ready for use. * @type {?function(!Event)} */ DOMApplicationCache.prototype.onupdateready; /** * Replaces the active cache with the latest version. * @throws {DOMException} */ DOMApplicationCache.prototype.swapCache = function() {}; /** * Manually triggers the update process. * @throws {DOMException} */ DOMApplicationCache.prototype.update = function() {}; /** @type {DOMApplicationCache} */ var applicationCache; /** @type {DOMApplicationCache} */ Window.prototype.applicationCache; /** * @see https://developer.mozilla.org/En/DOM/Worker/Functions_available_to_workers * @param {...string} var_args */ Window.prototype.importScripts = function(var_args) {}; /** * @see https://developer.mozilla.org/En/DOM/Worker/Functions_available_to_workers * @param {...string} var_args */ var importScripts = function(var_args) {}; /** * @see http://dev.w3.org/html5/postmsg/ * @interface */ function Transferable() {} /** * @see http://dev.w3.org/html5/workers/ * @constructor * @implements {EventTarget} */ function WebWorker() {} /** @override */ WebWorker.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ WebWorker.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ WebWorker.prototype.dispatchEvent = function(evt) {}; /** * Stops the worker process */ WebWorker.prototype.terminate = function() {}; /** * Posts a message to the worker thread. * @param {string} message */ WebWorker.prototype.postMessage = function(message) {}; /** * Sent when the worker thread posts a message to its creator. * @type {?function(!MessageEvent)} */ WebWorker.prototype.onmessage; /** * Sent when the worker thread encounters an error. * @type {?function(!Event)} */ WebWorker.prototype.onerror; /** * @see http://dev.w3.org/html5/workers/ * @constructor * @implements {EventTarget} */ function Worker(opt_arg0) {} /** @override */ Worker.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ Worker.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ Worker.prototype.dispatchEvent = function(evt) {}; /** * Stops the worker process */ Worker.prototype.terminate = function() {}; /** * Posts a message to the worker thread. * @param {*} message * @param {Array.=} opt_transfer */ Worker.prototype.postMessage = function(message, opt_transfer) {}; /** * Posts a message to the worker thread. * @param {*} message * @param {Array.=} opt_transfer */ Worker.prototype.webkitPostMessage = function(message, opt_transfer) {}; /** * Sent when the worker thread posts a message to its creator. * @type {?function(!MessageEvent)} */ Worker.prototype.onmessage = function() {}; /** * Sent when the worker thread encounters an error. * @type {?function(!Event)} */ Worker.prototype.onerror = function() {}; /** * @see http://dev.w3.org/html5/workers/ * @param {string} scriptURL The URL of the script to run in the SharedWorker. * @param {string=} opt_name A name that can later be used to obtain a * reference to the same SharedWorker. * @constructor * @implements {EventTarget} */ function SharedWorker(scriptURL, opt_name) {} /** @override */ SharedWorker.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ SharedWorker.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ SharedWorker.prototype.dispatchEvent = function(evt) {}; /** * @type {!MessagePort} */ SharedWorker.prototype.port; /** * Called on network errors for loading the initial script. * @type {?function(!Event)} */ SharedWorker.prototype.onerror = function() {}; /** * @see http://dev.w3.org/html5/workers/ * @interface */ function WorkerLocation() {} /** @type {string} */ WorkerLocation.prototype.protocol; /** @type {string} */ WorkerLocation.prototype.host; /** @type {string} */ WorkerLocation.prototype.hostname; /** @type {string} */ WorkerLocation.prototype.port; /** @type {string} */ WorkerLocation.prototype.pathname; /** @type {string} */ WorkerLocation.prototype.search; /** @type {string} */ WorkerLocation.prototype.hash; /** * @see http://dev.w3.org/html5/workers/ * @interface * @extends {EventTarget} */ function WorkerGlobalScope() {} /** @type {WorkerGlobalScope} */ WorkerGlobalScope.prototype.self; /** @type {WorkerLocation} */ WorkerGlobalScope.prototype.location; /** * Closes the worker represented by this WorkerGlobalScope. */ WorkerGlobalScope.prototype.close = function() {}; /** * Sent when the worker encounters an error. * @type {?function(!Event)} */ WorkerGlobalScope.prototype.onerror; /** * Sent when the worker goes offline. * @type {?function(!Event)} */ WorkerGlobalScope.prototype.onoffline; /** * Sent when the worker goes online. * @type {?function(!Event)} */ WorkerGlobalScope.prototype.ononline; /** * @see http://dev.w3.org/html5/workers/ * @interface * @extends {WorkerGlobalScope} */ function DedicatedWorkerGlobalScope() {} /** * Posts a message to creator of this worker. * @param {*} message * @param {Array.=} opt_transfer */ DedicatedWorkerGlobalScope.prototype.postMessage = function(message, opt_transfer) {}; /** * Posts a message to creator of this worker. * @param {*} message * @param {Array.=} opt_transfer */ DedicatedWorkerGlobalScope.prototype.webkitPostMessage = function(message, opt_transfer) {}; /** * Sent when the creator posts a message to this worker. * @type {?function(!MessageEvent)} */ DedicatedWorkerGlobalScope.prototype.onmessage = function() {}; /** * @see http://dev.w3.org/html5/workers/ * @interface * @extends {WorkerGlobalScope} */ function SharedWorkerGlobalScope() {} /** @type {string} */ SharedWorkerGlobalScope.prototype.name; /** * Sent when a connection to this worker is opened. * @type {?function(!Event)} */ SharedWorkerGlobalScope.prototype.onconnect = function() {}; /** @type {Element} */ HTMLElement.prototype.contextMenu; /** @type {boolean} */ HTMLElement.prototype.draggable; /** * This is actually a DOMSettableTokenList property. However since that * interface isn't currently defined and no known browsers implement this * feature, just define the property for now. * * @const * @type {Object} */ HTMLElement.prototype.dropzone; /** * @see http://www.w3.org/TR/html5/dom.html#dom-getelementsbyclassname * @param {string} classNames * @return {!NodeList} * @nosideeffects */ HTMLElement.prototype.getElementsByClassName = function(classNames) {}; // NOTE: Document.prototype.getElementsByClassName is in gecko_dom.js /** @type {boolean} */ HTMLElement.prototype.hidden; /** @type {boolean} */ HTMLElement.prototype.spellcheck; /** @type {string} */ HTMLAnchorElement.prototype.hash; /** @type {string} */ HTMLAnchorElement.prototype.host; /** @type {string} */ HTMLAnchorElement.prototype.hostname; /** @type {string} */ HTMLAnchorElement.prototype.pathname; /** @type {string} */ HTMLAnchorElement.prototype.port; /** @type {string} */ HTMLAnchorElement.prototype.protocol; /** @type {string} */ HTMLAnchorElement.prototype.search; /** @type {string} */ HTMLInputElement.prototype.autocomplete; /** @type {string} */ HTMLInputElement.prototype.dirname; /** @type {FileList} */ HTMLInputElement.prototype.files; /** @type {string} */ HTMLInputElement.prototype.list; /** @type {string} */ HTMLInputElement.prototype.max; /** @type {string} */ HTMLInputElement.prototype.min; /** @type {string} */ HTMLInputElement.prototype.pattern; /** @type {boolean} */ HTMLInputElement.prototype.multiple; /** @type {string} */ HTMLInputElement.prototype.placeholder; /** @type {boolean} */ HTMLInputElement.prototype.required; /** @type {string} */ HTMLInputElement.prototype.step; /** * @constructor * @extends {HTMLElement} */ function HTMLMediaElement() {} /** @type {MediaError} */ HTMLMediaElement.prototype.error; /** @type {string} */ HTMLMediaElement.prototype.src; /** @type {string} */ HTMLMediaElement.prototype.currentSrc; /** @type {number} */ HTMLMediaElement.prototype.networkState; /** @type {boolean} */ HTMLMediaElement.prototype.autobuffer; /** @type {TimeRanges} */ HTMLMediaElement.prototype.buffered; /** * Loads the media element. */ HTMLMediaElement.prototype.load = function() {}; /** * @param {string} type Type of the element in question in question. * @return {string} Whether it can play the type. * @nosideeffects */ HTMLMediaElement.prototype.canPlayType = function(type) {}; /** @type {number} */ HTMLMediaElement.prototype.readyState; /** @type {boolean} */ HTMLMediaElement.prototype.seeking; /** @type {number} */ HTMLMediaElement.prototype.currentTime; /** @type {number} */ HTMLMediaElement.prototype.startTime; /** @type {number} */ HTMLMediaElement.prototype.duration; /** @type {boolean} */ HTMLMediaElement.prototype.paused; /** @type {number} */ HTMLMediaElement.prototype.defaultPlaybackRate; /** @type {number} */ HTMLMediaElement.prototype.playbackRate; /** @type {TimeRanges} */ HTMLMediaElement.prototype.played; /** @type {TimeRanges} */ HTMLMediaElement.prototype.seekable; /** @type {boolean} */ HTMLMediaElement.prototype.ended; /** @type {boolean} */ HTMLMediaElement.prototype.autoplay; /** @type {boolean} */ HTMLMediaElement.prototype.loop; /** * Starts playing the media. */ HTMLMediaElement.prototype.play = function() {}; /** * Pauses the media. */ HTMLMediaElement.prototype.pause = function() {}; /** @type {boolean} */ HTMLMediaElement.prototype.controls; /** @type {number} */ HTMLMediaElement.prototype.volume; /** @type {boolean} */ HTMLMediaElement.prototype.muted; /** * @constructor * @extends {HTMLMediaElement} */ function HTMLAudioElement() {} /** * @constructor * @extends {HTMLMediaElement} */ function HTMLVideoElement() {} /** * Starts displaying the video in full screen mode. */ HTMLVideoElement.prototype.webkitEnterFullscreen = function() {}; /** * Starts displaying the video in full screen mode. */ HTMLVideoElement.prototype.webkitEnterFullScreen = function() {}; /** * Stops displaying the video in full screen mode. */ HTMLVideoElement.prototype.webkitExitFullscreen = function() {}; /** * Stops displaying the video in full screen mode. */ HTMLVideoElement.prototype.webkitExitFullScreen = function() {}; /** @type {string} */ HTMLVideoElement.prototype.width; /** @type {string} */ HTMLVideoElement.prototype.height; /** @type {number} */ HTMLVideoElement.prototype.videoWidth; /** @type {number} */ HTMLVideoElement.prototype.videoHeight; /** @type {string} */ HTMLVideoElement.prototype.poster; /** @type {boolean} */ HTMLVideoElement.prototype.webkitSupportsFullscreen; /** @type {boolean} */ HTMLVideoElement.prototype.webkitDisplayingFullscreen; /** * @constructor */ function MediaError() {} /** @type {number} */ MediaError.prototype.code; // HTML5 MessageChannel /** * @see http://dev.w3.org/html5/spec/comms.html#messagechannel * @constructor */ function MessageChannel() {} /** * Returns the first port. * @type {!MessagePort} */ MessageChannel.prototype.port1; /** * Returns the second port. * @type {!MessagePort} */ MessageChannel.prototype.port2; // HTML5 MessagePort /** * @see http://dev.w3.org/html5/spec/comms.html#messageport * @constructor * @implements {EventTarget} * @implements {Transferable} */ function MessagePort() {} /** @override */ MessagePort.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ MessagePort.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ MessagePort.prototype.dispatchEvent = function(evt) {}; /** * Posts a message through the channel, optionally with the given * Array of Transferables. * @param {*} message * @param {Array.=} opt_transfer */ MessagePort.prototype.postMessage = function(message, opt_transfer) { }; /** * Begins dispatching messages received on the port. */ MessagePort.prototype.start = function() {}; /** * Disconnects the port, so that it is no longer active. */ MessagePort.prototype.close = function() {}; /** * @type {?function(!MessageEvent)} */ MessagePort.prototype.onmessage; // HTML5 MessageEvent class /** * @see http://dev.w3.org/html5/spec/comms.html#messageevent * @constructor * @extends {Event} */ function MessageEvent() {} /** * Returns the data of the message. * @type {*} */ MessageEvent.prototype.data; /** * Returns the origin of the message, for server-sent events and cross-document * messaging. * @type {string} */ MessageEvent.prototype.origin; /** * Returns the last event ID, for server-sent events. * @type {string} */ MessageEvent.prototype.lastEventId; /** * Returns the last event ID, for server-sent events. * @type {Window} */ MessageEvent.prototype.source; /** * Returns the Array of MessagePorts sent with the message, for cross-document * messaging and channel messaging. * @type {Array.} */ MessageEvent.prototype.ports; /** * Initializes the event in a manner analogous to the similarly-named methods in * the DOM Events interfaces. * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {*} dataArg * @param {string} originArg * @param {string} lastEventIdArg * @param {Window} sourceArg * @param {Array.} portsArg * @override */ MessageEvent.prototype.initMessageEvent = function(typeArg, canBubbleArg, cancelableArg, dataArg, originArg, lastEventIdArg, sourceArg, portsArg) {}; /** * Initializes the event in a manner analogous to the similarly-named methods in * the DOM Events interfaces. * @param {string} namespaceURI * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {*} dataArg * @param {string} originArg * @param {string} lastEventIdArg * @param {Window} sourceArg * @param {Array.} portsArg */ MessageEvent.prototype.initMessageEventNS = function(namespaceURI, typeArg, canBubbleArg, cancelableArg, dataArg, originArg, lastEventIdArg, sourceArg, portsArg) {}; /** * HTML5 DataTransfer class * @see http://dev.w3.org/html5/spec/dnd.html#the-dragevent-and-datatransfer-interfaces * @constructor */ function DataTransfer() {} /** @type {string} */ DataTransfer.prototype.dropEffect; /** @type {string} */ DataTransfer.prototype.effectAllowed; /** @type {Array.} */ DataTransfer.prototype.types; /** @type {FileList} */ DataTransfer.prototype.files; /** * @param {string=} opt_format Format for which to remove data. */ DataTransfer.prototype.clearData = function(opt_format) {}; /** * @param {string} format Format for which to set data. * @param {string} data Data to add. */ DataTransfer.prototype.setData = function(format, data) {}; /** * @param {string} format Format for which to set data. * @return {string} Data for the given format. */ DataTransfer.prototype.getData = function(format) { return ''; }; /** * @param {HTMLElement} img The image to use when dragging. * @param {number} x Horizontal position of the cursor. * @param {number} y Vertical position of the cursor. */ DataTransfer.prototype.setDragImage = function(img, x, y) {}; /** * @param {HTMLElement} elem Element to receive drag result events. */ DataTransfer.prototype.addElement = function(elem) {}; /** * Addition for accessing clipboard file data that are part of the proposed * HTML5 spec. * @type {DataTransfer} */ MouseEvent.prototype.dataTransfer; /** * @constructor * @extends {Event} */ function ProgressEvent() {} /** @type {number} */ ProgressEvent.prototype.total; /** @type {number} */ ProgressEvent.prototype.loaded; /** @type {boolean} */ ProgressEvent.prototype.lengthComputable; /** * @constructor */ function TimeRanges() {} /** @type {number} */ TimeRanges.prototype.length; /** * @param {number} index The index. * @return {number} The start time of the range at index. * @throws {DOMException} */ TimeRanges.prototype.start = function(index) { return 0; }; /** * @param {number} index The index. * @return {number} The end time of the range at index. * @throws {DOMException} */ TimeRanges.prototype.end = function(index) { return 0; }; // HTML5 Web Socket class /** * @see http://dev.w3.org/html5/websockets/ * @constructor * @param {string} url * @param {string=} opt_protocol * @implements {EventTarget} */ function WebSocket(url, opt_protocol) {} /** @override */ WebSocket.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ WebSocket.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ WebSocket.prototype.dispatchEvent = function(evt) {}; /** * Returns the URL value that was passed to the constructor. * @type {string} */ WebSocket.prototype.URL; /** * The connection has not yet been established. * @type {number} */ WebSocket.prototype.CONNECTING = 0; /** * The Web Socket connection is established and communication is possible. * @type {number} */ WebSocket.prototype.OPEN = 1; /** * The connection has been closed or could not be opened. * @type {number} */ WebSocket.prototype.CLOSED = 2; /** * Represents the state of the connection. * @type {number} */ WebSocket.prototype.readyState; /** * Returns the number of bytes that have been queued but not yet sent. * @type {number} */ WebSocket.prototype.bufferedAmount; /** * An event handler called on open event. * @type {?function(!Event)} */ WebSocket.prototype.onopen; /** * An event handler called on message event. * @type {?function(!MessageEvent)} */ WebSocket.prototype.onmessage; /** * An event handler called on close event. * @type {?function(!Event)} */ WebSocket.prototype.onclose; /** * Transmits data using the connection. * @param {string|ArrayBuffer} data * @return {boolean} */ WebSocket.prototype.send = function(data) {}; /** * Closes the Web Socket connection or connection attempt, if any. */ WebSocket.prototype.close = function() {}; /** * @type {string} Sets the type of data (blob or arraybuffer) for binary data. */ WebSocket.prototype.binaryType; // HTML5 History /** * Pushes a new state into the session history. * @see http://www.w3.org/TR/html5/history.html#the-history-interface * @param {*} data New state. * @param {string} title The title for a new session history entry. * @param {string=} opt_url The URL for a new session history entry. */ History.prototype.pushState = function(data, title, opt_url) {}; /** * Replaces the current state in the session history. * @see http://www.w3.org/TR/html5/history.html#the-history-interface * @param {*} data New state. * @param {string} title The title for a session history entry. * @param {string=} opt_url The URL for a new session history entry. */ History.prototype.replaceState = function(data, title, opt_url) {}; /** * @see http://www.w3.org/TR/html5/history.html#event-definitions * @constructor * @extends {Event} */ function PopStateEvent() {} /** * @type {*} */ PopStateEvent.prototype.state; /** * Initializes the event after it has been created with document.createEvent * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {*} stateArg */ PopStateEvent.prototype.initPopStateEvent = function(typeArg, canBubbleArg, cancelableArg, stateArg) {}; /** * @see http://www.w3.org/TR/html5/history.html#event-definitions * @constructor * @extends {Event} */ function HashChangeEvent() {} /** @type {string} */ HashChangeEvent.prototype.oldURL; /** @type {string} */ HashChangeEvent.prototype.newURL; /** * Initializes the event after it has been created with document.createEvent * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {string} oldURLArg * @param {string} newURLArg */ HashChangeEvent.prototype.initHashChangeEvent = function(typeArg, canBubbleArg, cancelableArg, oldURLArg, newURLArg) {}; /** * @see http://www.w3.org/TR/html5/history.html#event-definitions * @constructor * @extends {Event} */ function PageTransitionEvent() {} /** @type {boolean} */ PageTransitionEvent.prototype.persisted; /** * Initializes the event after it has been created with document.createEvent * @param {string} typeArg * @param {boolean} canBubbleArg * @param {boolean} cancelableArg * @param {*} persistedArg */ PageTransitionEvent.prototype.initPageTransitionEvent = function(typeArg, canBubbleArg, cancelableArg, persistedArg) {}; /** * @constructor */ function FileList() {} /** @type {number} */ FileList.prototype.length; /** * @param {number} i File to return from the list. * @return {File} The ith file in the list. * @nosideeffects */ FileList.prototype.item = function(i) { return null; }; /** * @type {boolean} * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials */ XMLHttpRequest.prototype.withCredentials; /** * @type {XMLHttpRequestUpload} * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-upload-attribute */ XMLHttpRequest.prototype.upload; /** * @param {string} mimeType The mime type to override with. */ XMLHttpRequest.prototype.overrideMimeType = function(mimeType) {}; /** * @type {string} * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-responsetype-attribute */ XMLHttpRequest.prototype.responseType; /** * @type {*} * @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-responsetype-attribute */ XMLHttpRequest.prototype.response; /** * @type {ArrayBuffer} * Implemented as a draft spec in Firefox 4 as the way to get a requested array * buffer from an XMLHttpRequest. * @see https://developer.mozilla.org/En/Using_XMLHttpRequest#Receiving_binary_data_using_JavaScript_typed_arrays */ XMLHttpRequest.prototype.mozResponseArrayBuffer; /** * XMLHttpRequestEventTarget defines events for checking the status of a data * transfer between a client and a server. This should be a common base class * for XMLHttpRequest and XMLHttpRequestUpload. * * @constructor * @implements {EventTarget} */ function XMLHttpRequestEventTarget() {} /** @override */ XMLHttpRequestEventTarget.prototype.addEventListener = function( type, listener, useCapture) {}; /** @override */ XMLHttpRequestEventTarget.prototype.removeEventListener = function( type, listener, useCapture) {}; /** @override */ XMLHttpRequestEventTarget.prototype.dispatchEvent = function(evt) {}; /** * An event target to track the status of an upload. * * @constructor * @extends {XMLHttpRequestEventTarget} */ function XMLHttpRequestUpload() {} /** * @param {number=} opt_width * @param {number=} opt_height * @constructor * @extends {HTMLImageElement} */ function Image(opt_width, opt_height) {} /** * Dataset collection. * This is really a DOMStringMap but it behaves close enough to an object to * pass as an object. * @type {Object} * @const */ HTMLElement.prototype.dataset; /** * @constructor */ function DOMTokenList() {} /** * Returns the number of CSS classes applied to this Element. * @type {number} */ DOMTokenList.prototype.length; /** * @param {number} index The index of the item to return. * @return {string} The CSS class at the specified index. * @nosideeffects */ DOMTokenList.prototype.item = function(index) {}; /** * @param {string} token The CSS class to check for. * @return {boolean} Whether the CSS class has been applied to the Element. * @nosideeffects */ DOMTokenList.prototype.contains = function(token) {}; /** * @param {string} token The CSS class to add to this element. */ DOMTokenList.prototype.add = function(token) {}; /** * @param {string} token The CSS class to remove from this element. */ DOMTokenList.prototype.remove = function(token) {}; /** * @param {string} token The CSS class to toggle from this element. * @return {boolean} False if the token was removed; True otherwise. */ DOMTokenList.prototype.toggle = function(token) {}; /** * @return {string} A stringified representation of CSS classes. * @nosideeffects * @override */ DOMTokenList.prototype.toString = function() {}; /** * A better interface to CSS classes than className. * @type {DOMTokenList} * @see http://www.w3.org/TR/html5/elements.html#dom-classlist * @const */ HTMLElement.prototype.classList; /** * @param {number} length The length in bytes * @constructor * @noalias * @throws {Error} * @nosideeffects * @implements {Transferable} */ function ArrayBuffer(length) {} /** @type {number} */ ArrayBuffer.prototype.byteLength; /** * @param {number} begin * @param {number=} opt_end * @return {!ArrayBuffer} * @nosideeffects */ ArrayBuffer.prototype.slice = function(begin, opt_end) {}; /** * @constructor * @noalias */ function ArrayBufferView() {} /** @type {!ArrayBuffer} */ ArrayBufferView.prototype.buffer; /** @type {number} */ ArrayBufferView.prototype.byteOffset; /** @type {number} */ ArrayBufferView.prototype.byteLength; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} If the user passes a backing array, then indexed * accesses will modify the backing array. JSCompiler does not model * this well. In other words, if you have: * * var x = new ArrayBuffer(1); * var y = new Int8Array(x); * y[0] = 2; * * JSCompiler will not recognize that the last assignment modifies x. * We workaround this by marking all these arrays as @modifies {arguments}, * to introduce the possibility that x aliases y. */ function Int8Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Int8Array.BYTES_PER_ELEMENT; /** @type {number} */ Int8Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Int8Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Int8Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Int8Array} * @nosideeffects */ Int8Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Uint8Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Uint8Array.BYTES_PER_ELEMENT; /** @type {number} */ Uint8Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Uint8Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Uint8Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Uint8Array} * @nosideeffects */ Uint8Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Uint8ClampedArray(length, opt_byteOffset, opt_length) {} /** @type {number} */ Uint8ClampedArray.BYTES_PER_ELEMENT; /** @type {number} */ Uint8ClampedArray.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Uint8ClampedArray.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Uint8ClampedArray.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Uint8ClampedArray} * @nosideeffects */ Uint8ClampedArray.prototype.subarray = function(begin, opt_end) {}; /** * @typedef {Uint8ClampedArray} * @deprecated CanvasPixelArray has been replaced by Uint8ClampedArray * in the latest spec. * @see http://www.w3.org/TR/2dcontext/#imagedata */ var CanvasPixelArray; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Int16Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Int16Array.BYTES_PER_ELEMENT; /** @type {number} */ Int16Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Int16Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Int16Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Int16Array} * @nosideeffects */ Int16Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Uint16Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Uint16Array.BYTES_PER_ELEMENT; /** @type {number} */ Uint16Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Uint16Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Uint16Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Uint16Array} * @nosideeffects */ Uint16Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Int32Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Int32Array.BYTES_PER_ELEMENT; /** @type {number} */ Int32Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Int32Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Int32Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Int32Array} * @nosideeffects */ Int32Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Uint32Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Uint32Array.BYTES_PER_ELEMENT; /** @type {number} */ Uint32Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Uint32Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Uint32Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Uint32Array} * @nosideeffects */ Uint32Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Float32Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Float32Array.BYTES_PER_ELEMENT; /** @type {number} */ Float32Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Float32Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Float32Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Float32Array} * @nosideeffects */ Float32Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {number|ArrayBufferView|Array.|ArrayBuffer} length or array * or buffer * @param {number=} opt_byteOffset * @param {number=} opt_length * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @modifies {arguments} */ function Float64Array(length, opt_byteOffset, opt_length) {} /** @type {number} */ Float64Array.BYTES_PER_ELEMENT; /** @type {number} */ Float64Array.prototype.BYTES_PER_ELEMENT; /** @type {number} */ Float64Array.prototype.length; /** * @param {ArrayBufferView|Array.} array * @param {number=} opt_offset */ Float64Array.prototype.set = function(array, opt_offset) {}; /** * @param {number} begin * @param {number=} opt_end * @return {!Float64Array} * @nosideeffects */ Float64Array.prototype.subarray = function(begin, opt_end) {}; /** * @param {ArrayBuffer} buffer * @param {number=} opt_byteOffset * @param {number=} opt_byteLength * @extends {ArrayBufferView} * @constructor * @noalias * @throws {Error} * @nosideeffects */ function DataView(buffer, opt_byteOffset, opt_byteLength) {} /** * @param {number} byteOffset * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getInt8 = function(byteOffset) {}; /** * @param {number} byteOffset * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getUint8 = function(byteOffset) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getInt16 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getUint16 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getInt32 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getUint32 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getFloat32 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {boolean=} opt_littleEndian * @return {number} * @throws {Error} * @nosideeffects */ DataView.prototype.getFloat64 = function(byteOffset, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @throws {Error} */ DataView.prototype.setInt8 = function(byteOffset, value) {}; /** * @param {number} byteOffset * @param {number} value * @throws {Error} */ DataView.prototype.setUint8 = function(byteOffset, value) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setInt16 = function(byteOffset, value, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setUint16 = function(byteOffset, value, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setInt32 = function(byteOffset, value, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setUint32 = function(byteOffset, value, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setFloat32 = function( byteOffset, value, opt_littleEndian) {}; /** * @param {number} byteOffset * @param {number} value * @param {boolean=} opt_littleEndian * @throws {Error} */ DataView.prototype.setFloat64 = function( byteOffset, value, opt_littleEndian) {}; /** * Constraint Validation API properties and methods * @see http://www.w3.org/TR/2009/WD-html5-20090423/forms.html#the-constraint-validation-api */ /** @return {boolean} */ HTMLFormElement.prototype.checkValidity = function() {}; /** @type {boolean} */ HTMLFormElement.prototype.novalidate; /** @constructor */ function ValidityState() {} /** @type {boolean} */ ValidityState.prototype.customError; /** @type {boolean} */ ValidityState.prototype.patternMismatch; /** @type {boolean} */ ValidityState.prototype.rangeOverflow; /** @type {boolean} */ ValidityState.prototype.rangeUnderflow; /** @type {boolean} */ ValidityState.prototype.stepMismatch; /** @type {boolean} */ ValidityState.prototype.typeMismatch; /** @type {boolean} */ ValidityState.prototype.tooLong; /** @type {boolean} */ ValidityState.prototype.valid; /** @type {boolean} */ ValidityState.prototype.valueMissing; /** @type {boolean} */ HTMLButtonElement.prototype.autofocus; /** * @const * @type {NodeList} */ HTMLButtonElement.prototype.labels; /** @type {string} */ HTMLButtonElement.prototype.validationMessage; /** * @const * @type {ValidityState} */ HTMLButtonElement.prototype.validity; /** @type {boolean} */ HTMLButtonElement.prototype.willValidate; /** @return {boolean} */ HTMLButtonElement.prototype.checkValidity = function() {}; /** @param {string} message */ HTMLButtonElement.prototype.setCustomValidity = function(message) {}; /** @type {boolean} */ HTMLInputElement.prototype.autofocus; /** @type {boolean} */ HTMLInputElement.prototype.formNoValidate; /** * @const * @type {NodeList} */ HTMLInputElement.prototype.labels; /** @type {string} */ HTMLInputElement.prototype.validationMessage; /** * @const * @type {ValidityState} */ HTMLInputElement.prototype.validity; /** @type {boolean} */ HTMLInputElement.prototype.willValidate; /** @return {boolean} */ HTMLInputElement.prototype.checkValidity = function() {}; /** @param {string} message */ HTMLInputElement.prototype.setCustomValidity = function(message) {}; /** @type {Element} */ HTMLLabelElement.prototype.control; /** @type {boolean} */ HTMLSelectElement.prototype.autofocus; /** * @const * @type {NodeList} */ HTMLSelectElement.prototype.labels; /** @type {string} */ HTMLSelectElement.prototype.validationMessage; /** * @const * @type {ValidityState} */ HTMLSelectElement.prototype.validity; /** @type {boolean} */ HTMLSelectElement.prototype.willValidate; /** @return {boolean} */ HTMLSelectElement.prototype.checkValidity = function() {}; /** @param {string} message */ HTMLSelectElement.prototype.setCustomValidity = function(message) {}; /** @type {boolean} */ HTMLTextAreaElement.prototype.autofocus; /** * @const * @type {NodeList} */ HTMLTextAreaElement.prototype.labels; /** @type {string} */ HTMLTextAreaElement.prototype.validationMessage; /** * @const * @type {ValidityState} */ HTMLTextAreaElement.prototype.validity; /** @type {boolean} */ HTMLTextAreaElement.prototype.willValidate; /** @return {boolean} */ HTMLTextAreaElement.prototype.checkValidity = function() {}; /** @param {string} message */ HTMLTextAreaElement.prototype.setCustomValidity = function(message) {}; /** * @constructor * @extends {HTMLElement} * @see http://www.w3.org/TR/html5/the-embed-element.html#htmlembedelement */ function HTMLEmbedElement() {} /** * @type {string} * @see http://www.w3.org/TR/html5/dimension-attributes.html#dom-dim-width */ HTMLEmbedElement.prototype.width; /** * @type {string} * @see http://www.w3.org/TR/html5/dimension-attributes.html#dom-dim-height */ HTMLEmbedElement.prototype.height; /** * @type {string} * @see http://www.w3.org/TR/html5/the-embed-element.html#dom-embed-src */ HTMLEmbedElement.prototype.src; /** * @type {string} * @see http://www.w3.org/TR/html5/the-embed-element.html#dom-embed-type */ HTMLEmbedElement.prototype.type; // Fullscreen APIs. /** * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-element-requestfullscreen */ Element.prototype.requestFullscreen = function() {}; /** * @type {boolean} * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-fullscreenenabled */ Document.prototype.fullscreenEnabled; /** * @type {Element} * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-fullscreenelement */ Document.prototype.fullscreenElement; /** * @see http://www.w3.org/TR/2012/WD-fullscreen-20120703/#dom-document-exitfullscreen */ Document.prototype.exitFullscreen = function() {}; // Externs definitions of browser current implementations. // Firefox 10 implementation. Element.prototype.mozRequestFullScreen = function() {}; Element.prototype.mozRequestFullScreenWithKeys = function() {}; /** @type {boolean} */ Document.prototype.mozFullScreen; Document.prototype.mozCancelFullScreen = function() {}; /** @type {Element} */ Document.prototype.mozFullScreenElement; /** @type {boolean} */ Document.prototype.mozFullScreenEnabled; // Chrome 21 implementation. /** * The current fullscreen element for the document is set to this element. * Valid only for Webkit browsers. * @param {number=} opt_allowKeyboardInput Whether keyboard input is desired. * Should use ALLOW_KEYBOARD_INPUT constant. */ Element.prototype.webkitRequestFullScreen = function(opt_allowKeyboardInput) {}; /** * The current fullscreen element for the document is set to this element. * Valid only for Webkit browsers. * @param {number=} opt_allowKeyboardInput Whether keyboard input is desired. * Should use ALLOW_KEYBOARD_INPUT constant. */ Element.prototype.webkitRequestFullscreen = function(opt_allowKeyboardInput) {}; /** @type {boolean} */ Document.prototype.webkitIsFullScreen; Document.prototype.webkitCancelFullScreen = function() {}; /** @type {Element} */ Document.prototype.webkitCurrentFullScreenElement; /** @type {boolean} */ Document.prototype.webkitFullScreenKeyboardInputAllowed; /** @type {number} */ Element.ALLOW_KEYBOARD_INPUT = 1; /** @type {number} */ Element.prototype.ALLOW_KEYBOARD_INPUT = 1; /** @constructor */ function MutationObserverInit() {} /** @type {boolean} */ MutationObserverInit.prototype.childList; /** @type {boolean} */ MutationObserverInit.prototype.attributes; /** @type {boolean} */ MutationObserverInit.prototype.characterData; /** @type {boolean} */ MutationObserverInit.prototype.subtree; /** @type {boolean} */ MutationObserverInit.prototype.attributeOldValue; /** @type {boolean} */ MutationObserverInit.prototype.characterDataOldValue; /** @type {Array.} */ MutationObserverInit.prototype.attributeFilter; /** @constructor */ function MutationRecord() {} /** @type {string} */ MutationRecord.prototype.type; /** @type {Node} */ MutationRecord.prototype.target; /** @type {NodeList} */ MutationRecord.prototype.addedNodes; /** @type {NodeList} */ MutationRecord.prototype.removedNodes; /** @type {Node} */ MutationRecord.prototype.previouSibling; /** @type {Node} */ MutationRecord.prototype.nextSibling; /** @type {?string} */ MutationRecord.prototype.attributeName; /** @type {?string} */ MutationRecord.prototype.attributeNamespace; /** @type {?string} */ MutationRecord.prototype.oldValue; /** * @see http://www.w3.org/TR/domcore/#mutation-observers * @param {function(Array.)} callback * @constructor */ function MutationObserver(callback) {} /** * @param {Node} target * @param {MutationObserverInit=} options */ MutationObserver.prototype.observe = function(target, options) {}; MutationObserver.prototype.disconnect = function() {}; /** * @type {function(new:MutationObserver, function(Array.))} */ Window.prototype.MutationObserver; /** * @type {function(new:MutationObserver, function(Array.))} */ Window.prototype.WebKitMutationObserver; /** * @type {function(new:MutationObserver, function(Array.))} */ Window.prototype.MozMutationObserver; closure-compiler-20130227+dfsg1/externs/window.js0000644000175000017500000001062312115204405017711 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview JavaScript Built-Ins for windows properties. * * @externs */ // Window properties // Only common properties are here. Others such as open() // should be used with an explicit Window object. /** * @type {!Window} * @see https://developer.mozilla.org/en/DOM/window.top * @const */ var top; /** * @type {Navigator} * @see https://developer.mozilla.org/en/DOM/window.navigator * @const */ var navigator; /** * @type {!HTMLDocument} * @see https://developer.mozilla.org/en/DOM/window.document * @const */ var document; /** * @type {Location} * @see https://developer.mozilla.org/en/DOM/window.location * @const * @suppress {duplicate} */ var location; /** * @see https://developer.mozilla.org/En/DOM/Window.screen * @const */ var screen; /** * @type {!Window} * @see https://developer.mozilla.org/En/DOM/Window.self * @const */ var self; // Magic functions for Firefox's LiveConnect. // We'll probably never use these in practice. But redefining them // will fire up the JVM, so we want to reserve the symbol names. /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaArray */ var JavaArray; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaClass */ var JavaClass; // We just ripped this from the FF source; it doesn't appear to be // publicly documented. var JavaMember; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaObject */ var JavaObject; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/JavaPackage */ var JavaPackage; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Packages */ var Packages; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/java */ var java; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/netscape */ var netscape; /** * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/sun */ var sun; /** * @see https://developer.mozilla.org/en/DOM/window.alert */ function alert(x) {} /** * @param {number|undefined|null} immediateID * @see https://developer.mozilla.org/en-US/docs/DOM/window.clearImmediate * @see http://msdn.microsoft.com/en-us/library/ie/hh924825(v=vs.85).aspx */ function clearImmediate(immediateID) {} /** * @param {number|undefined?} intervalID * @see https://developer.mozilla.org/en/DOM/window.clearInterval */ function clearInterval(intervalID) {} /** * @param {number|undefined?} timeoutID * @see https://developer.mozilla.org/en/DOM/window.clearTimeout */ function clearTimeout(timeoutID) {} /** * @see https://developer.mozilla.org/en/DOM/window.confirm */ function confirm(x) {} /** * @see https://developer.mozilla.org/en/DOM/window.dump */ function dump(x) {} /** * @param {string} message * @param {string=} opt_value * @return {?string} * @see https://developer.mozilla.org/en/DOM/window.prompt */ function prompt(message, opt_value) {} /** * @param {function()} callback * @return {number} * @see https://developer.mozilla.org/en-US/docs/DOM/window.setImmediate * @see http://msdn.microsoft.com/en-us/library/ie/hh773176(v=vs.85).aspx */ function setImmediate(callback) {} /** * @param {Function|string} callback * @param {number} delay * @return {number} * @see https://developer.mozilla.org/en/DOM/window.setInterval * @see https://msdn.microsoft.com/en-us/library/ms536749(v=VS.85).aspx */ function setInterval(callback, delay) {} /** * @param {Function|string} callback * @param {number} delay * @return {number} * @see https://developer.mozilla.org/en/DOM/window.setTimeout * @see https://msdn.microsoft.com/en-us/library/ms536753(VS.85).aspx */ function setTimeout(callback, delay) {} closure-compiler-20130227+dfsg1/externs/gecko_event.js0000644000175000017500000000466012115204405020677 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over * W3C's event specification by Gecko. This file depends on * w3c_event.js. * * @externs */ // TODO: Almost all of it has not been annotated with types. /** @type {number} */ Event.prototype.HORIZONTAL_AXIS; /** @type {number} */ Event.prototype.VERTICAL_AXIS; /** @type {boolean} */ Event.prototype.altKey; /** @type {number} */ Event.prototype.axis; /** @type {number} */ Event.prototype.button; /** @type {boolean} */ Event.prototype.cancelBubble; /** @type {number} */ Event.prototype.charCode; /** @type {number} */ Event.prototype.clientX; /** @type {number} */ Event.prototype.clientY; /** @type {boolean} */ Event.prototype.ctrlKey; /** @type {number} */ Event.prototype.detail; /** @type {EventTarget} */ Event.prototype.explicitOriginalTarget; /** @type {boolean} */ Event.prototype.isChar; /** @type {number} */ Event.prototype.keyCode; /** @type {number} */ Event.prototype.layerX; /** @type {number} */ Event.prototype.layerY; /** @type {boolean} */ Event.prototype.metaKey; /** @type {EventTarget} */ Event.prototype.originalTarget; /** @type {number} */ Event.prototype.pageX; /** @type {number} */ Event.prototype.pageY; /** @type {EventTarget} */ Event.prototype.relatedTarget; /** @type {number} */ Event.prototype.screenX; /** @type {number} */ Event.prototype.screenY; /** @type {boolean} */ Event.prototype.shiftKey; /** @type {Window} */ Event.prototype.view; /** @type {number} */ Event.prototype.which; /** @type {Object} */ Event.prototype.state; /** @constructor */ function nsIDOMPageTransitionEvent() {} /** @type {boolean} */ nsIDOMPageTransitionEvent.prototype.persisted; //Methods Event.prototype.initKeyEvent; Event.prototype.initMouseEvent; Event.prototype.initUIEvent; Event.prototype.initMessageEvent; Event.prototype.preventBubble; Event.prototype.preventCapture; closure-compiler-20130227+dfsg1/externs/w3c_css3d.js0000644000175000017500000001141212115204405020172 0ustar apoapo/* * Copyright 2010 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. */ /** * @fileoverview Definitions for W3C's CSS 3D Transforms specification. * The whole file has been fully type annotated. Created from * http://www.w3.org/TR/css3-3d-transforms/ * * @externs */ /** * @constructor * @param {string} matrix * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ function CSSMatrix(matrix) {} /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m11; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m12; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m13; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m14; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m21; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m22; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m23; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m24; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m31; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m32; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m33; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m34; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m41; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m42; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m43; /** * @type {number} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.m44; /** * @param {string} string * @return {void} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.setMatrixValue = function(string) {}; /** * @param {CSSMatrix} secondMatrix * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.multiply = function(secondMatrix) {}; /** * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.inverse = function() {}; /** * @param {number} x * @param {number} y * @param {number} z * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.translate = function(x, y, z) {}; /** * @param {number} scaleX * @param {number} scaleY * @param {number} scaleZ * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.scale = function(scaleX, scaleY, scaleZ) {}; /** * @param {number} rotX * @param {number} rotY * @param {number} rotZ * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.rotate = function(rotX, rotY, rotZ) {}; /** * @param {number} x * @param {number} y * @param {number} z * @param {number} angle * @return {CSSMatrix} * @see http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface */ CSSMatrix.prototype.rotateAxisAngle = function(x, y, z, angle) {}; /** * @constructor * @param {string} matrix * @extends {CSSMatrix} * @see http://developer.apple.com/safari/library/documentation/AudioVideo/Reference/WebKitCSSMatrixClassReference/WebKitCSSMatrix/WebKitCSSMatrix.html#//apple_ref/javascript/instm/WebKitCSSMatrix/setMatrixValue */ function WebKitCSSMatrix(matrix) {} /** * @constructor * @param {string} matrix * @extends {CSSMatrix} * @see http://msdn.microsoft.com/en-us/library/windows/apps/hh453593.aspx */ function MSCSSMatrix(matrix) {} closure-compiler-20130227+dfsg1/externs/gecko_xml.js0000644000175000017500000000447512115204405020362 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the extensions over some of the * W3C's XML specifications by Gecko. This file depends on * w3c_xml.js. The whole file has been fully type annotated. * * @externs */ /** * XMLSerializer can be used to convert DOM subtree or DOM document into text. * XMLSerializer is available to unprivileged scripts. * * XMLSerializer is mainly useful for applications and extensions based on * Mozilla platform. While it's available to web pages, it's not part of any * standard and level of support in other browsers is unknown. * * @constructor */ function XMLSerializer() {} /** * Returns the serialized subtree in the form of a string * @param {Node} subtree * @return {string} */ XMLSerializer.prototype.serializeToString = function(subtree) {}; /** * The subtree rooted by the specified element is serialized to a byte stream * using the character set specified. * * @param {Node} subtree * @return {Object} */ XMLSerializer.prototype.serializeToStream = function(subtree) {}; /** * DOMParser is mainly useful for applications and extensions based on Mozilla * platform. While it's available to web pages, it's not part of any standard and * level of support in other browsers is unknown. * * @constructor */ function DOMParser() {} /** * The string passed in is parsed into a DOM document. * * Example: * var parser = new DOMParser(); * var doc = parser.parseFromString(aStr, "text/xml"); * * @param {string} src The UTF16 string to be parsed. * @param {string} type The content type of the string. * @return {Document} */ DOMParser.prototype.parseFromString = function(src, type) {}; /** * @type {function(new:DOMParser)} */ Window.prototype.DOMParser = function() {}; closure-compiler-20130227+dfsg1/externs/flash.js0000644000175000017500000001534712115204405017507 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for all the Flash Object JavaScript methods. This * file depends on w3c_dom2.js. * Created from * http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html * * @externs */ // Standard Methods. /** * Call a Flash function exported by ExternalInterface. * @param {string} xmlString The XML string passed to Flash. The outer element * should be {@code }. A sample invocation string: * {@code * test} * @return {string} The serialized return value from Flash that you can eval. */ HTMLObjectElement.prototype.CallFunction = function(xmlString) {}; /** * Returns the value of the Flash variable specified by varName or null if the * variable does not exist. * @param {string} varName The variable name. * @return {string?} The variable value. */ HTMLObjectElement.prototype.GetVariable = function(varName) {}; /** * Activates the frame number specified by {@code frameNumber} in the current * movie. * @param {number} frameNumber A non-negative integer frame number. */ HTMLObjectElement.prototype.GotoFrame = function(frameNumber) {}; /** * @return {boolean} Whether the movie is currently playing. */ HTMLObjectElement.prototype.IsPlaying = function() {}; /** * Loads the movie identified by {@code url} to the layer specified by {@code * layerNumber}. * @param {number} layerNumber The layer number. * @param {string} url The movie URL. */ HTMLObjectElement.prototype.LoadMovie = function(layerNumber, url) {}; /** * Pans a zoomed-in movie to the coordinates specified by x and y. Use mode to * specify whether the values for x and y are pixels or a percent of the window. * When mode is 0, the coordinates are pixels; when mode is 1, the coordinates * are percent of the window. * @param {number} x The x-coordinate. * @param {number} y The y-coordinate. * @param {number} mode The mode. */ HTMLObjectElement.prototype.Pan = function(x, y, mode) {}; /** * @return {number} The percent of the Flash Player movie that has streamed * into the browser so far; Possible values are from 0 to 100. */ HTMLObjectElement.prototype.PercentLoaded = function() {}; /** * Starts playing the movie. */ HTMLObjectElement.prototype.Play = function() {}; /** * Goes to the first frame. */ HTMLObjectElement.prototype.Rewind = function() {}; /** * Sets the value of the flash variable. * @param {string} variableName The variable name. * @param {string} value The value. */ HTMLObjectElement.prototype.SetVariable = function(variableName, value) {}; /** * Zooms in on a rectangular area of the movie. The units of the coordinates * are in twips (1440 units per inch). * @param {number} left The left coordinate. * @param {number} top The top coordinate. * @param {number} right The right coordinate. * @param {number} bottom The bottom coordinate. */ HTMLObjectElement.prototype.SetZoomRect = function(left, top, right, bottom) {}; /** * Stops playing the movie. */ HTMLObjectElement.prototype.StopPlay = function() {}; /** * @return {number} The total number of frames in the movie. */ HTMLObjectElement.prototype.TotalFrames = function() {}; /** * Zooms the view by a relative scale factor. * @param {number} percent The percentage scale factor, should be an integer. */ HTMLObjectElement.prototype.Zoom = function(percent) {}; // TellTarget Methods. /** * Executes the action in the timeline specified by {@code target} in the * specified frame. * @param {string} target The timeline. * @param {number} frameNumber The frame number. */ HTMLObjectElement.prototype.TCallFrame = function(target, frameNumber) {}; /** * Executes the action in the timeline specified by {@code target} in the * specified frame. * @param {string} target The timeline. * @param {string} label The frame label. */ HTMLObjectElement.prototype.TCallLabel = function(target, label) {}; /** * Returns the number of the current frame for the specified timeline. * @param {string} target The timeline. * @return {number} The number of the current frame. */ HTMLObjectElement.prototype.TCurentFrame = function(target) {}; /** * Returns the label of the current frame for the specified timeline. * @param {string} target The timeline. * @return {string} The label of the current frame, empty string if no * current frame. */ HTMLObjectElement.prototype.TCurrentLabel = function(target) {}; /** * Returns a string indicating the value of the property in the * specified timeline. * @param {string} target The timeline. * @param {number} property The integer corresponding to the desired property. * @return {string} The value of the property. */ HTMLObjectElement.prototype.TGetProperty = function(target, property) {}; /** * Returns a number indicating the value of the property in the specified * timeline. * @param {string} target The timeline. * @param {number} property The integer corresponding to the desired property. * @return {number} A number indicating the value of the property. */ HTMLObjectElement.prototype.TGetPropertyAsNumber = function(target, property) {}; /** * Goes to the specified frame number in the specified timeline. * @param {string} target The timeline. * @param {number} frameNumber The frame number. */ HTMLObjectElement.prototype.TGotoFrame = function(target, frameNumber) {}; /** * Goes to the specified frame label in the specified timeline. * @param {string} target The timeline. * @param {string} label The framelabel. */ HTMLObjectElement.prototype.TGotoLabel = function(target, label) {}; /** * Plays the specified timeline. * @param {number} target The timeline. */ HTMLObjectElement.prototype.TPlay = function(target) {}; /** * Sets the value of the property in the specified timeline. * @param {number} target The timeline. * @param {number} property The integer corresponding to the desired property. * @param {string|number} value The value. */ HTMLObjectElement.prototype.TSetProperty = function(target, property, value) {}; /** * Stops the specified timeline. * @param {number} target The timeline. */ HTMLObjectElement.prototype.TStopPlay = function(target) {}; closure-compiler-20130227+dfsg1/externs/es3.js0000644000175000017500000017151312115204405017102 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview ECMAScript 3 Built-Ins. This include common extensions so this * is actually ES3+Reality. * @externs */ // These built-ins are still needed for compilation. /** * @constructor * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments */ function Arguments() {} /** * @type {Function} * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/callee */ Arguments.prototype.callee; /** * Use the non-standard {@see Function.prototype.caller} property of a function * object instead. * @type {Function} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions/arguments/caller * @deprecated */ Arguments.prototype.caller; /** * @type {number} * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/length */ Arguments.prototype.length; /** * @type {!Arguments} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments */ var arguments; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Properties/Infinity * @const */ var Infinity; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Properties/NaN * @const */ var NaN; /** * @type {undefined} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Properties/undefined * @const */ var undefined; /** * @param {string} uri * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/decodeURI */ function decodeURI(uri) {} /** * @param {string} uri * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/decodeURIComponent */ function decodeURIComponent(uri) {} /** * @param {string} uri * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/encodeURI */ function encodeURI(uri) {} /** * @param {string} uri * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/encodeURIComponent */ function encodeURIComponent(uri) {} /** * Should only be used in browsers where encode/decodeURIComponent * are not present, as the latter handle fancy Unicode characters. * @param {string} str * @return {string} * @nosideeffects * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Predefined_Functions/escape_and_unescape_Functions */ function escape(str) {} /** * Should only be used in browsers where encode/decodeURIComponent * are not present, as the latter handle fancy Unicode characters. * @param {string} str * @return {string} * @nosideeffects * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Predefined_Functions/escape_and_unescape_Functions */ function unescape(str) {} /** * @param {*} num * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/isFinite */ function isFinite(num) {} /** * @param {*} num * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/isNaN */ function isNaN(num) {} /** * @param {*} num * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/parseFloat */ function parseFloat(num) {} /** * Parse an integer. Use of {@code parseInt} without {@code base} is strictly * banned in Google. If you really want to parse octal or hex based on the * leader, then pass {@code undefined} as the base. * * @param {*} num * @param {number|undefined} base * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/parseInt */ function parseInt(num, base) {} /** * @param {string} code * @return {*} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Functions/eval */ function eval(code) {} /** * @constructor * @param {*=} opt_value * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object */ function Object(opt_value) {} /** * The constructor of the current object. * @type {Function} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/constructor */ Object.prototype.constructor = function() {}; /** * Evaluates a string of JavaScript code in the context of the specified object. * Considered deprecated. * * @param {string} code * @return {Object} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/eval */ Object.prototype.eval = function(code) {}; /** * Binds an object's property to a function to be called when that property is * looked up. * Mozilla-only. * * @param {string} sprop * @param {Function} fun * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/defineGetter */ Object.prototype.__defineGetter__ = function(sprop, fun) {}; /** * Binds an object's property to a function to be called when an attempt is made * to set that property. * Mozilla-only. * * @param {string} sprop * @param {Function} fun * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/defineSetter */ Object.prototype.__defineSetter__ = function(sprop, fun) {}; /** * Returns whether the object has a property with the specified name. * * @param {*} propertyName Implicitly cast to a string. * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/hasOwnProperty */ Object.prototype.hasOwnProperty = function(propertyName) {}; /** * Returns whether an object exists in another object's prototype chain. * * @param {Object} other * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/isPrototypeOf */ Object.prototype.isPrototypeOf = function(other) {}; /** * Return the function bound as a getter to the specified property. * Mozilla-only. * * @param {string} sprop a string containing the name of the property whose * getter should be returned * @return {Function} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/lookupGetter */ Object.prototype.__lookupGetter__ = function(sprop) {}; /** * Return the function bound as a setter to the specified property. * Mozilla-only. * * @param {string} sprop a string containing the name of the property whose * setter should be returned. * @return {Function} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/lookupSetter */ Object.prototype.__lookupSetter__ = function(sprop) {}; /** * Executes a function when a non-existent method is called on an object. * Mozilla-only. * * @param {Function} fun * @return {*} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/noSuchMethod */ Object.prototype.__noSuchMethod__ = function(fun) {}; /** * Points to an object's context. For top-level objects, this is the e.g. window. * Mozilla-only. * * @type {Object} * @deprecated * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/parent */ Object.prototype.__parent__; /** * Points to the object which was used as prototype when the object was instantiated. * Mozilla-only. * * Will be null on Object.prototype. * * @type {Object} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/proto */ Object.prototype.__proto__; /** * Determine whether the specified property in an object can be enumerated by a * for..in loop, with the exception of properties inherited through the * prototype chain. * * @param {string} propertyName * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/propertyIsEnumerable */ Object.prototype.propertyIsEnumerable = function(propertyName) {}; /** * Returns a localized string representing the object. * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/toLocaleString */ Object.prototype.toLocaleString = function() {}; /** * Returns a string representing the source code of the object. * Mozilla-only. * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/toSource */ Object.prototype.toSource = function() {}; /** * Returns a string representing the object. * @this {*} * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/toString */ Object.prototype.toString = function() {}; /** * Removes a watchpoint set with the {@see Object.prototype.watch} method. * Mozilla-only. * @param {string} prop The name of a property of the object. * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/unwatch */ Object.prototype.unwatch = function(prop) {}; /** * Returns the object's {@code this} value. * @return {*} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/valueOf */ Object.prototype.valueOf = function() {}; /** * Sets a watchpoint method. * Mozilla-only. * @param {string} prop The name of a property of the object. * @param {Function} handler A function to call. * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Object/watch */ Object.prototype.watch = function(prop, handler) {}; /** * @constructor * @param {...*} var_args * @nosideeffects * @throws {Error} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function */ function Function(var_args) {} /** * @param {...*} var_args * @return {*} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/call */ Function.prototype.call = function(var_args) {}; /** * @param {...*} var_args * @return {*} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply */ Function.prototype.apply = function(var_args) {}; Function.prototype.arguments; /** * @type {number} * @deprecated * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/arity */ Function.prototype.arity; /** * Nonstandard; Mozilla and JScript only. * @type {Function} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/caller */ Function.prototype.caller; /** * Expected number of arguments. * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/length */ Function.prototype.length; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/name */ Function.prototype.name; /** * @this {Function} * @return {string} * @nosideeffects * @override */ Function.prototype.toString = function() {}; /** * @constructor * @param {...*} var_args * @return {!Array} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array */ function Array(var_args) {} // Functions: // TODO(nicksantos): Change @this {Object} to @this { {length: number} } /** * Returns a new array comprised of this array joined with other array(s) * and/or value(s). * * @param {...*} var_args * @return {!Array} * @this {Object} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/concat */ Array.prototype.concat = function(var_args) {}; /** * Joins all elements of an array into a string. * * @param {*=} opt_separator Specifies a string to separate each element of the * array. The separator is converted to a string if necessary. If omitted, * the array elements are separated with a comma. * @return {string} * @this {Object} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/join */ Array.prototype.join = function(opt_separator) {}; /** * Removes the last element from an array and returns that element. * * @return {T} * @this {{length: number}|Array.} * @modifies {this} * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/pop */ Array.prototype.pop = function() {}; /** * Mutates an array by appending the given elements and returning the new * length of the array. * * @param {...T} var_args * @return {number} The new length of the array. * @this {{length: number}|Array.} * @template T * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/push */ Array.prototype.push = function(var_args) {}; /** * Transposes the elements of an array in place: the first array element becomes the * last and the last becomes the first. * * @this {Object} * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/reverse */ Array.prototype.reverse = function() {}; /** * Removes the first element from an array and returns that element. This * method changes the length of the array. * * @this {{length: number}|Array.} * @modifies {this} * @return {T} * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/shift */ Array.prototype.shift = function() {}; /** * Extracts a section of an array and returns a new array. * * @param {*=} opt_begin Zero-based index at which to begin extraction. A * non-number type will be auto-cast by the browser to a number. * @param {*=} opt_end Zero-based index at which to end extraction. slice * extracts up to but not including end. * @return {!Array.} * @this {{length: number}|Array.} * @template T * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/slice */ Array.prototype.slice = function(opt_begin, opt_end) {}; /** * Sorts the elements of an array in place. * * @param {function(T,T):number=} opt_compareFunction Specifies a function that * defines the sort order. * @this {{length: number}|Array.} * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/sort */ Array.prototype.sort = function(opt_compareFunction) {}; /** * Changes the content of an array, adding new elements while removing old * elements. * * @param {*=} opt_index Index at which to start changing the array. If negative, * will begin that many elements from the end. A non-number type will be * auto-cast by the browser to a number. * @param {*=} opt_howMany An integer indicating the number of old array elements * to remove. * @param {...T} var_args * @return {!Array.} * @this {{length: number}|Array.} * @modifies {this} * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/splice */ Array.prototype.splice = function(opt_index, opt_howMany, var_args) {}; /** * @return {string} * @this {Object} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/toSource */ Array.prototype.toSource; /** * @this {Array} * @return {string} * @nosideeffects * @override */ Array.prototype.toString = function() {}; /** * Adds one or more elements to the beginning of an array and returns the new * length of the array. * * @param {...*} var_args * @return {number} The new length of the array * @this {Object} * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/unshift */ Array.prototype.unshift = function(var_args) {}; /** * Apply a function simultaneously against two values of the array (from * left-to-right) as to reduce it to a single value. * * @param {?function(?, T, number, !Array.) : R} callback * @param {*=} opt_initialValue * @return {R} * @this {{length: number}|Array.} * @template T,R * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/reduce */ Array.prototype.reduce = function(callback, opt_initialValue) {}; /** * Apply a function simultaneously against two values of the array (from * right-to-left) as to reduce it to a single value. * * @param {?function(?, T, number, !Array.) : R} callback * @param {*=} opt_initialValue * @return {R} * @this {{length: number}|Array.} * @template T,R * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/reduceRight */ Array.prototype.reduceRight = function(callback, opt_initialValue) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {?function(this:S, T, number, !Array.): ?} callback * @param {S=} opt_thisobj * @return {boolean} * @this {{length: number}|Array.} * @template T,S * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/every */ Array.prototype.every = function(callback, opt_thisobj) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {?function(this:S, T, number, !Array.): ?} callback * @param {S=} opt_thisobj * @return {!Array.} * @this {{length: number}|Array.} * @template T,S * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter */ Array.prototype.filter = function(callback, opt_thisobj) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {?function(this:S, T, number, !Array.): ?} callback * @param {S=} opt_thisobj * @this {{length: number}|Array.} * @template T,S * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/forEach */ Array.prototype.forEach = function(callback, opt_thisobj) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {T} obj * @param {number=} opt_fromIndex * @return {number} * @this {{length: number}|Array.} * @nosideeffects * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf */ Array.prototype.indexOf = function(obj, opt_fromIndex) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {T} obj * @param {number=} opt_fromIndex * @return {number} * @this {{length: number}|Array.} * @nosideeffects * @template T * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/lastIndexOf */ Array.prototype.lastIndexOf = function(obj, opt_fromIndex) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {?function(this:S, T, number, !Array.): R} callback * @param {S=} opt_thisobj * @return {!Array.} * @this {{length: number}|Array.} * @template T,S,R * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/map */ Array.prototype.map = function(callback, opt_thisobj) {}; /** * Available in ECMAScript 5, Mozilla 1.6+. * @param {?function(this:S, T, number, !Array.): ?} callback * @param {S=} opt_thisobj * @return {boolean} * @this {{length: number}|Array.} * @template T,S * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/some */ Array.prototype.some = function(callback, opt_thisobj) {}; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/index */ Array.prototype.index; /** * @type {?string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/input */ Array.prototype.input; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/length */ Array.prototype.length; /** * @param {{length: number}|Array.} arr * @param {?function(this:S, T, number, ?) : ?} callback * @param {S=} opt_context * @return {boolean} * @template T,S */ Array.every = function(arr, callback, opt_context) {}; /** * @param {{length: number}|Array.} arr * @param {?function(this:S, T, number, ?) : ?} callback * @param {S=} opt_context * @return {!Array} * @template T,S */ Array.filter = function(arr, callback, opt_context) {}; /** * @param {{length: number}|Array.} arr * @param {?function(this:S, T, number, ?) : ?} callback * @param {S=} opt_context * @template T,S */ Array.forEach = function(arr, callback, opt_context) {}; /** * Mozilla 1.6+ only. * @param {{length: number}|Array.} arr * @param {T} obj * @param {number=} opt_fromIndex * @return {number} * @template T * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf */ Array.indexOf = function(arr, obj, opt_fromIndex) {}; /** * Mozilla 1.6+ only. * @param {{length: number}|Array.} arr * @param {T} obj * @param {number=} opt_fromIndex * @return {number} * @template T * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/lastIndexOf */ Array.lastIndexOf = function(arr, obj, opt_fromIndex) {}; /** * @param {{length: number}|Array.} arr * @param {?function(this:S, T, number, !Array.): R} callback * @param {S=} opt_context * @return {!Array.} * @template T,S,R */ Array.map = function(arr, callback, opt_context) {}; /** * @param {{length: number}|Array.} arr * @param {function(?, T, number, Array) : R} callback * @param {?=} opt_initialValue * @return {R} * @template T,R */ Array.reduce = function(arr, callback, opt_initialValue) {}; /** * @param {{length: number}|Array.} arr * @param {function(?, T, number, Array) : R} callback * @param {?=} opt_initialValue * @return {R} * @template T,R */ Array.reduceRight = function(arr, callback, opt_initialValue) {}; /** * @param {{length: number}|Array.} arr * @param {?function(this:S, T, number, ?) : ?} callback * @param {S=} opt_context * @return {boolean} * @template T,S */ Array.some = function(arr, callback, opt_context) {}; /** * Introduced in 1.8.5. * @param {*} arr * @return {boolean} * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray */ Array.isArray = function(arr) {}; /** * @constructor * @param {*=} opt_value * @return {boolean} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Boolean */ function Boolean(opt_value) {} /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Boolean/toSource * @override */ Boolean.prototype.toSource = function() {}; /** * @this {boolean|Boolean} * @return {string} * @nosideeffects * @override */ Boolean.prototype.toString = function() {}; /** * @constructor * @param {*=} opt_value * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number */ function Number(opt_value) {} /** * @param {number=} opt_fractionDigits * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toExponential */ Number.prototype.toExponential = function(opt_fractionDigits) {}; /** * @param {*=} opt_digits * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toFixed */ Number.prototype.toFixed = function(opt_digits) {}; /** * @param {number=} opt_precision * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toPrecision */ Number.prototype.toPrecision = function(opt_precision) {}; /** * Returns a string representing the number. * @param {(number|Number)=} opt_radix An optional radix. * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/toString * @override */ Number.prototype.toString = function(opt_radix) {}; // Properties. /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/MAX_VALUE */ Number.MAX_VALUE; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/MIN_VALUE */ Number.MIN_VALUE; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/NaN */ Number.NaN; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/NEGATIVE_INFINITY */ Number.NEGATIVE_INFINITY; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Number/POSITIVE_INFINITY */ Number.POSITIVE_INFINITY; /** * @const * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math */ var Math = {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/abs */ Math.abs = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/acos */ Math.acos = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/asin */ Math.asin = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/atan */ Math.atan = function(x) {}; /** * @param {*} y * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/atan2 */ Math.atan2 = function(y, x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/ceil */ Math.ceil = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/cos */ Math.cos = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/exp */ Math.exp = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/floor */ Math.floor = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/log */ Math.log = function(x) {}; /** * @param {...*} var_args * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/max */ Math.max = function(var_args) {}; /** * @param {...*} var_args * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/min */ Math.min = function(var_args) {}; /** * @param {*} x * @param {*} y * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/pow */ Math.pow = function(x, y) {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/random */ Math.random = function() {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/round */ Math.round = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/sin */ Math.sin = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/sqrt */ Math.sqrt = function(x) {}; /** * @param {*} x * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/tan */ Math.tan = function(x) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/toSource */ Math.toSource = function() {}; // Properties: /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/E */ Math.E; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/LN2 */ Math.LN2; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/LN10 */ Math.LN10; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/LOG2E */ Math.LOG2E; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/LOG10E */ Math.LOG10E; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/PI */ Math.PI; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/SQRT1_2 */ Math.SQRT1_2; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/SQRT2 */ Math.SQRT2; /** * @param {?=} opt_yr_num * @param {?=} opt_mo_num * @param {?=} opt_day_num * @param {?=} opt_hr_num * @param {?=} opt_min_num * @param {?=} opt_sec_num * @param {?=} opt_ms_num * @constructor * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date */ function Date(opt_yr_num, opt_mo_num, opt_day_num, opt_hr_num, opt_min_num, opt_sec_num, opt_ms_num) {} /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/now */ Date.now = function() {}; /** * Parses a string representation of a date, and returns the number * of milliseconds since January 1, 1970, 00:00:00, local time. * @param {*} date * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/parse */ Date.parse = function(date) {}; /** * @param {number} year * @param {number} month * @param {number=} opt_date * @param {number=} opt_hours * @param {number=} opt_minute * @param {number=} opt_second * @param {number=} opt_ms * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/UTC */ Date.UTC = function(year, month, opt_date, opt_hours, opt_minute, opt_second, opt_ms) {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getDate */ Date.prototype.getDate = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getDay */ Date.prototype.getDay = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getMonth */ Date.prototype.getMonth = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getFullYear */ Date.prototype.getFullYear = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getYear */ Date.prototype.getYear = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getHours */ Date.prototype.getHours = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getMinutes */ Date.prototype.getMinutes = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getSeconds */ Date.prototype.getSeconds = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getMilliseconds */ Date.prototype.getMilliseconds = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getTime */ Date.prototype.getTime = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getTimezoneOffset */ Date.prototype.getTimezoneOffset = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCDate */ Date.prototype.getUTCDate = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCDay */ Date.prototype.getUTCDay = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCMonth */ Date.prototype.getUTCMonth = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCFullYear */ Date.prototype.getUTCFullYear = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCHours */ Date.prototype.getUTCHours = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCMinutes */ Date.prototype.getUTCMinutes = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCSeconds */ Date.prototype.getUTCSeconds = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/getUTCMilliseconds */ Date.prototype.getUTCMilliseconds = function() {}; /** * Sets the day of the month for a specified date according to local time. * * @param {number} dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setDate */ Date.prototype.setDate = function(dayValue) {}; /** * Set the month for a specified date according to local time. * * @param {number} monthValue * @param {number=} opt_dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setMonth */ Date.prototype.setMonth = function(monthValue, opt_dayValue) {}; /** * Sets the full year for a specified date according to local time. * * @param {number} yearValue * @param {number=} opt_monthValue * @param {number=} opt_dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setFullYear */ Date.prototype.setFullYear = function(yearValue, opt_monthValue, opt_dayValue) {}; /** * Sets the year for a specified date according to local time. * * @param {number} yearValue * @deprecated * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setYear */ Date.prototype.setYear = function(yearValue) {}; /** * Sets the hours for a specified date according to local time. * * @param {number} hoursValue * @param {number=} opt_minutesValue * @param {number=} opt_secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setHours */ Date.prototype.setHours = function(hoursValue, opt_minutesValue, opt_secondsValue, opt_msValue) {}; /** * Sets the minutes for a specified date according to local time. * * @param {number} minutesValue * @param {number=} opt_secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setMinutes */ Date.prototype.setMinutes = function(minutesValue, opt_secondsValue, opt_msValue) {}; /** * Sets the seconds for a specified date according to local time. * * @param {number} secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setSeconds */ Date.prototype.setSeconds = function(secondsValue, opt_msValue) {}; /** * Sets the milliseconds for a specified date according to local time. * * @param {number} millisecondsValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setMilliseconds */ Date.prototype.setMilliseconds = function(millisecondsValue) {}; /** * Sets the Date object to the time represented by a number of milliseconds * since January 1, 1970, 00:00:00 UTC. * * @param {number} timeValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setTime */ Date.prototype.setTime = function(timeValue) {}; /** * Sets the day of the month for a specified date according to universal time. * * @param {number} dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCDate */ Date.prototype.setUTCDate = function(dayValue) {}; /** * Sets the month for a specified date according to universal time. * * @param {number} monthValue * @param {number=} opt_dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCMonth */ Date.prototype.setUTCMonth = function(monthValue, opt_dayValue) {}; /** * Sets the full year for a specified date according to universal time. * * @param {number} yearValue * @param {number=} opt_monthValue * @param {number=} opt_dayValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCFullYear */ Date.prototype.setUTCFullYear = function(yearValue, opt_monthValue, opt_dayValue) {}; /** * Sets the hour for a specified date according to universal time. * * @param {number} hoursValue * @param {number=} opt_minutesValue * @param {number=} opt_secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCHours */ Date.prototype.setUTCHours = function(hoursValue, opt_minutesValue, opt_secondsValue, opt_msValue) {}; /** * Sets the minutes for a specified date according to universal time. * * @param {number} minutesValue * @param {number=} opt_secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCMinutes */ Date.prototype.setUTCMinutes = function(minutesValue, opt_secondsValue, opt_msValue) {}; /** * Sets the seconds for a specified date according to universal time. * * @param {number} secondsValue * @param {number=} opt_msValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCSeconds */ Date.prototype.setUTCSeconds = function(secondsValue, opt_msValue) {}; /** * Sets the milliseconds for a specified date according to universal time. * * @param {number} millisecondsValue * @modifies {this} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/setUTCMilliseconds */ Date.prototype.setUTCMilliseconds = function(millisecondsValue) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toSource * @override */ Date.prototype.toSource = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toDateString */ Date.prototype.toDateString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toGMTString */ Date.prototype.toGMTString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toTimeString */ Date.prototype.toTimeString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toUTCString */ Date.prototype.toUTCString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleDateString */ Date.prototype.toLocaleDateString = function() {}; /** * @param {string} formatString * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleFormat */ Date.prototype.toLocaleFormat = function(formatString) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleString * @override */ Date.prototype.toLocaleString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleTimeString */ Date.prototype.toLocaleTimeString = function() {}; /** * @this {Date} * @return {string} * @nosideeffects * @override */ Date.prototype.toString = function() {}; /** * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/valueOf */ Date.prototype.valueOf; /** * @constructor * @param {*=} opt_str * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String */ function String(opt_str) {} // Functions: /** * @param {...number} var_args * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/fromCharCode */ String.fromCharCode = function(var_args) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/anchor */ String.prototype.anchor = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/big */ String.prototype.big = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/blink */ String.prototype.blink = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/bold */ String.prototype.bold = function() {}; /** * Returns the specified character from a string. * * @param {number} index * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charAt */ String.prototype.charAt = function(index) {}; /** * Returns a number indicating the Unicode value of the character at the given * index. * * @param {number=} opt_index * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charCodeAt */ String.prototype.charCodeAt = function(opt_index) {}; /** * Combines the text of two or more strings and returns a new string. * * @param {...*} var_args * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/concat */ String.prototype.concat = function(var_args) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/fixed */ String.prototype.fixed = function() {}; /** * @param {string} color * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/fontcolor */ String.prototype.fontcolor = function(color) {}; /** * @param {number} size * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/fontsize */ String.prototype.fontsize = function(size) {}; /** * Returns the index within the calling String object of the first occurrence * of the specified value, starting the search at fromIndex, returns -1 if the * value is not found. * * @param {string|null} searchValue * @param {(number|null)=} opt_fromIndex * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/indexOf */ String.prototype.indexOf = function(searchValue, opt_fromIndex) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/italics */ String.prototype.italics = function() {}; /** * Returns the index within the calling String object of the last occurrence of * the specified value, or -1 if not found. The calling string is searched * backward, starting at fromIndex. * * @param {string|null} searchValue * @param {(number|null)=} opt_fromIndex * @return {number} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/lastIndexOf */ String.prototype.lastIndexOf = function(searchValue, opt_fromIndex) {}; /** * @param {string} hrefAttribute * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/link */ String.prototype.link = function(hrefAttribute) {}; /** * Returns a number indicating whether a reference string comes before or after * or is the same as the given string in sort order. * * @this {*} * @param {*} other * @return {number} * @nosideeffects * @see http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/String/Prototype */ String.prototype.localeCompare = function(other) {}; /** * Used to retrieve the matches when matching a string against a regular * expression. * * @param {*} regexp * @return {Array.} This should really return an Array with a few * special properties, but we do not have a good way to model this in * our type system. Also see Regexp.prototype.exec. * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/match */ String.prototype.match = function(regexp) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/quote */ String.prototype.quote = function() {}; /** * Finds a match between a regular expression and a string, and replaces the * matched substring with a new substring. * * This may have side-effects if the replacement function has side-effects. * * @param {RegExp|string} regex * @param {string|Function} str * @param {string=} opt_flags * @return {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace */ String.prototype.replace = function(regex, str, opt_flags) {}; /** * Executes the search for a match between a regular expression and this String * object. * * @param {RegExp|string} regexp * @return {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/search */ String.prototype.search = function(regexp) {}; /** * @param {number} begin * @param {number=} opt_end * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/slice */ String.prototype.slice = function(begin, opt_end) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/small */ String.prototype.small = function() {}; /** * @param {*=} opt_separator * @param {number=} opt_limit * @return {!Array.} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/split */ String.prototype.split = function(opt_separator, opt_limit) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/strike */ String.prototype.strike = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/sub */ String.prototype.sub = function() {}; /** * @param {number} start * @param {number=} opt_length * @return {string} The specified substring. * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/substr */ String.prototype.substr = function(start, opt_length) {}; /** * @param {number} start * @param {number=} opt_end * @return {string} The specified substring. * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/substring */ String.prototype.substring = function(start, opt_end) {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/sup */ String.prototype.sup = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/toLocaleUpperCase */ String.prototype.toLocaleUpperCase = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/toLocaleLowerCase */ String.prototype.toLocaleLowerCase = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/toLowerCase */ String.prototype.toLowerCase = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/toUpperCase */ String.prototype.toUpperCase = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/toSource * @override */ String.prototype.toSource = function() {}; /** * @this {string|String} * @return {string} * @nosideeffects * @override */ String.prototype.toString = function() {}; /** * @return {string} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/valueOf */ String.prototype.valueOf; /** * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/length */ String.prototype.length; /** * @constructor * @param {*=} opt_pattern * @param {*=} opt_flags * @return {!RegExp} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ function RegExp(opt_pattern, opt_flags) {} /** * @param {*} pattern * @param {*=} opt_flags * @return {void} * @modifies {this} * @deprecated * @see http://msdn.microsoft.com/en-us/library/x9cswe0z(v=VS.85).aspx * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/compile */ RegExp.prototype.compile = function(pattern, opt_flags) {}; /** * @param {*} str The string to search. * @return {Array.} This should really return an Array with a few * special properties, but we do not have a good way to model this in * our type system. Also see String.prototype.match. * @see http://msdn.microsoft.com/en-us/library/z908hy33(VS.85).aspx * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/exec */ RegExp.prototype.exec = function(str) {}; /** * @param {*} str The string to search. * @return {boolean} Whether the string was matched. * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/test */ RegExp.prototype.test = function(str) {}; /** * @this {RegExp} * @return {string} * @nosideeffects * @override */ RegExp.prototype.toString = function() {}; // Constructor properties: /** * The string against which the last regexp was matched. * @type {string} * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_input.html */ RegExp.input; /** * The last matched characters. * @type {Array} * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_lastMatch.html */ RegExp.lastMatch; /** * The last matched parenthesized substring, if any. * @type {string} * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_lastParen.html */ RegExp.lastParen; /** * The substring of the input up to the characters most recently matched. * @type {string} * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_leftContext.html */ RegExp.leftContext; /** * The substring of the input after the characters most recently matched. * @type {string} * @see http://www.devguru.com/Technologies/Ecmascript/Quickref/regexp_rightContext.html */ RegExp.rightContext; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$1; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$2; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$3; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$4; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$5; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$6; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$7; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$8; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp */ RegExp.$9; // Prototype properties: /** * Whether to test the regular expression against all possible matches * in a string, or only against the first. * @type {boolean} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/global */ RegExp.prototype.global; /** * Whether to ignore case while attempting a match in a string. * @type {boolean} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/ignoreCase */ RegExp.prototype.ignoreCase; /** * The index at which to start the next match. * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex */ RegExp.prototype.lastIndex; /** * Whether or not to search in strings across multiple lines. * @type {boolean} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/multiline */ RegExp.prototype.multiline; /** * The text of the pattern. * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/source */ RegExp.prototype.source; /** * @constructor * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!Error} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error */ function Error(opt_message, opt_file, opt_line) {} /** * Chrome/v8 specific, altering the maximum depth of the stack trace * (10 by default). * @type {number} * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi */ Error.stackTraceLimit; /** * Chrome/v8 specific, adds a stack trace to the error object. The optional * constructorOpt parameter allows you to pass in a function value. When * collecting the stack trace all frames above the topmost call to this * function, including that call, will be left out of the stack trace. * @param {Object} error The object to add the stack trace to. * @param {Function=} opt_constructor A function in the stack trace * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi */ Error.captureStackTrace = function(error, opt_constructor){}; /** * IE-only. * @type {string} * @see http://msdn.microsoft.com/en-us/library/2w6a45b5.aspx */ Error.prototype.description; /** * Mozilla-only. * @type {number} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/lineNumber */ Error.prototype.lineNumber; /** * Mozilla-only * @type {string} * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/fileName */ Error.prototype.fileName; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/name */ Error.prototype.name; /** * @type {string} * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Error/message */ Error.prototype.message; /** * Doesn't seem to exist, but closure/debug.js references it. */ Error.prototype.sourceURL; /** @type {string} */ Error.prototype.stack; /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!EvalError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/EvalError */ function EvalError(opt_message, opt_file, opt_line) {} /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!RangeError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RangeError */ function RangeError(opt_message, opt_file, opt_line) {} /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!ReferenceError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/ReferenceError */ function ReferenceError(opt_message, opt_file, opt_line) {} /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!SyntaxError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/SyntaxError */ function SyntaxError(opt_message, opt_file, opt_line) {} /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!TypeError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/TypeError */ function TypeError(opt_message, opt_file, opt_line) {} /** * @constructor * @extends {Error} * @param {*=} opt_message * @param {*=} opt_file * @param {*=} opt_line * @return {!URIError} * @nosideeffects * @see http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/URIError */ function URIError(opt_message, opt_file, opt_line) {} // JScript extensions. // @see http://msdn.microsoft.com/en-us/library/894hfyb4(VS.80).aspx /** * @param {string} progId * @param {string=} opt_location * @constructor * @see http://msdn.microsoft.com/en-us/library/7sw4ddf8.aspx */ function ActiveXObject(progId, opt_location) {} /** * @return {string} * @nosideeffects * @see http://msdn.microsoft.com/en-us/library/9k34bww2(VS.80).aspx */ function ScriptEngine() {} /** * @return {number} * @nosideeffects * @see http://msdn.microsoft.com/en-us/library/yf25ky07(VS.80).aspx */ function ScriptEngineMajorVersion() {} /** * @return {number} * @nosideeffects * @see http://msdn.microsoft.com/en-us/library/wx3812cz(VS.80).aspx */ function ScriptEngineMinorVersion() {} /** * @return {number} * @nosideeffects * @see http://msdn.microsoft.com/en-us/library/e98hsk2f(VS.80).aspx */ function ScriptEngineBuildVersion() {} closure-compiler-20130227+dfsg1/externs/w3c_navigation_timing.js0000644000175000017500000001426012115204405022665 0ustar apoapo/* * Copyright 2011 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. */ /** * @fileoverview Definitions for W3C's Navigation Timing specification. * * Created from * @see http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html * @see http://w3c-test.org/webperf/specs/ResourceTiming * @see http://www.w3.org/TR/performance-timeline * * @externs */ /** @constructor */ function PerformanceTiming() {} /** @type {number} */ PerformanceTiming.prototype.navigationStart; /** @type {number} */ PerformanceTiming.prototype.unloadEventStart; /** @type {number} */ PerformanceTiming.prototype.unloadEventEnd; /** @type {number} */ PerformanceTiming.prototype.redirectStart; /** @type {number} */ PerformanceTiming.prototype.redirectEnd; /** @type {number} */ PerformanceTiming.prototype.fetchStart; /** @type {number} */ PerformanceTiming.prototype.domainLookupStart; /** @type {number} */ PerformanceTiming.prototype.domainLookupEnd; /** @type {number} */ PerformanceTiming.prototype.connectStart; /** @type {number} */ PerformanceTiming.prototype.connectEnd; /** @type {number} */ PerformanceTiming.prototype.secureConnectionStart; /** @type {number} */ PerformanceTiming.prototype.requestStart; /** @type {number} */ PerformanceTiming.prototype.responseStart; /** @type {number} */ PerformanceTiming.prototype.responseEnd; /** @type {number} */ PerformanceTiming.prototype.domLoading; /** @type {number} */ PerformanceTiming.prototype.domInteractive; /** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventStart; /** @type {number} */ PerformanceTiming.prototype.domContentLoadedEventEnd; /** @type {number} */ PerformanceTiming.prototype.domComplete; /** @type {number} */ PerformanceTiming.prototype.loadEventStart; /** @type {number} */ PerformanceTiming.prototype.loadEventEnd; /** @constructor */ function PerformanceEntry() {} /** @type {string} */ PerformanceEntry.prototype.name; /** @type {string} */ PerformanceEntry.prototype.entryType; /** @type {number} */ PerformanceEntry.prototype.startTime; /** @type {number} */ PerformanceEntry.prototype.duration; /** * @constructor * @extends {PerformanceEntry} */ function PerformanceResourceTiming() {} /** @type {number} */ PerformanceResourceTiming.prototype.redirectStart; /** @type {number} */ PerformanceResourceTiming.prototype.redirectEnd; /** @type {number} */ PerformanceResourceTiming.prototype.fetchStart; /** @type {number} */ PerformanceResourceTiming.prototype.domainLookupStart; /** @type {number} */ PerformanceResourceTiming.prototype.domainLookupEnd; /** @type {number} */ PerformanceResourceTiming.prototype.connectStart; /** @type {number} */ PerformanceResourceTiming.prototype.connectEnd; /** @type {number} */ PerformanceResourceTiming.prototype.secureConnectionStart; /** @type {number} */ PerformanceResourceTiming.prototype.requestStart; /** @type {number} */ PerformanceResourceTiming.prototype.responseStart; /** @type {number} */ PerformanceResourceTiming.prototype.responseEnd; /** @type {string} */ PerformanceResourceTiming.prototype.initiatorType; /** @constructor */ function PerformanceNavigation() {} /** @type {number} */ PerformanceNavigation.prototype.TYPE_NAVIGATE = 0; /** @type {number} */ PerformanceNavigation.prototype.TYPE_RELOAD = 1; /** @type {number} */ PerformanceNavigation.prototype.TYPE_BACK_FORWARD = 2; /** @type {number} */ PerformanceNavigation.prototype.TYPE_RESERVED = 255; /** @type {number} */ PerformanceNavigation.prototype.type; /** @type {number} */ PerformanceNavigation.prototype.redirectCount; // Only available in WebKit, and only with the --enable-memory-info flag. /** @constructor */ function PerformanceMemory() {} /** @type {number} */ PerformanceMemory.prototype.jsHeapSizeLimit; /** @type {number} */ PerformanceMemory.prototype.totalJSHeapSize; /** @type {number} */ PerformanceMemory.prototype.usedJSHeapSize; /** @constructor */ function Performance() {} /** @type {PerformanceTiming} */ Performance.prototype.timing; /** @type {PerformanceNavigation} */ Performance.prototype.navigation; /** * Clears the buffer used to store the current list of * PerformanceResourceTiming resources. * @return {undefined} */ Performance.prototype.clearResourceTimings = function() {}; /** * Set the maximum number of PerformanceResourceTiming resources that may be * stored in the buffer. * @param {number} maxSize */ Performance.prototype.setResourceTimingBufferSize = function(maxSize) {}; /** * @return {Array.} A copy of the PerformanceEntry list, * in chronological order with respect to startTime. * @nosideeffects */ Performance.prototype.getEntries = function() {}; /** * @param {string} entryType Only return {@code PerformanceEntry}s with this * entryType. * @return {Array.} A copy of the PerformanceEntry list, * in chronological order with respect to startTime. * @nosideeffects */ Performance.prototype.getEntriesByType = function(entryType) {}; /** * @param {string} name Only return {@code PerformanceEntry}s with this name. * @param {string=} opt_entryType Only return {@code PerformanceEntry}s with * this entryType. * @return {Array.} PerformanceEntry list in chronological * order with respect to startTime. * @nosideeffects */ Performance.prototype.getEntriesByName = function(name, opt_entryType) {}; // Only available in WebKit, and only with the --enable-memory-info flag. /** @type {PerformanceMemory} */ Performance.prototype.memory; /** * @return {number} * @nosideeffects */ Performance.prototype.now = function() {}; /** * @return {number} * @nosideeffects */ Performance.prototype.webkitNow = function() {}; /** @type {Performance} */ Window.prototype.performance; closure-compiler-20130227+dfsg1/externs/w3c_selectors.js0000644000175000017500000000404512115204405021162 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's Selectors API. * This file depends on w3c_dom1.js. * @see http://www.w3.org/TR/selectors-api2/ * * @externs */ /** * @param {string} selectors * @return {Element} * @override */ Document.prototype.querySelector = function(selectors) {}; /** * @param {string} selectors * @return {!NodeList} * @override */ Document.prototype.querySelectorAll = function(selectors) {}; /** * @param {string} selectors * @return {Element} * @override */ Element.prototype.querySelector = function(selectors) {}; /** * @param {string} selectors * @return {!NodeList} * @override */ Element.prototype.querySelectorAll = function(selectors) {}; /** * @param {string} selectors * @param {(Node|NodeList)=} refNodes * @return {boolean} */ Element.prototype.matchesSelector = function(selectors, refNodes) {}; /** * @see https://developer.mozilla.org/en/DOM/Node.mozMatchesSelector * @param {string} selectors * @return {boolean} */ Element.prototype.mozMatchesSelector = function(selectors) {}; /** * @see http://developer.apple.com/library/safari/documentation/WebKit/Reference/ElementClassRef/Element/Element.html * @param {string} selectors * @return {boolean} */ Element.prototype.webkitMatchesSelector = function(selectors) {}; /** * @see http://msdn.microsoft.com/en-us/library/ff975201.aspx * @param {string} selectors * @return {boolean} */ Element.prototype.msMatchesSelector = function(selectors) {};closure-compiler-20130227+dfsg1/externs/w3c_css.js0000644000175000017500000015365212115204405017760 0ustar apoapo/* * Copyright 2008 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. */ /** * @fileoverview Definitions for W3C's CSS specification * The whole file has been fully type annotated. * http://www.w3.org/TR/DOM-Level-2-Style/css.html * @externs * * TODO(nicksantos): When there are no more occurrences of w3c_range.js and * gecko_dom.js being included directly in BUILD files, bug dbeam to split the * bottom part of this file into a separate externs. */ /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet */ function StyleSheet() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-type */ StyleSheet.prototype.type; /** * @type {boolean} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-disabled */ StyleSheet.prototype.disabled; /** * @type {Node} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-owner */ StyleSheet.prototype.ownerNode; /** * @type {StyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-parentStyleSheet */ StyleSheet.prototype.parentStyleSheet; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-href */ StyleSheet.prototype.href; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-title */ StyleSheet.prototype.title; /** * @type {MediaList} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-media */ StyleSheet.prototype.media; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList */ function StyleSheetList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList-length */ StyleSheetList.prototype.length; /** * @param {number} index * @return {StyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheetList-item */ StyleSheetList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList */ function MediaList() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-mediaText */ MediaList.prototype.mediaText; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-length */ MediaList.prototype.length; /** * @param {number} index * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-MediaList-item */ MediaList.prototype.item = function(index) {}; /** * @interface * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-LinkStyle */ function LinkStyle() {} /** * @type {StyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-LinkStyle-sheet */ LinkStyle.prototype.sheet; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle */ function DocumentStyle() {} /** * @type {StyleSheetList} * @see http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle-styleSheets */ DocumentStyle.prototype.styleSheets; /** * @constructor * @extends {StyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet */ function CSSStyleSheet() {} /** * @type {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-ownerRule */ CSSStyleSheet.prototype.ownerRule; /** * @type {CSSRuleList} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-cssRules */ CSSStyleSheet.prototype.cssRules; /** * @param {string} rule * @param {number} index * @return {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule */ CSSStyleSheet.prototype.insertRule = function(rule, index) {}; /** * @param {number} index * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule */ CSSStyleSheet.prototype.deleteRule = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList */ function CSSRuleList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList-length */ CSSRuleList.prototype.length; /** * @param {number} index * @return {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRuleList-item */ CSSRuleList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule */ function CSSRule() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.prototype.type; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-cssText */ CSSRule.prototype.cssText; /** * @type {CSSStyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-sheet */ CSSRule.prototype.parentStyleSheet; /** * @type {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-parentRule */ CSSRule.prototype.parentRule; /** * Indicates that the rule is a {@see CSSUnknownRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.UNKNOWN_RULE = 0; /** * Indicates that the rule is a {@see CSSStyleRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.STYLE_RULE = 1; /** * Indicates that the rule is a {@see CSSCharsetRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.CHARSET_RULE = 2; /** * Indicates that the rule is a {@see CSSImportRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.IMPORT_RULE = 3; /** * Indicates that the rule is a {@see CSSMediaRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.MEDIA_RULE = 4; /** * Indicates that the rule is a {@see CSSFontFaceRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.FONT_FACE_RULE = 5; /** * Indicates that the rule is a {@see CSSPageRule}. * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule-ruleType */ CSSRule.PAGE_RULE = 6; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule */ function CSSStyleRule() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule-selectorText */ CSSStyleRule.prototype.selectorText; /** * @type {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule-style */ CSSStyleRule.prototype.style; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule */ function CSSMediaRule() {} /** * @type {MediaList} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-mediaTypes */ CSSMediaRule.prototype.media; /** * @type {CSSRuleList} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-cssRules */ CSSMediaRule.prototype.cssRules; /** * @param {string} rule * @param {number} index * @return {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-insertRule */ CSSMediaRule.prototype.insertRule = function(rule, index) {}; /** * @param {number} index * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule-deleteRule */ CSSMediaRule.prototype.deleteRule = function(index) {}; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSFontFaceRule */ function CSSFontFaceRule() {} /** * @type {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSFontFaceRule-style */ CSSFontFaceRule.prototype.style; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule */ function CSSPageRule() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule-name */ CSSPageRule.prototype.selectorText; /** * @type {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPageRule-style */ CSSPageRule.prototype.style; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule */ function CSSImportRule() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-href */ CSSImportRule.prototype.href; /** * @type {MediaList} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-media */ CSSImportRule.prototype.media; /** * @type {CSSStyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule-styleSheet */ CSSImportRule.prototype.styleSheet; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSCharsetRule */ function CSSCharsetRule() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSCharsetRule-encoding */ CSSCharsetRule.prototype.encoding; /** * @constructor * @extends {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSUnknownRule */ function CSSUnknownRule() {} /** * @constructor * @extends {CSSProperties} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration */ function CSSStyleDeclaration() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-cssText */ CSSStyleDeclaration.prototype.cssText; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-length */ CSSStyleDeclaration.prototype.length; /** * @type {CSSRule} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-parentRule */ CSSStyleDeclaration.prototype.parentRule; /** * @param {string} propertyName * @return {CSSValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyCSSValue */ CSSStyleDeclaration.prototype.getPropertyCSSValue = function(propertyName) {}; /** * @param {string} propertyName * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyPriority */ CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {}; /** * @param {string} propertyName * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue */ CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {}; /** * @param {number} index * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item */ CSSStyleDeclaration.prototype.item = function(index) {}; /** * @param {string} propertyName * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty */ CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {}; /** * @param {string} propertyName * @param {string} value * @param {string=} opt_priority * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty */ CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, opt_priority) {}; // IE-specific /** * @param {string} name * @param {number=} opt_flags * @return {string|number|boolean|null} * @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx */ CSSStyleDeclaration.prototype.getAttribute = function(name, opt_flags) {}; /** * @param {string} name * @return {string|number|boolean|null} * @see http://msdn.microsoft.com/en-us/library/aa358797(VS.85).aspx */ CSSStyleDeclaration.prototype.getExpression = function(name) {}; /** * @param {string} name * @param {number=} opt_flags * @return {boolean} * @see http://msdn.microsoft.com/en-us/library/ms536696(VS.85).aspx */ CSSStyleDeclaration.prototype.removeAttribute = function(name, opt_flags) {}; /** * @param {string} name * @return {boolean} * @see http://msdn.microsoft.com/en-us/library/aa358798(VS.85).aspx */ CSSStyleDeclaration.prototype.removeExpression = function(name) {}; /** * @param {string} name * @param {*} value * @param {number=} opt_flags * @see http://msdn.microsoft.com/en-us/library/ms536739(VS.85).aspx */ CSSStyleDeclaration.prototype.setAttribute = function(name, value, opt_flags) {}; /** * @param {string} name * @param {string} expr * @param {string=} opt_language * @return {undefined} * @see http://msdn.microsoft.com/en-us/library/ms531196(VS.85).aspx */ CSSStyleDeclaration.prototype.setExpression = function(name, expr, opt_language) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue */ function CSSValue() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-cssText */ CSSValue.prototype.cssText; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-cssValueType */ CSSValue.prototype.cssValueType; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types */ CSSValue.CSS_INHERIT = 0; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types */ CSSValue.CSS_PRIMITIVE_VALUE = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types */ CSSValue.CSS_VALUE_LIST = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue-types */ CSSValue.CSS_CUSTOM = 3; /** * @constructor * @extends {CSSValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ function CSSPrimitiveValue() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.prototype.primitiveType; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_UNKNOWN = 0; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_NUMBER = 1; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_PERCENTAGE = 2; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_EMS = 3; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_EXS = 4; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_PX = 5; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_CM = 6; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_MM = 7; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_IN = 8; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_PT = 9; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_PC = 10; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_DEG = 11; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_RAD = 12; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_GRAD = 13; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_MS = 14; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_S = 15; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_HZ = 16; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_KHZ = 17; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_DIMENSION = 18; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_STRING = 19; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_URI = 20; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_IDENT = 21; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_ATTR = 22; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_COUNTER = 23; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_RECT = 24; /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue */ CSSPrimitiveValue.CSS_RGBCOLOR = 25; /** * @return {Counter} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getCounterValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR} */ CSSPrimitiveValue.prototype.getCounterValue = function() {}; /** * @param {number} unitType * @return {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getFloatValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR} */ CSSPrimitiveValue.prototype.getFloatValue = function(unitType) {}; /** * @return {RGBColor} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getRGBColorValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR} */ CSSPrimitiveValue.prototype.getRGBColorValue = function() {}; /** * @return {Rect} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getRectValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR} */ CSSPrimitiveValue.prototype.getRectValue = function() {}; /** * @return {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-getStringValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR} */ CSSPrimitiveValue.prototype.getStringValue = function() {}; /** * @param {number} unitType * @param {number} floatValue * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-setFloatValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}, * {@see DomException.NO_MODIFICATION_ALLOWED_ERR} */ CSSPrimitiveValue.prototype.setFloatValue = function(unitType, floatValue) {}; /** * @param {number} stringType * @param {string} stringValue * @return {undefined} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-setStringValue * @throws DOMException {@see DomException.INVALID_ACCESS_ERR}, * {@see DomException.NO_MODIFICATION_ALLOWED_ERR} */ CSSPrimitiveValue.prototype.setStringValue = function(stringType, stringValue) {}; /** * @constructor * @extends {CSSValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList */ function CSSValueList() {} /** * @type {number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList-length */ CSSValueList.prototype.length; /** * @param {number} index * @return {CSSValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValueList-item */ CSSValueList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor */ function RGBColor() {} /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-red */ RGBColor.prototype.red; /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-green */ RGBColor.prototype.green; /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-RGBColor-blue */ RGBColor.prototype.blue; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect */ function Rect() {} /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-top */ Rect.prototype.top; /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-right */ Rect.prototype.right; /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-bottom */ Rect.prototype.bottom; /** * @type {CSSPrimitiveValue} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Rect-left */ Rect.prototype.left; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter */ function Counter() {} /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-identifier */ Counter.prototype.identifier; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-listStyle */ Counter.prototype.listStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-Counter-separator */ Counter.prototype.separator; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ViewCSS */ function ViewCSS() {} /** * @param {Element} elt * @param {?string} pseudoElt * @return {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSview-getComputedStyle */ ViewCSS.prototype.getComputedStyle = function(elt, pseudoElt) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DocumentCSS */ function DocumentCSS() {} /** * @param {Element} elt * @param {string} pseudoElt * @return {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DocumentCSS-getOverrideStyle */ DocumentCSS.prototype.getOverrideStyle = function(elt, pseudoElt) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DOMImplementationCSS */ function DOMImplementationCSS() {} /** * @param {string} title * @param {string} media * @return {CSSStyleSheet} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-DOMImplementationCSS-createCSSStyleSheet * @throws DOMException {@see DomException.SYNTAX_ERR} */ DOMImplementationCSS.prototype.createCSSStyleSheet = function(title, media) {}; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ElementCSSInlineStyle */ function ElementCSSInlineStyle() {} /** * @type {CSSStyleDeclaration} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-ElementCSSInlineStyle-style */ ElementCSSInlineStyle.prototype.style; /** * @constructor * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties */ function CSSProperties() {} // CSS 2 properties /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-azimuth */ CSSProperties.prototype.azimuth; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-background */ CSSProperties.prototype.background; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundAttachment */ CSSProperties.prototype.backgroundAttachment; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundColor */ CSSProperties.prototype.backgroundColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundImage */ CSSProperties.prototype.backgroundImage; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundPosition */ CSSProperties.prototype.backgroundPosition; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-backgroundRepeat */ CSSProperties.prototype.backgroundRepeat; /** * @implicitCast * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-border */ CSSProperties.prototype.border; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderCollapse */ CSSProperties.prototype.borderCollapse; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderColor */ CSSProperties.prototype.borderColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderSpacing */ CSSProperties.prototype.borderSpacing; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue-borderStyle */ CSSProperties.prototype.borderStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTop */ CSSProperties.prototype.borderTop; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRight */ CSSProperties.prototype.borderRight; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottom */ CSSProperties.prototype.borderBottom; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeft */ CSSProperties.prototype.borderLeft; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopColor */ CSSProperties.prototype.borderTopColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightColor */ CSSProperties.prototype.borderRightColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomColor */ CSSProperties.prototype.borderBottomColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftColor */ CSSProperties.prototype.borderLeftColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopStyle */ CSSProperties.prototype.borderTopStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightStyle */ CSSProperties.prototype.borderRightStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomStyle */ CSSProperties.prototype.borderBottomStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftStyle */ CSSProperties.prototype.borderLeftStyle; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderTopWidth */ CSSProperties.prototype.borderTopWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderRightWidth */ CSSProperties.prototype.borderRightWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderBottomWidth */ CSSProperties.prototype.borderBottomWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderLeftWidth */ CSSProperties.prototype.borderLeftWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-borderWidth */ CSSProperties.prototype.borderWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-bottom */ CSSProperties.prototype.bottom; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-captionSide */ CSSProperties.prototype.captionSide; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-clear */ CSSProperties.prototype.clear; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-clip */ CSSProperties.prototype.clip; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-color */ CSSProperties.prototype.color; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-content */ CSSProperties.prototype.content; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-counterIncrement */ CSSProperties.prototype.counterIncrement; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-counterReset */ CSSProperties.prototype.counterReset; /** * This is not an official part of the W3C spec. In practice, this is a settable * property that works cross-browser. It is used in goog.dom.setProperties() and * needs to be extern'd so the --disambiguate_properties JS compiler pass works. * @type {string} */ CSSProperties.prototype.cssText; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cue */ CSSProperties.prototype.cue; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cueAfter */ CSSProperties.prototype.cueAfter; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cueBefore */ CSSProperties.prototype.cueBefore; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cursor */ CSSProperties.prototype.cursor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-direction */ CSSProperties.prototype.direction; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-display */ CSSProperties.prototype.display; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-elevation */ CSSProperties.prototype.elevation; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-emptyCells */ CSSProperties.prototype.emptyCells; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-cssFloat */ CSSProperties.prototype.cssFloat; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-font */ CSSProperties.prototype.font; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontFamily */ CSSProperties.prototype.fontFamily; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontSize */ CSSProperties.prototype.fontSize; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontSizeAdjust */ CSSProperties.prototype.fontSizeAdjust; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontStretch */ CSSProperties.prototype.fontStretch; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontStyle */ CSSProperties.prototype.fontStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontVariant */ CSSProperties.prototype.fontVariant; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-fontWeight */ CSSProperties.prototype.fontWeight; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-height */ CSSProperties.prototype.height; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-left */ CSSProperties.prototype.left; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-letterSpacing */ CSSProperties.prototype.letterSpacing; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-lineHeight */ CSSProperties.prototype.lineHeight; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyle */ CSSProperties.prototype.listStyle; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyleImage */ CSSProperties.prototype.listStyleImage; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStylePosition */ CSSProperties.prototype.listStylePosition; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-listStyleType */ CSSProperties.prototype.listStyleType; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-margin */ CSSProperties.prototype.margin; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginTop */ CSSProperties.prototype.marginTop; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginRight */ CSSProperties.prototype.marginRight; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginBottom */ CSSProperties.prototype.marginBottom; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marginLeft */ CSSProperties.prototype.marginLeft; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-markerOffset */ CSSProperties.prototype.markerOffset; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-marks */ CSSProperties.prototype.marks; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-maxHeight */ CSSProperties.prototype.maxHeight; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-maxWidth */ CSSProperties.prototype.maxWidth; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-minHeight */ CSSProperties.prototype.minHeight; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-minWidth */ CSSProperties.prototype.minWidth; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-orphans */ CSSProperties.prototype.orphans; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outline */ CSSProperties.prototype.outline; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineColor */ CSSProperties.prototype.outlineColor; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineStyle */ CSSProperties.prototype.outlineStyle; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-outlineWidth */ CSSProperties.prototype.outlineWidth; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-overflow */ CSSProperties.prototype.overflow; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-padding */ CSSProperties.prototype.padding; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingTop */ CSSProperties.prototype.paddingTop; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingRight */ CSSProperties.prototype.paddingRight; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingBottom */ CSSProperties.prototype.paddingBottom; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-paddingLeft */ CSSProperties.prototype.paddingLeft; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-page */ CSSProperties.prototype.page; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakAfter */ CSSProperties.prototype.pageBreakAfter; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakBefore */ CSSProperties.prototype.pageBreakBefore; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pageBreakInside */ CSSProperties.prototype.pageBreakInside; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pause */ CSSProperties.prototype.pause; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pauseAfter */ CSSProperties.prototype.pauseAfter; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pauseBefore */ CSSProperties.prototype.pauseBefore; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pitch */ CSSProperties.prototype.pitch; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-pitchRange */ CSSProperties.prototype.pitchRange; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-playDuring */ CSSProperties.prototype.playDuring; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-position */ CSSProperties.prototype.position; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-quotes */ CSSProperties.prototype.quotes; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-richness */ CSSProperties.prototype.richness; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-right */ CSSProperties.prototype.right; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-size */ CSSProperties.prototype.size; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speak */ CSSProperties.prototype.speak; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakHeader */ CSSProperties.prototype.speakHeader; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakNumeral */ CSSProperties.prototype.speakNumeral; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speakPunctuation */ CSSProperties.prototype.speakPunctuation; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-speechRate */ CSSProperties.prototype.speechRate; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-stress */ CSSProperties.prototype.stress; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-tableLayout */ CSSProperties.prototype.tableLayout; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textAlign */ CSSProperties.prototype.textAlign; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textDecoration */ CSSProperties.prototype.textDecoration; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textIndent */ CSSProperties.prototype.textIndent; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textShadow */ CSSProperties.prototype.textShadow; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-textTransform */ CSSProperties.prototype.textTransform; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-top */ CSSProperties.prototype.top; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-unicodeBidi */ CSSProperties.prototype.unicodeBidi; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-verticalAlign */ CSSProperties.prototype.verticalAlign; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-visibility */ CSSProperties.prototype.visibility; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-voiceFamily */ CSSProperties.prototype.voiceFamily; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-volume */ CSSProperties.prototype.volume; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-whiteSpace */ CSSProperties.prototype.whiteSpace; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-widows */ CSSProperties.prototype.widows; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-width */ CSSProperties.prototype.width; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-wordSpacing */ CSSProperties.prototype.wordSpacing; /** * @type {string} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-wordWrap */ CSSProperties.prototype.wordWrap; /** * @type {string|number} * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSProperties-zIndex */ CSSProperties.prototype.zIndex; // CSS 3 properties /** * @type {string} * @see http://www.w3.org/TR/css3-ui/#box-sizing */ CSSProperties.prototype.boxSizing; /** * @type {string|number} * @see http://www.w3.org/TR/css3-color/#transparency */ CSSProperties.prototype.opacity; // CSS 3 transforms /** * @type {string} * @see http://www.w3.org/TR/css3-2d-transforms/#backface-visibility-property */ CSSProperties.prototype.backfaceVisibility; /** * @type {string} * @see http://www.w3.org/TR/css3-2d-transforms/#perspective */ CSSProperties.prototype.perspective; /** * @type {string|number} * @see http://www.w3.org/TR/css3-2d-transforms/#perspective-origin */ CSSProperties.prototype.perspectiveOrigin; /** * @type {string} * @see http://www.w3.org/TR/css3-2d-transforms/#effects */ CSSProperties.prototype.transform; /** * @type {string|number} * @see http://www.w3.org/TR/css3-2d-transforms/#transform-origin */ CSSProperties.prototype.transformOrigin; /** * @type {string} * @see http://www.w3.org/TR/css3-2d-transforms/#transform-style */ CSSProperties.prototype.transformStyle; // CSS 3 transitions /** * @type {string} * @see http://www.w3.org/TR/css3-transitions/#transition */ CSSProperties.prototype.transition; /** * @type {string} * @see http://www.w3.org/TR/css3-transitions/#transition-delay */ CSSProperties.prototype.transitionDelay; /** * @type {string} * @see http://www.w3.org/TR/css3-transitions/#transition-duration */ CSSProperties.prototype.transitionDuration; /** * @type {string} * @see http://www.w3.org/TR/css3-transitions/#transition-property-property */ CSSProperties.prototype.transitionProperty; /** * @type {string} * @see http://www.w3.org/TR/css3-transitions/#transition-timing-function */ CSSProperties.prototype.transitionTimingFunction; /** * TODO(dbeam): Put this in separate file named w3c_cssom.js. * Externs for the CSSOM View Module. * @see http://www.w3.org/TR/cssom-view/ */ // http://www.w3.org/TR/cssom-view/#extensions-to-the-window-interface /** * @param {string} media_query_list * @return {MediaQueryList} * @see http://www.w3.org/TR/cssom-view/#dom-window-matchmedia */ Window.prototype.matchMedia = function(media_query_list) {}; /** * @type {Screen} * @see http://www.w3.org/TR/cssom-view/#dom-window-screen */ Window.prototype.screen; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-innerwidth */ Window.prototype.innerWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-innerheight */ Window.prototype.innerHeight; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollx */ Window.prototype.scrollX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-pagexoffset */ Window.prototype.pageXOffset; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-scrolly */ Window.prototype.scrollY; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-pageyoffset */ Window.prototype.pageYOffset; /** * @param {number} x * @param {number} y * @see http://www.w3.org/TR/cssom-view/#dom-window-scroll */ Window.prototype.scroll = function(x, y) {}; /** * @param {number} x * @param {number} y * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollto */ Window.prototype.scrollTo = function(x, y) {}; /** * @param {number} x * @param {number} y * @see http://www.w3.org/TR/cssom-view/#dom-window-scrollby */ Window.prototype.scrollBy = function(x, y) {}; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-screenx */ Window.prototype.screenX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-screeny */ Window.prototype.screenY; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-outerwidth */ Window.prototype.outerWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-window-outerheight */ Window.prototype.outerHeight; /** * @constructor * @see http://www.w3.org/TR/cssom-view/#mediaquerylist */ function MediaQueryList() {} /** * @type {string} * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-media */ MediaQueryList.prototype.media; /** * @type {boolean} * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-matches */ MediaQueryList.prototype.matches; /** * @param {MediaQueryListListener} listener * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener */ MediaQueryList.prototype.addListener = function(listener) {}; /** * @param {MediaQueryListListener} listener * @see http://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener */ MediaQueryList.prototype.removeListener = function(listener) {}; /** * @typedef {(function(!MediaQueryList) : void)} * @see http://www.w3.org/TR/cssom-view/#mediaquerylistlistener */ var MediaQueryListListener; /** * @constructor * @see http://www.w3.org/TR/cssom-view/#screen */ function Screen() {} /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-availwidth */ Screen.prototype.availWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-availheight */ Screen.prototype.availHeight; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-width */ Screen.prototype.width; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-height */ Screen.prototype.height; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-colordepth */ Screen.prototype.colorDepth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-screen-pixeldepth */ Screen.prototype.pixelDepth; // http://www.w3.org/TR/cssom-view/#extensions-to-the-document-interface /** * @param {number} x * @param {number} y * @return {?Element} * @see http://www.w3.org/TR/cssom-view/#dom-document-elementfrompoint */ Document.prototype.elementFromPoint = function(x, y) {}; /** * @param {number} x * @param {number} y * @return {CaretPosition} * @see http://www.w3.org/TR/cssom-view/#dom-document-caretpositionfrompoint */ Document.prototype.caretPositionFromPoint = function(x, y) {}; /** * @constructor * @see http://www.w3.org/TR/cssom-view/#caretposition */ function CaretPosition() {} /** * @type {Node} * @see http://www.w3.org/TR/cssom-view/#dom-caretposition-offsetnode */ CaretPosition.prototype.offsetNode; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-caretposition-offset */ CaretPosition.prototype.offset; // http://www.w3.org/TR/cssom-view/#extensions-to-the-element-interface /** * @return {ClientRectList} * @see http://www.w3.org/TR/cssom-view/#dom-element-getclientrects */ Element.prototype.getClientRects = function() {}; /** * @return {ClientRect} * @see http://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect */ Element.prototype.getBoundingClientRect = function() {}; /** * @param {boolean=} opt_top * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollintoview */ Element.prototype.scrollIntoView = function(opt_top) {}; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-scrolltop */ Element.prototype.scrollTop; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollleft */ Element.prototype.scrollLeft; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollwidth */ Element.prototype.scrollWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-scrollheight */ Element.prototype.scrollHeight; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-clienttop */ Element.prototype.clientTop; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-clientleft */ Element.prototype.clientLeft; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-clientwidth */ Element.prototype.clientWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-element-clientheight */ Element.prototype.clientHeight; // http://www.w3.org/TR/cssom-view/#extensions-to-the-htmlelement-interface /** * @type {Element} * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetparent */ HTMLElement.prototype.offsetParent; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsettop */ HTMLElement.prototype.offsetTop; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetleft */ HTMLElement.prototype.offsetLeft; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetwidth */ HTMLElement.prototype.offsetWidth; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetheight */ HTMLElement.prototype.offsetHeight; // http://www.w3.org/TR/cssom-view/#extensions-to-the-range-interface /** * @return {ClientRectList} * @see http://www.w3.org/TR/cssom-view/#dom-range-getclientrects */ Range.prototype.getClientRects = function() {}; /** * @return {ClientRect} * @see http://www.w3.org/TR/cssom-view/#dom-range-getboundingclientrect */ Range.prototype.getBoundingClientRect = function() {}; // http://www.w3.org/TR/cssom-view/#extensions-to-the-mouseevent-interface // MouseEvent: screen{X,Y} and client{X,Y} are in DOM Level 2/3 Event as well, // so it seems like a specification issue. I've emailed www-style@w3.org in // hopes of resolving the conflict, but in the mean time they can live here // (http://lists.w3.org/Archives/Public/www-style/2012May/0039.html). /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-screenx */ //MouseEvent.prototype.screenX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-screeny */ //MouseEvent.prototype.screenY; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-pagex */ MouseEvent.prototype.pageX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-pagey */ MouseEvent.prototype.pageY; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-clientx */ //MouseEvent.prototype.clientX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-clienty */ //MouseEvent.prototype.clientY; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-x */ MouseEvent.prototype.x; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-y */ MouseEvent.prototype.y; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-offsetx */ MouseEvent.prototype.offsetX; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-mouseevent-offsety */ MouseEvent.prototype.offsetY; // http://www.w3.org/TR/cssom-view/#rectangles /** * @constructor * @see http://www.w3.org/TR/cssom-view/#the-clientrectlist-interface */ function ClientRectList() {} /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrectlist-length */ ClientRectList.prototype.length; /** * @param {number} index * @return {ClientRect} * @see http://www.w3.org/TR/cssom-view/#dom-clientrectlist-item */ ClientRectList.prototype.item = function(index) {}; /** * @constructor * @see http://www.w3.org/TR/cssom-view/#the-clientrect-interface */ function ClientRect() {} /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-top */ ClientRect.prototype.top; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-right */ ClientRect.prototype.right; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-bottom */ ClientRect.prototype.bottom; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-left */ ClientRect.prototype.left; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-width */ ClientRect.prototype.width; /** * @type {number} * @see http://www.w3.org/TR/cssom-view/#dom-clientrect-height */ ClientRect.prototype.height; closure-compiler-20130227+dfsg1/rhino/0000755000175000017500000000000014433670117015505 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/build-date0000644000175000017500000000004714433667662017456 0ustar apoapoThis version was built on @datestamp@. closure-compiler-20130227+dfsg1/rhino/README.md0000644000175000017500000000606514433667662017006 0ustar apoapo# Rhino: JavaScript in Java ![Rhino](https://developer.mozilla.org/@api/deki/files/832/=Rhino.jpg) Rhino is an implementation of JavaScript in Java. ## License Rhino is licensed under the [MPL 2.0](./LICENSE.txt). ## Releases
      Rhino 1.7R5January 29, 2015
      Rhino 1.7.6April 15, 2015
      Rhino 1.7.7June 17, 2015
      Rhino 1.7.7.1February 2, 2016
      Rhino 1.7.7.2August 24, 2017
      [Release Notes](./RELEASE-NOTES.md) for recent releases. [Compatability table](http://mozilla.github.io/rhino/compat/engines.html) which shows which advanced JavaScript features from ES5, 6, and 7 are implemented in Rhino. ## Documentation Information for script builders and embedders: [https://developer.mozilla.org/en-US/docs/Rhino_documentation](https://developer.mozilla.org/en-US/docs/Rhino_documentation) JavaDoc for all the APIs: [http://mozilla.github.io/rhino/javadoc/index.html](http://mozilla.github.io/rhino/javadoc/index.html) More resources if you get stuck: [https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Community](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino/Community) ## Building ### How to Build Rhino builds with `Gradle`. Here are some useful tasks: ``` ./gradlew jar ``` Build and create `Rhino` jar in the `build/libs` directory. ``` ./gradlew test ``` Build and run all the tests. ``` ./gradlew testBenchmark ``` Build and run benchmark tests. ## Releasing and publishing new version 1. Ensure all tests are passing 2. Remove `-SNAPSHOT` from version in `gradle.properties` in project root folder 3. Create file `gradle.properties` in `$HOME/.gradle` folder with following properties. Populate them with maven repo credentials and repo location. ``` mavenUser= mavenPassword= mavenSnapshotRepo= mavenReleaseRepo= ``` 4. Run `Gradle` task to publish artifacts to Maven Central. ``` ./gradlew publish ``` 5. Increase version and add `-SNAPSHOT` to it in `gradle.properties` in project root folder. 6. Push `gradle.properties` to `GitHub` ## Running Rhino can run as a stand-alone interpreter from the command line: ``` java -jar buildGradle/libs/rhino-1.7.7.2.jar Rhino 1.7.7.2 2017 08 24 js> print('Hello, World!'); Hello, World! js> ``` You can also embed it, as most people do. See below for more docs. ## Issues Most issues are managed on GitHub: [https://github.com/mozilla/rhino/issues](https://github.com/mozilla/rhino/issues) ## More Help The Google group is the best place to go with questions: [https://groups.google.com/forum/#!forum/mozilla-rhino](https://groups.google.com/forum/#!forum/mozilla-rhino) closure-compiler-20130227+dfsg1/rhino/testsrc/0000755000175000017500000000000014433667662017207 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/build.xml0000644000175000017500000002546714433667662021046 0ustar apoapo closure-compiler-20130227+dfsg1/rhino/testsrc/opt1.skip0000644000175000017500000000156714433667662020773 0ustar apoapo# Rhino skip list: opt1.skip. Tests to be run at -opt 1 # These tests are skipped by Rhino either because a bug with a regression # test has yet to be fixed, or because the test is not applicable to Rhino. # program too large/complex ecma_3/Statements/regress-302439.js ecma_3/Statements/regress-324650.js ecma_3/Statements/regress-74474-002.js ecma_3/Statements/regress-74474-003.js js1_5/Regress/regress-244470.js js1_5/Regress/regress-308085.js js1_5/Regress/regress-80981.js js1_5/Regress/regress-89443.js js1_5/extensions/regress-226507.js # program too large/complex; could have better error message js1_5/Regress/regress-111557.js js1_5/Regress/regress-155081-2.js js1_5/Regress/regress-155081.js js1_5/extensions/regress-311161.js # Missing line number information on error js1_5/Regress/regress-167328.js js1_5/extensions/regress-50447.js js1_5/Exceptions/regress-257751.js closure-compiler-20130227+dfsg1/rhino/testsrc/base.skip0000644000175000017500000005570614433667662021026 0ustar apoapo# Rhino base skip list: these are tests that are always skipped for Rhino # These tests are skipped by Rhino either because a bug with a regression # test has yet to be fixed, or because the test is not applicable to Rhino. # # bad test: cannot write a property that has only a getter defined js1_5/extensions/regress-341956-01.js # timer resolution test js1_5/extensions/regress-363258.js # Needs investigation js1_7/regress/regress-406477.js e4x/extensions/regress-410192.js # should be investigated: OutOfMemoryError js1_5/Regress/regress-330352.js # test consumes much memory, will get OutOfMemoryError depending on JVM # settings js1_5/extensions/regress-367501-04.js # stress test that can potentially time out js1_5/Regress/regress-404755.js # uses deprecated way of doing dynamic scope tests/js1_5/Scope/regress-181834.js # should be investigated: semicolon insertion ecma_3/LexicalConventions/7.9.1.js ecma_3/extensions/7.9.1.js # tests that do not work on non-English locales or for timezones outside the US ecma_3/Date/15.9.5.5.js js1_5/Regress/regress-58116.js # Fails on Macs? lc2/JavaToJS/char-002.js # Stress test; Reports of StackOverflowError (Mac only?) js1_5/Regress/regress-98901.js # performance regression test js1_5/Array/regress-99120-01.js js1_5/extensions/regress-371636.js # ES5 requires decoder to throw URIError on overlong UTF-8 sequences js1_5/Regress/regress-172699.js # large concatenation of strings js1_5/Function/regress-338121-01.js js1_5/Function/regress-338121-02.js js1_5/Function/regress-338121-03.js # needs investigation: Date corner case ecma_3/Date/15.9.5.5-02.js # Infinite recursion e4x/Regress/regress-394941.js # GC tests js1_5/GC # bad test: random string generated will be too long about 1% of the time lc3/JavaArray/ToArray-001.js # would imply we would always need an activation for every non-leaf func js1_5/Regress/regress-383682.js # test depends on E4X, but test framework calls version(150) js1_5/Regress/regress-309242.js # performance test: Global vars should not be more than 2.5 times slower than local js1_5/Regress/regress-169559.js # infinite recursion js1_5/Regress/regress-234389.js # object watch not implemented js1_5/Regress/regress-240577.js js1_5/Function/regress-364023.js js1_5/Object/regress-362872-01.js js1_5/Object/regress-362872-02.js js1_5/Regress/regress-385393-06.js js1_5/extensions/regress-385393-09.js js1_7/GC/regress-381374.js # toLocaleFormat not implemented js1_5/extensions/toLocaleFormat-01.js js1_5/extensions/toLocaleFormat-02.js # source too complex js1_5/Expressions/regress-394673.js # import, export not implemented js1_5/Regress/regress-249211.js js1_7/lexical/regress-346642-03.js js1_5/Regress/regress-362583.js js1_5/extensions/regress-355622.js # sharp-variables not implemented js1_5/extensions/regress-379523.js # concatentated string too large js1_5/Regress/regress-280769-1.js # stack overflow js1_5/Regress/regress-280769-2.js # performance tests js1_5/Regress/regress-313967-01.js js1_5/Regress/regress-313967-02.js js1_5/Array/regress-99120-02.js # concatentated string too large js1_5/Regress/regress-347306-01.js # import, export not implemented js1_5/decompilation/regress-349484.js # concatenated string too large js1_5/String/regress-157334-01.js js1_5/String/regress-56940-01.js js1_5/String/regress-56940-02.js # runs out of heap js1_5/Regress/regress-271716-n.js js1_5/Regress/regress-303213.js js1_5/Regress/regress-312588.js js1_5/Array/regress-157652.js # Tests that create very large strings by concatentation js1_5/extensions/regress-336409-1.js js1_5/extensions/regress-336409-2.js js1_5/extensions/regress-336410-1.js js1_5/extensions/regress-336410-2.js e4x/XML/regress-324422-1.js js1_5/Function/regress-338001.js js1_5/Regress/regress-159334.js js1_5/Regress/regress-191633.js js1_5/Regress/regress-280769-3.js js1_5/Regress/regress-280769-4.js js1_5/String/regress-314890.js js1_5/String/regress-322772.js js1_5/extensions/regress-363988.js # bug 152646 Will not fix this in Rhino; too much of a corner case js1_5/Regress/regress-152646.js #Verify error for bad branch js1_5/Regress/regress-96526-001.js js1_5/Regress/regress-96526-002.js js1_5/Regress/regress-96526-003.js # JSObject not used for Rhino LiveConnect lc3/ConvertJSObject lc3/Exceptions/throw_js_types.js lc3/JavaClass/ToJSObject-001.js lc3/JSObject/ToJSObject-001.js #Rhino implements Date.toLocaleTimeString() differently than SpiderMonkey ecma_3/Date/15.9.5.7.js #Rhino - as permitted by ECMA - does not allow indirect calls to eval js1_4/Eval/eval-001.js js1_4/Eval/eval-002.js js1_4/Eval/eval-003.js js1_5/Regress/regress-68498-003.js #Rhino compiled mode is limited by Java classfile size limitations #js1_5/Regress/regress-80981.js #js1_5/Regress/regress-90445.js #js1_5/Regress/regress-111557.js #Rhino relies on JVM to throw StackOverflow exception and does not #detect too deep recursion explicitly. js1_5/extensions/regress-192465.js js1_5/extensions/regress-226507.js js1_5/Regress/regress-89443.js # Skip these two; see http://bugzilla.mozilla.org/show_bug.cgi?id=81086, large # switch statement #ecma_3/Statements/regress-74474-002.js #ecma_3/Statements/regress-74474-003.js #Compiled mode cannot catch infinite recursion errors js1_5/Regress/regress-96128-n.js js1_5/Exceptions/regress-121658.js # This test uses the (non-ECMA) 'it' object of SpiderMonkey js1_2/version120/regress-99663.js # This test uses the gc() function of SpiderMonkey ecma_3/Function/regress-104584.js #This test uses the Error.stack property of SpiderMonkey (non-ECMA) js1_5/Exceptions/errstack-001.js #This test uses the clone() function in SpiderMonkey's js.c file #js1_5/Regress/regress-127557.js # apparently works, which doesn't make sense #These tests break with new Unicode in JDK 1.4 ecma/String/15.5.4.11-2.js ecma/String/15.5.4.11-5.js ecma/String/15.5.4.12-1.js ecma/String/15.5.4.12-4.js ecma/String/15.5.4.12-5.js # Rhino doesn't implement the (non-ECMA) f.caller property js1_5/Function/regress-222029-001.js js1_5/Function/regress-222029-002.js # WONTFIX bug 119719 js1_5/Regress/regress-119719.js # Bug Number 240317 relaxed errors for reserved identifiers but Rhino did not js1_5/Regress/regress-240317.js # Spidermonkey now defaults lineNumber and fileName # to the location and file where the exception occured. # exclude new test which assumes the defaults are # set according to Spidermonkey. js1_5/extensions/regress-50447-1.js # Invalid bug e4x/Regress/regress-278112.js # Invalid test? Cannot convert NaN to java.lang.Long lc3/template.js # Depends on generators being closed due to a call to gc(). Java finalizers # are not run immediately in a call to gc(), but instead usuallly are run # later on a different thread. js1_7/geniter/regress-347739.js js1_7/geniter/regress-349012-01.js js1_7/geniter/regress-349331.js # function named "yield" js1_7/lexical/regress-351515.js # keywords used as identifiers js1_5/extensions/regress-381304.js # Needs investigation: syntax errors in setters js1_5/Regress/regress-361617.js js1_5/extensions/regress-356106.js # decompilation js1_7/block/regress-344601.js js1_7/block/regress-351794.js js1_5/Regress/regress-10278.js # bad test: will fail if time of day increments a second between calls to Date() ecma/Date/15.9.2.1.js ecma/Date/15.9.2.2-2.js # JS 1.7 not yet implemented js1_7/decompilation # "y" flag for regexps js1_7/regexp/yflag.js # yield and xml-filtering predicate js1_7/geniter/regress-352605.js # js1_7 needs investigation js1_7/extensions/basic-for-each.js js1_7/extensions/regress-346642-06.js js1_7/extensions/regress-351102-03.js js1_7/extensions/regress-351102-04.js js1_7/extensions/regress-351102-05.js js1_7/extensions/regress-351102-07.js js1_7/extensions/regress-353214-01.js js1_7/extensions/regress-353214-02.js js1_7/extensions/regress-353249.js js1_7/extensions/regress-353454.js js1_7/extensions/regress-354945-02.js js1_7/extensions/regress-355145.js js1_7/extensions/regress-367629.js js1_7/extensions/regress-368213.js js1_7/extensions/regress-368224.js js1_7/extensions/regress-379482.js js1_7/extensions/regress-379566.js js1_7/extensions/regress-381301.js js1_7/extensions/regress-381303.js js1_7/iterable/regress-340526-02.js js1_7/regress/regress-350387.js js1_7/regress/regress-351503-01.js js1_7/regress/regress-351503-02.js js1_7/regress/regress-352870-01.js js1_7/regress/regress-352870-02.js js1_7/regress/regress-363040-01.js js1_7/regress/regress-363040-02.js js1_7/regress/regress-375695.js js1_7/regress/regress-379483.js # JS 1.8 not yet implemented js1_8 # nondeterministic js1_5/Regress/regress-211590.js # long-running tests ecma/Date/15.9.5.10-2.js ecma/Date/15.9.5.11-2.js ecma/Date/15.9.5.12-2.js ecma/Date/15.9.5.8.js js1_5/Regress/regress-366122.js # "options" built-in function ecma_2/Exceptions/lexical-011.js ecma_2/Exceptions/lexical-014.js ecma_2/Exceptions/lexical-016.js ecma_2/Exceptions/lexical-021.js ecma_2/LexicalConventions/keywords-001.js js1_6/Regress/regress-378492.js js1_5/Regress/regress-115436.js js1_5/Regress/regress-214761.js js1_5/Regress/regress-253150.js js1_5/Regress/regress-306633.js js1_7/iterable/regress-355075-01.js js1_7/iterable/regress-355075-02.js js1_7/block/regress-347559.js js1_5/Regress/regress-383674.js # Function "exec" must be called directly, and not by way of a function of # another name. js1_5/extensions/regress-367119-01.js js1_5/extensions/regress-367119-02.js # Obsolete JS 1.2 behavior js1_2/operator/equality.js js1_3/regress/function-001-n.js js1_3/Script/function-001-n.js js1_2/function/function-001-n.js ecma_2/Exceptions/function-001.js # likely bugs. need investigation js1_6/Array/regress-386030.js ecma_3/Array/regress-387501.js js1_7/lexical/regress-336376-01.js # minor decompilation incompatibility js1_5/extensions/regress-384680.js # bad tests? Looks like Daylight savings time failures. ecma/Date/15.9.5.28-1.js ecma/Date/15.9.5.29-1.js # uninvestigated failures. May be some bugs in here. e4x/Regress/regress-373082.js e4x/XML/regress-376773.js e4x/decompilation/regress-352013.js e4x/decompilation/regress-352789.js e4x/extensions/regress-374163.js js1_5/Regress/regress-346237.js js1_5/decompilation/regress-351219.js js1_5/decompilation/regress-352453.js js1_5/decompilation/regress-353146.js js1_5/decompilation/regress-375882.js js1_5/extensions/regress-330569.js js1_5/extensions/regress-351448.js js1_5/extensions/regress-355736.js js1_5/extensions/regress-374589.js e4x/Expressions/11.1.4-01.js e4x/Expressions/11.1.4-02.js e4x/Expressions/11.1.4-03.js e4x/Expressions/11.1.4-04.js e4x/Expressions/11.1.4-08.js e4x/Expressions/11.1.4.js e4x/Expressions/11.6.1.js e4x/GC/regress-280844-1.js e4x/GC/regress-280844-2.js e4x/GC/regress-313952-02.js e4x/GC/regress-324278.js e4x/GC/regress-339785.js e4x/GC/regress-357063-01.js e4x/GC/regress-357063-02.js e4x/Global/13.1.2.1.js e4x/Regress/regress-257679.js e4x/Regress/regress-308111.js e4x/Regress/regress-309897.js e4x/Regress/regress-311580.js e4x/Regress/regress-319872.js e4x/Regress/regress-321547.js e4x/Regress/regress-322499.js e4x/Regress/regress-323338-1.js e4x/Regress/regress-323338-2.js e4x/Regress/regress-327564.js e4x/Regress/regress-329257.js e4x/Regress/regress-331558.js e4x/Regress/regress-344455.js e4x/Regress/regress-347155.js e4x/decompilation/regress-350226.js e4x/Regress/regress-350238.js e4x/decompilation/regress-350531.js e4x/Regress/regress-350629.js e4x/decompilation/regress-351706.js e4x/decompilation/regress-351988.js e4x/Regress/regress-352097.js e4x/Regress/regress-352223.js e4x/decompilation/regress-352649.js e4x/Regress/regress-354998.js e4x/Regress/regress-355478.js e4x/Regress/regress-356238-02.js e4x/Regress/regress-356238-03.js e4x/Regress/regress-361451.js e4x/Regress/regress-364017.js e4x/Regress/regress-369740.js e4x/Regress/regress-370016.js e4x/Regress/regress-370048-01.js e4x/Regress/regress-370048-02.js e4x/Regress/regress-370372.js e4x/Regress/regress-371369.js e4x/Regress/regress-372563.js e4x/TypeConversion/10.2.1.js e4x/TypeConversion/10.5.1.js e4x/TypeConversion/10.5.js e4x/TypeConversion/10.6.1.js e4x/TypeConversion/10.6.js e4x/TypeConversion/regress-302097.js e4x/Types/9.1.1.10.js e4x/Types/9.1.1.11.js e4x/Types/9.1.1.12.js e4x/Types/9.1.1.13.js e4x/Types/9.1.1.4.js e4x/Types/9.1.1.5.js e4x/Types/9.1.1.7.js e4x/Types/9.1.1.8.js e4x/Types/9.1.1.9.js e4x/Types/9.2.1.10.js e4x/Types/9.2.1.3.js e4x/Types/9.2.1.4.js e4x/Types/9.2.1.5.js e4x/Types/9.2.1.6.js e4x/Types/9.2.1.7.js e4x/Types/9.2.1.9.js e4x/XML/13.4.3.js e4x/XML/13.4.4.1.js e4x/XML/13.4.4.10.js e4x/XML/13.4.4.17.js e4x/XML/13.4.4.26.js e4x/XML/13.4.4.28.js e4x/XML/13.4.4.3-01.js e4x/XML/13.4.4.3-02.js e4x/XML/regress-324422-2.js e4x/XMLList/regress-373072.js e4x/decompilation/decompile-xml-escapes.js e4x/extensions/regress-312196.js e4x/extensions/regress-313080.js e4x/extensions/regress-337226.js e4x/extensions/regress-352846-01.js e4x/extensions/regress-352846-02.js e4x/extensions/regress-352846-03.js e4x/extensions/regress-353165.js ecma/Array/15.4.3.1-2.js ecma/Array/15.4.5.1-1.js ecma/Boolean/15.6.3.1-1.js ecma/FunctionObjects/15.3.3.1-2.js ecma/Number/15.7.3.1-3.js ecma/ObjectObjects/15.2.3.1-1.js ecma/Statements/12.6.3-11.js ecma/Statements/12.6.3-2.js ecma/String/15.5.3.1-1.js ecma_2/RegExp/exec-001.js ecma_2/RegExp/regexp-enumerate-001.js ecma_2/String/replace-001.js ecma_2/instanceof/instanceof-003.js ecma_3/Array/regress-322135-02.js ecma_3/Array/regress-322135-03.js ecma_3/Array/regress-322135-04.js ecma_3/Date/15.9.3.2-1.js ecma_3/Exceptions/15.11.7.6-001.js ecma_3/Object/8.6.1-01.js ecma_3/RegExp/regress-188206.js ecma_3/RegExp/regress-289669.js ecma_3/RegExp/regress-307456.js ecma_3/RegExp/regress-309840.js ecma_3/RegExp/regress-311414.js ecma_3/RegExp/regress-330684.js ecma_3/Statements/regress-121744.js ecma_3/String/15.5.4.14.js ecma_3/String/regress-304376.js ecma_3/extensions/regress-274152.js js1_1/regress/function-001.js js1_2/function/Function_object.js js1_2/function/regexparg-1.js js1_2/function/tostring-1.js js1_2/function/tostring-2.js js1_5/Array/regress-313153.js js1_5/Array/regress-330812.js js1_5/Array/regress-345961.js js1_5/Array/regress-350256-03.js js1_5/Array/regress-364104.js js1_5/Date/regress-301738-02.js js1_5/Date/regress-309925-02.js js1_5/Date/regress-346363.js js1_5/Date/toLocaleFormat.js js1_5/Exceptions/regress-315147.js js1_5/Exceptions/regress-332472.js js1_5/Exceptions/regress-333728.js js1_5/Exceptions/regress-342359.js js1_5/Exceptions/regress-350650-n.js js1_5/decompilation/regress-346902.js js1_5/decompilation/regress-346904.js js1_5/Expressions/regress-96526-delelem.js js1_5/Function/regress-344120.js js1_5/GC/regress-203278-2.js js1_5/GC/regress-278725.js js1_5/GC/regress-316885-01.js js1_5/GC/regress-324278.js js1_5/GC/regress-346794.js js1_5/GC/regress-348532.js js1_5/GetSet/getset-002.js js1_5/GetSet/regress-353264.js js1_5/LexicalConventions/regress-343675.js js1_5/Object/regress-308806-01.js js1_5/Regress/regress-103602.js js1_5/Regress/regress-106244.js js1_5/Regress/regress-139316.js js1_5/Regress/regress-173067.js js1_5/Regress/regress-203278-1.js js1_5/Regress/regress-213482.js js1_5/Regress/regress-248444.js js1_5/Regress/regress-252892.js js1_5/Regress/regress-261886.js js1_5/Regress/regress-261887.js js1_5/Regress/regress-274035.js js1_5/Regress/regress-280769-5.js js1_5/Regress/regress-280769.js js1_5/Regress/regress-281606.js js1_5/Regress/regress-290575.js js1_5/Regress/regress-294302.js js1_5/Regress/regress-315974.js js1_5/Regress/regress-317533.js js1_5/Regress/regress-319391.js js1_5/Regress/regress-320119.js js1_5/Regress/regress-321971.js js1_5/Regress/regress-323314-1.js js1_5/Regress/regress-325925.js js1_5/Regress/regress-328664.js js1_5/Regress/regress-329530.js js1_5/Regress/regress-334807-02.js js1_5/Regress/regress-334807-04.js js1_5/Regress/regress-336100.js js1_5/decompilation/regress-346892.js js1_5/decompilation/regress-346915.js js1_5/decompilation/regress-349491.js js1_5/decompilation/regress-349596.js js1_5/Regress/regress-349648.js js1_5/decompilation/regress-349650.js js1_5/decompilation/regress-350242.js js1_5/decompilation/regress-350263.js js1_5/Regress/regress-350268.js js1_5/decompilation/regress-350271.js js1_5/decompilation/regress-350666.js js1_5/Regress/regress-350692.js js1_5/decompilation/regress-351104.js js1_5/decompilation/regress-351597.js js1_5/decompilation/regress-351693.js js1_5/decompilation/regress-351793.js js1_5/Regress/regress-352009.js js1_5/decompilation/regress-352013.js js1_5/Regress/regress-352197.js js1_5/decompilation/regress-352202.js js1_5/decompilation/regress-352375.js js1_5/decompilation/regress-352649.js js1_5/decompilation/regress-353000.js js1_5/Regress/regress-354924.js js1_5/Regress/regress-355341.js js1_5/Regress/regress-355344.js js1_5/Regress/regress-355556.js js1_5/decompilation/regress-355992.js js1_5/Regress/regress-356693.js js1_5/Regress/regress-360969-05.js js1_5/Regress/regress-360969-06.js js1_5/Regress/regress-361467.js js1_5/Regress/regress-3649-n.js js1_5/Regress/regress-372364.js js1_5/extensions/getset-001.js js1_5/extensions/getset-003.js js1_5/extensions/regress-164697.js js1_5/extensions/regress-245148.js js1_5/extensions/regress-254375.js js1_5/extensions/regress-303277.js js1_5/extensions/regress-306738.js js1_5/extensions/regress-313630.js js1_5/extensions/regress-333541.js js1_5/extensions/regress-335700.js js1_5/extensions/regress-338804-02.js js1_5/extensions/regress-342960.js js1_5/extensions/regress-345967.js js1_5/extensions/regress-346494.js js1_5/extensions/regress-347306-02.js js1_5/extensions/regress-348986.js js1_5/extensions/regress-349616.js js1_5/extensions/regress-350531.js js1_5/extensions/regress-352060.js js1_5/extensions/regress-352094.js js1_5/extensions/regress-352372.js js1_5/extensions/regress-352455.js js1_5/extensions/regress-353214.js js1_5/extensions/regress-354541-02.js js1_5/extensions/regress-354541-04.js js1_5/extensions/regress-355339.js js1_5/extensions/regress-355497.js js1_5/extensions/regress-355820.js js1_5/extensions/regress-356085.js js1_5/extensions/regress-361346.js js1_5/extensions/regress-361360.js js1_5/extensions/regress-361552.js js1_5/extensions/regress-361558.js js1_5/extensions/regress-361571.js js1_5/extensions/regress-361964.js js1_5/extensions/regress-365869.js js1_5/extensions/regress-367923.js js1_5/extensions/regress-368859.js js1_6/Array/regress-290592.js js1_6/Array/regress-310425-01.js js1_6/Array/regress-320887.js js1_6/Regress/regress-350417.js js1_6/decompilation/regress-352084.js js1_6/decompilation/regress-352613-01.js js1_6/decompilation/regress-352613-02.js js1_6/Regress/regress-355002.js js1_6/Regress/regress-372565.js js1_6/String/regress-306591.js js1_6/extensions/regress-312385-01.js js1_6/extensions/regress-352392.js lc2/Arrays/array-008-n.js e4x/Expressions/regress-366123.js e4x/decompilation/regress-352459.js ecma/Date/15.9.5.31-1.js ecma/Date/15.9.5.35-1.js js1_5/Regress/regress-306727.js js1_5/Regress/regress-308566.js js1_5/Regress/regress-312260.js js1_5/Regress/regress-322430.js js1_5/decompilation/regress-356083.js js1_5/decompilation/regress-356248.js js1_5/extensions/regress-361856.js js1_5/extensions/regress-365527.js js1_5/extensions/regress-376052.js js1_6/Regress/regress-353078.js ecma_3/RegExp/regress-375715-01-n.js js1_5/extensions/regress-380581.js js1_5/extensions/regress-381205.js js1_5/extensions/regress-381211.js e4x/Regress/regress-383255.js ecma_3/RegExp/regress-375711.js ecma_3/RegExp/regress-375715-04.js js1_5/Regress/regress-367561-03.js js1_5/extensions/regress-367630.js js1_5/extensions/regress-375801.js js1_5/extensions/regress-380831.js js1_5/extensions/regress-382509.js js1_5/extensions/regress-383965.js js1_6/Regress/regress-382509.js e4x/Regress/regress-380833.js js1_5/extensions/regress-358594-01.js js1_5/extensions/regress-358594-02.js js1_5/extensions/regress-358594-03.js js1_5/extensions/regress-358594-04.js js1_5/extensions/regress-358594-05.js js1_5/extensions/regress-358594-06.js js1_7/extensions/regress-380933.js ecma_3/extensions/regress-368516.js js1_5/extensions/regress-351463-01.js js1_5/extensions/regress-355655.js js1_7/block/regress-352422.js js1_7/block/regress-352786.js js1_7/block/regress-352907.js js1_7/block/regress-376410.js js1_7/block/regress-411279.js ecma_3/Number/15.7.4.2-01.js ecma_3/Number/15.7.4.3-01.js ecma_3/Number/15.7.4.7-2.js e4x/Regress/regress-355569.js ecma_3/Array/15.4.5.1-01.js ecma_3/Operators/order-01.js ecma_3/Statements/regress-444979.js ecma_3/String/15.5.4.11.js ecma_3/Unicode/regress-352044-01.js ecma_3/extensions/regress-430740.js ecma_3_1/Object/regress-444787.js ecma_3_1/RegExp/regress-305064.js js1_5/Array/regress-456845.js js1_5/Regress/regress-352604.js js1_5/Regress/regress-410852.js js1_5/Regress/regress-416628.js js1_5/Regress/regress-420919.js js1_5/Regress/regress-422348.js js1_5/Regress/regress-449627.js js1_5/Regress/regress-450369.js js1_5/Regress/regress-451946.js js1_5/Regress/regress-452008.js js1_5/Regress/regress-452170.js js1_5/Regress/regress-452333.js js1_5/Regress/regress-452336.js js1_5/Regress/regress-452495.js js1_5/Regress/regress-452573-01.js js1_5/Regress/regress-452573-02.js js1_5/Regress/regress-452713.js js1_5/Regress/regress-452724-01.js js1_5/Regress/regress-452724-02.js js1_5/Regress/regress-452853.js js1_5/Regress/regress-452884-01.js js1_5/Regress/regress-452884-02.js js1_5/Regress/regress-453173.js js1_5/Regress/regress-453397.js js1_5/Regress/regress-453701.js js1_5/Regress/regress-453747.js js1_5/Regress/regress-454682.js js1_5/Regress/regress-454981.js js1_5/Regress/regress-455605.js js1_5/Regress/regress-455748.js js1_5/Regress/regress-455758-01.js js1_5/Regress/regress-455758-02.js js1_5/Regress/regress-456477-01.js js1_5/Regress/regress-456477-02.js js1_5/Regress/regress-456494.js js1_5/Regress/regress-456540-01.js js1_5/Regress/regress-456540-02.js js1_5/Regress/regress-457456.js js1_5/Regress/regress-457778.js js1_5/Regress/regress-459085.js js1_5/decompilation/regress-437288-02.js js1_5/decompilation/regress-457824.js js1_5/extensions/regress-352604.js js1_5/extensions/regress-356378.js js1_5/extensions/regress-359024.js js1_5/extensions/regress-416834.js js1_5/extensions/regress-418730.js js1_5/extensions/regress-420612.js js1_5/extensions/regress-420869-01.js js1_5/extensions/regress-421621.js js1_5/extensions/regress-424257.js js1_5/extensions/regress-424683-01.js js1_5/extensions/regress-432075.js js1_5/extensions/regress-434837-01.js js1_5/extensions/regress-435345-01.js js1_5/extensions/regress-437288-01.js js1_5/extensions/regress-452178.js js1_5/extensions/regress-452338.js js1_5/extensions/regress-452372.js js1_5/extensions/regress-452565.js js1_5/extensions/regress-453249.js js1_5/extensions/regress-455380.js js1_5/extensions/regress-455408.js js1_5/extensions/regress-455413.js js1_6/extensions/regress-455464-01.js js1_6/extensions/regress-455464-02.js js1_6/extensions/regress-455464-03.js js1_6/extensions/regress-456826.js js1_7/extensions/regress-455982-01.js js1_7/extensions/regress-455982-02.js js1_7/regress/regress-385133-01.js js1_7/regress/regress-385133-02.js js1_7/regress/regress-410649.js js1_7/regress/regress-420399.js js1_7/regress/regress-452703.js js1_7/regress/regress-452960.js js1_7/regress/regress-453049.js js1_7/regress/regress-453051.js js1_7/regress/regress-453411.js closure-compiler-20130227+dfsg1/rhino/testsrc/assert.js0000644000175000017500000003102114433667662021043 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function MjsUnitAssertionError(message) { this.message = message; // This allows fetching the stack trace using TryCatch::StackTrace. this.stack = new Error("").stack; } /* * This file is included in all mini jsunit test cases. The test * framework expects lines that signal failed tests to start with * the f-word and ignore all other lines. */ MjsUnitAssertionError.prototype.toString = function () { return this.message; }; // Expected and found values the same objects, or the same primitive // values. // For known primitive values, please use assertEquals. var assertSame; // Expected and found values are identical primitive values or functions // or similarly structured objects (checking internal properties // of, e.g., Number and Date objects, the elements of arrays // and the properties of non-Array objects). var assertEquals; // The difference between expected and found value is within certain tolerance. var assertEqualsDelta; // The found object is an Array with the same length and elements // as the expected object. The expected object doesn't need to be an Array, // as long as it's "array-ish". var assertArrayEquals; // The found object must have the same enumerable properties as the // expected object. The type of object isn't checked. var assertPropertiesEqual; // Assert that the string conversion of the found value is equal to // the expected string. Only kept for backwards compatability, please // check the real structure of the found value. var assertToStringEquals; // Checks that the found value is true. Use with boolean expressions // for tests that doesn't have their own assertXXX function. var assertTrue; // Checks that the found value is false. var assertFalse; // Checks that the found value is null. Kept for historical compatibility, // please just use assertEquals(null, expected). var assertNull; // Checks that the found value is *not* null. var assertNotNull; // Assert that the passed function or eval code throws an exception. // The optional second argument is an exception constructor that the // thrown exception is checked against with "instanceof". // The optional third argument is a message type string that is compared // to the type property on the thrown exception. var assertThrows; // Assert that the passed function or eval code does not throw an exception. var assertDoesNotThrow; // Asserts that the found value is an instance of the constructor passed // as the second argument. var assertInstanceof; // Assert that this code is never executed (i.e., always fails if executed). var assertUnreachable; // Assert that the function code is (not) optimized. If "no sync" is passed // as second argument, we do not wait for the concurrent optimization thread to // finish when polling for optimization status. // Only works with --allow-natives-syntax. var assertOptimized; var assertUnoptimized; (function () { // Scope for utility functions. function classOf(object) { // Argument must not be null or undefined. var string = Object.prototype.toString.call(object); // String has format [object ]. return string.substring(8, string.length - 1); } function PrettyPrint(value) { switch (typeof value) { case "string": return JSON.stringify(value); case "number": if (value === 0 && (1 / value) < 0) return "-0"; // FALLTHROUGH. case "boolean": case "undefined": case "function": return String(value); case "object": if (value === null) return "null"; var objectClass = classOf(value); switch (objectClass) { case "Number": case "String": case "Boolean": case "Date": return objectClass + "(" + PrettyPrint(value.valueOf()) + ")"; case "RegExp": return value.toString(); case "Array": return "[" + value.map(PrettyPrintArrayElement).join(",") + "]"; case "Object": break; default: return objectClass + "()"; } // [[Class]] is "Object". var name = value.constructor.name; if (name) return name + "()"; return "Object()"; default: return "-- unknown value --"; } } function PrettyPrintArrayElement(value, index, array) { if (value === undefined && !(index in array)) return ""; return PrettyPrint(value); } function fail(expectedText, found, name_opt) { var message = "Fail" + "ure"; if (name_opt) { // Fix this when we ditch the old test runner. message += " (" + name_opt + ")"; } message += ": expected <" + expectedText + "> found <" + PrettyPrint(found) + ">"; throw new MjsUnitAssertionError(message); } function deepObjectEquals(a, b) { var aProps = Object.keys(a); aProps.sort(); var bProps = Object.keys(b); bProps.sort(); if (!deepEquals(aProps, bProps)) { return false; } for (var i = 0; i < aProps.length; i++) { if (!deepEquals(a[aProps[i]], b[aProps[i]])) { return false; } } return true; } function deepEquals(a, b) { if (a === b) { // Check for -0. if (a === 0) return (1 / a) === (1 / b); return true; } if (typeof a != typeof b) return false; if (typeof a == "number") return isNaN(a) && isNaN(b); if (typeof a !== "object" && typeof a !== "function") return false; // Neither a nor b is primitive. var objectClass = classOf(a); if (objectClass !== classOf(b)) return false; if (objectClass === "RegExp") { // For RegExp, just compare pattern and flags using its toString. return (a.toString() === b.toString()); } // Functions are only identical to themselves. if (objectClass === "Function") return false; if (objectClass === "Array") { var elementCount = 0; if (a.length != b.length) { return false; } for (var i = 0; i < a.length; i++) { if (!deepEquals(a[i], b[i])) return false; } return true; } if (objectClass == "String" || objectClass == "Number" || objectClass == "Boolean" || objectClass == "Date") { if (a.valueOf() !== b.valueOf()) return false; } return deepObjectEquals(a, b); } function checkArity(args, arity, name) { if (args.length < arity) { fail(PrettyPrint(arity), args.length, name + " requires " + arity + " or more arguments"); } } assertSame = function assertSame(expected, found, name_opt) { checkArity(arguments, 2, "assertSame"); // TODO(mstarzinger): We should think about using Harmony's egal operator // or the function equivalent Object.is() here. if (found === expected) { if (expected !== 0 || (1 / expected) == (1 / found)) return; } else if ((expected !== expected) && (found !== found)) { return; } fail(PrettyPrint(expected), found, name_opt); }; assertEquals = function assertEquals(expected, found, name_opt) { checkArity(arguments, 2, "assertEquals"); if (!deepEquals(found, expected)) { fail(PrettyPrint(expected), found, name_opt); } }; assertEqualsDelta = function assertEqualsDelta(expected, found, delta, name_opt) { assertTrue(Math.abs(expected - found) <= delta, name_opt); }; assertArrayEquals = function assertArrayEquals(expected, found, name_opt) { var start = ""; if (name_opt) { start = name_opt + " - "; } assertEquals(expected.length, found.length, start + "array length"); if (expected.length == found.length) { for (var i = 0; i < expected.length; ++i) { assertEquals(expected[i], found[i], start + "array element at index " + i); } } }; assertPropertiesEqual = function assertPropertiesEqual(expected, found, name_opt) { // Check properties only. if (!deepObjectEquals(expected, found)) { fail(expected, found, name_opt); } }; assertToStringEquals = function assertToStringEquals(expected, found, name_opt) { if (expected != String(found)) { fail(expected, found, name_opt); } }; assertTrue = function assertTrue(value, name_opt) { assertEquals(true, value, name_opt); }; assertFalse = function assertFalse(value, name_opt) { assertEquals(false, value, name_opt); }; assertNull = function assertNull(value, name_opt) { if (value !== null) { fail("null", value, name_opt); } }; assertNotNull = function assertNotNull(value, name_opt) { if (value === null) { fail("not null", value, name_opt); } }; assertThrows = function assertThrows(code, type_opt, cause_opt) { var threwException = true; try { if (typeof code == 'function') { code(); } else { eval(code); } threwException = false; } catch (e) { if (typeof type_opt == 'function') { assertInstanceof(e, type_opt); } if (arguments.length >= 3) { assertEquals(e.type, cause_opt); } // Success. return; } throw new MjsUnitAssertionError("Did not throw exception"); }; assertInstanceof = function assertInstanceof(obj, type) { if (!(obj instanceof type)) { var actualTypeName = null; var actualConstructor = Object.getPrototypeOf(obj).constructor; if (typeof actualConstructor == "function") { actualTypeName = actualConstructor.name || String(actualConstructor); } fail("Object <" + PrettyPrint(obj) + "> is not an instance of <" + (type.name || type) + ">" + (actualTypeName ? " but of < " + actualTypeName + ">" : "")); } }; assertDoesNotThrow = function assertDoesNotThrow(code, name_opt) { try { if (typeof code == 'function') { code(); } else { eval(code); } } catch (e) { fail("threw an exception: ", e.message || e, name_opt); } }; assertUnreachable = function assertUnreachable(name_opt) { // Fix this when we ditch the old test runner. var message = "Fail" + "ure: unreachable"; if (name_opt) { message += " - " + name_opt; } throw new MjsUnitAssertionError(message); }; var OptimizationStatusImpl = undefined; var OptimizationStatus = function(fun, sync_opt) { if (OptimizationStatusImpl === undefined) { try { OptimizationStatusImpl = new Function( "fun", "sync", "return %GetOptimizationStatus(fun, sync);"); } catch (e) { throw new Error("natives syntax not allowed"); } } return OptimizationStatusImpl(fun, sync_opt); } assertUnoptimized = function assertUnoptimized(fun, sync_opt, name_opt) { if (sync_opt === undefined) sync_opt = ""; assertTrue(OptimizationStatus(fun, sync_opt) != 1, name_opt); } assertOptimized = function assertOptimized(fun, sync_opt, name_opt) { if (sync_opt === undefined) sync_opt = ""; assertTrue(OptimizationStatus(fun, sync_opt) != 2, name_opt); } })(); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/0000755000175000017500000000000014433667662021324 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/0000755000175000017500000000000014433667662024003 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/string-base64.js0000644000175000017500000001171714433667662026740 0ustar apoapo/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla XML-RPC Client component. * * The Initial Developer of the Original Code is * Digital Creations 2, Inc. * Portions created by the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Martijn Pieters (original author) * Samuel Sieb * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // From: http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 /* Convert data (an array of integers) to a Base64 string. */ var toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var base64Pad = '='; function toBase64(data) { var result = ''; var length = data.length; var i; // Convert every three bytes to 4 ascii characters. for (i = 0; i < (length - 2); i += 3) { result += toBase64Table[data.charCodeAt(i) >> 2]; result += toBase64Table[((data.charCodeAt(i) & 0x03) << 4) + (data.charCodeAt(i+1) >> 4)]; result += toBase64Table[((data.charCodeAt(i+1) & 0x0f) << 2) + (data.charCodeAt(i+2) >> 6)]; result += toBase64Table[data.charCodeAt(i+2) & 0x3f]; } // Convert the remaining 1 or 2 bytes, pad out to 4 characters. if (length%3) { i = length - (length%3); result += toBase64Table[data.charCodeAt(i) >> 2]; if ((length%3) == 2) { result += toBase64Table[((data.charCodeAt(i) & 0x03) << 4) + (data.charCodeAt(i+1) >> 4)]; result += toBase64Table[(data.charCodeAt(i+1) & 0x0f) << 2]; result += base64Pad; } else { result += toBase64Table[(data.charCodeAt(i) & 0x03) << 4]; result += base64Pad + base64Pad; } } return result; } /* Convert Base64 data to a string */ var toBinaryTable = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 ]; function base64ToString(data) { var result = ''; var leftbits = 0; // number of bits decoded, but yet to be appended var leftdata = 0; // bits decoded, but yet to be appended // Convert one by one. for (var i = 0; i < data.length; i++) { var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; var padding = (data.charCodeAt(i) == base64Pad.charCodeAt(0)); // Skip illegal characters and whitespace if (c == -1) continue; // Collect data into leftdata, update bitcount leftdata = (leftdata << 6) | c; leftbits += 6; // If we have 8 or more bits, append 8 bits to the result if (leftbits >= 8) { leftbits -= 8; // Append if not padding. if (!padding) result += String.fromCharCode((leftdata >> leftbits) & 0xff); leftdata &= (1 << leftbits) - 1; } } // If there are any bits left, the base64 string was corrupted if (leftbits) throw Components.Exception('Corrupted base64 string'); return result; } var str = ""; for ( var i = 0; i < 8192; i++ ) str += String.fromCharCode( (25 * Math.random()) + 97 ); for ( var i = 8192; i <= 16384; i *= 2 ) { var base64; base64 = toBase64(str); base64ToString(base64); // Double the string str += str; } toBinaryTable = null; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/access-fannkuch.js0000644000175000017500000000302714433667662027377 0ustar apoapo/* The Great Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Isaac Gouy */ function fannkuch(n) { var check = 0; var perm = Array(n); var perm1 = Array(n); var count = Array(n); var maxPerm = Array(n); var maxFlipsCount = 0; var m = n - 1; for (var i = 0; i < n; i++) perm1[i] = i; var r = n; while (true) { // write-out the first 30 permutations if (check < 30){ var s = ""; for(var i=0; i> 1; for (var i = 0; i < k2; i++) { var temp = perm[i]; perm[i] = perm[k - i]; perm[k - i] = temp; } flipsCount++; } if (flipsCount > maxFlipsCount) { maxFlipsCount = flipsCount; for (var i = 0; i < n; i++) maxPerm[i] = perm1[i]; } } while (true) { if (r == n) return maxFlipsCount; var perm0 = perm1[0]; var i = 0; while (i < r) { var j = i + 1; perm1[i] = perm1[j]; i = j; } perm1[r] = perm0; count[r] = count[r] - 1; if (count[r] > 0) break; r++; } } } var n = 8; var ret = fannkuch(n); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/bitops-bitwise-and.js0000644000175000017500000000266414433667662030055 0ustar apoapo/* * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ bitwiseAndValue = 4294967296; for (var i = 0; i < 600000; i++) bitwiseAndValue = bitwiseAndValue & i; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/math-partial-sums.js0000644000175000017500000000135314433667662027713 0ustar apoapo// The Computer Language Shootout // http://shootout.alioth.debian.org/ // contributed by Isaac Gouy function partial(n){ var a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = 0.0; var twothirds = 2.0/3.0; var alt = -1.0; var k2 = k3 = sk = ck = 0.0; for (var k = 1; k <= n; k++){ k2 = k*k; k3 = k2*k; sk = Math.sin(k); ck = Math.cos(k); alt = -alt; a1 += Math.pow(twothirds,k-1); a2 += Math.pow(k,-0.5); a3 += 1.0/(k*(k+1.0)); a4 += 1.0/(k3 * sk*sk); a5 += 1.0/(k3 * ck*ck); a6 += 1.0/k; a7 += 1.0/k2; a8 += alt/k; a9 += alt/(2*k -1); } } for (var i = 1024; i <= 16384; i *= 2) { partial(i); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/bitops-bits-in-byte.js0000644000175000017500000000060414433667662030145 0ustar apoapo// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com) // 1 op = 2 assigns, 16 compare/branches, 8 ANDs, (0-8) ADDs, 8 SHLs // O(n) function bitsinbyte(b) { var m = 1, c = 0; while(m<0x100) { if(b & m) c++; m <<= 1; } return c; } function TimeFunc(func) { var x, y, t; for(var x=0; x<350; x++) for(var y=0; y<256; y++) func(y); } TimeFunc(bitsinbyte); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/string-tagcloud.js0000644000175000017500000052553714433667662027470 0ustar apoapo /* * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Portions from: json.js 2007-10-10 Public Domain */ // This test parses a JSON string giving tag names and popularity, and // generates html markup for a "tagcloud" view. if (!Object.prototype.toJSONString) { Array.prototype.toJSONString = function (w) { var a = [], // The array holding the partial texts. i, // Loop counter. l = this.length, v; // The value to be stringified. for (i = 0; i < l; i += 1) { v = this[i]; switch (typeof v) { case 'object': if (v && typeof v.toJSONString === 'function') { a.push(v.toJSONString(w)); } else { a.push('null'); } break; case 'string': case 'number': case 'boolean': a.push(v.toJSONString()); break; default: a.push('null'); } } return '[' + a.join(',') + ']'; }; Boolean.prototype.toJSONString = function () { return String(this); }; Date.prototype.toJSONString = function () { function f(n) { return n < 10 ? '0' + n : n; } return '"' + this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z"'; }; Number.prototype.toJSONString = function () { return isFinite(this) ? String(this) : 'null'; }; Object.prototype.toJSONString = function (w) { var a = [], // The array holding the partial texts. k, // The current key. i, // The loop counter. v; // The current value. if (w) { for (i = 0; i < w.length; i += 1) { k = w[i]; if (typeof k === 'string') { v = this[k]; switch (typeof v) { case 'object': if (v) { if (typeof v.toJSONString === 'function') { a.push(k.toJSONString() + ':' + v.toJSONString(w)); } } else { a.push(k.toJSONString() + ':null'); } break; case 'string': case 'number': case 'boolean': a.push(k.toJSONString() + ':' + v.toJSONString()); } } } } else { for (k in this) { if (typeof k === 'string' && Object.prototype.hasOwnProperty.apply(this, [k])) { v = this[k]; switch (typeof v) { case 'object': if (v) { if (typeof v.toJSONString === 'function') { a.push(k.toJSONString() + ':' + v.toJSONString()); } } else { a.push(k.toJSONString() + ':null'); } break; case 'string': case 'number': case 'boolean': a.push(k.toJSONString() + ':' + v.toJSONString()); } } } } return '{' + a.join(',') + '}'; }; (function (s) { var m = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }; s.parseJSON = function (filter) { var j; function walk(k, v) { var i, n; if (v && typeof v === 'object') { for (i in v) { if (Object.prototype.hasOwnProperty.apply(v, [i])) { n = walk(i, v[i]); if (n !== undefined) { v[i] = n; } } } } return filter(k, v); } if (/^[\],:{}\s]*$/.test(this.replace(/\\./g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { j = eval('(' + this + ')'); return typeof filter === 'function' ? walk('', j) : j; } throw new SyntaxError('parseJSON'); }; s.toJSONString = function () { if (/["\\\x00-\x1f]/.test(this)) { return '"' + this.replace(/[\x00-\x1f\\"]/g, function (a) { var c = m[a]; if (c) { return c; } c = a.charCodeAt(); return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); }) + '"'; } return '"' + this + '"'; }; })(String.prototype); } var tagInfoJSON = '[\n {\n \"tag\": "titillation",\n \"popularity\": 4294967296\n },\n {\n \"tag\": "foamless",\n \"popularity\": 1257718401\n },\n {\n \"tag\": "snarler",\n \"popularity\": 613166183\n },\n {\n \"tag\": "multangularness",\n \"popularity\": 368304452\n },\n {\n \"tag\": "Fesapo unventurous",\n \"popularity\": 248026512\n },\n {\n \"tag\": "esthesioblast",\n \"popularity\": 179556755\n },\n {\n \"tag\": "echeneidoid",\n \"popularity\": 136641578\n },\n {\n \"tag\": "embryoctony",\n \"popularity\": 107852576\n },\n {\n \"tag\": "undilatory",\n \"popularity\": 87537981\n },\n {\n \"tag\": "predisregard",\n \"popularity\": 72630939\n },\n {\n \"tag\": "allergenic",\n \"popularity\": 61345190\n },\n {\n \"tag\": "uncloudy",\n \"popularity\": 52580571\n },\n {\n \"tag\": "unforeseeably",\n \"popularity\": 45628109\n },\n {\n \"tag\": "sturniform",\n \"popularity\": 40013489\n },\n {\n \"tag\": "anesthetize",\n \"popularity\": 35409226\n },\n {\n \"tag\": "ametabolia",\n \"popularity\": 31583050\n },\n {\n \"tag\": "angiopathy",\n \"popularity\": 28366350\n },\n {\n \"tag\": "sultanaship",\n \"popularity\": 25634218\n },\n {\n \"tag\": "Frenchwise",\n \"popularity\": 23292461\n },\n {\n \"tag\": "cerviconasal",\n \"popularity\": 21268909\n },\n {\n \"tag\": "mercurialness",\n \"popularity\": 19507481\n },\n {\n \"tag\": "glutelin venditate",\n \"popularity\": 17964042\n },\n {\n \"tag\": "acred overblack",\n \"popularity\": 16603454\n },\n {\n \"tag\": "Atik",\n \"popularity\": 15397451\n },\n {\n \"tag\": "puncturer",\n \"popularity\": 14323077\n },\n {\n \"tag\": "pukatea",\n \"popularity\": 13361525\n },\n {\n \"tag\": "suberize",\n \"popularity\": 12497261\n },\n {\n \"tag\": "Godfrey",\n \"popularity\": 11717365\n },\n {\n \"tag\": "tetraptote",\n \"popularity\": 11011011\n },\n {\n \"tag\": "lucidness",\n \"popularity\": 10369074\n },\n {\n \"tag\": "tartness",\n \"popularity\": 9783815\n },\n {\n \"tag\": "axfetch",\n \"popularity\": 9248634\n },\n {\n \"tag\": "preacquittal",\n \"popularity\": 8757877\n },\n {\n \"tag\": "matris",\n \"popularity\": 8306671\n },\n {\n \"tag\": "hyphenate",\n \"popularity\": 7890801\n },\n {\n \"tag\": "semifabulous",\n \"popularity\": 7506606\n },\n {\n \"tag\": "oppressiveness",\n \"popularity\": 7150890\n },\n {\n \"tag\": "Protococcales",\n \"popularity\": 6820856\n },\n {\n \"tag\": "unpreventive",\n \"popularity\": 6514045\n },\n {\n \"tag\": "Cordia",\n \"popularity\": 6228289\n },\n {\n \"tag\": "Wakamba leaflike",\n \"popularity\": 5961668\n },\n {\n \"tag\": "dacryoma",\n \"popularity\": 5712480\n },\n {\n \"tag\": "inguinal",\n \"popularity\": 5479211\n },\n {\n \"tag\": "responseless",\n \"popularity\": 5260507\n },\n {\n \"tag\": "supplementarily",\n \"popularity\": 5055158\n },\n {\n \"tag\": "emu",\n \"popularity\": 4862079\n },\n {\n \"tag\": "countermeet",\n \"popularity\": 4680292\n },\n {\n \"tag\": "purrer",\n \"popularity\": 4508918\n },\n {\n \"tag\": "Corallinaceae",\n \"popularity\": 4347162\n },\n {\n \"tag\": "speculum",\n \"popularity\": 4194304\n },\n {\n \"tag\": "crimpness",\n \"popularity\": 4049690\n },\n {\n \"tag\": "antidetonant",\n \"popularity\": 3912727\n },\n {\n \"tag\": "topeewallah",\n \"popularity\": 3782875\n },\n {\n \"tag\": "fidalgo ballant",\n \"popularity\": 3659640\n },\n {\n \"tag\": "utriculose",\n \"popularity\": 3542572\n },\n {\n \"tag\": "testata",\n \"popularity\": 3431259\n },\n {\n \"tag\": "beltmaking",\n \"popularity\": 3325322\n },\n {\n \"tag\": "necrotype",\n \"popularity\": 3224413\n },\n {\n \"tag\": "ovistic",\n \"popularity\": 3128215\n },\n {\n \"tag\": "swindlership",\n \"popularity\": 3036431\n },\n {\n \"tag\": "augustal",\n \"popularity\": 2948792\n },\n {\n \"tag\": "Titoist",\n \"popularity\": 2865047\n },\n {\n \"tag\": "trisoctahedral",\n \"popularity\": 2784963\n },\n {\n \"tag\": "sequestrator",\n \"popularity\": 2708327\n },\n {\n \"tag\": "sideburns",\n \"popularity\": 2634939\n },\n {\n \"tag\": "paraphrasia",\n \"popularity\": 2564616\n },\n {\n \"tag\": "graminology unbay",\n \"popularity\": 2497185\n },\n {\n \"tag\": "acaridomatium emargination",\n \"popularity\": 2432487\n },\n {\n \"tag\": "roofward",\n \"popularity\": 2370373\n },\n {\n \"tag\": "lauder",\n \"popularity\": 2310705\n },\n {\n \"tag\": "subjunctive",\n \"popularity\": 2253354\n },\n {\n \"tag\": "subelongate",\n \"popularity\": 2198199\n },\n {\n \"tag\": "guacimo",\n \"popularity\": 2145128\n },\n {\n \"tag\": "cockade",\n \"popularity\": 2094033\n },\n {\n \"tag\": "misgauge",\n \"popularity\": 2044818\n },\n {\n \"tag\": "unexpensive",\n \"popularity\": 1997388\n },\n {\n \"tag\": "chebel",\n \"popularity\": 1951657\n },\n {\n \"tag\": "unpursuing",\n \"popularity\": 1907543\n },\n {\n \"tag\": "kilobar",\n \"popularity\": 1864969\n },\n {\n \"tag\": "obsecration",\n \"popularity\": 1823863\n },\n {\n \"tag\": "nacarine",\n \"popularity\": 1784157\n },\n {\n \"tag\": "spirituosity",\n \"popularity\": 1745787\n },\n {\n \"tag\": "movableness deity",\n \"popularity\": 1708692\n },\n {\n \"tag\": "exostracism",\n \"popularity\": 1672816\n },\n {\n \"tag\": "archipterygium",\n \"popularity\": 1638104\n },\n {\n \"tag\": "monostrophic",\n \"popularity\": 1604506\n },\n {\n \"tag\": "gynecide",\n \"popularity\": 1571974\n },\n {\n \"tag\": "gladden",\n \"popularity\": 1540462\n },\n {\n \"tag\": "throughbred",\n \"popularity\": 1509927\n },\n {\n \"tag\": "groper",\n \"popularity\": 1480329\n },\n {\n \"tag\": "Xenosaurus",\n \"popularity\": 1451628\n },\n {\n \"tag\": "photoetcher",\n \"popularity\": 1423788\n },\n {\n \"tag\": "glucosid",\n \"popularity\": 1396775\n },\n {\n \"tag\": "Galtonian",\n \"popularity\": 1370555\n },\n {\n \"tag\": "mesosporic",\n \"popularity\": 1345097\n },\n {\n \"tag\": "theody",\n \"popularity\": 1320370\n },\n {\n \"tag\": "zaffer",\n \"popularity\": 1296348\n },\n {\n \"tag\": "probiology",\n \"popularity\": 1273003\n },\n {\n \"tag\": "rhizomic",\n \"popularity\": 1250308\n },\n {\n \"tag\": "superphosphate",\n \"popularity\": 1228240\n },\n {\n \"tag\": "Hippolytan",\n \"popularity\": 1206776\n },\n {\n \"tag\": "garget",\n \"popularity\": 1185892\n },\n {\n \"tag\": "diploplacula",\n \"popularity\": 1165568\n },\n {\n \"tag\": "orohydrographical",\n \"popularity\": 1145785\n },\n {\n \"tag\": "enhypostatize",\n \"popularity\": 1126521\n },\n {\n \"tag\": "polisman",\n \"popularity\": 1107759\n },\n {\n \"tag\": "acetometer",\n \"popularity\": 1089482\n },\n {\n \"tag\": "unsnatched",\n \"popularity\": 1071672\n },\n {\n \"tag\": "yabber",\n \"popularity\": 1054313\n },\n {\n \"tag\": "demiwolf",\n \"popularity\": 1037390\n },\n {\n \"tag\": "chromascope",\n \"popularity\": 1020888\n },\n {\n \"tag\": "seamanship",\n \"popularity\": 1004794\n },\n {\n \"tag\": "nonfenestrated",\n \"popularity\": 989092\n },\n {\n \"tag\": "hydrophytism",\n \"popularity\": 973771\n },\n {\n \"tag\": "dotter",\n \"popularity\": 958819\n },\n {\n \"tag\": "thermoperiodism",\n \"popularity\": 944222\n },\n {\n \"tag\": "unlawyerlike",\n \"popularity\": 929970\n },\n {\n \"tag\": "enantiomeride citywards",\n \"popularity\": 916052\n },\n {\n \"tag\": "unmetallurgical",\n \"popularity\": 902456\n },\n {\n \"tag\": "prickled",\n \"popularity\": 889174\n },\n {\n \"tag\": "strangerwise manioc",\n \"popularity\": 876195\n },\n {\n \"tag\": "incisorial",\n \"popularity\": 863510\n },\n {\n \"tag\": "irrationalize",\n \"popularity\": 851110\n },\n {\n \"tag\": "nasology",\n \"popularity\": 838987\n },\n {\n \"tag\": "fatuism",\n \"popularity\": 827131\n },\n {\n \"tag\": "Huk",\n \"popularity\": 815535\n },\n {\n \"tag\": "properispomenon",\n \"popularity\": 804192\n },\n {\n \"tag\": "unpummelled",\n \"popularity\": 793094\n },\n {\n \"tag\": "technographically",\n \"popularity\": 782233\n },\n {\n \"tag\": "underfurnish",\n \"popularity\": 771603\n },\n {\n \"tag\": "sinter",\n \"popularity\": 761198\n },\n {\n \"tag\": "lateroanterior",\n \"popularity\": 751010\n },\n {\n \"tag\": "nonpersonification",\n \"popularity\": 741034\n },\n {\n \"tag\": "Sitophilus",\n \"popularity\": 731264\n },\n {\n \"tag\": "unstudded overexerted",\n \"popularity\": 721694\n },\n {\n \"tag\": "tracheation",\n \"popularity\": 712318\n },\n {\n \"tag\": "thirteenth begloze",\n \"popularity\": 703131\n },\n {\n \"tag\": "bespice",\n \"popularity\": 694129\n },\n {\n \"tag\": "doppia",\n \"popularity\": 685305\n },\n {\n \"tag\": "unadorned",\n \"popularity\": 676656\n },\n {\n \"tag\": "dovelet engraff",\n \"popularity\": 668176\n },\n {\n \"tag\": "diphyozooid",\n \"popularity\": 659862\n },\n {\n \"tag\": "mure",\n \"popularity\": 651708\n },\n {\n \"tag\": "Tripitaka",\n \"popularity\": 643710\n },\n {\n \"tag\": "Billjim",\n \"popularity\": 635865\n },\n {\n \"tag\": "pyramidical",\n \"popularity\": 628169\n },\n {\n \"tag\": "circumlocutionist",\n \"popularity\": 620617\n },\n {\n \"tag\": "slapstick",\n \"popularity\": 613207\n },\n {\n \"tag\": "preobedience",\n \"popularity\": 605934\n },\n {\n \"tag\": "unfriarlike",\n \"popularity\": 598795\n },\n {\n \"tag\": "microchromosome",\n \"popularity\": 591786\n },\n {\n \"tag\": "Orphicism",\n \"popularity\": 584905\n },\n {\n \"tag\": "peel",\n \"popularity\": 578149\n },\n {\n \"tag\": "obediential",\n \"popularity\": 571514\n },\n {\n \"tag\": "Peripatidea",\n \"popularity\": 564997\n },\n {\n \"tag\": "undoubtful",\n \"popularity\": 558596\n },\n {\n \"tag\": "lodgeable",\n \"popularity\": 552307\n },\n {\n \"tag\": "pustulated woodchat",\n \"popularity\": 546129\n },\n {\n \"tag\": "antepast",\n \"popularity\": 540057\n },\n {\n \"tag\": "sagittoid matrimoniously",\n \"popularity\": 534091\n },\n {\n \"tag\": "Albizzia",\n \"popularity\": 528228\n },\n {\n \"tag\": "Elateridae unnewness",\n \"popularity\": 522464\n },\n {\n \"tag\": "convertingness",\n \"popularity\": 516798\n },\n {\n \"tag\": "Pelew",\n \"popularity\": 511228\n },\n {\n \"tag\": "recapitulation",\n \"popularity\": 505751\n },\n {\n \"tag\": "shack",\n \"popularity\": 500365\n },\n {\n \"tag\": "unmellowed",\n \"popularity\": 495069\n },\n {\n \"tag\": "pavis capering",\n \"popularity\": 489859\n },\n {\n \"tag\": "fanfare",\n \"popularity\": 484735\n },\n {\n \"tag\": "sole",\n \"popularity\": 479695\n },\n {\n \"tag\": "subarcuate",\n \"popularity\": 474735\n },\n {\n \"tag\": "multivious",\n \"popularity\": 469856\n },\n {\n \"tag\": "squandermania",\n \"popularity\": 465054\n },\n {\n \"tag\": "scintle",\n \"popularity\": 460329\n },\n {\n \"tag\": "hash chirognomic",\n \"popularity\": 455679\n },\n {\n \"tag\": "linseed",\n \"popularity\": 451101\n },\n {\n \"tag\": "redoubtable",\n \"popularity\": 446596\n },\n {\n \"tag\": "poachy reimpact",\n \"popularity\": 442160\n },\n {\n \"tag\": "limestone",\n \"popularity\": 437792\n },\n {\n \"tag\": "serranid",\n \"popularity\": 433492\n },\n {\n \"tag\": "pohna",\n \"popularity\": 429258\n },\n {\n \"tag\": "warwolf",\n \"popularity\": 425088\n },\n {\n \"tag\": "ruthenous",\n \"popularity\": 420981\n },\n {\n \"tag\": "dover",\n \"popularity\": 416935\n },\n {\n \"tag\": "deuteroalbumose",\n \"popularity\": 412950\n },\n {\n \"tag\": "pseudoprophetic",\n \"popularity\": 409025\n },\n {\n \"tag\": "dissoluteness",\n \"popularity\": 405157\n },\n {\n \"tag\": "preinvention",\n \"popularity\": 401347\n },\n {\n \"tag\": "swagbellied",\n \"popularity\": 397592\n },\n {\n \"tag\": "Ophidia",\n \"popularity\": 393892\n },\n {\n \"tag\": "equanimity",\n \"popularity\": 390245\n },\n {\n \"tag\": "troutful",\n \"popularity\": 386651\n },\n {\n \"tag\": "uke",\n \"popularity\": 383108\n },\n {\n \"tag\": "preacquaint",\n \"popularity\": 379616\n },\n {\n \"tag\": "shoq",\n \"popularity\": 376174\n },\n {\n \"tag\": "yox",\n \"popularity\": 372780\n },\n {\n \"tag\": "unelemental",\n \"popularity\": 369434\n },\n {\n \"tag\": "Yavapai",\n \"popularity\": 366134\n },\n {\n \"tag\": "joulean",\n \"popularity\": 362880\n },\n {\n \"tag\": "dracontine",\n \"popularity\": 359672\n },\n {\n \"tag\": "hardmouth",\n \"popularity\": 356507\n },\n {\n \"tag\": "sylvanize",\n \"popularity\": 353386\n },\n {\n \"tag\": "intraparenchymatous meadowbur",\n \"popularity\": 350308\n },\n {\n \"tag\": "uncharily",\n \"popularity\": 347271\n },\n {\n \"tag\": "redtab flexibly",\n \"popularity\": 344275\n },\n {\n \"tag\": "centervelic",\n \"popularity\": 341319\n },\n {\n \"tag\": "unravellable",\n \"popularity\": 338403\n },\n {\n \"tag\": "infortunately",\n \"popularity\": 335526\n },\n {\n \"tag\": "cannel",\n \"popularity\": 332687\n },\n {\n \"tag\": "oxyblepsia",\n \"popularity\": 329885\n },\n {\n \"tag\": "Damon",\n \"popularity\": 327120\n },\n {\n \"tag\": "etherin",\n \"popularity\": 324391\n },\n {\n \"tag\": "luminal",\n \"popularity\": 321697\n },\n {\n \"tag\": "interrogatorily presbyte",\n \"popularity\": 319038\n },\n {\n \"tag\": "hemiclastic",\n \"popularity\": 316414\n },\n {\n \"tag\": "poh flush",\n \"popularity\": 313823\n },\n {\n \"tag\": "Psoroptes",\n \"popularity\": 311265\n },\n {\n \"tag\": "dispirit",\n \"popularity\": 308740\n },\n {\n \"tag\": "nashgab",\n \"popularity\": 306246\n },\n {\n \"tag\": "Aphidiinae",\n \"popularity\": 303784\n },\n {\n \"tag\": "rhapsody nonconstruction",\n \"popularity\": 301353\n },\n {\n \"tag\": "Osmond",\n \"popularity\": 298952\n },\n {\n \"tag\": "Leonis",\n \"popularity\": 296581\n },\n {\n \"tag\": "Lemnian",\n \"popularity\": 294239\n },\n {\n \"tag\": "acetonic gnathonic",\n \"popularity\": 291926\n },\n {\n \"tag\": "surculus",\n \"popularity\": 289641\n },\n {\n \"tag\": "diagonally",\n \"popularity\": 287384\n },\n {\n \"tag\": "counterpenalty",\n \"popularity\": 285154\n },\n {\n \"tag\": "Eugenie",\n \"popularity\": 282952\n },\n {\n \"tag\": "hornbook",\n \"popularity\": 280776\n },\n {\n \"tag\": "miscoin",\n \"popularity\": 278626\n },\n {\n \"tag\": "admi",\n \"popularity\": 276501\n },\n {\n \"tag\": "Tarmac",\n \"popularity\": 274402\n },\n {\n \"tag\": "inexplicable",\n \"popularity\": 272328\n },\n {\n \"tag\": "rascallion",\n \"popularity\": 270278\n },\n {\n \"tag\": "dusterman",\n \"popularity\": 268252\n },\n {\n \"tag\": "osteostomous unhoroscopic",\n \"popularity\": 266250\n },\n {\n \"tag\": "spinibulbar",\n \"popularity\": 264271\n },\n {\n \"tag\": "phototelegraphically",\n \"popularity\": 262315\n },\n {\n \"tag\": "Manihot",\n \"popularity\": 260381\n },\n {\n \"tag\": "neighborhood",\n \"popularity\": 258470\n },\n {\n \"tag\": "Vincetoxicum",\n \"popularity\": 256581\n },\n {\n \"tag\": "khirka",\n \"popularity\": 254713\n },\n {\n \"tag\": "conscriptive",\n \"popularity\": 252866\n },\n {\n \"tag\": "synechthran",\n \"popularity\": 251040\n },\n {\n \"tag\": "Guttiferales",\n \"popularity\": 249235\n },\n {\n \"tag\": "roomful",\n \"popularity\": 247450\n },\n {\n \"tag\": "germinal",\n \"popularity\": 245685\n },\n {\n \"tag\": "untraitorous",\n \"popularity\": 243939\n },\n {\n \"tag\": "nondissenting",\n \"popularity\": 242213\n },\n {\n \"tag\": "amotion",\n \"popularity\": 240506\n },\n {\n \"tag\": "badious",\n \"popularity\": 238817\n },\n {\n \"tag\": "sumpit",\n \"popularity\": 237147\n },\n {\n \"tag\": "ectozoic",\n \"popularity\": 235496\n },\n {\n \"tag\": "elvet",\n \"popularity\": 233862\n },\n {\n \"tag\": "underclerk",\n \"popularity\": 232246\n },\n {\n \"tag\": "reticency",\n \"popularity\": 230647\n },\n {\n \"tag\": "neutroclusion",\n \"popularity\": 229065\n },\n {\n \"tag\": "unbelieving",\n \"popularity\": 227500\n },\n {\n \"tag\": "histogenetic",\n \"popularity\": 225952\n },\n {\n \"tag\": "dermamyiasis",\n \"popularity\": 224421\n },\n {\n \"tag\": "telenergy",\n \"popularity\": 222905\n },\n {\n \"tag\": "axiomatic",\n \"popularity\": 221406\n },\n {\n \"tag\": "undominoed",\n \"popularity\": 219922\n },\n {\n \"tag\": "periosteoma",\n \"popularity\": 218454\n },\n {\n \"tag\": "justiciaryship",\n \"popularity\": 217001\n },\n {\n \"tag\": "autoluminescence",\n \"popularity\": 215563\n },\n {\n \"tag\": "osmous",\n \"popularity\": 214140\n },\n {\n \"tag\": "borgh",\n \"popularity\": 212731\n },\n {\n \"tag\": "bedebt",\n \"popularity\": 211337\n },\n {\n \"tag\": "considerableness adenoidism",\n \"popularity\": 209957\n },\n {\n \"tag\": "sailorizing",\n \"popularity\": 208592\n },\n {\n \"tag\": "Montauk",\n \"popularity\": 207240\n },\n {\n \"tag\": "Bridget",\n \"popularity\": 205901\n },\n {\n \"tag\": "Gekkota",\n \"popularity\": 204577\n },\n {\n \"tag\": "subcorymbose",\n \"popularity\": 203265\n },\n {\n \"tag\": "undersap",\n \"popularity\": 201967\n },\n {\n \"tag\": "poikilothermic",\n \"popularity\": 200681\n },\n {\n \"tag\": "enneatical",\n \"popularity\": 199409\n },\n {\n \"tag\": "martinetism",\n \"popularity\": 198148\n },\n {\n \"tag\": "sustanedly",\n \"popularity\": 196901\n },\n {\n \"tag\": "declaration",\n \"popularity\": 195665\n },\n {\n \"tag\": "myringoplasty",\n \"popularity\": 194442\n },\n {\n \"tag\": "Ginkgo",\n \"popularity\": 193230\n },\n {\n \"tag\": "unrecurrent",\n \"popularity\": 192031\n },\n {\n \"tag\": "proprecedent",\n \"popularity\": 190843\n },\n {\n \"tag\": "roadman",\n \"popularity\": 189666\n },\n {\n \"tag\": "elemin",\n \"popularity\": 188501\n },\n {\n \"tag\": "maggot",\n \"popularity\": 187347\n },\n {\n \"tag\": "alitrunk",\n \"popularity\": 186204\n },\n {\n \"tag\": "introspection",\n \"popularity\": 185071\n },\n {\n \"tag\": "batiker",\n \"popularity\": 183950\n },\n {\n \"tag\": "backhatch oversettle",\n \"popularity\": 182839\n },\n {\n \"tag\": "thresherman",\n \"popularity\": 181738\n },\n {\n \"tag\": "protemperance",\n \"popularity\": 180648\n },\n {\n \"tag\": "undern",\n \"popularity\": 179568\n },\n {\n \"tag\": "tweeg",\n \"popularity\": 178498\n },\n {\n \"tag\": "crosspath",\n \"popularity\": 177438\n },\n {\n \"tag\": "Tangaridae",\n \"popularity\": 176388\n },\n {\n \"tag\": "scrutation",\n \"popularity\": 175348\n },\n {\n \"tag\": "piecemaker",\n \"popularity\": 174317\n },\n {\n \"tag\": "paster",\n \"popularity\": 173296\n },\n {\n \"tag\": "unpretendingness",\n \"popularity\": 172284\n },\n {\n \"tag\": "inframundane",\n \"popularity\": 171281\n },\n {\n \"tag\": "kiblah",\n \"popularity\": 170287\n },\n {\n \"tag\": "playwrighting",\n \"popularity\": 169302\n },\n {\n \"tag\": "gonepoiesis snowslip",\n \"popularity\": 168326\n },\n {\n \"tag\": "hoodwise",\n \"popularity\": 167359\n },\n {\n \"tag\": "postseason",\n \"popularity\": 166401\n },\n {\n \"tag\": "equivocality",\n \"popularity\": 165451\n },\n {\n \"tag\": "Opiliaceae nuclease",\n \"popularity\": 164509\n },\n {\n \"tag\": "sextipara",\n \"popularity\": 163576\n },\n {\n \"tag\": "weeper",\n \"popularity\": 162651\n },\n {\n \"tag\": "frambesia",\n \"popularity\": 161735\n },\n {\n \"tag\": "answerable",\n \"popularity\": 160826\n },\n {\n \"tag\": "Trichosporum",\n \"popularity\": 159925\n },\n {\n \"tag\": "cajuputol",\n \"popularity\": 159033\n },\n {\n \"tag\": "pleomorphous",\n \"popularity\": 158148\n },\n {\n \"tag\": "aculeolate",\n \"popularity\": 157270\n },\n {\n \"tag\": "wherever",\n \"popularity\": 156400\n },\n {\n \"tag\": "collapse",\n \"popularity\": 155538\n },\n {\n \"tag\": "porky",\n \"popularity\": 154683\n },\n {\n \"tag\": "perule",\n \"popularity\": 153836\n },\n {\n \"tag\": "Nevada",\n \"popularity\": 152996\n },\n {\n \"tag\": "conalbumin",\n \"popularity\": 152162\n },\n {\n \"tag\": "tsunami",\n \"popularity\": 151336\n },\n {\n \"tag\": "Gulf",\n \"popularity\": 150517\n },\n {\n \"tag\": "hertz",\n \"popularity\": 149705\n },\n {\n \"tag\": "limmock",\n \"popularity\": 148900\n },\n {\n \"tag\": "Tartarize",\n \"popularity\": 148101\n },\n {\n \"tag\": "entosphenoid",\n \"popularity\": 147310\n },\n {\n \"tag\": "ibis",\n \"popularity\": 146524\n },\n {\n \"tag\": "unyeaned",\n \"popularity\": 145746\n },\n {\n \"tag\": "tritural",\n \"popularity\": 144973\n },\n {\n \"tag\": "hundredary",\n \"popularity\": 144207\n },\n {\n \"tag\": "stolonlike",\n \"popularity\": 143448\n },\n {\n \"tag\": "chorister",\n \"popularity\": 142694\n },\n {\n \"tag\": "mismove",\n \"popularity\": 141947\n },\n {\n \"tag\": "Andine",\n \"popularity\": 141206\n },\n {\n \"tag\": "Annette proneur escribe",\n \"popularity\": 140471\n },\n {\n \"tag\": "exoperidium",\n \"popularity\": 139742\n },\n {\n \"tag\": "disedge",\n \"popularity\": 139019\n },\n {\n \"tag\": "hypochloruria",\n \"popularity\": 138302\n },\n {\n \"tag\": "prepupa",\n \"popularity\": 137590\n },\n {\n \"tag\": "assent",\n \"popularity\": 136884\n },\n {\n \"tag\": "hydrazobenzene",\n \"popularity\": 136184\n },\n {\n \"tag\": "emballonurid",\n \"popularity\": 135489\n },\n {\n \"tag\": "roselle",\n \"popularity\": 134800\n },\n {\n \"tag\": "unifiedly",\n \"popularity\": 134117\n },\n {\n \"tag\": "clang",\n \"popularity\": 133439\n },\n {\n \"tag\": "acetolytic",\n \"popularity\": 132766\n },\n {\n \"tag\": "cladodont",\n \"popularity\": 132098\n },\n {\n \"tag\": "recoast",\n \"popularity\": 131436\n },\n {\n \"tag\": "celebrated tydie Eocarboniferous",\n \"popularity\": 130779\n },\n {\n \"tag\": "superconsciousness",\n \"popularity\": 130127\n },\n {\n \"tag\": "soberness",\n \"popularity\": 129480\n },\n {\n \"tag\": "panoramist",\n \"popularity\": 128838\n },\n {\n \"tag\": "Orbitolina",\n \"popularity\": 128201\n },\n {\n \"tag\": "overlewd",\n \"popularity\": 127569\n },\n {\n \"tag\": "demiquaver",\n \"popularity\": 126942\n },\n {\n \"tag\": "kamelaukion",\n \"popularity\": 126319\n },\n {\n \"tag\": "flancard",\n \"popularity\": 125702\n },\n {\n \"tag\": "tricuspid",\n \"popularity\": 125089\n },\n {\n \"tag\": "bepelt",\n \"popularity\": 124480\n },\n {\n \"tag\": "decuplet",\n \"popularity\": 123877\n },\n {\n \"tag\": "Rockies",\n \"popularity\": 123278\n },\n {\n \"tag\": "unforgeability",\n \"popularity\": 122683\n },\n {\n \"tag\": "mocha",\n \"popularity\": 122093\n },\n {\n \"tag\": "scrunge",\n \"popularity\": 121507\n },\n {\n \"tag\": "delighter",\n \"popularity\": 120926\n },\n {\n \"tag\": "willey Microtinae",\n \"popularity\": 120349\n },\n {\n \"tag\": "unhuntable",\n \"popularity\": 119777\n },\n {\n \"tag\": "historically",\n \"popularity\": 119208\n },\n {\n \"tag\": "vicegerentship",\n \"popularity\": 118644\n },\n {\n \"tag\": "hemangiosarcoma",\n \"popularity\": 118084\n },\n {\n \"tag\": "harpago",\n \"popularity\": 117528\n },\n {\n \"tag\": "unionoid",\n \"popularity\": 116976\n },\n {\n \"tag\": "wiseman",\n \"popularity\": 116429\n },\n {\n \"tag\": "diclinism",\n \"popularity\": 115885\n },\n {\n \"tag\": "Maud",\n \"popularity\": 115345\n },\n {\n \"tag\": "scaphocephalism",\n \"popularity\": 114809\n },\n {\n \"tag\": "obtenebration",\n \"popularity\": 114277\n },\n {\n \"tag\": "cymar predreadnought",\n \"popularity\": 113749\n },\n {\n \"tag\": "discommend",\n \"popularity\": 113225\n },\n {\n \"tag\": "crude",\n \"popularity\": 112704\n },\n {\n \"tag\": "upflash",\n \"popularity\": 112187\n },\n {\n \"tag\": "saltimbank",\n \"popularity\": 111674\n },\n {\n \"tag\": "posthysterical",\n \"popularity\": 111165\n },\n {\n \"tag\": "trample",\n \"popularity\": 110659\n },\n {\n \"tag\": "ungirthed",\n \"popularity\": 110157\n },\n {\n \"tag\": "unshakable",\n \"popularity\": 109658\n },\n {\n \"tag\": "hepatocystic",\n \"popularity\": 109163\n },\n {\n \"tag\": "psammophyte",\n \"popularity\": 108671\n },\n {\n \"tag\": "millionfold",\n \"popularity\": 108183\n },\n {\n \"tag\": "outtaste",\n \"popularity\": 107698\n },\n {\n \"tag\": "poppycockish",\n \"popularity\": 107217\n },\n {\n \"tag\": "viduine",\n \"popularity\": 106739\n },\n {\n \"tag\": "pleasureman",\n \"popularity\": 106264\n },\n {\n \"tag\": "cholesterolemia",\n \"popularity\": 105792\n },\n {\n \"tag\": "hostlerwife",\n \"popularity\": 105324\n },\n {\n \"tag\": "figure undergrass",\n \"popularity\": 104859\n },\n {\n \"tag\": "bedrape",\n \"popularity\": 104398\n },\n {\n \"tag\": "nuttishness",\n \"popularity\": 103939\n },\n {\n \"tag\": "fow",\n \"popularity\": 103484\n },\n {\n \"tag\": "rachianesthesia",\n \"popularity\": 103031\n },\n {\n \"tag\": "recruitable",\n \"popularity\": 102582\n },\n {\n \"tag\": "semianatomical Oenotheraceae",\n \"popularity\": 102136\n },\n {\n \"tag\": "extracapsular",\n \"popularity\": 101693\n },\n {\n \"tag\": "unsigneted",\n \"popularity\": 101253\n },\n {\n \"tag\": "fissural",\n \"popularity\": 100816\n },\n {\n \"tag\": "ayous",\n \"popularity\": 100381\n },\n {\n \"tag\": "crestfallenness odontograph",\n \"popularity\": 99950\n },\n {\n \"tag\": "monopodium",\n \"popularity\": 99522\n },\n {\n \"tag\": "germfree",\n \"popularity\": 99096\n },\n {\n \"tag\": "dauphin",\n \"popularity\": 98673\n },\n {\n \"tag\": "nonagesimal",\n \"popularity\": 98254\n },\n {\n \"tag\": "waterchat",\n \"popularity\": 97836\n },\n {\n \"tag\": "Entelodon",\n \"popularity\": 97422\n },\n {\n \"tag\": "semischolastic",\n \"popularity\": 97010\n },\n {\n \"tag\": "somata",\n \"popularity\": 96602\n },\n {\n \"tag\": "expositorily",\n \"popularity\": 96195\n },\n {\n \"tag\": "bass",\n \"popularity\": 95792\n },\n {\n \"tag\": "calorimetry",\n \"popularity\": 95391\n },\n {\n \"tag\": "entireness",\n \"popularity\": 94993\n },\n {\n \"tag\": "ratline soppiness",\n \"popularity\": 94597\n },\n {\n \"tag\": "shor",\n \"popularity\": 94204\n },\n {\n \"tag\": "coprecipitation",\n \"popularity\": 93813\n },\n {\n \"tag\": "unblushingly",\n \"popularity\": 93425\n },\n {\n \"tag\": "macarize",\n \"popularity\": 93040\n },\n {\n \"tag\": "scruplesomeness",\n \"popularity\": 92657\n },\n {\n \"tag\": "offsaddle",\n \"popularity\": 92276\n },\n {\n \"tag\": "hypertragical",\n \"popularity\": 91898\n },\n {\n \"tag\": "uncassock loined",\n \"popularity\": 91522\n },\n {\n \"tag\": "interlobate",\n \"popularity\": 91149\n },\n {\n \"tag\": "releasor orrisroot stoloniferously",\n \"popularity\": 90778\n },\n {\n \"tag\": "elementoid",\n \"popularity\": 90410\n },\n {\n \"tag\": "Lentilla",\n \"popularity\": 90043\n },\n {\n \"tag\": "distressing",\n \"popularity\": 89679\n },\n {\n \"tag\": "hydrodrome",\n \"popularity\": 89318\n },\n {\n \"tag\": "Jeannette",\n \"popularity\": 88958\n },\n {\n \"tag\": "Kuli",\n \"popularity\": 88601\n },\n {\n \"tag\": "taxinomist",\n \"popularity\": 88246\n },\n {\n \"tag\": "southwestwardly",\n \"popularity\": 87894\n },\n {\n \"tag\": "polyparia",\n \"popularity\": 87543\n },\n {\n \"tag\": "exmeridian",\n \"popularity\": 87195\n },\n {\n \"tag\": "splenius regimentaled",\n \"popularity\": 86849\n },\n {\n \"tag\": "Sphaeropsidaceae",\n \"popularity\": 86505\n },\n {\n \"tag\": "unbegun",\n \"popularity\": 86163\n },\n {\n \"tag\": "something",\n \"popularity\": 85823\n },\n {\n \"tag\": "contaminable nonexpulsion",\n \"popularity\": 85486\n },\n {\n \"tag\": "douser",\n \"popularity\": 85150\n },\n {\n \"tag\": "prostrike",\n \"popularity\": 84817\n },\n {\n \"tag\": "worky",\n \"popularity\": 84485\n },\n {\n \"tag\": "folliful",\n \"popularity\": 84156\n },\n {\n \"tag\": "prioracy",\n \"popularity\": 83828\n },\n {\n \"tag\": "undermentioned",\n \"popularity\": 83503\n },\n {\n \"tag\": "Judaica",\n \"popularity\": 83179\n },\n {\n \"tag\": "multifarious",\n \"popularity\": 82858\n },\n {\n \"tag\": "poogye",\n \"popularity\": 82538\n },\n {\n \"tag\": "Sparganium",\n \"popularity\": 82221\n },\n {\n \"tag\": "thurrock",\n \"popularity\": 81905\n },\n {\n \"tag\": "outblush",\n \"popularity\": 81591\n },\n {\n \"tag\": "Strophanthus supraordination",\n \"popularity\": 81279\n },\n {\n \"tag\": "gingerroot",\n \"popularity\": 80969\n },\n {\n \"tag\": "unconscient",\n \"popularity\": 80661\n },\n {\n \"tag\": "unconstitutionally",\n \"popularity\": 80354\n },\n {\n \"tag\": "plaguily",\n \"popularity\": 80050\n },\n {\n \"tag\": "waterily equatorwards",\n \"popularity\": 79747\n },\n {\n \"tag\": "nondeposition",\n \"popularity\": 79446\n },\n {\n \"tag\": "dronishly",\n \"popularity\": 79147\n },\n {\n \"tag\": "gateado",\n \"popularity\": 78849\n },\n {\n \"tag\": "dislink",\n \"popularity\": 78553\n },\n {\n \"tag\": "Joceline",\n \"popularity\": 78259\n },\n {\n \"tag\": "amphiboliferous",\n \"popularity\": 77967\n },\n {\n \"tag\": "bushrope",\n \"popularity\": 77676\n },\n {\n \"tag\": "plumicorn sulphosalicylic",\n \"popularity\": 77387\n },\n {\n \"tag\": "nonefficiency",\n \"popularity\": 77100\n },\n {\n \"tag\": "hieroscopy",\n \"popularity\": 76815\n },\n {\n \"tag\": "causativeness",\n \"popularity\": 76531\n },\n {\n \"tag\": "swird paleoeremology",\n \"popularity\": 76249\n },\n {\n \"tag\": "camphoric",\n \"popularity\": 75968\n },\n {\n \"tag\": "retaining",\n \"popularity\": 75689\n },\n {\n \"tag\": "thyreoprotein",\n \"popularity\": 75411\n },\n {\n \"tag\": "carbona",\n \"popularity\": 75136\n },\n {\n \"tag\": "protectively",\n \"popularity\": 74861\n },\n {\n \"tag\": "mosasaur",\n \"popularity\": 74589\n },\n {\n \"tag\": "reciprocator",\n \"popularity\": 74317\n },\n {\n \"tag\": "detentive",\n \"popularity\": 74048\n },\n {\n \"tag\": "supravital",\n \"popularity\": 73780\n },\n {\n \"tag\": "Vespertilionidae",\n \"popularity\": 73513\n },\n {\n \"tag\": "parka",\n \"popularity\": 73248\n },\n {\n \"tag\": "pickaway",\n \"popularity\": 72984\n },\n {\n \"tag\": "oleaceous",\n \"popularity\": 72722\n },\n {\n \"tag\": "anticogitative",\n \"popularity\": 72462\n },\n {\n \"tag\": "woe",\n \"popularity\": 72203\n },\n {\n \"tag\": "skeuomorph",\n \"popularity\": 71945\n },\n {\n \"tag\": "helpmeet",\n \"popularity\": 71689\n },\n {\n \"tag\": "Hexactinellida brickmaking",\n \"popularity\": 71434\n },\n {\n \"tag\": "resink",\n \"popularity\": 71180\n },\n {\n \"tag\": "diluter",\n \"popularity\": 70928\n },\n {\n \"tag\": "micromicron",\n \"popularity\": 70677\n },\n {\n \"tag\": "parentage",\n \"popularity\": 70428\n },\n {\n \"tag\": "galactorrhoea",\n \"popularity\": 70180\n },\n {\n \"tag\": "gey",\n \"popularity\": 69934\n },\n {\n \"tag\": "gesticulatory",\n \"popularity\": 69689\n },\n {\n \"tag\": "wergil",\n \"popularity\": 69445\n },\n {\n \"tag\": "Lecanora",\n \"popularity\": 69202\n },\n {\n \"tag\": "malanders karst",\n \"popularity\": 68961\n },\n {\n \"tag\": "vibetoite",\n \"popularity\": 68721\n },\n {\n \"tag\": "unrequitedness",\n \"popularity\": 68483\n },\n {\n \"tag\": "outwash",\n \"popularity\": 68245\n },\n {\n \"tag\": "unsacred",\n \"popularity\": 68009\n },\n {\n \"tag\": "unabetted dividend",\n \"popularity\": 67775\n },\n {\n \"tag\": "untraveling",\n \"popularity\": 67541\n },\n {\n \"tag\": "thermobattery",\n \"popularity\": 67309\n },\n {\n \"tag\": "polypragmist",\n \"popularity\": 67078\n },\n {\n \"tag\": "irrefutableness",\n \"popularity\": 66848\n },\n {\n \"tag\": "remiges",\n \"popularity\": 66620\n },\n {\n \"tag\": "implode",\n \"popularity\": 66393\n },\n {\n \"tag\": "superfluousness",\n \"popularity\": 66166\n },\n {\n \"tag\": "croakily unalleviated",\n \"popularity\": 65942\n },\n {\n \"tag\": "edicule",\n \"popularity\": 65718\n },\n {\n \"tag\": "entophytous",\n \"popularity\": 65495\n },\n {\n \"tag\": "benefactorship Toryish",\n \"popularity\": 65274\n },\n {\n \"tag\": "pseudoamateurish",\n \"popularity\": 65054\n },\n {\n \"tag\": "flueless Iguanodontoidea snipnose",\n \"popularity\": 64835\n },\n {\n \"tag\": "zealotical Zamicrus interpole",\n \"popularity\": 64617\n },\n {\n \"tag\": "whereabout",\n \"popularity\": 64401\n },\n {\n \"tag\": "benzazide",\n \"popularity\": 64185\n },\n {\n \"tag\": "pokeweed",\n \"popularity\": 63971\n },\n {\n \"tag\": "calamitoid",\n \"popularity\": 63757\n },\n {\n \"tag\": "sporozoal",\n \"popularity\": 63545\n },\n {\n \"tag\": "physcioid Welshwoman",\n \"popularity\": 63334\n },\n {\n \"tag\": "wanting",\n \"popularity\": 63124\n },\n {\n \"tag\": "unencumbering",\n \"popularity\": 62915\n },\n {\n \"tag\": "Tupi",\n \"popularity\": 62707\n },\n {\n \"tag\": "potbank",\n \"popularity\": 62501\n },\n {\n \"tag\": "bulked",\n \"popularity\": 62295\n },\n {\n \"tag\": "uparise",\n \"popularity\": 62090\n },\n {\n \"tag\": "Sudra",\n \"popularity\": 61887\n },\n {\n \"tag\": "hyperscrupulosity",\n \"popularity\": 61684\n },\n {\n \"tag\": "subterraneously unmaid",\n \"popularity\": 61483\n },\n {\n \"tag\": "poisonousness",\n \"popularity\": 61282\n },\n {\n \"tag\": "phare",\n \"popularity\": 61083\n },\n {\n \"tag\": "dicynodont",\n \"popularity\": 60884\n },\n {\n \"tag\": "chewer",\n \"popularity\": 60687\n },\n {\n \"tag\": "uliginous",\n \"popularity\": 60490\n },\n {\n \"tag\": "tinman",\n \"popularity\": 60295\n },\n {\n \"tag\": "coconut",\n \"popularity\": 60100\n },\n {\n \"tag\": "phryganeoid",\n \"popularity\": 59907\n },\n {\n \"tag\": "bismillah",\n \"popularity\": 59714\n },\n {\n \"tag\": "tautomeric",\n \"popularity\": 59523\n },\n {\n \"tag\": "jerquer",\n \"popularity\": 59332\n },\n {\n \"tag\": "Dryopithecinae",\n \"popularity\": 59143\n },\n {\n \"tag\": "ghizite",\n \"popularity\": 58954\n },\n {\n \"tag\": "unliveable",\n \"popularity\": 58766\n },\n {\n \"tag\": "craftsmaster",\n \"popularity\": 58579\n },\n {\n \"tag\": "semiscenic",\n \"popularity\": 58394\n },\n {\n \"tag\": "danaid",\n \"popularity\": 58209\n },\n {\n \"tag\": "flawful",\n \"popularity\": 58025\n },\n {\n \"tag\": "risibleness",\n \"popularity\": 57841\n },\n {\n \"tag\": "Muscovite",\n \"popularity\": 57659\n },\n {\n \"tag\": "snaringly",\n \"popularity\": 57478\n },\n {\n \"tag\": "brilliantwise",\n \"popularity\": 57297\n },\n {\n \"tag\": "plebeity",\n \"popularity\": 57118\n },\n {\n \"tag\": "historicalness",\n \"popularity\": 56939\n },\n {\n \"tag\": "piecemeal",\n \"popularity\": 56761\n },\n {\n \"tag\": "maxillipedary",\n \"popularity\": 56584\n },\n {\n \"tag\": "Hypenantron",\n \"popularity\": 56408\n },\n {\n \"tag\": "quaintness avigate",\n \"popularity\": 56233\n },\n {\n \"tag\": "ave",\n \"popularity\": 56059\n },\n {\n \"tag\": "mediaevally",\n \"popularity\": 55885\n },\n {\n \"tag\": "brucite",\n \"popularity\": 55712\n },\n {\n \"tag\": "Schwendenerian",\n \"popularity\": 55541\n },\n {\n \"tag\": "julole",\n \"popularity\": 55370\n },\n {\n \"tag\": "palaeolith",\n \"popularity\": 55199\n },\n {\n \"tag\": "cotyledonary",\n \"popularity\": 55030\n },\n {\n \"tag\": "rond",\n \"popularity\": 54861\n },\n {\n \"tag\": "boomster tassoo",\n \"popularity\": 54694\n },\n {\n \"tag\": "cattishly",\n \"popularity\": 54527\n },\n {\n \"tag\": "tonguefence",\n \"popularity\": 54360\n },\n {\n \"tag\": "hexastylar triskele",\n \"popularity\": 54195\n },\n {\n \"tag\": "ariot",\n \"popularity\": 54030\n },\n {\n \"tag\": "intarsist",\n \"popularity\": 53867\n },\n {\n \"tag\": "Oscines",\n \"popularity\": 53704\n },\n {\n \"tag\": "Spaniolize",\n \"popularity\": 53541\n },\n {\n \"tag\": "smellfungus",\n \"popularity\": 53380\n },\n {\n \"tag\": "redisplay",\n \"popularity\": 53219\n },\n {\n \"tag\": "phosphene",\n \"popularity\": 53059\n },\n {\n \"tag\": "phycomycete",\n \"popularity\": 52900\n },\n {\n \"tag\": "prophetic",\n \"popularity\": 52741\n },\n {\n \"tag\": "overtrustful",\n \"popularity\": 52584\n },\n {\n \"tag\": "pinitol",\n \"popularity\": 52427\n },\n {\n \"tag\": "asthmatic",\n \"popularity\": 52270\n },\n {\n \"tag\": "convulsive",\n \"popularity\": 52115\n },\n {\n \"tag\": "draughtswoman",\n \"popularity\": 51960\n },\n {\n \"tag\": "unetymologizable",\n \"popularity\": 51806\n },\n {\n \"tag\": "centrarchoid",\n \"popularity\": 51652\n },\n {\n \"tag\": "mesioincisal",\n \"popularity\": 51500\n },\n {\n \"tag\": "transbaikal",\n \"popularity\": 51348\n },\n {\n \"tag\": "silveriness",\n \"popularity\": 51196\n },\n {\n \"tag\": "costotomy",\n \"popularity\": 51046\n },\n {\n \"tag\": "caracore",\n \"popularity\": 50896\n },\n {\n \"tag\": "depotentiation",\n \"popularity\": 50747\n },\n {\n \"tag\": "glossoepiglottidean",\n \"popularity\": 50598\n },\n {\n \"tag\": "upswell",\n \"popularity\": 50450\n },\n {\n \"tag\": "flecnodal",\n \"popularity\": 50303\n },\n {\n \"tag\": "coventrate",\n \"popularity\": 50157\n },\n {\n \"tag\": "duchesse",\n \"popularity\": 50011\n },\n {\n \"tag\": "excisemanship trophied",\n \"popularity\": 49866\n },\n {\n \"tag\": "cytinaceous",\n \"popularity\": 49721\n },\n {\n \"tag\": "assuringly",\n \"popularity\": 49577\n },\n {\n \"tag\": "unconducted upliftitis",\n \"popularity\": 49434\n },\n {\n \"tag\": "rachicentesis",\n \"popularity\": 49292\n },\n {\n \"tag\": "antiangular",\n \"popularity\": 49150\n },\n {\n \"tag\": "advisal",\n \"popularity\": 49008\n },\n {\n \"tag\": "birdcatcher",\n \"popularity\": 48868\n },\n {\n \"tag\": "secularistic",\n \"popularity\": 48728\n },\n {\n \"tag\": "grandeeism superinformal",\n \"popularity\": 48588\n },\n {\n \"tag\": "unapprehension",\n \"popularity\": 48449\n },\n {\n \"tag\": "excipulum",\n \"popularity\": 48311\n },\n {\n \"tag\": "decimole",\n \"popularity\": 48174\n },\n {\n \"tag\": "semidrachm",\n \"popularity\": 48037\n },\n {\n \"tag\": "uvulotome",\n \"popularity\": 47901\n },\n {\n \"tag\": "Lemaneaceae",\n \"popularity\": 47765\n },\n {\n \"tag\": "corrade",\n \"popularity\": 47630\n },\n {\n \"tag\": "Kuroshio",\n \"popularity\": 47495\n },\n {\n \"tag\": "Araliophyllum",\n \"popularity\": 47361\n },\n {\n \"tag\": "victoriousness cardiosphygmograph",\n \"popularity\": 47228\n },\n {\n \"tag\": "reinvent",\n \"popularity\": 47095\n },\n {\n \"tag\": "Macrotolagus",\n \"popularity\": 46963\n },\n {\n \"tag\": "strenuousness",\n \"popularity\": 46831\n },\n {\n \"tag\": "deviability",\n \"popularity\": 46700\n },\n {\n \"tag\": "phyllospondylous",\n \"popularity\": 46570\n },\n {\n \"tag\": "bisect rudderhole",\n \"popularity\": 46440\n },\n {\n \"tag\": "crownwork",\n \"popularity\": 46311\n },\n {\n \"tag\": "Ascalabota",\n \"popularity\": 46182\n },\n {\n \"tag\": "prostatomyomectomy",\n \"popularity\": 46054\n },\n {\n \"tag\": "neurosyphilis",\n \"popularity\": 45926\n },\n {\n \"tag\": "tabloid scraplet",\n \"popularity\": 45799\n },\n {\n \"tag\": "nonmedullated servility",\n \"popularity\": 45673\n },\n {\n \"tag\": "melopoeic practicalization",\n \"popularity\": 45547\n },\n {\n \"tag\": "nonrhythmic",\n \"popularity\": 45421\n },\n {\n \"tag\": "deplorer",\n \"popularity\": 45296\n },\n {\n \"tag\": "Ophion",\n \"popularity\": 45172\n },\n {\n \"tag\": "subprioress",\n \"popularity\": 45048\n },\n {\n \"tag\": "semiregular",\n \"popularity\": 44925\n },\n {\n \"tag\": "praelection",\n \"popularity\": 44802\n },\n {\n \"tag\": "discinct",\n \"popularity\": 44680\n },\n {\n \"tag\": "preplace",\n \"popularity\": 44558\n },\n {\n \"tag\": "paternoster",\n \"popularity\": 44437\n },\n {\n \"tag\": "suboccipital",\n \"popularity\": 44316\n },\n {\n \"tag\": "Teutophil",\n \"popularity\": 44196\n },\n {\n \"tag\": "tracheole",\n \"popularity\": 44076\n },\n {\n \"tag\": "subsmile",\n \"popularity\": 43957\n },\n {\n \"tag\": "nonapostatizing",\n \"popularity\": 43839\n },\n {\n \"tag\": "cleidotomy",\n \"popularity\": 43720\n },\n {\n \"tag\": "hingle",\n \"popularity\": 43603\n },\n {\n \"tag\": "jocoque",\n \"popularity\": 43486\n },\n {\n \"tag\": "trundler notidanian",\n \"popularity\": 43369\n },\n {\n \"tag\": "strangling misdaub",\n \"popularity\": 43253\n },\n {\n \"tag\": "noncancellable",\n \"popularity\": 43137\n },\n {\n \"tag\": "lavabo",\n \"popularity\": 43022\n },\n {\n \"tag\": "lanterloo",\n \"popularity\": 42907\n },\n {\n \"tag\": "uncitizenly",\n \"popularity\": 42793\n },\n {\n \"tag\": "autoturning",\n \"popularity\": 42679\n },\n {\n \"tag\": "Haganah",\n \"popularity\": 42566\n },\n {\n \"tag\": "Glecoma",\n \"popularity\": 42453\n },\n {\n \"tag\": "membered",\n \"popularity\": 42341\n },\n {\n \"tag\": "consuetudinal",\n \"popularity\": 42229\n },\n {\n \"tag\": "gatehouse",\n \"popularity\": 42117\n },\n {\n \"tag\": "tetherball",\n \"popularity\": 42006\n },\n {\n \"tag\": "counterrevolutionist numismatical",\n \"popularity\": 41896\n },\n {\n \"tag\": "pagehood plateiasmus",\n \"popularity\": 41786\n },\n {\n \"tag\": "pelterer",\n \"popularity\": 41676\n },\n {\n \"tag\": "splenemphraxis",\n \"popularity\": 41567\n },\n {\n \"tag\": "Crypturidae",\n \"popularity\": 41458\n },\n {\n \"tag\": "caboodle",\n \"popularity\": 41350\n },\n {\n \"tag\": "Filaria",\n \"popularity\": 41242\n },\n {\n \"tag\": "noninvincibility",\n \"popularity\": 41135\n },\n {\n \"tag\": "preadvertisement",\n \"popularity\": 41028\n },\n {\n \"tag\": "bathrobe",\n \"popularity\": 40921\n },\n {\n \"tag\": "nitrifier",\n \"popularity\": 40815\n },\n {\n \"tag\": "furthermore",\n \"popularity\": 40709\n },\n {\n \"tag\": "recrate",\n \"popularity\": 40604\n },\n {\n \"tag\": "inexist",\n \"popularity\": 40499\n },\n {\n \"tag\": "Mocoan",\n \"popularity\": 40395\n },\n {\n \"tag\": "forint",\n \"popularity\": 40291\n },\n {\n \"tag\": "cardiomyoliposis",\n \"popularity\": 40187\n },\n {\n \"tag\": "channeling",\n \"popularity\": 40084\n },\n {\n \"tag\": "quebrachine",\n \"popularity\": 39981\n },\n {\n \"tag\": "magistery",\n \"popularity\": 39879\n },\n {\n \"tag\": "koko",\n \"popularity\": 39777\n },\n {\n \"tag\": "nobilify",\n \"popularity\": 39676\n },\n {\n \"tag\": "articulate taprooted",\n \"popularity\": 39575\n },\n {\n \"tag\": "cardiotonic Nicaragua",\n \"popularity\": 39474\n },\n {\n \"tag\": "assertiveness",\n \"popularity\": 39374\n },\n {\n \"tag\": "springtail",\n \"popularity\": 39274\n },\n {\n \"tag\": "spontoon",\n \"popularity\": 39174\n },\n {\n \"tag\": "plesiobiosis",\n \"popularity\": 39075\n },\n {\n \"tag\": "rooinek",\n \"popularity\": 38976\n },\n {\n \"tag\": "hairif falsehood",\n \"popularity\": 38878\n },\n {\n \"tag\": "synodally",\n \"popularity\": 38780\n },\n {\n \"tag\": "biodynamics",\n \"popularity\": 38683\n },\n {\n \"tag\": "trickling",\n \"popularity\": 38585\n },\n {\n \"tag\": "oxfly daystar",\n \"popularity\": 38489\n },\n {\n \"tag\": "epicycloidal",\n \"popularity\": 38392\n },\n {\n \"tag\": "shorthand",\n \"popularity\": 38296\n },\n {\n \"tag\": "herpolhode",\n \"popularity\": 38201\n },\n {\n \"tag\": "polysynthesism",\n \"popularity\": 38105\n },\n {\n \"tag\": "cany",\n \"popularity\": 38010\n },\n {\n \"tag\": "sideage",\n \"popularity\": 37916\n },\n {\n \"tag\": "strainableness",\n \"popularity\": 37822\n },\n {\n \"tag\": "superformidable",\n \"popularity\": 37728\n },\n {\n \"tag\": "slendang",\n \"popularity\": 37634\n },\n {\n \"tag\": "impropriation",\n \"popularity\": 37541\n },\n {\n \"tag\": "ficklehearted",\n \"popularity\": 37449\n },\n {\n \"tag\": "wintrify",\n \"popularity\": 37356\n },\n {\n \"tag\": "geomorphogenist",\n \"popularity\": 37264\n },\n {\n \"tag\": "smuggleable",\n \"popularity\": 37173\n },\n {\n \"tag\": "delapsion",\n \"popularity\": 37081\n },\n {\n \"tag\": "projective",\n \"popularity\": 36990\n },\n {\n \"tag\": "unglue exfoliation",\n \"popularity\": 36900\n },\n {\n \"tag\": "Acerae",\n \"popularity\": 36810\n },\n {\n \"tag\": "unstaged",\n \"popularity\": 36720\n },\n {\n \"tag\": "ranal",\n \"popularity\": 36630\n },\n {\n \"tag\": "worrier",\n \"popularity\": 36541\n },\n {\n \"tag\": "unhid",\n \"popularity\": 36452\n },\n {\n \"tag\": "adequation",\n \"popularity\": 36363\n },\n {\n \"tag\": "strongylid Sokotri",\n \"popularity\": 36275\n },\n {\n \"tag\": "fumingly",\n \"popularity\": 36187\n },\n {\n \"tag\": "gynosporangium phaenogenetic",\n \"popularity\": 36100\n },\n {\n \"tag\": "uniunguiculate",\n \"popularity\": 36012\n },\n {\n \"tag\": "prudelike",\n \"popularity\": 35926\n },\n {\n \"tag\": "seminomata",\n \"popularity\": 35839\n },\n {\n \"tag\": "trinklet",\n \"popularity\": 35753\n },\n {\n \"tag\": "risorial",\n \"popularity\": 35667\n },\n {\n \"tag\": "pericardiocentesis",\n \"popularity\": 35581\n },\n {\n \"tag\": "filmist",\n \"popularity\": 35496\n },\n {\n \"tag\": "Nana",\n \"popularity\": 35411\n },\n {\n \"tag\": "cynipoid",\n \"popularity\": 35326\n },\n {\n \"tag\": "cteniform",\n \"popularity\": 35242\n },\n {\n \"tag\": "semiflex",\n \"popularity\": 35158\n },\n {\n \"tag\": "solstitially",\n \"popularity\": 35074\n },\n {\n \"tag\": "Algarsife",\n \"popularity\": 34991\n },\n {\n \"tag\": "noncriminal",\n \"popularity\": 34908\n },\n {\n \"tag\": "compassion",\n \"popularity\": 34825\n },\n {\n \"tag\": "Buddhic",\n \"popularity\": 34743\n },\n {\n \"tag\": "vellicative dactylically hotfoot",\n \"popularity\": 34661\n },\n {\n \"tag\": "chicory",\n \"popularity\": 34579\n },\n {\n \"tag\": "transperitoneally",\n \"popularity\": 34497\n },\n {\n \"tag\": "pennae",\n \"popularity\": 34416\n },\n {\n \"tag\": "Flamandize",\n \"popularity\": 34335\n },\n {\n \"tag\": "underviewer",\n \"popularity\": 34254\n },\n {\n \"tag\": "assoil",\n \"popularity\": 34174\n },\n {\n \"tag\": "saccharobacillus",\n \"popularity\": 34094\n },\n {\n \"tag\": "biacetylene",\n \"popularity\": 34014\n },\n {\n \"tag\": "mouchardism",\n \"popularity\": 33935\n },\n {\n \"tag\": "anisomeric",\n \"popularity\": 33856\n },\n {\n \"tag\": "digestive",\n \"popularity\": 33777\n },\n {\n \"tag\": "darlingly",\n \"popularity\": 33698\n },\n {\n \"tag\": "liman",\n \"popularity\": 33620\n },\n {\n \"tag\": "soldanrie",\n \"popularity\": 33542\n },\n {\n \"tag\": "sully",\n \"popularity\": 33464\n },\n {\n \"tag\": "brightsmith",\n \"popularity\": 33387\n },\n {\n \"tag\": "inwrap antiliturgist ureterocervical",\n \"popularity\": 33309\n },\n {\n \"tag\": "discommodity",\n \"popularity\": 33232\n },\n {\n \"tag\": "typical aggrandizer",\n \"popularity\": 33156\n },\n {\n \"tag\": "xenogeny",\n \"popularity\": 33079\n },\n {\n \"tag\": "uncountrified",\n \"popularity\": 33003\n },\n {\n \"tag\": "Podarge",\n \"popularity\": 32928\n },\n {\n \"tag\": "uninterviewed",\n \"popularity\": 32852\n },\n {\n \"tag\": "underprior",\n \"popularity\": 32777\n },\n {\n \"tag\": "leiomyomatous",\n \"popularity\": 32702\n },\n {\n \"tag\": "postdysenteric",\n \"popularity\": 32627\n },\n {\n \"tag\": "Fusicladium",\n \"popularity\": 32553\n },\n {\n \"tag\": "Dulcinea",\n \"popularity\": 32478\n },\n {\n \"tag\": "interspersion",\n \"popularity\": 32404\n },\n {\n \"tag\": "preobligate",\n \"popularity\": 32331\n },\n {\n \"tag\": "subaggregate",\n \"popularity\": 32257\n },\n {\n \"tag\": "grammarianism",\n \"popularity\": 32184\n },\n {\n \"tag\": "palikar",\n \"popularity\": 32111\n },\n {\n \"tag\": "facileness",\n \"popularity\": 32039\n },\n {\n \"tag\": "deuterofibrinose",\n \"popularity\": 31966\n },\n {\n \"tag\": "pseudesthesia",\n \"popularity\": 31894\n },\n {\n \"tag\": "sedimentary",\n \"popularity\": 31822\n },\n {\n \"tag\": "typewrite",\n \"popularity\": 31751\n },\n {\n \"tag\": "immemorable",\n \"popularity\": 31679\n },\n {\n \"tag\": "Myrtus",\n \"popularity\": 31608\n },\n {\n \"tag\": "hauchecornite",\n \"popularity\": 31537\n },\n {\n \"tag\": "galleylike",\n \"popularity\": 31467\n },\n {\n \"tag\": "thimber",\n \"popularity\": 31396\n },\n {\n \"tag\": "Hegelianism",\n \"popularity\": 31326\n },\n {\n \"tag\": "strig",\n \"popularity\": 31256\n },\n {\n \"tag\": "skyre",\n \"popularity\": 31187\n },\n {\n \"tag\": "eupepticism",\n \"popularity\": 31117\n },\n {\n \"tag\": "eponymism",\n \"popularity\": 31048\n },\n {\n \"tag\": "flunkeyhood",\n \"popularity\": 30979\n },\n {\n \"tag\": "Abama",\n \"popularity\": 30911\n },\n {\n \"tag\": "adiadochokinesis",\n \"popularity\": 30842\n },\n {\n \"tag\": "spendthrifty",\n \"popularity\": 30774\n },\n {\n \"tag\": "chalcedony",\n \"popularity\": 30706\n },\n {\n \"tag\": "authorism",\n \"popularity\": 30638\n },\n {\n \"tag\": "nasturtium",\n \"popularity\": 30571\n },\n {\n \"tag\": "Acanthocereus",\n \"popularity\": 30504\n },\n {\n \"tag\": "uncollapsible",\n \"popularity\": 30437\n },\n {\n \"tag\": "excursionist",\n \"popularity\": 30370\n },\n {\n \"tag\": "fogbow",\n \"popularity\": 30303\n },\n {\n \"tag\": "overlie",\n \"popularity\": 30237\n },\n {\n \"tag\": "velours",\n \"popularity\": 30171\n },\n {\n \"tag\": "zoodendria madrigal stagbush",\n \"popularity\": 30105\n },\n {\n \"tag\": "imi",\n \"popularity\": 30039\n },\n {\n \"tag\": "cojudge",\n \"popularity\": 29974\n },\n {\n \"tag\": "depurate argal",\n \"popularity\": 29909\n },\n {\n \"tag\": "unrecognition",\n \"popularity\": 29844\n },\n {\n \"tag\": "paunchful",\n \"popularity\": 29779\n },\n {\n \"tag\": "invalued",\n \"popularity\": 29714\n },\n {\n \"tag\": "probang",\n \"popularity\": 29650\n },\n {\n \"tag\": "chetvert",\n \"popularity\": 29586\n },\n {\n \"tag\": "enactable",\n \"popularity\": 29522\n },\n {\n \"tag\": "detoxicate adhibit",\n \"popularity\": 29458\n },\n {\n \"tag\": "kullaite",\n \"popularity\": 29395\n },\n {\n \"tag\": "undazzling",\n \"popularity\": 29332\n },\n {\n \"tag\": "excalation",\n \"popularity\": 29269\n },\n {\n \"tag\": "sievings",\n \"popularity\": 29206\n },\n {\n \"tag\": "disenthral",\n \"popularity\": 29143\n },\n {\n \"tag\": "disinterestedly",\n \"popularity\": 29081\n },\n {\n \"tag\": "stanner",\n \"popularity\": 29018\n },\n {\n \"tag\": "recapitulative",\n \"popularity\": 28956\n },\n {\n \"tag\": "objectivist",\n \"popularity\": 28895\n },\n {\n \"tag\": "hypermetropia",\n \"popularity\": 28833\n },\n {\n \"tag\": "incumbency",\n \"popularity\": 28772\n },\n {\n \"tag\": "protegee",\n \"popularity\": 28711\n },\n {\n \"tag\": "zealotic",\n \"popularity\": 28650\n },\n {\n \"tag\": "predebit",\n \"popularity\": 28589\n },\n {\n \"tag\": "cupolar",\n \"popularity\": 28528\n },\n {\n \"tag\": "unattributed",\n \"popularity\": 28468\n },\n {\n \"tag\": "louisine",\n \"popularity\": 28408\n },\n {\n \"tag\": "illustrate",\n \"popularity\": 28348\n },\n {\n \"tag\": "inofficiousness",\n \"popularity\": 28288\n },\n {\n \"tag\": "Americawards",\n \"popularity\": 28228\n },\n {\n \"tag\": "foreflap",\n \"popularity\": 28169\n },\n {\n \"tag\": "eruditeness",\n \"popularity\": 28110\n },\n {\n \"tag\": "copiopsia",\n \"popularity\": 28051\n },\n {\n \"tag\": "sporuliferous",\n \"popularity\": 27992\n },\n {\n \"tag\": "muttering",\n \"popularity\": 27934\n },\n {\n \"tag\": "prepsychology adrip",\n \"popularity\": 27875\n },\n {\n \"tag\": "unfriendly",\n \"popularity\": 27817\n },\n {\n \"tag\": "sulphanilic",\n \"popularity\": 27759\n },\n {\n \"tag\": "Coelococcus",\n \"popularity\": 27701\n },\n {\n \"tag\": "undoubtfulness",\n \"popularity\": 27643\n },\n {\n \"tag\": "flaringly",\n \"popularity\": 27586\n },\n {\n \"tag\": "unordain",\n \"popularity\": 27529\n },\n {\n \"tag\": "fratchety",\n \"popularity\": 27472\n },\n {\n \"tag\": "decadentism dolefully",\n \"popularity\": 27415\n },\n {\n \"tag\": "synthronus",\n \"popularity\": 27358\n },\n {\n \"tag\": "maiid",\n \"popularity\": 27301\n },\n {\n \"tag\": "rhinobyon",\n \"popularity\": 27245\n },\n {\n \"tag\": "Didynamia",\n \"popularity\": 27189\n },\n {\n \"tag\": "millionairedom",\n \"popularity\": 27133\n },\n {\n \"tag\": "mulierine",\n \"popularity\": 27077\n },\n {\n \"tag\": "Mayo",\n \"popularity\": 27021\n },\n {\n \"tag\": "perceivedness",\n \"popularity\": 26966\n },\n {\n \"tag\": "unadoration",\n \"popularity\": 26911\n },\n {\n \"tag\": "regraft",\n \"popularity\": 26856\n },\n {\n \"tag\": "witch",\n \"popularity\": 26801\n },\n {\n \"tag\": "ungrow",\n \"popularity\": 26746\n },\n {\n \"tag\": "glossopharyngeus",\n \"popularity\": 26691\n },\n {\n \"tag\": "unstirrable",\n \"popularity\": 26637\n },\n {\n \"tag\": "synodsman",\n \"popularity\": 26583\n },\n {\n \"tag\": "placentalian",\n \"popularity\": 26529\n },\n {\n \"tag\": "corpulently",\n \"popularity\": 26475\n },\n {\n \"tag\": "photochromoscope",\n \"popularity\": 26421\n },\n {\n \"tag\": "indusiate retinasphaltum chokestrap",\n \"popularity\": 26368\n },\n {\n \"tag\": "murdrum",\n \"popularity\": 26314\n },\n {\n \"tag\": "belatedness",\n \"popularity\": 26261\n },\n {\n \"tag\": "Cochin",\n \"popularity\": 26208\n },\n {\n \"tag\": "Leonist",\n \"popularity\": 26155\n },\n {\n \"tag\": "keeker confined",\n \"popularity\": 26102\n },\n {\n \"tag\": "unintellectual",\n \"popularity\": 26050\n },\n {\n \"tag\": "nymphaline bait",\n \"popularity\": 25997\n },\n {\n \"tag\": "sarcosporidiosis",\n \"popularity\": 25945\n },\n {\n \"tag\": "catawamptiously",\n \"popularity\": 25893\n },\n {\n \"tag\": "outshame",\n \"popularity\": 25841\n },\n {\n \"tag\": "animalism",\n \"popularity\": 25790\n },\n {\n \"tag\": "epithalamial",\n \"popularity\": 25738\n },\n {\n \"tag\": "ganner",\n \"popularity\": 25687\n },\n {\n \"tag\": "desilicify",\n \"popularity\": 25635\n },\n {\n \"tag\": "dandyism",\n \"popularity\": 25584\n },\n {\n \"tag\": "hyleg",\n \"popularity\": 25533\n },\n {\n \"tag\": "photophysical",\n \"popularity\": 25483\n },\n {\n \"tag\": "underload",\n \"popularity\": 25432\n },\n {\n \"tag\": "unintrusive",\n \"popularity\": 25382\n },\n {\n \"tag\": "succinamic",\n \"popularity\": 25331\n },\n {\n \"tag\": "matchy",\n \"popularity\": 25281\n },\n {\n \"tag\": "concordal",\n \"popularity\": 25231\n },\n {\n \"tag\": "exteriority",\n \"popularity\": 25181\n },\n {\n \"tag\": "sterculiad",\n \"popularity\": 25132\n },\n {\n \"tag\": "sulfoxylic",\n \"popularity\": 25082\n },\n {\n \"tag\": "oversubscription",\n \"popularity\": 25033\n },\n {\n \"tag\": "chiasmic",\n \"popularity\": 24984\n },\n {\n \"tag\": "pseudoparthenogenesis",\n \"popularity\": 24935\n },\n {\n \"tag\": "indorse",\n \"popularity\": 24886\n },\n {\n \"tag\": "Krishnaite",\n \"popularity\": 24837\n },\n {\n \"tag\": "calcinize",\n \"popularity\": 24788\n },\n {\n \"tag\": "rhodium",\n \"popularity\": 24740\n },\n {\n \"tag\": "tragopan",\n \"popularity\": 24692\n },\n {\n \"tag\": "overwhelmingly",\n \"popularity\": 24643\n },\n {\n \"tag\": "procidence accorporate",\n \"popularity\": 24595\n },\n {\n \"tag\": "polemize speelless",\n \"popularity\": 24548\n },\n {\n \"tag\": "radiocarpal goran",\n \"popularity\": 24500\n },\n {\n \"tag\": "counteroffer Pelodytes",\n \"popularity\": 24452\n },\n {\n \"tag\": "lionhearted",\n \"popularity\": 24405\n },\n {\n \"tag\": "paramastoid",\n \"popularity\": 24358\n },\n {\n \"tag\": "murine",\n \"popularity\": 24310\n },\n {\n \"tag\": "woodbined",\n \"popularity\": 24263\n },\n {\n \"tag\": "packthread",\n \"popularity\": 24217\n },\n {\n \"tag\": "citreous",\n \"popularity\": 24170\n },\n {\n \"tag\": "unfallaciously",\n \"popularity\": 24123\n },\n {\n \"tag\": "tentwork reincarnadine",\n \"popularity\": 24077\n },\n {\n \"tag\": "verminousness",\n \"popularity\": 24030\n },\n {\n \"tag\": "sillometer",\n \"popularity\": 23984\n },\n {\n \"tag\": "jointy",\n \"popularity\": 23938\n },\n {\n \"tag\": "streptolysin",\n \"popularity\": 23892\n },\n {\n \"tag\": "Florentinism",\n \"popularity\": 23847\n },\n {\n \"tag\": "monosomatous",\n \"popularity\": 23801\n },\n {\n \"tag\": "capsulociliary",\n \"popularity\": 23756\n },\n {\n \"tag\": "organum",\n \"popularity\": 23710\n },\n {\n \"tag\": "overtly",\n \"popularity\": 23665\n },\n {\n \"tag\": "ophthalmoscopical",\n \"popularity\": 23620\n },\n {\n \"tag\": "supposititiously",\n \"popularity\": 23575\n },\n {\n \"tag\": "radiochemistry",\n \"popularity\": 23530\n },\n {\n \"tag\": "flaxtail",\n \"popularity\": 23486\n },\n {\n \"tag\": "pretympanic",\n \"popularity\": 23441\n },\n {\n \"tag\": "auscultation",\n \"popularity\": 23397\n },\n {\n \"tag\": "hairdresser",\n \"popularity\": 23352\n },\n {\n \"tag\": "chaffless",\n \"popularity\": 23308\n },\n {\n \"tag\": "polioencephalitis",\n \"popularity\": 23264\n },\n {\n \"tag\": "axolotl",\n \"popularity\": 23220\n },\n {\n \"tag\": "smous",\n \"popularity\": 23177\n },\n {\n \"tag\": "morgen disenamour toothed",\n \"popularity\": 23133\n },\n {\n \"tag\": "chaiseless",\n \"popularity\": 23089\n },\n {\n \"tag\": "frugally",\n \"popularity\": 23046\n },\n {\n \"tag\": "combustive antievolutionist cinenegative",\n \"popularity\": 23003\n },\n {\n \"tag\": "malacolite",\n \"popularity\": 22960\n },\n {\n \"tag\": "borne",\n \"popularity\": 22917\n },\n {\n \"tag\": "mercaptole",\n \"popularity\": 22874\n },\n {\n \"tag\": "judicatory",\n \"popularity\": 22831\n },\n {\n \"tag\": "noctivagation",\n \"popularity\": 22789\n },\n {\n \"tag\": "synthete",\n \"popularity\": 22746\n },\n {\n \"tag\": "tomboyism",\n \"popularity\": 22704\n },\n {\n \"tag\": "serranoid",\n \"popularity\": 22661\n },\n {\n \"tag\": "impostorism",\n \"popularity\": 22619\n },\n {\n \"tag\": "flagellosis Talitha",\n \"popularity\": 22577\n },\n {\n \"tag\": "pseudoviscous",\n \"popularity\": 22535\n },\n {\n \"tag\": "Galleriidae",\n \"popularity\": 22494\n },\n {\n \"tag\": "undulation didelph Comintern",\n \"popularity\": 22452\n },\n {\n \"tag\": "triangulopyramidal",\n \"popularity\": 22411\n },\n {\n \"tag\": "middlings",\n \"popularity\": 22369\n },\n {\n \"tag\": "piperazin",\n \"popularity\": 22328\n },\n {\n \"tag\": "endostitis",\n \"popularity\": 22287\n },\n {\n \"tag\": "swordlike",\n \"popularity\": 22246\n },\n {\n \"tag\": "forthwith",\n \"popularity\": 22205\n },\n {\n \"tag\": "menaceful",\n \"popularity\": 22164\n },\n {\n \"tag\": "explantation defective",\n \"popularity\": 22123\n },\n {\n \"tag\": "arrear",\n \"popularity\": 22083\n },\n {\n \"tag\": "engraft",\n \"popularity\": 22042\n },\n {\n \"tag\": "revolunteer",\n \"popularity\": 22002\n },\n {\n \"tag\": "foliaceous",\n \"popularity\": 21962\n },\n {\n \"tag\": "pseudograph",\n \"popularity\": 21922\n },\n {\n \"tag\": "maenaite",\n \"popularity\": 21882\n },\n {\n \"tag\": "interfinger",\n \"popularity\": 21842\n },\n {\n \"tag\": "macroscopically",\n \"popularity\": 21802\n },\n {\n \"tag\": "bluewood",\n \"popularity\": 21762\n },\n {\n \"tag\": "chikara",\n \"popularity\": 21723\n },\n {\n \"tag\": "reprehension diazeuxis nickelous",\n \"popularity\": 21683\n },\n {\n \"tag\": "vacuation",\n \"popularity\": 21644\n },\n {\n \"tag\": "Sartish",\n \"popularity\": 21605\n },\n {\n \"tag\": "pseudogyny",\n \"popularity\": 21566\n },\n {\n \"tag\": "friedcake",\n \"popularity\": 21527\n },\n {\n \"tag\": "thraw",\n \"popularity\": 21488\n },\n {\n \"tag\": "bifid",\n \"popularity\": 21449\n },\n {\n \"tag\": "truthlessly",\n \"popularity\": 21411\n },\n {\n \"tag\": "lungy",\n \"popularity\": 21372\n },\n {\n \"tag\": "fluoborite",\n \"popularity\": 21334\n },\n {\n \"tag\": "anthropolithic",\n \"popularity\": 21295\n },\n {\n \"tag\": "coachee straw",\n \"popularity\": 21257\n },\n {\n \"tag\": "dehorner Grecize",\n \"popularity\": 21219\n },\n {\n \"tag\": "spondylopyosis",\n \"popularity\": 21181\n },\n {\n \"tag\": "institutionary",\n \"popularity\": 21143\n },\n {\n \"tag\": "agentry",\n \"popularity\": 21105\n },\n {\n \"tag\": "musing bietle",\n \"popularity\": 21068\n },\n {\n \"tag\": "cormophyte",\n \"popularity\": 21030\n },\n {\n \"tag\": "semielliptic",\n \"popularity\": 20993\n },\n {\n \"tag\": "ependytes",\n \"popularity\": 20955\n },\n {\n \"tag\": "coachmaster",\n \"popularity\": 20918\n },\n {\n \"tag\": "overexuberant",\n \"popularity\": 20881\n },\n {\n \"tag\": "selectable",\n \"popularity\": 20844\n },\n {\n \"tag\": "saclike",\n \"popularity\": 20807\n },\n {\n \"tag\": "mullion",\n \"popularity\": 20770\n },\n {\n \"tag\": "pantheonize prevalency",\n \"popularity\": 20733\n },\n {\n \"tag\": "trophosperm",\n \"popularity\": 20697\n },\n {\n \"tag\": "paraphrasist",\n \"popularity\": 20660\n },\n {\n \"tag\": "undercarry",\n \"popularity\": 20624\n },\n {\n \"tag\": "thallogenic",\n \"popularity\": 20587\n },\n {\n \"tag\": "bulgy forbid",\n \"popularity\": 20551\n },\n {\n \"tag\": "proliquor gratulatory",\n \"popularity\": 20515\n },\n {\n \"tag\": "booker",\n \"popularity\": 20479\n },\n {\n \"tag\": "wizen",\n \"popularity\": 20443\n },\n {\n \"tag\": "synchondrosially",\n \"popularity\": 20407\n },\n {\n \"tag\": "herbless",\n \"popularity\": 20371\n },\n {\n \"tag\": "arfvedsonite",\n \"popularity\": 20336\n },\n {\n \"tag\": "Neuroptera",\n \"popularity\": 20300\n },\n {\n \"tag\": "fingerstone",\n \"popularity\": 20265\n },\n {\n \"tag\": "Odontoglossae",\n \"popularity\": 20229\n },\n {\n \"tag\": "transmigrator",\n \"popularity\": 20194\n },\n {\n \"tag\": "Dehaites",\n \"popularity\": 20159\n },\n {\n \"tag\": "Molinist",\n \"popularity\": 20124\n },\n {\n \"tag\": "novelistic",\n \"popularity\": 20089\n },\n {\n \"tag\": "astelic",\n \"popularity\": 20054\n },\n {\n \"tag\": "pyelometry",\n \"popularity\": 20019\n },\n {\n \"tag\": "pigmentation",\n \"popularity\": 19984\n },\n {\n \"tag\": "epinaos",\n \"popularity\": 19950\n },\n {\n \"tag\": "outdare",\n \"popularity\": 19915\n },\n {\n \"tag\": "Funje philaristocracy",\n \"popularity\": 19881\n },\n {\n \"tag\": "keddah",\n \"popularity\": 19846\n },\n {\n \"tag\": "axoidean",\n \"popularity\": 19812\n },\n {\n \"tag\": "ovule",\n \"popularity\": 19778\n },\n {\n \"tag\": "solidify",\n \"popularity\": 19744\n },\n {\n \"tag\": "noncelestial",\n \"popularity\": 19710\n },\n {\n \"tag\": "overmultiplication",\n \"popularity\": 19676\n },\n {\n \"tag\": "hexatetrahedron",\n \"popularity\": 19642\n },\n {\n \"tag\": "pliciform",\n \"popularity\": 19609\n },\n {\n \"tag\": "zimbalon",\n \"popularity\": 19575\n },\n {\n \"tag\": "annexational",\n \"popularity\": 19542\n },\n {\n \"tag\": "eurhodol",\n \"popularity\": 19508\n },\n {\n \"tag\": "yark",\n \"popularity\": 19475\n },\n {\n \"tag\": "illegality nitroalizarin",\n \"popularity\": 19442\n },\n {\n \"tag\": "quadratum",\n \"popularity\": 19409\n },\n {\n \"tag\": "saccharine",\n \"popularity\": 19376\n },\n {\n \"tag\": "unemploy",\n \"popularity\": 19343\n },\n {\n \"tag\": "uniclinal unipotent",\n \"popularity\": 19310\n },\n {\n \"tag\": "turbo",\n \"popularity\": 19277\n },\n {\n \"tag\": "sybarism",\n \"popularity\": 19244\n },\n {\n \"tag\": "motacilline",\n \"popularity\": 19212\n },\n {\n \"tag\": "weaselly",\n \"popularity\": 19179\n },\n {\n \"tag\": "plastid",\n \"popularity\": 19147\n },\n {\n \"tag\": "wasting",\n \"popularity\": 19114\n },\n {\n \"tag\": "begrime fluting",\n \"popularity\": 19082\n },\n {\n \"tag\": "Nephilinae",\n \"popularity\": 19050\n },\n {\n \"tag\": "disregardance",\n \"popularity\": 19018\n },\n {\n \"tag\": "Shakerlike",\n \"popularity\": 18986\n },\n {\n \"tag\": "uniped",\n \"popularity\": 18954\n },\n {\n \"tag\": "knap",\n \"popularity\": 18922\n },\n {\n \"tag\": "electivism undergardener",\n \"popularity\": 18890\n },\n {\n \"tag\": "hulverheaded",\n \"popularity\": 18858\n },\n {\n \"tag\": "unruptured",\n \"popularity\": 18827\n },\n {\n \"tag\": "solemnize credently",\n \"popularity\": 18795\n },\n {\n \"tag\": "pentastomoid possessingly",\n \"popularity\": 18764\n },\n {\n \"tag\": "octose",\n \"popularity\": 18733\n },\n {\n \"tag\": "psithurism indefensibility",\n \"popularity\": 18701\n },\n {\n \"tag\": "torrentuous cyanometer subcrenate",\n \"popularity\": 18670\n },\n {\n \"tag\": "photoplaywright tapaculo",\n \"popularity\": 18639\n },\n {\n \"tag\": "univalence",\n \"popularity\": 18608\n },\n {\n \"tag\": "Porthetria",\n \"popularity\": 18577\n },\n {\n \"tag\": "funambulo",\n \"popularity\": 18546\n },\n {\n \"tag\": "pedion",\n \"popularity\": 18515\n },\n {\n \"tag\": "horticulturally",\n \"popularity\": 18485\n },\n {\n \"tag\": "marennin",\n \"popularity\": 18454\n },\n {\n \"tag\": "horselaugh",\n \"popularity\": 18423\n },\n {\n \"tag\": "semiexecutive",\n \"popularity\": 18393\n },\n {\n \"tag\": "Monopteridae",\n \"popularity\": 18363\n },\n {\n \"tag\": "commonable",\n \"popularity\": 18332\n },\n {\n \"tag\": "dreariment",\n \"popularity\": 18302\n },\n {\n \"tag\": "disbud",\n \"popularity\": 18272\n },\n {\n \"tag\": "monocled",\n \"popularity\": 18242\n },\n {\n \"tag\": "hurlbarrow",\n \"popularity\": 18212\n },\n {\n \"tag\": "opiateproof",\n \"popularity\": 18182\n },\n {\n \"tag\": "Fahrenheit",\n \"popularity\": 18152\n },\n {\n \"tag\": "writhed",\n \"popularity\": 18122\n },\n {\n \"tag\": "Volstead",\n \"popularity\": 18093\n },\n {\n \"tag\": "yesternight",\n \"popularity\": 18063\n },\n {\n \"tag\": "readmittance",\n \"popularity\": 18033\n },\n {\n \"tag\": "reiterable",\n \"popularity\": 18004\n },\n {\n \"tag\": "triquetral",\n \"popularity\": 17975\n },\n {\n \"tag\": "guillotinement",\n \"popularity\": 17945\n },\n {\n \"tag\": "repermission",\n \"popularity\": 17916\n },\n {\n \"tag\": "assishly",\n \"popularity\": 17887\n },\n {\n \"tag\": "daidle",\n \"popularity\": 17858\n },\n {\n \"tag\": "prismatoid",\n \"popularity\": 17829\n },\n {\n \"tag\": "irreptitious",\n \"popularity\": 17800\n },\n {\n \"tag\": "sourdeline",\n \"popularity\": 17771\n },\n {\n \"tag\": "Austrian",\n \"popularity\": 17742\n },\n {\n \"tag\": "psychorrhagic",\n \"popularity\": 17713\n },\n {\n \"tag\": "Monumbo",\n \"popularity\": 17685\n },\n {\n \"tag\": "cloiochoanitic",\n \"popularity\": 17656\n },\n {\n \"tag\": "hant",\n \"popularity\": 17628\n },\n {\n \"tag\": "roily pulldown",\n \"popularity\": 17599\n },\n {\n \"tag\": "recongratulation",\n \"popularity\": 17571\n },\n {\n \"tag\": "Peking",\n \"popularity\": 17543\n },\n {\n \"tag\": "erdvark",\n \"popularity\": 17514\n },\n {\n \"tag\": "antimnemonic",\n \"popularity\": 17486\n },\n {\n \"tag\": "noncapillarity",\n \"popularity\": 17458\n },\n {\n \"tag\": "irrepressive",\n \"popularity\": 17430\n },\n {\n \"tag\": "Petromyzontes",\n \"popularity\": 17402\n },\n {\n \"tag\": "piscatorially",\n \"popularity\": 17374\n },\n {\n \"tag\": "cholesterosis",\n \"popularity\": 17346\n },\n {\n \"tag\": "denunciate",\n \"popularity\": 17319\n },\n {\n \"tag\": "unmetalled",\n \"popularity\": 17291\n },\n {\n \"tag\": "Tigris enruin",\n \"popularity\": 17263\n },\n {\n \"tag\": "anaspalin",\n \"popularity\": 17236\n },\n {\n \"tag\": "monodromy",\n \"popularity\": 17208\n },\n {\n \"tag\": "Canichanan",\n \"popularity\": 17181\n },\n {\n \"tag\": "mesolabe",\n \"popularity\": 17154\n },\n {\n \"tag\": "trichothallic overcunningness",\n \"popularity\": 17127\n },\n {\n \"tag\": "spinsterishly",\n \"popularity\": 17099\n },\n {\n \"tag\": "sensilla",\n \"popularity\": 17072\n },\n {\n \"tag\": "wifelkin",\n \"popularity\": 17045\n },\n {\n \"tag\": "suppositionless",\n \"popularity\": 17018\n },\n {\n \"tag\": "irksomeness",\n \"popularity\": 16991\n },\n {\n \"tag\": "sanbenito",\n \"popularity\": 16964\n },\n {\n \"tag\": "nonstatement",\n \"popularity\": 16938\n },\n {\n \"tag\": "phenoloid",\n \"popularity\": 16911\n },\n {\n \"tag\": "Steinberger",\n \"popularity\": 16884\n },\n {\n \"tag\": "replicated boom",\n \"popularity\": 16858\n },\n {\n \"tag\": "sciomachiology",\n \"popularity\": 16831\n },\n {\n \"tag\": "starwise",\n \"popularity\": 16805\n },\n {\n \"tag\": "prerich",\n \"popularity\": 16778\n },\n {\n \"tag\": "unspawned",\n \"popularity\": 16752\n },\n {\n \"tag\": "unindentable",\n \"popularity\": 16726\n },\n {\n \"tag\": "stromatic",\n \"popularity\": 16700\n },\n {\n \"tag\": "fetishize",\n \"popularity\": 16673\n },\n {\n \"tag\": "dihydroxy",\n \"popularity\": 16647\n },\n {\n \"tag\": "precaudal",\n \"popularity\": 16621\n },\n {\n \"tag\": "Madagascar",\n \"popularity\": 16595\n },\n {\n \"tag\": "repinement",\n \"popularity\": 16570\n },\n {\n \"tag\": "noncathedral wenzel",\n \"popularity\": 16544\n },\n {\n \"tag\": "corollike",\n \"popularity\": 16518\n },\n {\n \"tag\": "pubes unamortization",\n \"popularity\": 16492\n },\n {\n \"tag\": "brickcroft",\n \"popularity\": 16467\n },\n {\n \"tag\": "intertrabecular",\n \"popularity\": 16441\n },\n {\n \"tag\": "formulaic",\n \"popularity\": 16416\n },\n {\n \"tag\": "arienzo",\n \"popularity\": 16390\n },\n {\n \"tag\": "Mazzinian",\n \"popularity\": 16365\n },\n {\n \"tag\": "wallowishly",\n \"popularity\": 16339\n },\n {\n \"tag\": "sysselman",\n \"popularity\": 16314\n },\n {\n \"tag\": "seligmannite",\n \"popularity\": 16289\n },\n {\n \"tag\": "harlequinery",\n \"popularity\": 16264\n },\n {\n \"tag\": "zucchetto",\n \"popularity\": 16239\n },\n {\n \"tag\": "malonyl",\n \"popularity\": 16214\n },\n {\n \"tag\": "patwari",\n \"popularity\": 16189\n },\n {\n \"tag\": "neoholmia venturesomeness",\n \"popularity\": 16164\n },\n {\n \"tag\": "Dehwar",\n \"popularity\": 16139\n },\n {\n \"tag\": "fetiferous",\n \"popularity\": 16114\n },\n {\n \"tag\": "chromatophore",\n \"popularity\": 16090\n },\n {\n \"tag\": "reregistration",\n \"popularity\": 16065\n },\n {\n \"tag\": "alienor",\n \"popularity\": 16040\n },\n {\n \"tag\": "Hexagynia",\n \"popularity\": 16016\n },\n {\n \"tag\": "cerebrotonia",\n \"popularity\": 15991\n },\n {\n \"tag\": "deedbox",\n \"popularity\": 15967\n },\n {\n \"tag\": "staab",\n \"popularity\": 15943\n },\n {\n \"tag\": "uratemia",\n \"popularity\": 15918\n },\n {\n \"tag\": "flaunt",\n \"popularity\": 15894\n },\n {\n \"tag\": "bogy",\n \"popularity\": 15870\n },\n {\n \"tag\": "subcartilaginous",\n \"popularity\": 15846\n },\n {\n \"tag\": "protonephridial",\n \"popularity\": 15822\n },\n {\n \"tag\": "Boswellia",\n \"popularity\": 15798\n },\n {\n \"tag\": "relaxant untiaraed protoepiphyte",\n \"popularity\": 15774\n },\n {\n \"tag\": "nesslerization",\n \"popularity\": 15750\n },\n {\n \"tag\": "precession",\n \"popularity\": 15726\n },\n {\n \"tag\": "peat",\n \"popularity\": 15702\n },\n {\n \"tag\": "unbit",\n \"popularity\": 15678\n },\n {\n \"tag\": "snailish",\n \"popularity\": 15655\n },\n {\n \"tag\": "porismatical",\n \"popularity\": 15631\n },\n {\n \"tag\": "hooflike",\n \"popularity\": 15608\n },\n {\n \"tag\": "resuppose phene cranic",\n \"popularity\": 15584\n },\n {\n \"tag\": "peptonization kipskin",\n \"popularity\": 15561\n },\n {\n \"tag\": "birdstone",\n \"popularity\": 15537\n },\n {\n \"tag\": "empty inferoanterior",\n \"popularity\": 15514\n },\n {\n \"tag\": "androtauric",\n \"popularity\": 15491\n },\n {\n \"tag\": "triamide",\n \"popularity\": 15467\n },\n {\n \"tag\": "showmanry",\n \"popularity\": 15444\n },\n {\n \"tag\": "doing",\n \"popularity\": 15421\n },\n {\n \"tag\": "bouchaleen",\n \"popularity\": 15398\n },\n {\n \"tag\": "precollude",\n \"popularity\": 15375\n },\n {\n \"tag\": "finger",\n \"popularity\": 15352\n },\n {\n \"tag\": "limnetic intermessenger",\n \"popularity\": 15329\n },\n {\n \"tag\": "uncharitable picrotoxic",\n \"popularity\": 15306\n },\n {\n \"tag\": "nationalizer Phasmidae",\n \"popularity\": 15283\n },\n {\n \"tag\": "laughingstock",\n \"popularity\": 15261\n },\n {\n \"tag\": "nondeferential",\n \"popularity\": 15238\n },\n {\n \"tag\": "uproariously",\n \"popularity\": 15215\n },\n {\n \"tag\": "manzanilla",\n \"popularity\": 15193\n },\n {\n \"tag\": "khahoon",\n \"popularity\": 15170\n },\n {\n \"tag\": "olericulturally longshanks",\n \"popularity\": 15148\n },\n {\n \"tag\": "enthusiastically methionic",\n \"popularity\": 15125\n },\n {\n \"tag\": "pobs",\n \"popularity\": 15103\n },\n {\n \"tag\": "tricarpellate",\n \"popularity\": 15081\n },\n {\n \"tag\": "souterrain",\n \"popularity\": 15058\n },\n {\n \"tag\": "tethelin",\n \"popularity\": 15036\n },\n {\n \"tag\": "tartle",\n \"popularity\": 15014\n },\n {\n \"tag\": "tidelike",\n \"popularity\": 14992\n },\n {\n \"tag\": "cosmoramic",\n \"popularity\": 14970\n },\n {\n \"tag\": "pretardiness",\n \"popularity\": 14948\n },\n {\n \"tag\": "insoul",\n \"popularity\": 14926\n },\n {\n \"tag\": "anthroxan",\n \"popularity\": 14904\n },\n {\n \"tag\": "jilter",\n \"popularity\": 14882\n },\n {\n \"tag\": "pectinibranchian trematode",\n \"popularity\": 14860\n },\n {\n \"tag\": "Renaissancist",\n \"popularity\": 14838\n },\n {\n \"tag\": "imaginant",\n \"popularity\": 14817\n },\n {\n \"tag\": "supercensure",\n \"popularity\": 14795\n },\n {\n \"tag\": "festilogy",\n \"popularity\": 14773\n },\n {\n \"tag\": "regression",\n \"popularity\": 14752\n },\n {\n \"tag\": "mesobregmate languorously",\n \"popularity\": 14730\n },\n {\n \"tag\": "unsupernaturalized",\n \"popularity\": 14709\n },\n {\n \"tag\": "boobyish",\n \"popularity\": 14687\n },\n {\n \"tag\": "scopolamine",\n \"popularity\": 14666\n },\n {\n \"tag\": "reamputation unchristianly",\n \"popularity\": 14645\n },\n {\n \"tag\": "cuneatic",\n \"popularity\": 14623\n },\n {\n \"tag\": "heathberry",\n \"popularity\": 14602\n },\n {\n \"tag\": "hate",\n \"popularity\": 14581\n },\n {\n \"tag\": "redeemableness",\n \"popularity\": 14560\n },\n {\n \"tag\": "damasse",\n \"popularity\": 14539\n },\n {\n \"tag\": "thrillsome",\n \"popularity\": 14518\n },\n {\n \"tag\": "disseverment",\n \"popularity\": 14497\n },\n {\n \"tag\": "underbishopric Ostyak",\n \"popularity\": 14476\n },\n {\n \"tag\": "Exoascales",\n \"popularity\": 14455\n },\n {\n \"tag\": "soiled",\n \"popularity\": 14434\n },\n {\n \"tag\": "Cain",\n \"popularity\": 14413\n },\n {\n \"tag\": "mismanageable arenae",\n \"popularity\": 14392\n },\n {\n \"tag\": "manducate unhinderably",\n \"popularity\": 14372\n },\n {\n \"tag\": "peregrin",\n \"popularity\": 14351\n },\n {\n \"tag\": "musicianly",\n \"popularity\": 14330\n },\n {\n \"tag\": "aln",\n \"popularity\": 14310\n },\n {\n \"tag\": "intercentrum",\n \"popularity\": 14289\n },\n {\n \"tag\": "roothold",\n \"popularity\": 14269\n },\n {\n \"tag\": "jane aneurism",\n \"popularity\": 14248\n },\n {\n \"tag\": "insinuatively forefeel phytolatrous",\n \"popularity\": 14228\n },\n {\n \"tag\": "kanchil",\n \"popularity\": 14208\n },\n {\n \"tag\": "Austrophile",\n \"popularity\": 14187\n },\n {\n \"tag\": "unterrorized",\n \"popularity\": 14167\n },\n {\n \"tag\": "admeasure",\n \"popularity\": 14147\n },\n {\n \"tag\": "electrodissolution",\n \"popularity\": 14127\n },\n {\n \"tag\": "unweddedly",\n \"popularity\": 14107\n },\n {\n \"tag\": "unannoying",\n \"popularity\": 14087\n },\n {\n \"tag\": "uningenuous",\n \"popularity\": 14067\n },\n {\n \"tag\": "omnibenevolent",\n \"popularity\": 14047\n },\n {\n \"tag\": "commissure",\n \"popularity\": 14027\n },\n {\n \"tag\": "tellureted",\n \"popularity\": 14007\n },\n {\n \"tag\": "suffragan",\n \"popularity\": 13987\n },\n {\n \"tag\": "sphaeriaceous",\n \"popularity\": 13967\n },\n {\n \"tag\": "unfearing",\n \"popularity\": 13947\n },\n {\n \"tag\": "stentoriousness precounsellor",\n \"popularity\": 13928\n },\n {\n \"tag\": "haemaspectroscope",\n \"popularity\": 13908\n },\n {\n \"tag\": "teras",\n \"popularity\": 13888\n },\n {\n \"tag\": "pulicine",\n \"popularity\": 13869\n },\n {\n \"tag\": "colicystopyelitis",\n \"popularity\": 13849\n },\n {\n \"tag\": "Physalia",\n \"popularity\": 13830\n },\n {\n \"tag\": "Saxicolidae",\n \"popularity\": 13810\n },\n {\n \"tag\": "peritonital",\n \"popularity\": 13791\n },\n {\n \"tag\": "dysphotic",\n \"popularity\": 13771\n },\n {\n \"tag\": "unabandoned",\n \"popularity\": 13752\n },\n {\n \"tag\": "rashful",\n \"popularity\": 13733\n },\n {\n \"tag\": "goodyness Manobo",\n \"popularity\": 13714\n },\n {\n \"tag\": "glaring",\n \"popularity\": 13694\n },\n {\n \"tag\": "horrorful",\n \"popularity\": 13675\n },\n {\n \"tag\": "intercepting",\n \"popularity\": 13656\n },\n {\n \"tag\": "semifine",\n \"popularity\": 13637\n },\n {\n \"tag\": "Gaypoo",\n \"popularity\": 13618\n },\n {\n \"tag\": "Metrosideros",\n \"popularity\": 13599\n },\n {\n \"tag\": "thoracicolumbar",\n \"popularity\": 13580\n },\n {\n \"tag\": "unserried",\n \"popularity\": 13561\n },\n {\n \"tag\": "keeperess cauterization",\n \"popularity\": 13542\n },\n {\n \"tag\": "administrant",\n \"popularity\": 13523\n },\n {\n \"tag\": "unpropitiatedness",\n \"popularity\": 13505\n },\n {\n \"tag\": "pensileness",\n \"popularity\": 13486\n },\n {\n \"tag\": "quinaldic unreceivable",\n \"popularity\": 13467\n },\n {\n \"tag\": "Carnaria",\n \"popularity\": 13448\n },\n {\n \"tag\": "azothionium wurrus",\n \"popularity\": 13430\n },\n {\n \"tag\": "mistresshood",\n \"popularity\": 13411\n },\n {\n \"tag\": "Savara",\n \"popularity\": 13393\n },\n {\n \"tag\": "dasyurine",\n \"popularity\": 13374\n },\n {\n \"tag\": "superideal",\n \"popularity\": 13356\n },\n {\n \"tag\": "Parisianize",\n \"popularity\": 13337\n },\n {\n \"tag\": "underearth",\n \"popularity\": 13319\n },\n {\n \"tag\": "athrogenic",\n \"popularity\": 13301\n },\n {\n \"tag\": "communicate",\n \"popularity\": 13282\n },\n {\n \"tag\": "denervation enworthed",\n \"popularity\": 13264\n },\n {\n \"tag\": "subbromide",\n \"popularity\": 13246\n },\n {\n \"tag\": "stenocoriasis",\n \"popularity\": 13228\n },\n {\n \"tag\": "facetiousness",\n \"popularity\": 13209\n },\n {\n \"tag\": "twaddling",\n \"popularity\": 13191\n },\n {\n \"tag\": "tetartoconid",\n \"popularity\": 13173\n },\n {\n \"tag\": "audiophile",\n \"popularity\": 13155\n },\n {\n \"tag\": "fustigate",\n \"popularity\": 13137\n },\n {\n \"tag\": "Sorbian cacophonia",\n \"popularity\": 13119\n },\n {\n \"tag\": "fondish",\n \"popularity\": 13101\n },\n {\n \"tag\": "endomastoiditis",\n \"popularity\": 13084\n },\n {\n \"tag\": "sniptious",\n \"popularity\": 13066\n },\n {\n \"tag\": "glochidiate",\n \"popularity\": 13048\n },\n {\n \"tag\": "polycarboxylic",\n \"popularity\": 13030\n },\n {\n \"tag\": "stamp",\n \"popularity\": 13012\n },\n {\n \"tag\": "tritonymph endotoxoid",\n \"popularity\": 12995\n },\n {\n \"tag\": "wolfskin",\n \"popularity\": 12977\n },\n {\n \"tag\": "oncosimeter",\n \"popularity\": 12959\n },\n {\n \"tag\": "outward",\n \"popularity\": 12942\n },\n {\n \"tag\": "circumscribed",\n \"popularity\": 12924\n },\n {\n \"tag\": "autohemolytic",\n \"popularity\": 12907\n },\n {\n \"tag\": "isorhamnose",\n \"popularity\": 12889\n },\n {\n \"tag\": "monarchomachic",\n \"popularity\": 12872\n },\n {\n \"tag\": "phaenomenon",\n \"popularity\": 12855\n },\n {\n \"tag\": "angiopressure",\n \"popularity\": 12837\n },\n {\n \"tag\": "similarize",\n \"popularity\": 12820\n },\n {\n \"tag\": "unseeable",\n \"popularity\": 12803\n },\n {\n \"tag\": "Toryize",\n \"popularity\": 12785\n },\n {\n \"tag\": "fruitling",\n \"popularity\": 12768\n },\n {\n \"tag\": "axle",\n \"popularity\": 12751\n },\n {\n \"tag\": "priestal cocked",\n \"popularity\": 12734\n },\n {\n \"tag\": "serotoxin",\n \"popularity\": 12717\n },\n {\n \"tag\": "unmovably",\n \"popularity\": 12700\n },\n {\n \"tag\": "darbha",\n \"popularity\": 12683\n },\n {\n \"tag\": "Mongolize",\n \"popularity\": 12666\n },\n {\n \"tag\": "clusteringly",\n \"popularity\": 12649\n },\n {\n \"tag\": "tendence",\n \"popularity\": 12632\n },\n {\n \"tag\": "foziness",\n \"popularity\": 12615\n },\n {\n \"tag\": "brickkiln lithify",\n \"popularity\": 12598\n },\n {\n \"tag\": "unpriest",\n \"popularity\": 12581\n },\n {\n \"tag\": "convincer",\n \"popularity\": 12564\n },\n {\n \"tag\": "mornlike",\n \"popularity\": 12548\n },\n {\n \"tag\": "overaddiction ostentatiousness",\n \"popularity\": 12531\n },\n {\n \"tag\": "diffusively moccasin pendom",\n \"popularity\": 12514\n },\n {\n \"tag\": "boose",\n \"popularity\": 12498\n },\n {\n \"tag\": "myonosus",\n \"popularity\": 12481\n },\n {\n \"tag\": "handsome",\n \"popularity\": 12464\n },\n {\n \"tag\": "paroxysmic",\n \"popularity\": 12448\n },\n {\n \"tag\": "Ulidian",\n \"popularity\": 12431\n },\n {\n \"tag\": "heartache",\n \"popularity\": 12415\n },\n {\n \"tag\": "torporize",\n \"popularity\": 12398\n },\n {\n \"tag\": "hippish",\n \"popularity\": 12382\n },\n {\n \"tag\": "stigmal militation",\n \"popularity\": 12366\n },\n {\n \"tag\": "matmaker",\n \"popularity\": 12349\n },\n {\n \"tag\": "marantaceous bivoluminous",\n \"popularity\": 12333\n },\n {\n \"tag\": "Uraniidae",\n \"popularity\": 12317\n },\n {\n \"tag\": "risper",\n \"popularity\": 12301\n },\n {\n \"tag\": "tintinnabulation",\n \"popularity\": 12284\n },\n {\n \"tag\": "tributorian",\n \"popularity\": 12268\n },\n {\n \"tag\": "ashamedly",\n \"popularity\": 12252\n },\n {\n \"tag\": "Macrourus",\n \"popularity\": 12236\n },\n {\n \"tag\": "Chora",\n \"popularity\": 12220\n },\n {\n \"tag\": "caul",\n \"popularity\": 12204\n },\n {\n \"tag\": "exsector",\n \"popularity\": 12188\n },\n {\n \"tag\": "acutish",\n \"popularity\": 12172\n },\n {\n \"tag\": "amphichrome",\n \"popularity\": 12156\n },\n {\n \"tag\": "guarder",\n \"popularity\": 12140\n },\n {\n \"tag\": "sculpturally",\n \"popularity\": 12124\n },\n {\n \"tag\": "benightmare",\n \"popularity\": 12108\n },\n {\n \"tag\": "chucky",\n \"popularity\": 12093\n },\n {\n \"tag\": "Venetian",\n \"popularity\": 12077\n },\n {\n \"tag\": "autotheater",\n \"popularity\": 12061\n },\n {\n \"tag\": "planarioid",\n \"popularity\": 12045\n },\n {\n \"tag\": "handkerchiefful",\n \"popularity\": 12030\n },\n {\n \"tag\": "fuliginousness potentize",\n \"popularity\": 12014\n },\n {\n \"tag\": "pantheum",\n \"popularity\": 11998\n },\n {\n \"tag\": "heavyweight",\n \"popularity\": 11983\n },\n {\n \"tag\": "unbrick",\n \"popularity\": 11967\n },\n {\n \"tag\": "duomachy",\n \"popularity\": 11952\n },\n {\n \"tag\": "polyphyodont",\n \"popularity\": 11936\n },\n {\n \"tag\": "hibernacle",\n \"popularity\": 11921\n },\n {\n \"tag\": "undistend",\n \"popularity\": 11905\n },\n {\n \"tag\": "hystericky",\n \"popularity\": 11890\n },\n {\n \"tag\": "paleolimnology",\n \"popularity\": 11875\n },\n {\n \"tag\": "cedarware",\n \"popularity\": 11859\n },\n {\n \"tag\": "overwrested",\n \"popularity\": 11844\n },\n {\n \"tag\": "Syriacism",\n \"popularity\": 11829\n },\n {\n \"tag\": "pretan",\n \"popularity\": 11813\n },\n {\n \"tag\": "formant",\n \"popularity\": 11798\n },\n {\n \"tag\": "pharmacopoeist Fedia",\n \"popularity\": 11783\n },\n {\n \"tag\": "exorcist eerisome",\n \"popularity\": 11768\n },\n {\n \"tag\": "separation",\n \"popularity\": 11753\n },\n {\n \"tag\": "infancy",\n \"popularity\": 11738\n },\n {\n \"tag\": "ecrasite",\n \"popularity\": 11723\n },\n {\n \"tag\": "propolize",\n \"popularity\": 11708\n },\n {\n \"tag\": "uncram phyllin",\n \"popularity\": 11693\n },\n {\n \"tag\": "thymopathy",\n \"popularity\": 11678\n },\n {\n \"tag\": "omniscient",\n \"popularity\": 11663\n },\n {\n \"tag\": "coussinet hazer",\n \"popularity\": 11648\n },\n {\n \"tag\": "contributiveness",\n \"popularity\": 11633\n },\n {\n \"tag\": "septifluous",\n \"popularity\": 11618\n },\n {\n \"tag\": "halfness",\n \"popularity\": 11603\n },\n {\n \"tag\": "tocher",\n \"popularity\": 11589\n },\n {\n \"tag\": "monotonist",\n \"popularity\": 11574\n },\n {\n \"tag\": "headchair",\n \"popularity\": 11559\n },\n {\n \"tag\": "everywhence",\n \"popularity\": 11544\n },\n {\n \"tag\": "gerate",\n \"popularity\": 11530\n },\n {\n \"tag\": "unrepellent",\n \"popularity\": 11515\n },\n {\n \"tag\": "inidoneous",\n \"popularity\": 11500\n },\n {\n \"tag\": "Rifi",\n \"popularity\": 11486\n },\n {\n \"tag\": "unstop",\n \"popularity\": 11471\n },\n {\n \"tag\": "conformer",\n \"popularity\": 11457\n },\n {\n \"tag\": "vivisectionally",\n \"popularity\": 11442\n },\n {\n \"tag\": "nonfinishing",\n \"popularity\": 11428\n },\n {\n \"tag\": "tyranness",\n \"popularity\": 11413\n },\n {\n \"tag\": "shepherdage havoc",\n \"popularity\": 11399\n },\n {\n \"tag\": "coronale",\n \"popularity\": 11385\n },\n {\n \"tag\": "airmarker",\n \"popularity\": 11370\n },\n {\n \"tag\": "subpanel",\n \"popularity\": 11356\n },\n {\n \"tag\": "conciliation",\n \"popularity\": 11342\n },\n {\n \"tag\": "supergun",\n \"popularity\": 11327\n },\n {\n \"tag\": "photoheliography",\n \"popularity\": 11313\n },\n {\n \"tag\": "cacosmia",\n \"popularity\": 11299\n },\n {\n \"tag\": "caressant",\n \"popularity\": 11285\n },\n {\n \"tag\": "swivet",\n \"popularity\": 11270\n },\n {\n \"tag\": "coddler",\n \"popularity\": 11256\n },\n {\n \"tag\": "rakehellish",\n \"popularity\": 11242\n },\n {\n \"tag\": "recohabitation",\n \"popularity\": 11228\n },\n {\n \"tag\": "postillator",\n \"popularity\": 11214\n },\n {\n \"tag\": "receipt",\n \"popularity\": 11200\n },\n {\n \"tag\": "nonconformistical",\n \"popularity\": 11186\n },\n {\n \"tag\": "unglorified",\n \"popularity\": 11172\n },\n {\n \"tag\": "unordinariness",\n \"popularity\": 11158\n },\n {\n \"tag\": "tetrahydroxy",\n \"popularity\": 11144\n },\n {\n \"tag\": "haploperistomic corporeity",\n \"popularity\": 11130\n },\n {\n \"tag\": "varical",\n \"popularity\": 11117\n },\n {\n \"tag\": "pilferment",\n \"popularity\": 11103\n },\n {\n \"tag\": "reverentially playcraft",\n \"popularity\": 11089\n },\n {\n \"tag\": "unretentive",\n \"popularity\": 11075\n },\n {\n \"tag\": "readiness",\n \"popularity\": 11061\n },\n {\n \"tag\": "thermomagnetism",\n \"popularity\": 11048\n },\n {\n \"tag\": "spotless",\n \"popularity\": 11034\n },\n {\n \"tag\": "semishrubby",\n \"popularity\": 11020\n },\n {\n \"tag\": "metrotomy",\n \"popularity\": 11007\n },\n {\n \"tag\": "hocker",\n \"popularity\": 10993\n },\n {\n \"tag\": "anecdotal",\n \"popularity\": 10979\n },\n {\n \"tag\": "tetrabelodont",\n \"popularity\": 10966\n },\n {\n \"tag\": "Ramillied",\n \"popularity\": 10952\n },\n {\n \"tag\": "sympatheticism",\n \"popularity\": 10939\n },\n {\n \"tag\": "kiskatom",\n \"popularity\": 10925\n },\n {\n \"tag\": "concyclically",\n \"popularity\": 10912\n },\n {\n \"tag\": "tunicless",\n \"popularity\": 10899\n },\n {\n \"tag\": "formalistic",\n \"popularity\": 10885\n },\n {\n \"tag\": "thermacogenesis",\n \"popularity\": 10872\n },\n {\n \"tag\": "multimotored",\n \"popularity\": 10858\n },\n {\n \"tag\": "inversive",\n \"popularity\": 10845\n },\n {\n \"tag\": "Jatki",\n \"popularity\": 10832\n },\n {\n \"tag\": "highest",\n \"popularity\": 10818\n },\n {\n \"tag\": "rubidic",\n \"popularity\": 10805\n },\n {\n \"tag\": "acranial",\n \"popularity\": 10792\n },\n {\n \"tag\": "pulvinulus",\n \"popularity\": 10779\n },\n {\n \"tag\": "nattiness",\n \"popularity\": 10766\n },\n {\n \"tag\": "antisimoniacal",\n \"popularity\": 10752\n },\n {\n \"tag\": "tetanize",\n \"popularity\": 10739\n },\n {\n \"tag\": "spectrophobia",\n \"popularity\": 10726\n },\n {\n \"tag\": "monopolitical",\n \"popularity\": 10713\n },\n {\n \"tag\": "teallite",\n \"popularity\": 10700\n },\n {\n \"tag\": "alicyclic interpellator",\n \"popularity\": 10687\n },\n {\n \"tag\": "nonsynthesized",\n \"popularity\": 10674\n },\n {\n \"tag\": "wheelwrighting",\n \"popularity\": 10661\n },\n {\n \"tag\": "pelliculate",\n \"popularity\": 10648\n },\n {\n \"tag\": "Euphyllopoda",\n \"popularity\": 10635\n },\n {\n \"tag\": "graver",\n \"popularity\": 10622\n },\n {\n \"tag\": "automorph",\n \"popularity\": 10609\n },\n {\n \"tag\": "underhanded",\n \"popularity\": 10597\n },\n {\n \"tag\": "causal",\n \"popularity\": 10584\n },\n {\n \"tag\": "odoom",\n \"popularity\": 10571\n },\n {\n \"tag\": "apodictical",\n \"popularity\": 10558\n },\n {\n \"tag\": "foundery",\n \"popularity\": 10545\n },\n {\n \"tag\": "unneighbored",\n \"popularity\": 10533\n },\n {\n \"tag\": "woolshearing",\n \"popularity\": 10520\n },\n {\n \"tag\": "boschveld",\n \"popularity\": 10507\n },\n {\n \"tag\": "unhardened lipopod",\n \"popularity\": 10495\n },\n {\n \"tag\": "unenriching",\n \"popularity\": 10482\n },\n {\n \"tag\": "spak",\n \"popularity\": 10469\n },\n {\n \"tag\": "yogasana",\n \"popularity\": 10457\n },\n {\n \"tag\": "depoetize",\n \"popularity\": 10444\n },\n {\n \"tag\": "parousiamania",\n \"popularity\": 10432\n },\n {\n \"tag\": "longlegs",\n \"popularity\": 10419\n },\n {\n \"tag\": "gelatinizability",\n \"popularity\": 10407\n },\n {\n \"tag\": "edeology",\n \"popularity\": 10394\n },\n {\n \"tag\": "sodwork",\n \"popularity\": 10382\n },\n {\n \"tag\": "somnambule",\n \"popularity\": 10369\n },\n {\n \"tag\": "antiquing",\n \"popularity\": 10357\n },\n {\n \"tag\": "intaker",\n \"popularity\": 10344\n },\n {\n \"tag\": "Gerberia",\n \"popularity\": 10332\n },\n {\n \"tag\": "preadmit",\n \"popularity\": 10320\n },\n {\n \"tag\": "bullhorn",\n \"popularity\": 10307\n },\n {\n \"tag\": "sororal",\n \"popularity\": 10295\n },\n {\n \"tag\": "phaeophyceous",\n \"popularity\": 10283\n },\n {\n \"tag\": "omphalopsychite",\n \"popularity\": 10271\n },\n {\n \"tag\": "substantious",\n \"popularity\": 10258\n },\n {\n \"tag\": "undemonstratively",\n \"popularity\": 10246\n },\n {\n \"tag\": "corallike blackit",\n \"popularity\": 10234\n },\n {\n \"tag\": "amoebous",\n \"popularity\": 10222\n },\n {\n \"tag\": "Polypodium",\n \"popularity\": 10210\n },\n {\n \"tag\": "blodite",\n \"popularity\": 10198\n },\n {\n \"tag\": "hordarian",\n \"popularity\": 10186\n },\n {\n \"tag\": "nonmoral",\n \"popularity\": 10174\n },\n {\n \"tag\": "dredgeful",\n \"popularity\": 10162\n },\n {\n \"tag\": "nourishingly",\n \"popularity\": 10150\n },\n {\n \"tag\": "seamy",\n \"popularity\": 10138\n },\n {\n \"tag\": "vara",\n \"popularity\": 10126\n },\n {\n \"tag\": "incorruptibleness",\n \"popularity\": 10114\n },\n {\n \"tag\": "manipulator",\n \"popularity\": 10102\n },\n {\n \"tag\": "chromodiascope uncountably",\n \"popularity\": 10090\n },\n {\n \"tag\": "typhemia",\n \"popularity\": 10078\n },\n {\n \"tag\": "Smalcaldic",\n \"popularity\": 10066\n },\n {\n \"tag\": "precontrive",\n \"popularity\": 10054\n },\n {\n \"tag\": "sowarry",\n \"popularity\": 10042\n },\n {\n \"tag\": "monopodic",\n \"popularity\": 10031\n },\n {\n \"tag\": "recodify",\n \"popularity\": 10019\n },\n {\n \"tag\": "phosphowolframic rimple",\n \"popularity\": 10007\n },\n {\n \"tag\": "triconch",\n \"popularity\": 9995\n },\n {\n \"tag\": "pycnodontoid",\n \"popularity\": 9984\n },\n {\n \"tag\": "bradyspermatism",\n \"popularity\": 9972\n },\n {\n \"tag\": "extensionist",\n \"popularity\": 9960\n },\n {\n \"tag\": "characterize",\n \"popularity\": 9949\n },\n {\n \"tag\": "anatreptic proteolytic",\n \"popularity\": 9937\n },\n {\n \"tag\": "waterboard",\n \"popularity\": 9925\n },\n {\n \"tag\": "allopathically",\n \"popularity\": 9914\n },\n {\n \"tag\": "arithmetician",\n \"popularity\": 9902\n },\n {\n \"tag\": "subsist",\n \"popularity\": 9891\n },\n {\n \"tag\": "Islamitish",\n \"popularity\": 9879\n },\n {\n \"tag\": "biddy",\n \"popularity\": 9868\n },\n {\n \"tag\": "reverberation",\n \"popularity\": 9856\n },\n {\n \"tag\": "Zaporogue",\n \"popularity\": 9845\n },\n {\n \"tag\": "soapberry",\n \"popularity\": 9833\n },\n {\n \"tag\": "physiognomics",\n \"popularity\": 9822\n },\n {\n \"tag\": "hospitalization",\n \"popularity\": 9810\n },\n {\n \"tag\": "dissembler",\n \"popularity\": 9799\n },\n {\n \"tag\": "festinate",\n \"popularity\": 9788\n },\n {\n \"tag\": "angiectopia",\n \"popularity\": 9776\n },\n {\n \"tag\": "Pulicidae",\n \"popularity\": 9765\n },\n {\n \"tag\": "beslimer",\n \"popularity\": 9754\n },\n {\n \"tag\": "nontreaty",\n \"popularity\": 9743\n },\n {\n \"tag\": "unhaggled",\n \"popularity\": 9731\n },\n {\n \"tag\": "catfall",\n \"popularity\": 9720\n },\n {\n \"tag\": "stola",\n \"popularity\": 9709\n },\n {\n \"tag\": "pataco",\n \"popularity\": 9698\n },\n {\n \"tag\": "ontologistic",\n \"popularity\": 9686\n },\n {\n \"tag\": "aerosphere",\n \"popularity\": 9675\n },\n {\n \"tag\": "deobstruent",\n \"popularity\": 9664\n },\n {\n \"tag\": "threepence",\n \"popularity\": 9653\n },\n {\n \"tag\": "cyprinoid",\n \"popularity\": 9642\n },\n {\n \"tag\": "overbank",\n \"popularity\": 9631\n },\n {\n \"tag\": "prostyle",\n \"popularity\": 9620\n },\n {\n \"tag\": "photoactivation",\n \"popularity\": 9609\n },\n {\n \"tag\": "homothetic",\n \"popularity\": 9598\n },\n {\n \"tag\": "roguedom",\n \"popularity\": 9587\n },\n {\n \"tag\": "underschool",\n \"popularity\": 9576\n },\n {\n \"tag\": "tractility",\n \"popularity\": 9565\n },\n {\n \"tag\": "gardenin",\n \"popularity\": 9554\n },\n {\n \"tag\": "Micromastictora",\n \"popularity\": 9543\n },\n {\n \"tag\": "gossypine",\n \"popularity\": 9532\n },\n {\n \"tag\": "amylodyspepsia",\n \"popularity\": 9521\n },\n {\n \"tag\": "Luciana",\n \"popularity\": 9510\n },\n {\n \"tag\": "meetly nonfisherman",\n \"popularity\": 9500\n },\n {\n \"tag\": "backhanded",\n \"popularity\": 9489\n },\n {\n \"tag\": "decrustation",\n \"popularity\": 9478\n },\n {\n \"tag\": "pinrail",\n \"popularity\": 9467\n },\n {\n \"tag\": "Mahori",\n \"popularity\": 9456\n },\n {\n \"tag\": "unsizable",\n \"popularity\": 9446\n },\n {\n \"tag\": "disawa",\n \"popularity\": 9435\n },\n {\n \"tag\": "launderability inconsidered",\n \"popularity\": 9424\n },\n {\n \"tag\": "unclassical",\n \"popularity\": 9414\n },\n {\n \"tag\": "inobtrusiveness",\n \"popularity\": 9403\n },\n {\n \"tag\": "sialogenous",\n \"popularity\": 9392\n },\n {\n \"tag\": "sulphonamide",\n \"popularity\": 9382\n },\n {\n \"tag\": "diluvion",\n \"popularity\": 9371\n },\n {\n \"tag\": "deuteranope",\n \"popularity\": 9361\n },\n {\n \"tag\": "addition",\n \"popularity\": 9350\n },\n {\n \"tag\": "bockeret",\n \"popularity\": 9339\n },\n {\n \"tag\": "unidentified",\n \"popularity\": 9329\n },\n {\n \"tag\": "caryatic",\n \"popularity\": 9318\n },\n {\n \"tag\": "misattribution",\n \"popularity\": 9308\n },\n {\n \"tag\": "outray",\n \"popularity\": 9297\n },\n {\n \"tag\": "areometrical",\n \"popularity\": 9287\n },\n {\n \"tag\": "antilogism",\n \"popularity\": 9277\n },\n {\n \"tag\": "inadjustable",\n \"popularity\": 9266\n },\n {\n \"tag\": "byssus",\n \"popularity\": 9256\n },\n {\n \"tag\": "trun",\n \"popularity\": 9245\n },\n {\n \"tag\": "thereology",\n \"popularity\": 9235\n },\n {\n \"tag\": "extort",\n \"popularity\": 9225\n },\n {\n \"tag\": "bumpkin",\n \"popularity\": 9214\n },\n {\n \"tag\": "sulphobenzide",\n \"popularity\": 9204\n },\n {\n \"tag\": "hydrogeology",\n \"popularity\": 9194\n },\n {\n \"tag\": "nidulariaceous",\n \"popularity\": 9183\n },\n {\n \"tag\": "propodiale",\n \"popularity\": 9173\n },\n {\n \"tag\": "fierily",\n \"popularity\": 9163\n },\n {\n \"tag\": "aerotonometry",\n \"popularity\": 9153\n },\n {\n \"tag\": "pelobatid oversuperstitious",\n \"popularity\": 9142\n },\n {\n \"tag\": "restringent",\n \"popularity\": 9132\n },\n {\n \"tag\": "tetrapodic",\n \"popularity\": 9122\n },\n {\n \"tag\": "heroicness Vendidad",\n \"popularity\": 9112\n },\n {\n \"tag\": "Sphingurus",\n \"popularity\": 9102\n },\n {\n \"tag\": "sclerote",\n \"popularity\": 9092\n },\n {\n \"tag\": "unkeyed",\n \"popularity\": 9082\n },\n {\n \"tag\": "superparliamentary",\n \"popularity\": 9072\n },\n {\n \"tag\": "hetericism",\n \"popularity\": 9061\n },\n {\n \"tag\": "hucklebone",\n \"popularity\": 9051\n },\n {\n \"tag\": "yojan",\n \"popularity\": 9041\n },\n {\n \"tag\": "bossed",\n \"popularity\": 9031\n },\n {\n \"tag\": "spiderwork",\n \"popularity\": 9021\n },\n {\n \"tag\": "millfeed dullery",\n \"popularity\": 9011\n },\n {\n \"tag\": "adnoun",\n \"popularity\": 9001\n },\n {\n \"tag\": "mesometric",\n \"popularity\": 8992\n },\n {\n \"tag\": "doublehandedness",\n \"popularity\": 8982\n },\n {\n \"tag\": "suppurant",\n \"popularity\": 8972\n },\n {\n \"tag\": "Berlinize",\n \"popularity\": 8962\n },\n {\n \"tag\": "sontag",\n \"popularity\": 8952\n },\n {\n \"tag\": "biplane",\n \"popularity\": 8942\n },\n {\n \"tag\": "insula",\n \"popularity\": 8932\n },\n {\n \"tag\": "unbrand",\n \"popularity\": 8922\n },\n {\n \"tag\": "Basilosaurus",\n \"popularity\": 8913\n },\n {\n \"tag\": "prenomination",\n \"popularity\": 8903\n },\n {\n \"tag\": "untextual",\n \"popularity\": 8893\n },\n {\n \"tag\": "coleslaw",\n \"popularity\": 8883\n },\n {\n \"tag\": "langsyne",\n \"popularity\": 8874\n },\n {\n \"tag\": "impede",\n \"popularity\": 8864\n },\n {\n \"tag\": "irrigator",\n \"popularity\": 8854\n },\n {\n \"tag\": "deflocculation",\n \"popularity\": 8844\n },\n {\n \"tag\": "narghile",\n \"popularity\": 8835\n },\n {\n \"tag\": "unguardedly ebenaceous",\n \"popularity\": 8825\n },\n {\n \"tag\": "conversantly subocular",\n \"popularity\": 8815\n },\n {\n \"tag\": "hydroponic",\n \"popularity\": 8806\n },\n {\n \"tag\": "anthropopsychism",\n \"popularity\": 8796\n },\n {\n \"tag\": "panoptic",\n \"popularity\": 8787\n },\n {\n \"tag\": "insufferable",\n \"popularity\": 8777\n },\n {\n \"tag\": "salema",\n \"popularity\": 8768\n },\n {\n \"tag\": "Myriapoda",\n \"popularity\": 8758\n },\n {\n \"tag\": "regarrison",\n \"popularity\": 8748\n },\n {\n \"tag\": "overlearned",\n \"popularity\": 8739\n },\n {\n \"tag\": "ultraroyalist conventical bureaucratical",\n \"popularity\": 8729\n },\n {\n \"tag\": "epicaridan",\n \"popularity\": 8720\n },\n {\n \"tag\": "poetastress",\n \"popularity\": 8711\n },\n {\n \"tag\": "monophthalmus",\n \"popularity\": 8701\n },\n {\n \"tag\": "simnel",\n \"popularity\": 8692\n },\n {\n \"tag\": "compotor",\n \"popularity\": 8682\n },\n {\n \"tag\": "hydrolase",\n \"popularity\": 8673\n },\n {\n \"tag\": "attemptless",\n \"popularity\": 8663\n },\n {\n \"tag\": "visceroptosis",\n \"popularity\": 8654\n },\n {\n \"tag\": "unpreparedly",\n \"popularity\": 8645\n },\n {\n \"tag\": "mastage",\n \"popularity\": 8635\n },\n {\n \"tag\": "preinfluence",\n \"popularity\": 8626\n },\n {\n \"tag\": "Siwan",\n \"popularity\": 8617\n },\n {\n \"tag\": "ceratotheca belvedere",\n \"popularity\": 8607\n },\n {\n \"tag\": "disenablement",\n \"popularity\": 8598\n },\n {\n \"tag\": "nine",\n \"popularity\": 8589\n },\n {\n \"tag\": "spellingdown abridgment",\n \"popularity\": 8580\n },\n {\n \"tag\": "twilightless",\n \"popularity\": 8571\n },\n {\n \"tag\": "overflow",\n \"popularity\": 8561\n },\n {\n \"tag\": "mismeasurement",\n \"popularity\": 8552\n },\n {\n \"tag\": "nawabship",\n \"popularity\": 8543\n },\n {\n \"tag\": "Phrynosoma",\n \"popularity\": 8534\n },\n {\n \"tag\": "unanticipatingly",\n \"popularity\": 8525\n },\n {\n \"tag\": "blankite",\n \"popularity\": 8516\n },\n {\n \"tag\": "role",\n \"popularity\": 8506\n },\n {\n \"tag\": "peperine edelweiss",\n \"popularity\": 8497\n },\n {\n \"tag\": "unhysterical",\n \"popularity\": 8488\n },\n {\n \"tag\": "attentiveness",\n \"popularity\": 8479\n },\n {\n \"tag\": "scintillant",\n \"popularity\": 8470\n },\n {\n \"tag\": "stenostomatous",\n \"popularity\": 8461\n },\n {\n \"tag\": "pectinite",\n \"popularity\": 8452\n },\n {\n \"tag\": "herring",\n \"popularity\": 8443\n },\n {\n \"tag\": "interroom",\n \"popularity\": 8434\n },\n {\n \"tag\": "laccol",\n \"popularity\": 8425\n },\n {\n \"tag\": "unpartably kylite",\n \"popularity\": 8416\n },\n {\n \"tag\": "spirivalve",\n \"popularity\": 8407\n },\n {\n \"tag\": "hoosegow",\n \"popularity\": 8398\n },\n {\n \"tag\": "doat",\n \"popularity\": 8389\n },\n {\n \"tag\": "amphibian",\n \"popularity\": 8380\n },\n {\n \"tag\": "exposit",\n \"popularity\": 8371\n },\n {\n \"tag\": "canopy",\n \"popularity\": 8363\n },\n {\n \"tag\": "houndlike",\n \"popularity\": 8354\n },\n {\n \"tag\": "spikebill",\n \"popularity\": 8345\n },\n {\n \"tag\": "wiseacre pyrotechnic",\n \"popularity\": 8336\n },\n {\n \"tag\": "confessingly woodman",\n \"popularity\": 8327\n },\n {\n \"tag\": "overside",\n \"popularity\": 8318\n },\n {\n \"tag\": "oftwhiles",\n \"popularity\": 8310\n },\n {\n \"tag\": "Musophagidae",\n \"popularity\": 8301\n },\n {\n \"tag\": "slumberer",\n \"popularity\": 8292\n },\n {\n \"tag\": "leiotrichy",\n \"popularity\": 8283\n },\n {\n \"tag\": "Mantispidae",\n \"popularity\": 8275\n },\n {\n \"tag\": "perceptually",\n \"popularity\": 8266\n },\n {\n \"tag\": "biller",\n \"popularity\": 8257\n },\n {\n \"tag\": "eudaemonical",\n \"popularity\": 8249\n },\n {\n \"tag\": "underfiend",\n \"popularity\": 8240\n },\n {\n \"tag\": "impartible",\n \"popularity\": 8231\n },\n {\n \"tag\": "saxicavous",\n \"popularity\": 8223\n },\n {\n \"tag\": "yapster",\n \"popularity\": 8214\n },\n {\n \"tag\": "aliseptal",\n \"popularity\": 8205\n },\n {\n \"tag\": "omniparient",\n \"popularity\": 8197\n },\n {\n \"tag\": "nishiki",\n \"popularity\": 8188\n },\n {\n \"tag\": "yuzluk",\n \"popularity\": 8180\n },\n {\n \"tag\": "solderer",\n \"popularity\": 8171\n },\n {\n \"tag\": "Pinna",\n \"popularity\": 8162\n },\n {\n \"tag\": "reinterfere",\n \"popularity\": 8154\n },\n {\n \"tag\": "superepic",\n \"popularity\": 8145\n },\n {\n \"tag\": "ronquil",\n \"popularity\": 8137\n },\n {\n \"tag\": "bratstvo",\n \"popularity\": 8128\n },\n {\n \"tag\": "Thea",\n \"popularity\": 8120\n },\n {\n \"tag\": "hermaphroditical",\n \"popularity\": 8111\n },\n {\n \"tag\": "enlief",\n \"popularity\": 8103\n },\n {\n \"tag\": "Jesuate",\n \"popularity\": 8095\n },\n {\n \"tag\": "gaysome",\n \"popularity\": 8086\n },\n {\n \"tag\": "iliohypogastric",\n \"popularity\": 8078\n },\n {\n \"tag\": "regardance",\n \"popularity\": 8069\n },\n {\n \"tag\": "cumulately",\n \"popularity\": 8061\n },\n {\n \"tag\": "haustorial nucleolocentrosome",\n \"popularity\": 8053\n },\n {\n \"tag\": "cosmocrat",\n \"popularity\": 8044\n },\n {\n \"tag\": "onyxitis",\n \"popularity\": 8036\n },\n {\n \"tag\": "Cabinda",\n \"popularity\": 8028\n },\n {\n \"tag\": "coresort",\n \"popularity\": 8019\n },\n {\n \"tag\": "drusy preformant",\n \"popularity\": 8011\n },\n {\n \"tag\": "piningly",\n \"popularity\": 8003\n },\n {\n \"tag\": "bootlessly",\n \"popularity\": 7994\n },\n {\n \"tag\": "talari",\n \"popularity\": 7986\n },\n {\n \"tag\": "amidoacetal",\n \"popularity\": 7978\n },\n {\n \"tag\": "pschent",\n \"popularity\": 7970\n },\n {\n \"tag\": "consumptional scarer titivate",\n \"popularity\": 7962\n },\n {\n \"tag\": "Anserinae",\n \"popularity\": 7953\n },\n {\n \"tag\": "flaunter",\n \"popularity\": 7945\n },\n {\n \"tag\": "reindeer",\n \"popularity\": 7937\n },\n {\n \"tag\": "disparage",\n \"popularity\": 7929\n },\n {\n \"tag\": "superheat",\n \"popularity\": 7921\n },\n {\n \"tag\": "Chromatium",\n \"popularity\": 7912\n },\n {\n \"tag\": "Tina",\n \"popularity\": 7904\n },\n {\n \"tag\": "rededicatory",\n \"popularity\": 7896\n },\n {\n \"tag\": "nontransient",\n \"popularity\": 7888\n },\n {\n \"tag\": "Phocaean brinkless",\n \"popularity\": 7880\n },\n {\n \"tag\": "ventriculose",\n \"popularity\": 7872\n },\n {\n \"tag\": "upplough",\n \"popularity\": 7864\n },\n {\n \"tag\": "succorless",\n \"popularity\": 7856\n },\n {\n \"tag\": "hayrake",\n \"popularity\": 7848\n },\n {\n \"tag\": "merriness amorphia",\n \"popularity\": 7840\n },\n {\n \"tag\": "merycism",\n \"popularity\": 7832\n },\n {\n \"tag\": "checkrow",\n \"popularity\": 7824\n },\n {\n \"tag\": "scry",\n \"popularity\": 7816\n },\n {\n \"tag\": "obvolve",\n \"popularity\": 7808\n },\n {\n \"tag\": "orchard",\n \"popularity\": 7800\n },\n {\n \"tag\": "isomerize",\n \"popularity\": 7792\n },\n {\n \"tag\": "competitrix",\n \"popularity\": 7784\n },\n {\n \"tag\": "unbannered",\n \"popularity\": 7776\n },\n {\n \"tag\": "undoctrined",\n \"popularity\": 7768\n },\n {\n \"tag\": "theologian",\n \"popularity\": 7760\n },\n {\n \"tag\": "nebby",\n \"popularity\": 7752\n },\n {\n \"tag\": "Cardiazol",\n \"popularity\": 7745\n },\n {\n \"tag\": "phagedenic",\n \"popularity\": 7737\n },\n {\n \"tag\": "nostalgic",\n \"popularity\": 7729\n },\n {\n \"tag\": "orthodoxy",\n \"popularity\": 7721\n },\n {\n \"tag\": "oversanguine",\n \"popularity\": 7713\n },\n {\n \"tag\": "lish",\n \"popularity\": 7705\n },\n {\n \"tag\": "ketogenic",\n \"popularity\": 7698\n },\n {\n \"tag\": "syndicalize",\n \"popularity\": 7690\n },\n {\n \"tag\": "leeftail",\n \"popularity\": 7682\n },\n {\n \"tag\": "bulbomedullary",\n \"popularity\": 7674\n },\n {\n \"tag\": "reletter",\n \"popularity\": 7667\n },\n {\n \"tag\": "bitterly",\n \"popularity\": 7659\n },\n {\n \"tag\": "participatory",\n \"popularity\": 7651\n },\n {\n \"tag\": "baldberry",\n \"popularity\": 7643\n },\n {\n \"tag\": "prowaterpower",\n \"popularity\": 7636\n },\n {\n \"tag\": "lexicographical",\n \"popularity\": 7628\n },\n {\n \"tag\": "Anisodactyli",\n \"popularity\": 7620\n },\n {\n \"tag\": "amphipodous",\n \"popularity\": 7613\n },\n {\n \"tag\": "triglandular",\n \"popularity\": 7605\n },\n {\n \"tag\": "xanthopsin",\n \"popularity\": 7597\n },\n {\n \"tag\": "indefinitude",\n \"popularity\": 7590\n },\n {\n \"tag\": "bookworm",\n \"popularity\": 7582\n },\n {\n \"tag\": "suffocative",\n \"popularity\": 7574\n },\n {\n \"tag\": "uncongested tyrant",\n \"popularity\": 7567\n },\n {\n \"tag\": "alow harmoniously Pamir",\n \"popularity\": 7559\n },\n {\n \"tag\": "monander",\n \"popularity\": 7552\n },\n {\n \"tag\": "bagatelle",\n \"popularity\": 7544\n },\n {\n \"tag\": "membranology",\n \"popularity\": 7537\n },\n {\n \"tag\": "parturifacient",\n \"popularity\": 7529\n },\n {\n \"tag\": "excitovascular",\n \"popularity\": 7522\n },\n {\n \"tag\": "homopolar",\n \"popularity\": 7514\n },\n {\n \"tag\": "phobiac",\n \"popularity\": 7507\n },\n {\n \"tag\": "clype",\n \"popularity\": 7499\n },\n {\n \"tag\": "unsubversive",\n \"popularity\": 7492\n },\n {\n \"tag\": "bostrychoidal scorpionwort",\n \"popularity\": 7484\n },\n {\n \"tag\": "biliteralism",\n \"popularity\": 7477\n },\n {\n \"tag\": "dentatocostate",\n \"popularity\": 7469\n },\n {\n \"tag\": "Pici",\n \"popularity\": 7462\n },\n {\n \"tag\": "sideritic",\n \"popularity\": 7454\n },\n {\n \"tag\": "syntaxis",\n \"popularity\": 7447\n },\n {\n \"tag\": "ingest",\n \"popularity\": 7440\n },\n {\n \"tag\": "rigmarolish",\n \"popularity\": 7432\n },\n {\n \"tag\": "ocreaceous",\n \"popularity\": 7425\n },\n {\n \"tag\": "hyperbrachyskelic",\n \"popularity\": 7418\n },\n {\n \"tag\": "basophobia",\n \"popularity\": 7410\n },\n {\n \"tag\": "substantialness",\n \"popularity\": 7403\n },\n {\n \"tag\": "agglutinoid",\n \"popularity\": 7396\n },\n {\n \"tag\": "longleaf",\n \"popularity\": 7388\n },\n {\n \"tag\": "electroengraving",\n \"popularity\": 7381\n },\n {\n \"tag\": "laparoenterotomy",\n \"popularity\": 7374\n },\n {\n \"tag\": "oxalylurea",\n \"popularity\": 7366\n },\n {\n \"tag\": "unattaintedly",\n \"popularity\": 7359\n },\n {\n \"tag\": "pennystone",\n \"popularity\": 7352\n },\n {\n \"tag\": "Plumbaginaceae",\n \"popularity\": 7345\n },\n {\n \"tag\": "horntip",\n \"popularity\": 7337\n },\n {\n \"tag\": "begrudge",\n \"popularity\": 7330\n },\n {\n \"tag\": "bechignoned",\n \"popularity\": 7323\n },\n {\n \"tag\": "hologonidium",\n \"popularity\": 7316\n },\n {\n \"tag\": "Pulian",\n \"popularity\": 7309\n },\n {\n \"tag\": "gratulation",\n \"popularity\": 7301\n },\n {\n \"tag\": "Sebright",\n \"popularity\": 7294\n },\n {\n \"tag\": "coinstantaneous emotionally",\n \"popularity\": 7287\n },\n {\n \"tag\": "thoracostracan",\n \"popularity\": 7280\n },\n {\n \"tag\": "saurodont",\n \"popularity\": 7273\n },\n {\n \"tag\": "coseat",\n \"popularity\": 7266\n },\n {\n \"tag\": "irascibility",\n \"popularity\": 7259\n },\n {\n \"tag\": "occlude",\n \"popularity\": 7251\n },\n {\n \"tag\": "metallurgist",\n \"popularity\": 7244\n },\n {\n \"tag\": "extraviolet",\n \"popularity\": 7237\n },\n {\n \"tag\": "clinic",\n \"popularity\": 7230\n },\n {\n \"tag\": "skater",\n \"popularity\": 7223\n },\n {\n \"tag\": "linguistic",\n \"popularity\": 7216\n },\n {\n \"tag\": "attacheship",\n \"popularity\": 7209\n },\n {\n \"tag\": "Rachianectes",\n \"popularity\": 7202\n },\n {\n \"tag\": "foliolose",\n \"popularity\": 7195\n },\n {\n \"tag\": "claudetite",\n \"popularity\": 7188\n },\n {\n \"tag\": "aphidian scratching",\n \"popularity\": 7181\n },\n {\n \"tag\": "Carida",\n \"popularity\": 7174\n },\n {\n \"tag\": "tiepin polymicroscope",\n \"popularity\": 7167\n },\n {\n \"tag\": "telpherage",\n \"popularity\": 7160\n },\n {\n \"tag\": "meek",\n \"popularity\": 7153\n },\n {\n \"tag\": "swiftness",\n \"popularity\": 7146\n },\n {\n \"tag\": "gentes",\n \"popularity\": 7139\n },\n {\n \"tag\": "uncommemorated",\n \"popularity\": 7132\n },\n {\n \"tag\": "Lazarus",\n \"popularity\": 7125\n },\n {\n \"tag\": "redivive",\n \"popularity\": 7119\n },\n {\n \"tag\": "nonfebrile",\n \"popularity\": 7112\n },\n {\n \"tag\": "nymphet",\n \"popularity\": 7105\n },\n {\n \"tag\": "areologically",\n \"popularity\": 7098\n },\n {\n \"tag\": "undonkey",\n \"popularity\": 7091\n },\n {\n \"tag\": "projecting",\n \"popularity\": 7084\n },\n {\n \"tag\": "pinnigrade",\n \"popularity\": 7077\n },\n {\n \"tag\": "butylation",\n \"popularity\": 7071\n },\n {\n \"tag\": "philologistic lenticle",\n \"popularity\": 7064\n },\n {\n \"tag\": "nooky",\n \"popularity\": 7057\n },\n {\n \"tag\": "incestuousness",\n \"popularity\": 7050\n },\n {\n \"tag\": "palingenetically",\n \"popularity\": 7043\n },\n {\n \"tag\": "mitochondria",\n \"popularity\": 7037\n },\n {\n \"tag\": "truthify",\n \"popularity\": 7030\n },\n {\n \"tag\": "titanyl",\n \"popularity\": 7023\n },\n {\n \"tag\": "bestride",\n \"popularity\": 7016\n },\n {\n \"tag\": "chende",\n \"popularity\": 7010\n },\n {\n \"tag\": "Chaucerian monophote",\n \"popularity\": 7003\n },\n {\n \"tag\": "cutback",\n \"popularity\": 6996\n },\n {\n \"tag\": "unpatiently",\n \"popularity\": 6989\n },\n {\n \"tag\": "subvitreous",\n \"popularity\": 6983\n },\n {\n \"tag\": "organizable",\n \"popularity\": 6976\n },\n {\n \"tag\": "anniverse uncomprehensible",\n \"popularity\": 6969\n },\n {\n \"tag\": "hyalescence",\n \"popularity\": 6963\n },\n {\n \"tag\": "amniochorial",\n \"popularity\": 6956\n },\n {\n \"tag\": "Corybantian",\n \"popularity\": 6949\n },\n {\n \"tag\": "genocide Scaphitidae",\n \"popularity\": 6943\n },\n {\n \"tag\": "accordionist",\n \"popularity\": 6936\n },\n {\n \"tag\": "becheck",\n \"popularity\": 6930\n },\n {\n \"tag\": "overproduce",\n \"popularity\": 6923\n },\n {\n \"tag\": "unmaniac frijolillo",\n \"popularity\": 6916\n },\n {\n \"tag\": "multisulcated",\n \"popularity\": 6910\n },\n {\n \"tag\": "wennebergite",\n \"popularity\": 6903\n },\n {\n \"tag\": "tautousious mowth",\n \"popularity\": 6897\n },\n {\n \"tag\": "marigold",\n \"popularity\": 6890\n },\n {\n \"tag\": "affray",\n \"popularity\": 6884\n },\n {\n \"tag\": "nonidolatrous",\n \"popularity\": 6877\n },\n {\n \"tag\": "aphrasia",\n \"popularity\": 6871\n },\n {\n \"tag\": "muddlingly",\n \"popularity\": 6864\n },\n {\n \"tag\": "clear",\n \"popularity\": 6858\n },\n {\n \"tag\": "Clitoria",\n \"popularity\": 6851\n },\n {\n \"tag\": "apportionment underwaist",\n \"popularity\": 6845\n },\n {\n \"tag\": "kodakist",\n \"popularity\": 6838\n },\n {\n \"tag\": "Momotidae",\n \"popularity\": 6832\n },\n {\n \"tag\": "cryptovalency",\n \"popularity\": 6825\n },\n {\n \"tag\": "floe",\n \"popularity\": 6819\n },\n {\n \"tag\": "aphagia",\n \"popularity\": 6812\n },\n {\n \"tag\": "brontograph",\n \"popularity\": 6806\n },\n {\n \"tag\": "tubulous",\n \"popularity\": 6799\n },\n {\n \"tag\": "unhorse",\n \"popularity\": 6793\n },\n {\n \"tag\": "chlordane",\n \"popularity\": 6787\n },\n {\n \"tag\": "colloquy brochan",\n \"popularity\": 6780\n },\n {\n \"tag\": "sloosh",\n \"popularity\": 6774\n },\n {\n \"tag\": "battered",\n \"popularity\": 6767\n },\n {\n \"tag\": "monocularity pluriguttulate",\n \"popularity\": 6761\n },\n {\n \"tag\": "chiastoneury",\n \"popularity\": 6755\n },\n {\n \"tag\": "Sanguinaria",\n \"popularity\": 6748\n },\n {\n \"tag\": "confessionary",\n \"popularity\": 6742\n },\n {\n \"tag\": "enzymic",\n \"popularity\": 6736\n },\n {\n \"tag\": "cord",\n \"popularity\": 6729\n },\n {\n \"tag\": "oviducal",\n \"popularity\": 6723\n },\n {\n \"tag\": "crozzle outsea",\n \"popularity\": 6717\n },\n {\n \"tag\": "balladical",\n \"popularity\": 6710\n },\n {\n \"tag\": "uncollectibleness",\n \"popularity\": 6704\n },\n {\n \"tag\": "predorsal",\n \"popularity\": 6698\n },\n {\n \"tag\": "reauthenticate",\n \"popularity\": 6692\n },\n {\n \"tag\": "ravissant",\n \"popularity\": 6685\n },\n {\n \"tag\": "advantageousness",\n \"popularity\": 6679\n },\n {\n \"tag\": "rung",\n \"popularity\": 6673\n },\n {\n \"tag\": "duncedom",\n \"popularity\": 6667\n },\n {\n \"tag\": "hematolite",\n \"popularity\": 6660\n },\n {\n \"tag\": "thisness",\n \"popularity\": 6654\n },\n {\n \"tag\": "mapau",\n \"popularity\": 6648\n },\n {\n \"tag\": "Hecatic",\n \"popularity\": 6642\n },\n {\n \"tag\": "meningoencephalocele",\n \"popularity\": 6636\n },\n {\n \"tag\": "confection sorra",\n \"popularity\": 6630\n },\n {\n \"tag\": "unsedate",\n \"popularity\": 6623\n },\n {\n \"tag\": "meningocerebritis",\n \"popularity\": 6617\n },\n {\n \"tag\": "biopsychological",\n \"popularity\": 6611\n },\n {\n \"tag\": "clavicithern",\n \"popularity\": 6605\n },\n {\n \"tag\": "resun",\n \"popularity\": 6599\n },\n {\n \"tag\": "bayamo",\n \"popularity\": 6593\n },\n {\n \"tag\": "seeableness",\n \"popularity\": 6587\n },\n {\n \"tag\": "hypsidolichocephalism",\n \"popularity\": 6581\n },\n {\n \"tag\": "salivous",\n \"popularity\": 6574\n },\n {\n \"tag\": "neumatize",\n \"popularity\": 6568\n },\n {\n \"tag\": "stree",\n \"popularity\": 6562\n },\n {\n \"tag\": "markshot",\n \"popularity\": 6556\n },\n {\n \"tag\": "phraseologically",\n \"popularity\": 6550\n },\n {\n \"tag\": "yealing",\n \"popularity\": 6544\n },\n {\n \"tag\": "puggy",\n \"popularity\": 6538\n },\n {\n \"tag\": "sexadecimal",\n \"popularity\": 6532\n },\n {\n \"tag\": "unofficerlike",\n \"popularity\": 6526\n },\n {\n \"tag\": "curiosa",\n \"popularity\": 6520\n },\n {\n \"tag\": "pedomotor",\n \"popularity\": 6514\n },\n {\n \"tag\": "astrally",\n \"popularity\": 6508\n },\n {\n \"tag\": "prosomatic",\n \"popularity\": 6502\n },\n {\n \"tag\": "bulletheaded",\n \"popularity\": 6496\n },\n {\n \"tag\": "fortuned",\n \"popularity\": 6490\n },\n {\n \"tag\": "pixy",\n \"popularity\": 6484\n },\n {\n \"tag\": "protectrix",\n \"popularity\": 6478\n },\n {\n \"tag\": "arthritical",\n \"popularity\": 6472\n },\n {\n \"tag\": "coction",\n \"popularity\": 6466\n },\n {\n \"tag\": "Anthropos",\n \"popularity\": 6460\n },\n {\n \"tag\": "runer",\n \"popularity\": 6454\n },\n {\n \"tag\": "prenotify",\n \"popularity\": 6449\n },\n {\n \"tag\": "microspheric gastroparalysis",\n \"popularity\": 6443\n },\n {\n \"tag\": "Jovicentrical",\n \"popularity\": 6437\n },\n {\n \"tag\": "ceratopsid",\n \"popularity\": 6431\n },\n {\n \"tag\": "Theodoric",\n \"popularity\": 6425\n },\n {\n \"tag\": "Pactolus",\n \"popularity\": 6419\n },\n {\n \"tag\": "spawning",\n \"popularity\": 6413\n },\n {\n \"tag\": "nonconfidential",\n \"popularity\": 6407\n },\n {\n \"tag\": "halotrichite infumate",\n \"popularity\": 6402\n },\n {\n \"tag\": "undiscriminatingly",\n \"popularity\": 6396\n },\n {\n \"tag\": "unexasperated",\n \"popularity\": 6390\n },\n {\n \"tag\": "isoeugenol",\n \"popularity\": 6384\n },\n {\n \"tag\": "pressboard",\n \"popularity\": 6378\n },\n {\n \"tag\": "unshrew",\n \"popularity\": 6372\n },\n {\n \"tag\": "huffingly",\n \"popularity\": 6367\n },\n {\n \"tag\": "wagaun",\n \"popularity\": 6361\n },\n {\n \"tag\": "squirt Philistine",\n \"popularity\": 6355\n },\n {\n \"tag\": "kryptic",\n \"popularity\": 6349\n },\n {\n \"tag\": "paraform",\n \"popularity\": 6344\n },\n {\n \"tag\": "preverify",\n \"popularity\": 6338\n },\n {\n \"tag\": "dalar",\n \"popularity\": 6332\n },\n {\n \"tag\": "interdictor appraisingly",\n \"popularity\": 6326\n },\n {\n \"tag\": "chipped",\n \"popularity\": 6321\n },\n {\n \"tag\": "Pteropoda",\n \"popularity\": 6315\n },\n {\n \"tag\": "Bohairic",\n \"popularity\": 6309\n },\n {\n \"tag\": "felting",\n \"popularity\": 6303\n },\n {\n \"tag\": "compurgatorial",\n \"popularity\": 6298\n },\n {\n \"tag\": "unclead",\n \"popularity\": 6292\n },\n {\n \"tag\": "stockish",\n \"popularity\": 6286\n },\n {\n \"tag\": "mulligatawny",\n \"popularity\": 6281\n },\n {\n \"tag\": "Monotheletism",\n \"popularity\": 6275\n },\n {\n \"tag\": "lutanist",\n \"popularity\": 6269\n },\n {\n \"tag\": "gluttonize",\n \"popularity\": 6264\n },\n {\n \"tag\": "hackneyed",\n \"popularity\": 6258\n },\n {\n \"tag\": "yield",\n \"popularity\": 6253\n },\n {\n \"tag\": "sulphonamido",\n \"popularity\": 6247\n },\n {\n \"tag\": "granulative",\n \"popularity\": 6241\n },\n {\n \"tag\": "swingy",\n \"popularity\": 6236\n },\n {\n \"tag\": "Desmidiales",\n \"popularity\": 6230\n },\n {\n \"tag\": "tootlish",\n \"popularity\": 6224\n },\n {\n \"tag\": "unsatisfiedly",\n \"popularity\": 6219\n },\n {\n \"tag\": "burucha",\n \"popularity\": 6213\n },\n {\n \"tag\": "premeditatingly",\n \"popularity\": 6208\n },\n {\n \"tag\": "cowrie",\n \"popularity\": 6202\n },\n {\n \"tag\": "pleurolysis",\n \"popularity\": 6197\n },\n {\n \"tag\": "nationalist",\n \"popularity\": 6191\n },\n {\n \"tag\": "Pholadacea",\n \"popularity\": 6186\n },\n {\n \"tag\": "anakrousis",\n \"popularity\": 6180\n },\n {\n \"tag\": "proctorial",\n \"popularity\": 6175\n },\n {\n \"tag\": "cavillation",\n \"popularity\": 6169\n },\n {\n \"tag\": "cervicobregmatic",\n \"popularity\": 6163\n },\n {\n \"tag\": "interspecific",\n \"popularity\": 6158\n },\n {\n \"tag\": "Teutonity",\n \"popularity\": 6152\n },\n {\n \"tag\": "snakeholing",\n \"popularity\": 6147\n },\n {\n \"tag\": "balcony",\n \"popularity\": 6142\n },\n {\n \"tag\": "latchless",\n \"popularity\": 6136\n },\n {\n \"tag\": "Mithraea",\n \"popularity\": 6131\n },\n {\n \"tag\": "pseudepigraph",\n \"popularity\": 6125\n },\n {\n \"tag\": "flosser",\n \"popularity\": 6120\n },\n {\n \"tag\": "kotyle",\n \"popularity\": 6114\n },\n {\n \"tag\": "outdo",\n \"popularity\": 6109\n },\n {\n \"tag\": "interclerical",\n \"popularity\": 6103\n },\n {\n \"tag\": "aurar",\n \"popularity\": 6098\n },\n {\n \"tag\": "apophyseal",\n \"popularity\": 6093\n },\n {\n \"tag\": "Miro",\n \"popularity\": 6087\n },\n {\n \"tag\": "Priscillian",\n \"popularity\": 6082\n },\n {\n \"tag\": "alluvia",\n \"popularity\": 6076\n },\n {\n \"tag\": "exordize",\n \"popularity\": 6071\n },\n {\n \"tag\": "breakage",\n \"popularity\": 6066\n },\n {\n \"tag\": "unclosable",\n \"popularity\": 6060\n },\n {\n \"tag\": "monocondylous",\n \"popularity\": 6055\n },\n {\n \"tag\": "dyarchy",\n \"popularity\": 6050\n },\n {\n \"tag\": "subchelate",\n \"popularity\": 6044\n },\n {\n \"tag\": "hearsay",\n \"popularity\": 6039\n },\n {\n \"tag\": "prestigiously",\n \"popularity\": 6034\n },\n {\n \"tag\": "unimuscular",\n \"popularity\": 6028\n },\n {\n \"tag\": "lingwort",\n \"popularity\": 6023\n },\n {\n \"tag\": "jealous",\n \"popularity\": 6018\n },\n {\n \"tag\": "artilleryman",\n \"popularity\": 6012\n },\n {\n \"tag\": "phantasmagorially",\n \"popularity\": 6007\n },\n {\n \"tag\": "stagnum",\n \"popularity\": 6002\n },\n {\n \"tag\": "organotropism shatteringly",\n \"popularity\": 5997\n },\n {\n \"tag\": "Mytilus Hebraist",\n \"popularity\": 5991\n },\n {\n \"tag\": "returf",\n \"popularity\": 5986\n },\n {\n \"tag\": "townfolk",\n \"popularity\": 5981\n },\n {\n \"tag\": "propitiative",\n \"popularity\": 5976\n },\n {\n \"tag\": "Anita unsullied",\n \"popularity\": 5970\n },\n {\n \"tag\": "bandoleered",\n \"popularity\": 5965\n },\n {\n \"tag\": "cubby",\n \"popularity\": 5960\n },\n {\n \"tag\": "Hexanchus",\n \"popularity\": 5955\n },\n {\n \"tag\": "circuminsular",\n \"popularity\": 5949\n },\n {\n \"tag\": "chamberletted eumycete",\n \"popularity\": 5944\n },\n {\n \"tag\": "secure",\n \"popularity\": 5939\n },\n {\n \"tag\": "Edwardean",\n \"popularity\": 5934\n },\n {\n \"tag\": "strenth",\n \"popularity\": 5929\n },\n {\n \"tag\": "exhaustless",\n \"popularity\": 5923\n },\n {\n \"tag\": "electioneerer",\n \"popularity\": 5918\n },\n {\n \"tag\": "estoile",\n \"popularity\": 5913\n },\n {\n \"tag\": "redden",\n \"popularity\": 5908\n },\n {\n \"tag\": "solicitee",\n \"popularity\": 5903\n },\n {\n \"tag\": "nonpatented",\n \"popularity\": 5898\n },\n {\n \"tag\": "lemming",\n \"popularity\": 5893\n },\n {\n \"tag\": "marled subalate",\n \"popularity\": 5887\n },\n {\n \"tag\": "premial horizonward",\n \"popularity\": 5882\n },\n {\n \"tag\": "nonrefueling",\n \"popularity\": 5877\n },\n {\n \"tag\": "rupturewort",\n \"popularity\": 5872\n },\n {\n \"tag\": "unfed",\n \"popularity\": 5867\n },\n {\n \"tag\": "empanelment",\n \"popularity\": 5862\n },\n {\n \"tag\": "isoosmosis",\n \"popularity\": 5857\n },\n {\n \"tag\": "jipijapa",\n \"popularity\": 5852\n },\n {\n \"tag\": "Fiji",\n \"popularity\": 5847\n },\n {\n \"tag\": "interferant",\n \"popularity\": 5842\n },\n {\n \"tag\": "reconstitution",\n \"popularity\": 5837\n },\n {\n \"tag\": "dockyardman",\n \"popularity\": 5832\n },\n {\n \"tag\": "dolichopodous",\n \"popularity\": 5826\n },\n {\n \"tag\": "whiteworm",\n \"popularity\": 5821\n },\n {\n \"tag\": "atheistically",\n \"popularity\": 5816\n },\n {\n \"tag\": "nonconcern",\n \"popularity\": 5811\n },\n {\n \"tag\": "scarabaeidoid",\n \"popularity\": 5806\n },\n {\n \"tag\": "triumviri",\n \"popularity\": 5801\n },\n {\n \"tag\": "rakit",\n \"popularity\": 5796\n },\n {\n \"tag\": "leecheater",\n \"popularity\": 5791\n },\n {\n \"tag\": "Arthrostraca",\n \"popularity\": 5786\n },\n {\n \"tag\": "upknit",\n \"popularity\": 5781\n },\n {\n \"tag\": "tymbalon",\n \"popularity\": 5776\n },\n {\n \"tag\": "inventurous",\n \"popularity\": 5771\n },\n {\n \"tag\": "perradiate",\n \"popularity\": 5766\n },\n {\n \"tag\": "seer",\n \"popularity\": 5762\n },\n {\n \"tag\": "Auricularia",\n \"popularity\": 5757\n },\n {\n \"tag\": "wettish exclusivity",\n \"popularity\": 5752\n },\n {\n \"tag\": "arteriosympathectomy",\n \"popularity\": 5747\n },\n {\n \"tag\": "tunlike",\n \"popularity\": 5742\n },\n {\n \"tag\": "cephalocercal",\n \"popularity\": 5737\n },\n {\n \"tag\": "meaninglessness",\n \"popularity\": 5732\n },\n {\n \"tag\": "fountful",\n \"popularity\": 5727\n },\n {\n \"tag\": "appraisement",\n \"popularity\": 5722\n },\n {\n \"tag\": "geniculated",\n \"popularity\": 5717\n },\n {\n \"tag\": "rotator",\n \"popularity\": 5712\n },\n {\n \"tag\": "foremarch biography",\n \"popularity\": 5707\n },\n {\n \"tag\": "arid",\n \"popularity\": 5703\n },\n {\n \"tag\": "inapprehensible",\n \"popularity\": 5698\n },\n {\n \"tag\": "chlorosulphonic",\n \"popularity\": 5693\n },\n {\n \"tag\": "braguette",\n \"popularity\": 5688\n },\n {\n \"tag\": "panophthalmitis",\n \"popularity\": 5683\n },\n {\n \"tag\": "pro objurgatorily",\n \"popularity\": 5678\n },\n {\n \"tag\": "zooplasty",\n \"popularity\": 5673\n },\n {\n \"tag\": "Terebratulidae",\n \"popularity\": 5669\n },\n {\n \"tag\": "Mahran",\n \"popularity\": 5664\n },\n {\n \"tag\": "anthologize merocele",\n \"popularity\": 5659\n },\n {\n \"tag\": "firecracker chiropractic",\n \"popularity\": 5654\n },\n {\n \"tag\": "tenorist",\n \"popularity\": 5649\n },\n {\n \"tag\": "amphitene",\n \"popularity\": 5645\n },\n {\n \"tag\": "silverbush toadstone",\n \"popularity\": 5640\n },\n {\n \"tag\": "entozoological",\n \"popularity\": 5635\n },\n {\n \"tag\": "trustlessness",\n \"popularity\": 5630\n },\n {\n \"tag\": "reassay",\n \"popularity\": 5625\n },\n {\n \"tag\": "chrysalides",\n \"popularity\": 5621\n },\n {\n \"tag\": "truncation",\n \"popularity\": 5616\n },\n {\n \"tag\": "unwavered mausoleal",\n \"popularity\": 5611\n },\n {\n \"tag\": "unserrated",\n \"popularity\": 5606\n },\n {\n \"tag\": "frampler",\n \"popularity\": 5602\n },\n {\n \"tag\": "celestial",\n \"popularity\": 5597\n },\n {\n \"tag\": "depreter",\n \"popularity\": 5592\n },\n {\n \"tag\": "retaliate",\n \"popularity\": 5588\n },\n {\n \"tag\": "decempunctate",\n \"popularity\": 5583\n },\n {\n \"tag\": "submitter",\n \"popularity\": 5578\n },\n {\n \"tag\": "phenothiazine",\n \"popularity\": 5573\n },\n {\n \"tag\": "hobbledehoyish",\n \"popularity\": 5569\n },\n {\n \"tag\": "erraticness",\n \"popularity\": 5564\n },\n {\n \"tag\": "ovariodysneuria",\n \"popularity\": 5559\n },\n {\n \"tag\": "puja",\n \"popularity\": 5555\n },\n {\n \"tag\": "cesspool",\n \"popularity\": 5550\n },\n {\n \"tag\": "sonation",\n \"popularity\": 5545\n },\n {\n \"tag\": "moggan",\n \"popularity\": 5541\n },\n {\n \"tag\": "overjutting",\n \"popularity\": 5536\n },\n {\n \"tag\": "cohobate",\n \"popularity\": 5531\n },\n {\n \"tag\": "Distoma",\n \"popularity\": 5527\n },\n {\n \"tag\": "Plectognathi",\n \"popularity\": 5522\n },\n {\n \"tag\": "dumple caliphate",\n \"popularity\": 5517\n },\n {\n \"tag\": "shiko",\n \"popularity\": 5513\n },\n {\n \"tag\": "downness",\n \"popularity\": 5508\n },\n {\n \"tag\": "whippletree",\n \"popularity\": 5504\n },\n {\n \"tag\": "nymphaeum",\n \"popularity\": 5499\n },\n {\n \"tag\": "there trest",\n \"popularity\": 5494\n },\n {\n \"tag\": "psychrometer",\n \"popularity\": 5490\n },\n {\n \"tag\": "pyelograph",\n \"popularity\": 5485\n },\n {\n \"tag\": "unsalvable",\n \"popularity\": 5481\n },\n {\n \"tag\": "bescreen",\n \"popularity\": 5476\n },\n {\n \"tag\": "cushy",\n \"popularity\": 5471\n },\n {\n \"tag\": "plicatolobate",\n \"popularity\": 5467\n },\n {\n \"tag\": "lakie",\n \"popularity\": 5462\n },\n {\n \"tag\": "anthropodeoxycholic",\n \"popularity\": 5458\n },\n {\n \"tag\": "resatisfaction",\n \"popularity\": 5453\n },\n {\n \"tag\": "unravelment unaccidental",\n \"popularity\": 5449\n },\n {\n \"tag\": "telewriter monogeneous",\n \"popularity\": 5444\n },\n {\n \"tag\": "unsabred",\n \"popularity\": 5440\n },\n {\n \"tag\": "startlingly",\n \"popularity\": 5435\n },\n {\n \"tag\": "Aralia",\n \"popularity\": 5431\n },\n {\n \"tag\": "alamonti",\n \"popularity\": 5426\n },\n {\n \"tag\": "Franklinization",\n \"popularity\": 5422\n },\n {\n \"tag\": "parliament",\n \"popularity\": 5417\n },\n {\n \"tag\": "schoolkeeper",\n \"popularity\": 5413\n },\n {\n \"tag\": "nonsociety",\n \"popularity\": 5408\n },\n {\n \"tag\": "parenthetic",\n \"popularity\": 5404\n },\n {\n \"tag\": "stog",\n \"popularity\": 5399\n },\n {\n \"tag\": "Pristipomidae",\n \"popularity\": 5395\n },\n {\n \"tag\": "exocarp",\n \"popularity\": 5390\n },\n {\n \"tag\": "monaxonial",\n \"popularity\": 5386\n },\n {\n \"tag\": "tramroad",\n \"popularity\": 5381\n },\n {\n \"tag\": "hookah",\n \"popularity\": 5377\n },\n {\n \"tag\": "saccharonic",\n \"popularity\": 5372\n },\n {\n \"tag\": "perimetrium",\n \"popularity\": 5368\n },\n {\n \"tag\": "libelluloid",\n \"popularity\": 5364\n },\n {\n \"tag\": "overrunningly",\n \"popularity\": 5359\n },\n {\n \"tag\": "untwister",\n \"popularity\": 5355\n },\n {\n \"tag\": "ninnyhammer",\n \"popularity\": 5350\n },\n {\n \"tag\": "metranate",\n \"popularity\": 5346\n },\n {\n \"tag\": "sarcoblast",\n \"popularity\": 5341\n },\n {\n \"tag\": "porkish",\n \"popularity\": 5337\n },\n {\n \"tag\": "chauvinistic",\n \"popularity\": 5333\n },\n {\n \"tag\": "sexagesimal",\n \"popularity\": 5328\n },\n {\n \"tag\": "hematogenic",\n \"popularity\": 5324\n },\n {\n \"tag\": "selfpreservatory",\n \"popularity\": 5320\n },\n {\n \"tag\": "myelauxe",\n \"popularity\": 5315\n },\n {\n \"tag\": "triply",\n \"popularity\": 5311\n },\n {\n \"tag\": "metaphysicous",\n \"popularity\": 5306\n },\n {\n \"tag\": "vitrinoid",\n \"popularity\": 5302\n },\n {\n \"tag\": "glabellae",\n \"popularity\": 5298\n },\n {\n \"tag\": "moonlighter",\n \"popularity\": 5293\n },\n {\n \"tag\": "monotheistically epexegetical",\n \"popularity\": 5289\n },\n {\n \"tag\": "pseudolateral",\n \"popularity\": 5285\n },\n {\n \"tag\": "heptamethylene",\n \"popularity\": 5280\n },\n {\n \"tag\": "salvadora",\n \"popularity\": 5276\n },\n {\n \"tag\": "unjovial diphenylthiourea",\n \"popularity\": 5272\n },\n {\n \"tag\": "thievishness",\n \"popularity\": 5268\n },\n {\n \"tag\": "unridable",\n \"popularity\": 5263\n },\n {\n \"tag\": "underhandedly",\n \"popularity\": 5259\n },\n {\n \"tag\": "fungiform",\n \"popularity\": 5255\n },\n {\n \"tag\": "scruffle",\n \"popularity\": 5250\n },\n {\n \"tag\": "preindisposition",\n \"popularity\": 5246\n },\n {\n \"tag\": "Amadis",\n \"popularity\": 5242\n },\n {\n \"tag\": "Culex",\n \"popularity\": 5238\n },\n {\n \"tag\": "churning",\n \"popularity\": 5233\n },\n {\n \"tag\": "imperite",\n \"popularity\": 5229\n },\n {\n \"tag\": "levorotation",\n \"popularity\": 5225\n },\n {\n \"tag\": "barbate",\n \"popularity\": 5221\n },\n {\n \"tag\": "knotwort",\n \"popularity\": 5216\n },\n {\n \"tag\": "gypsiferous",\n \"popularity\": 5212\n },\n {\n \"tag\": "tourmalinic",\n \"popularity\": 5208\n },\n {\n \"tag\": "helleboric",\n \"popularity\": 5204\n },\n {\n \"tag\": "pneumograph",\n \"popularity\": 5199\n },\n {\n \"tag\": "Peltigeraceae",\n \"popularity\": 5195\n },\n {\n \"tag\": "busine",\n \"popularity\": 5191\n },\n {\n \"tag\": "Ailuridae",\n \"popularity\": 5187\n },\n {\n \"tag\": "azotate",\n \"popularity\": 5183\n },\n {\n \"tag\": "unlikable",\n \"popularity\": 5178\n },\n {\n \"tag\": "sloyd",\n \"popularity\": 5174\n },\n {\n \"tag\": "biblioclasm",\n \"popularity\": 5170\n },\n {\n \"tag\": "Seres",\n \"popularity\": 5166\n },\n {\n \"tag\": "unaccurateness",\n \"popularity\": 5162\n },\n {\n \"tag\": "scrollwise",\n \"popularity\": 5157\n },\n {\n \"tag\": "flandowser",\n \"popularity\": 5153\n },\n {\n \"tag\": "unblackened",\n \"popularity\": 5149\n },\n {\n \"tag\": "schistosternia",\n \"popularity\": 5145\n },\n {\n \"tag\": "fuse",\n \"popularity\": 5141\n },\n {\n \"tag\": "narthecal",\n \"popularity\": 5137\n },\n {\n \"tag\": "Cueva",\n \"popularity\": 5133\n },\n {\n \"tag\": "appositeness",\n \"popularity\": 5128\n },\n {\n \"tag\": "proindustrial",\n \"popularity\": 5124\n },\n {\n \"tag\": "dermatorrhoea",\n \"popularity\": 5120\n },\n {\n \"tag\": "oxyurous tendential",\n \"popularity\": 5116\n },\n {\n \"tag\": "isopurpurin",\n \"popularity\": 5112\n },\n {\n \"tag\": "impose",\n \"popularity\": 5108\n },\n {\n \"tag\": "wordsmanship",\n \"popularity\": 5104\n },\n {\n \"tag\": "saturator",\n \"popularity\": 5100\n },\n {\n \"tag\": "Nordicity",\n \"popularity\": 5096\n },\n {\n \"tag\": "interaccuse",\n \"popularity\": 5092\n },\n {\n \"tag\": "acridinic",\n \"popularity\": 5087\n },\n {\n \"tag\": "scholion",\n \"popularity\": 5083\n },\n {\n \"tag\": "pseudoaconitine",\n \"popularity\": 5079\n },\n {\n \"tag\": "doctorial",\n \"popularity\": 5075\n },\n {\n \"tag\": "Etchimin",\n \"popularity\": 5071\n },\n {\n \"tag\": "oliviform",\n \"popularity\": 5067\n },\n {\n \"tag\": "Pele",\n \"popularity\": 5063\n },\n {\n \"tag\": "Chiromantis Progymnasium",\n \"popularity\": 5059\n },\n {\n \"tag\": "toxosis",\n \"popularity\": 5055\n },\n {\n \"tag\": "spadilla",\n \"popularity\": 5051\n },\n {\n \"tag\": "Actinopterygii",\n \"popularity\": 5047\n },\n {\n \"tag\": "untiring",\n \"popularity\": 5043\n },\n {\n \"tag\": "butyral",\n \"popularity\": 5039\n },\n {\n \"tag\": "Gymnoderinae",\n \"popularity\": 5035\n },\n {\n \"tag\": "testudo",\n \"popularity\": 5031\n },\n {\n \"tag\": "frigorify",\n \"popularity\": 5027\n },\n {\n \"tag\": "aliency",\n \"popularity\": 5023\n },\n {\n \"tag\": "jargon",\n \"popularity\": 5019\n },\n {\n \"tag\": "counterservice",\n \"popularity\": 5015\n },\n {\n \"tag\": "isostrychnine",\n \"popularity\": 5011\n },\n {\n \"tag\": "tellership",\n \"popularity\": 5007\n },\n {\n \"tag\": "miscegenetic",\n \"popularity\": 5003\n },\n {\n \"tag\": "sorcer",\n \"popularity\": 4999\n },\n {\n \"tag\": "tilewright",\n \"popularity\": 4995\n },\n {\n \"tag\": "cyanoplastid",\n \"popularity\": 4991\n },\n {\n \"tag\": "fluxionally",\n \"popularity\": 4987\n },\n {\n \"tag\": "proudhearted",\n \"popularity\": 4983\n },\n {\n \"tag\": "blithely",\n \"popularity\": 4979\n },\n {\n \"tag\": "jestproof",\n \"popularity\": 4975\n },\n {\n \"tag\": "jestwise",\n \"popularity\": 4971\n },\n {\n \"tag\": "nonassimilable",\n \"popularity\": 4967\n },\n {\n \"tag\": "compurgation",\n \"popularity\": 4964\n },\n {\n \"tag\": "unhate",\n \"popularity\": 4960\n },\n {\n \"tag\": "haplodonty",\n \"popularity\": 4956\n },\n {\n \"tag\": "cardholder",\n \"popularity\": 4952\n },\n {\n \"tag\": "rainlight megohmmeter overstout",\n \"popularity\": 4948\n },\n {\n \"tag\": "itchless",\n \"popularity\": 4944\n },\n {\n \"tag\": "begiggle",\n \"popularity\": 4940\n },\n {\n \"tag\": "chromatosphere",\n \"popularity\": 4936\n },\n {\n \"tag\": "typicality",\n \"popularity\": 4932\n },\n {\n \"tag\": "overgrown",\n \"popularity\": 4928\n },\n {\n \"tag\": "envolume",\n \"popularity\": 4925\n },\n {\n \"tag\": "pachycholia",\n \"popularity\": 4921\n },\n {\n \"tag\": "passageable",\n \"popularity\": 4917\n },\n {\n \"tag\": "pathopoiesis",\n \"popularity\": 4913\n },\n {\n \"tag\": "overbreak",\n \"popularity\": 4909\n },\n {\n \"tag\": "satyric",\n \"popularity\": 4905\n },\n {\n \"tag\": "unaudited",\n \"popularity\": 4901\n },\n {\n \"tag\": "whimble",\n \"popularity\": 4898\n },\n {\n \"tag\": "pressureless",\n \"popularity\": 4894\n },\n {\n \"tag\": "Selene",\n \"popularity\": 4890\n },\n {\n \"tag\": "slithery",\n \"popularity\": 4886\n },\n {\n \"tag\": "nondisfigurement",\n \"popularity\": 4882\n },\n {\n \"tag\": "overdelicious",\n \"popularity\": 4878\n },\n {\n \"tag\": "Perca",\n \"popularity\": 4875\n },\n {\n \"tag\": "Palladium",\n \"popularity\": 4871\n },\n {\n \"tag\": "insagacity",\n \"popularity\": 4867\n },\n {\n \"tag\": "peristoma",\n \"popularity\": 4863\n },\n {\n \"tag\": "uncreativeness",\n \"popularity\": 4859\n },\n {\n \"tag\": "incomparability surfboarding",\n \"popularity\": 4856\n },\n {\n \"tag\": "bacillar",\n \"popularity\": 4852\n },\n {\n \"tag\": "ulcerative",\n \"popularity\": 4848\n },\n {\n \"tag\": "stychomythia",\n \"popularity\": 4844\n },\n {\n \"tag\": "sesma somatics nonentry",\n \"popularity\": 4840\n },\n {\n \"tag\": "unsepulchred",\n \"popularity\": 4837\n },\n {\n \"tag\": "cephalanthium",\n \"popularity\": 4833\n },\n {\n \"tag\": "Asiaticization",\n \"popularity\": 4829\n },\n {\n \"tag\": "killeen",\n \"popularity\": 4825\n },\n {\n \"tag\": "Pseudococcus",\n \"popularity\": 4822\n },\n {\n \"tag\": "untractable",\n \"popularity\": 4818\n },\n {\n \"tag\": "apolegamic",\n \"popularity\": 4814\n },\n {\n \"tag\": "hyperpnea",\n \"popularity\": 4810\n },\n {\n \"tag\": "martyrolatry",\n \"popularity\": 4807\n },\n {\n \"tag\": "Sarmatic",\n \"popularity\": 4803\n },\n {\n \"tag\": "nonsurface",\n \"popularity\": 4799\n },\n {\n \"tag\": "adjoined",\n \"popularity\": 4796\n },\n {\n \"tag\": "vasiform",\n \"popularity\": 4792\n },\n {\n \"tag\": "tastelessness",\n \"popularity\": 4788\n },\n {\n \"tag\": "rumbo",\n \"popularity\": 4784\n },\n {\n \"tag\": "subdititious",\n \"popularity\": 4781\n },\n {\n \"tag\": "reparticipation",\n \"popularity\": 4777\n },\n {\n \"tag\": "Yorkshireism",\n \"popularity\": 4773\n },\n {\n \"tag\": "outcrow",\n \"popularity\": 4770\n },\n {\n \"tag\": "casserole",\n \"popularity\": 4766\n },\n {\n \"tag\": "semideltaic",\n \"popularity\": 4762\n },\n {\n \"tag\": "freemason",\n \"popularity\": 4759\n },\n {\n \"tag\": "catkin",\n \"popularity\": 4755\n },\n {\n \"tag\": "conscient",\n \"popularity\": 4751\n },\n {\n \"tag\": "reliably",\n \"popularity\": 4748\n },\n {\n \"tag\": "Telembi",\n \"popularity\": 4744\n },\n {\n \"tag\": "hide",\n \"popularity\": 4740\n },\n {\n \"tag\": "social",\n \"popularity\": 4737\n },\n {\n \"tag\": "ichneutic",\n \"popularity\": 4733\n },\n {\n \"tag\": "polypotome blouse pentagrammatic",\n \"popularity\": 4729\n },\n {\n \"tag\": "airdrome pesthole",\n \"popularity\": 4726\n },\n {\n \"tag\": "unportended",\n \"popularity\": 4722\n },\n {\n \"tag\": "sheerly",\n \"popularity\": 4719\n },\n {\n \"tag\": "acardiac",\n \"popularity\": 4715\n },\n {\n \"tag\": "fetor",\n \"popularity\": 4711\n },\n {\n \"tag\": "storax",\n \"popularity\": 4708\n },\n {\n \"tag\": "syndactylic",\n \"popularity\": 4704\n },\n {\n \"tag\": "otiatrics",\n \"popularity\": 4700\n },\n {\n \"tag\": "range",\n \"popularity\": 4697\n },\n {\n \"tag\": "branchway",\n \"popularity\": 4693\n },\n {\n \"tag\": "beatific",\n \"popularity\": 4690\n },\n {\n \"tag\": "Rugosa",\n \"popularity\": 4686\n },\n {\n \"tag\": "rafty",\n \"popularity\": 4682\n },\n {\n \"tag\": "gapy",\n \"popularity\": 4679\n },\n {\n \"tag\": "heterocercal",\n \"popularity\": 4675\n },\n {\n \"tag\": "actinopterygious",\n \"popularity\": 4672\n },\n {\n \"tag\": "glauconite",\n \"popularity\": 4668\n },\n {\n \"tag\": "limbless priest",\n \"popularity\": 4665\n },\n {\n \"tag\": "chrysene",\n \"popularity\": 4661\n },\n {\n \"tag\": "isentropic",\n \"popularity\": 4658\n },\n {\n \"tag\": "lairdess",\n \"popularity\": 4654\n },\n {\n \"tag\": "butterhead choliambic",\n \"popularity\": 4650\n },\n {\n \"tag\": "hexaseme",\n \"popularity\": 4647\n },\n {\n \"tag\": "treeify",\n \"popularity\": 4643\n },\n {\n \"tag\": "coronetted fructify",\n \"popularity\": 4640\n },\n {\n \"tag\": "admiralty",\n \"popularity\": 4636\n },\n {\n \"tag\": "Flosculariidae",\n \"popularity\": 4633\n },\n {\n \"tag\": "limaceous",\n \"popularity\": 4629\n },\n {\n \"tag\": "subterconscious",\n \"popularity\": 4626\n },\n {\n \"tag\": "stayless",\n \"popularity\": 4622\n },\n {\n \"tag\": "psha",\n \"popularity\": 4619\n },\n {\n \"tag\": "Mediterraneanize",\n \"popularity\": 4615\n },\n {\n \"tag\": "impenetrably",\n \"popularity\": 4612\n },\n {\n \"tag\": "Myrmeleonidae",\n \"popularity\": 4608\n },\n {\n \"tag\": "germander",\n \"popularity\": 4605\n },\n {\n \"tag\": "Buri",\n \"popularity\": 4601\n },\n {\n \"tag\": "papyrotamia",\n \"popularity\": 4598\n },\n {\n \"tag\": "Toxylon",\n \"popularity\": 4594\n },\n {\n \"tag\": "batatilla",\n \"popularity\": 4591\n },\n {\n \"tag\": "fabella assumer",\n \"popularity\": 4587\n },\n {\n \"tag\": "macromethod",\n \"popularity\": 4584\n },\n {\n \"tag\": "Blechnum",\n \"popularity\": 4580\n },\n {\n \"tag\": "pantography",\n \"popularity\": 4577\n },\n {\n \"tag\": "seminovel",\n \"popularity\": 4574\n },\n {\n \"tag\": "disembarrassment",\n \"popularity\": 4570\n },\n {\n \"tag\": "bushmaking",\n \"popularity\": 4567\n },\n {\n \"tag\": "neurosis",\n \"popularity\": 4563\n },\n {\n \"tag\": "Animalia",\n \"popularity\": 4560\n },\n {\n \"tag\": "Bernice",\n \"popularity\": 4556\n },\n {\n \"tag\": "wisen",\n \"popularity\": 4553\n },\n {\n \"tag\": "subhymenium",\n \"popularity\": 4549\n },\n {\n \"tag\": "esophagomycosis",\n \"popularity\": 4546\n },\n {\n \"tag\": "wireworks",\n \"popularity\": 4543\n },\n {\n \"tag\": "Sabellidae",\n \"popularity\": 4539\n },\n {\n \"tag\": "fustianish",\n \"popularity\": 4536\n },\n {\n \"tag\": "professively",\n \"popularity\": 4532\n },\n {\n \"tag\": "overcorruptly",\n \"popularity\": 4529\n },\n {\n \"tag\": "overcreep",\n \"popularity\": 4526\n },\n {\n \"tag\": "Castilloa",\n \"popularity\": 4522\n },\n {\n \"tag\": "forelady Georgie",\n \"popularity\": 4519\n },\n {\n \"tag\": "outsider",\n \"popularity\": 4515\n },\n {\n \"tag\": "Enukki",\n \"popularity\": 4512\n },\n {\n \"tag\": "gypsy",\n \"popularity\": 4509\n },\n {\n \"tag\": "Passamaquoddy",\n \"popularity\": 4505\n },\n {\n \"tag\": "reposit",\n \"popularity\": 4502\n },\n {\n \"tag\": "overtenderness",\n \"popularity\": 4499\n },\n {\n \"tag\": "keratome",\n \"popularity\": 4495\n },\n {\n \"tag\": "interclavicular hypermonosyllable Susanna",\n \"popularity\": 4492\n },\n {\n \"tag\": "mispropose",\n \"popularity\": 4489\n },\n {\n \"tag\": "Membranipora",\n \"popularity\": 4485\n },\n {\n \"tag\": "lampad",\n \"popularity\": 4482\n },\n {\n \"tag\": "header",\n \"popularity\": 4479\n },\n {\n \"tag\": "triseriate",\n \"popularity\": 4475\n },\n {\n \"tag\": "distrainment",\n \"popularity\": 4472\n },\n {\n \"tag\": "staphyloplastic",\n \"popularity\": 4469\n },\n {\n \"tag\": "outscour",\n \"popularity\": 4465\n },\n {\n \"tag\": "tallowmaking",\n \"popularity\": 4462\n },\n {\n \"tag\": "plugger",\n \"popularity\": 4459\n },\n {\n \"tag\": "fashionize",\n \"popularity\": 4455\n },\n {\n \"tag\": "puzzle",\n \"popularity\": 4452\n },\n {\n \"tag\": "imbrue",\n \"popularity\": 4449\n },\n {\n \"tag\": "osteoblast",\n \"popularity\": 4445\n },\n {\n \"tag\": "Hydrocores",\n \"popularity\": 4442\n },\n {\n \"tag\": "Lutra",\n \"popularity\": 4439\n },\n {\n \"tag\": "upridge scarfy",\n \"popularity\": 4435\n },\n {\n \"tag\": "ancon taffle",\n \"popularity\": 4432\n },\n {\n \"tag\": "impest",\n \"popularity\": 4429\n },\n {\n \"tag\": "uncollatedness",\n \"popularity\": 4426\n },\n {\n \"tag\": "hypersensitize",\n \"popularity\": 4422\n },\n {\n \"tag\": "autographically",\n \"popularity\": 4419\n },\n {\n \"tag\": "louther",\n \"popularity\": 4416\n },\n {\n \"tag\": "Ollie",\n \"popularity\": 4413\n },\n {\n \"tag\": "recompensate",\n \"popularity\": 4409\n },\n {\n \"tag\": "Shan",\n \"popularity\": 4406\n },\n {\n \"tag\": "brachycnemic",\n \"popularity\": 4403\n },\n {\n \"tag\": "Carinatae",\n \"popularity\": 4399\n },\n {\n \"tag\": "geotherm",\n \"popularity\": 4396\n },\n {\n \"tag\": "sawback",\n \"popularity\": 4393\n },\n {\n \"tag\": "Novatianist",\n \"popularity\": 4390\n },\n {\n \"tag\": "reapproach",\n \"popularity\": 4387\n },\n {\n \"tag\": "myelopoietic",\n \"popularity\": 4383\n },\n {\n \"tag\": "cyanin",\n \"popularity\": 4380\n },\n {\n \"tag\": "unsmutted",\n \"popularity\": 4377\n },\n {\n \"tag\": "nonpapist",\n \"popularity\": 4374\n },\n {\n \"tag\": "transbaikalian",\n \"popularity\": 4370\n },\n {\n \"tag\": "connately",\n \"popularity\": 4367\n },\n {\n \"tag\": "tenderize iterance",\n \"popularity\": 4364\n },\n {\n \"tag\": "hydrostatical",\n \"popularity\": 4361\n },\n {\n \"tag\": "unflag",\n \"popularity\": 4358\n },\n {\n \"tag\": "translate",\n \"popularity\": 4354\n },\n {\n \"tag\": "Scorzonera",\n \"popularity\": 4351\n },\n {\n \"tag\": "uncomforted",\n \"popularity\": 4348\n },\n {\n \"tag\": "risser varied",\n \"popularity\": 4345\n },\n {\n \"tag\": "plumbate",\n \"popularity\": 4342\n },\n {\n \"tag\": "Usneaceae",\n \"popularity\": 4338\n },\n {\n \"tag\": "fohat",\n \"popularity\": 4335\n },\n {\n \"tag\": "slagging",\n \"popularity\": 4332\n },\n {\n \"tag\": "superserious",\n \"popularity\": 4329\n },\n {\n \"tag\": "theocracy",\n \"popularity\": 4326\n },\n {\n \"tag\": "valonia",\n \"popularity\": 4323\n },\n {\n \"tag\": "Sapindales",\n \"popularity\": 4319\n },\n {\n \"tag\": "palaeozoologist",\n \"popularity\": 4316\n },\n {\n \"tag\": "yalb",\n \"popularity\": 4313\n },\n {\n \"tag\": "unviewed",\n \"popularity\": 4310\n },\n {\n \"tag\": "polyarteritis",\n \"popularity\": 4307\n },\n {\n \"tag\": "vectorial",\n \"popularity\": 4304\n },\n {\n \"tag\": "skimpingly",\n \"popularity\": 4301\n },\n {\n \"tag\": "athort",\n \"popularity\": 4297\n },\n {\n \"tag\": "tribofluorescence",\n \"popularity\": 4294\n },\n {\n \"tag\": "benzonitrol",\n \"popularity\": 4291\n },\n {\n \"tag\": "swiller subobtuse subjacency",\n \"popularity\": 4288\n },\n {\n \"tag\": "uncompassed",\n \"popularity\": 4285\n },\n {\n \"tag\": "cacochymia",\n \"popularity\": 4282\n },\n {\n \"tag\": "commensalist butadiene",\n \"popularity\": 4279\n },\n {\n \"tag\": "culpable",\n \"popularity\": 4276\n },\n {\n \"tag\": "contributive",\n \"popularity\": 4273\n },\n {\n \"tag\": "attemperately",\n \"popularity\": 4269\n },\n {\n \"tag\": "spelt",\n \"popularity\": 4266\n },\n {\n \"tag\": "exoneration",\n \"popularity\": 4263\n },\n {\n \"tag\": "antivivisectionist",\n \"popularity\": 4260\n },\n {\n \"tag\": "granitification",\n \"popularity\": 4257\n },\n {\n \"tag\": "palladize",\n \"popularity\": 4254\n },\n {\n \"tag\": "marksmanship",\n \"popularity\": 4251\n },\n {\n \"tag\": "bullydom",\n \"popularity\": 4248\n },\n {\n \"tag\": "spirality",\n \"popularity\": 4245\n },\n {\n \"tag\": "caliginous",\n \"popularity\": 4242\n },\n {\n \"tag\": "reportedly",\n \"popularity\": 4239\n },\n {\n \"tag\": "polyad",\n \"popularity\": 4236\n },\n {\n \"tag\": "arthroempyesis",\n \"popularity\": 4233\n },\n {\n \"tag\": "semibay facultatively",\n \"popularity\": 4229\n },\n {\n \"tag\": "metastatically",\n \"popularity\": 4226\n },\n {\n \"tag\": "prophetically",\n \"popularity\": 4223\n },\n {\n \"tag\": "Linguatula elapid",\n \"popularity\": 4220\n },\n {\n \"tag\": "pyknatom",\n \"popularity\": 4217\n },\n {\n \"tag\": "centimeter",\n \"popularity\": 4214\n },\n {\n \"tag\": "mensurate",\n \"popularity\": 4211\n },\n {\n \"tag\": "migraine",\n \"popularity\": 4208\n },\n {\n \"tag\": "pentagamist",\n \"popularity\": 4205\n },\n {\n \"tag\": "querken",\n \"popularity\": 4202\n },\n {\n \"tag\": "ambulance",\n \"popularity\": 4199\n },\n {\n \"tag\": "Stokavian",\n \"popularity\": 4196\n },\n {\n \"tag\": "malvasian",\n \"popularity\": 4193\n },\n {\n \"tag\": "uncouthsome",\n \"popularity\": 4190\n },\n {\n \"tag\": "readable",\n \"popularity\": 4187\n },\n {\n \"tag\": "enlodge",\n \"popularity\": 4184\n },\n {\n \"tag\": "plasterwise Appendiculariidae perspectograph",\n \"popularity\": 4181\n },\n {\n \"tag\": "inkweed",\n \"popularity\": 4178\n },\n {\n \"tag\": "streep",\n \"popularity\": 4175\n },\n {\n \"tag\": "diadelphian cultured",\n \"popularity\": 4172\n },\n {\n \"tag\": "hymenopterous",\n \"popularity\": 4169\n },\n {\n \"tag\": "unexorableness",\n \"popularity\": 4166\n },\n {\n \"tag\": "cascaron",\n \"popularity\": 4163\n },\n {\n \"tag\": "undaintiness",\n \"popularity\": 4160\n },\n {\n \"tag\": "Curtana",\n \"popularity\": 4157\n },\n {\n \"tag\": "scurvied",\n \"popularity\": 4154\n },\n {\n \"tag\": "molluscoidal",\n \"popularity\": 4151\n },\n {\n \"tag\": "yurt",\n \"popularity\": 4148\n },\n {\n \"tag\": "deciduitis",\n \"popularity\": 4145\n },\n {\n \"tag\": "creephole",\n \"popularity\": 4142\n },\n {\n \"tag\": "quatrefeuille",\n \"popularity\": 4139\n },\n {\n \"tag\": "bicapitate adenomatome",\n \"popularity\": 4136\n },\n {\n \"tag\": "damassin",\n \"popularity\": 4134\n },\n {\n \"tag\": "planching",\n \"popularity\": 4131\n },\n {\n \"tag\": "dashedly inferential",\n \"popularity\": 4128\n },\n {\n \"tag\": "lobe",\n \"popularity\": 4125\n },\n {\n \"tag\": "Hyrachyus",\n \"popularity\": 4122\n },\n {\n \"tag\": "knab",\n \"popularity\": 4119\n },\n {\n \"tag\": "discohexaster",\n \"popularity\": 4116\n },\n {\n \"tag\": "malign",\n \"popularity\": 4113\n },\n {\n \"tag\": "pedagoguism",\n \"popularity\": 4110\n },\n {\n \"tag\": "shrubbery",\n \"popularity\": 4107\n },\n {\n \"tag\": "undershrub",\n \"popularity\": 4104\n },\n {\n \"tag\": "bureaucrat",\n \"popularity\": 4101\n },\n {\n \"tag\": "pantaleon",\n \"popularity\": 4098\n },\n {\n \"tag\": "mesoventral",\n \"popularity\": 4096\n }]'; var log2 = Math.log(2); var tagInfo = tagInfoJSON.parseJSON(function(a, b) { if (a == "popularity") { return Math.log(b) / log2; } else {return b; } }); function makeTagCloud(tagInfo) { var output = '
      '; tagInfo.sort(function(a, b) { if (a.tag < b.tag) { return -1; } else if (a.tag == b.tag) { return 0; } else return 1; }); for (var i = 0; i < tagInfo.length; i++) { var tag = tagInfo[i].tag; var validates = true; for (var j = 0; j < tag.length; j++) { var ch = tag.charCodeAt(j); if (ch < 0x20 || ch >= 0x7f) { validates = false; break; } } if (!validates) continue; var url = "http://example.com/tag/" + tag.replace(" ", "").toLowerCase(); var popularity = tagInfo[i].popularity; var color = 'rgb(' + Math.floor(255 * (popularity - 12) / 20) + ', 0, 255)'; output += ' ' + tag + ' \n'; } output += '
      '; output.replace(" ", " "); return output; } var tagcloud = makeTagCloud(tagInfo); tagInfo = null; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/controlflow-recursive.js0000644000175000017500000000077014433667662030722 0ustar apoapo// The Computer Language Shootout // http://shootout.alioth.debian.org/ // contributed by Isaac Gouy function ack(m,n){ if (m==0) { return n+1; } if (n==0) { return ack(m-1,1); } return ack(m-1, ack(m,n-1) ); } function fib(n) { if (n < 2){ return 1; } return fib(n-2) + fib(n-1); } function tak(x,y,z) { if (y >= x) return z; return tak(tak(x-1,y,z), tak(y-1,z,x), tak(z-1,x,y)); } for ( var i = 3; i <= 5; i++ ) { ack(3,i); fib(17.0+i); tak(3*i+3,2*i+2,i+1); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/3d-morph.js0000644000175000017500000000353714433667662026002 0ustar apoapo/* * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var loops = 15 var nx = 120 var nz = 120 function morph(a, f) { var PI2nx = Math.PI * 8/nx var sin = Math.sin var f30 = -(50 * sin(f*Math.PI*2)) for (var i = 0; i < nz; ++i) { for (var j = 0; j < nx; ++j) { a[3*(i*nx+j)+1] = sin((j-1) * PI2nx ) * -f30 } } } var a = Array() for (var i=0; i < nx*nz*3; ++i) a[i] = 0 for (var i = 0; i < loops; ++i) { morph(a, i/loops) } testOutput = 0; for (var i = 0; i < nx; i++) testOutput += a[3*(i*nx+i)+1]; a = null; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/crypto-sha1.js0000644000175000017500000001430314433667662026514 0ustar apoapo/* * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined * in FIPS PUB 180-1 * Version 2.1a Copyright Paul Johnston 2000 - 2002. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for details. */ /* * Configurable variables. You may need to tweak these to be compatible with * the server-side, but the defaults work in most cases. */ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ /* * These are the functions you'll usually want to call * They take string arguments and return either hex or base-64 encoded strings */ function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} /* * Perform a simple self-test to see if the VM is working */ function sha1_vm_test() { return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; } /* * Calculate the SHA-1 of an array of big-endian words, and a bit length */ function core_sha1(x, len) { /* append padding */ x[len >> 5] |= 0x80 << (24 - len % 32); x[((len + 64 >> 9) << 4) + 15] = len; var w = Array(80); var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; var e = -1009589776; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; var olde = e; for(var j = 0; j < 80; j++) { if(j < 16) w[j] = x[i + j]; else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); e = safe_add(e, olde); } return Array(a, b, c, d, e); } /* * Perform the appropriate triplet combination function for the current * iteration */ function sha1_ft(t, b, c, d) { if(t < 20) return (b & c) | ((~b) & d); if(t < 40) return b ^ c ^ d; if(t < 60) return (b & c) | (b & d) | (c & d); return b ^ c ^ d; } /* * Determine the appropriate additive constant for the current iteration */ function sha1_kt(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; } /* * Calculate the HMAC-SHA1 of a key and some data */ function core_hmac_sha1(key, data) { var bkey = str2binb(key); if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); return core_sha1(opad.concat(hash), 512 + 160); } /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safe_add(x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); } /* * Bitwise rotate a 32-bit number to the left. */ function rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } /* * Convert an 8-bit or 16-bit string to an array of big-endian words * In 8-bit function, characters >255 have their hi-byte silently ignored. */ function str2binb(str) { var bin = Array(); var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz) bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); return bin; } /* * Convert an array of big-endian words to a string */ function binb2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); return str; } /* * Convert an array of big-endian words to a hex string. */ function binb2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); } return str; } /* * Convert an array of big-endian words to a base-64 string */ function binb2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; } var plainText = "Two households, both alike in dignity,\n\ In fair Verona, where we lay our scene,\n\ From ancient grudge break to new mutiny,\n\ Where civil blood makes civil hands unclean.\n\ From forth the fatal loins of these two foes\n\ A pair of star-cross'd lovers take their life;\n\ Whole misadventured piteous overthrows\n\ Do with their death bury their parents' strife.\n\ The fearful passage of their death-mark'd love,\n\ And the continuance of their parents' rage,\n\ Which, but their children's end, nought could remove,\n\ Is now the two hours' traffic of our stage;\n\ The which if you with patient ears attend,\n\ What here shall miss, our toil shall strive to mend."; for (var i = 0; i <4; i++) { plainText += plainText; } var sha1Output = hex_sha1(plainText); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/access-binary-trees.js0000644000175000017500000000234014433667662030203 0ustar apoapo/* The Great Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Isaac Gouy */ function TreeNode(left,right,item){ this.left = left; this.right = right; this.item = item; } TreeNode.prototype.itemCheck = function(){ if (this.left==null) return this.item; else return this.item + this.left.itemCheck() - this.right.itemCheck(); } function bottomUpTree(item,depth){ if (depth>0){ return new TreeNode( bottomUpTree(2*item-1, depth-1) ,bottomUpTree(2*item, depth-1) ,item ); } else { return new TreeNode(null,null,item); } } var ret; for ( var n = 4; n <= 7; n += 1 ) { var minDepth = 4; var maxDepth = Math.max(minDepth + 2, n); var stretchDepth = maxDepth + 1; var check = bottomUpTree(0,stretchDepth).itemCheck(); var longLivedTree = bottomUpTree(0,maxDepth); for (var depth=minDepth; depth<=maxDepth; depth+=2){ var iterations = 1 << (maxDepth - depth + minDepth); check = 0; for (var i=1; i<=iterations; i++){ check += bottomUpTree(i,depth).itemCheck(); check += bottomUpTree(-i,depth).itemCheck(); } } ret = longLivedTree.itemCheck(); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/date-format-xparb.js0000644000175000017500000002743514433667662027671 0ustar apoapo/* * Copyright (C) 2004 Baron Schwartz * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, version 2.1. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ Date.parseFunctions = {count:0}; Date.parseRegexes = []; Date.formatFunctions = {count:0}; Date.prototype.dateFormat = function(format) { if (Date.formatFunctions[format] == null) { Date.createNewFormat(format); } var func = Date.formatFunctions[format]; return this[func](); } Date.createNewFormat = function(format) { var funcName = "format" + Date.formatFunctions.count++; Date.formatFunctions[format] = funcName; var code = "Date.prototype." + funcName + " = function(){return "; var special = false; var ch = ''; for (var i = 0; i < format.length; ++i) { ch = format.charAt(i); if (!special && ch == "\\") { special = true; } else if (special) { special = false; code += "'" + String.escape(ch) + "' + "; } else { code += Date.getFormatCode(ch); } } eval(code.substring(0, code.length - 3) + ";}"); } Date.getFormatCode = function(character) { switch (character) { case "d": return "String.leftPad(this.getDate(), 2, '0') + "; case "D": return "Date.dayNames[this.getDay()].substring(0, 3) + "; case "j": return "this.getDate() + "; case "l": return "Date.dayNames[this.getDay()] + "; case "S": return "this.getSuffix() + "; case "w": return "this.getDay() + "; case "z": return "this.getDayOfYear() + "; case "W": return "this.getWeekOfYear() + "; case "F": return "Date.monthNames[this.getMonth()] + "; case "m": return "String.leftPad(this.getMonth() + 1, 2, '0') + "; case "M": return "Date.monthNames[this.getMonth()].substring(0, 3) + "; case "n": return "(this.getMonth() + 1) + "; case "t": return "this.getDaysInMonth() + "; case "L": return "(this.isLeapYear() ? 1 : 0) + "; case "Y": return "this.getFullYear() + "; case "y": return "('' + this.getFullYear()).substring(2, 4) + "; case "a": return "(this.getHours() < 12 ? 'am' : 'pm') + "; case "A": return "(this.getHours() < 12 ? 'AM' : 'PM') + "; case "g": return "((this.getHours() %12) ? this.getHours() % 12 : 12) + "; case "G": return "this.getHours() + "; case "h": return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + "; case "H": return "String.leftPad(this.getHours(), 2, '0') + "; case "i": return "String.leftPad(this.getMinutes(), 2, '0') + "; case "s": return "String.leftPad(this.getSeconds(), 2, '0') + "; case "O": return "this.getGMTOffset() + "; case "T": return "this.getTimezone() + "; case "Z": return "(this.getTimezoneOffset() * -60) + "; default: return "'" + String.escape(character) + "' + "; } } Date.parseDate = function(input, format) { if (Date.parseFunctions[format] == null) { Date.createParser(format); } var func = Date.parseFunctions[format]; return Date[func](input); } Date.createParser = function(format) { var funcName = "parse" + Date.parseFunctions.count++; var regexNum = Date.parseRegexes.length; var currentGroup = 1; Date.parseFunctions[format] = funcName; var code = "Date." + funcName + " = function(input){\n" + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n" + "var d = new Date();\n" + "y = d.getFullYear();\n" + "m = d.getMonth();\n" + "d = d.getDate();\n" + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n" + "if (results && results.length > 0) {" var regex = ""; var special = false; var ch = ''; for (var i = 0; i < format.length; ++i) { ch = format.charAt(i); if (!special && ch == "\\") { special = true; } else if (special) { special = false; regex += String.escape(ch); } else { obj = Date.formatCodeToRegex(ch, currentGroup); currentGroup += obj.g; regex += obj.s; if (obj.g && obj.c) { code += obj.c; } } } code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n" + "{return new Date(y, m, d, h, i, s);}\n" + "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n" + "{return new Date(y, m, d, h, i);}\n" + "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n" + "{return new Date(y, m, d, h);}\n" + "else if (y > 0 && m >= 0 && d > 0)\n" + "{return new Date(y, m, d);}\n" + "else if (y > 0 && m >= 0)\n" + "{return new Date(y, m);}\n" + "else if (y > 0)\n" + "{return new Date(y);}\n" + "}return null;}"; Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$"); eval(code); } Date.formatCodeToRegex = function(character, currentGroup) { switch (character) { case "D": return {g:0, c:null, s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"}; case "j": case "d": return {g:1, c:"d = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{1,2})"}; case "l": return {g:0, c:null, s:"(?:" + Date.dayNames.join("|") + ")"}; case "S": return {g:0, c:null, s:"(?:st|nd|rd|th)"}; case "w": return {g:0, c:null, s:"\\d"}; case "z": return {g:0, c:null, s:"(?:\\d{1,3})"}; case "W": return {g:0, c:null, s:"(?:\\d{2})"}; case "F": return {g:1, c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n", s:"(" + Date.monthNames.join("|") + ")"}; case "M": return {g:1, c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n", s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"}; case "n": case "m": return {g:1, c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n", s:"(\\d{1,2})"}; case "t": return {g:0, c:null, s:"\\d{1,2}"}; case "L": return {g:0, c:null, s:"(?:1|0)"}; case "Y": return {g:1, c:"y = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{4})"}; case "y": return {g:1, c:"var ty = parseInt(results[" + currentGroup + "], 10);\n" + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", s:"(\\d{1,2})"}; case "a": return {g:1, c:"if (results[" + currentGroup + "] == 'am') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s:"(am|pm)"}; case "A": return {g:1, c:"if (results[" + currentGroup + "] == 'AM') {\n" + "if (h == 12) { h = 0; }\n" + "} else { if (h < 12) { h += 12; }}", s:"(AM|PM)"}; case "g": case "G": case "h": case "H": return {g:1, c:"h = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{1,2})"}; case "i": return {g:1, c:"i = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{2})"}; case "s": return {g:1, c:"s = parseInt(results[" + currentGroup + "], 10);\n", s:"(\\d{2})"}; case "O": return {g:0, c:null, s:"[+-]\\d{4}"}; case "T": return {g:0, c:null, s:"[A-Z]{3}"}; case "Z": return {g:0, c:null, s:"[+-]\\d{1,5}"}; default: return {g:0, c:null, s:String.escape(character)}; } } Date.prototype.getTimezone = function() { return this.toString().replace( /^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace( /^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3"); } Date.prototype.getGMTOffset = function() { return (this.getTimezoneOffset() > 0 ? "-" : "+") + String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0") + String.leftPad(this.getTimezoneOffset() % 60, 2, "0"); } Date.prototype.getDayOfYear = function() { var num = 0; Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; for (var i = 0; i < this.getMonth(); ++i) { num += Date.daysInMonth[i]; } return num + this.getDate() - 1; } Date.prototype.getWeekOfYear = function() { // Skip to Thursday of this week var now = this.getDayOfYear() + (4 - this.getDay()); // Find the first Thursday of the year var jan1 = new Date(this.getFullYear(), 0, 1); var then = (7 - jan1.getDay() + 4); document.write(then); return String.leftPad(((now - then) / 7) + 1, 2, "0"); } Date.prototype.isLeapYear = function() { var year = this.getFullYear(); return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))); } Date.prototype.getFirstDayOfMonth = function() { var day = (this.getDay() - (this.getDate() - 1)) % 7; return (day < 0) ? (day + 7) : day; } Date.prototype.getLastDayOfMonth = function() { var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7; return (day < 0) ? (day + 7) : day; } Date.prototype.getDaysInMonth = function() { Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; return Date.daysInMonth[this.getMonth()]; } Date.prototype.getSuffix = function() { switch (this.getDate()) { case 1: case 21: case 31: return "st"; case 2: case 22: return "nd"; case 3: case 23: return "rd"; default: return "th"; } } String.escape = function(string) { return string.replace(/('|\\)/g, "\\$1"); } String.leftPad = function (val, size, ch) { var result = new String(val); if (ch == null) { ch = " "; } while (result.length < size) { result = ch + result; } return result; } Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]; Date.monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; Date.dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; Date.y2kYear = 50; Date.monthNumbers = { Jan:0, Feb:1, Mar:2, Apr:3, May:4, Jun:5, Jul:6, Aug:7, Sep:8, Oct:9, Nov:10, Dec:11}; Date.patterns = { ISO8601LongPattern:"Y-m-d H:i:s", ISO8601ShortPattern:"Y-m-d", ShortDatePattern: "n/j/Y", LongDatePattern: "l, F d, Y", FullDateTimePattern: "l, F d, Y g:i:s A", MonthDayPattern: "F d", ShortTimePattern: "g:i A", LongTimePattern: "g:i:s A", SortableDateTimePattern: "Y-m-d\\TH:i:s", UniversalSortableDateTimePattern: "Y-m-d H:i:sO", YearMonthPattern: "F, Y"}; var date = new Date("1/1/2007 1:11:11"); for (i = 0; i < 4000; ++i) { var shortFormat = date.dateFormat("Y-m-d"); var longFormat = date.dateFormat("l, F d, Y g:i:s A"); date.setTime(date.getTime() + 84266956); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/access-nbody.js0000644000175000017500000001006714433667662026717 0ustar apoapo/* The Great Computer Language Shootout http://shootout.alioth.debian.org/ contributed by Isaac Gouy */ var PI = 3.141592653589793; var SOLAR_MASS = 4 * PI * PI; var DAYS_PER_YEAR = 365.24; function Body(x,y,z,vx,vy,vz,mass){ this.x = x; this.y = y; this.z = z; this.vx = vx; this.vy = vy; this.vz = vz; this.mass = mass; } Body.prototype.offsetMomentum = function(px,py,pz) { this.vx = -px / SOLAR_MASS; this.vy = -py / SOLAR_MASS; this.vz = -pz / SOLAR_MASS; return this; } function Jupiter(){ return new Body( 4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01, 1.66007664274403694e-03 * DAYS_PER_YEAR, 7.69901118419740425e-03 * DAYS_PER_YEAR, -6.90460016972063023e-05 * DAYS_PER_YEAR, 9.54791938424326609e-04 * SOLAR_MASS ); } function Saturn(){ return new Body( 8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01, -2.76742510726862411e-03 * DAYS_PER_YEAR, 4.99852801234917238e-03 * DAYS_PER_YEAR, 2.30417297573763929e-05 * DAYS_PER_YEAR, 2.85885980666130812e-04 * SOLAR_MASS ); } function Uranus(){ return new Body( 1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01, 2.96460137564761618e-03 * DAYS_PER_YEAR, 2.37847173959480950e-03 * DAYS_PER_YEAR, -2.96589568540237556e-05 * DAYS_PER_YEAR, 4.36624404335156298e-05 * SOLAR_MASS ); } function Neptune(){ return new Body( 1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01, 2.68067772490389322e-03 * DAYS_PER_YEAR, 1.62824170038242295e-03 * DAYS_PER_YEAR, -9.51592254519715870e-05 * DAYS_PER_YEAR, 5.15138902046611451e-05 * SOLAR_MASS ); } function Sun(){ return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS); } function NBodySystem(bodies){ this.bodies = bodies; var px = 0.0; var py = 0.0; var pz = 0.0; var size = this.bodies.length; for (var i=0; i Math.abs(normal[1])) if (Math.abs(normal[0]) > Math.abs(normal[2])) this.axis = 0; else this.axis = 2; else if (Math.abs(normal[1]) > Math.abs(normal[2])) this.axis = 1; else this.axis = 2; var u = (this.axis + 1) % 3; var v = (this.axis + 2) % 3; var u1 = edge1[u]; var v1 = edge1[v]; var u2 = edge2[u]; var v2 = edge2[v]; this.normal = normalise(normal); this.nu = normal[u] / normal[this.axis]; this.nv = normal[v] / normal[this.axis]; this.nd = dot(normal, p1) / normal[this.axis]; var det = u1 * v2 - v1 * u2; this.eu = p1[u]; this.ev = p1[v]; this.nu1 = u1 / det; this.nv1 = -v1 / det; this.nu2 = v2 / det; this.nv2 = -u2 / det; this.material = [0.7, 0.7, 0.7]; } Triangle.prototype.intersect = function(orig, dir, near, far) { var u = (this.axis + 1) % 3; var v = (this.axis + 2) % 3; var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v]; var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d; if (t < near || t > far) return null; var Pu = orig[u] + t * dir[u] - this.eu; var Pv = orig[v] + t * dir[v] - this.ev; var a2 = Pv * this.nu1 + Pu * this.nv1; if (a2 < 0) return null; var a3 = Pu * this.nu2 + Pv * this.nv2; if (a3 < 0) return null; if ((a2 + a3) > 1) return null; return t; } function Scene(a_triangles) { this.triangles = a_triangles; this.lights = []; this.ambient = [0,0,0]; this.background = [0.8,0.8,1]; } var zero = new Array(0,0,0); Scene.prototype.intersect = function(origin, dir, near, far) { var closest = null; for (i = 0; i < this.triangles.length; i++) { var triangle = this.triangles[i]; var d = triangle.intersect(origin, dir, near, far); if (d == null || d > far || d < near) continue; far = d; closest = triangle; } if (!closest) return [this.background[0],this.background[1],this.background[2]]; var normal = closest.normal; var hit = add(origin, scale(dir, far)); if (dot(dir, normal) > 0) normal = [-normal[0], -normal[1], -normal[2]]; var colour = null; if (closest.shader) { colour = closest.shader(closest, hit, dir); } else { colour = closest.material; } // do reflection var reflected = null; if (colour.reflection > 0.001) { var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir); reflected = this.intersect(hit, reflection, 0.0001, 1000000); if (colour.reflection >= 0.999999) return reflected; } var l = [this.ambient[0], this.ambient[1], this.ambient[2]]; for (var i = 0; i < this.lights.length; i++) { var light = this.lights[i]; var toLight = sub(light, hit); var distance = lengthVector(toLight); scaleVector(toLight, 1.0/distance); distance -= 0.0001; if (this.blocked(hit, toLight, distance)) continue; var nl = dot(normal, toLight); if (nl > 0) addVector(l, scale(light.colour, nl)); } l = scalev(l, colour); if (reflected) { l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection)); } return l; } Scene.prototype.blocked = function(O, D, far) { var near = 0.0001; var closest = null; for (i = 0; i < this.triangles.length; i++) { var triangle = this.triangles[i]; var d = triangle.intersect(O, D, near, far); if (d == null || d > far || d < near) continue; return true; } return false; } // this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where // that somewhere is function Camera(origin, lookat, up) { var zaxis = normaliseVector(subVector(lookat, origin)); var xaxis = normaliseVector(cross(up, zaxis)); var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis))); var m = new Array(16); m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2]; m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2]; m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2]; invertMatrix(m); m[3] = 0; m[7] = 0; m[11] = 0; this.origin = origin; this.directions = new Array(4); this.directions[0] = normalise([-0.7, 0.7, 1]); this.directions[1] = normalise([ 0.7, 0.7, 1]); this.directions[2] = normalise([ 0.7, -0.7, 1]); this.directions[3] = normalise([-0.7, -0.7, 1]); this.directions[0] = transformMatrix(m, this.directions[0]); this.directions[1] = transformMatrix(m, this.directions[1]); this.directions[2] = transformMatrix(m, this.directions[2]); this.directions[3] = transformMatrix(m, this.directions[3]); } Camera.prototype.generateRayPair = function(y) { rays = new Array(new Object(), new Object()); rays[0].origin = this.origin; rays[1].origin = this.origin; rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y)); rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y)); return rays; } function renderRows(camera, scene, pixels, width, height, starty, stopy) { for (var y = starty; y < stopy; y++) { var rays = camera.generateRayPair(y / height); for (var x = 0; x < width; x++) { var xp = x / width; var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp)); var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp))); var l = scene.intersect(origin, dir); pixels[y][x] = l; } } } Camera.prototype.render = function(scene, pixels, width, height) { var cam = this; var row = 0; renderRows(cam, scene, pixels, width, height, 0, height); } function raytraceScene() { var startDate = new Date().getTime(); var numTriangles = 2 * 6; var triangles = new Array();//numTriangles); var tfl = createVector(-10, 10, -10); var tfr = createVector( 10, 10, -10); var tbl = createVector(-10, 10, 10); var tbr = createVector( 10, 10, 10); var bfl = createVector(-10, -10, -10); var bfr = createVector( 10, -10, -10); var bbl = createVector(-10, -10, 10); var bbr = createVector( 10, -10, 10); // cube!!! // front var i = 0; triangles[i++] = new Triangle(tfl, tfr, bfr); triangles[i++] = new Triangle(tfl, bfr, bfl); // back triangles[i++] = new Triangle(tbl, tbr, bbr); triangles[i++] = new Triangle(tbl, bbr, bbl); // triangles[i-1].material = [0.7,0.2,0.2]; // triangles[i-1].material.reflection = 0.8; // left triangles[i++] = new Triangle(tbl, tfl, bbl); // triangles[i-1].reflection = 0.6; triangles[i++] = new Triangle(tfl, bfl, bbl); // triangles[i-1].reflection = 0.6; // right triangles[i++] = new Triangle(tbr, tfr, bbr); triangles[i++] = new Triangle(tfr, bfr, bbr); // top triangles[i++] = new Triangle(tbl, tbr, tfr); triangles[i++] = new Triangle(tbl, tfr, tfl); // bottom triangles[i++] = new Triangle(bbl, bbr, bfr); triangles[i++] = new Triangle(bbl, bfr, bfl); //Floor!!!! var green = createVector(0.0, 0.4, 0.0); var grey = createVector(0.4, 0.4, 0.4); grey.reflection = 1.0; var floorShader = function(tri, pos, view) { var x = ((pos[0]/32) % 2 + 2) % 2; var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2; if (x < 1 != z < 1) { //in the real world we use the fresnel term... // var angle = 1-dot(view, tri.normal); // angle *= angle; // angle *= angle; // angle *= angle; //grey.reflection = angle; return grey; } else return green; } var ffl = createVector(-1000, -30, -1000); var ffr = createVector( 1000, -30, -1000); var fbl = createVector(-1000, -30, 1000); var fbr = createVector( 1000, -30, 1000); triangles[i++] = new Triangle(fbl, fbr, ffr); triangles[i-1].shader = floorShader; triangles[i++] = new Triangle(fbl, ffr, ffl); triangles[i-1].shader = floorShader; var _scene = new Scene(triangles); _scene.lights[0] = createVector(20, 38, -22); _scene.lights[0].colour = createVector(0.7, 0.3, 0.3); _scene.lights[1] = createVector(-23, 40, 17); _scene.lights[1].colour = createVector(0.7, 0.3, 0.3); _scene.lights[2] = createVector(23, 20, 17); _scene.lights[2].colour = createVector(0.7, 0.7, 0.7); _scene.ambient = createVector(0.1, 0.1, 0.1); // _scene.background = createVector(0.7, 0.7, 1.0); var size = 30; var pixels = new Array(); for (var y = 0; y < size; y++) { pixels[y] = new Array(); for (var x = 0; x < size; x++) { pixels[y][x] = 0; } } var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0)); _camera.render(_scene, pixels, size, size); return pixels; } function arrayToCanvasCommands(pixels) { var s = '\nvar pixels = ['; var size = 30; for (var y = 0; y < size; y++) { s += "["; for (var x = 0; x < size; x++) { s += "[" + pixels[y][x] + "],"; } s+= "],"; } s += '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\ \n\ \n\ var size = 30;\n\ canvas.fillStyle = "red";\n\ canvas.fillRect(0, 0, size, size);\n\ canvas.scale(1, -1);\n\ canvas.translate(0, -size);\n\ \n\ if (!canvas.setFillColor)\n\ canvas.setFillColor = function(r, g, b, a) {\n\ this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\ }\n\ \n\ for (var y = 0; y < size; y++) {\n\ for (var x = 0; x < size; x++) {\n\ var l = pixels[y][x];\n\ canvas.setFillColor(l[0], l[1], l[2], 1);\n\ canvas.fillRect(x, y, 1, 1);\n\ }\n\ }'; return s; } testOutput = arrayToCanvasCommands(raytraceScene()); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/regexp-dna.js0000644000175000017500000032270614433667662026405 0ustar apoapo// The Computer Language Shootout // http://shootout.alioth.debian.org/ // // contributed by Jesse Millikan // Base on the Ruby version by jose fco. gonzalez var l; var dnaInput = ">ONE Homo sapiens alu\n\ GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ GAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATC\n\ GCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGC\n\ GGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGG\n\ TCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAA\n\ AAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAG\n\ GAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACT\n\ CCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCC\n\ TGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAG\n\ ACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGC\n\ GTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGA\n\ ACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGA\n\ CAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCA\n\ CTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCA\n\ ACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCG\n\ CCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG\n\ AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTC\n\ CGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCG\n\ AGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACC\n\ CCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAG\n\ CTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAG\n\ CCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGG\n\ CCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATC\n\ ACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAA\n\ AAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGC\n\ TGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCC\n\ ACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGG\n\ CTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGG\n\ AGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATT\n\ AGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAA\n\ TCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGC\n\ CTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAA\n\ TCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAG\n\ CCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGT\n\ GGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCG\n\ GGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAG\n\ CGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTG\n\ GGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATG\n\ GTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGT\n\ AATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTT\n\ GCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCT\n\ CAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCG\n\ GGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTC\n\ TCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACT\n\ CGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAG\n\ ATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGG\n\ CGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTG\n\ AGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATA\n\ CAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGG\n\ CAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGC\n\ ACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCAC\n\ GCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTC\n\ GAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCG\n\ GGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCT\n\ TGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGG\n\ CGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCA\n\ GCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGG\n\ CCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGC\n\ GCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGG\n\ CGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGA\n\ CTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGG\n\ CCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAA\n\ ACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCC\n\ CAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGT\n\ GAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAA\n\ AGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGG\n\ ATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTAC\n\ TAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGA\n\ GGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGC\n\ GCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGG\n\ TGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC\n\ AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAA\n\ ATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGA\n\ GAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC\n\ AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTG\n\ TAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGAC\n\ CAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGT\n\ GGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAAC\n\ CCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACA\n\ GAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACT\n\ TTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAAC\n\ ATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCC\n\ TGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAG\n\ GTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCG\n\ TCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAG\n\ GCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCC\n\ GTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCT\n\ ACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCC\n\ GAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCC\n\ GGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCAC\n\ CTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAA\n\ ATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTG\n\ AGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCAC\n\ TGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCT\n\ CACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAG\n\ TTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAG\n\ CCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATC\n\ GCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCT\n\ GGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATC\n\ CCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCC\n\ TGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGG\n\ CGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG\n\ AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCG\n\ AGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGG\n\ AGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGT\n\ GAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAA\n\ TCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGC\n\ AGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCA\n\ AAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGG\n\ CGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTC\n\ TACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCG\n\ GGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGAT\n\ CGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCG\n\ CGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAG\n\ GTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACA\n\ AAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCA\n\ GGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCAC\n\ TCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGC\n\ CTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGA\n\ GACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGG\n\ CGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTG\n\ AACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCG\n\ ACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGC\n\ ACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCC\n\ AACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGC\n\ GCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCG\n\ GAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACT\n\ CCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCC\n\ GAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAAC\n\ CCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA\n\ GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGA\n\ GCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAG\n\ GCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGAT\n\ CACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTA\n\ AAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGG\n\ CTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGC\n\ CACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTG\n\ GCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAG\n\ GAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAAT\n\ TAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGA\n\ ATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAG\n\ CCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTA\n\ ATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCA\n\ GCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGG\n\ TGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCC\n\ GGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGA\n\ GCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT\n\ GGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT\n\ GGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTG\n\ TAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGT\n\ TGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTC\n\ TCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGC\n\ GGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGT\n\ CTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTAC\n\ TCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGA\n\ GATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGG\n\ GCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCT\n\ GAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT\n\ ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAG\n\ GCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTG\n\ CACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCA\n\ CGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTT\n\ CGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCC\n\ GGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGC\n\ TTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGG\n\ GCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCC\n\ AGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTG\n\ GCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCG\n\ CGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAG\n\ GCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAG\n\ ACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAG\n\ GCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGA\n\ AACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATC\n\ CCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAG\n\ TGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAA\n\ AAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCG\n\ GATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTA\n\ CTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGG\n\ AGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCG\n\ CGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCG\n\ GTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGT\n\ CAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAA\n\ AATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGG\n\ AGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTC\n\ CAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCT\n\ GTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA\n\ CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG\n\ TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAA\n\ CCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGAC\n\ AGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCAC\n\ TTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAA\n\ CATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGC\n\ CTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGA\n\ GGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCC\n\ GTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGA\n\ GGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCC\n\ CGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGC\n\ TACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGC\n\ CGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGC\n\ CGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCA\n\ CCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAA\n\ AATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCT\n\ GAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCA\n\ CTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGC\n\ TCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGA\n\ GTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTA\n\ GCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAAT\n\ CGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCC\n\ TGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAAT\n\ CCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGC\n\ CTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTG\n\ GCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGG\n\ GAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGC\n\ GAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG\n\ GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGG\n\ TGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTA\n\ ATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTG\n\ CAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTC\n\ AAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGG\n\ GCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCT\n\ CTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTC\n\ GGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGA\n\ TCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGC\n\ GCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGA\n\ GGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATAC\n\ AAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGC\n\ AGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCA\n\ CTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACG\n\ CCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCG\n\ AGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGG\n\ GCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTT\n\ GAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGC\n\ GACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAG\n\ CACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGC\n\ CAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCG\n\ CGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGC\n\ GGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGAC\n\ TCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGC\n\ CGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAA\n\ CCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCC\n\ AGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTG\n\ AGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\n\ GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ GAGGCTGAGGCAGGAGAATC\n\ >TWO IUB ambiguity codes\n\ cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg\n\ tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa\n\ NtactMcSMtYtcMgRtacttctWBacgaaatatagScDtttgaagacacatagtVgYgt\n\ cattHWtMMWcStgttaggKtSgaYaaccWStcgBttgcgaMttBYatcWtgacaYcaga\n\ gtaBDtRacttttcWatMttDBcatWtatcttactaBgaYtcttgttttttttYaaScYa\n\ HgtgttNtSatcMtcVaaaStccRcctDaataataStcYtRDSaMtDttgttSagtRRca\n\ tttHatSttMtWgtcgtatSSagactYaaattcaMtWatttaSgYttaRgKaRtccactt\n\ tattRggaMcDaWaWagttttgacatgttctacaaaRaatataataaMttcgDacgaSSt\n\ acaStYRctVaNMtMgtaggcKatcttttattaaaaagVWaHKYagtttttatttaacct\n\ tacgtVtcVaattVMBcttaMtttaStgacttagattWWacVtgWYagWVRctDattBYt\n\ gtttaagaagattattgacVatMaacattVctgtBSgaVtgWWggaKHaatKWcBScSWa\n\ accRVacacaaactaccScattRatatKVtactatatttHttaagtttSKtRtacaaagt\n\ RDttcaaaaWgcacatWaDgtDKacgaacaattacaRNWaatHtttStgttattaaMtgt\n\ tgDcgtMgcatBtgcttcgcgaDWgagctgcgaggggVtaaScNatttacttaatgacag\n\ cccccacatYScaMgtaggtYaNgttctgaMaacNaMRaacaaacaKctacatagYWctg\n\ ttWaaataaaataRattagHacacaagcgKatacBttRttaagtatttccgatctHSaat\n\ actcNttMaagtattMtgRtgaMgcataatHcMtaBSaRattagttgatHtMttaaKagg\n\ YtaaBataSaVatactWtataVWgKgttaaaacagtgcgRatatacatVtHRtVYataSa\n\ KtWaStVcNKHKttactatccctcatgWHatWaRcttactaggatctataDtDHBttata\n\ aaaHgtacVtagaYttYaKcctattcttcttaataNDaaggaaaDYgcggctaaWSctBa\n\ aNtgctggMBaKctaMVKagBaactaWaDaMaccYVtNtaHtVWtKgRtcaaNtYaNacg\n\ gtttNattgVtttctgtBaWgtaattcaagtcaVWtactNggattctttaYtaaagccgc\n\ tcttagHVggaYtgtNcDaVagctctctKgacgtatagYcctRYHDtgBattDaaDgccK\n\ tcHaaStttMcctagtattgcRgWBaVatHaaaataYtgtttagMDMRtaataaggatMt\n\ ttctWgtNtgtgaaaaMaatatRtttMtDgHHtgtcattttcWattRSHcVagaagtacg\n\ ggtaKVattKYagactNaatgtttgKMMgYNtcccgSKttctaStatatNVataYHgtNa\n\ BKRgNacaactgatttcctttaNcgatttctctataScaHtataRagtcRVttacDSDtt\n\ aRtSatacHgtSKacYagttMHtWataggatgactNtatSaNctataVtttRNKtgRacc\n\ tttYtatgttactttttcctttaaacatacaHactMacacggtWataMtBVacRaSaatc\n\ cgtaBVttccagccBcttaRKtgtgcctttttRtgtcagcRttKtaaacKtaaatctcac\n\ aattgcaNtSBaaccgggttattaaBcKatDagttactcttcattVtttHaaggctKKga\n\ tacatcBggScagtVcacattttgaHaDSgHatRMaHWggtatatRgccDttcgtatcga\n\ aacaHtaagttaRatgaVacttagattVKtaaYttaaatcaNatccRttRRaMScNaaaD\n\ gttVHWgtcHaaHgacVaWtgttScactaagSgttatcttagggDtaccagWattWtRtg\n\ ttHWHacgattBtgVcaYatcggttgagKcWtKKcaVtgaYgWctgYggVctgtHgaNcV\n\ taBtWaaYatcDRaaRtSctgaHaYRttagatMatgcatttNattaDttaattgttctaa\n\ ccctcccctagaWBtttHtBccttagaVaatMcBHagaVcWcagBVttcBtaYMccagat\n\ gaaaaHctctaacgttagNWRtcggattNatcRaNHttcagtKttttgWatWttcSaNgg\n\ gaWtactKKMaacatKatacNattgctWtatctaVgagctatgtRaHtYcWcttagccaa\n\ tYttWttaWSSttaHcaaaaagVacVgtaVaRMgattaVcDactttcHHggHRtgNcctt\n\ tYatcatKgctcctctatVcaaaaKaaaagtatatctgMtWtaaaacaStttMtcgactt\n\ taSatcgDataaactaaacaagtaaVctaggaSccaatMVtaaSKNVattttgHccatca\n\ cBVctgcaVatVttRtactgtVcaattHgtaaattaaattttYtatattaaRSgYtgBag\n\ aHSBDgtagcacRHtYcBgtcacttacactaYcgctWtattgSHtSatcataaatataHt\n\ cgtYaaMNgBaatttaRgaMaatatttBtttaaaHHKaatctgatWatYaacttMctctt\n\ ttVctagctDaaagtaVaKaKRtaacBgtatccaaccactHHaagaagaaggaNaaatBW\n\ attccgStaMSaMatBttgcatgRSacgttVVtaaDMtcSgVatWcaSatcttttVatag\n\ ttactttacgatcaccNtaDVgSRcgVcgtgaacgaNtaNatatagtHtMgtHcMtagaa\n\ attBgtataRaaaacaYKgtRccYtatgaagtaataKgtaaMttgaaRVatgcagaKStc\n\ tHNaaatctBBtcttaYaBWHgtVtgacagcaRcataWctcaBcYacYgatDgtDHccta\n\ aagacYRcaggattHaYgtKtaatgcVcaataMYacccatatcacgWDBtgaatcBaata\n\ cKcttRaRtgatgaBDacggtaattaaYtataStgVHDtDctgactcaaatKtacaatgc\n\ gYatBtRaDatHaactgtttatatDttttaaaKVccYcaaccNcBcgHaaVcattHctcg\n\ attaaatBtatgcaaaaatYMctSactHatacgaWacattacMBgHttcgaatVaaaaca\n\ BatatVtctgaaaaWtctRacgBMaatSgRgtgtcgactatcRtattaScctaStagKga\n\ DcWgtYtDDWKRgRtHatRtggtcgaHgggcgtattaMgtcagccaBggWVcWctVaaat\n\ tcgNaatcKWagcNaHtgaaaSaaagctcYctttRVtaaaatNtataaccKtaRgtttaM\n\ tgtKaBtRtNaggaSattHatatWactcagtgtactaKctatttgRYYatKatgtccgtR\n\ tttttatttaatatVgKtttgtatgtNtataRatWYNgtRtHggtaaKaYtKSDcatcKg\n\ taaYatcSRctaVtSMWtVtRWHatttagataDtVggacagVcgKWagBgatBtaaagNc\n\ aRtagcataBggactaacacRctKgttaatcctHgDgttKHHagttgttaatgHBtatHc\n\ DaagtVaBaRccctVgtgDtacRHSctaagagcggWYaBtSaKtHBtaaactYacgNKBa\n\ VYgtaacttagtVttcttaatgtBtatMtMtttaattaatBWccatRtttcatagVgMMt\n\ agctStKctaMactacDNYgKYHgaWcgaHgagattacVgtttgtRaSttaWaVgataat\n\ gtgtYtaStattattMtNgWtgttKaccaatagNYttattcgtatHcWtctaaaNVYKKt\n\ tWtggcDtcgaagtNcagatacgcattaagaccWctgcagcttggNSgaNcHggatgtVt\n\ catNtRaaBNcHVagagaaBtaaSggDaatWaatRccaVgggStctDaacataKttKatt\n\ tggacYtattcSatcttagcaatgaVBMcttDattctYaaRgatgcattttNgVHtKcYR\n\ aatRKctgtaaacRatVSagctgtWacBtKVatctgttttKcgtctaaDcaagtatcSat\n\ aWVgcKKataWaYttcccSaatgaaaacccWgcRctWatNcWtBRttYaattataaNgac\n\ acaatagtttVNtataNaYtaatRaVWKtBatKagtaatataDaNaaaaataMtaagaaS\n\ tccBcaatNgaataWtHaNactgtcDtRcYaaVaaaaaDgtttRatctatgHtgttKtga\n\ aNSgatactttcgagWaaatctKaaDaRttgtggKKagcDgataaattgSaacWaVtaNM\n\ acKtcaDaaatttctRaaVcagNacaScRBatatctRatcctaNatWgRtcDcSaWSgtt\n\ RtKaRtMtKaatgttBHcYaaBtgatSgaSWaScMgatNtctcctatttctYtatMatMt\n\ RRtSaattaMtagaaaaStcgVgRttSVaScagtgDtttatcatcatacRcatatDctta\n\ tcatVRtttataaHtattcYtcaaaatactttgVctagtaaYttagatagtSYacKaaac\n\ gaaKtaaatagataatSatatgaaatSgKtaatVtttatcctgKHaatHattagaaccgt\n\ YaaHactRcggSBNgtgctaaBagBttgtRttaaattYtVRaaaattgtaatVatttctc\n\ ttcatgBcVgtgKgaHaaatattYatagWacNctgaaMcgaattStagWaSgtaaKagtt\n\ ttaagaDgatKcctgtaHtcatggKttVDatcaaggtYcgccagNgtgcVttttagagat\n\ gctaccacggggtNttttaSHaNtatNcctcatSaaVgtactgBHtagcaYggYVKNgta\n\ KBcRttgaWatgaatVtagtcgattYgatgtaatttacDacSctgctaaaStttaWMagD\n\ aaatcaVYctccgggcgaVtaaWtStaKMgDtttcaaMtVgBaatccagNaaatcYRMBg\n\ gttWtaaScKttMWtYataRaDBMaDataatHBcacDaaKDactaMgagttDattaHatH\n\ taYatDtattDcRNStgaatattSDttggtattaaNSYacttcDMgYgBatWtaMagact\n\ VWttctttgYMaYaacRgHWaattgRtaagcattctMKVStatactacHVtatgatcBtV\n\ NataaBttYtSttacKgggWgYDtgaVtYgatDaacattYgatggtRDaVDttNactaSa\n\ MtgNttaacaaSaBStcDctaccacagacgcaHatMataWKYtaYattMcaMtgSttDag\n\ cHacgatcaHttYaKHggagttccgatYcaatgatRaVRcaagatcagtatggScctata\n\ ttaNtagcgacgtgKaaWaactSgagtMYtcttccaKtStaacggMtaagNttattatcg\n\ tctaRcactctctDtaacWYtgaYaSaagaWtNtatttRacatgNaatgttattgWDDcN\n\ aHcctgaaHacSgaataaRaataMHttatMtgaSDSKatatHHaNtacagtccaYatWtc\n\ actaactatKDacSaStcggataHgYatagKtaatKagStaNgtatactatggRHacttg\n\ tattatgtDVagDVaRctacMYattDgtttYgtctatggtKaRSttRccRtaaccttaga\n\ gRatagSaaMaacgcaNtatgaaatcaRaagataatagatactcHaaYKBctccaagaRa\n\ BaStNagataggcgaatgaMtagaatgtcaKttaaatgtaWcaBttaatRcggtgNcaca\n\ aKtttScRtWtgcatagtttWYaagBttDKgcctttatMggNttattBtctagVtacata\n\ aaYttacacaaRttcYtWttgHcaYYtaMgBaBatctNgcDtNttacgacDcgataaSat\n\ YaSttWtcctatKaatgcagHaVaacgctgcatDtgttaSataaaaYSNttatagtaNYt\n\ aDaaaNtggggacttaBggcHgcgtNtaaMcctggtVtaKcgNacNtatVaSWctWtgaW\n\ cggNaBagctctgaYataMgaagatBSttctatacttgtgtKtaattttRagtDtacata\n\ tatatgatNHVgBMtKtaKaNttDHaagatactHaccHtcatttaaagttVaMcNgHata\n\ tKtaNtgYMccttatcaaNagctggacStttcNtggcaVtattactHaSttatgNMVatt\n\ MMDtMactattattgWMSgtHBttStStgatatRaDaagattttctatMtaaaaaggtac\n\ taaVttaSacNaatactgMttgacHaHRttgMacaaaatagttaatatWKRgacDgaRta\n\ tatttattatcYttaWtgtBRtWatgHaaattHataagtVaDtWaVaWtgStcgtMSgaS\n\ RgMKtaaataVacataatgtaSaatttagtcgaaHtaKaatgcacatcggRaggSKctDc\n\ agtcSttcccStYtccRtctctYtcaaKcgagtaMttttcRaYDttgttatctaatcata\n\ NctctgctatcaMatactataggDaHaaSttMtaDtcNatataattctMcStaaBYtaNa\n\ gatgtaatHagagSttgWHVcttatKaYgDctcttggtgttMcRaVgSgggtagacaata\n\ aDtaattSaDaNaHaBctattgNtaccaaRgaVtKNtaaYggHtaKKgHcatctWtctDt\n\ ttctttggSDtNtaStagttataaacaattgcaBaBWggHgcaaaBtYgctaatgaaatW\n\ cDcttHtcMtWWattBHatcatcaaatctKMagtDNatttWaBtHaaaNgMttaaStagt\n\ tctctaatDtcRVaYttgttMtRtgtcaSaaYVgSWDRtaatagctcagDgcWWaaaBaa\n\ RaBctgVgggNgDWStNaNBKcBctaaKtttDcttBaaggBttgaccatgaaaNgttttt\n\ tttatctatgttataccaaDRaaSagtaVtDtcaWatBtacattaWacttaSgtattggD\n\ gKaaatScaattacgWcagKHaaccaYcRcaRttaDttRtttHgaHVggcttBaRgtccc\n\ tDatKaVtKtcRgYtaKttacgtatBtStaagcaattaagaRgBagSaattccSWYttta\n\ ttVaataNctgHgttaaNBgcVYgtRtcccagWNaaaacaDNaBcaaaaRVtcWMgBagM\n\ tttattacgDacttBtactatcattggaaatVccggttRttcatagttVYcatYaSHaHc\n\ ttaaagcNWaHataaaRWtctVtRYtagHtaaaYMataHYtNBctNtKaatattStgaMc\n\ BtRgctaKtgcScSttDgYatcVtggaaKtaagatWccHccgKYctaNNctacaWctttt\n\ gcRtgtVcgaKttcMRHgctaHtVaataaDtatgKDcttatBtDttggNtacttttMtga\n\ acRattaaNagaactcaaaBBVtcDtcgaStaDctgaaaSgttMaDtcgttcaccaaaag\n\ gWtcKcgSMtcDtatgtttStaaBtatagDcatYatWtaaaBacaKgcaDatgRggaaYc\n\ taRtccagattDaWtttggacBaVcHtHtaacDacYgtaatataMagaatgHMatcttat\n\ acgtatttttatattacHactgttataMgStYaattYaccaattgagtcaaattaYtgta\n\ tcatgMcaDcgggtcttDtKgcatgWRtataatatRacacNRBttcHtBgcRttgtgcgt\n\ catacMtttBctatctBaatcattMttMYgattaaVYatgDaatVagtattDacaacDMa\n\ tcMtHcccataagatgBggaccattVWtRtSacatgctcaaggggYtttDtaaNgNtaaB\n\ atggaatgtctRtaBgBtcNYatatNRtagaacMgagSaSDDSaDcctRagtVWSHtVSR\n\ ggaacaBVaccgtttaStagaacaMtactccagtttVctaaRaaHttNcttagcaattta\n\ ttaatRtaaaatctaacDaBttggSagagctacHtaaRWgattcaaBtctRtSHaNtgta\n\ cattVcaHaNaagtataccacaWtaRtaaVKgMYaWgttaKggKMtKcgWatcaDatYtK\n\ SttgtacgaccNctSaattcDcatcttcaaaDKttacHtggttHggRRaRcaWacaMtBW\n\ VHSHgaaMcKattgtaRWttScNattBBatYtaNRgcggaagacHSaattRtttcYgacc\n\ BRccMacccKgatgaacttcgDgHcaaaaaRtatatDtatYVtttttHgSHaSaatagct\n\ NYtaHYaVYttattNtttgaaaYtaKttWtctaNtgagaaaNctNDctaaHgttagDcRt\n\ tatagccBaacgcaRBtRctRtggtaMYYttWtgataatcgaataattattataVaaaaa\n\ ttacNRVYcaaMacNatRttcKatMctgaagactaattataaYgcKcaSYaatMNctcaa\n\ cgtgatttttBacNtgatDccaattattKWWcattttatatatgatBcDtaaaagttgaa\n\ VtaHtaHHtBtataRBgtgDtaataMttRtDgDcttattNtggtctatctaaBcatctaR\n\ atgNacWtaatgaagtcMNaacNgHttatactaWgcNtaStaRgttaaHacccgaYStac\n\ aaaatWggaYaWgaattattcMaactcBKaaaRVNcaNRDcYcgaBctKaacaaaaaSgc\n\ tccYBBHYaVagaatagaaaacagYtctVccaMtcgtttVatcaatttDRtgWctagtac\n\ RttMctgtDctttcKtWttttataaatgVttgBKtgtKWDaWagMtaaagaaattDVtag\n\ gttacatcatttatgtcgMHaVcttaBtVRtcgtaYgBRHatttHgaBcKaYWaatcNSc\n\ tagtaaaaatttacaatcactSWacgtaatgKttWattagttttNaggtctcaagtcact\n\ attcttctaagKggaataMgtttcataagataaaaatagattatDgcBVHWgaBKttDgc\n\ atRHaagcaYcRaattattatgtMatatattgHDtcaDtcaaaHctStattaatHaccga\n\ cNattgatatattttgtgtDtRatagSacaMtcRtcattcccgacacSattgttKaWatt\n\ NHcaacttccgtttSRtgtctgDcgctcaaMagVtBctBMcMcWtgtaacgactctcttR\n\ ggRKSttgYtYatDccagttDgaKccacgVatWcataVaaagaataMgtgataaKYaaat\n\ cHDaacgataYctRtcYatcgcaMgtNttaBttttgatttaRtStgcaacaaaataccVg\n\ aaDgtVgDcStctatatttattaaaaRKDatagaaagaKaaYYcaYSgKStctccSttac\n\ agtcNactttDVttagaaagMHttRaNcSaRaMgBttattggtttaRMggatggcKDgWR\n\ tNaataataWKKacttcKWaaagNaBttaBatMHtccattaacttccccYtcBcYRtaga\n\ ttaagctaaYBDttaNtgaaaccHcaRMtKtaaHMcNBttaNaNcVcgVttWNtDaBatg\n\ ataaVtcWKcttRggWatcattgaRagHgaattNtatttctctattaattaatgaDaaMa\n\ tacgttgggcHaYVaaNaDDttHtcaaHtcVVDgBVagcMacgtgttaaBRNtatRtcag\n\ taagaggtttaagacaVaaggttaWatctccgtVtaDtcDatttccVatgtacNtttccg\n\ tHttatKgScBatgtVgHtYcWagcaKtaMYaaHgtaattaSaHcgcagtWNaatNccNN\n\ YcacgVaagaRacttctcattcccRtgtgtaattagcSttaaStWaMtctNNcSMacatt\n\ ataaactaDgtatWgtagtttaagaaaattgtagtNagtcaataaatttgatMMYactaa\n\ tatcggBWDtVcYttcDHtVttatacYaRgaMaacaStaatcRttttVtagaDtcacWat\n\ ttWtgaaaagaaagNRacDtttStVatBaDNtaactatatcBSMcccaSttccggaMatg\n\ attaaWatKMaBaBatttgataNctgttKtVaagtcagScgaaaDggaWgtgttttKtWt\n\ atttHaatgtagttcactaaKMagttSYBtKtaYgaactcagagRtatagtVtatcaaaW\n\ YagcgNtaDagtacNSaaYDgatBgtcgataacYDtaaactacagWDcYKaagtttatta\n\ gcatcgagttKcatDaattgattatDtcagRtWSKtcgNtMaaaaacaMttKcaWcaaSV\n\ MaaaccagMVtaMaDtMaHaBgaacataBBVtaatVYaNSWcSgNtDNaaKacacBttta\n\ tKtgtttcaaHaMctcagtaacgtcgYtactDcgcctaNgagagcYgatattttaaattt\n\ ccattttacatttDaaRctattttWctttacgtDatYtttcagacgcaaVttagtaaKaa\n\ aRtgVtccataBggacttatttgtttaWNtgttVWtaWNVDaattgtatttBaagcBtaa\n\ BttaaVatcHcaVgacattccNggtcgacKttaaaRtagRtctWagaYggtgMtataatM\n\ tgaaRttattttgWcttNtDRRgMDKacagaaaaggaaaRStcccagtYccVattaNaaK\n\ StNWtgacaVtagaagcttSaaDtcacaacgDYacWDYtgtttKatcVtgcMaDaSKStV\n\ cgtagaaWaKaagtttcHaHgMgMtctataagBtKaaaKKcactggagRRttaagaBaaN\n\ atVVcgRcKSttDaactagtSttSattgttgaaRYatggttVttaataaHttccaagDtg\n\ atNWtaagHtgcYtaactRgcaatgMgtgtRaatRaNaacHKtagactactggaatttcg\n\ ccataacgMctRgatgttaccctaHgtgWaYcactcacYaattcttaBtgacttaaacct\n\ gYgaWatgBttcttVttcgttWttMcNYgtaaaatctYgMgaaattacNgaHgaacDVVM\n\ tttggtHtctaaRgtacagacgHtVtaBMNBgattagcttaRcttacaHcRctgttcaaD\n\ BggttKaacatgKtttYataVaNattccgMcgcgtagtRaVVaattaKaatggttRgaMc\n\ agtatcWBttNtHagctaatctagaaNaaacaYBctatcgcVctBtgcaaagDgttVtga\n\ HtactSNYtaaNccatgtgDacgaVtDcgKaRtacDcttgctaagggcagMDagggtBWR\n\ tttSgccttttttaacgtcHctaVtVDtagatcaNMaVtcVacatHctDWNaataRgcgt\n\ aVHaggtaaaaSgtttMtattDgBtctgatSgtRagagYtctSaKWaataMgattRKtaa\n\ catttYcgtaacacattRWtBtcggtaaatMtaaacBatttctKagtcDtttgcBtKYYB\n\ aKttctVttgttaDtgattttcttccacttgSaaacggaaaNDaattcYNNaWcgaaYat\n\ tttMgcBtcatRtgtaaagatgaWtgaccaYBHgaatagataVVtHtttVgYBtMctaMt\n\ cctgaDcYttgtccaaaRNtacagcMctKaaaggatttacatgtttaaWSaYaKttBtag\n\ DacactagctMtttNaKtctttcNcSattNacttggaacaatDagtattRtgSHaataat\n\ gccVgacccgatactatccctgtRctttgagaSgatcatatcgDcagWaaHSgctYYWta\n\ tHttggttctttatVattatcgactaagtgtagcatVgtgHMtttgtttcgttaKattcM\n\ atttgtttWcaaStNatgtHcaaaDtaagBaKBtRgaBgDtSagtatMtaacYaatYtVc\n\ KatgtgcaacVaaaatactKcRgtaYtgtNgBBNcKtcttaccttKgaRaYcaNKtactt\n\ tgagSBtgtRagaNgcaaaNcacagtVtttHWatgttaNatBgtttaatNgVtctgaata\n\ tcaRtattcttttttttRaaKcRStctcggDgKagattaMaaaKtcaHacttaataataK\n\ taRgDtKVBttttcgtKaggHHcatgttagHggttNctcgtatKKagVagRaaaggaaBt\n\ NatttVKcRttaHctaHtcaaatgtaggHccaBataNaNaggttgcWaatctgatYcaaa\n\ HaatWtaVgaaBttagtaagaKKtaaaKtRHatMaDBtBctagcatWtatttgWttVaaa\n\ ScMNattRactttgtYtttaaaagtaagtMtaMaSttMBtatgaBtttaKtgaatgagYg\n\ tNNacMtcNRacMMHcttWtgtRtctttaacaacattattcYaMagBaacYttMatcttK\n\ cRMtgMNccattaRttNatHaHNaSaaHMacacaVaatacaKaSttHatattMtVatWga\n\ ttttttaYctttKttHgScWaacgHtttcaVaaMgaacagNatcgttaacaaaaagtaca\n\ HBNaattgttKtcttVttaaBtctgctacgBgcWtttcaggacacatMgacatcccagcg\n\ gMgaVKaBattgacttaatgacacacaaaaaatRKaaBctacgtRaDcgtagcVBaacDS\n\ BHaaaaSacatatacagacRNatcttNaaVtaaaataHattagtaaaaSWccgtatWatg\n\ gDttaactattgcccatcttHaSgYataBttBaactattBtcHtgatcaataSttaBtat\n\ KSHYttWggtcYtttBttaataccRgVatStaHaKagaatNtagRMNgtcttYaaSaact\n\ cagDSgagaaYtMttDtMRVgWKWtgMaKtKaDttttgactatacataatcNtatNaHat\n\ tVagacgYgatatatttttgtStWaaatctWaMgagaRttRatacgStgattcttaagaD\n\ taWccaaatRcagcagaaNKagtaaDggcgccBtYtagSBMtactaaataMataBSacRM\n\ gDgattMMgtcHtcaYDtRaDaacggttDaggcMtttatgttaNctaattaVacgaaMMt\n\ aatDccSgtattgaRtWWaccaccgagtactMcgVNgctDctaMScatagcgtcaactat\n\ acRacgHRttgctatttaatgaattataYKttgtaagWgtYttgcHgMtaMattWaWVta\n\ RgcttgYgttBHtYataSccStBtgtagMgtDtggcVaaSBaatagDttgBgtctttctc\n\ attttaNagtHKtaMWcYactVcgcgtatMVtttRacVagDaatcttgctBBcRDgcaac\n\ KttgatSKtYtagBMagaRtcgBattHcBWcaactgatttaatttWDccatttatcgagS\n\ KaWttataHactaHMttaatHtggaHtHagaatgtKtaaRactgtttMatacgatcaagD\n\ gatKaDctataMggtHDtggHacctttRtatcttYattttgacttgaaSaataaatYcgB\n\ aaaaccgNatVBttMacHaKaataagtatKgtcaagactcttaHttcggaattgttDtct\n\ aaccHttttWaaatgaaatataaaWattccYDtKtaaaacggtgaggWVtctattagtga\n\ ctattaagtMgtttaagcatttgSgaaatatccHaaggMaaaattttcWtatKctagDtY\n\ tMcctagagHcactttactatacaaacattaacttaHatcVMYattYgVgtMttaaRtga\n\ aataaDatcaHgtHHatKcDYaatcttMtNcgatYatgSaMaNtcttKcWataScKggta\n\ tcttacgcttWaaagNatgMgHtctttNtaacVtgttcMaaRatccggggactcMtttaY\n\ MtcWRgNctgNccKatcttgYDcMgattNYaRagatHaaHgKctcataRDttacatBatc\n\ cattgDWttatttaWgtcggagaaaaatacaatacSNtgggtttccttacSMaagBatta\n\ caMaNcactMttatgaRBacYcYtcaaaWtagctSaacttWgDMHgaggatgBVgcHaDt\n\ ggaactttggtcNatNgtaKaBcccaNtaagttBaacagtatacDYttcctNgWgcgSMc\n\ acatStctHatgRcNcgtacacaatRttMggaNKKggataaaSaYcMVcMgtaMaHtgat\n\ tYMatYcggtcttcctHtcDccgtgRatcattgcgccgatatMaaYaataaYSggatagc\n\ gcBtNtaaaScaKgttBgagVagttaKagagtatVaactaSacWactSaKatWccaKaaa\n\ atBKgaaKtDMattttgtaaatcRctMatcaaMagMttDgVatggMaaWgttcgaWatga\n\ aatttgRtYtattaWHKcRgctacatKttctaccaaHttRatctaYattaaWatVNccat\n\ NgagtcKttKataStRaatatattcctRWatDctVagttYDgSBaatYgttttgtVaatt\n\ taatagcagMatRaacttBctattgtMagagattaaactaMatVtHtaaatctRgaaaaa\n\ aaatttWacaacaYccYDSaattMatgaccKtaBKWBattgtcaagcHKaagttMMtaat\n\ ttcKcMagNaaKagattggMagaggtaatttYacatcWaaDgatMgKHacMacgcVaaca\n\ DtaDatatYggttBcgtatgWgaSatttgtagaHYRVacaRtctHaaRtatgaactaata\n\ tctSSBgggaaHMWtcaagatKgagtDaSatagttgattVRatNtctMtcSaagaSHaat\n\ aNataataRaaRgattctttaataaagWaRHcYgcatgtWRcttgaaggaMcaataBRaa\n\ ccagStaaacNtttcaatataYtaatatgHaDgcStcWttaacctaRgtYaRtataKtgM\n\ ttttatgactaaaatttacYatcccRWtttHRtattaaatgtttatatttgttYaatMca\n\ RcSVaaDatcgtaYMcatgtagacatgaaattgRtcaaYaaYtRBatKacttataccaNa\n\ aattVaBtctggacaagKaaYaaatatWtMtatcYaaVNtcgHaactBaagKcHgtctac\n\ aatWtaDtSgtaHcataHtactgataNctRgttMtDcDttatHtcgtacatcccaggStt\n\ aBgtcacacWtccNMcNatMVaVgtccDYStatMaccDatggYaRKaaagataRatttHK\n\ tSaaatDgataaacttaHgttgVBtcttVttHgDacgaKatgtatatNYataactctSat\n\ atatattgcHRRYttStggaactHgttttYtttaWtatMcttttctatctDtagVHYgMR\n\ BgtHttcctaatYRttKtaagatggaVRataKDctaMtKBNtMtHNtWtttYcVtattMc\n\ gRaacMcctNSctcatttaaagDcaHtYccSgatgcaatYaaaaDcttcgtaWtaattct\n\ cgttttScttggtaatctttYgtctaactKataHacctMctcttacHtKataacacagcN\n\ RatgKatttttSaaatRYcgDttaMRcgaaattactMtgcgtaagcgttatBtttttaat\n\ taagtNacatHgttcRgacKcBBtVgatKttcgaBaatactDRgtRtgaNacWtcacYtt\n\ aaKcgttctHaKttaNaMgWgWaggtctRgaKgWttSttBtDcNtgtttacaaatYcDRt\n\ gVtgcctattcNtctaaaDMNttttNtggctgagaVctDaacVtWccaagtaacacaNct\n\ gaScattccDHcVBatcgatgtMtaatBgHaatDctMYgagaatgYWKcctaatNaStHa\n\ aaKccgHgcgtYaaYtattgtStgtgcaaRtattaKatattagaWVtcaMtBagttatta\n\ gNaWHcVgcaattttDcMtgtaRHVYtHtctgtaaaaHVtMKacatcgNaatttMatatg\n\ ttgttactagWYtaRacgataKagYNKcattataNaRtgaacKaYgcaaYYacaNccHat\n\ MatDcNgtHttRaWttagaaDcaaaaaatagggtKDtStaDaRtaVtHWKNtgtattVct\n\ SVgRgataDaRaWataBgaagaaKtaataaYgDcaStaNgtaDaaggtattHaRaWMYaY\n\ aWtggttHYgagVtgtgcttttcaaDKcagVcgttagacNaaWtagtaataDttctggtt\n\ VcatcataaagtgKaaaNaMtaBBaattaatWaattgctHaVKaSgDaaVKaHtatatat\n\ HatcatSBagNgHtatcHYMHgttDgtaHtBttWatcgtttaRaattgStKgSKNWKatc\n\ agDtctcagatttctRtYtBatBgHHtKaWtgYBgacVVWaKtacKcDttKMaKaVcggt\n\ gttataagaataaHaatattagtataatMHgttYgaRttagtaRtcaaVatacggtcMcg\n\ agtaaRttacWgactKRYataaaagSattYaWgagatYagKagatgSaagKgttaatMgg\n\ tataatgttWYttatgagaaacctNVataatHcccKtDctcctaatactggctHggaSag\n\ gRtKHaWaattcgSatMatttagaggcYtctaMcgctcataSatatgRagacNaaDagga\n\ VBagaYttKtacNaKgtSYtagttggaWcatcWttaatctatgaVtcgtgtMtatcaYcg\n\ tRccaaYgDctgcMgtgtWgacWtgataacacgcgctBtgttaKtYDtatDcatcagKaV\n\ MctaatcttgVcaaRgcRMtDcgattaHttcaNatgaatMtactacVgtRgatggaWttt\n\ actaaKatgagSaaKggtaNtactVaYtaaKRagaacccacaMtaaMtKtatBcttgtaa\n\ WBtMctaataaVcDaaYtcRHBtcgttNtaaHatttBNgRStVDattBatVtaagttaYa\n\ tVattaagaBcacggtSgtVtatttaRattgatgtaHDKgcaatattKtggcctatgaWD\n\ KRYcggattgRctatNgatacaatMNttctgtcRBYRaaaHctNYattcHtaWcaattct\n\ BtMKtVgYataatMgYtcagcttMDataVtggRtKtgaatgccNcRttcaMtRgattaac\n\ attRcagcctHtWMtgtDRagaKaBtgDttYaaaaKatKgatctVaaYaacWcgcatagB\n\ VtaNtRtYRaggBaaBtgKgttacataagagcatgtRattccacttaccatRaaatgWgD\n\ aMHaYVgVtaSctatcgKaatatattaDgacccYagtgtaYNaaatKcagtBRgagtcca\n\ tgKgaaaccBgaagBtgSttWtacgatWHaYatcgatttRaaNRgcaNaKVacaNtDgat\n\ tgHVaatcDaagcgtatgcNttaDataatcSataaKcaataaHWataBtttatBtcaKtK\n\ tatagttaDgSaYctacaRatNtaWctSaatatttYaKaKtaccWtatcRagacttaYtt\n\ VcKgSDcgagaagatccHtaattctSttatggtKYgtMaHagVaBRatttctgtRgtcta\n\ tgggtaHKgtHacHtSYacgtacacHatacKaaBaVaccaDtatcSaataaHaagagaat\n\ ScagactataaRttagcaaVcaHataKgDacatWccccaagcaBgagWatctaYttgaaa\n\ tctVNcYtttWagHcgcgcDcVaaatgttKcHtNtcaatagtgtNRaactttttcaatgg\n\ WgBcgDtgVgtttctacMtaaataaaRggaaacWaHttaRtNtgctaaRRtVBctYtVta\n\ tDcattDtgaccYatagatYRKatNYKttNgcctagtaWtgaactaMVaacctgaStttc\n\ tgaKVtaaVaRKDttVtVctaDNtataaaDtccccaagtWtcgatcactDgYaBcatcct\n\ MtVtacDaaBtYtMaKNatNtcaNacgDatYcatcgcaRatWBgaacWttKttagYtaat\n\ tcggttgSWttttDWctttacYtatatWtcatDtMgtBttgRtVDggttaacYtacgtac\n\ atgaattgaaWcttMStaDgtatattgaDtcRBcattSgaaVBRgagccaaKtttcDgcg\n\ aSMtatgWattaKttWtgDBMaggBBttBaatWttRtgcNtHcgttttHtKtcWtagHSt\n\ aacagttgatatBtaWSaWggtaataaMttaKacDaatactcBttcaatatHttcBaaSa\n\ aatYggtaRtatNtHcaatcaHtagVtgtattataNggaMtcttHtNagctaaaggtaga\n\ YctMattNaMVNtcKtactBKcaHHcBttaSagaKacataYgctaKaYgttYcgacWVtt\n\ WtSagcaacatcccHaccKtcttaacgaKttcacKtNtacHtatatRtaaatacactaBt\n\ ttgaHaRttggttWtatYagcatYDatcggagagcWBataagRtacctataRKgtBgatg\n\ aDatataSttagBaHtaatNtaDWcWtgtaattacagKttcNtMagtattaNgtctcgtc\n\ ctcttBaHaKcKccgtRcaaYagSattaagtKataDatatatagtcDtaacaWHcaKttD\n\ gaaRcgtgYttgtcatatNtatttttatggccHtgDtYHtWgttatYaacaattcaWtat\n\ NgctcaaaSttRgctaatcaaatNatcgtttaBtNNVtgttataagcaaagattBacgtD\n\ atttNatttaaaDcBgtaSKgacgtagataatttcHMVNttgttBtDtgtaWKaaRMcKM\n\ tHtaVtagataWctccNNaSWtVaHatctcMgggDgtNHtDaDttatatVWttgttattt\n\ aacctttcacaaggaSaDcggttttttatatVtctgVtaacaStDVaKactaMtttaSNa\n\ gtgaaattaNacttSKctattcctctaSagKcaVttaagNaVcttaVaaRNaHaaHttat\n\ gtHttgtgatMccaggtaDcgaccgtWgtWMtttaHcRtattgScctatttKtaaccaag\n\ tYagaHgtWcHaatgccKNRtttagtMYSgaDatctgtgaWDtccMNcgHgcaaacNDaa\n\ aRaStDWtcaaaaHKtaNBctagBtgtattaactaattttVctagaatggcWSatMaccc\n\ ttHttaSgSgtgMRcatRVKtatctgaaaccDNatYgaaVHNgatMgHRtacttaaaRta\n\ tStRtDtatDttYatattHggaBcttHgcgattgaKcKtttcRataMtcgaVttWacatN\n\ catacctRataDDatVaWNcggttgaHtgtMacVtttaBHtgagVttMaataattatgtt\n\ cttagtttgtgcDtSatttgBtcaacHattaaBagVWcgcaSYttMgcttacYKtVtatc\n\ aYaKctgBatgcgggcYcaaaaacgNtctagKBtattatctttKtaVttatagtaYtRag\n\ NtaYataaVtgaatatcHgcaaRataHtacacatgtaNtgtcgYatWMatttgaactacR\n\ ctaWtWtatacaatctBatatgYtaagtatgtgtatSttactVatcttYtaBcKgRaSgg\n\ RaaaaatgcagtaaaWgtaRgcgataatcBaataccgtatttttccatcNHtatWYgatH\n\ SaaaDHttgctgtccHtggggcctaataatttttctatattYWtcattBtgBRcVttaVM\n\ RSgctaatMagtYtttaaaaatBRtcBttcaaVtaacagctccSaaSttKNtHtKYcagc\n\ agaaaccccRtttttaaDcDtaStatccaagcgctHtatcttaDRYgatDHtWcaaaBcW\n\ gKWHttHataagHacgMNKttMKHccaYcatMVaacgttaKgYcaVaaBtacgcaacttt\n\ MctaaHaatgtBatgagaSatgtatgSRgHgWaVWgataaatatttccKagVgataattW\n\ aHNcYggaaatgctHtKtaDtctaaagtMaatVDVactWtSaaWaaMtaHtaSKtcBRaN\n\ cttStggtBttacNagcatagRgtKtgcgaacaacBcgKaatgataagatgaaaattgta\n\ ctgcgggtccHHWHaaNacaBttNKtKtcaaBatatgctaHNgtKcDWgtttatNgVDHg\n\ accaacWctKaaggHttgaRgYaatHcaBacaatgagcaaattactgtaVaaYaDtagat\n\ tgagNKggtggtgKtWKaatacagDRtatRaMRtgattDggtcaaYRtatttNtagaDtc\n\ acaaSDctDtataatcgtactaHttatacaatYaacaaHttHatHtgcgatRRttNgcat\n\ SVtacWWgaaggagtatVMaVaaattScDDKNcaYBYaDatHgtctatBagcaacaagaa\n\ tgagaaRcataaKNaRtBDatcaaacgcattttttaaBtcSgtacaRggatgtMNaattg\n\ gatatWtgagtattaaaVctgcaYMtatgatttttYgaHtgtcttaagWBttHttgtctt\n\ attDtcgtatWtataataSgctaHagcDVcNtaatcaagtaBDaWaDgtttagYctaNcc\n\ DtaKtaHcttaataacccaRKtacaVaatNgcWRaMgaattatgaBaaagattVYaHMDc\n\ aDHtcRcgYtcttaaaWaaaVKgatacRtttRRKYgaatacaWVacVcRtatMacaBtac\n\ tggMataaattttHggNagSctacHgtBagcgtcgtgattNtttgatSaaggMttctttc\n\ ttNtYNagBtaaacaaatttMgaccttacataattgYtcgacBtVMctgStgMDtagtaR\n\ ctHtatgttcatatVRNWataDKatWcgaaaaagttaaaagcacgHNacgtaatctttMR\n\ tgacttttDacctataaacgaaatatgattagaactccSYtaBctttaataacWgaaaYa\n\ tagatgWttcatKtNgatttttcaagHtaYgaaRaDaagtaggagcttatVtagtctttc\n\ attaaaatcgKtattaRttacagVaDatgcatVgattgggtctttHVtagKaaRBtaHta\n\ aggccccaaaaKatggtttaMWgtBtaaacttcactttKHtcgatctccctaYaBacMgt\n\ cttBaBaNgcgaaacaatctagtHccHtKttcRtRVttccVctttcatacYagMVtMcag\n\ aMaaacaataBctgYtaatRaaagattaaccatVRatHtaRagcgcaBcgDttStttttc\n\ VtttaDtKgcaaWaaaaatSccMcVatgtKgtaKgcgatatgtagtSaaaDttatacaaa\n\ catYaRRcVRHctKtcgacKttaaVctaDaatgttMggRcWaacttttHaDaKaDaBctg\n\ taggcgtttaHBccatccattcNHtDaYtaataMttacggctNVaacDattgatatttta\n\ cVttSaattacaaRtataNDgacVtgaacataVRttttaDtcaaacataYDBtttaatBa\n\ DtttYDaDaMccMttNBttatatgagaaMgaNtattHccNataattcaHagtgaaggDga\n\ tgtatatatgYatgaStcataaBStWacgtcccataRMaaDattggttaaattcMKtctM\n\ acaBSactcggaatDDgatDgcWctaacaccgggaVcacWKVacggtaNatatacctMta\n\ tgatagtgcaKagggVaDtgtaacttggagtcKatatcgMcttRaMagcattaBRaStct\n\ YSggaHYtacaactMBaagDcaBDRaaacMYacaHaattagcattaaaHgcgctaaggSc\n\ cKtgaaKtNaBtatDDcKBSaVtgatVYaagVtctSgMctacgttaacWaaattctSgtD\n\ actaaStaaattgcagBBRVctaatatacctNttMcRggctttMttagacRaHcaBaacV\n\ KgaataHttttMgYgattcYaNRgttMgcVaaacaVVcDHaatttgKtMYgtatBtVVct\n\ WgVtatHtacaaHttcacgatagcagtaaNattBatatatttcVgaDagcggttMaagtc\n\ ScHagaaatgcYNggcgtttttMtStggtRatctacttaaatVVtBacttHNttttaRca\n\ aatcacagHgagagtMgatcSWaNRacagDtatactaaDKaSRtgattctccatSaaRtt\n\ aaYctacacNtaRtaactggatgaccYtacactttaattaattgattYgttcagDtNKtt\n\ agDttaaaaaaaBtttaaNaYWKMBaaaacVcBMtatWtgBatatgaacVtattMtYatM\n\ NYDKNcKgDttDaVtaaaatgggatttctgtaaatWtctcWgtVVagtcgRgacttcccc\n\ taDcacagcRcagagtgtWSatgtacatgttaaSttgtaaHcgatgggMagtgaacttat\n\ RtttaVcaccaWaMgtactaatSSaHtcMgaaYtatcgaaggYgggcgtgaNDtgttMNg\n\ aNDMtaattcgVttttaacatgVatgtWVMatatcaKgaaattcaBcctccWcttgaaWH\n\ tWgHtcgNWgaRgctcBgSgaattgcaaHtgattgtgNagtDttHHgBttaaWcaaWagc\n\ aSaHHtaaaVctRaaMagtaDaatHtDMtcVaWMtagSagcttHSattaacaaagtRacM\n\ tRtctgttagcMtcaBatVKtKtKacgagaSNatSactgtatatcBctgagVtYactgta\n\ aattaaaggcYgDHgtaacatSRDatMMccHatKgttaacgactKtgKagtcttcaaHRV\n\ tccttKgtSataatttacaactggatDNgaacttcaRtVaagDcaWatcBctctHYatHa\n\ DaaatttagYatSatccaWtttagaaatVaacBatHcatcgtacaatatcgcNYRcaata\n\ YaRaYtgattVttgaatgaVaactcRcaNStgtgtattMtgaggtNttBaDRcgaaaagc\n\ tNgBcWaWgtSaDcVtgVaatMKBtttcgtttctaaHctaaagYactgMtatBDtcStga\n\ ccgtSDattYaataHctgggaYYttcggttaWaatctggtRagWMaDagtaacBccacta\n\ cgHWMKaatgatWatcctgHcaBaSctVtcMtgtDttacctaVgatYcWaDRaaaaRtag\n\ atcgaMagtggaRaWctctgMgcWttaagKBRtaaDaaWtctgtaagYMttactaHtaat\n\ cttcataacggcacBtSgcgttNHtgtHccatgttttaaagtatcgaKtMttVcataYBB\n\ aKtaMVaVgtattNDSataHcagtWMtaggtaSaaKgttgBtVtttgttatcatKcgHac\n\ acRtctHatNVagSBgatgHtgaRaSgttRcctaacaaattDNttgacctaaYtBgaaaa\n\ tagttattactcttttgatgtNNtVtgtatMgtcttRttcatttgatgacacttcHSaaa\n\ ccaWWDtWagtaRDDVNacVaRatgttBccttaatHtgtaaacStcVNtcacaSRttcYa\n\ gacagaMMttttgMcNttBcgWBtactgVtaRttctccaaYHBtaaagaBattaYacgat\n\ ttacatctgtaaMKaRYtttttactaaVatWgctBtttDVttctggcDaHaggDaagtcg\n\ aWcaagtagtWttHtgKtVataStccaMcWcaagataagatcactctHatgtcYgaKcat\n\ cagatactaagNSStHcctRRNtattgtccttagttagMVgtatagactaactctVcaat\n\ MctgtttgtgttgccttatWgtaBVtttctggMcaaKgDWtcgtaaYStgSactatttHg\n\ atctgKagtagBtVacRaagRtMctatgggcaaaKaaaatacttcHctaRtgtDcttDat\n\ taggaaatttcYHaRaaBttaatggcacKtgctHVcaDcaaaVDaaaVcgMttgtNagcg\n\ taDWgtcgttaatDgKgagcSatatcSHtagtagttggtgtHaWtaHKtatagctgtVga\n\ ttaBVaatgaataagtaatVatSttaHctttKtttgtagttaccttaatcgtagtcctgB\n\ cgactatttVcMacHaaaggaatgDatggKtaHtgStatattaaSagctWcctccRtata\n\ BaDYcgttgcNaagaggatRaaaYtaWgNtSMcaatttactaacatttaaWttHtatBat\n\ tgtcgacaatNgattgcNgtMaaaKaBDattHacttggtRtttaYaacgVactBtaBaKt\n\ gBttatgVttgtVttcaatcWcNctDBaaBgaDHacBttattNtgtDtatttVSaaacag\n\ gatgcRatSgtaSaNtgBatagttcHBgcBBaaattaHgtDattatDaKaatBaaYaaMa\n\ ataaataKtttYtagtBgMatNcatgtttgaNagtgttgtgKaNaSagtttgaSMaYBca\n\ aaacDStagttVacaaaaactaaWttBaagtctgtgcgtMgtaattctcctacctcaNtt\n\ taaccaaaaVtBcacataacaccccBcWMtatVtggaatgaWtcaaWaaaaaaaaWtDta\n\ atatRcctDWtcctaccMtVVatKttaWaaKaaatataaagScHBagaggBaSMtaWaVt\n\ atattactSaaaKNaactatNatccttgaYctattcaaaVgatttYHcRagattttaSat\n\ aggttattcVtaaagaKgtattattKtRttNcggcRgtgtgtWYtaacHgKatKgatYta\n\ cYagDtWcHBDctctgRaYKaYagcactKcacSaRtBttttBHKcMtNtcBatttatttt\n\ tgSatVgaaagaWtcDtagDatatgMacaacRgatatatgtttgtKtNRaatatNatgYc\n\ aHtgHataacKtgagtagtaacYttaNccaaatHcacaacaVDtagtaYtccagcattNt\n\ acKtBtactaaagaBatVtKaaHBctgStgtBgtatgaSNtgDataaccctgtagcaBgt\n\ gatcttaDataStgaMaccaSBBgWagtacKcgattgaDgNNaaaacacagtSatBacKD\n\ gcgtataBKcatacactaSaatYtYcDaactHttcatRtttaatcaattataRtttgtaa\n\ gMcgNttcatcBtYBagtNWNMtSHcattcRctttttRWgaKacKttgggagBcgttcgc\n\ MaWHtaatactgtctctatttataVgtttaBScttttaBMaNaatMacactYtBMggtHa\n\ cMagtaRtctgcatttaHtcaaaatttgagKtgNtactBacaHtcgtatttctMaSRagc\n\ agttaatgtNtaaattgagagWcKtaNttagVtacgatttgaatttcgRtgtWcVatcgt\n\ taaDVctgtttBWgaccagaaagtcSgtVtatagaBccttttcctaaattgHtatcggRa\n\ ttttcaaggcYSKaagWaWtRactaaaacccBatMtttBaatYtaagaactSttcgaaSc\n\ aatagtattgaccaagtgttttctaacatgtttNVaatcaaagagaaaNattaaRtttta\n\ VaaaccgcaggNMtatattVctcaagaggaacgBgtttaacaagttcKcYaatatactaa\n\ ccBaaaSggttcNtattctagttRtBacgScVctcaatttaatYtaaaaaaatgSaatga\n\ tagaMBRatgRcMcgttgaWHtcaVYgaatYtaatctttYttatRaWtctgBtDcgatNa\n\ tcKaBaDgatgtaNatWKctccgatattaacattNaaacDatgBgttctgtDtaaaMggt\n\ gaBaSHataacgccSctaBtttaRBtcNHcDatcDcctagagtcRtaBgWttDRVHagat\n\ tYatgtatcWtaHtttYcattWtaaagtctNgtStggRNcgcggagSSaaagaaaatYcH\n\ DtcgctttaatgYcKBVSgtattRaYBaDaaatBgtatgaHtaaRaRgcaSWNtagatHa\n\ acttNctBtcaccatctMcatattccaSatttgcgaDagDgtatYtaaaVDtaagtttWV\n\ aagtagYatRttaagDcNgacKBcScagHtattatcDaDactaaaaaYgHttBcgaDttg\n\ gataaaKSRcBMaBcgaBSttcWtgNBatRaccgattcatttataacggHVtaattcaca\n\ agagVttaaRaatVVRKcgWtVgacctgDgYaaHaWtctttcacMagggatVgactagMa\n\ aataKaaNWagKatagNaaWtaaaatttgaattttatttgctaaVgaHatBatcaaBWcB\n\ gttcMatcgBaaNgttcgSNaggSaRtttgHtRtattaNttcDcatSaVttttcgaaaaa\n\ ttgHatctaRaggSaNatMDaaatDcacgattttagaHgHaWtYgattaatHNSttatMS\n\ gggNtcKtYatRggtttgtMWVtttaYtagcagBagHaYagttatatggtBacYcattaR\n\ SataBatMtttaaatctHcaaaSaaaagttNSaaWcWRccRtKaagtBWtcaaattSttM\n\ tattggaaaccttaacgttBtWatttatatWcDaatagattcctScacctaagggRaaYt\n\ aNaatgVtBcttaaBaacaMVaaattatStYgRcctgtactatcMcVKatttcgSgatRH\n\ MaaaHtagtaaHtVgcaaataatatcgKKtgccaatBNgaaWcVttgagttaKatagttc\n\ aggKDatDtattgaKaVcaKtaataDataataHSaHcattagttaatRVYcNaHtaRcaa\n\ ggtNHcgtcaaccaBaaagYtHWaaaRcKgaYaaDttgcWYtataRgaatatgtYtgcKt\n\ aNttWacatYHctRaDtYtattcBttttatcSataYaYgttWaRagcacHMgtttHtYtt\n\ YaatcggtatStttcgtRSattaaDaKMaatatactaNBaWgctacacYtgaYVgtgHta\n\ aaRaaRgHtagtWattataaaSDaaWtgMattatcgaaaagtaYRSaWtSgNtBgagcRY\n\ aMDtactaacttaWgtatctagacaagNtattHggataatYttYatcataDcgHgttBtt\n\ ctttVttgccgaaWtaaaacgKgtatctaaaaaNtccDtaDatBMaMggaatNKtatBaa\n\ atVtccRaHtaSacataHattgtttKVYattcataVaattWtcgtgMttcttKtgtctaa\n\ cVtatctatatBRataactcgKatStatattcatHHRttKtccaacgtgggtgRgtgaMt\n\ attattggctatcgtgacMtRcBDtcttgtactaatRHttttaagatcgVMDStattatY\n\ BtttDttgtBtNttgRcMtYtgBacHaWaBaatDKctaagtgaaactaatgRaaKgatcc\n\ aagNaaaatattaggWNtaagtatacttttKcgtcggSYtcttgRctataYcttatataa\n\ agtatattaatttataVaacacaDHatctatttttKYVatHRactttaBHccaWagtact\n\ BtcacgaVgcgttRtttttttSVgtSagtBaaattctgaHgactcttgMcattttagVta\n\ agaattHctHtcaDaaNtaacRggWatagttcgtSttgaDatcNgNagctagDgatcNtt\n\ KgttgtaDtctttRaaYStRatDtgMggactSttaDtagSaVtBDttgtDgccatcacaM\n\ attaaaMtNacaVcgSWcVaaDatcaHaatgaattaMtatccVtctBtaattgtWattat\n\ BRcWcaatgNNtactWYtDaKttaaatcactcagtRaaRgatggtKgcgccaaHgaggat\n\ StattYcaNMtcaBttacttatgagDaNtaMgaaWtgtttcttctaHtMNgttatctaWW\n\ atMtBtaaatagDVatgtBYtatcggcttaagacMRtaHScgatatYgRDtcattatSDa\n\ HggaaataNgaWSRRaaaBaatagBattaDctttgHWNttacaataaaaaaatacggttt\n\ gHgVtaHtWMttNtBtctagtMcgKMgHgYtataHaNagWtcaacYattaataYRgtaWK\n\ gaBctataaccgatttaHaNBRaRaMtccggtNgacMtctcatttgcaattcWgMactta\n\ caaDaaNtactWatVtttagccttMaatcagVaagtctVaaDaBtattaattaYtNaYtg\n\ gattaKtaKctYaMtattYgatattataatKtVgDcttatatNBtcgttgtStttttMag\n\ aggttaHYSttcKgtcKtDNtataagttataagSgttatDtRttattgttttSNggRtca\n\ aKMNatgaatattgtBWtaMacctgggYgaSgaagYataagattacgagaatBtggtRcV\n\ HtgYggaDgaYaKagWagctatagacgaaHgtWaNgacttHRatVaWacKYtgRVNgVcS\n\ gRWctacatcKSactctgWYtBggtataagcttNRttVtgRcaWaaatDMatYattaact\n\ ttcgaagRatSctgccttgcRKaccHtttSNVagtagHagBagttagaccaRtataBcca\n\ taatSHatRtcHagacBWatagcaMtacaRtgtgaaBatctKRtScttccaNaatcNgta\n\ atatWtcaMgactctBtWtaaNactHaaaaRctcgcatggctMcaaNtcagaaaaacaca\n\ gtggggWttRttagtaagaVctVMtcgaatcttcMaaaHcaHBttcgattatgtcaDagc\n\ YRtBtYcgacMgtDcagcgaNgttaataatagcagKYYtcgtaBtYctMaRtaRtDagaa\n\ aacacatgYaBttgattattcgaaNttBctSataaMataWRgaHtttccgtDgaYtatgg\n\ tDgHKgMtatttVtMtVagttaRatMattRagataaccctKctMtSttgaHagtcStcta\n\ tttccSagatgttccacgaggYNttHRacgattcDatatDcataaaatBBttatcgaHtN\n\ HaaatatDNaggctgaNcaaggagttBttMgRagVatBcRtaWgatgBtSgaKtcgHttt\n\ gaatcaaDaHttcSBgHcagtVaaSttDcagccgttNBtgttHagYtattctttRWaaVt\n\ SttcatatKaaRaaaNacaVtVctMtSDtDtRHRcgtaatgctcttaaatSacacaatcg\n\ HattcaWcttaaaatHaaatcNctWttaNMcMtaKctVtcctaagYgatgatcYaaaRac\n\ tctaRDaYagtaacgtDgaggaaatctcaaacatcaScttcKttNtaccatNtaNataca\n\ tttHaaDHgcaDatMWaaBttcRggctMaagctVYcacgatcaDttatYtaatcKatWat\n\ caatVYtNagatttgattgaYttttYgacttVtcKaRagaaaHVgDtaMatKYagagttN\n\ atWttaccNtYtcDWgSatgaRgtMatgKtcgacaagWtacttaagtcgKtgatccttNc\n\ ttatagMatHVggtagcgHctatagccctYttggtaattKNaacgaaYatatVctaataM\n\ aaaYtgVtcKaYtaataacagaatHcacVagatYWHttagaaSMaatWtYtgtaaagNaa\n\ acaVgaWtcacNWgataNttcaSagctMDaRttgNactaccgataMaaatgtttattDtc\n\ aagacgctDHYYatggttcaagccNctccttcMctttagacBtaaWtaWVHggaaaaNat\n\ ttaDtDtgctaaHHtMtatNtMtagtcatttgcaaaRatacagRHtatDNtgtDgaatVg\n\ tVNtcaaatYBMaaaagcaKgtgatgatMgWWMaHttttMgMagatDtataaattaacca\n\ actMtacataaattgRataatacgBtKtaataattRgtatDagDtcRDacctatRcagag\n\ cSHatNtcaScNtttggacNtaaggaccgtgKNttgttNcttgaaRgYgRtNtcagttBc\n\ ttttcHtKtgcttYaaNgYagtaaatgaatggWaMattBHtatctatSgtcYtgcHtaat\n\ tHgaaMtHcagaaSatggtatgccaHBtYtcNattWtgtNgctttaggtttgtWatNtgH\n\ tgcDttactttttttgcNtactKtWRaVcttcatagtgSNKaNccgaataaBttataata\n\ YtSagctttaaatSttggctaaKSaatRccgWHgagDttaaatcatgagMtcgagtVtaD\n\ ggaBtatttgDacataaacgtagYRagBWtgDStKDgatgaagttcattatttaKWcata\n\ aatWRgatataRgttRacaaNKttNtKagaaYaStaactScattattaacgatttaaatg\n\ DtaattagatHgaYataaactatggggatVHtgccgtNgatNYcaStRtagaccacWcaM\n\ tatRagHgVactYtWHtcttcatgatWgagaKggagtatgaWtDtVtNaNtcgYYgtaaa\n\ ctttaDtBactagtaDctatagtaatatttatatataacgHaaaRagKattSagttYtSt\n\ >THREE Homo sapiens frequency\n\ agagagacgatgaaaattaatcgtcaatacgctggcgaacactgagggggacccaatgct\n\ cttctcggtctaaaaaggaatgtgtcagaaattggtcagttcaaaagtagaccggatctt\n\ tgcggagaacaattcacggaacgtagcgttgggaaatatcctttctaccacacatcggat\n\ tttcgccctctcccattatttattgtgttctcacatagaattattgtttagacatccctc\n\ gttgtatggagagttgcccgagcgtaaaggcataatccatataccgccgggtgagtgacc\n\ tgaaattgtttttagttgggatttcgctatggattagcttacacgaagagattctaatgg\n\ tactataggataattataatgctgcgtggcgcagtacaccgttacaaacgtcgttcgcat\n\ atgtggctaacacggtgaaaatacctacatcgtatttgcaatttcggtcgtttcatagag\n\ cgcattgaattactcaaaaattatatatgttgattatttgattagactgcgtggaaagaa\n\ ggggtactcaagccatttgtaaaagctgcatctcgcttaagtttgagagcttacattagt\n\ ctatttcagtcttctaggaaatgtctgtgtgagtggttgtcgtccataggtcactggcat\n\ atgcgattcatgacatgctaaactaagaaagtagattactattaccggcatgcctaatgc\n\ gattgcactgctatgaaggtgcggacgtcgcgcccatgtagccctgataataccaatact\n\ tacatttggtcagcaattctgacattatacctagcacccataaatttactcagacttgag\n\ gacaggctcttggagtcgatcttctgtttgtatgcatgtgatcatatagatgaataagcg\n\ atgcgactagttagggcatagtatagatctgtgtatacagttcagctgaacgtccgcgag\n\ tggaagtacagctgagatctatcctaaaatgcaaccatatcgttcacacatgatatgaac\n\ ccagggggaaacattgagttcagttaaattggcagcgaatcccccaagaagaaggcggag\n\ tgacgttgaacgggcttatggtttttcagtacttcctccgtataagttgagcgaaatgta\n\ aacagaataatcgttgtgttaacaacattaaaatcgcggaatatgatgagaatacacagt\n\ gtgagcatttcacttgtaaaatatctttggtagaacttactttgctttaaatatgttaaa\n\ ccgatctaataatctacaaaacggtagattttgcctagcacattgcgtccttctctattc\n\ agatagaggcaatactcagaaggttttatccaaagcactgtgttgactaacctaagtttt\n\ agtctaataatcatgattgattataggtgccgtggactacatgactcgtccacaaataat\n\ acttagcagatcagcaattggccaagcacccgacttttatttaatggttgtgcaatagtc\n\ cagattcgtattcgggactctttcaaataatagtttcctggcatctaagtaagaaaagct\n\ cataaggaagcgatattatgacacgctcttccgccgctgttttgaaacttgagtattgct\n\ cgtccgaaattgagggtcacttcaaaatttactgagaagacgaagatcgactaaagttaa\n\ aatgctagtccacagttggtcaagttgaattcatccacgagttatatagctattttaatt\n\ tatagtcgagtgtacaaaaaacatccacaataagatttatcttagaataacaacccccgt\n\ atcatcgaaatcctccgttatggcctgactcctcgagcttatagcatttgtgctggcgct\n\ cttgccaggaacttgctcgcgaggtggtgacgagtgagatgatcagtttcattatgatga\n\ tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgtcattat\n\ catgctccattaacaggttatttaattgatactgacgaaattttttcacaatgggttttc\n\ tagaatttaatatcagtaattgaagccttcataggggtcctactagtatcctacacgacg\n\ caggtccgcagtatcctggagggacgtgttactgattaaaagggtcaaaggaatgaaggc\n\ tcacaatgttacctgcttcaccatagtgagccgatgagttttacattagtactaaatccc\n\ aaatcatactttacgatgaggcttgctagcgctaaagagaatacatacaccaccacatag\n\ aattgttagcgatgatatcaaatagactcctggaagtgtcagggggaaactgttcaatat\n\ ttcgtccacaggactgaccaggcatggaaaagactgacgttggaaactataccatctcac\n\ gcccgacgcttcactaattgatgatccaaaaaatatagcccggattcctgattagcaaag\n\ ggttcacagagaaagatattatcgacgtatatcccaaaaaacagacgtaatgtgcatctt\n\ cgaatcgggatgaatacttgtatcataaaaatgtgacctctagtatacaggttaatgtta\n\ gtgatacacaatactcgtgggccatgggttctcaaataaaatgtaatattgcgtcgatca\n\ ctcacccacgtatttggtctaattatgttttatttagtgacaatccaatagataaccggt\n\ cctattaagggctatatttttagcgaccacgcgtttaaacaaaggattgtatgtagatgg\n\ taccagtttaattgccagtgggcaatcctaagcaaaatgagattctatcctaaagtttgg\n\ gcttgatataagatttcggatgtatgggttttataatcgttggagagctcaatcatgagc\n\ taatacatggatttcgctacctcaccgagagaccttgcatgaagaattctaaccaaaagt\n\ ttaataggccggattggattgagttaattaagaccttgttcagtcatagtaaaaaccctt\n\ aaattttaccgattgacaaagtgagcagtcgcaataccctatgcgaaacgcctcgatagt\n\ gactaggtatacaaggtttttgagttcctttgaaatagttaactaatttaaaattaatta\n\ acgacatggaaatcacagaacctaatgctttgtaggagttatttatgctgtttactgcct\n\ ctacaaccctaataaagcagtcctaagaatgaaacgcatcttttagttcagaaagtggta\n\ tccagggtggtcaatttaataaattcaacatcgggtctcaggatattcggtcatataatt\n\ tattaagggctcttcgagtcttactctgagtgaaattggaaacagtcatccttttcgttg\n\ tgaggcatcttacaccgctatcgatatacaatgcattccaccgcggtgtcccgtacacaa\n\ ggaaacttgttaccttggggatataagaaaactcacacgtctcattattaaactgagtac\n\ aatttttgcacgagaaagtaatgcaatacaatatgatgaaagccagctaatgaaaaggga\n\ tggaacgcacctcggatctgttgcactggattaaaatccgattatttttaaaaatattca\n\ gtgctagagcatatcaggtctacttttttatctggtatgtaaagcccacggagcgatagt\n\ gagatccttacgactcaacgaaaagttataacataactcccgttagccaaagcccaatcc\n\ cgattactgccctaccctaacgtctgccatctaaatatcgaacttgttatgatcaatgtg\n\ actacctcccaccctttccccttcatttgttccactggggataagctagcgttttcagaa\n\ tcaatgcaataagaatagccaattgtctcacttcatcagagctcttggcaattccaggcg\n\ ctacgtggttctggaatatattcatttttcaaatagtaatacgtttagtgttgctattgt\n\ ctacacgtttggatattacgttatgtgagcggacatcaatagttgtctaactctttagta\n\ agccagagatagcactcttagcgaatggataccatcttccataagtttagttaatagtcc\n\ gaaacaactgcttcgagcatatttgaacctccttgtaggcaaatagcctcttcaaagcaa\n\ tcttactaatagatagagtttgttttaagggactactagaaatgggacaatcttaatagt\n\ atgacctaaactgacatttaaagatatatccaggtggcaagcataaagatcattgcgcca\n\ cctccaccgtgggattacttatcagtcgatatcctatatgctaagtttgcgacggcagaa\n\ tacaaactaagctgagttgatgctaaccttacctatgataccccattggaccggttaaca\n\ gccctacttattccaaataaaagaacttttatgctgtagaagctattatagtgatgcctg\n\ gtaacttcagtatattaaaatgacacacatacgccatatagagctcctggaactttgaat\n\ aatgagcgaacttcgaagttgaagagcaagaaaccatatgtcacggttgcctaaagcccg\n\ gtaaccagacatgtgctatcattgatcattatcgaggttttcataaccttgacccattat\n\ cggctgtgcgcggacaagtacttaaatcactagtttcttcacctgcttatcggtaagaaa\n\ taaggttggcaaagaatcgcataagacggacgtagagccgcagcgttgtgcgagtccagg\n\ tgcatgcgcagcaataggattttaaattttgttccatttttaatttagccgtaaggatgt\n\ ccgtaaatgattgaaaattggattcaatctttgggcctatgctactggaacctgatcgac\n\ aaaatttcaaacatacgttaactccgaaagaccgtatttttgcggctagaatagtcagtc\n\ gcttggagccatataccttaccacttaaacgacgtgctcctgtagttgaaatataaacag\n\ aacacaaagactaccgatcatatcaactgaagatctttgtaactttgaggcgaagcaccc\n\ tcttcgagacaactaagagtaaagtaccgggcgccgcaaggagtcgattgggaccctaaa\n\ tcttgacgaattgctaagaggctcagagctaccactgtaatttctctagagcccataata\n\ aatgaacgatacatccgtaggtagcacctaagggattataatggaagccaaatgcagtta\n\ ataatattatatactggcgtacacgattcgacggatctctcacatagtgattcacgaccc\n\ ccccctttgattgacacagcgtcagcattttgcaagaacgatcttctgcatagggtgcgc\n\ caccgtaaggatgacgtcgaagctacaactgggtataatttaccatgcttccctgatgct\n\ gagtgcaatacactaagaatgagtttttaccccatatcaccagtatttgttctgttattg\n\ cgaagaaatggctatgctgagttggcgactaaagtcacccatcctttttattaggtaacc\n\ ccctcccttaaactaactgatttgctggagctgccctgcatacatatactttatcattta\n\ tggacgtccgtgacgcttattatccaccatagtcgatatgctacacggattcattaatgg\n\ atcgtaggagtttaagttatatttactaagatcggtctcggctactatcccgccttaccc\n\ ggcgctatttacggccatttttaatatattgacggtaattattcctatggtttcgaccgc\n\ acgtccttggacaagaaagaatggcaaaaaaaatgtaaaagaaaaaaaatattgagtccc\n\ taccatcatataaaaaatatgtgatgagtaacttgacgaaatgttagtggttattaaaga\n\ ctatctattacaccttttgttttctgtcgtagtatattaaagtctagaagccttacagga\n\ aaatcagggttatacagccgatactccgcagcatgaatcatcgaggaggtgtcctaccat\n\ cgcgccttgtaatcttgtctgtgtatactgtatttagaccttttatacaaagtaaatatc\n\ tcggctttatgtgattgggaggggcctactcaaacatgatgacttgacctaataatcact\n\ gtgcgggcgtcttatgactagctattccttgaaatccaccaccaaatggttaatatgtaa\n\ aaactttgacgatgaaacaaggtgaatgtgtagttactttgtgtaattagctgcgtcgag\n\ cattgcttgtaaaaccgtcaatcgcacacgttacttccataaaatttctacgaatacacc\n\ cttcttaaaaaaaacgtaggaattcacgagtttaacaaacgataactgtataaagtggaa\n\ gtccgaagaaagcagatgcccgaactactcgaagatgtttcgttttcttaaccatagggg\n\ cttcttaatggcccactacgcacattttgttcaagcccgagagggacatccccattacgg\n\ gagtattactaaaactgttccgtaatacgttcagcaagggatgaaaaaggccactgctca\n\ agttattgacgtgggagtattacatcggaagcctgaatcccacactatgatggtctgtac\n\ aggcctagggactgcgtctagacggtattaccggcttctaatcatacgatcgtgagtctt\n\ aacgggaagtaaggctcacacctaccccaaaccatttatctatgtaagtataaaattgtg\n\ cgtaagtgttcaaagtggacaataaagacgtggcaaaaacccccgcacataagccgcttt\n\ agatttcacaaataccaatgcggttaaaaacatccttgagtcgtacatacaccatactcg\n\ cgttaaacggatataacagaagataataaatccggatgtggagtcggtgtaactatagaa\n\ agccaagtgaaataatgcttaccagtcatttagctatacggctttcatttcatgtcaaga\n\ gggtggagtttgacctgtacagttgatatatcaccgatacttagaactcacctaaagcta\n\ aaattgctcgcagcgtgtaatccgcatattacaaacaatagatgggattcattatacata\n\ agacacgatgatctgctttttcaggttgcgagatgttgcctatcgtcaatcgagtcctgc\n\ cttacaccacttaaacaaaagtattgacagggaacctattttcgaggtattatatagtcc\n\ agcttgaatatcaatttgacagttaacctagtgaaaatcagtaagaggaaatacgccaca\n\ ttctccagtgaaattctacgggttatcgtctagtccaactatcaattataactcacgaga\n\ tataagtaaattctcgtacttggcctgatttttattatactttggatccttagtaaacag\n\ gaagggagaaaccttcaacgaaaaacactggattttgttttactctcaaagctcttatat\n\ gacggaaataccctgtcaagtcttaactttattactagactaatgaaatgggcttggggt\n\ ggccagaatcatagtacaatttagcggatacactattcggactttcctatcggctgtctg\n\ gttggataagtatggggactaataggctagacatacctatacttaaactatacaggcgtc\n\ atctatctctgcaactttggagttccctgatgttctcccgccctttgggttcacatcttc\n\ tataccgacacccctaataacgattagtttgtgggttagagtaaattaatacggttaata\n\ ttaatgtatcgttgaaaagctggtgtcgccaataaggtaaccggctaggcagagtatatg\n\ tcacgaagtataactaccctaatgataagctgtaggaataaaattaatgctgtctctaag\n\ cgaagagatatttccgactctgttttaatgacgaatctcattacttctgacttgcaaatg\n\ ttcaatatggcacggtttcacggcacctttgtgacgcatataatgaacttagaagattat\n\ aacgacggaactttatatgataatccgttacgattaaagaatctgttaaatatcataatg\n\ gcattcagttctagaccgtgcatcatggtaaacttactttctctgcatggcgacatacat\n\ ttcgctattcaaattcgcgtgtggttacacccactcgcacctttggaatattaagagaag\n\ atgatcagaaaatccattcgctcaatttttctgacgtacgtctaatttatcctaggagac\n\ aaatcgttttatgtctctcacatttttgaagaaaggttcgagagacaatactcaggtcct\n\ gaactgctagaagatactcggtggagcgtggcaacaatgaaaaactcgtgacataaatga\n\ atgatacttttccaagttcagttaagtgaatatgtttaacatacccggcttttcgatctt\n\ aagctgacgctggacgtgcgagtaatgtcagtctcttacatacactagtgactccaagtt\n\ tcgtcaaaaacgccccctcccttctcgagcccactcacgctatgtattgacgcgaacttg\n\ ttcgggatcagacttttcaggagttcggtcgcgtgtccctatgtgctaatatataagtta\n\ gatcgcattagatgctaatctgaatacttatagacgaccttcaacgagaacgggtaccac\n\ cttgaggctagagttaggtgtgaaacgacaggtagggacatataaaatttgagtgcggct\n\ ttagttaagggtttaattacctactcaaacatcacgctcgcgcccttcgtacgtaatcga\n\ ccatctagaggctaaggggactgtactaggtagtgattaatgatatcctagacgcacgtg\n\ ccttagatcttcagactctgatggtccgcgatcaccgtaattgtagtcctccaactcgat\n\ cactttgttggcgtcaaagaaattacgatatctaaatacttataatacaataaccaagga\n\ tgagaatgactcatcgcgttggagttatattgcttgaagttctatggaatgaaagcacgt\n\ tatctgccgtcccaatatctccagtgagctaattcattggacggtccactttgatcaatc\n\ cccgaggagatgttcggacactttagtctgtaacacttagcgttgagaccacgaacaatt\n\ gattactcagtcttgaaggtgttttccaaagttcattttaaataagactacgataggcct\n\ ttcctattgatataaactacccggctctgttgttcgtgtgagtcgtacttctctgtgttt\n\ ttctgattatagcaagattcgattcttagtgtaaacagcgatttttatttgacccgtcaa\n\ tgagaagcgcataggatctaagcaaaattatcaagttgtgccacaaggtaagatctttcc\n\ agttattgcaggtaggatgtatcccacgttgatagtatgaggtctgacgtcaactgtcta\n\ ggagagttgaccgcgtgcgggtacaccggatttgcatcgatgttgagaacgcagaactcc\n\ cactgtcgtggcggcgttcctgatatttagcaagaggcgttgataaagccctcatcatct\n\ agatctcgacctcatctgccctcttgctccatcattttctacacagactactttcctatc\n\ tacgttagtataattgctttctatcttagtatcatttagagcttctccgtcaacaggttc\n\ gtgctattaaagttagtacgaaagggacaacttgtagcaacgcatttaatcggttttcga\n\ ctacttcgcacaaaatcagataaagaagtttgtcattctattagacattgaattgcgcaa\n\ ttgacttgtaccacttatgatcgaacactgaatcaagactgtgattaactaaaatagaca\n\ agccactatatcaactaataaaaacgcccctggtggtcgaacatagttgactacaggata\n\ attaattggactggagccattacattctctacaatcgtatcacttcccaagtagacaact\n\ ttgaccttgtagtttcatgtacaaaaaaatgctttcgcaggagcacattggtagttcaat\n\ agtttcatgggaacctcttgagccgtcttctgtgggtgtgttcggatagtaggtactgat\n\ aaagtcgtgtcgctttcgatgagagggaattcaccggaaaacaccttggttaacaggata\n\ gtctatgtaaacttcgagacatgtttaagagttaccagcttaatccacggtgctctacta\n\ gtatcatcagctgtcttgcctcgcctagaaatatgcattctatcgttatcctatcaacgg\n\ ttgccgtactgagcagccttattgtggaagagtaatatataaatgtagtcttgtctttac\n\ gaagcagacgtaagtaataatgacttggaataccaaaactaaacatagtggattatcata\n\ ctcaagaactctccagataaataacagtttttacgatacgtcaccaatgagcttaaagat\n\ taggatcctcaaaactgatacaaacgctaattcatttgttattggatccagtatcagtta\n\ aactgaatggagtgaagattgtagaatgttgttctggcctcgcatggggtctaggtgata\n\ tacaatttctcatacttacacggtagtggaaatctgattctagcttcgtagctgactata\n\ ctcaaggaaccactgctcaaggtaggagactagttccgaccctacagtcaaagtggccga\n\ agcttaaactatagactagttgttaaatgctgatttcaagatatcatctatatacagttt\n\ ggacaattatgtgtgcgaaactaaaattcatgctattcagatggatttcacttatgcctt\n\ agaaacagatattgcccgagctcaatcaacagttttagccggaaacaatcgaagcatagg\n\ gacaatgtatcttttcctaaattgccatgtgcagatttctgagtgtcacgaagcgcataa\n\ tagaatcttgtgttgcctcaactcgttgaaaagtttaaaacaatcgcagcagtctttttg\n\ gggtctactgtgtgtttgcaaaataactgaaagaaacgcttgaacaactctgaagtagct\n\ cgagtactcattaaagtgtaacacattagtgaatatcggccaatgaaccaaacgcttccc\n\ ggtacgctatctctctcatcgggaggcgatgtgcaggttatctacgaaagcatcccttta\n\ cgttgagagtgtcgatgcatgaacctcattgtaacaatagcccagcaaattctcatacgt\n\ gcctcagggtccgggcgtactcctccatggaagggcgcgcatctagtgttataccaactc\n\ gctttttaactactatgctgtagttctacaggcatagtggccagtattttctaacttctc\n\ tggatagatgctctcactcctcatccatcacggcttcagtttacgtcttacttgcttgtt\n\ cagcaacggatggaggcattaagtatcttcactgttccctaaaattgctgttcaatatca\n\ aagtaaggacgatacagggaaagctcaagcacactcattgaatactgccccagttgcaac\n\ ctcacttaatctgacaaaaataatgactactctaagtgttgcggaagcagtctcttccac\n\ gagcttgtctgtatcacttcgtataggcatgtaactcgatagacacgaacaccgagtgag\n\ aaactatattcttgcttccgtgtgtgtgacaccaggtaattgatgcggatataagctgga\n\ gatcactcacgcccacacaaggcgctgctacctctttattccaatgtgtaagaatttgct\n\ aacttcatttctagaccgcagctttgcggtcataatttcacggtacggacccttgggtta\n\ gagacttgataacacacttcgcagtttccaccgcgcacatgttttagtggcttctaacat\n\ agaatttttgttgtgacataaagagtgcgtgggagacttgcccgaccgttaagccataat\n\ caattgaaagccccgtgagtcacatctaattggttgtactgcgcatttagctatccttta\n\ gctgactcgaagagattcgattcctaatataggttaattagatggctgccgcgcgaagta\n\ aaacgtgaaaaacgtagtgcgcagatctgcataactcgcgcttaattacttatgagtagt\n\ tccaagttcgctacgttatgagagagattggaattaagcaaatatgttttatggtgattt\n\ tgggatgagaaggactgctaagtacggctactaaacaaatttctaaaaccgccatctacc\n\ ttatcttggagacatttaagttgtatatgtcactagtctagcttttgtctgtgggacgcg\n\ ttctcggaatgagggaaatgcaagagccgattcatcaaatgcttatctaagaaagtagtg\n\ gactattacaccaagcacgaatgccagggaactgctttcttgctcaggacctcgcgacaa\n\ ggtaccccgcataagtcctagaattacatttggtcagcaatgctgacatttgaccgtgaa\n\ aacataattttaatcagaaggcagctcacccgcttgctctagatcttatctttgtatgaa\n\ tgtcagaatttactgcaatatccgttccgaatagtgagggcttagtatagttctctgtat\n\ acaggtcacatcaaactccccctgtcctagtacagctctgagctttaattaattgcatac\n\ atttccttcaatcatcagatgaaaacaccgcgaatcatgctcttctcgtatagggcaaga\n\ gaagcaacaaacaactagcccgactcacgttcatccgccgtatccttgttcagttcttac\n\ tccgtattaggtcagcgaaatctaatcagaataatcggtcgcgtatcaaaattaaaatcc\n\ cgcttgaggttgacaattaaaacgctgagcagttatcggctattagatagtggggtgaaa\n\ gtaattggctggaattatgttaaaacgtgatattaagctaaaatacgctacttgttgccg\n\ acctaattcagtcattcgatattcagttagagccaagaataacaagcttgtataaattga\n\ acggggtgcactaaacgatgtgttactctaatattcagcttggagtatacctgaaggcga\n\ attcatgtatcggccaataataagacgttgaagatcacaatttggactagcaaaagaagg\n\ tgatttatgcgtggggattgagtccactgtacgagtacggtctctggaaaattataggtt\n\ cagggaatataaggaagtaaagataattaccaagagatttttggtatcgctatgacccag\n\ aggtgttctaacgtctgttttgatccgcagaatttctgcctcaatgcatatttgacggac\n\ ttgaactagagcctctaaagttaaatggcgacgcaactgttcctaaacttcaattattac\n\ tactctttttttcctagggtattgtagaggccagtggacaaaataaatcaaatttaagat\n\ gtttcggacattaacatcccccgtagcatagaaatcatcagttatccaatctctcatcga\n\ gcttttacaatttctgctggcgctatggacagcatatgccgcgagacctccgcaagactc\n\ acttgatcactgtaagtatcttcattagaggttagagcctatagttaagctgctgaccta\n\ gtaaaattggtattttctaattttattgctcaagttaaaggttagtgaagggataatgac\n\ gttatttttgaacaatgggttgtattcaattttatatcacgaatggaacccttcattccc\n\ ggcataatactagacgacacgaacaagctccgatctatcagccaggcacgtgttaaggtt\n\ taattccggcaaaccaatgaagcatcaaaaggtgacctgatgcaacttagggtcacgatg\n\ agtttttcaggactacttattacctattaataagttaacatgagccttcataccccgtaa\n\ gacaatacatactccaccaattagaattctgagccatcttatctttttgtatcatcgaag\n\ ggtatggccgaataggttaattagttactcctaacgtctctacaggcatgcatttgacgc\n\ accttcgaaaatagtcaatctctcgccacacgcgtctagtatgcagcatcaaaaatatag\n\ tccacggtttccggattaccaaacgcggcaaagagaaacattgtatcgacggagataact\n\ taatacagaaggaaggggcatcttcgaatacggatgaataattctatctgtttattctga\n\ catcttgttttcaggttaatcttacgcattcaaatgacgcctgccccatgcgtgcgcaat\n\ tattttctaatattgacgagagcaatctcactccttttgggtctatttatgttttattga\n\ ggcacaagcctatacagaacaggtactattaaggccgtgagtgtgagactcaaaccgtgg\n\ aaacaaaggatgggttgttcttggtacaagttttagtgcatgtgggcaatccttaccaaa\n\ atcagatgctatccttaactttgggctgcatttaagatggcggttggaggcctgtgagaa\n\ tcctgcgtgtcatctttaatgaccgaattcatccatgtagattcagatcacacactcatt\n\ ccttgatgttgtctaaacaaaagttgttgtggacgcattggagggagttaagtaacaact\n\ tgggatcgcatacttataaaaattatatgttaaactttcacaaacgctgaagtccaaagt\n\ aactagcccaaacgcctcgagagtcactaggtattaatggtgtttgagttcctgtgaaat\n\ agtgttcgaaggtaaaatttatgtaccaaatcgaaagaacacttaataaggcttgcttgc\n\ acggaggtatgatgtttactgactctacaaccctaattttccagtacgtacattcattcc\n\ aataggttagttctcaaagtgctatacaggctcctcaattgatgatatgcttcagccgct\n\ ctatggatattagctcattttatttaggaagcccgcttagaggcttactatgagggaaat\n\ gccaaaatgtcatacttttcggtgtgtcccatatgacaccgctttacatagaatttgaat\n\ taaaacgcgctctcccgttcactaccatacttggtaccgtgcgcatattacatatagata\n\ taggatcattttttaaagctgtactaggtttgatcgacaatcttatgctatactatatga\n\ tgtaaccctcataatcaataccgatcgtacgatcctagcataggtggcaagcgattttat\n\ gccgattattgtgttaaatagtctgtgagtgtgattatcagggctacgttggtagagggg\n\ ttgtatagacctcgcacacattgtgacatacttaacaatatacgaaaactgatataataa\n\ atccccttacccaaacaccaatcccgttgaatcaactaccataacgtctcccatataaat\n\ tgcctacttgtttgcataaatctgaatacataacaccattgcaccttcttgtgttccaat\n\ cccgttaagattgccttgtcagatgatatgcaagaacaatagcatttgctagcaattatt\n\ aacagctcttcgaattgcctccacataacgcgggagggtatattttaatttggcaaatac\n\ taagtactgttggcgtcatatgctattaacggttggatattaagttatgtcagccgtaag\n\ caagagtgggcgaaatattttgttacccagtgagagcactcttagagtttggatacaata\n\ ggccatatgttgacttaagaggacgtaactacgccgtacaccattgttcaaccgacttct\n\ tggcaaatagaatcgtattagcaatcttaagaatagagacacgttcgtgttagggtatac\n\ tacaaatccgaaaatcttaagaggatcacctaaactgaaatttatacatatttcaacgtg\n\ gatagatttaacataattcagccacctccaacctgggagtaattttcagtagatttacta\n\ gatgattagtggcccaacgcacttgactatataagatctggggatcctaacctgacctat\n\ gagacaaaattggaaacgttaacagcccttatgtgtacaaagaaaagtaagttgttgctg\n\ ttcaacagatgatagtcatgacgcgtaacttcactatagtaaattgaaacaaatacgcaa\n\ tttagacagaatggtacggtcatgaatgacagtaattcgaagtgctagaccaacttaaaa\n\ taggtaaacgtgcccgaaaccccccttaacagaaagctgctatcatggtgcagtatcgac\n\ gtgttcagaaacttgtaacttttgagcaggtccgagcacatggaagtatatcacgtgttt\n\ ctgaaccggcttatccctaagatatatccgtcgcaaactttcgatttagtcccacgtaga\n\ gcccaagcgttgtgcgactccacgtgcatgcccagaaatacgagtttaaatttggttaca\n\ tggttaattttgaccgaagcatcgcactttatgattgataattggattcaatatgtcgcc\n\ ctatgcgaatgcaacatgatccacaatttggctataagacgtttaatccgtatcacactt\n\ tgtttgcggctagtatagtaacgcccgtgcaccaagagtcagtaacaattataagtactc\n\ cgcaggtacttcaaatataaaaactaatcaaacacgacccatatgatcatctgaagatat\n\ ttggaactttctcgacaaccaccctcgtactcaatacttacactaatcgacaggcacacg\n\ caacgtgtacagtcgcaccatattgagtcaagatttgcttagtggcgatgagcgtacacg\n\ cttatttctctagtcacaattagttatctacgagacatcacgagggagcaaataagcgat\n\ gttatggctacacataggcacgtatgaatatgatataagccagttaaacagtcgaaccat\n\ cgagcaaattctcatgcaccaacccacacgttgaggcacaaagagtaagctgtttgaatg\n\ taacttcttctgctgagcgggccccaacgtaaggatcaactagaagagaaaactcggtat\n\ tagtttaaatgcgtcacggagcatgagtgcatttcactaagaatgtctgtgtaaccaata\n\ taacatctatttgttatctgattgcctacttatggctttgcggtcgtggcgactaatgtc\n\ tccaatccttttgaggtcggtaccaactccctttaaattacgctgtgcaggctcatgcac\n\ tgcatacatatacggtagcaggtagggacctcacgcacccttattataatcaatagtagt\n\ tatcagtcaacgaggcaggaatgctgaggtcgaggtgttggtatattttctatgtgccgt\n\ ctaggcgactatcacgcattaccaggcgagatttaagccaattttgaatatagtcaacgt\n\ aatttttactatgggttccaccgaaacgccttgcacaactaagaatcccataaaatatcg\n\ atatcaaataaaagattgtgtcaataccttcatatatattttttcggttgactaacgtga\n\ actaaggttaggggttttgtatgtctatataggaaacagtttcttttctgtcctacttta\n\ gtaaagtcttcaagccttactccaaaatcacggtgattaagccgttactcagcagcatga\n\ ttctgcctgctcgggtcctaaaatccagccttgtaagagtcgctgtgtattagctaggga\n\ gacctttgttaaaaaggatatatcgcggcgggatgtgagtgcgtggcgcatactcaatct\n\ tcagctcgtgtcattataatatctctcccccacgcttttcactagatatgccgtgtaagc\n\ aaacaccttatgcttaatttcgaaaatattggtacttgaaaaaagctgtaggggtactta\n\ atgtctggtaggagatcaggagagaattgagtgtaaaaccgtaaagccctcacctgactt\n\ catgtaaatggcttagaagactccatgatttaataaatactacgaaggaaagactggatc\n\ taaagataactctagtaaggccaactcccttcaatgctgttgccagttataatccaagag\n\ ctgtccttttctgaaccatagcggcttctgaagcgaactagaagcaaagttggttctagc\n\ cagacagccacataccctgtacgggtgtattactaaaactggtccggtattagttcacca\n\ agggaggaattaggcaaaggatctaggtatgcaagtcggagtattacatccctaccctga\n\ atccatcaataggttcctctgtactggccttcgcaatgagtattcaaggttgtacagccg\n\ tataataataagatagtgactatgaacgggaagtaacccgctcaccttccccaaaacatt\n\ gttatatctaagtattaaagtctgccgtagtgttaatactcgaaaataaacaactggcaa\n\ attacaccgcacttaagccgcttttgatttatatttttccaatgcgcttttaaaaataat\n\ tcagtcctacatactaattaagacccttaaacggagatatcacaagttaagttttaacca\n\ tctcgactaggtggaactatagatacccaactcaatttatcattacctgtaatgttccta\n\ gaaggattgcatttcatgtcaagacggtggagtttcacagcgaaacttcagtgtgaacag\n\ attctgagaaatcacctaaacctattagtcagagcacccggttagaaccagttgtcaaaa\n\ aatagagcggttgcatgagacagaagtaacgatgagatccgttgtaacgttgagacatct\n\ ggcctatcgtcaatacagtcctcccttaaaaatatttttaaatactaggcaaacccaaca\n\ taggttagtcctatgtgatacgccacatggtatatcattttgtaacgttacctagggata\n\ atcaggaagtggaattacgcaaaagtagacagtgaaatgcttagggttatagtctagtcc\n\ aaagataaaggataaagcacgtcagagaactatattagccgaatgggaatcattgttagg\n\ agactgtggatcatgtctaaaaagcaacgcagaaacagtcatcgaaaaaatctcgttttt\n\ gtttgaatctaaaagagctttgatgaccgatagtacctgtatactagttactgtattacg\n\ tgtctaatgatttcggattggggtccccagaatcagacgtcattgtagacgattcaagtt\n\ taccaatttaatttcccagctctccttggagaactatcgccaataattgcagtcactttc\n\ cttttctgaaacgataaagccgtcagagttctctgcaacgttggacttacctgaggttct\n\ aacccactttcggttctaatagtagttaacgacacaacgaataacctttactgtggggct\n\ ttcacgatattttttcgcttattattaatggttacgtcataagctggtgtccaaattaag\n\ gttaccggcttcgcagagtagttgtatccaagtataacttccctaatcataagatcgagg\n\ tagaaaattaatgctgtctctaaccgaacagatatgtcccactatgtggtatggacgttg\n\ ctaattacttctgaagggaaattggtcattatggatacgtgtctaccatcaggtcggacg\n\ cagatatggttctgtcttcagttgatccaccgttctttataggataataactgacgatta\n\ aagattatggtaaatagattaagccaattctcttcttgtcagtgaagcatccttaactga\n\ cttgctctgcagcccctcatacatttagctattcaaagtaccggctcgtttcaaactctc\n\ ccacctttggaagaggttgtcaacttgataagtatatcatttacagcattttttcggacg\n\ tacctctaatgtttcattgcagaaaattagttttttctatcgcacattttgcaagtaacg\n\ ttagagacacaattatctgcgaatgaactgctagatctgacgaccgggagcctcgcaaat\n\ atcaaaaaagactgacatatatcaaggagtcgttgacaagtgctggtaagtcaattggtt\n\ tatctgtcccggcgtttcgatcttaagctgaccatgcacggcagagtaatgtcactctcg\n\ ttcttacaagtctgtctccaagggtcggcaaaaaagacccctccattctcgagcccactc\n\ acgatatgtagggacgacaacttgtgcggcttatgaattgtctggactgcgggcgagggt\n\ ccatatctccgaagttagaagggacatacctttagatgataagatcaattcttattgacg\n\ aaattcatccacaacggggaacaacttcaccctagacttacgtctgaaaagacacctagc\n\ gtcttataaaaggtcagtgccccgtttcgtaaggctggaattacctacgcaaacttaaac\n\ ctcgcgcccttccttacgtatcgacaagatagaggctatcgcgaatgtactacggaggca\n\ tgaatcatatactagaaccaagtgcctgtgatattaacaagatgatccgacgcgagcacc\n\ gtaattctaggcataaaactccagcaatttgggggccgaaaacaaatgacgttagctaat\n\ taattatatgacatgatcaaaggaggtcaatcacgcatcgagttcgacgtatattcattg\n\ aacttcgtgcgtttgaaagaaacttttatgaaggcaaaattgatcctgtctcctatttca\n\ tgcgtacctcctagttgataattccccgagcagtggttaggacacttttgtcggtatcaa\n\ gttccggtctcaaaacgtaaaattctgtaatctgtatggatggtctgtgaattagttaat\n\ ttttatgaagtcgtcgagacgcagttcctattgatttattctaaacggagatgtgcttcg\n\ tgggactcggaagtagatctgtgtttatgattattgctactttagatgctgactgttaac\n\ tccgtgttgtttttcaaccgtatatcacaaccgaattggatagaacctatagtttcaagt\n\ tctgccacaaggtatcatatttacagttagtgctggttgcttctttcaaacgtggtgagt\n\ ttgtgctatcacgtcaacggtagagctcagtggaccgagtgcgcgttcaaccctgttcca\n\ gagagggtgtgatagcacatataccacgctcgtcgaggcgttcatgatagtttgcaagag\n\ ccggtgttaaacacatattattattgttatccaactaatcggacctatgcataaagcatt\n\ gtctaaacagaataattgcctatatacggtagttttagtgatttatatcttagtatcagt\n\ tagagcttcgaactcttcaggttcctcatatttaacgttcttcgaaagcgaaaacttcta\n\ caaacgaatgtaagcggttttccaagtagtacctataaatcacagaaagatctgtctcag\n\ tatagttgaaatggtattcagctagtgacgtgtaccaattatcatagttcactcaagcaa\n\ gacgctcattaacgaatatagacaagacactatatcatataataaaaaagaacatggtgc\n\ tcgaacatagttgaattcaccatattgaaggggaatgctgacatgtaattcgctactaga\n\ cgatcaattccctacttgtcaaagttgaactggtacgttcttggaattaaatatgattgc\n\ gctggaccaaattgcgacttcttgagtttcagggcaaacgattgagccggaggatgtccg\n\ tctcttacctttcttgcttatgataaacgacggtccctgtacatcactgggaattctcag\n\ caaaaataattgggtaaatcgagactcgatgtattcggccacaaaggtgttagacgttaa\n\ agattattcaacggggcgataataggatcataaccggtatgcaagcgcattgaaagagcc\n\ atgagatccttatccgataaacgctgcacggtatgtgcagccttattgtcgatcacgaat\n\ ttataaatgtagtctgggctgtaagttgaagacctaagttataatgaagtgcaataccaa\n\ atcgattcatagtggattatcagactcaagatatctcctgataaattacagttgttaaga\n\ tacggataaaatgagatttaagattagcagcctctaatctgtttcaatcccgttggaatg\n\ tggtatgcgatcaaggttaagttaaaatcaagcctgtcttcagtcttgattcttgttctg\n\ ccatcgcatgcggtctacgtgagttaatatgtagcttacgttctagcttgtgctaatctg\n\ agtatagattcgtagaggaatattatcaagcttccacgcctcaacgtacgtgtattggtc\n\ acacaagacactaaaagtggaagtagcgtaaactatagtctagttgttaaatgctcagtt\n\ cttgttatattcgatatactcttggctaatttatgtctgagtatataaaattaatgatat\n\ taacttgcatttcacggatcccttagaaaaagattttgaccgagcgcattataaacggtt\n\ acaccgaatcaatagaagcatacccaatagctttctttgaatttattgcctgcgcaactt\n\ ggctgactctctagatccgaataattctatatggtcgtgacgaaactagttcattactgt\n\ ttaaaatgccaacatgtcttttgggccgataatggctctttgcaaaattactcaatgata\n\ cgattgatcaaagcggtagttgctagtggtagcatgtaagtctatcaaatgtctgattat\n\ ccgaaaatcttccaaaagagtccacgtaccatatctatctcatagcgacgcgaggggaac\n\ cttatctaactatcattccatttaccgggtgactctcgatgcaggatccgattgggataa\n\ attgcccagaaatggctcattcctgactaagggtaaggccgttctcagcaagggaacccc\n\ gcgaatctaggcttataccatctagattgttaactacttgcctgtagttctacagccata\n\ ctggacagttgtttctaaatgatcgggattcatgctagcactcctctgaatgcaccgcgt\n\ aagtttaactattacgtccgtgggcagataaggatggaggctgtatgtatcttaactgtt\n\ acctaatatggctggtaattatcaaagtaaggaccttaatgccatagcgctagcaatcgc\n\ tttgtatactgaccatgtgccaacctctcttaatctgtaaaatataatgtcttagctaac\n\ tgtggacgatcatgtctctgcctagagcttcgctgtatcaattcctatagccagcgtact\n\ agtgacacaacaacaccgtgtgagaaaagatattagtccttacgtctgtctctctacagc\n\ ttattgatgaggattgaacatggacatatagctccccctcaaaagcagatgctacctctt\n\ tattccattctcgaacatttgccgaacttaatttcgacaaacctgaggtcacgtcttaat\n\ ttatcggtaacgtcacgtccctttgagactggataaatatattaccaggggccaacgagc\n\ aattgttggaggcgcttctataatacaaggtgtcttgtcaaagaaagacggcgtgcgtct\n\ cgtgcaactcacttaaccaatattaatgtgaaacccccctctctcacatcttatgcggtg\n\ tactgccctggtacatttcctgtacaggactccaacagtgtagattcctaagatagctgt\n\ tggagttgcctcacgccagatcgaaaaactgaataaactagtgagctgagctgcagaaat\n\ accgcttaattacttatgactagttcaaagggacctacgtgatgtcagacattgcaagga\n\ agaaattaggtttgtgcgtcattttggctggactagcactccttacttcccctactattc\n\ aaatgtcgtaaacagcatgagacaggatcgtgctgacatttaaggtctattgggaacgag\n\ gctacctttggtcgcgcgctcgcgttctccgaatgaccgaaatgcatgagcacagtatgc\n\ aattgcttatagatctaaggtctggtcgttgaaaccaagcacgtaggcctgggaaatcag\n\ ttcttcctcagcaactacacaaaagcgtccaagcattagtacttgtagtaaatgtccgaa\n\ cctatgcgctcatttgaaagtcaaaaaatatttttaagcagtaggcacctaacccgattc\n\ ctctacttagtagctttctttgattctcagaattgactgcaatatcactgcacaattctg\n\ tgccattactagacttctctgtattaacgtctcatcttactaacactcgcctaggacaca\n\ tctgagagtgaagtatttcaatacatttactgaaatcttcagttctaaaatccccgaata\n\ aggctcttatcggtttggccaacacaagaaaaaaacttcttgcaccactcaccttcatac\n\ gcaggagcctggggaacttagtaataactatttcggcagacaaagcttataacaagttgc\n\ cggcgcgtataatatttaaaagaccccttgagctgctcaattaaaacgctcacctggtat\n\ aggctattagatagtgccgtcttagtaaggggcgggaattatcggataaactgatatttt\n\ gataaaataaccgacttgttcacgacataagtcactaaggagattttatctttctccaaa\n\ gtatatcttccttggataatttcaaagcgctgcaatttaagttctgttactagtttatgc\n\ tgctgggaggtgaccggaaggcgtagtaatctagaggcaaattataagaagttcatcata\n\ tcattttcgactacaaaaacaaggtgttgtatgccggcgcattgtgtaaactggacgagt\n\ accctagatggaaaattatacgttaagccaagatttcgatgtaatgataattacctacac\n\ atttttgctatccataggaacaagagctgttctataggctcgtggcatacgaacatttgc\n\ tgccgctatgaatattggaagctcttcaactacagactctattcttaattgccgtcgaaa\n\ atgggccgaatcggctattattaatactcggtttttccgaggggattgttgtcgacagtc\n\ gtaattattattaatattgatgttggtgaggtcatttaaatacaaccttgcagacaatga\n\ ataagggatccaatctctcatactccttttacaattgctcatgcccctatgcaaacctta\n\ tgccgccacacctccgcaactctctcttctgaactgtaagtagcttcattactggtttga\n\ gactatactgaagctgatgacattctaaaatggctattttcgaatgtgattcataatgtt\n\ tatcgtttgggatggcagaatcacgttatttttgatatagcccgggtattctattgtata\n\ gaacgtatgctacaagtcattccccgaagaagactagaagtaaacaacatgcgaccatcg\n\ ttaagccacgcaaggctgtagctttatttcccgataacctatcttccataaatagcggac\n\ agcaggatactgacgctcaacatcagtggttatggtctaatttttaacttttaataaggt\n\ aacttcagcaggcatacacagtaactctttaatttataatcaaattagaagtctgacact\n\ tcttatatttttctatcatccaacgcgatcgcccattagcttattgtgttactaataacg\n\ tatctaaaccaatccttttcaagctactgcctatattgtcaatatatacaaacaacagga\n\ tagtaggctgcttaaaaaatattgtcaaccgtgtacgctttacaatacccggaaatcaca\n\ aactttgtagacaacgagtgaaatttatacactacgaagggccagcgtacaagacccatg\n\ aattaggcgatatgtttattctgacatattggtttatccttaatctgtcgctgtaaaatg\n\ aagccgcccccatccctgcgaattttttttcgaagattcacgactgaaatataaatacgt\n\ ttggctatatttatgttggagggaggcaatagcctttactgttaaccgaagatttagcca\n\ gtgagtgtgacactaaaacactggaataaatgcaggcgttcttctgggtaaaaggtttag\n\ tcaatctcgcctataagttcatatagctctggatataattatctggcccatgcatttatc\n\ atggcgcttggtgccctgtgtgaagccggcctctcatattgaaggtccgaagtattccat\n\ gtacattaagatcactctctcattcatgcatcttggcttaacaaatctggttgtccaagc\n\ tttccaggcacgtatggtacaaattcggatcgaatacttataaaaatgatatgttaaact\n\ gtctaaaacgctcatctacaaagtaaagtgcactaaccaatagagtctcaagaccgtgta\n\ atgctggtgcactgaatgtgtaatacggttagaagggattagttatgttacaaatccatt\n\ gaaaacttaagaagcattgcgtgctcggagggtgcatcttttatcaagagactaacatta\n\ ttttcaacgacgtacatgctttacaatagggtacttatcaaacgccgagaaacgcgccta\n\ tagtgatgttatgattatgacccgatatccattggaccgaattttatgtaggttcccagc\n\ gtactcgcgtaatatctcggtattgccataatgtaatacttgtcggtctctcccagatga\n\ aaaagcgttacagagtatttcaatgaaaaacagcgcgcaacgtcaatacctttaggggta\n\ acggccgctgatttcatatagatatacgataagttggtatagctctactaggtggcatcc\n\ acaatcgttgcatttactatagctggttacaatcataatctataccgttccttacatact\n\ accatagcgggatagcgtttttttgccgttgattgggtttaagaggatgtcagtctcatt\n\ atatccgattcggtgggagagccgttgttttcaaatcgcacactttgtgacataatgtac\n\ aagataacaaaactgatataagatataaactgtcaatatcaccttgacacttgaatcaaa\n\ gtaaattaactcgcaaatataatttgactaattgggtgcagatttctcaattaataaaaa\n\ aatggcaccggatgggcttacaagccccttatcattcacttgtatcatgatttccaagaa\n\ caatagaatttgctagcaagtatgaacagagattcgaattgcatccacagtacgccggag\n\ cgtttattttaatgtggatatgacgatgtactgttggcggcatttgctagtaaccggtcc\n\ ttatttacgtagcgcacacgtaagcatgtctgggagaaatatggtggtacaatctcagag\n\ aaagattacagtttggtttaaataggacttatcgggtcggaagtggaacttaataagcag\n\ tacacaattgggcaacagacgtcttgcctattacaataggattacaatgcgttagatttc\n\ agacacgttcgtgtttggctattcgtcaattccctaaatagttagacgatcaactattat\n\ caaagtgattctttgttcatcctccattcatgtaacagatggcacactacgcataacgcc\n\ gaggaattttaacgagatttaagagagcagttcgggcacaacccacttgactttataaca\n\ gctcggcagcataaacggtaatatgtgacaaatttccaaacgttataagaacgtatgtgt\n\ acttagaaaactaagtggttcatgttcaacagatgtgacgcagcaagcctaacttatcta\n\ ttggttttgctataaaagaacaaagttacacagaatcctaagggcttgtttcacacttat\n\ gcctagtgcttcaccatcttaaaatagcgaaaccggcacgaatcaaaccttaaaacaatg\n\ cgcagatattggtgatggtgactccgggtatgataatggtaactgttgaccagcgcccac\n\ ctcatcgaagtatagaaagtggttaggataaggatgagaccgaacttatttccggccata\n\ actttagattttctacctagtacacaacatcagggcggacacgaaaccgccatcacatca\n\ tataccaggtttaatttgcttaatgggggaagtgtcaacgaaccttcgaactttagcagg\n\ catatggccattatatatggccccagagcagaatgctacagcagacaaaatttggattta\n\ tgtagtttaatacctatcaaacttggtgtgaccatacttgtctaacgacagtgcacaaag\n\ tgtaagttacaattattactactcagcagcttctgcaatgataaaatcttatcatacacg\n\ tcacatatgataatatctacttagggggaacgggctccacaacctacatagtactcaata\n\ cttacactattcgacaggcacaccaaacctgtacagtcccaaaagattgagtcaactttg\n\ cagtactgcagatcacagtaatagcttagttagcgagtcaaaattagttttctacgagac\n\ tgcacgaccgtgcaaatttccgatgtgttggctacaaatagcaacgtatgaatttgtttg\n\ aagccacgtaaactgtacaaccttagagataagtctcaggctactaaaaacacgttgtgg\n\ cactaacaggatcatggttgattcttacttattcggctgaccggcccaataagtaacctt\n\ caactagaacagaataatcgggagtagtttaattcagtcaaggtgcaggtctcattgtaa\n\ ctaacaagctctgtgtaaccaagttaaaatcgttttcttagcggattccctacttatgga\n\ tttgagctcgtccacaatattcgatacaagaagtttgtggtccgtaacaacgaaatttta\n\ attacgctgtgcagcctcatccaaggaattaatagaaggttgatggtaggctccgaacgc\n\ tccatgattataatcaagtggactgtgcagtaaacgaggaaggtatcctgacgtcgtggt\n\ gttcgtttttgttatttgtgccctatacgagtagataaaccatgaacagcacagtgtgaa\n\ cccatggttgattttaggctaccttatttttaatttccgttacacagaaacgaattccac\n\ aactaacatgccattaatttttcgatatcttataaaagatggtcgaaattcattcattta\n\ ttttttttcggttctcgaaagtcaactaagctgtcgcgttttgtttctctttagaggtaa\n\ aagtggctttgatctcctacgtttggatactagtcaaccattactccatttgatccgtga\n\ gtatcacctgtctaacatccagcattatgactcctcggcgaagaaaagacacacttctta\n\ gagtcgatgtgtattagctagggacacagttgtttaatacgatagtgagcccagggaggg\n\ cagtgcgtcccccagtagatttattcagctagtgtaagtataagatatctcacccacgag\n\ gttcaagtgatatgcagtcttagaataatacttatcctgaatttcgatattatgggtact\n\ tcaataatccgctagcgctactttatgtctcgttggacagcaggacacatggcagtctta\n\ aacactaaagacatcacctgaatgaatgtaatgggattacaagaatcaatgaggtattat\n\ atacgacgtaggaaactctggatatatacagtaatctagttacgccatcgcacttcattc\n\ ctctggaaacttagaagacatcagctgtacgtggaggaaccagacccccgtatgtagcca\n\ aatagaaccaaagttgcttatacaaacacacccaatgacaatggaccgctggagttcgta\n\ aactcggaacgtagtactgcacaaacccagcatttagcaataggagctacgtatgcaact\n\ cccacgtggtaataccttcaagctatcaatatataggtgcctagctaatcgcattcgcaa\n\ gcagtattcaagcttgtaaaccagtataataattacagaggctctatgaaacccaacttt\n\ ccagctaaaagtcccaattaaatggttatttcgtacttttaaagtcgcccgttctgttat\n\ tacgcgaattgattctactccaaaattaaacacaaattatcaaccgtttcatttatattt\n\ gtcaatgcagctgtttaaaataaggctctactaaattataattaagacacttattaccag\n\ atttctctagttaagtttgaaccagctcgactaccgcgaaagatacattcccttctctat\n\ ttttcagttcatctatgggtcagagaagcattgaatttattctattcaccctcgtcgttc\n\ acagcgaatcgtcagtgtgatcagtgtatgagaaatatcctaaaccgtttagtcagacca\n\ cacgcttagaacaagtggtctaaaaagactgccctggaaggagtaagaagtatacagctg\n\ atccggtgtatccttcagtcatctgccctatactaattacacgacgcaaggaaaaatagg\n\ tttattttctaggcaaacccttcataggtgactccgatgtgttacgaatcatgcttgaga\n\ atgtgctatcgttaccgacggataataacgatctccaatgaaccaaatgtagaatgtcta\n\ ttgattacccttttactattcgacttagagataggagatagaacctcagtgtactttttt\n\ agccgaatgggaatctttgggaggtgaatggccataaggtcgtaaatccaaccctcttaa\n\ agtcttccatattatatcgttgttcgtggaatcgataacagatttgttgacccatagtaa\n\ atgtatactagtttatgttgtaagtgtagattgttttccgattgccgtccaaactttatg\n\ tcgtaattgtagaccagtaaagttgaccaaggtaagtgcccagcgatcctgcgagatcga\n\ tcgccaatttttccagtcactgtaagtgtaggtttagataaagccgtatgagttatatca\n\ taagggcctcggaaagcagcttcgaaccaaagttcccttataatagtagtttaactataa\n\ aagtatatactggtctgtcgccctttcacgatttgttttaccggtttatgaagcgttacg\n\ tcattagagcggctccaatttaaggttaacggcttccatgtgtagttgtatacaaggata\n\ acttaaagtatctgttcagcgagctagttaagttatcctcgatagaacacaactcagagg\n\ tcccaagatcgggtttgcaacttgctaatttattctcaaggcaaattgggaattatcgat\n\ acctgtataccataaggtcgctcgatgtgatgcttatgtcttctggtgatcctaccttag\n\ ttagtgctgattaacggaacattaatgtttatcgttttgagatttagccaattctctgat\n\ tctaactcaagatgccttatctgacgtgctatgcagcccctaagtattttacattgtaat\n\ aggacacgctcctttaaaactcgccaaaaggtcgttgtggttctctactggttaactata\n\ taatttacagctttgttgagctagttcctctttggtttaagtcctcaatattagttggtt\n\ cgagcgataagttggctagttaccttagtcactatattagatccgaatgttatgcttcat\n\ ctgaagaccgccaccctccaaaatttcttttaagactcacttattgcaaggtgtaggtga\n\ attcggctcgtttctcaagtggtgtatctgtacacgagtttccatattttcatcaacagc\n\ caccgcacacttatgtcactctaggtattaaaagtcgctctacaaggggacgcaattaag\n\ aaacagacatgctagtcaaaaataaacatagcgaggcaccactaattcggccgcttatca\n\ atgggatgctctgcgcgagacgcgccagagctcagtagttagttcggacatacatttact\n\ tcagatgatcaattagttttctacaaatgcttactctaccccgaaaaaagtcaccagact\n\ cttacgtctctttagtatccttccgtcttatataaggtcagtcccccgtttcggtaccct\n\ ggaatttactaagaataatgaaacagcccccaaggacgtacgtttacaaatgatagacca\n\ gatcgcctagcttattccgacgcatgttgcatagaattgaaccaacggaatgtgagagta\n\ actagatgagccgaccacagcacccgtttgcgtcgcagaatacgcctgatagttcggcca\n\ cgaaatcatatgtcctttgagtattaagtatttgtaatgatcaatcgagctcaagcaagc\n\ ttacacttcctcggatattcagggaacttagtgcctttgaaagatacgttgatcaacgaa\n\ aaattgataatggctcatatggaatgcctacctcatagtgctgaattaacacagcactgc\n\ ggacctaacttttcgaggtttcaagttcacgtctcaaaacctaataggctggaatatgta\n\ gggatcctcggtgaatttgtgattgggtttgttgtagtactgaccaagtgaatattcttt\n\ ttttctaaaagcagatctgctgccgggcactacgaaggagatctctgtgtatcattattg\n\ cttcttgacatgatgactcttaaatcactgtgggtgtgcaaaacgatagcacaacccaat\n\ tcgatagtacatattgttgatacttcgcactaaaccgttcatatttaaaggttgtgctcc\n\ ttccttcgttaaatactggtgacttggtcctatctactattagctagacctctggggaac\n\ cacgcccccgtaaaacctgtgcaagagagggggtcatacatcttagacatcgcgcctcca\n\ ccagggaagcattgggtgattgaccaggtgtgtaacaaatatgattattcttatactaat\n\ attagcaaagatgcataatgatttgtattaaatgtataattgaattgataagggtctttt\n\ agtcagtgatagagtagtataaggtagacattagaactcttaaccggacgcagatttttc\n\ ggtcttagtaagccaattagtcgacaaaacaaggtaagagcggttactagtagtacctat\n\ aatgcactgaatcttcggtcgaagtatagttctaatgctatgcagattgtgacggcgaca\n\ aatgttcagacttatatcatgaaacaagctcttgtaagtattgacaaatgaaaagattga\n\ atatttttaaatacaaaatgcgcctacttattaggggaattaaccagattgaaggccaat\n\ cctcacatgtaatgagataatagacgataaatgaaattcttgtaatagttgaactgctac\n\ gtgatgggtattatatatgattgagatcctccaattgccgacgtcttgtcttgatgccca\n\ aaagattgtcaacgaggagctccctcgcgtacctgtcgtccgtatcataaacgacgcgac\n\ atgtacagcactccgaagtataagcaataataatgcgggtaatccagactagatcttttc\n\ ggactcaatgcggtttcacggtaaacatgattaataccggagagtagtcgagcttatcag\n\ cgatgcaagcgaattcattgtgccaggagatacgttgcagataaaaccggcaacgtatgt\n\ caacaagttttggcgatctcgttgtttgtattcgacgaggcgcgggaacttcaagaacta\n\ tcgtatattcaagtccattaccttttagtttcagactggtggagctgactaaagttatat\n\ catcattttgtacactggtttagttaacgataatttcagatttaacatgaccagacgata\n\ atcgctgtatatccagttggaatgtggtttgccagaaaggttaacttataatcaagcctc\n\ tcttcagtcttgattcgtcgtatcccatccattgcgctatacctcagtgtatttggagct\n\ gtagttataccgtgtgctaagatcagtagacatgacgagagcaatattatctaccttaca\n\ agcatcaacggacgtctagtcggaacaaaagactctaaaactcgaacttcaggttaatat\n\ actatagttctgtattcagcagttattcttatattcgatattatcttgcctattggatgt\n\ ctgactttagtatattaatcatagtatctgccatgtaaaggtgccagtactaaatctgtt\n\ tcacagtgcgaattataaacggttacaaccattaaagacaacaagaccctatagctttat\n\ ttgaattttgtcaatgcgcaacttggagctcgcgatacatcccaattagtctatagggtc\n\ gggacgattctacggcatttctggttataatgacaacatggattgtggcccgagaatcgc\n\ tctttcattaattaagcaatcattacagtcttataagcgctacttccgagtggtagcagg\n\ taactcgatataaggtcgcatgagccgaatagcttaaaaaacaggccaccgaacattgat\n\ agagaataccgaccacagcgcaacctttgattactttcattaaattgtacggctcactcg\n\ acatcaagcttaagattgcgataatgtgaactcaaatggatcagtactgaagaaccgtaa\n\ cccacttcgcagaaagcgtacccagagaagatacgctgttacaatatacagggtgaaatt\n\ attgcctgttcttcgtaaccatttcgccaaacttggttagaaatgatagccattcatgat\n\ agaaataagctgaatgataccagtatctttaactatgtagtcagggggaagataacgatg\n\ gtccatgtatgtttctgatatgtgacagtattggccgcgtaatttgctaacgaagctact\n\ taatgcctttgagcttcatatagatttctttaatcaaaatcggcaaaaagatagtatgag\n\ ctataatatatgctagtagagaactctggaccatcatctatatgaatactgattcgagcg\n\ tgcaattactttagcctgcgtactactgactctacaaaacactctgagataagtttgtag\n\ tcagtaagtcgctctctataaaccttttggatgaccattgtacagccacttatagatccc\n\ aataaatagcacaggagacagagtttttcaatgctcgatcatttgccgatagtattttcg\n\ tctaacctcagggcacctattatttgatacctaacctaacggccctttcacaatggagaa\n\ atatatgacatcgggacaaacacaaatggtgggtggccaggagatatgacatggtggcgt\n\ ctctaagaaacacggactccctctaggcaaactcacgtaaccaattttaatgtcaaacaa\n\ aacgctcgaaaagattttgccgtgtaatgacctggtacattgactggtcaggaatacatc\n\ actgtagttgccgtagtgtcctgttggtgttccatcaagacacatcgtataacgcaattt\n\ acgacggacatcagatcaagttatacagattatttaagtatcacgtgtgcattgggacat\n\ aagggatctcacacatgccttggaacatttttgctttgtgccgctttttcgctgcactac\n\ caatccttacttaccagtatattcaaaggtcgttaacagaatgagaaaggttagggctct\n\ aagttatcgtcgattgggatagacgagacatttgcgagcgccctccacggatacgaatct\n\ cccatatcaatgtgaactggatgctatgcagtttagttcttacgtctcctagtggtaaaa\n\ atcaaagtagcactcgcatagcagttattcagaacctaatacacaaaaccgtcaaacatt\n\ ttctaattctaggtatgggccgatcataggagctaaggtgaaactcataaatgttttgtt\n\ agatctagcatcctaaaaagatgcatatactgagtagctggcgtgcattctctcaattgt\n\ atcctttttaactgaactagtcggtcccatttcgtgactgagatctattaaccgataaga\n\ ttaataacactcgcattcgtatcagctcagagtgaagtttttcaataatttgactgatat\n\ attaacttctaaaataaccctttaagcctcggatccgtttcccaatcacatcaaaaattc\n\ ttattccaactatctacggattaacaacgtgcatggggatcgtagtaagaacttgttccg\n\ atcactttgagtatatcaagttgacggcccggttattattgaatagaaacattcacctgc\n\ taaattaaataccgcacatcggatacccgatttcagagggccgtcttactaagggcaggc\n\ tttgttcggtttaactgagatgttcattattttacagtatgcttcaactaatatgtaacg\n\ aaggacagtggatctgtctccatagtagatcttcagtcgtgaatttcataccgctcctat\n\ ttaagttcgcgttcgagttgttgatcatggcacgtgaaagcaacccctagtattctagac\n\ gaaaattttttctagttcatctgataatttgccaattcaaaaacaaccgctggtttcccg\n\ gcgcattctctaaaatggaagtcgaacctagagccattatttgtcggtaacccatgagtt\n\ ccttcttttcagaagttaatacactgtggtcctatacagaggaaaaacagcggttatata\n\ cgatcgtggcataacaacattggatcaagatagcaatttggctacctattctaattctca\n\ ctagattcggtattccactacaatatcggcagattaggattggatgaataatcggtgttt\n\ aagtccggttgcgtctccaatctcctaatttttattaatattgatcttggtgacctattg\n\ taaataaaaacttcaagactttgaataacggtgaaaagatagaagactcatttgaaaatg\n\ gatcatccacagatccaaacattagcaagacactaatccccaactagctattctgatcgc\n\ gatcgtgctgcagtactcctgtcacaatagtctgttcatgatctaattctttttgggctt\n\ tgttcgatggtgattcagaatctttatccggtcgcttccctgtagctactttgtggggat\n\ attgcccggggattatagggttgagatcgtttcctaaaagtatttaaaccaagtagactt\n\ caactaaactacatcagaacatcgtgaagacaccatacgcggtacctttatttaccgata\n\ acatttcttcaagaaataccggtaagcagcataatgaccctaaacagctcggggtatcgt\n\ cgtagttttaaattttatttaggttactgctcaaggaataaaaactaactatttaattta\n\ taataatattacaaggctcacactgattagatttgtctataagacttcgcgatcccccat\n\ taccggattgtcttaagaataaactagataaaccatgcattttctagataaggcctttag\n\ tctaattagatacaaaaaacacgatagttgcatccttaatttattgtgtcaaacctggaa\n\ ccttttaattacccgcaaatcactttatgtcgagactacctctgaaatttattatctacc\n\ taccgcatgaggacttgaaccatcttgtaggagttatgtttattagctaagattcgttta\n\ tcctgtagcggtccatgtatattcaacaagcaaaaagcactcagaattgtttttagttga\n\ gtcaagactgatatataaataagtttccctagttttttcgtggtgggacgatattgaatt\n\ gaatcttaaccgaagagtttcccactctgtcgcacaataatacacgccaatatttccagc\n\ cctgcttatgccttaatcggttactcaatctcccattgaagttcattttgatctgcatag\n\ aagtttcgggcccagccttttttctgccaccttcctccaagctctgtagacgcactctaa\n\ gattgatgctcacatgtattaattctacattaacataaatatataagtcatgcatcttcg\n\ agtaaaatatctggttctccaacatgtcctggcacgtatcgttataatgcccatacatgt\n\ agtattaaaatgattgggttaactggatattaagatcatcgaaattgtaaagtcaaatta\n\ acaatactgtctcaagaccgtgtattcctcgtgctcggaagggctattacgcttacttcc\n\ gttttggtatcttaatatgactttcaaaaattaagttgcagtgagtcctacctgcgtgca\n\ tcggttagcaagagtataaaagttgtttaaacgaactacttgctttacaataccggtcgt\n\ atatatcgccgtgaatccagaagattgtcttctttggattatcaaccgagatcctgtgga\n\ ccgatgttttgggaccttcacagaggactccaggtagagctcgcttttgcattaatctaa\n\ gaattgtacctctctaaaagatctaaaacagtgaatgtgtatttcatggaaaaacacaga\n\ gaaacgtaaattactttaggccgaaaggcacatgagttattatacatatacgagatggtg\n\ gtatacatcgaattcggggcatacactatagttgcattgtatttagctgctttaaataat\n\ atgatattaccttccttacataagacattaccggcataccctggttttcaacttgtgggg\n\ ctttttgacgatcgcactctcatttgatccgagtagggcggtgacccctgcttttcaaat\n\ acaaaaatttcgctatgaaggtaatagattacttttcgctgttatgatagaaacggtaaa\n\ tttaaaattgaaacttctagaaaagtaaagtaacgagaaatgattttgtgaataatgcgg\n\ tcatgattgcgcaagtaagaaaaaaaggcaaaaggatgcgcggaatagaaacttatcagt\n\ cacgggtatcttgatttcattcttcttgtcaattgccgacataggatgaaatcagattcc\n\ aatgcaatacacagtaacccccacccttgattgtaatgtcgatttgaagttgtacgcgtc\n\ gacgaagtggatagtatacgggccttttgtacggtgcgatcaactatgaatctcggcgag\n\ ttagatggtcgtacaatctcacacatagaggtcacttgcctgtaatgacgaattttcggc\n\ taggtactcgaactttattagaagtaaaaatgtgggcaaaagaaggattccattttacaa\n\ gacgattacaatgagttacatgtctctcaacgtagtctttccctagtagtctttgaacta\n\ tttaggtactccagaaaattttagcaaagggtttctgtgtgaatccgccattcatgttta\n\ tgatggaacaataagaataacgccctcgtatgttatcgacagtgaagtcagcagttcggc\n\ caaaaacatattcaatttagtacagatccccagaagttaagctaagtgctctaaaatggc\n\ ctaaacggttatcaaagtaggtctaattactatactaacgggtgcatcgtaataactgct\n\ gtcgatgcaacactatatgatagtgtcgttttgctatatatgtacaatgtgacaaagaag\n\ ccttagcgattcttgcaaacttaggacttcggattctcaatcttaaatgtccgaaaacgc\n\ aaagattcaaaaatttaatctatgagcagatatgcctgatggtgactacgcgtatgttaa\n\ ggctaaatgttgacaaccgcacacataatcgaactattgatagtcgggagcataaccagg\n\ tgaacgtactttgttcacgacatttattgacatgttctaaatacgtctcaaaatcacggc\n\ gcactagaaaacgcaatcaaatcattgtcctggtttaagggccgtaatgccggtagtgtc\n\ aaacttcatgagaactttagctggcttttggccagtatttagggaccaagagcactagcc\n\ ttaagctgaatattttgccatttatctactgttataactttaaaacttggtggcaccaga\n\ cttgtcgatacacacgcatcaatctgtaacgtaaaaggtttactaagaacaagcgtagga\n\ attgagtttatattatatttaaactaaaagatgatattagcttctgagggcgatagggct\n\ ccaaatcataaagaggaatatattattacacgattagaaacccacaacatacctcgaatc\n\ gcccaaaagtttgacgaaacttggcagtactccacatctcagtaatacagttgggagagt\n\ ctcaaatgttgttttattactcaatgaaccaccctcataatttcactgctgttccattaa\n\ atttgcaaacgatcatttgctttgaagaaacgtaaaatcgacaaaattacagataagtag\n\ atgcataataaaaaaaactgctcgctataacacgatcatcgtgcattcttacttaggagc\n\ atcacccgcacaataacgtaccttaaactacaacactattagaccgagtactgtaattca\n\ cgaaagctcaagctcgcattgtaaagaacttgctctctcgtaaaatgtgataatagtttg\n\ cggagaggattcaattattttccattgcacctactccactagattcgataaaagaaggtg\n\ gtcctcccttaaaaagaaatgttaagtaacatcggaaccataagcaaagcatgtaagtga\n\ accgtcatccttccctaagaaacataaaggtttttaataatgtcgactgtgaactataac\n\ tgcatcctttcctgacctactccggttccttgttgttatttctgaacgagaccagtagat\n\ aaacaatgtaaaccacagtgggtaccaatggtgcatgtgacgctaccgttgttttaagtg\n\ cccgtacaaacataagaagtcataatcttacttgaaattaattttgccttttattttttt\n\ tcaggctcgaaattaatgatttgttttttttgaccttctagttacgctaatatgcggtcg\n\ cctgtggtttctattgagtcctataacgggatgggatctaatacgtttggttactagtaa\n\ acaaggtataaatttgataccggagtatcaactgtataacatcaagctttatgactcata\n\ cgcgaagtaatgacacaaggctttcaggagatcgcgagtacagagccactaaggggtgta\n\ ttacgatagtgacaccaccgagcgcactcactccccaagtagatttatgatcctacgcta\n\ agtattagatatataaccaaagaggttctagtcagtgcaactcttagaataataattagc\n\ cggttttgcctttttaggcctaatgcaatattcagctagcccttatgtatctcgcgttcc\n\ acagcaccactcatggcacgcgtttaaactaatcaaatataatctatgaatgttatgcca\n\ gtacttgaataaatcaggttttttataagtccttgcatactctcgttatatactgttaga\n\ gtcttaccccatagaaattctttcatctgcaaacttagaagaattctcagctacggggag\n\ cataaagtccccaggatgttgacaaatacaacaaatgtggcttatacaaacactccatat\n\ gaaaatcgaaccctcgtggtagttttagccgaaccttgtacggataaatccctccatttt\n\ ccaatagcagatacctatcctactacctcgtggtattaaattaaagcttgaaatatagag\n\ ctgcatagcttatccaattcccaagcacgagtctaccgtcgtaaccacgatttgatttac\n\ agacgctagagcaaacccatctttaaacatataagtaaaaattaaagggtgagtgcgtac\n\ gtgtttactagcaacttcgcttattaagacaattgtttataagccataattaaaaacata\n\ tgttcaacaggttcattgatatttgtaattgcacaggtttttaataaggatctacgtaag\n\ tataatgaacaaactttttaccagagttatattctgtactttgaaaatgctcctctaccg\n\ ccttagagactttcaattagattttttgcagttaatctatgcgtaagtgaaccatgcaag\n\ ggatgcgattcaaccgcctcgtgctaaccctatcgtctgtctcataactgtaggtctaat\n\ ataattttcagttttcgaacacataaccctttgaaaatctgctatttaatgtctcacctg\n\ catgcactatcttctatactgctcagaacggctatacgtcactatgctccaagtgacgat\n\ ttaaacgaagcaaggaataataggtttattttagtgcaaaacaattaagtgcggactacg\n\ tgctctttacaataagccttgtgattgggctataggttaagtcccatattaacgatctcc\n\ aatgtacaaaatcgacaatcgctttgcattacccggttactagtcgaattacagatagct\n\ gttagatactcactctaattttggacaacaatcccaatcttggggtcgtctatcgcctga\n\ agctcgtaaatccttccatcttaaacgattacatattatagacttgttcggggtagagat\n\ atcacagttgtgcaaacattgtaaatcgatactagtttatgttggtagtctagttgcttt\n\ taccattccccgaaaaacttgatctactatttcgacaacagtaaacttgaactaggtaag\n\ tgaaaacagagaatgcctcatagtgccactatttgtccactatatgtaagtgtagcttta\n\ cataatccactatgactgagatcattacggcctaggaaagcagcgtagaaaaaaagggcc\n\ cggatattacgactgtaactataaaactagttactggtagcgcgccatgtatagatttgt\n\ tttaccggttgtggttgcgttaacgaatttcagccgcgaaaattgatccgttaaccagtc\n\ catctcgacttctataaaacgataaagtaaagttgatgttcagcctccttcttatggttg\n\ catcgagagtacactactcagtgggaaatagatcggggttcctacttcagattgtattat\n\ ctaggcaattgccgattgtgccatacctggataaaataagctacctacatgtgatgctta\n\ tctattatcgtcatactaccttagggtgtcctgttgaacgctacattaatctttagccgt\n\ ttgagatgttccaatggataggagtctaacgcatgatgaagtttaggaaggcagagcatc\n\ ccactaagtatgtgacagtgtatttcgaaacgagacgttataaatagaaaaaaggtcctt\n\ ctggttctattctgctgaactattgaatggaaagattggttgacctacgtactatttgct\n\ tgaagtcatcaatttgacggggtgagagacatatggtgcatactttacggactctatatt\n\ ttagatcagaagcttagcagtcttctctacaccccctcacgacataattgcttttaagaa\n\ tctatgtttgattcctctacgggaattcggatccgttcgcatgtgcggtttatctaaacc\n\ aggggacatatgttcagctaaagcatacgaacactttgctaactagacgtatgtatagta\n\ gctataaatcccgacgatatttacaaaaagaaatgagactcaaatatatacatagcgacc\n\ ctacacttattcgcaccctgatctaggcgatcctagcacccacacccgaaagtgagcact\n\ agtgtcttccgtattaaatttactgcagttgagattttagttgtctactaaggattactc\n\ taacccgtaataaggatcaagactcggtactagctttactatcattccctatgtgttttc\n\ ctaactcacaagggtacgtaccagcctatgtaattacaataatgataaagacacaaagga\n\ agtaactttacaaatgagtctccagttacactagcttagtccctcccatcttgctttgaa\n\ gtctaaatacgcaatctctgaggatatacagcagaagaacactcataacgttggagtcca\n\ agaattagactcatagggcccccaacatttaatatgtactgtgagtttgaaggtgttcta\n\ ttgttaattcctgctcttgatacatgacacgtactccgtgtttaaggcttcggactgact\n\ ttctttcataagttgagcaacgaaaatttcagaatcgataagttggattcactaactaat\n\ acggctgattgaaaactccactccggacctatatggtcgacctttatacgtaaccgatat\n\ aaaacttataggctggtatatcgagccttcctagcgcaatttcggatggggtttcttcta\n\ ctactcaacaacggaatagtctttgtttagtaaaccagagctcaggacgcccaatacgta\n\ ggagagcgctgtggagcatgtgtcattatggactggagcactcttaaatcactctgcgtg\n\ tgctaaacgatagatcataacatgtcctgagtaaattttcttgatacgtcgcaatatacc\n\ gttattagttaaacgttctcatccgtcatgcgtgaaatacggctgtcgtgctcagatata\n\ ctattagcgactcatctcgcctaacacgcacacgtataaactcggaatgactgccgctct\n\ tacatattagaaatacagactacaccacggaagcattgggtcattctcaaccgctgtata\n\ aaagatgattagtcttataataagattaccaaagaggcagaatcatgggtagtaaatcta\n\ ttattcaagtgattaccgtcgtgtaggcagggagtgaggacgagatggtactcaggacaa\n\ atattaaccggacgaagtggtttacgtcgtactttcactattagtagtaaatacaaggta\n\ acaccggggaatagtactaaatataatgatatctatcttcgggagaacgagtcgtctatt\n\ gctttgaacattctcaaggcgtaaaatgtgctgacttatagcatgatacaaccgattgtt\n\ acttttgtctattcaaaagattgaatagttttttatacaaaagccgcatacttatgacgg\n\ ctagtatacagtttcatcccctagcatcaatgctatggacagtattgaacttataggaaa\n\ ttcttctaatagggcaaatccgtcgtgatgcctattttttttcagtcacatcctcaaatg\n\ gcactagtattgtcgggatcccattaacaggctcaaccacgagctcacgcgaggacatgt\n\ agtccgtatctttaacgaagcgacagcgacagaactcccatggataaccaattataaggc\n\ ccgtaatcctctagacatcgtttaccaataaatccgctttctccgtaatcatgttgaata\n\ ccccagagtagtccagatgataaccgatgaaacacaagtctttctcaatgcacttacggt\n\ gaacttattaccgccaacgtagctcatcaaggttgcgacatctagttgtgtgtttgcgac\n\ gagcccagcgaacttcatcaactttcgtatattcaacgccttgtaattttactttaagac\n\ gcctggtgatgtagattcttagataatcagtttgttatcggctgtactttaccataattt\n\ cacaggtttcaggtcaagaagattatagctgtatatacagttccatgctcggtgcacaga\n\ aacgtgatcggataataatcaatcgcttatgtcgtctttaggcgtatccaatacatgccc\n\ cgataccgcagtgtatttcgacatgtaggtataccgtcgcatttgagctcgagtcaggac\n\ gtcagctagattagattccttaatagaatataccgacctctagtccgaactaaactatag\n\ ataacgccaacttcaggttaattgtctagtcgtctgtttgcagatgggattcttagatga\n\ gtgagtatcggccatattggttcgagcactttagtttttgatgcataggatatgcaatgt\n\ atagctgaaagtactttatctgtttcaaactcacattgattaaaccggtaaacctttaaa\n\ gactacaagaaaatattcagtgagggcaattttgtcaatcacaatcttccagctagagat\n\ acttcacaatttgtcttgaggctacgcaacattagacggattttcgcgttttattgaaat\n\ aatcgaggggcccaagagtatccatagttcattttgtaagatttctttacaggcttatta\n\ cagcttcttcagactcctacatgcttacgagttatatgctagcatgtgaacaatagatta\n\ atatacaggaaaacgtacattgagagagatgaccctacacagcgcaaccgttgagtactt\n\ tcattaaagggtaacgctctcgagacagcatccttaagatggccttattgtcaaatcatt\n\ tgcagaagtacgcaagatccctaaccaacgtagaagaatccctacaaacacatgagacgc\n\ ggtgaaaatagacagggtgttagtattcaatcttcggagtatcaatttcgccaatcttgg\n\ tgagaaagcataccctttcttcagagaaagaagatcaatcataacactatctttaacgag\n\ gtacgcacgcgcatcattacctgcctccatggatctttaggatagcggaaagtattggca\n\ gcgtattgtgatttcgttcctactttatcaatttcacattcatatacatgtcttttatca\n\ aaatcgccaataagataggatgagctatattagatgctagtagagttcgcgccaacatca\n\ tcgataggaatactcaggacagcgtgataggacttttcaatccctaatactctctataat\n\ tataactctctcttaagtttggaggcagtaacgcgctctatataatcagtttgctgcacc\n\ attcttcagcctctgatacatacaaataaattccacagcagtaagagggtttaattgaga\n\ catcttgggaacttaggattttactctaacatcaccgaaacgattattggataccgtacc\n\ taaacgaactttctcaaggcagtaatataggacatccgcaataacacaaatgctgcctcc\n\ ccaggagttatgtcttcctggaggctatatcttacacccactcactataggcaaactaaa\n\ gtttaaatgttgattgtctaaaaaaaagatagataagagttggccggcgtagcacatgcg\n\ aaagtgaatcgtaagctataattctctggacttgaagttctgtcctgttcctctgcaaga\n\ aacaaacttcctttaaagctatttacgacgcacatctcagcaagttataaacatgttgga\n\ agtttctagtcggaattcccaaagaacggatctatctaatgcattcctacatttttcctg\n\ tctgccgatggtgccatcctattcaaagaatttcttaaaagtagattaaatgggactttt\n\ aacaatgagtaaccttacgcctctaagggttcctcgagtgccatacaccagtcaggtccg\n\ agccacatacacggagaacattctaacatagcattctcaactcgatcatttgcaggttac\n\ ttctttcctatcctagtgctaaaaatcatacttgcaatcccatagcacggattaagaacc\n\ taagaaacaattcagtaaaacatgttcgaattcttggtatgggaacatcattgcagctat\n\ ggtctaacgcattaatgtttgggtacatcttccatcatataaacaggaagagtctgacga\n\ cagggagtgcttgcgatcatgtctatcattgtgaaatcaaattgtagctcacatgtcgtc\n\ tatgagagcgtgtatccgataagatttagaaaaatagaagtcgtataagatctcactgaa\n\ cttttgaatgaatgtgaagcatatatgatctgctttaataaaactttatccataggatac\n\ gtttccaaatcaattcaataattattagtcaaaatagataaggatgaacaacctgaaggc\n\ cgatcggacgtagaaagtggtcccatcactttgagttgatattgttgaaccacacgttat\n\ tatggttttcaaacagtctcaggatattgtatatacagataatccgataccagttgtctg\n\ acgcccctcttacgtaccccaccctttgtgacgtttaaagcagttgttcagtattttaaa\n\ ctaggcggcaactaatttggaaagaagcacagtggatatgtctaaattcttgttattcag\n\ gcctgaatttaatacaccgcatagttaacttcgcggtagagttgttcatcatgcctcctc\n\ taagctaccacttctatgatacaccaatagttgttctacggaatctgataattggccaag\n\ tcataaacttccgctgcgttcaacccccttgctcgaatatccaactcgaaaagacagcct\n\ tttggtgtccggaacaaatcagttacttcttttctgatgttaattctctgtggtcagata\n\ cagaccaaaaactccgcggatttaccatcctccaagaacaaatttgcatcaacatagcat\n\ tttggctacatattctaagtctcaatagtttaggttttcaactacattatcccaacatta\n\ ggattggaggaataatagctgggtaagtccccttgcgtctacaatcgactattttttatg\n\ aatatgcttctgccgcacctatggttattaaaaaagtcatgactttgaagaaccctgaaa\n\ agatagatgaatcaggtgtaatggcagcagccaaagagcatataattagcaacactctaa\n\ gaacattatagatatgatgatagcgatcgtcatgatgttatccggtcacaatagtagctt\n\ catcagctaattcgttttgccagtggtgacttgcgctggaagaatcgttatacggtccct\n\ tccctcttgatacggtgggggcttattcaaccgcgtggattgggttgtcatacttgcatt\n\ aaacgatgtaaaccatctagtagtcaactatactaaatcacaaaatagtgatcaatacat\n\ acccgcttcatggttttaaccatttaattgattaaagatattccgctaagaaccattatc\n\ tacctaaactgatcgccgtatcctagtagtttgaaatttgatgtaccgtaatgatcaacg\n\ aagtaaaacgttatattgtatgtagaataataggtcttggagctaaatgatgtgattggt\n\ agtgaagacttacccttacaactttaccggtttctcggaagaatatactagagaatcaat\n\ gcatgggctacataagcactttagtctaatgagataaaaaatacacgagtcttccatcat\n\ gaattttttgtcgaaaaactcgaacctggtaatttaaaccatatatctttatgtcgtcaa\n\ taactctcatatgttttatataacttcccaatcacgacttgtaactgcttgttcgactga\n\ gctgtttgagctatgaggccgggatccggttgagctacatctatttgctacaagaaaaat\n\ gaaagcacatttgttgggagttctggctacactcatagagaaataagtggcccgagtggg\n\ tgcggcctgcctccatattcaagtgtatcttaaaccaagtggttccaacgctcgcgctaa\n\ agaattaaagcctttatttcctccacggagtagcccgtaatccggttcgaaagagaccat\n\ tgaagttaattttcatatccagtgaagtttaggcacaagcatgtgttctgccacatgcct\n\ caaagcgctcttcaaccaagatatgattcatcctaacttcgatgaatgcgtctgtaacat\n\ aaatatagaaggaatgattcggcgagttaattttcgccttctccaacatggcatccctac\n\ gttcgttataaggaccatacatgtaggttttaaaggtttgcggttaatcgatatttacat\n\ catagaaattctatagtcaaatttacaagactctagatactcactcgttgcagccggcta\n\ ggaagcgctttgtaccttacttcccttttcgttgcgtaatatgaatttcatatagtaagt\n\ tcaaggcactcatacctccgtgaagagggtagatagactattaaagttgtttaatagtac\n\ gtattgatggaaatgacccgtaggagatttaccactcaatccacaagattcgctgctgtg\n\ cattatcaaaacagtgcatgtcgaaacatgggttgggtccttcaaacacgaatccaggta\n\ gagatacctttgcaattttt\n"; dnaInput = dnaInput + dnaInput + dnaInput; var ilen, clen, seqs = [ /agggtaaa|tttaccct/ig, /[cgt]gggtaaa|tttaccc[acg]/ig, /a[act]ggtaaa|tttacc[agt]t/ig, /ag[act]gtaaa|tttac[agt]ct/ig, /agg[act]taaa|ttta[agt]cct/ig, /aggg[acg]aaa|ttt[cgt]ccct/ig, /agggt[cgt]aa|tt[acg]accct/ig, /agggta[cgt]a|t[acg]taccct/ig, /agggtaa[cgt]|[acg]ttaccct/ig], subs = { B: '(c|g|t)', D: '(a|g|t)', H: '(a|c|t)', K: '(g|t)', M: '(a|c)', N: '(a|c|g|t)', R: '(a|g)', S: '(c|t)', V: '(a|c|g)', W: '(a|t)', Y: '(c|t)' } ilen = dnaInput.length; // There is no in-place substitution dnaInput = dnaInput.replace(/>.*\n|\n/g,"") clen = dnaInput.length var dnaOutputString; for(i in seqs) dnaOutputString += seqs[i].source + " " + (dnaInput.match(seqs[i]) || []).length + "\n"; // match returns null if no matches, so replace with empty for(k in subs) dnaInput = dnaInput.replace(k, subs[k]) // FIXME: Would like this to be a global substitution in a future version of SunSpider. // search string, replacement string, flags closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/string-fasta.js0000644000175000017500000000350614433667662026747 0ustar apoapo// The Great Computer Language Shootout // http://shootout.alioth.debian.org // // Contributed by Ian Osgood var last = 42, A = 3877, C = 29573, M = 139968; function rand(max) { last = (last * A + C) % M; return max * last / M; } var ALU = "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" + "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" + "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" + "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" + "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" + "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" + "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"; var IUB = { a:0.27, c:0.12, g:0.12, t:0.27, B:0.02, D:0.02, H:0.02, K:0.02, M:0.02, N:0.02, R:0.02, S:0.02, V:0.02, W:0.02, Y:0.02 } var HomoSap = { a: 0.3029549426680, c: 0.1979883004921, g: 0.1975473066391, t: 0.3015094502008 } function makeCumulative(table) { var last = null; for (var c in table) { if (last) table[c] += table[last]; last = c; } } function fastaRepeat(n, seq) { var seqi = 0, lenOut = 60; while (n>0) { if (n0) { if (n= x1) { IncX1 = 1; IncX2 = 1; } else { IncX1 = -1; IncX2 = -1; } if (y2 >= y1) { IncY1 = 1; IncY2 = 1; } else { IncY1 = -1; IncY2 = -1; } if (dx >= dy) { IncX1 = 0; IncY2 = 0; Den = dx; Num = dx / 2; NumAdd = dy; NumPix = dx; } else { IncX2 = 0; IncY1 = 0; Den = dy; Num = dy / 2; NumAdd = dx; NumPix = dy; } NumPix = Math.round(Q.LastPx + NumPix); var i = Q.LastPx; for (; i < NumPix; i++) { Num += NumAdd; if (Num >= Den) { Num -= Den; x += IncX1; y += IncY1; } x += IncX2; y += IncY2; } Q.LastPx = NumPix; } function CalcCross(V0, V1) { var Cross = new Array(); Cross[0] = V0[1]*V1[2] - V0[2]*V1[1]; Cross[1] = V0[2]*V1[0] - V0[0]*V1[2]; Cross[2] = V0[0]*V1[1] - V0[1]*V1[0]; return Cross; } function CalcNormal(V0, V1, V2) { var A = new Array(); var B = new Array(); for (var i = 0; i < 3; i++) { A[i] = V0[i] - V1[i]; B[i] = V2[i] - V1[i]; } A = CalcCross(A, B); var Length = Math.sqrt(A[0]*A[0] + A[1]*A[1] + A[2]*A[2]); for (var i = 0; i < 3; i++) A[i] = A[i] / Length; A[3] = 1; return A; } function CreateP(X,Y,Z) { this.V = [X,Y,Z,1]; } // multiplies two matrices function MMulti(M1, M2) { var M = [[],[],[],[]]; var i = 0; var j = 0; for (; i < 4; i++) { j = 0; for (; j < 4; j++) M[i][j] = M1[i][0] * M2[0][j] + M1[i][1] * M2[1][j] + M1[i][2] * M2[2][j] + M1[i][3] * M2[3][j]; } return M; } //multiplies matrix with vector function VMulti(M, V) { var Vect = new Array(); var i = 0; for (;i < 4; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3]; return Vect; } function VMulti2(M, V) { var Vect = new Array(); var i = 0; for (;i < 3; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2]; return Vect; } // add to matrices function MAdd(M1, M2) { var M = [[],[],[],[]]; var i = 0; var j = 0; for (; i < 4; i++) { j = 0; for (; j < 4; j++) M[i][j] = M1[i][j] + M2[i][j]; } return M; } function Translate(M, Dx, Dy, Dz) { var T = [ [1,0,0,Dx], [0,1,0,Dy], [0,0,1,Dz], [0,0,0,1] ]; return MMulti(T, M); } function RotateX(M, Phi) { var a = Phi; a *= Math.PI / 180; var Cos = Math.cos(a); var Sin = Math.sin(a); var R = [ [1,0,0,0], [0,Cos,-Sin,0], [0,Sin,Cos,0], [0,0,0,1] ]; return MMulti(R, M); } function RotateY(M, Phi) { var a = Phi; a *= Math.PI / 180; var Cos = Math.cos(a); var Sin = Math.sin(a); var R = [ [Cos,0,Sin,0], [0,1,0,0], [-Sin,0,Cos,0], [0,0,0,1] ]; return MMulti(R, M); } function RotateZ(M, Phi) { var a = Phi; a *= Math.PI / 180; var Cos = Math.cos(a); var Sin = Math.sin(a); var R = [ [Cos,-Sin,0,0], [Sin,Cos,0,0], [0,0,1,0], [0,0,0,1] ]; return MMulti(R, M); } function DrawQube() { // calc current normals var CurN = new Array(); var i = 5; Q.LastPx = 0; for (; i > -1; i--) CurN[i] = VMulti2(MQube, Q.Normal[i]); if (CurN[0][2] < 0) { if (!Q.Line[0]) { DrawLine(Q[0], Q[1]); Q.Line[0] = true; }; if (!Q.Line[1]) { DrawLine(Q[1], Q[2]); Q.Line[1] = true; }; if (!Q.Line[2]) { DrawLine(Q[2], Q[3]); Q.Line[2] = true; }; if (!Q.Line[3]) { DrawLine(Q[3], Q[0]); Q.Line[3] = true; }; } if (CurN[1][2] < 0) { if (!Q.Line[2]) { DrawLine(Q[3], Q[2]); Q.Line[2] = true; }; if (!Q.Line[9]) { DrawLine(Q[2], Q[6]); Q.Line[9] = true; }; if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; if (!Q.Line[10]) { DrawLine(Q[7], Q[3]); Q.Line[10] = true; }; } if (CurN[2][2] < 0) { if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; } if (CurN[3][2] < 0) { if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; if (!Q.Line[8]) { DrawLine(Q[5], Q[1]); Q.Line[8] = true; }; if (!Q.Line[0]) { DrawLine(Q[1], Q[0]); Q.Line[0] = true; }; if (!Q.Line[11]) { DrawLine(Q[0], Q[4]); Q.Line[11] = true; }; } if (CurN[4][2] < 0) { if (!Q.Line[11]) { DrawLine(Q[4], Q[0]); Q.Line[11] = true; }; if (!Q.Line[3]) { DrawLine(Q[0], Q[3]); Q.Line[3] = true; }; if (!Q.Line[10]) { DrawLine(Q[3], Q[7]); Q.Line[10] = true; }; if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; } if (CurN[5][2] < 0) { if (!Q.Line[8]) { DrawLine(Q[1], Q[5]); Q.Line[8] = true; }; if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; if (!Q.Line[9]) { DrawLine(Q[6], Q[2]); Q.Line[9] = true; }; if (!Q.Line[1]) { DrawLine(Q[2], Q[1]); Q.Line[1] = true; }; } Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; Q.LastPx = 0; } function Loop() { if (Testing.LoopCount > Testing.LoopMax) return; var TestingStr = String(Testing.LoopCount); while (TestingStr.length < 3) TestingStr = "0" + TestingStr; MTrans = Translate(I, -Q[8].V[0], -Q[8].V[1], -Q[8].V[2]); MTrans = RotateX(MTrans, 1); MTrans = RotateY(MTrans, 3); MTrans = RotateZ(MTrans, 5); MTrans = Translate(MTrans, Q[8].V[0], Q[8].V[1], Q[8].V[2]); MQube = MMulti(MTrans, MQube); var i = 8; for (; i > -1; i--) { Q[i].V = VMulti(MTrans, Q[i].V); } DrawQube(); Testing.LoopCount++; Loop(); } function Init(CubeSize) { // init/reset vars Origin.V = [150,150,20,1]; Testing.LoopCount = 0; Testing.LoopMax = 50; Testing.TimeMax = 0; Testing.TimeAvg = 0; Testing.TimeMin = 0; Testing.TimeTemp = 0; Testing.TimeTotal = 0; Testing.Init = false; // transformation matrix MTrans = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; // position information of qube MQube = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; // entity matrix I = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; // create qube Q[0] = new CreateP(-CubeSize,-CubeSize, CubeSize); Q[1] = new CreateP(-CubeSize, CubeSize, CubeSize); Q[2] = new CreateP( CubeSize, CubeSize, CubeSize); Q[3] = new CreateP( CubeSize,-CubeSize, CubeSize); Q[4] = new CreateP(-CubeSize,-CubeSize,-CubeSize); Q[5] = new CreateP(-CubeSize, CubeSize,-CubeSize); Q[6] = new CreateP( CubeSize, CubeSize,-CubeSize); Q[7] = new CreateP( CubeSize,-CubeSize,-CubeSize); // center of gravity Q[8] = new CreateP(0, 0, 0); // anti-clockwise edge check Q.Edge = [[0,1,2],[3,2,6],[7,6,5],[4,5,1],[4,0,3],[1,5,6]]; // calculate squad normals Q.Normal = new Array(); for (var i = 0; i < Q.Edge.length; i++) Q.Normal[i] = CalcNormal(Q[Q.Edge[i][0]].V, Q[Q.Edge[i][1]].V, Q[Q.Edge[i][2]].V); // line drawn ? Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; // create line pixels Q.NumPx = 9 * 2 * CubeSize; for (var i = 0; i < Q.NumPx; i++) CreateP(0,0,0); MTrans = Translate(MTrans, Origin.V[0], Origin.V[1], Origin.V[2]); MQube = MMulti(MTrans, MQube); var i = 0; for (; i < 9; i++) { Q[i].V = VMulti(MTrans, Q[i].V); } DrawQube(); Testing.Init = true; Loop(); } for ( var i = 20; i <= 160; i *= 2 ) { Init(i); } Q = null; MTrans = null; MQube = null; I = null; Origin = null; Testing = null; LoopTime = null; DisplArea = null; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/access-nsieve.js0000644000175000017500000000133114433667662027067 0ustar apoapo// The Great Computer Language Shootout // http://shootout.alioth.debian.org/ // // modified by Isaac Gouy function pad(number,width){ var s = number.toString(); var prefixWidth = width - s.length; if (prefixWidth>0){ for (var i=1; i<=prefixWidth; i++) s = " " + s; } return s; } function nsieve(m, isPrime){ var i, k, count; for (i=2; i<=m; i++) { isPrime[i] = true; } count = 0; for (i=2; i<=m; i++){ if (isPrime[i]) { for (k=i+i; k<=m; k+=i) isPrime[k] = false; count++; } } return count; } function sieve() { for (var i = 1; i <= 3; i++ ) { var m = (1<> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i+10], 17, -42063); b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); } /* * These functions implement the four basic operations the algorithm uses. */ function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } function md5_hh(a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); } function md5_ii(a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); } /* * Calculate the HMAC-MD5, of a key and some data */ function core_hmac_md5(key, data) { var bkey = str2binl(key); if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); var ipad = Array(16), opad = Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); } /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safe_add(x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); } /* * Bitwise rotate a 32-bit number to the left. */ function bit_rol(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); } /* * Convert a string to an array of little-endian words * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. */ function str2binl(str) { var bin = Array(); var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz) bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); return bin; } /* * Convert an array of little-endian words to a string */ function binl2str(bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); return str; } /* * Convert an array of little-endian words to a hex string. */ function binl2hex(binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); } return str; } /* * Convert an array of little-endian words to a base-64 string */ function binl2b64(binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; for(var i = 0; i < binarray.length * 4; i += 3) { var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(var j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } return str; } var plainText = "Rebellious subjects, enemies to peace,\n\ Profaners of this neighbour-stained steel,--\n\ Will they not hear? What, ho! you men, you beasts,\n\ That quench the fire of your pernicious rage\n\ With purple fountains issuing from your veins,\n\ On pain of torture, from those bloody hands\n\ Throw your mistemper'd weapons to the ground,\n\ And hear the sentence of your moved prince.\n\ Three civil brawls, bred of an airy word,\n\ By thee, old Capulet, and Montague,\n\ Have thrice disturb'd the quiet of our streets,\n\ And made Verona's ancient citizens\n\ Cast by their grave beseeming ornaments,\n\ To wield old partisans, in hands as old,\n\ Canker'd with peace, to part your canker'd hate:\n\ If ever you disturb our streets again,\n\ Your lives shall pay the forfeit of the peace.\n\ For this time, all the rest depart away:\n\ You Capulet; shall go along with me:\n\ And, Montague, come you this afternoon,\n\ To know our further pleasure in this case,\n\ To old Free-town, our common judgment-place.\n\ Once more, on pain of death, all men depart." for (var i = 0; i <4; i++) { plainText += plainText; } var md5Output = hex_md5(plainText); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/date-format-tofte.js0000644000175000017500000002440214433667662027665 0ustar apoapofunction arrayExists(array, x) { for (var i = 0; i < array.length; i++) { if (array[i] == x) return true; } return false; } Date.prototype.formatDate = function (input,time) { // formatDate : // a PHP date like function, for formatting date strings // See: http://www.php.net/date // // input : format string // time : epoch time (seconds, and optional) // // if time is not passed, formatting is based on // the current "this" date object's set time. // // supported: // a, A, B, d, D, F, g, G, h, H, i, j, l (lowercase L), L, // m, M, n, O, r, s, S, t, U, w, W, y, Y, z // // unsupported: // I (capital i), T, Z var switches = ["a", "A", "B", "d", "D", "F", "g", "G", "h", "H", "i", "j", "l", "L", "m", "M", "n", "O", "r", "s", "S", "t", "U", "w", "W", "y", "Y", "z"]; var daysLong = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var daysShort = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; var monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; var monthsLong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var daysSuffix = ["st", "nd", "rd", "th", "th", "th", "th", // 1st - 7th "th", "th", "th", "th", "th", "th", "th", // 8th - 14th "th", "th", "th", "th", "th", "th", "st", // 15th - 21st "nd", "rd", "th", "th", "th", "th", "th", // 22nd - 28th "th", "th", "st"]; // 29th - 31st function a() { // Lowercase Ante meridiem and Post meridiem return self.getHours() > 11? "pm" : "am"; } function A() { // Uppercase Ante meridiem and Post meridiem return self.getHours() > 11? "PM" : "AM"; } function B(){ // Swatch internet time. code simply grabbed from ppk, // since I was feeling lazy: // http://www.xs4all.nl/~ppk/js/beat.html var off = (self.getTimezoneOffset() + 60)*60; var theSeconds = (self.getHours() * 3600) + (self.getMinutes() * 60) + self.getSeconds() + off; var beat = Math.floor(theSeconds/86.4); if (beat > 1000) beat -= 1000; if (beat < 0) beat += 1000; if ((""+beat).length == 1) beat = "00"+beat; if ((""+beat).length == 2) beat = "0"+beat; return beat; } function d() { // Day of the month, 2 digits with leading zeros return new String(self.getDate()).length == 1? "0"+self.getDate() : self.getDate(); } function D() { // A textual representation of a day, three letters return daysShort[self.getDay()]; } function F() { // A full textual representation of a month return monthsLong[self.getMonth()]; } function g() { // 12-hour format of an hour without leading zeros return self.getHours() > 12? self.getHours()-12 : self.getHours(); } function G() { // 24-hour format of an hour without leading zeros return self.getHours(); } function h() { // 12-hour format of an hour with leading zeros if (self.getHours() > 12) { var s = new String(self.getHours()-12); return s.length == 1? "0"+ (self.getHours()-12) : self.getHours()-12; } else { var s = new String(self.getHours()); return s.length == 1? "0"+self.getHours() : self.getHours(); } } function H() { // 24-hour format of an hour with leading zeros return new String(self.getHours()).length == 1? "0"+self.getHours() : self.getHours(); } function i() { // Minutes with leading zeros return new String(self.getMinutes()).length == 1? "0"+self.getMinutes() : self.getMinutes(); } function j() { // Day of the month without leading zeros return self.getDate(); } function l() { // A full textual representation of the day of the week return daysLong[self.getDay()]; } function L() { // leap year or not. 1 if leap year, 0 if not. // the logic should match iso's 8601 standard. var y_ = Y(); if ( (y_ % 4 == 0 && y_ % 100 != 0) || (y_ % 4 == 0 && y_ % 100 == 0 && y_ % 400 == 0) ) { return 1; } else { return 0; } } function m() { // Numeric representation of a month, with leading zeros return self.getMonth() < 9? "0"+(self.getMonth()+1) : self.getMonth()+1; } function M() { // A short textual representation of a month, three letters return monthsShort[self.getMonth()]; } function n() { // Numeric representation of a month, without leading zeros return self.getMonth()+1; } function O() { // Difference to Greenwich time (GMT) in hours var os = Math.abs(self.getTimezoneOffset()); var h = ""+Math.floor(os/60); var m = ""+(os%60); h.length == 1? h = "0"+h:1; m.length == 1? m = "0"+m:1; return self.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; } function r() { // RFC 822 formatted date var r; // result // Thu , 21 Dec 2000 r = D() + ", " + j() + " " + M() + " " + Y() + // 16 : 01 : 07 +0200 " " + H() + ":" + i() + ":" + s() + " " + O(); return r; } function S() { // English ordinal suffix for the day of the month, 2 characters return daysSuffix[self.getDate()-1]; } function s() { // Seconds, with leading zeros return new String(self.getSeconds()).length == 1? "0"+self.getSeconds() : self.getSeconds(); } function t() { // thanks to Matt Bannon for some much needed code-fixes here! var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31]; if (L()==1 && n()==2) return 29; // leap day return daysinmonths[n()]; } function U() { // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) return Math.round(self.getTime()/1000); } function W() { // Weeknumber, as per ISO specification: // http://www.cl.cam.ac.uk/~mgk25/iso-time.html // if the day is three days before newyears eve, // there's a chance it's "week 1" of next year. // here we check for that. var beforeNY = 364+L() - z(); var afterNY = z(); var weekday = w()!=0?w()-1:6; // makes sunday (0), into 6. if (beforeNY <= 2 && weekday <= 2-beforeNY) { return 1; } // similarly, if the day is within threedays of newyears // there's a chance it belongs in the old year. var ny = new Date("January 1 " + Y() + " 00:00:00"); var nyDay = ny.getDay()!=0?ny.getDay()-1:6; if ( (afterNY <= 2) && (nyDay >=4) && (afterNY >= (6-nyDay)) ) { // Since I'm not sure we can just always return 53, // i call the function here again, using the last day // of the previous year, as the date, and then just // return that week. var prevNY = new Date("December 31 " + (Y()-1) + " 00:00:00"); return prevNY.formatDate("W"); } // week 1, is the week that has the first thursday in it. // note that this value is not zero index. if (nyDay <= 3) { // first day of the year fell on a thursday, or earlier. return 1 + Math.floor( ( z() + nyDay ) / 7 ); } else { // first day of the year fell on a friday, or later. return 1 + Math.floor( ( z() - ( 7 - nyDay ) ) / 7 ); } } function w() { // Numeric representation of the day of the week return self.getDay(); } function Y() { // A full numeric representation of a year, 4 digits // we first check, if getFullYear is supported. if it // is, we just use that. ppks code is nice, but wont // work with dates outside 1900-2038, or something like that if (self.getFullYear) { var newDate = new Date("January 1 2001 00:00:00 +0000"); var x = newDate .getFullYear(); if (x == 2001) { // i trust the method now return self.getFullYear(); } } // else, do this: // codes thanks to ppk: // http://www.xs4all.nl/~ppk/js/introdate.html var x = self.getYear(); var y = x % 100; y += (y < 38) ? 2000 : 1900; return y; } function y() { // A two-digit representation of a year var y = Y()+""; return y.substring(y.length-2,y.length); } function z() { // The day of the year, zero indexed! 0 through 366 var t = new Date("January 1 " + Y() + " 00:00:00"); var diff = self.getTime() - t.getTime(); return Math.floor(diff/1000/60/60/24); } var self = this; if (time) { // save time var prevTime = self.getTime(); self.setTime(time); } var ia = input.split(""); var ij = 0; while (ia[ij]) { if (ia[ij] == "\\") { // this is our way of allowing users to escape stuff ia.splice(ij,1); } else { if (arrayExists(switches,ia[ij])) { ia[ij] = eval(ia[ij] + "()"); } } ij++; } // reset time, back to what it was if (prevTime) { self.setTime(prevTime); } return ia.join(""); } var date = new Date("1/1/2007 1:11:11"); for (i = 0; i < 500; ++i) { var shortFormat = date.formatDate("Y-m-d"); var longFormat = date.formatDate("l, F d, Y g:i:s A"); date.setTime(date.getTime() + 84266956); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/crypto-aes.js0000644000175000017500000004122614433667662026434 0ustar apoapo/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * AES Cipher function: encrypt 'input' with Rijndael algorithm * * takes byte-array 'input' (16 bytes) * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) * * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage * * returns byte-array encrypted value (16 bytes) */ function Cipher(input, w) { // main Cipher function [§5.1] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; state = AddRoundKey(state, w, 0, Nb); for (var round=1; round 6 && i%Nk == 4) { temp = SubWord(temp); } for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; } return w; } function SubWord(w) { // apply SBox to 4-byte word w for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; return w; } function RotWord(w) { // rotate 4-byte word w left by one byte w[4] = w[0]; for (var i=0; i<4; i++) w[i] = w[i+1]; return w; } // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] var Rcon = [ [0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x00], [0x04, 0x00, 0x00, 0x00], [0x08, 0x00, 0x00, 0x00], [0x10, 0x00, 0x00, 0x00], [0x20, 0x00, 0x00, 0x00], [0x40, 0x00, 0x00, 0x00], [0x80, 0x00, 0x00, 0x00], [0x1b, 0x00, 0x00, 0x00], [0x36, 0x00, 0x00, 0x00] ]; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf * for each block * - outputblock = cipher(counter, key) * - cipherblock = plaintext xor outputblock */ function AESEncryptCtr(plaintext, password, nBits) { if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1 var nBytes = nBits/8; // no bytes in key var pwBytes = new Array(nBytes); for (var i=0; i>> i*8) & 0xff; for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; // generate key schedule - an expansion of the key into distinct Key Rounds for each round var keySchedule = KeyExpansion(key); var blockCount = Math.ceil(plaintext.length/blockSize); var ciphertext = new Array(blockCount); // ciphertext as array of strings for (var b=0; b>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- // calculate length of final block: var blockLength = b>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff; var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block ciphertext[b] = unescCtrlChars(ciphertext[b]); var pt = ''; for (var i=0; i>18 & 0x3f; h2 = bits>>12 & 0x3f; h3 = bits>>6 & 0x3f; h4 = bits & 0x3f; // end of string? index to '=' in b64 if (isNaN(o3)) h4 = 64; if (isNaN(o2)) h3 = 64; // use hexets to index into b64, and append result to encoded string enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < str.length); return enc; } function decodeBase64(str) { var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; do { // unpack four hexets into three octets using index points in b64 h1 = b64.indexOf(str.charAt(i++)); h2 = b64.indexOf(str.charAt(i++)); h3 = b64.indexOf(str.charAt(i++)); h4 = b64.indexOf(str.charAt(i++)); bits = h1<<18 | h2<<12 | h3<<6 | h4; o1 = bits>>16 & 0xff; o2 = bits>>8 & 0xff; o3 = bits & 0xff; if (h3 == 64) enc += String.fromCharCode(o1); else if (h4 == 64) enc += String.fromCharCode(o1, o2); else enc += String.fromCharCode(o1, o2, o3); } while (i < str.length); return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode } function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters str = str.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } ); str = str.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } ); return str; } function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters str = str.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; return String.fromCharCode(cc); } ); str = str.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; return String.fromCharCode(cc); } ); return str; } function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors var s = ''; for (var i=0; i= tDistribution.length) ? 1.96 : tDistribution[n]; } function timeMean(times) { // only consider last iterations times = times.slice(-count); var sum = 0; for (var i = 0; i < count; i++) { sum += times[i]; } return sum / count; } function timeDisplay(times) { var mean = timeMean(times); var deltaSquared = 0; for (var i = 0; i < count; i++) { deltaSquared += Math.pow(times[i] - mean, 2) } var variance = deltaSquared / (count - 1); var stdDev = Math.sqrt(variance); var sqrtCount = Math.sqrt(count); var stdErr = stdDev / sqrtCount; var percent = ((tDist(count) * stdErr / mean) * 100).toFixed(1); return lpad(mean.toFixed(1), 8) + "ms +- " + lpad(percent, 4) + "%"; } // Print a measurement for the Junit "Measurement Plots" Plugin function printMeasurement(name, value) { print("" + RUN_NAME + '-' + name + "" + timeMean(value) + ""); } // calculate mean, variance and display 95% CI print("============================================"); print("RESULTS (means and 95% confidence intervals)"); print("--------------------------------------------"); print(rpad("Total:", 22) + timeDisplay(results.times)); print("--------------------------------------------"); for (var p in results.categories) { if (!results.categories.hasOwnProperty(p)) continue; var category = results.categories[p]; print(rpad(" " + category.name + ":", 22) + timeDisplay(category.times)); for (var q in category.tests) { if (!category.tests.hasOwnProperty(q)) continue; var test = category.tests[q]; print(rpad(" " + test.name + ":", 22) + timeDisplay(test.times)); } } timeMean(results.times); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/bitops-3bit-bits-in-byte.js0000644000175000017500000000136514433667662031011 0ustar apoapo// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com // 1 op = 6 ANDs, 3 SHRs, 3 SHLs, 4 assigns, 2 ADDs // O(1) function fast3bitlookup(b) { var c, bi3b = 0xE994; // 0b1110 1001 1001 0100; // 3 2 2 1 2 1 1 0 c = 3 & (bi3b >> ((b << 1) & 14)); c += 3 & (bi3b >> ((b >> 2) & 14)); c += 3 & (bi3b >> ((b >> 5) & 6)); return c; /* lir4,0xE994; 9 instructions, no memory access, minimal register dependence, 6 shifts, 2 adds, 1 inline assign rlwinmr5,r3,1,28,30 rlwinmr6,r3,30,28,30 rlwinmr7,r3,27,29,30 rlwnmr8,r4,r5,30,31 rlwnmr9,r4,r6,30,31 rlwnmr10,r4,r7,30,31 addr3,r8,r9 addr3,r3,r10 */ } function TimeFunc(func) { var x, y, t; for(var x=0; x<500; x++) for(var y=0; y<256; y++) func(y); } TimeFunc(fast3bitlookup); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/math-spectral-norm.js0000644000175000017500000000154414433667662030062 0ustar apoapo// The Great Computer Language Shootout // http://shootout.alioth.debian.org/ // // contributed by Ian Osgood function A(i,j) { return 1/((i+j)*(i+j+1)/2+i+1); } function Au(u,v) { for (var i=0; i>5; for (i=0; i>5] & 1<<(i&31)) { for (var j=i+i; j>5] &= ~(1<<(j&31)); count++; } } function sieve() { for (var i = 4; i <= 4; i++) { var isPrime = new Array((10000<>5); primes(isPrime, i); } } sieve(); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/string-validate-input.js0000644000175000017500000000365714433667662030606 0ustar apoapoletters = new Array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"); numbers = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26); colors = new Array("FF","CC","99","66","33","00"); var endResult; function doTest() { endResult = ""; // make up email address for (var k=0;k<4000;k++) { name = makeName(6); (k%2)?email=name+"@mac.com":email=name+"(at)mac.com"; // validate the email address var pattern = /^[a-zA-Z0-9\-\._]+@[a-zA-Z0-9\-_]+(\.?[a-zA-Z0-9\-_]*)\.[a-zA-Z]{2,3}$/; if(pattern.test(email)) { var r = email + " appears to be a valid email address."; addResult(r); } else { r = email + " does NOT appear to be a valid email address."; addResult(r); } } // make up ZIP codes for (var s=0;s<4000;s++) { var zipGood = true; var zip = makeNumber(4); (s%2)?zip=zip+"xyz":zip=zip.concat("7"); // validate the zip code for (var i = 0; i < zip.length; i++) { var ch = zip.charAt(i); if (ch < "0" || ch > "9") { zipGood = false; r = zip + " contains letters."; addResult(r); } } if (zipGood && zip.length>5) { zipGood = false; r = zip + " is longer than five characters."; addResult(r); } if (zipGood) { r = zip + " appears to be a valid ZIP code."; addResult(r); } } } function makeName(n) { var tmp = ""; for (var i=0;i for documentation, downloads, license, etc. (c) 2005 Bob Ippolito. All rights Reserved. ***/ for (var i = 0; i < 2; i++) { var decompressedMochiKit = function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(H(1q)!="L"){1q.2X("B.J")}if(H(B)=="L"){B={}}if(H(B.J)=="L"){B.J={}}B.J.1Y="1.3.1";B.J.1r="B.J";B.J.2l=G(7V,vR){if(7V===O){7V={}}R(u i=1;i=0;i--){aw.e9(o[i])}}N{X.1c(o)}}F X},1R:G(7U,1i,av){if(!av){av=0}if(1i){u l=1i.K;if(H(l)!="2y"){if(H(B.15)!="L"){1i=B.15.2G(1i);l=1i.K}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(!7U){7U=[]}R(u i=av;i>b},vG:G(a,b){F a>>>b},eq:G(a,b){F a==b},ne:G(a,b){F a!=b},gt:G(a,b){F a>b},ge:G(a,b){F a>=b},lt:G(a,b){F al){7T=l}}69=[];R(i=0;i<7T;i++){u fa=[];R(u j=1;j0){ap=m.2o(me.am,ap)}u 4o=me.f7;if(!4o){4o=D}F me.f5.1w(4o,ap)};7Q.f7=f6;7Q.f5=ao;7Q.am=5f;F 7Q},lF:G(7P){u mp=B.J.1O;R(u k in 7P){u f4=7P[k];if(H(f4)=="G"){7P[k]=mp(f4,7P)}}},5u:G(mo,mn,ml,mk){B.J.ae.5M(mo,mn,ml,mk)},mj:{"5L":1h,"1n":1h,"2y":1h},2f:G(a,b){if(a==b){F 0}u f3=(H(a)=="L"||a===O);u f2=(H(b)=="L"||b===O);if(f3&&f2){F 0}N{if(f3){F-1}N{if(f2){F 1}}}u m=B.J;u f1=m.mj;if(!(H(a)in f1&&H(b)in f1)){1f{F m.ae.3C(a,b)}1e(e){if(e!=m.4d){14 e}}}if(ab){F 1}}u f0=m.U;14 Y 3p(f0(a)+" 3W "+f0(b)+" 9v 2E be vv")},eM:G(a,b){F B.J.2f(a.9P(),b.9P())},eL:G(a,b){u mi=B.J.2f;u 7O=a.K;u al=0;if(7O>b.K){al=1;7O=b.K}N{if(7O0))){u kv=B.S.d5(3s);3s=kv[0];68=kv[1]}N{if(M.K==1){u o=3s;3s=[];68=[];R(u k in o){u v=o[k];if(H(v)!="G"){3s.1c(k);68.1c(v)}}}}u W=[];u lT=28.2a(3s.K,68.K);u eT=B.J.af;R(u i=0;i=2J){14 I.25}5c+=3a;F W}}},4c:G(aa,p,q){u m=B.J;u I=B.15;u lb=m.2r(I.1Q,m.1R(O,M,1));u 2r=m.2r;u 1a=I.1a;F{U:G(){F"4c(...)"},1l:m.24("U"),1a:G(){F aa.1w(D,2r(1a,lb))}}},ep:G(aa,1V,I){1V=B.15.1Q(1V);u m=B.J;F{U:G(){F"ep(...)"},1l:m.24("U"),1a:G(){F aa.1w(I,1V.1a())}}},55:G(p,q){u I=B.15;u m=B.J;if(M.K==1){F I.1Q(M[0])}u 64=m.2r(I.1Q,M);F{U:G(){F"55(...)"},1l:m.24("U"),1a:G(){1M(64.K>1){1f{F 64[0].1a()}1e(e){if(e!=I.25){14 e}64.2P()}}if(64.K==1){u a9=64.2P();D.1a=m.1O("1a",a9);F D.1a()}14 I.25}}},9Z:G(3b,1V){u I=B.15;1V=I.1Q(1V);F{U:G(){F"9Z(...)"},1l:B.J.24("U"),1a:G(){u W=1V.1a();if(!3b(W)){D.1a=G(){14 I.25};D.1a()}F W}}},eo:G(3b,1V){1V=B.15.1Q(1V);u m=B.J;u 1O=m.1O;F{"U":G(){F"eo(...)"},"1l":m.24("U"),"1a":G(){1M(1h){u W=1V.1a();if(!3b(W)){2K}}D.1a=1O("1a",1V);F W}}},a7:G(63,2u,la){2u.62[63]=-1;u m=B.J;u l9=m.eI;F{U:G(){F"en("+63+", ...)"},1l:m.24("U"),1a:G(){u W;u i=2u.62[63];if(i==2u.29){W=la.1a();2u.a8.1c(W);2u.29+=1;2u.62[63]+=1}N{W=2u.a8[i-2u.2a];2u.62[63]+=1;if(i==2u.2a&&l9(2u.62)!=2u.2a){2u.2a+=1;2u.a8.2P()}}F W}}},en:G(a6,n){u W=[];u 2u={"62":[],"a8":[],"29":-1,"2a":-1};if(M.K==1){n=2}u I=B.15;a6=I.1Q(a6);u a7=I.a7;R(u i=0;i0&&4k>=2J)||(3a<0&&4k<=2J)){14 B.15.25}u W=4k;4k+=3a;F W},U:G(){F"7I("+[4k,2J,3a].2b(", ")+")"},1l:B.J.24("U")}},l0:G(a5,l7){u x=l7||0;u I=B.15;a5=I.1Q(a5);1f{1M(1h){x+=a5.1a()}}1e(e){if(e!=I.25){14 e}}F x},em:G(a4){u I=B.15;a4=I.1Q(a4);1f{1M(1h){a4.1a()}}1e(e){if(e!=I.25){14 e}}},9a:G(7J,1A,I){u m=B.J;if(M.K>2){1A=m.1O(1A,I)}if(m.3A(7J)){1f{R(u i=0;i<7J.K;i++){1A(7J[i])}}1e(e){if(e!=B.15.25){14 e}}}N{I=B.15;I.em(I.4c(1A,7J))}},kZ:G(l6,1A){u I=B.15;1f{I.a0(1A,l6).1a();F 1m}1e(e){if(e!=I.25){14 e}F 1h}},kY:G(l5,4j){u W=B.15.2G(l5);if(M.K==1){4j=B.J.2f}W.iz(4j);F W},kX:G(l4){u W=B.15.2G(l4);W.vg();F W},kW:G(l3,1A){u I=B.15;1f{I.a1(1A,l3).1a();F 1h}1e(e){if(e!=I.25){14 e}F 1m}},kV:G(1g,5b){if(B.J.3A(5b)){R(u i=0;i<5b.K;i++){1g.1c(5b[i])}}N{u I=B.15;5b=I.1Q(5b);1f{1M(1h){1g.1c(5b.1a())}}1e(e){if(e!=I.25){14 e}}}F 1g},ek:G(a3,eH){u m=B.J;u I=B.15;if(M.K<2){eH=m.4i.eE}a3=I.1Q(a3);u pk=L;u k=L;u v;G eF(){v=a3.1a();k=eH(v)}G l2(){u 7j=v;v=L;F 7j}u eG=1h;F{U:G(){F"ek(...)"},1a:G(){1M(k==pk){eF();if(eG){eG=1m;2K}}pk=k;F[k,{1a:G(){if(v==L){eF()}if(k!=pk){14 I.25}F l2()}}]}}},kU:G(a2,eD){u m=B.J;u I=B.15;if(M.K<2){eD=m.4i.eE}a2=I.1Q(a2);u ey=[];u eA=1h;u ez;1M(1h){1f{u eB=a2.1a();u 2h=eD(eB)}1e(e){if(e==I.25){2K}14 e}if(eA||2h!=ez){u eC=[];ey.1c([2h,eC])}eC.1c(eB);eA=1m;ez=2h}F ey},9X:G(ex){u i=0;F{U:G(){F"9X(...)"},1l:B.J.24("U"),1a:G(){if(i>=ex.K){14 B.15.25}F ex[i++]}}},eh:G(ew){F(ew&&H(ew.ei)=="G")},9V:G(l1){F{U:G(){F"9V(...)"},1l:B.J.24("U"),1a:G(){u W=l1.ei();if(W===O||W===L){14 B.15.25}F W}}}});B.15.1W=["9Y","9X","eh","9V",];B.15.1z=["25","9W","1Q","eu","et","7b","1a","es","a1","a0","er","4c","ep","55","9Z","eo","en","2G","7H","7I","l0","em","9a","kZ","kY","kX","kW","kV","ek","kU"];B.15.2d=G(){u m=B.J;D.25=Y m.5a("25");D.9Y=Y m.4a();D.9W("ej",m.3A,D.9X);D.9W("ei",D.eh,D.9V);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.15.2d();if(!B.3d){7H=B.15.7H}B.J.2Y(D,B.15);if(H(1q)!="L"){1q.2X("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1H 3F on B.J!"}if(H(B.1H)=="L"){B.1H={}}B.1H.1r="B.1H";B.1H.1Y="1.3.1";B.1H.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1H.1l=G(){F D.1K()};B.1H.1z=["5C","49","7A","kR","2L","5Z","kG","ch","kE","kC"];B.1H.1W=["ef","e8","e7"];B.1H.49=G(1P,kT,3z){D.1P=1P;D.3N=kT;D.3z=3z;D.vf=Y 3Q()};B.1H.49.1U={U:G(){u m=B.J;F"49("+m.2r(m.U,[D.1P,D.3N,D.3z]).2b(", ")+")"},1l:B.J.24("U")};B.J.2l(B.1H,{ef:G(7F){u I=B.1H;if(H(7F)=="1n"){7F=I.5C[7F]}F G(1t){u 7G=1t.3N;if(H(7G)=="1n"){7G=I.5C[7G]}F 7G>=7F}},e8:G(){u kS=B.1H.49;R(u i=0;i=0&&D.4h.K>D.ec){D.4h.2P()}},c8:G(9U){u ea=0;if(!(H(9U)=="L"||9U===O)){ea=28.29(0,D.4h.K-9U)}F D.4h.9T(ea)},kJ:G(7B){if(H(7B)=="L"||7B===O){7B=30}u 9S=D.c8(7B);if(9S.K){u 1g=2r(G(m){F"\\n ["+m.1P+"] "+m.3N+": "+m.3z.2b(" ")},9S);1g.e9("va "+9S.K+" v9:");F 1g.2b("")}F""},v8:G(kI){if(H(B.1I)=="L"){cq(D.kJ())}N{B.1I.bY(kI||1m)}}};B.1H.2d=G(){D.5C={8M:40,8L:50,8K:30,8J:20,8I:10};u m=B.J;m.5u("49",D.e8,D.e7);u 61=m.2z;u e6=D.7A;u 60=e6.1U.kH;m.2l(D.7A.1U,{kF:61(60,"8I"),5Z:61(60,"8J"),dE:61(60,"8M"),kD:61(60,"8L"),kB:61(60,"8K")});u I=D;u 5Y=G(1b){F G(){I.2L[1b].1w(I.2L,M)}};D.5Z=5Y("5Z");D.kG=5Y("dE");D.ch=5Y("kF");D.kE=5Y("kD");D.kC=5Y("kB");D.2L=Y e6();D.2L.e5=1h;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};if(H(5X)=="L"&&H(2v)!="L"&&2v.kA&&H(kz)!="L"){5X=G(){5X.3G=M;u ev=2v.kA("v7");ev.v6("5X",1m,1h);kz(ev)}}B.1H.2d();B.J.2Y(D,B.1H);if(H(1q)!="L"){1q.2X("B.1D")}if(H(B)=="L"){B={}}if(H(B.1D)=="L"){B.1D={}}B.1D.1r="B.1D";B.1D.1Y="1.3.1";B.1D.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1D.1l=G(){F D.1K()};B.1D.ks=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u 7z=1y.2R("-");if(7z.K===0){F O}F Y 3Q(7z[0],7z[1]-1,7z[2])};B.1D.ky=/(\\d{4,})(?:-(\\d{1,2})(?:-(\\d{1,2})(?:[T ](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2})(?:\\.(\\d+))?)?(?:(Z)|([+-])(\\d{1,2})(?::(\\d{1,2}))?)?)?)?)?/;B.1D.kr=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u X=1y.3C(B.1D.ky);if(H(X)=="L"||X===O){F O}u 5W,7y,7x,9R,2a,9Q,7w;5W=3w(X[1],10);if(H(X[2])=="L"||X[2]===""){F Y 3Q(5W)}7y=3w(X[2],10)-1;7x=3w(X[3],10);if(H(X[4])=="L"||X[4]===""){F Y 3Q(5W,7y,7x)}9R=3w(X[4],10);2a=3w(X[5],10);9Q=(H(X[6])!="L"&&X[6]!=="")?3w(X[6],10):0;if(H(X[7])!="L"&&X[7]!==""){7w=28.ha(c5*4M("0."+X[7]))}N{7w=0}if((H(X[8])=="L"||X[8]==="")&&(H(X[9])=="L"||X[9]==="")){F Y 3Q(5W,7y,7x,9R,2a,9Q,7w)}u 58;if(H(X[9])!="L"&&X[9]!==""){58=3w(X[10],10)*v5;if(H(X[11])!="L"&&X[11]!==""){58+=3w(X[11],10)*kw}if(X[9]=="-"){58=-58}}N{58=0}F Y 3Q(3Q.v4(5W,7y,7x,9R,2a,9Q,7w)-58)};B.1D.dY=G(2g,kx){if(H(2g)=="L"||2g===O){F O}u hh=2g.v3();u mm=2g.v2();u ss=2g.v1();u 1g=[((kx&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)];F 1g.2b(":")};B.1D.kq=G(2g,7v){if(H(2g)=="L"||2g===O){F O}u ku=7v?"T":" ";u kt=7v?"Z":"";if(7v){2g=Y 3Q(2g.9P()+(2g.v0()*kw))}F B.1D.dX(2g)+ku+B.1D.dY(2g,7v)+kt};B.1D.dX=G(2g){if(H(2g)=="L"||2g===O){F O}u e4=B.1D.e3;F[2g.dZ(),e4(2g.e1()+1),e4(2g.e0())].2b("-")};B.1D.kp=G(d){d=d+"";if(H(d)!="1n"||d.K===0){F O}u a=d.2R("/");F Y 3Q(a[2],a[0]-1,a[1])};B.1D.e3=G(n){F(n>9)?n:"0"+n};B.1D.ko=G(d){if(H(d)=="L"||d===O){F O}u e2=B.1D.e3;F[e2(d.e1()+1),e2(d.e0()),d.dZ()].2b("/")};B.1D.kn=G(d){if(H(d)=="L"||d===O){F O}F[d.e1()+1,d.e0(),d.dZ()].2b("/")};B.1D.1z=["ks","kr","dY","kq","dX","kp","ko","kn"];B.1D.1W=[];B.1D.2k={":3e":B.1D.1z,":1p":B.1D.1z};B.1D.2d=G(){u 2w=D.1r+".";R(u k in D){u o=D[k];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+k}1e(e){}}}};B.1D.2d();if(H(B.J)!="L"){B.J.2Y(D,B.1D)}N{(G(km,dW){if((H(1x)=="L"&&H(1q)=="L")||(H(B.3d)=="5L"&&B.3d)){u 1p=dW.2k[":1p"];R(u i=0;i<1p.K;i++){km[1p[i]]=dW[1p[i]]}}})(D,B.1D)}if(H(1q)!="L"){1q.2X("B.1s")}if(H(B)=="L"){B={}}if(H(B.1s)=="L"){B.1s={}}B.1s.1r="B.1s";B.1s.1Y="1.3.1";B.1s.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1s.1l=G(){F D.1K()};B.1s.ke=G(kl,kk,kj,ki,kh,dV,kg,9N,kf){F G(1P){1P=4M(1P);if(H(1P)=="L"||1P===O||k8(1P)){F kl}u 9L=kk;u 9K=kj;if(1P<0){1P=-1P}N{9L=9L.23(/-/,"")}u me=M.2U;u 9M=B.1s.dJ(ki);if(kh){1P=1P*3k;9K=9M.9y+9K}1P=B.1s.dK(1P,dV);u 9O=1P.2R(/\\./);u 3r=9O[0];u 3P=(9O.K==1)?"":9O[1];u X="";1M(3r.K9N){u i=3r.K-9N;X=9M.9A+3r.2W(i,3r.K)+X;3r=3r.2W(0,i)}}X=3r+X;if(dV>0){1M(3P.K=0)){D.9u()}},jR:G(X){D.9x(X);D.jX()},9x:G(X){D.2H=((X 2C 2x)?1:0);D.53[D.2H]=X;D.9u()},dD:G(){if(D.2H!=-1){if(!D.7l){14 Y B.1k.dj(D)}D.7l=1m;F}},3o:G(X){D.dD();if(X 2C B.1k.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}D.9x(X)},52:G(X){D.dD();u I=B.1k;if(X 2C I.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}if(!(X 2C 2x)){X=Y I.9p(X)}D.9x(X)},jP:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,fn)},5Q:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,O)},jA:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(O,fn)},9w:G(cb,eb){if(D.7r){14 Y 2x("uQ uP 9v 2E be re-uO")}D.55.1c([cb,eb]);if(D.2H>=0){D.9u()}F D},9u:G(){u dC=D.55;u 56=D.2H;u X=D.53[56];u I=D;u cb=O;1M(dC.K>0&&D.54===0){u 2n=dC.2P();u f=2n[56];if(f===O){2V}1f{X=f(X);56=((X 2C 2x)?1:0);if(X 2C B.1k.2t){cb=G(X){I.jR(X)};D.jQ()}}1e(3O){56=1;if(!(3O 2C 2x)){3O=Y B.1k.9p(3O)}X=3O}}D.2H=56;D.53[56]=X;if(cb&&D.54){X.jP(cb);X.7r=1h}}};B.J.2l(B.1k,{dk:G(){F dB("("+M[0].jN+")")},dp:G(uN){u d=Y B.1k.2t();d.3o.1w(d,M);F d},9q:G(uM){u d=Y B.1k.2t();d.52.1w(d,M);F d},do:G(){u I=M.2U;if(!I.7q){u dy=[G(){F Y 7q()},G(){F Y dA("jO.dz")},G(){F Y dA("uL.dz")},G(){F Y dA("jO.dz.4.0")},G(){14 Y B.1k.dh("uK uJ 2E uI 7q")}];R(u i=0;i1){u m=B.J;u qs=m.dw.1w(O,m.1R(O,M,1));if(qs){5F+="?"+qs}}2s.cp("uB",5F,1h);F I.dl(2s)},jv:G(5F){u I=B.1k;u d=I.dn.1w(I,M);d=d.5Q(I.dk);F d},dm:G(jJ,dv){u d=Y B.1k.2t();u m=B.J;if(H(dv)!="L"){d.5Q(G(){F dv})}u jI=uA(m.1O("3o",d),28.8B(jJ*c5));d.7m=G(){1f{uz(jI)}1e(e){}};F d},ju:G(jH,1A){u m=B.J;u jG=m.2z.1w(m,m.1R(O,M,1));F B.1k.dm(jH).5Q(G(X){F jG()})}});B.1k.5O=G(){D.5S=[];D.4e=1m;D.id=D.7n()};B.1k.5O.1U={bX:B.1k.5O,uy:G(){d=Y B.1k.2t();if(D.4e){D.5S.1c(d)}N{D.4e=1h;d.3o(D)}F d},jF:G(){if(!D.4e){14 3p("ux to jF an jE 5O")}D.4e=1m;if(D.5S.K>0){D.4e=1h;D.5S.2P().3o(D)}},7n:B.J.4f(),U:G(){u 9t;if(D.4e){9t="4e, "+D.5S.K+" 5S"}N{9t="jE"}F"5O("+D.id+", "+9t+")"},1l:B.J.24("U")};B.1k.7i=G(2G,du,jC,jB,jD){D.2G=2G;D.9r=Y 7o(D.2G.K);D.55=[];D.id=D.7n();D.2H=-1;D.54=0;D.53=[O,O];D.7m=jD;D.7l=1m;if(D.2G.K===0&&!du){D.3o(D.9r)}D.dr=0;D.jz=du;D.jy=jC;D.jx=jB;u 9s=0;B.J.2r(B.J.1O(G(d){d.5Q(B.J.1O(D.dt,D),9s,1h);d.jA(B.J.1O(D.dt,D),9s,1m);9s+=1},D),D.2G)};B.J.2l(B.1k.7i.1U,B.1k.2t.1U);B.J.2l(B.1k.7i.1U,{dt:G(ds,7k,5R){D.9r[ds]=[7k,5R];D.dr+=1;if(D.2H!==0){if(7k&&D.jz){D.3o([ds,5R])}N{if(!7k&&D.jy){D.52(5R)}N{if(D.dr==D.2G.K){D.3o(D.9r)}}}}if(!7k&&D.jx){5R=O}F 5R}});B.1k.jt=G(jw){u d=Y B.1k.7i(jw,1m,1h,1m);d.5Q(G(dq){u 7j=[];R(u i=0;i=0){u 9m=Q.1S[Q.j4];7d.1c(1b);7c.1c((9m.3m)?9m.3m:9m.7X);F O}7d.1c(1b);7c.1c("");F O}if(4Y=="cu"||4Y=="P"||4Y=="8d"||4Y=="6m"){F Q.5h}7d.1c(1b);7c.1c(Q.3m||"");F O}F Q.5h});F[7d,7c]},94:G(1N,1A){u I=B.S;u d3=I.1Z;u W;1f{I.1Z=1N;W=1A()}1e(e){I.1Z=d3;14 e}I.1Z=d3;F W},j3:G(1b,j2,3y,j1){B.S.9b.5M(1b,j2,3y,j1)},9k:G(1j,7a){u im=B.15;u I=B.S;u 1Q=im.1Q;u iY=im.7b;u 4c=im.4c;u iX=I.9b;u iZ=I.9k;u iW=B.J.4d;1M(1h){if(H(1j)=="L"||1j===O){F O}if(H(1j.3T)!="L"&&1j.3T>0){F 1j}if(H(1j)=="2y"||H(1j)=="5L"){1j=1j.1l()}if(H(1j)=="1n"){F I.1Z.4S(1j)}if(H(1j.j0)=="G"){1j=1j.j0(7a);2V}if(H(1j)=="G"){1j=1j(7a);2V}u 9l=O;1f{9l=1Q(1j)}1e(e){}if(9l){F 4c(iZ,9l,iY(7a))}1f{1j=iX.3C(1j,7a);2V}1e(e){if(e!=iW){14 e}}F I.1Z.4S(1j.1l())}F L},iV:G(1j,79,iU){u o={};o[79]=iU;1f{F B.S.4X(1j,o)}1e(e){}F O},iT:G(1j,79){u I=B.S;u d2=I.4U.99[79];1j=I.1E(1j);1f{if(d2){F 1j[d2]}F 1j.fm(79)}1e(e){}F O},4X:G(1j,5K){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}if(5K){u d0=B.J.8Z;if(I.4U.6X){R(u k in 5K){u v=5K[k];if(H(v)=="3n"&&H(Q[k])=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}N{u iS=I.4U.99;R(k in 5K){v=5K[k];u d1=iS[k];if(k=="1T"&&H(v)=="1n"){Q.1T.3x=v}N{if(H(d1)=="1n"){Q[d1]=v}N{if(H(Q[k])=="3n"&&H(v)=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}}}}F Q},9j:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}u 78=[I.9k(B.J.1R(O,M,1),Q)];u iR=B.J.2o;1M(78.K){u n=78.2P();if(H(n)=="L"||n===O){}N{if(H(n.3T)=="2y"){Q.2c(n)}N{78=iR(n,78)}}}F Q},iQ:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j);M[0]=Q}u cY;1M((cY=Q.6n)){Q.6S(cY)}if(M.K<2){F Q}N{F I.9j.1w(D,M)}},cX:G(1b,4b){u Q;u I=B.S;u m=B.J;if(H(4b)=="1n"||H(4b)=="2y"){u 3G=m.1R([1b,O],M,1);F M.2U.1w(D,3G)}if(H(1b)=="1n"){if(4b&&"1b"in 4b&&!I.4U.6X){1b=("<"+1b+" 1b=\\""+I.9c(4b.1b)+"\\">")}Q=I.1Z.2S(1b)}N{Q=1b}if(4b){I.4X(Q,4b)}if(M.K<=2){F Q}N{u 3G=m.1R([Q],M,2);F I.9j.1w(D,3G)}},cw:G(){u m=B.J;F m.2z.1w(D,m.1R([B.S.cX],M))},cs:G(5J,1d){u I=B.S;5J=I.1E(5J);u cW=5J.3t;if(1d){1d=I.1E(1d);cW.uj(1d,5J)}N{cW.6S(5J)}F 1d},1E:G(id){u I=B.S;if(M.K==1){F((H(id)=="1n")?I.1Z.hN(id):id)}N{F B.J.2r(I.1E,M)}},4q:G(iP,cV,cU){if(M.K==2){cU=cV}u I=B.S;u el=I.1E(iP);u 77=I.1Z;if(!el||el==77){F L}if(el.iO){F el.iO[cV]}if(H(77.5k)=="L"){F L}if(77.5k===O){F L}u 9i=77.5k.g4(el,O);if(H(9i)=="L"||9i===O){F L}F 9i.6q(cU)},aH:G(76,9g,4W){u I=B.S;if(H(76)=="L"||76===O){76="*"}if(H(4W)=="L"||4W===O){4W=I.1Z}4W=I.1E(4W);u 9h=(4W.fr(76)||I.1Z.1p);if(H(9g)=="L"||9g===O){F B.J.1R(O,9h)}u cR=[];R(u i=0;i<9h.K;i++){u cS=9h[i];u cT=cS.3M.2R(" ");R(u j=0;j/g,">")},iB:G(2q){F B.S.cG(2q).2b("")},cG:G(2q,1g){if(H(1g)=="L"||1g===O){1g=[]}u 70=[2q];u I=B.S;u cB=I.9c;u iA=I.4U;1M(70.K){2q=70.hP();if(H(2q)=="1n"){1g.1c(2q)}N{if(2q.3T==1){1g.1c("<"+2q.cD.8G());u 71=[];u cF=iA(2q);R(u i=0;i");70.1c("");u cC=2q.5h;R(i=cC.K-1;i>=0;i--){70.1c(cC[i])}}N{1g.1c("/>")}}N{if(2q.3T==3){1g.1c(cB(2q.iv))}}}}F 1g},97:G(ix,cA){u m=B.J;u iy=m.1R(O,M,1);B.15.9a(m.47(O,m.2r(B.S.1E,iy)),G(cA){cA.1T.3u=ix})},iw:G(1j,iu){u W=[];(G(1j){u cn=1j.5h;if(cn){R(u i=0;i0){u it=m.47;2T=G(1j){F it(2T.ir,1j.6Y)};2T.cx={};B.15.9a(6Z.6Y,G(a){2T.cx[a.1b]=a.3m});2T.ir=G(a){F(2T.cx[a.1b]!=a.3m)};2T.6X=1m;2T.99={"iq":"3M","ip":"ud","uc":"ub","R":"u9"}}N{2T=G(1j){F 1j.6Y};2T.6X=1h;2T.99={}}D.4U=2T;u 1C=D.cw;D.io=1C("ul");D.il=1C("ol");D.ik=1C("li");D.ij=1C("td");D.cm=1C("tr");D.ii=1C("u8");D.ih=1C("u7");D.ig=1C("u6");D.ie=1C("u5");D.ic=1C("th");D.cv=1C("ck");D.8d=1C("cj");D.A=1C("a");D.6m=1C("4u");D.ib=1C("u4");D.ia=1C("2e");D.i9=1C("tt");D.i8=1C("4O");D.i7=1C("h1");D.i6=1C("h2");D.i5=1C("h3");D.i4=1C("br");D.i3=1C("hr");D.i2=1C("u3");D.i1=1C("u2");D.cu=1C("u1");D.P=1C("p");D.ct=1C("u0");D.i0=1C("hJ");D.hZ=1C("tZ");D.hY=1C("tY");D.hX=1C("tX");D.hW=1C("tW");D.hV=1C("tV");D.hU=m.2z(D.97,"98");D.hT=m.2z(D.97,"8c");D.hS=D.cs;D.$=D.1E;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)}});B.S.2d(((H(2O)=="L")?D:2O));if(!B.3d){95=B.S.95;94=B.S.94}B.J.2Y(D,B.S);if(H(1q)!="L"){1q.2X("B.1I");1q.2M("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.1H",[]);1x.26("B.J",[])}1f{if(H(B.J)=="L"||H(B.1H)=="L"){14""}}1e(e){14"B.1I 3F on B.J 3W B.1H!"}if(H(B.1I)=="L"){B.1I={}}B.1I.1r="B.1I";B.1I.1Y="1.3.1";B.1I.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1I.1l=G(){F D.1K()};B.1I.bY=G(6W){u m=B.1I;6W=!(!6W);if(m.3l&&m.3l.8Q!=6W){m.3l.hA();m.3l=O}if(!m.3l||m.3l.8P){m.3l=Y m.1I(6W,B.1H.2L)}F m.3l};B.1I.1I=G(4R,6V){if(H(6V)=="L"||6V===O){6V=B.1H.2L}D.2L=6V;u tU=B.J.2l;u c3=B.J.8Z;u 1O=B.J.1O;u hM=B.J.4L;u 2m=2O;u 6U="tT";if(H(B.S)!="L"){2m=B.S.cr()}if(!4R){u 5F=2m.tS.tR.2R("?")[0].23(/[:\\/.><&]/g,"hR");u 1b=6U+"hR"+5F;u 5D=2m.cp("",1b,"tQ,tP,3V=hQ");if(!5D){cq("tO tN to cp tM 2O tL to hP-up tK.");F L}5D.2v.fl(""+"<5E><8Y>[B.1I]"+"<5s>");5D.2v.hG();5D.2v.8Y+=" "+2m.2v.8Y;2m=5D}u 1N=2m.2v;D.1N=1N;u 21=1N.hN(6U);u c4=!!21;if(21&&H(21.5B)!="L"){21.5B.2L=D.2L;21.5B.6K();F 21.5B}if(c4){u cl;1M((cl=21.6n)){21.6S(cl)}}N{21=1N.2S("4u");21.id=6U}21.5B=D;u 8T=1N.2S("ck");u 8S=1N.2S("ck");u 6O=1N.2S("2e");u 6N=1N.2S("2e");u 6M=1N.2S("2e");u 6L=1N.2S("2e");u 3L=1N.2S("4u");u 42=1N.2S("4u");u 8U=6U+"tz";D.8N=hM(D.8N);u 4T=[];u 6R=O;u cf=G(1t){u 6T=1t.3N;if(H(6T)=="2y"){6T=B.1H.5C[6T]}F 6T};u cd=G(1t){F 1t.3z.2b(" ")};u ca=1O(G(1t){u 8W=cf(1t);u 7X=cd(1t);u c=D.8N[8W];u p=1N.2S("cj");p.3M="B-49 B-5C-"+8W;p.1T.3x="ty: 2N; 4F-8X: -hL-4O-3y; 4F-8X: -o-4O-3y; 4F-8X: 4O-3y; 4F-8X: 4O-tx; hK-3y: 2K-hK; 3y-hJ: tw; 3U: "+c;p.2c(1N.4S(8W+": "+7X));42.2c(p);42.2c(1N.2S("br"));if(3L.ci>3L.hI){3L.4C=0}N{3L.4C=3L.hI}},D);u hD=G(1t){4T[4T.K]=1t;ca(1t)};u hF=G(){u cg,ce;1f{cg=Y 8V(8T.3m);ce=Y 8V(8S.3m)}1e(e){ch("2x in 47 tv: "+e.43);F O}F G(1t){F(cg.hH(cf(1t))&&ce.hH(cd(1t)))}};u cc=G(){1M(42.6n){42.6S(42.6n)}};u hB=G(){4T=[];cc()};u bZ=1O(G(){if(D.8P){F}D.8P=1h;if(B.1I.3l==D){B.1I.3l=O}D.2L.c9(8U);21.5B=O;if(4R){21.3t.6S(21)}N{D.2m.hG()}},D);u c7=G(){cc();R(u i=0;i<4T.K;i++){u 1t=4T[i];if(6R===O||6R(1t)){ca(1t)}}};D.6K=G(){6R=hF();c7();D.2L.c9(8U);D.2L.hE(8U,6R,hD)};u c0=1O(G(){4T=D.2L.c8();c7()},D);u c2=1O(G(6Q){6Q=6Q||2O.6D;2h=6Q.6w||6Q.8t;if(2h==13){D.6K()}},D);u 31="3u: 8c; z-c6: c5; 2I: 2N; 6f: 2N; 6P: tu; 5A: 3k%; he-3U: 4F; c1: "+D.8O;if(4R){31+="; 3V: ts; 3E-3D: fO 8a 8y"}N{31+="; 3V: 3k%;"}21.1T.3x=31;if(!c4){1N.5s.2c(21)}31={"3x":"5A: 33%; 3u: 8Q; c1: "+D.8O};c3(8T,{"3m":"8L|8M|8K|8J|8I","hC":c2,"1T":31});21.2c(8T);c3(8S,{"3m":".*","hC":c2,"1T":31});21.2c(8S);31="5A: 8%; 3u:8Q; c1: "+D.8O;6O.2c(1N.4S("tq"));6O.8R=1O("6K",D);6O.1T.3x=31;21.2c(6O);6N.2c(1N.4S("tp"));6N.8R=c0;6N.1T.3x=31;21.2c(6N);6M.2c(1N.4S("tn"));6M.8R=hB;6M.1T.3x=31;21.2c(6M);6L.2c(1N.4S("tm"));6L.8R=bZ;6L.1T.3x=31;21.2c(6L);3L.1T.3x="fS: tk; 5A: 3k%";42.1T.3x="5A: 3k%; 3V: "+(4R?"tj":"3k%");3L.2c(42);21.2c(3L);D.6K();c0();if(4R){D.2m=L}N{D.2m=2m}D.8Q=4R;D.hA=bZ;D.8P=1m;F D};B.1I.1I.1U={"8O":"ti tg,tf-te","8N":{"8M":"1v","8L":"gU","8K":"1F","8J":"8y","8I":"bx"}};B.1I.1W=["1I"];B.1I.1z=["bY"];B.1I.2d=G(){D.2k={":3e":D.1z,":1p":B.J.2o(D.1z,D.1W)};B.J.3f(D);B.1I.3l=O};B.1I.2d();B.J.2Y(D,B.1I);if(H(1q)!="L"){1q.2X("B.V");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.V 3F on B.J"}if(H(B.V)=="L"){B.V={}}B.V.1r="B.V";B.V.1Y="1.3.1";B.V.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.V.1l=G(){F D.1K()};B.V.V=G(1v,hz,1F,6J){if(H(6J)=="L"||6J===O){6J=1}D.1B={r:1v,g:hz,b:1F,a:6J}};B.V.V.1U={bX:B.V.V,tc:G(hy){u 1B=D.1B;u m=B.V;F m.V.3Y(1B.r,1B.g,1B.b,hy)},tb:G(1o){u 1G=D.41();1G.h=1o;u m=B.V;F m.V.4H(1G)},ta:G(hx){u 1G=D.41();1G.s=hx;u m=B.V;F m.V.4H(1G)},t9:G(hw){u 1G=D.41();1G.l=hw;u m=B.V;F m.V.4H(1G)},t8:G(hv){u 1G=D.41();1G.l=28.29(1G.l-hv,0);u m=B.V;F m.V.4H(1G)},t7:G(hu){u 1G=D.41();1G.l=28.2a(1G.l+hu,1);u m=B.V;F m.V.4H(1G)},fJ:G(ht,5z){if(H(5z)=="L"||5z===O){5z=0.5}u sf=1-5z;u s=D.1B;u d=ht.1B;u df=5z;F B.V.V.3Y((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df))},h4:G(hs){u a=D.6r();u b=hs.6r();F B.J.2f([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a])},hq:G(){F D.41().b>0.5},t6:G(){F(!D.hq())},t5:G(){u c=D.41();u 2Z=B.V.6F;u W=D.ho;if(!W){u 5y=(2Z(c.h,bF).6I(0)+","+2Z(c.s,3k).hp(4)+"%"+","+2Z(c.l,3k).hp(4)+"%");u a=c.a;if(a>=1){a=1;W="1G("+5y+")"}N{if(a<=0){a=0}W="t4("+5y+","+a+")"}D.ho=W}F W},hl:G(){u c=D.1B;u 2Z=B.V.6F;u W=D.hn;if(!W){u 5y=(2Z(c.r,3h).6I(0)+","+2Z(c.g,3h).6I(0)+","+2Z(c.b,3h).6I(0));if(c.a!=1){W="t3("+5y+","+c.a+")"}N{W="1B("+5y+")"}D.hn=W}F W},6r:G(){F B.J.4L(D.1B)},t2:G(){u m=B.V;u c=D.1B;u 2Z=B.V.6F;u W=D.hm;if(!W){W=("#"+m.6E(2Z(c.r,3h))+m.6E(2Z(c.g,3h))+m.6E(2Z(c.b,3h)));D.hm=W}F W},t1:G(){u 2Q=D.2Q;u c=D.1B;if(H(2Q)=="L"||2Q===O){2Q=B.V.bA(D.1B);D.2Q=2Q}F B.J.4L(2Q)},41:G(){u 1G=D.1G;u c=D.1B;if(H(1G)=="L"||1G===O){1G=B.V.bC(D.1B);D.1G=1G}F B.J.4L(1G)},1l:G(){F D.hl()},U:G(){u c=D.1B;u hk=[c.r,c.g,c.b,c.a];F D.bX.1r+"("+hk.2b(", ")+")"}};B.J.2l(B.V.V,{3Y:G(1v,bW,1F,8H){u hj=B.V.V;if(M.K==1){u 1B=1v;1v=1B.r;bW=1B.g;1F=1B.b;if(H(1B.a)=="L"){8H=L}N{8H=1B.a}}F Y hj(1v,bW,1F,8H)},4H:G(1o,t0,sZ,sY){u m=B.V;F m.V.3Y(m.bB.1w(m,M))},sX:G(1o,sW,sV,sU){u m=B.V;F m.V.3Y(m.bz.1w(m,M))},hi:G(1b){u 8F=B.V.V;if(1b.3Z(0)=="\\""){1b=1b.3H(1,1b.K-2)}u bV=8F.by[1b.8G()];if(H(bV)=="1n"){F 8F.bT(bV)}N{if(1b=="aP"){F 8F.sT()}}F O},8f:G(4Q){u I=B.V.V;u bU=4Q.3H(0,3);if(bU=="1B"){F I.h9(4Q)}N{if(bU=="1G"){F I.h8(4Q)}N{if(4Q.3Z(0)=="#"){F I.bT(4Q)}}}F I.hi(4Q)},bT:G(4P){if(4P.3Z(0)=="#"){4P=4P.2W(1)}u 8E=[];u i,5x;if(4P.K==3){R(i=0;i<3;i++){5x=4P.3H(i,1);8E.1c(3w(5x+5x,16)/3h)}}N{R(i=0;i<6;i+=2){5x=4P.3H(i,2);8E.1c(3w(5x,16)/3h)}}u bS=B.V.V;F bS.3Y.1w(bS,8E)},bG:G(4O,hf,hg,4N){if(4N.2A(4O)===0){4N=4N.2W(4N.2A("(",3)+1,4N.K-1)}u bR=4N.2R(/\\s*,\\s*/);u bP=[];R(u i=0;i0){F 8D}}F O},ba:G(Q){u 2F=B.V.V;F 2F.bN(Q,"aZ","he-3U")||2F.sN()},sM:G(Q){u 2F=B.V.V;F 2F.bN(Q,"3U","3U")||2F.sL()},sK:G(){F B.J.4L(B.V.V.by)}});B.J.2l(B.V,{6F:G(v,8C){v*=8C;if(v<0){F 0}N{if(v>8C){F 8C}N{F v}}},hc:G(n1,n2,1o){if(1o>6){1o-=6}N{if(1o<0){1o+=6}}u 2i;if(1o<1){2i=n1+(n2-n1)*1o}N{if(1o<3){2i=n2}N{if(1o<4){2i=n1+(n2-n1)*(4-1o)}N{2i=n1}}}F 2i},bz:G(1o,5w,3i,bM){if(M.K==1){u 2Q=1o;1o=2Q.h;5w=2Q.s;3i=2Q.v;bM=2Q.a}u 1v;u 3K;u 1F;if(5w===0){1v=0;3K=0;1F=0}N{u i=28.8B(1o*6);u f=(1o*6)-i;u p=3i*(1-5w);u q=3i*(1-(5w*f));u t=3i*(1-(5w*(1-f)));hd(i){3j 1:1v=q;3K=3i;1F=p;2K;3j 2:1v=p;3K=3i;1F=t;2K;3j 3:1v=p;3K=q;1F=3i;2K;3j 4:1v=t;3K=p;1F=3i;2K;3j 5:1v=3i;3K=p;1F=q;2K;3j 6:3j 0:1v=3i;3K=t;1F=p;2K}}F{r:1v,g:3K,b:1F,a:bM}},bB:G(1o,5v,3v,bL){if(M.K==1){u 1G=1o;1o=1G.h;5v=1G.s;3v=1G.l;bL=1G.a}u 1v;u 8A;u 1F;if(5v===0){1v=3v;8A=3v;1F=3v}N{u m2;if(3v<=0.5){m2=3v*(1+5v)}N{m2=3v+5v-(3v*5v)}u m1=(2*3v)-m2;u f=B.V.hc;u h6=1o*6;1v=f(m1,m2,h6+2);8A=f(m1,m2,h6);1F=f(m1,m2,h6-2)}F{r:1v,g:8A,b:1F,a:bL}},bA:G(1v,4K,1F,bK){if(M.K==1){u 1B=1v;1v=1B.r;4K=1B.g;1F=1B.b;bK=1B.a}u 29=28.29(28.29(1v,4K),1F);u 2a=28.2a(28.2a(1v,4K),1F);u 1o;u 8z;u hb=29;if(2a==29){1o=0;8z=0}N{u 6H=(29-2a);8z=6H/29;if(1v==29){1o=(4K-1F)/6H}N{if(4K==29){1o=2+((1F-1v)/6H)}N{1o=4+((1v-4K)/6H)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:8z,v:hb,a:bK}},bC:G(1v,4J,1F,bI){if(M.K==1){u 1B=1v;1v=1B.r;4J=1B.g;1F=1B.b;bI=1B.a}u 29=28.29(1v,28.29(4J,1F));u 2a=28.2a(1v,28.2a(4J,1F));u 1o;u 6G;u bJ=(29+2a)/2;u 4I=29-2a;if(4I===0){1o=0;6G=0}N{if(bJ<=0.5){6G=4I/(29+2a)}N{6G=4I/(2-29-2a)}if(1v==29){1o=(4J-1F)/4I}N{if(4J==29){1o=2+((1F-1v)/4I)}N{1o=4+((1v-4J)/4I)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:6G,l:bJ,a:bI}},6E:G(1P){1P=28.ha(1P);u bH=1P.1l(16);if(1P<16){F"0"+bH}F bH},2d:G(){u m=B.J;D.V.h9=m.1O(D.V.bG,D.V,"1B","3Y",[1/3h,1/3h,1/3h,1]);D.V.h8=m.1O(D.V.bG,D.V,"1G","4H",[1/bF,0.bE,0.bE,1]);u 4G=1/3;u bD={8y:[0,0,0],1F:[0,0,1],gY:[0.6,0.4,0.2],gX:[0,1,1],sJ:[4G,4G,4G],gR:[0.5,0.5,0.5],bx:[0,1,0],sI:[2*4G,2*4G,2*4G],gN:[1,0,1],gL:[1,0.5,0],gK:[0.5,0,0.5],1v:[1,0,0],aP:[0,0,0,0],4F:[1,1,1],gI:[1,1,0]};u h7=G(1b,r,g,b,a){u W=D.3Y(r,g,b,a);D[1b]=G(){F W};F W};R(u k in bD){u 1b=k+"V";u h5=m.2o([h7,D.V,1b],bD[k]);D.V[1b]=m.1O.1w(O,h5)}u h0=G(){R(u i=0;i1){u 1d=B.S.1E(M[0]);u 2D=M[1];u 1i=M[2];u 1A=M[3];R(u i=5o.K-1;i>=0;i--){u o=5o[i];if(o[0]===1d&&o[1]===2D&&o[4]===1i&&o[5]===1A){I.6t(o);5o.4y(i,1);F 1h}}}N{u 5n=m.bi(5o,bh);if(5n>=0){I.6t(bh);5o.4y(5n,1);F 1h}}F 1m},8i:G(1d,2D){1d=B.S.1E(1d);u m=B.J;u 8l=m.bg(m.1R(O,M,1));u I=B.1u;u bd=I.6t;u 4z=I.4x;if(8l.K===0){R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d){bd(4A);4z.4y(i,1)}}}N{u bf={};R(u i=0;i<8l.K;i++){bf[8l[i]]=1h}R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d&&4A[1]in bf){bd(4A);4z.4y(i,1)}}}},8h:G(1d,2D){u bc=B.1u.4x;1d=B.S.1E(1d);u 3G=B.J.1R(O,M,2);u 5m=[];R(u i=0;i1){u e=Y 2x("mZ bb mY in mX \'2D\', mW bb mV");e.bb=5m;14 e}}}});B.1u.1W=[];B.1u.1z=["6s","8j","8h","8i"];B.1u.2d=G(2m){u m=B.J;D.1Z=2v;D.3X=2m;1f{D.6s(2O,"g8",D.g7)}1e(e){}D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.1u.2d(D);if(!B.3d){6s=B.1u.6s;8j=B.1u.8j;8i=B.1u.8i;8h=B.1u.8h}B.J.2Y(D,B.1u);if(H(1q)!="L"){1q.2X("B.1X");1q.2M("B.J");1q.2M("B.S");1q.2M("B.V")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.S",[]);1x.26("B.V",[])}1f{if(H(B.J)=="L"||H(B.S)=="L"||H(B.V)=="L"){14""}}1e(e){14"B.1X 3F on B.J, B.S 3W B.V!"}if(H(B.1X)=="L"){B.1X={}}B.1X.1r="B.1X";B.1X.1Y="1.3.1";B.1X.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1X.1l=G(){F D.1K()};B.1X.aI=G(e,g6){e=B.S.1E(e);D.fN(g6);if(D.1S.fL){e=D.g5(e)}u 4w=D.1S.3U;u C=B.V.V;if(D.1S.3U=="aW"){4w=C.ba(e)}N{if(!(4w 2C C)){4w=C.8f(4w)}}D.82=(4w.6r().a<=0);u 5l=D.1S.aV;if(D.1S.aV=="fM"){5l=C.ba(e.8g)}N{if(!(5l 2C C)){5l=C.8f(5l)}}D.g3(e,4w,5l)};B.1X.aI.1U={g5:G(e){u mU=e.3t;u 1N=B.S.b9();if(H(1N.5k)=="L"||1N.5k===O){F e}u 4v=1N.5k.g4(e,O);if(H(4v)=="L"||4v===O){F e}u b8=B.S.6m({"1T":{3u:"8c",mT:4v.6q("6p-3D"),85:4v.6q("6p-3g"),mS:4v.6q("6p-6f"),86:4v.6q("6p-2I"),6p:"2N"}});b8.6o=e.6o;e.6o="";e.2c(b8);F e},g3:G(e,b7,8e){if(D.1S.3E){D.g2(e,8e)}if(D.fy()){D.fX(e,b7,8e)}if(D.fx()){D.fV(e,b7,8e)}},g2:G(el,g1){u b6="6l 8a "+D.aQ(g1);u g0="3E-2I: "+b6;u fZ="3E-3g: "+b6;u fY="1T=\'"+g0+";"+fZ+"\'";el.6o="<4u "+fY+">"+el.6o+""},fX:G(el,fW,b5){u b4=D.b1(b5);R(u i=0;i=0;i--){b2.2c(D.b0(fU,b3,i,"6f"))}el.1T.mP=0;el.2c(b2)},b1:G(fT){u 2q=B.S;F 2q.6m({1T:{aZ:fT.1l()}})},b0:G(aY,fQ,n,aX){u 6k=B.S.8d();u 2p=6k.1T;2p.aZ=aY.1l();2p.3u="8c";2p.3V="6l";2p.fS="fR";2p.mO="6l";u 8b=D.aQ(aY,fQ);if(D.1S.3E&&n===0){2p.mN="8a";2p.mM="6l";2p.84="2N";2p.83="2N";2p.mL="2N";2p.3V="2N";2p.fP=8b.1l()}N{if(8b){2p.fP=8b.1l();2p.mK="8a";2p.mJ="2N 6l"}}if(!D.1S.4r&&(n==(D.1S.89-1))){2p.3V="fO"}D.fI(6k,n,aX);D.fG(6k,n,aX);F 6k},fN:G(fK){D.1S={6g:"1p",3U:"aW",aV:"fM",5j:1h,3E:1m,4r:1m,fL:1m};B.J.2l(D.1S,fK);D.1S.89=(D.1S.4r?2:4)},aL:G(){u 88=D.1S.6g;if(D.6h(88,"1p","3D")){F""}u aU=(88.2A("tl")!=-1);u aT=(88.2A("tr")!=-1);if(aU&&aT){F""}if(aU){F"2I"}if(aT){F"3g"}F""},aK:G(){u 87=D.1S.6g;if(D.6h(87,"1p","6f")){F""}u aS=(87.2A("bl")!=-1);u aR=(87.2A("br")!=-1);if(aS&&aR){F""}if(aS){F"2I"}if(aR){F"3g"}F""},aQ:G(aN,aO){if(aN=="aP"){F aO}N{if(D.1S.3E){F D.1S.3E}N{if(D.1S.5j){F aO.fJ(aN)}}}F""},fI:G(el,n,fH){u 6j=D.fE(n)+"px";u aM=(fH=="3D"?D.aL():D.aK());u 4t=el.1T;if(aM=="2I"){4t.86=6j;4t.85="2N"}N{if(aM=="3g"){4t.85=6j;4t.86="2N"}N{4t.86=6j;4t.85=6j}}},fG:G(el,n,fF){u 6i=D.fz(n)+"px";u aJ=(fF=="3D"?D.aL():D.aK());u 4s=el.1T;if(aJ=="2I"){4s.84=6i;4s.83="2N"}N{if(aJ=="3g"){4s.83=6i;4s.84="2N"}N{4s.84=6i;4s.83=6i}}},fE:G(n){if(D.82){F 0}u o=D.1S;if(o.4r&&o.5j){u fD=[1,0];F fD[n]}N{if(o.4r){u fC=[2,1];F fC[n]}N{if(o.5j){u fB=[3,2,1,0];F fB[n]}N{u fA=[5,3,2,1];F fA[n]}}}},fz:G(n){u o=D.1S;u 5i;if(o.4r&&(o.5j||D.82)){F 1}N{if(o.4r){5i=[1,0]}N{if(o.5j){5i=[2,1,1,1]}N{if(o.3E){5i=[0,2,0,0]}N{if(D.82){5i=[5,3,2,1]}N{F 0}}}}}F 5i[n]},6h:G(1y){R(u i=1;i")}}})()}',62,1976,'||||||||||||||||||||||||||||||var|||||||MochiKit||this||return|function|typeof|self|Base|length|undefined|arguments|else|null||elem|for|DOM||repr|Color|rval|res|new||||||throw|Iter|||||next|name|push|src|catch|try|lst|true|obj|node|Async|toString|false|string|hue|all|dojo|NAME|Format|msg|Signal|red|apply|JSAN|str|EXPORT|func|rgb|_425|DateTime|getElement|blue|hsl|Logging|LoggingPane|type|__repr__|_event|while|doc|bind|num|iter|extend|options|style|prototype|seq|EXPORT_OK|Visual|VERSION|_document||_434||replace|forwardCall|StopIteration|use||Math|max|min|join|appendChild|__new__|button|compare|date|key|val|_329|EXPORT_TAGS|update|win|pair|concat|_596|dom|map|req|Deferred|sync|document|base|Error|number|partial|indexOf||instanceof|sig|not|cls|list|fired|left|stop|break|logger|require|0px|window|shift|hsv|split|createElement|_423|callee|continue|substring|provide|_exportSymbols|ccc||_464|||||||||step|pred|_51|__compat__|common|nameFunctions|right|255|_517|case|100|_loggingPane|value|object|callback|TypeError|_251|_246|_113|parentNode|display|_522|parseInt|cssText|wrap|info|isArrayLike|end|match|top|border|depends|args|substr|mouse|code|_519|_443|className|level|err|frac|Date|_135|_85|nodeType|color|height|and|_window|fromRGB|charAt||asHSL|_444|message||||filter||LogMessage|AdapterRegistry|_366|imap|NotFound|locked|counter|_262|_messages|operator|cmp|_165|_161|pairs|arr|_52|setAttribute|computedStyle|compact|_614|_610|div|_576|_572|_observers|splice|_565|_566|_555|scrollTop|page|modifier|white|_541|fromHSL|_539|_535|_528|clone|parseFloat|_505|pre|_499|_497|_427|createTextNode|_446|attributeArray|_388|_379|updateNodeAttributes|_341|_326||box|errback|results|paused|chain|_285||ofs||NamedError|_175|_147|_122|_83|_54|_17|childNodes|_619|blend|defaultView|_574|_569|idx|_562|must|_554|_specialKeys|body|Coordinates|registerComparator|_521|_516|hex|mid|_478|width|loggingPane|LogLevel|nwin|head|url|setElementClass|callStack|path|dest|_359|boolean|register|Dimensions|DeferredLock|_313|addCallback|_310|waiting|onreadystatechange|_290|LOCALE|year|printfire|_214|log|_213|_211|pos|_155|_153||typeMatcher|listMinMax|_114|_40|itr|typ|_19|_634|_625|bottom|corners|_hasString|_612|_608|_595|1px|DIV|firstChild|innerHTML|padding|getPropertyValue|asRGB|connect|_disconnect|_559|middle|which|clientY|scrollLeft|clientX|client|charCode|relatedTarget|event|toColorPart|clampColorComponent|_537|_534|toFixed|_468|buildAndApplyFilter|_442|_441|_440|_439|position|_463|_447|removeChild|_449|uid|_428|_426|compliant|attributes|_422|_409|_412|_400|_395|_390|_389|_377|_375|_363|attr|ctx|repeat|_340|_339|isNotEmpty|_335|_333|opera|DeferredList|ret|_309|silentlyCancelled|canceller|_nextId|Array|_293|XMLHttpRequest|chained|_281|tail|_252|_225|msec|day|month|iso|Logger|_208|listeners|_200|_198|_194|_196|reduce|range|_169|_162|truth|registerRepr|_121|_70|_58|_56|_47|_45|_41|_13|_1|script|text|uri|documentElement|_630|_629|isTransparent|borderRightWidth|borderLeftWidth|marginRight|marginLeft|_602|_599|numSlices|solid|_597|block|SPAN|_579|fromString|offsetParent|signal|disconnectAll|disconnect|_570|_563|_557|preventDefault|stopPropagation|clientTop|clientLeft|pageY|pageX|keyCode|meta|ctrl|alt|target|black|_532|_524|floor|_513|_512|_500|_495|toLowerCase|_487|DEBUG|INFO|WARNING|FATAL|ERROR|colorTable|logFont|closed|inline|onclick|_438|_437|_445|RegExp|_452|space|title|updatetree|||||withDocument|withWindow||setDisplayForElement|none|renames|forEach|domConverters|escapeHTML|addElementClass|removeElementClass|once|_378|_380|_376|appendChildNodes|coerceToDOM|_355|opt|clientWidth|opacity|GenericError|fail|resultList|_307|_301|_fire|can|addCallbacks|_resback|percent|decimal|separator|twoDigitFloat|_274|_273|_264|_257|_250|_249|_254|_248|_243|_242|fmt|_240|_245|getTime|sec|hour|_209|slice|_206|iterateNextIter|registerIteratorFactory|arrayLikeIter|iteratorRegistry|takewhile|ifilterfalse|ifilter|_181|_176|_168|_166|_159|_tee|deque|arg|fun|jsonRegistry|reprString|reprRegistry|comparatorRegistry|urlEncode|_110|_108|cur|_95|_87|_71|im_preargs||_53|_57|_46|present|like|array|Argument|_15|_12|_632|_631|_633|SUBMODULES|only|_628|_627|_626|roundElement|_624|getElementsByTagAndClassName|_RoundCorners|_613|_whichSideBottom|_whichSideTop|_609|_605|_606|transparent|_borderColor|_604|_603|_601|_600|bgColor|fromElement|_594|_592|backgroundColor|_createCornerSlice|_createCorner|_590|_589|_587|_586|_581|_578|_577|currentDocument|fromBackground|errors|_568|_564||sigs|flattenArguments|_561|findIdentical|_560|_558||_556|attachEvent|addEventListener|funcOrStr|Event||_548|fromCharCode|String|_specialMacKeys|any|green|_namedColors|hsvToRGB|rgbToHSV|hslToRGB|rgbToHSL|_542|01|360|_fromColorString|_540|_536|_538|_529|_523|_518|fromComputedStyle|_511|_507|_508|_506|_501|fromHexString|_498|_496|_486|__class__|createLoggingPane|_459|_461|font|_462|_430|_435|1000|index|_460|getMessages|removeListener|_451||_457|_450|infore|_448|_456|logDebug|offsetHeight|span|input|_436|TR||HTML|open|alert|currentWindow|swapDOM|SELECT|FORM|INPUT|createDOMFunc|ignoreAttr|_421|call|_417|_410|_415|nodeName|_414|_413|emitHTML|good|_406|_399|_397|_393|_392|addLoadEvent|addToCallStack|_387|_386|_381|_382|_383|_373|_372|_369|createDOM|_365|Function|_360|_362|_358|_344|nodeWalk|formContents|_337|_338|_334|_332|offsetTop|offsetLeft|visibility|parentElement|||XMLHttpRequestError|BrowserComplianceError|CancelledError|AlreadyCalledError|evalJSONRequest|sendXMLHttpRequest|wait|doSimpleXMLHttpRequest|getXMLHttpRequest|succeed|_312|finishedCount|_308|_cbDeferred|_303|_297|queryString|_nothing|_289|XMLHTTP|ActiveXObject|eval|_284|_check|error|_279|default|rstrip|lstrip|formatLocale|roundToFixed|truncToFixed|_276|pow|_272|_271|_270|sign|_265|_263|tmp|_238|_232|toISODate|toISOTime|getFullYear|getDate|getMonth|_230|_padTwo|_228|useNativeConsole|_212|compareLogMessage|isLogMessage|unshift|_207||maxSize|_202|_199|logLevelAtLeast|console|hasIterateNext|iterateNext|arrayLike|groupby||exhaust|tee|dropwhile|applymap||islice|izip|cycle|count||_189|_188|_183|_185|_184|_186|_187|_182|identity|fetch|_180|_177|listMin|reprNumber|reprArrayLike|compareArrayLike|compareDateLike|isDateLike|findValue|_128|__export__|keyComparator|_124|_118|_93|_94|_90|_88|_84|_77|_68|_67|_66|_65|_60|im_func|_55|im_self|_48|_44|_42|_39|_36|_33|_27|_26|_25|_22|_24|_20|javascript|write|getAttribute||org|www|http|getElementsByTagName|roundClass|_623|_622|_621|_620|_isBottomRounded|_isTopRounded|_borderSize|_618|_617|_616|_615|_marginSize|_611|_setBorder|_607|_setMargin|blendedColor|_598|__unstable__wrapElement|fromParent|_setOptions|2px|borderColor|_593|hidden|overflow|_591|_588|_roundBottomCorners|_585|_roundTopCorners|_584|_583|_582|_580|_renderBorder|_roundCornersImpl|getComputedStyle|_doWrap|_571|_unloadCache|onunload|detachEvent|removeEventListener|_listener|objOrFunc|_552||_551|_549|onload|delete|112|KEY_F|KEY_|MINUS|KEY_SEMICOLON|KEY_DELETE|KEY_INSERT|KEY_ARROW_DOWN|KEY_ARROW_RIGHT|KEY_ARROW_UP||KEY_ARROW_LEFT|KEY_HOME|KEY_END|KEY_PAGE_DOWN|KEY_PAGE_UP|KEY_ENTER|KEY_NUM_PAD_CLEAR|63236|mousemove|contextmenu|click|mouseout|mouseover|_src|yellow|708090|purple|orange|ff00ff|magenta|778899|d3d3d3|808080|gray|696969|2f4f4f|darkred|a9a9a9|00ffff|cyan|brown|_547|_546||||compareRGB|_545||_543|fromHSLString|fromRGBString|round|_533|_hslValue|switch|background|_503|_504||fromName|_488|col|toRGBString|_hexString|_rgbString|_hslString|toPrecision|isLight||_481|_477|_476|_475|_474|_473|_469|_466|closePane|_458|onkeypress|_454|addListener|_455|close|test|scrollHeight|option|word|moz|_431|getElementById|html|pop|200|_|removeElement|showElement|hideElement|CANVAS|STRONG|FIELDSET|LEGEND|OPTGROUP|OPTION|TEXTAREA|LABEL|HR|BR|H3|H2|H1|PRE|TT|BUTTON|IMG|TH||TABLE||TFOOT|THEAD|TBODY|TD|LI|OL|||UL|checked|class|ignoreAttrFilter||_424|_419|nodeValue|scrapeText|_416|_418|sort|_411|toHTML|_404|hasElementClass|_403|_402|_401|swapElementClass|_398|_394|toggleElementClass|_391|focusOnLoad|_newCallStack|currentStyle|_371|replaceChildNodes|_364|_361|getNodeAttribute|_357|setNodeAttribute|_354|_352|_350|_353|toDOM|_346|_345|registerDOMConverter|selectedIndex|setElementPosition|setElementDimensions|tagName|absolute|getBoxObjectFor|getBoundingClientRect|elementPosition|_325|_324|_322|_323|offsetWidth|elementDimensions|clientHeight|innerWidth|getViewportDimensions|setOpacity|status|_317|deferred|_316|_newNamedError|maybeDeferred||gatherResults|callLater|loadJSONDoc|_311|consumeErrors|fireOnOneErrback|fireOnOneCallback|addErrback|_305|_304|_306|unlocked|release|_300|_299|_298|_296|_xhr_onreadystatechange|_xhr_canceller|304|responseText|Msxml2|addBoth|_pause|_continue|result|the|are|they|instances|_unpause|cancel|_280|_278|en_US|strip|percentFormat|twoDigitAverage|numberFormatter|_277|_275|isNaN|_259|_258|_260|_255|_253|_numberFormatter|_241|_239|_237|_236|_235|_234|_233|_231|toAmericanDate|toPaddedAmericanDate|americanDate|toISOTimestamp|isoTimestamp|isoDate|foot|sep||60000|_221|_isoRegexp|dispatchEvent|createEvent|warning|logWarning|fatal|logFatal|debug|logError|baseLog|_210|getMessageText|logToConsole|dispatchListeners|_204|_203|ident|_201|postError|alertListener|_197|_192|groupby_as_array|iextend|some|reversed|sorted|every|sum|_190|eat|_174|_173|_172|_171|_167|_163|_158|_157|_151|_144|_141||_139|_136|_134||_133|_132|zip|merge|isUndefined|isCallable|listMax|_131|_130|encodeURIComponent||_127|method|parseQueryString|evalJSON|registerJSON|serializeJSON|objMin|objMax|reverseKeyComparator|arrayEqual|objEqual|bindMethods|xfilter|xmap|isEmpty|isNull|isUndefinedOrNull|itemgetter|items|keys|setdefault|_126|_120|decodeURIComponent|_119|len|_109|_107|_104|_105|_101|_102|_98|||_100|_97|_96|_91|json|__json__|_82|_81|_80|_79|_76||_75|_74|_73|_69|_primitives|_64|_63||_62|_61|_59|_wrapDumbFunction|_49|_50|_31|_30|_21|_7|application|MochiKit_|createElementNS|namespaceURI|lastIndexOf|xul|there|gatekeeper|keymaster|mozilla|getElementsComputedStyle|_hasSingleTextChild|borderWidth|borderStyle|borderBottomWidth|borderTopWidth|borderTopStyle|fontSize|paddingBottom|insertBefore|paddingTop|marginBottom|marginTop|_575|property|see|handling|thrown|Multiple|element|||given|123|KEY_NUM_PAD_|105|KEY_APOSTROPHE|222|KEY_RIGHT_SQUARE_BRACKET|221|KEY_REVERSE_SOLIDUS|220|KEY_LEFT_SQUARE_BRACKET||219|KEY_GRAVE_ACCENT|192|KEY_SOLIDUS|191|KEY_FULL_STOP|190|KEY_HYPHEN|189||KEY_COMMA|188|KEY_EQUALS_SIGN|187|186|KEY_SCROLL_LOCK|145|KEY_NUM_LOCK|144|KEY_NUM_PAD_SOLIDUS|111|KEY_NUM_PAD_FULL_STOP|110|KEY_NUM_PAD_HYPHEN|109|KEY_NUM_PAD_PLUS_SIGN|107|KEY_NUM_PAD_ASTERISK|106|KEY_SELECT|KEY_WINDOWS_RIGHT|KEY_WINDOWS_LEFT|KEY_PRINT_SCREEN|KEY_SPACEBAR|KEY_ESCAPE|KEY_CAPS_LOCK|KEY_PAUSE|KEY_ALT|KEY_CTRL|KEY_SHIFT|KEY_TAB|KEY_BACKSPACE|63242|63272|63302|63233|63235|63232|63234|63273|63275|63277|63276|63289|returnValue|cancelBubble|keypress|KEY_UNKNOWN|keyup|keydown|shiftKey|metaKey||ctrlKey|altKey|toElement|srcElement|9acd32||yellowgreen||ffff00|f5f5f5|whitesmoke||ffffff|f5deb3|wheat|ee82ee|violet|40e0d0|turquoise|ff6347|tomato|d8bfd8|thistle|008080|teal|d2b48c|tan|4682b4|steelblue|00ff7f|springgreen|fffafa|snow|slategrey|slategray|6a5acd|slateblue|87ceeb|skyblue|c0c0c0|silver|a0522d|sienna|fff5ee|seashell|2e8b57|seagreen|f4a460|sandybrown|fa8072|salmon|8b4513|saddlebrown|4169e1|royalblue|bc8f8f|rosybrown|ff0000|800080|b0e0e6|powderblue|dda0dd|plum|ffc0cb|pink|cd853f||peru|ffdab9|peachpuff|ffefd5|papayawhip|db7093|palevioletred|afeeee|paleturquoise|98fb98|palegreen|eee8aa||palegoldenrod|da70d6|orchid|ff4500|orangered|ffa500|6b8e23|olivedrab|808000|olive|fdf5e6|oldlace|000080|navy|ffdead|navajowhite|ffe4b5|moccasin|ffe4e1|mistyrose|f5fffa|mintcream|191970|midnightblue|c71585|mediumvioletred|48d1cc|mediumturquoise|00fa9a|mediumspringgreen|7b68ee|mediumslateblue|3cb371|mediumseagreen|9370db|mediumpurple|ba55d3|mediumorchid|0000cd|mediumblue|66cdaa|mediumaquamarine|800000|maroon|faf0e6|linen|32cd32|limegreen|00ff00|lime|ffffe0|lightyellow|b0c4de|lightsteelblue|lightslategrey|lightslategray||87cefa|lightskyblue|20b2aa|lightseagreen|ffa07a|lightsalmon|ffb6c1|lightpink|lightgrey|90ee90|lightgreen|lightgray|fafad2|lightgoldenrodyellow|e0ffff|lightcyan|f08080|lightcoral|add8e6|lightblue|fffacd|lemonchiffon|7cfc00|lawngreen|fff0f5|lavenderblush|e6e6fa|lavender|f0e68c|khaki|fffff0|ivory|4b0082|indigo|cd5c5c|indianred|ff69b4|hotpink|f0fff0|honeydew|grey|adff2f|greenyellow|008000|daa520|goldenrod|ffd700||gold|f8f8ff|ghostwhite|dcdcdc|gainsboro|fuchsia|228b22|forestgreen|fffaf0|floralwhite|b22222|firebrick|1e90ff|dodgerblue|dimgrey|dimgray|00bfff|deepskyblue|ff1493|deeppink|9400d3|darkviolet|00ced1|darkturquoise|darkslategrey|darkslategray|483d8b|darkslateblue|8fbc8f|darkseagreen|e9967a|darksalmon|8b0000|9932cc|darkorchid|ff8c00|darkorange|556b2f|darkolivegreen|8b008b|darkmagenta|bdb76b|darkkhaki|darkgrey|006400|darkgreen|darkgray|b8860b|darkgoldenrod|008b8b|darkcyan|00008b|darkblue|dc143c|crimson|fff8dc|cornsilk|6495ed|cornflowerblue|ff7f50|coral|d2691e||chocolate|7fff00|chartreuse|5f9ea0|cadetblue|deb887|burlywood|a52a2a|8a2be2|blueviolet|0000ff|ffebcd||blanchedalmond|000000|ffe4c4|bisque|f5f5dc|beige|f0ffff|azure|7fffd4|aquamarine|aqua|faebd7|antiquewhite|f0f8ff|aliceblue|lightGray|darkGray|namedColors|blackColor|fromText|whiteColor|_510|_509|PI|rad|deg|transparentColor|_494|_493|_492|fromHSV|_491|_490|_489|asHSV|toHexString|rgba|hsla|toHSLString|isDark|lighterColorWithLevel|darkerColorWithLevel|colorWithLightness|colorWithSaturation|colorWithHue|colorWithAlpha||serif|sans|Verdana||8pt|8em|auto||Close|Clear||Load|Filter||10em||fixed|regex|emergency|line|margin|_Listener|dtd|loose|html4|w3|EN|Transitional|DTD|W3C|PUBLIC|DOCTYPE|blocking|due|debugging|able|Not|resizable|dependent|href|location|_MochiKit_LoggingPane|_429|canvas|strong|fieldset|legend|optgroup|select|form|textarea|label|img|table|tfoot|thead|tbody|htmlFor||useMap|usemap|defaultChecked|hasChildNodes|quot|amp|_405|focus|replaceChild|checkbox||radio|_win|BODY||safari|version|userAgent|navigator|innerHeight|alpha|khtml|Tried|acquire|clearTimeout|setTimeout|GET|ignore|send|abort|failed|Request|readyState|support|does|Browser|Microsoft|_288|_287|used|Deferreds|Chained|success|unfired|fr_FR|de_DE|00|abs|search|pattern|Invalid|getTimezoneOffset|getSeconds|getMinutes|getHours|UTC|3600000|initEvent|Events|debuggingBookmarklet|MESSAGES|LAST|_205|clear|ninfo|nlevel|timestamp|reverse|takes|initial|with|sequence|empty|iterable|numbers|dateLike|escape|find|forward|unregister|unescape|Object|compared|item|contains|logor|logand|cle|clt|cge|cgt|cne|ceq|zrshift|rshift|lshift|xor|mul|mod|sub|add|neg|lognot|_9|_2'.split('|'),0,{}) /* * jQuery 1.2.1 - New Wave Javascript * * Copyright (c) 2007 John Resig (jquery.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ * $Rev: 3353 $ */ var decompressedJQuery = function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(G(){9(1m E!="W")H w=E;H E=18.15=G(a,b){I 6 7u E?6.5N(a,b):1u E(a,b)};9(1m $!="W")H D=$;18.$=E;H u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;E.1b=E.3A={5N:G(c,a){c=c||U;9(1m c=="1M"){H m=u.2S(c);9(m&&(m[1]||!a)){9(m[1])c=E.4D([m[1]],a);J{H b=U.3S(m[3]);9(b)9(b.22!=m[3])I E().1Y(c);J{6[0]=b;6.K=1;I 6}J c=[]}}J I 1u E(a).1Y(c)}J 9(E.1n(c))I 1u E(U)[E.1b.2d?"2d":"39"](c);I 6.6v(c.1c==1B&&c||(c.4c||c.K&&c!=18&&!c.1y&&c[0]!=W&&c[0].1y)&&E.2h(c)||[c])},4c:"1.2.1",7Y:G(){I 6.K},K:0,21:G(a){I a==W?E.2h(6):6[a]},2o:G(a){H b=E(a);b.4Y=6;I b},6v:G(a){6.K=0;1B.3A.1a.16(6,a);I 6},N:G(a,b){I E.N(6,a,b)},4I:G(a){H b=-1;6.N(G(i){9(6==a)b=i});I b},1x:G(f,d,e){H c=f;9(f.1c==3X)9(d==W)I 6.K&&E[e||"1x"](6[0],f)||W;J{c={};c[f]=d}I 6.N(G(a){L(H b 1i c)E.1x(e?6.R:6,b,E.1e(6,c[b],e,a,b))})},17:G(b,a){I 6.1x(b,a,"3C")},2g:G(e){9(1m e!="5i"&&e!=S)I 6.4n().3g(U.6F(e));H t="";E.N(e||6,G(){E.N(6.3j,G(){9(6.1y!=8)t+=6.1y!=1?6.6x:E.1b.2g([6])})});I t},5m:G(b){9(6[0])E(b,6[0].3H).6u().3d(6[0]).1X(G(){H a=6;1W(a.1w)a=a.1w;I a}).3g(6);I 6},8m:G(a){I 6.N(G(){E(6).6q().5m(a)})},8d:G(a){I 6.N(G(){E(6).5m(a)})},3g:G(){I 6.3z(1q,Q,1,G(a){6.58(a)})},6j:G(){I 6.3z(1q,Q,-1,G(a){6.3d(a,6.1w)})},6g:G(){I 6.3z(1q,P,1,G(a){6.12.3d(a,6)})},50:G(){I 6.3z(1q,P,-1,G(a){6.12.3d(a,6.2q)})},2D:G(){I 6.4Y||E([])},1Y:G(t){H b=E.1X(6,G(a){I E.1Y(t,a)});I 6.2o(/[^+>] [^+>]/.14(t)||t.1g("..")>-1?E.4V(b):b)},6u:G(e){H f=6.1X(G(){I 6.67?E(6.67)[0]:6.4R(Q)});H d=f.1Y("*").4O().N(G(){9(6[F]!=W)6[F]=S});9(e===Q)6.1Y("*").4O().N(G(i){H c=E.M(6,"2P");L(H a 1i c)L(H b 1i c[a])E.1j.1f(d[i],a,c[a][b],c[a][b].M)});I f},1E:G(t){I 6.2o(E.1n(t)&&E.2W(6,G(b,a){I t.16(b,[a])})||E.3m(t,6))},5V:G(t){I 6.2o(t.1c==3X&&E.3m(t,6,Q)||E.2W(6,G(a){I(t.1c==1B||t.4c)?E.2A(a,t)<0:a!=t}))},1f:G(t){I 6.2o(E.1R(6.21(),t.1c==3X?E(t).21():t.K!=W&&(!t.11||E.11(t,"2Y"))?t:[t]))},3t:G(a){I a?E.3m(a,6).K>0:P},7c:G(a){I 6.3t("."+a)},3i:G(b){9(b==W){9(6.K){H c=6[0];9(E.11(c,"24")){H e=c.4Z,a=[],Y=c.Y,2G=c.O=="24-2G";9(e<0)I S;L(H i=2G?e:0,33=2G?e+1:Y.K;i<33;i++){H d=Y[i];9(d.26){H b=E.V.1h&&!d.9V["1Q"].9L?d.2g:d.1Q;9(2G)I b;a.1a(b)}}I a}J I 6[0].1Q.1p(/\\r/g,"")}}J I 6.N(G(){9(b.1c==1B&&/4k|5j/.14(6.O))6.2Q=(E.2A(6.1Q,b)>=0||E.2A(6.2H,b)>=0);J 9(E.11(6,"24")){H a=b.1c==1B?b:[b];E("9h",6).N(G(){6.26=(E.2A(6.1Q,a)>=0||E.2A(6.2g,a)>=0)});9(!a.K)6.4Z=-1}J 6.1Q=b})},4o:G(a){I a==W?(6.K?6[0].3O:S):6.4n().3g(a)},6H:G(a){I 6.50(a).28()},6E:G(i){I 6.2J(i,i+1)},2J:G(){I 6.2o(1B.3A.2J.16(6,1q))},1X:G(b){I 6.2o(E.1X(6,G(a,i){I b.2O(a,i,a)}))},4O:G(){I 6.1f(6.4Y)},3z:G(f,d,g,e){H c=6.K>1,a;I 6.N(G(){9(!a){a=E.4D(f,6.3H);9(g<0)a.8U()}H b=6;9(d&&E.11(6,"1I")&&E.11(a[0],"4m"))b=6.4l("1K")[0]||6.58(U.5B("1K"));E.N(a,G(){H a=c?6.4R(Q):6;9(!5A(0,a))e.2O(b,a)})})}};G 5A(i,b){H a=E.11(b,"1J");9(a){9(b.3k)E.3G({1d:b.3k,3e:P,1V:"1J"});J E.5f(b.2g||b.6s||b.3O||"");9(b.12)b.12.3b(b)}J 9(b.1y==1)E("1J",b).N(5A);I a}E.1k=E.1b.1k=G(){H c=1q[0]||{},a=1,2c=1q.K,5e=P;9(c.1c==8o){5e=c;c=1q[1]||{}}9(2c==1){c=6;a=0}H b;L(;a<2c;a++)9((b=1q[a])!=S)L(H i 1i b){9(c==b[i])6r;9(5e&&1m b[i]==\'5i\'&&c[i])E.1k(c[i],b[i]);J 9(b[i]!=W)c[i]=b[i]}I c};H F="15"+(1u 3D()).3B(),6p=0,5c={};E.1k({8a:G(a){18.$=D;9(a)18.15=w;I E},1n:G(a){I!!a&&1m a!="1M"&&!a.11&&a.1c!=1B&&/G/i.14(a+"")},4a:G(a){I a.2V&&!a.1G||a.37&&a.3H&&!a.3H.1G},5f:G(a){a=E.36(a);9(a){9(18.6l)18.6l(a);J 9(E.V.1N)18.56(a,0);J 3w.2O(18,a)}},11:G(b,a){I b.11&&b.11.27()==a.27()},1L:{},M:G(c,d,b){c=c==18?5c:c;H a=c[F];9(!a)a=c[F]=++6p;9(d&&!E.1L[a])E.1L[a]={};9(b!=W)E.1L[a][d]=b;I d?E.1L[a][d]:a},30:G(c,b){c=c==18?5c:c;H a=c[F];9(b){9(E.1L[a]){2E E.1L[a][b];b="";L(b 1i E.1L[a])1T;9(!b)E.30(c)}}J{2a{2E c[F]}29(e){9(c.53)c.53(F)}2E E.1L[a]}},N:G(a,b,c){9(c){9(a.K==W)L(H i 1i a)b.16(a[i],c);J L(H i=0,48=a.K;i<48;i++)9(b.16(a[i],c)===P)1T}J{9(a.K==W)L(H i 1i a)b.2O(a[i],i,a[i]);J L(H i=0,48=a.K,3i=a[0];i<48&&b.2O(3i,i,3i)!==P;3i=a[++i]){}}I a},1e:G(c,b,d,e,a){9(E.1n(b))b=b.2O(c,[e]);H f=/z-?4I|7T-?7Q|1r|69|7P-?1H/i;I b&&b.1c==4W&&d=="3C"&&!f.14(a)?b+"2T":b},1o:{1f:G(b,c){E.N((c||"").2l(/\\s+/),G(i,a){9(!E.1o.3K(b.1o,a))b.1o+=(b.1o?" ":"")+a})},28:G(b,c){b.1o=c!=W?E.2W(b.1o.2l(/\\s+/),G(a){I!E.1o.3K(c,a)}).66(" "):""},3K:G(t,c){I E.2A(c,(t.1o||t).3s().2l(/\\s+/))>-1}},2k:G(e,o,f){L(H i 1i o){e.R["3r"+i]=e.R[i];e.R[i]=o[i]}f.16(e,[]);L(H i 1i o)e.R[i]=e.R["3r"+i]},17:G(e,p){9(p=="1H"||p=="2N"){H b={},42,41,d=["7J","7I","7G","7F"];E.N(d,G(){b["7C"+6]=0;b["7B"+6+"5Z"]=0});E.2k(e,b,G(){9(E(e).3t(\':3R\')){42=e.7A;41=e.7w}J{e=E(e.4R(Q)).1Y(":4k").5W("2Q").2D().17({4C:"1P",2X:"4F",19:"2Z",7o:"0",1S:"0"}).5R(e.12)[0];H a=E.17(e.12,"2X")||"3V";9(a=="3V")e.12.R.2X="7g";42=e.7e;41=e.7b;9(a=="3V")e.12.R.2X="3V";e.12.3b(e)}});I p=="1H"?42:41}I E.3C(e,p)},3C:G(h,j,i){H g,2w=[],2k=[];G 3n(a){9(!E.V.1N)I P;H b=U.3o.3Z(a,S);I!b||b.4y("3n")==""}9(j=="1r"&&E.V.1h){g=E.1x(h.R,"1r");I g==""?"1":g}9(j.1t(/4u/i))j=y;9(!i&&h.R[j])g=h.R[j];J 9(U.3o&&U.3o.3Z){9(j.1t(/4u/i))j="4u";j=j.1p(/([A-Z])/g,"-$1").2p();H d=U.3o.3Z(h,S);9(d&&!3n(h))g=d.4y(j);J{L(H a=h;a&&3n(a);a=a.12)2w.4w(a);L(a=0;a<2w.K;a++)9(3n(2w[a])){2k[a]=2w[a].R.19;2w[a].R.19="2Z"}g=j=="19"&&2k[2w.K-1]!=S?"2s":U.3o.3Z(h,S).4y(j)||"";L(a=0;a<2k.K;a++)9(2k[a]!=S)2w[a].R.19=2k[a]}9(j=="1r"&&g=="")g="1"}J 9(h.3Q){H f=j.1p(/\\-(\\w)/g,G(m,c){I c.27()});g=h.3Q[j]||h.3Q[f];9(!/^\\d+(2T)?$/i.14(g)&&/^\\d/.14(g)){H k=h.R.1S;H e=h.4v.1S;h.4v.1S=h.3Q.1S;h.R.1S=g||0;g=h.R.71+"2T";h.R.1S=k;h.4v.1S=e}}I g},4D:G(a,e){H r=[];e=e||U;E.N(a,G(i,d){9(!d)I;9(d.1c==4W)d=d.3s();9(1m d=="1M"){d=d.1p(/(<(\\w+)[^>]*?)\\/>/g,G(m,a,b){I b.1t(/^(70|6Z|6Y|9Q|4t|9N|9K|3a|9G|9E)$/i)?m:a+">"});H s=E.36(d).2p(),1s=e.5B("1s"),2x=[];H c=!s.1g("<9y")&&[1,"<24>",""]||!s.1g("<9w")&&[1,"<6T>",""]||s.1t(/^<(9u|1K|9t|9r|9p)/)&&[1,"<1I>",""]||!s.1g("<4m")&&[2,"<1I><1K>",""]||(!s.1g("<9m")||!s.1g("<9k"))&&[3,"<1I><1K><4m>",""]||!s.1g("<6Y")&&[2,"<1I><1K><6L>",""]||E.V.1h&&[1,"1s<1s>",""]||[0,"",""];1s.3O=c[1]+d+c[2];1W(c[0]--)1s=1s.5p;9(E.V.1h){9(!s.1g("<1I")&&s.1g("<1K")<0)2x=1s.1w&&1s.1w.3j;J 9(c[1]=="<1I>"&&s.1g("<1K")<0)2x=1s.3j;L(H n=2x.K-1;n>=0;--n)9(E.11(2x[n],"1K")&&!2x[n].3j.K)2x[n].12.3b(2x[n]);9(/^\\s/.14(d))1s.3d(e.6F(d.1t(/^\\s*/)[0]),1s.1w)}d=E.2h(1s.3j)}9(0===d.K&&(!E.11(d,"2Y")&&!E.11(d,"24")))I;9(d[0]==W||E.11(d,"2Y")||d.Y)r.1a(d);J r=E.1R(r,d)});I r},1x:G(c,d,a){H e=E.4a(c)?{}:E.5o;9(d=="26"&&E.V.1N)c.12.4Z;9(e[d]){9(a!=W)c[e[d]]=a;I c[e[d]]}J 9(E.V.1h&&d=="R")I E.1x(c.R,"9e",a);J 9(a==W&&E.V.1h&&E.11(c,"2Y")&&(d=="9d"||d=="9a"))I c.97(d).6x;J 9(c.37){9(a!=W){9(d=="O"&&E.11(c,"4t")&&c.12)6G"O 94 93\'t 92 91";c.90(d,a)}9(E.V.1h&&/6C|3k/.14(d)&&!E.4a(c))I c.4p(d,2);I c.4p(d)}J{9(d=="1r"&&E.V.1h){9(a!=W){c.69=1;c.1E=(c.1E||"").1p(/6O\\([^)]*\\)/,"")+(3I(a).3s()=="8S"?"":"6O(1r="+a*6A+")")}I c.1E?(3I(c.1E.1t(/1r=([^)]*)/)[1])/6A).3s():""}d=d.1p(/-([a-z])/8Q,G(z,b){I b.27()});9(a!=W)c[d]=a;I c[d]}},36:G(t){I(t||"").1p(/^\\s+|\\s+$/g,"")},2h:G(a){H r=[];9(1m a!="8P")L(H i=0,2c=a.K;i<2c;i++)r.1a(a[i]);J r=a.2J(0);I r},2A:G(b,a){L(H i=0,2c=a.K;i<2c;i++)9(a[i]==b)I i;I-1},1R:G(a,b){9(E.V.1h){L(H i=0;b[i];i++)9(b[i].1y!=8)a.1a(b[i])}J L(H i=0;b[i];i++)a.1a(b[i]);I a},4V:G(b){H r=[],2f={};2a{L(H i=0,6y=b.K;i<6y;i++){H a=E.M(b[i]);9(!2f[a]){2f[a]=Q;r.1a(b[i])}}}29(e){r=b}I r},2W:G(b,a,c){9(1m a=="1M")a=3w("P||G(a,i){I "+a+"}");H d=[];L(H i=0,4g=b.K;i<4g;i++)9(!c&&a(b[i],i)||c&&!a(b[i],i))d.1a(b[i]);I d},1X:G(c,b){9(1m b=="1M")b=3w("P||G(a){I "+b+"}");H d=[];L(H i=0,4g=c.K;i<4g;i++){H a=b(c[i],i);9(a!==S&&a!=W){9(a.1c!=1B)a=[a];d=d.8M(a)}}I d}});H v=8K.8I.2p();E.V={4s:(v.1t(/.+(?:8F|8E|8C|8B)[\\/: ]([\\d.]+)/)||[])[1],1N:/6w/.14(v),34:/34/.14(v),1h:/1h/.14(v)&&!/34/.14(v),35:/35/.14(v)&&!/(8z|6w)/.14(v)};H y=E.V.1h?"4h":"5h";E.1k({5g:!E.V.1h||U.8y=="8x",4h:E.V.1h?"4h":"5h",5o:{"L":"8w","8v":"1o","4u":y,5h:y,4h:y,3O:"3O",1o:"1o",1Q:"1Q",3c:"3c",2Q:"2Q",8u:"8t",26:"26",8s:"8r"}});E.N({1D:"a.12",8q:"15.4e(a,\'12\')",8p:"15.2I(a,2,\'2q\')",8n:"15.2I(a,2,\'4d\')",8l:"15.4e(a,\'2q\')",8k:"15.4e(a,\'4d\')",8j:"15.5d(a.12.1w,a)",8i:"15.5d(a.1w)",6q:"15.11(a,\'8h\')?a.8f||a.8e.U:15.2h(a.3j)"},G(i,n){E.1b[i]=G(a){H b=E.1X(6,n);9(a&&1m a=="1M")b=E.3m(a,b);I 6.2o(E.4V(b))}});E.N({5R:"3g",8c:"6j",3d:"6g",8b:"50",89:"6H"},G(i,n){E.1b[i]=G(){H a=1q;I 6.N(G(){L(H j=0,2c=a.K;j<2c;j++)E(a[j])[n](6)})}});E.N({5W:G(a){E.1x(6,a,"");6.53(a)},88:G(c){E.1o.1f(6,c)},87:G(c){E.1o.28(6,c)},86:G(c){E.1o[E.1o.3K(6,c)?"28":"1f"](6,c)},28:G(a){9(!a||E.1E(a,[6]).r.K){E.30(6);6.12.3b(6)}},4n:G(){E("*",6).N(G(){E.30(6)});1W(6.1w)6.3b(6.1w)}},G(i,n){E.1b[i]=G(){I 6.N(n,1q)}});E.N(["85","5Z"],G(i,a){H n=a.2p();E.1b[n]=G(h){I 6[0]==18?E.V.1N&&3y["84"+a]||E.5g&&38.33(U.2V["5a"+a],U.1G["5a"+a])||U.1G["5a"+a]:6[0]==U?38.33(U.1G["6n"+a],U.1G["6m"+a]):h==W?(6.K?E.17(6[0],n):S):6.17(n,h.1c==3X?h:h+"2T")}});H C=E.V.1N&&3x(E.V.4s)<83?"(?:[\\\\w*57-]|\\\\\\\\.)":"(?:[\\\\w\\82-\\81*57-]|\\\\\\\\.)",6k=1u 47("^>\\\\s*("+C+"+)"),6i=1u 47("^("+C+"+)(#)("+C+"+)"),6h=1u 47("^([#.]?)("+C+"*)");E.1k({55:{"":"m[2]==\'*\'||15.11(a,m[2])","#":"a.4p(\'22\')==m[2]",":":{80:"im[3]-0",2I:"m[3]-0==i",6E:"m[3]-0==i",3v:"i==0",3u:"i==r.K-1",6f:"i%2==0",6e:"i%2","3v-46":"a.12.4l(\'*\')[0]==a","3u-46":"15.2I(a.12.5p,1,\'4d\')==a","7X-46":"!15.2I(a.12.5p,2,\'4d\')",1D:"a.1w",4n:"!a.1w",7W:"(a.6s||a.7V||15(a).2g()||\'\').1g(m[3])>=0",3R:\'"1P"!=a.O&&15.17(a,"19")!="2s"&&15.17(a,"4C")!="1P"\',1P:\'"1P"==a.O||15.17(a,"19")=="2s"||15.17(a,"4C")=="1P"\',7U:"!a.3c",3c:"a.3c",2Q:"a.2Q",26:"a.26||15.1x(a,\'26\')",2g:"\'2g\'==a.O",4k:"\'4k\'==a.O",5j:"\'5j\'==a.O",54:"\'54\'==a.O",52:"\'52\'==a.O",51:"\'51\'==a.O",6d:"\'6d\'==a.O",6c:"\'6c\'==a.O",2r:\'"2r"==a.O||15.11(a,"2r")\',4t:"/4t|24|6b|2r/i.14(a.11)",3K:"15.1Y(m[3],a).K",7S:"/h\\\\d/i.14(a.11)",7R:"15.2W(15.32,G(1b){I a==1b.T;}).K"}},6a:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,1u 47("^([:.#]*)("+C+"+)")],3m:G(a,c,b){H d,2b=[];1W(a&&a!=d){d=a;H f=E.1E(a,c,b);a=f.t.1p(/^\\s*,\\s*/,"");2b=b?c=f.r:E.1R(2b,f.r)}I 2b},1Y:G(t,o){9(1m t!="1M")I[t];9(o&&!o.1y)o=S;o=o||U;H d=[o],2f=[],3u;1W(t&&3u!=t){H r=[];3u=t;t=E.36(t);H l=P;H g=6k;H m=g.2S(t);9(m){H p=m[1].27();L(H i=0;d[i];i++)L(H c=d[i].1w;c;c=c.2q)9(c.1y==1&&(p=="*"||c.11.27()==p.27()))r.1a(c);d=r;t=t.1p(g,"");9(t.1g(" ")==0)6r;l=Q}J{g=/^([>+~])\\s*(\\w*)/i;9((m=g.2S(t))!=S){r=[];H p=m[2],1R={};m=m[1];L(H j=0,31=d.K;j<31;j++){H n=m=="~"||m=="+"?d[j].2q:d[j].1w;L(;n;n=n.2q)9(n.1y==1){H h=E.M(n);9(m=="~"&&1R[h])1T;9(!p||n.11.27()==p.27()){9(m=="~")1R[h]=Q;r.1a(n)}9(m=="+")1T}}d=r;t=E.36(t.1p(g,""));l=Q}}9(t&&!l){9(!t.1g(",")){9(o==d[0])d.44();2f=E.1R(2f,d);r=d=[o];t=" "+t.68(1,t.K)}J{H k=6i;H m=k.2S(t);9(m){m=[0,m[2],m[3],m[1]]}J{k=6h;m=k.2S(t)}m[2]=m[2].1p(/\\\\/g,"");H f=d[d.K-1];9(m[1]=="#"&&f&&f.3S&&!E.4a(f)){H q=f.3S(m[2]);9((E.V.1h||E.V.34)&&q&&1m q.22=="1M"&&q.22!=m[2])q=E(\'[@22="\'+m[2]+\'"]\',f)[0];d=r=q&&(!m[3]||E.11(q,m[3]))?[q]:[]}J{L(H i=0;d[i];i++){H a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];9(a=="*"&&d[i].11.2p()=="5i")a="3a";r=E.1R(r,d[i].4l(a))}9(m[1]==".")r=E.4X(r,m[2]);9(m[1]=="#"){H e=[];L(H i=0;r[i];i++)9(r[i].4p("22")==m[2]){e=[r[i]];1T}r=e}d=r}t=t.1p(k,"")}}9(t){H b=E.1E(t,r);d=r=b.r;t=E.36(b.t)}}9(t)d=[];9(d&&o==d[0])d.44();2f=E.1R(2f,d);I 2f},4X:G(r,m,a){m=" "+m+" ";H c=[];L(H i=0;r[i];i++){H b=(" "+r[i].1o+" ").1g(m)>=0;9(!a&&b||a&&!b)c.1a(r[i])}I c},1E:G(t,r,h){H d;1W(t&&t!=d){d=t;H p=E.6a,m;L(H i=0;p[i];i++){m=p[i].2S(t);9(m){t=t.7O(m[0].K);m[2]=m[2].1p(/\\\\/g,"");1T}}9(!m)1T;9(m[1]==":"&&m[2]=="5V")r=E.1E(m[3],r,Q).r;J 9(m[1]==".")r=E.4X(r,m[2],h);J 9(m[1]=="["){H g=[],O=m[3];L(H i=0,31=r.K;i<31;i++){H a=r[i],z=a[E.5o[m[2]]||m[2]];9(z==S||/6C|3k|26/.14(m[2]))z=E.1x(a,m[2])||\'\';9((O==""&&!!z||O=="="&&z==m[5]||O=="!="&&z!=m[5]||O=="^="&&z&&!z.1g(m[5])||O=="$="&&z.68(z.K-m[5].K)==m[5]||(O=="*="||O=="~=")&&z.1g(m[5])>=0)^h)g.1a(a)}r=g}J 9(m[1]==":"&&m[2]=="2I-46"){H e={},g=[],14=/(\\d*)n\\+?(\\d*)/.2S(m[3]=="6f"&&"2n"||m[3]=="6e"&&"2n+1"||!/\\D/.14(m[3])&&"n+"+m[3]||m[3]),3v=(14[1]||1)-0,d=14[2]-0;L(H i=0,31=r.K;i<31;i++){H j=r[i],12=j.12,22=E.M(12);9(!e[22]){H c=1;L(H n=12.1w;n;n=n.2q)9(n.1y==1)n.4U=c++;e[22]=Q}H b=P;9(3v==1){9(d==0||j.4U==d)b=Q}J 9((j.4U+d)%3v==0)b=Q;9(b^h)g.1a(j)}r=g}J{H f=E.55[m[1]];9(1m f!="1M")f=E.55[m[1]][m[2]];f=3w("P||G(a,i){I "+f+"}");r=E.2W(r,f,h)}}I{r:r,t:t}},4e:G(b,c){H d=[];H a=b[c];1W(a&&a!=U){9(a.1y==1)d.1a(a);a=a[c]}I d},2I:G(a,e,c,b){e=e||1;H d=0;L(;a;a=a[c])9(a.1y==1&&++d==e)1T;I a},5d:G(n,a){H r=[];L(;n;n=n.2q){9(n.1y==1&&(!a||n!=a))r.1a(n)}I r}});E.1j={1f:G(g,e,c,h){9(E.V.1h&&g.4j!=W)g=18;9(!c.2u)c.2u=6.2u++;9(h!=W){H d=c;c=G(){I d.16(6,1q)};c.M=h;c.2u=d.2u}H i=e.2l(".");e=i[0];c.O=i[1];H b=E.M(g,"2P")||E.M(g,"2P",{});H f=E.M(g,"2t",G(){H a;9(1m E=="W"||E.1j.4T)I a;a=E.1j.2t.16(g,1q);I a});H j=b[e];9(!j){j=b[e]={};9(g.4S)g.4S(e,f,P);J g.7N("43"+e,f)}j[c.2u]=c;6.1Z[e]=Q},2u:1,1Z:{},28:G(d,c,b){H e=E.M(d,"2P"),2L,4I;9(1m c=="1M"){H a=c.2l(".");c=a[0]}9(e){9(c&&c.O){b=c.4Q;c=c.O}9(!c){L(c 1i e)6.28(d,c)}J 9(e[c]){9(b)2E e[c][b.2u];J L(b 1i e[c])9(!a[1]||e[c][b].O==a[1])2E e[c][b];L(2L 1i e[c])1T;9(!2L){9(d.4P)d.4P(c,E.M(d,"2t"),P);J d.7M("43"+c,E.M(d,"2t"));2L=S;2E e[c]}}L(2L 1i e)1T;9(!2L){E.30(d,"2P");E.30(d,"2t")}}},1F:G(d,b,e,c,f){b=E.2h(b||[]);9(!e){9(6.1Z[d])E("*").1f([18,U]).1F(d,b)}J{H a,2L,1b=E.1n(e[d]||S),4N=!b[0]||!b[0].2M;9(4N)b.4w(6.4M({O:d,2m:e}));b[0].O=d;9(E.1n(E.M(e,"2t")))a=E.M(e,"2t").16(e,b);9(!1b&&e["43"+d]&&e["43"+d].16(e,b)===P)a=P;9(4N)b.44();9(f&&f.16(e,b)===P)a=P;9(1b&&c!==P&&a!==P&&!(E.11(e,\'a\')&&d=="4L")){6.4T=Q;e[d]()}6.4T=P}I a},2t:G(d){H a;d=E.1j.4M(d||18.1j||{});H b=d.O.2l(".");d.O=b[0];H c=E.M(6,"2P")&&E.M(6,"2P")[d.O],3q=1B.3A.2J.2O(1q,1);3q.4w(d);L(H j 1i c){3q[0].4Q=c[j];3q[0].M=c[j].M;9(!b[1]||c[j].O==b[1]){H e=c[j].16(6,3q);9(a!==P)a=e;9(e===P){d.2M();d.3p()}}}9(E.V.1h)d.2m=d.2M=d.3p=d.4Q=d.M=S;I a},4M:G(c){H a=c;c=E.1k({},a);c.2M=G(){9(a.2M)a.2M();a.7L=P};c.3p=G(){9(a.3p)a.3p();a.7K=Q};9(!c.2m&&c.65)c.2m=c.65;9(E.V.1N&&c.2m.1y==3)c.2m=a.2m.12;9(!c.4K&&c.4J)c.4K=c.4J==c.2m?c.7H:c.4J;9(c.64==S&&c.63!=S){H e=U.2V,b=U.1G;c.64=c.63+(e&&e.2R||b.2R||0);c.7E=c.7D+(e&&e.2B||b.2B||0)}9(!c.3Y&&(c.61||c.60))c.3Y=c.61||c.60;9(!c.5F&&c.5D)c.5F=c.5D;9(!c.3Y&&c.2r)c.3Y=(c.2r&1?1:(c.2r&2?3:(c.2r&4?2:0)));I c}};E.1b.1k({3W:G(c,a,b){I c=="5Y"?6.2G(c,a,b):6.N(G(){E.1j.1f(6,c,b||a,b&&a)})},2G:G(d,b,c){I 6.N(G(){E.1j.1f(6,d,G(a){E(6).5X(a);I(c||b).16(6,1q)},c&&b)})},5X:G(a,b){I 6.N(G(){E.1j.28(6,a,b)})},1F:G(c,a,b){I 6.N(G(){E.1j.1F(c,a,6,Q,b)})},7x:G(c,a,b){9(6[0])I E.1j.1F(c,a,6[0],P,b)},25:G(){H a=1q;I 6.4L(G(e){6.4H=0==6.4H?1:0;e.2M();I a[6.4H].16(6,[e])||P})},7v:G(f,g){G 4G(e){H p=e.4K;1W(p&&p!=6)2a{p=p.12}29(e){p=6};9(p==6)I P;I(e.O=="4x"?f:g).16(6,[e])}I 6.4x(4G).5U(4G)},2d:G(f){5T();9(E.3T)f.16(U,[E]);J E.3l.1a(G(){I f.16(6,[E])});I 6}});E.1k({3T:P,3l:[],2d:G(){9(!E.3T){E.3T=Q;9(E.3l){E.N(E.3l,G(){6.16(U)});E.3l=S}9(E.V.35||E.V.34)U.4P("5S",E.2d,P);9(!18.7t.K)E(18).39(G(){E("#4E").28()})}}});E.N(("7s,7r,39,7q,6n,5Y,4L,7p,"+"7n,7m,7l,4x,5U,7k,24,"+"51,7j,7i,7h,3U").2l(","),G(i,o){E.1b[o]=G(f){I f?6.3W(o,f):6.1F(o)}});H x=P;G 5T(){9(x)I;x=Q;9(E.V.35||E.V.34)U.4S("5S",E.2d,P);J 9(E.V.1h){U.7f("<7d"+"7y 22=4E 7z=Q "+"3k=//:><\\/1J>");H a=U.3S("4E");9(a)a.62=G(){9(6.2C!="1l")I;E.2d()};a=S}J 9(E.V.1N)E.4B=4j(G(){9(U.2C=="5Q"||U.2C=="1l"){4A(E.4B);E.4B=S;E.2d()}},10);E.1j.1f(18,"39",E.2d)}E.1b.1k({39:G(g,d,c){9(E.1n(g))I 6.3W("39",g);H e=g.1g(" ");9(e>=0){H i=g.2J(e,g.K);g=g.2J(0,e)}c=c||G(){};H f="4z";9(d)9(E.1n(d)){c=d;d=S}J{d=E.3a(d);f="5P"}H h=6;E.3G({1d:g,O:f,M:d,1l:G(a,b){9(b=="1C"||b=="5O")h.4o(i?E("<1s/>").3g(a.40.1p(/<1J(.|\\s)*?\\/1J>/g,"")).1Y(i):a.40);56(G(){h.N(c,[a.40,b,a])},13)}});I 6},7a:G(){I E.3a(6.5M())},5M:G(){I 6.1X(G(){I E.11(6,"2Y")?E.2h(6.79):6}).1E(G(){I 6.2H&&!6.3c&&(6.2Q||/24|6b/i.14(6.11)||/2g|1P|52/i.14(6.O))}).1X(G(i,c){H b=E(6).3i();I b==S?S:b.1c==1B?E.1X(b,G(a,i){I{2H:c.2H,1Q:a}}):{2H:c.2H,1Q:b}}).21()}});E.N("5L,5K,6t,5J,5I,5H".2l(","),G(i,o){E.1b[o]=G(f){I 6.3W(o,f)}});H B=(1u 3D).3B();E.1k({21:G(d,b,a,c){9(E.1n(b)){a=b;b=S}I E.3G({O:"4z",1d:d,M:b,1C:a,1V:c})},78:G(b,a){I E.21(b,S,a,"1J")},77:G(c,b,a){I E.21(c,b,a,"45")},76:G(d,b,a,c){9(E.1n(b)){a=b;b={}}I E.3G({O:"5P",1d:d,M:b,1C:a,1V:c})},75:G(a){E.1k(E.59,a)},59:{1Z:Q,O:"4z",2z:0,5G:"74/x-73-2Y-72",6o:Q,3e:Q,M:S},49:{},3G:G(s){H f,2y=/=(\\?|%3F)/g,1v,M;s=E.1k(Q,s,E.1k(Q,{},E.59,s));9(s.M&&s.6o&&1m s.M!="1M")s.M=E.3a(s.M);9(s.1V=="4b"){9(s.O.2p()=="21"){9(!s.1d.1t(2y))s.1d+=(s.1d.1t(/\\?/)?"&":"?")+(s.4b||"5E")+"=?"}J 9(!s.M||!s.M.1t(2y))s.M=(s.M?s.M+"&":"")+(s.4b||"5E")+"=?";s.1V="45"}9(s.1V=="45"&&(s.M&&s.M.1t(2y)||s.1d.1t(2y))){f="4b"+B++;9(s.M)s.M=s.M.1p(2y,"="+f);s.1d=s.1d.1p(2y,"="+f);s.1V="1J";18[f]=G(a){M=a;1C();1l();18[f]=W;2a{2E 18[f]}29(e){}}}9(s.1V=="1J"&&s.1L==S)s.1L=P;9(s.1L===P&&s.O.2p()=="21")s.1d+=(s.1d.1t(/\\?/)?"&":"?")+"57="+(1u 3D()).3B();9(s.M&&s.O.2p()=="21"){s.1d+=(s.1d.1t(/\\?/)?"&":"?")+s.M;s.M=S}9(s.1Z&&!E.5b++)E.1j.1F("5L");9(!s.1d.1g("8g")&&s.1V=="1J"){H h=U.4l("9U")[0];H g=U.5B("1J");g.3k=s.1d;9(!f&&(s.1C||s.1l)){H j=P;g.9R=g.62=G(){9(!j&&(!6.2C||6.2C=="5Q"||6.2C=="1l")){j=Q;1C();1l();h.3b(g)}}}h.58(g);I}H k=P;H i=18.6X?1u 6X("9P.9O"):1u 6W();i.9M(s.O,s.1d,s.3e);9(s.M)i.5C("9J-9I",s.5G);9(s.5y)i.5C("9H-5x-9F",E.49[s.1d]||"9D, 9C 9B 9A 5v:5v:5v 9z");i.5C("X-9x-9v","6W");9(s.6U)s.6U(i);9(s.1Z)E.1j.1F("5H",[i,s]);H c=G(a){9(!k&&i&&(i.2C==4||a=="2z")){k=Q;9(d){4A(d);d=S}1v=a=="2z"&&"2z"||!E.6S(i)&&"3U"||s.5y&&E.6R(i,s.1d)&&"5O"||"1C";9(1v=="1C"){2a{M=E.6Q(i,s.1V)}29(e){1v="5k"}}9(1v=="1C"){H b;2a{b=i.5s("6P-5x")}29(e){}9(s.5y&&b)E.49[s.1d]=b;9(!f)1C()}J E.5r(s,i,1v);1l();9(s.3e)i=S}};9(s.3e){H d=4j(c,13);9(s.2z>0)56(G(){9(i){i.9q();9(!k)c("2z")}},s.2z)}2a{i.9o(s.M)}29(e){E.5r(s,i,S,e)}9(!s.3e)c();I i;G 1C(){9(s.1C)s.1C(M,1v);9(s.1Z)E.1j.1F("5I",[i,s])}G 1l(){9(s.1l)s.1l(i,1v);9(s.1Z)E.1j.1F("6t",[i,s]);9(s.1Z&&!--E.5b)E.1j.1F("5K")}},5r:G(s,a,b,e){9(s.3U)s.3U(a,b,e);9(s.1Z)E.1j.1F("5J",[a,s,e])},5b:0,6S:G(r){2a{I!r.1v&&9n.9l=="54:"||(r.1v>=6N&&r.1v<9j)||r.1v==6M||E.V.1N&&r.1v==W}29(e){}I P},6R:G(a,c){2a{H b=a.5s("6P-5x");I a.1v==6M||b==E.49[c]||E.V.1N&&a.1v==W}29(e){}I P},6Q:G(r,b){H c=r.5s("9i-O");H d=b=="6K"||!b&&c&&c.1g("6K")>=0;H a=d?r.9g:r.40;9(d&&a.2V.37=="5k")6G"5k";9(b=="1J")E.5f(a);9(b=="45")a=3w("("+a+")");I a},3a:G(a){H s=[];9(a.1c==1B||a.4c)E.N(a,G(){s.1a(3f(6.2H)+"="+3f(6.1Q))});J L(H j 1i a)9(a[j]&&a[j].1c==1B)E.N(a[j],G(){s.1a(3f(j)+"="+3f(6))});J s.1a(3f(j)+"="+3f(a[j]));I s.66("&").1p(/%20/g,"+")}});E.1b.1k({1A:G(b,a){I b?6.1U({1H:"1A",2N:"1A",1r:"1A"},b,a):6.1E(":1P").N(G(){6.R.19=6.3h?6.3h:"";9(E.17(6,"19")=="2s")6.R.19="2Z"}).2D()},1z:G(b,a){I b?6.1U({1H:"1z",2N:"1z",1r:"1z"},b,a):6.1E(":3R").N(G(){6.3h=6.3h||E.17(6,"19");9(6.3h=="2s")6.3h="2Z";6.R.19="2s"}).2D()},6J:E.1b.25,25:G(a,b){I E.1n(a)&&E.1n(b)?6.6J(a,b):a?6.1U({1H:"25",2N:"25",1r:"25"},a,b):6.N(G(){E(6)[E(6).3t(":1P")?"1A":"1z"]()})},9c:G(b,a){I 6.1U({1H:"1A"},b,a)},9b:G(b,a){I 6.1U({1H:"1z"},b,a)},99:G(b,a){I 6.1U({1H:"25"},b,a)},98:G(b,a){I 6.1U({1r:"1A"},b,a)},96:G(b,a){I 6.1U({1r:"1z"},b,a)},95:G(c,a,b){I 6.1U({1r:a},c,b)},1U:G(k,i,h,g){H j=E.6D(i,h,g);I 6[j.3L===P?"N":"3L"](G(){j=E.1k({},j);H f=E(6).3t(":1P"),3y=6;L(H p 1i k){9(k[p]=="1z"&&f||k[p]=="1A"&&!f)I E.1n(j.1l)&&j.1l.16(6);9(p=="1H"||p=="2N"){j.19=E.17(6,"19");j.2U=6.R.2U}}9(j.2U!=S)6.R.2U="1P";j.3M=E.1k({},k);E.N(k,G(c,a){H e=1u E.2j(3y,j,c);9(/25|1A|1z/.14(a))e[a=="25"?f?"1A":"1z":a](k);J{H b=a.3s().1t(/^([+-]=)?([\\d+-.]+)(.*)$/),1O=e.2b(Q)||0;9(b){H d=3I(b[2]),2i=b[3]||"2T";9(2i!="2T"){3y.R[c]=(d||1)+2i;1O=((d||1)/e.2b(Q))*1O;3y.R[c]=1O+2i}9(b[1])d=((b[1]=="-="?-1:1)*d)+1O;e.3N(1O,d,2i)}J e.3N(1O,a,"")}});I Q})},3L:G(a,b){9(E.1n(a)){b=a;a="2j"}9(!a||(1m a=="1M"&&!b))I A(6[0],a);I 6.N(G(){9(b.1c==1B)A(6,a,b);J{A(6,a).1a(b);9(A(6,a).K==1)b.16(6)}})},9f:G(){H a=E.32;I 6.N(G(){L(H i=0;i-8O?r:3I(E.17(6.T,6.1e))||0},3N:G(c,b,e){6.5u=(1u 3D()).3B();6.1O=c;6.2D=b;6.2i=e||6.2i||"2T";6.2v=6.1O;6.4q=6.4i=0;6.4r();H f=6;G t(){I f.2F()}t.T=6.T;E.32.1a(t);9(E.32.K==1){H d=4j(G(){H a=E.32;L(H i=0;i6.Y.2e+6.5u){6.2v=6.2D;6.4q=6.4i=1;6.4r();6.Y.3M[6.1e]=Q;H a=Q;L(H i 1i 6.Y.3M)9(6.Y.3M[i]!==Q)a=P;9(a){9(6.Y.19!=S){6.T.R.2U=6.Y.2U;6.T.R.19=6.Y.19;9(E.17(6.T,"19")=="2s")6.T.R.19="2Z"}9(6.Y.1z)6.T.R.19="2s";9(6.Y.1z||6.Y.1A)L(H p 1i 6.Y.3M)E.1x(6.T.R,p,6.Y.3P[p])}9(a&&E.1n(6.Y.1l))6.Y.1l.16(6.T);I P}J{H n=t-6.5u;6.4i=n/6.Y.2e;6.4q=E.3J[6.Y.3J||(E.3J.5q?"5q":"6B")](6.4i,n,0,1,6.Y.2e);6.2v=6.1O+((6.2D-6.1O)*6.4q);6.4r()}I Q}};E.2j.2F={2R:G(a){a.T.2R=a.2v},2B:G(a){a.T.2B=a.2v},1r:G(a){E.1x(a.T.R,"1r",a.2v)},6z:G(a){a.T.R[a.1e]=a.2v+a.2i}};E.1b.6m=G(){H c=0,3E=0,T=6[0],5t;9(T)8L(E.V){H b=E.17(T,"2X")=="4F",1D=T.12,23=T.23,2K=T.3H,4f=1N&&3x(4s)<8J;9(T.6V){5w=T.6V();1f(5w.1S+38.33(2K.2V.2R,2K.1G.2R),5w.3E+38.33(2K.2V.2B,2K.1G.2B));9(1h){H d=E("4o").17("8H");d=(d=="8G"||E.5g&&3x(4s)>=7)&&2||d;1f(-d,-d)}}J{1f(T.5l,T.5z);1W(23){1f(23.5l,23.5z);9(35&&/^t[d|h]$/i.14(1D.37)||!4f)d(23);9(4f&&!b&&E.17(23,"2X")=="4F")b=Q;23=23.23}1W(1D.37&&!/^1G|4o$/i.14(1D.37)){9(!/^8D|1I-9S.*$/i.14(E.17(1D,"19")))1f(-1D.2R,-1D.2B);9(35&&E.17(1D,"2U")!="3R")d(1D);1D=1D.12}9(4f&&b)1f(-2K.1G.5l,-2K.1G.5z)}5t={3E:3E,1S:c}}I 5t;G d(a){1f(E.17(a,"9T"),E.17(a,"8A"))}G 1f(l,t){c+=3x(l)||0;3E+=3x(t)||0}}})();',62,616,'||||||this|||if|||||||||||||||||||||||||||||||||function|var|return|else|length|for|data|each|type|false|true|style|null|elem|document|browser|undefined||options|||nodeName|parentNode||test|jQuery|apply|css|window|display|push|fn|constructor|url|prop|add|indexOf|msie|in|event|extend|complete|typeof|isFunction|className|replace|arguments|opacity|div|match|new|status|firstChild|attr|nodeType|hide|show|Array|success|parent|filter|trigger|body|height|table|script|tbody|cache|string|safari|start|hidden|value|merge|left|break|animate|dataType|while|map|find|global||get|id|offsetParent|select|toggle|selected|toUpperCase|remove|catch|try|cur|al|ready|duration|done|text|makeArray|unit|fx|swap|split|target||pushStack|toLowerCase|nextSibling|button|none|handle|guid|now|stack|tb|jsre|timeout|inArray|scrollTop|readyState|end|delete|step|one|name|nth|slice|doc|ret|preventDefault|width|call|events|checked|scrollLeft|exec|px|overflow|documentElement|grep|position|form|block|removeData|rl|timers|max|opera|mozilla|trim|tagName|Math|load|param|removeChild|disabled|insertBefore|async|encodeURIComponent|append|oldblock|val|childNodes|src|readyList|multiFilter|color|defaultView|stopPropagation|args|old|toString|is|last|first|eval|parseInt|self|domManip|prototype|getTime|curCSS|Date|top||ajax|ownerDocument|parseFloat|easing|has|queue|curAnim|custom|innerHTML|orig|currentStyle|visible|getElementById|isReady|error|static|bind|String|which|getComputedStyle|responseText|oWidth|oHeight|on|shift|json|child|RegExp|ol|lastModified|isXMLDoc|jsonp|jquery|previousSibling|dir|safari2|el|styleFloat|state|setInterval|radio|getElementsByTagName|tr|empty|html|getAttribute|pos|update|version|input|float|runtimeStyle|unshift|mouseover|getPropertyValue|GET|clearInterval|safariTimer|visibility|clean|__ie_init|absolute|handleHover|lastToggle|index|fromElement|relatedTarget|click|fix|evt|andSelf|removeEventListener|handler|cloneNode|addEventListener|triggered|nodeIndex|unique|Number|classFilter|prevObject|selectedIndex|after|submit|password|removeAttribute|file|expr|setTimeout|_|appendChild|ajaxSettings|client|active|win|sibling|deep|globalEval|boxModel|cssFloat|object|checkbox|parsererror|offsetLeft|wrapAll|dequeue|props|lastChild|swing|handleError|getResponseHeader|results|startTime|00|box|Modified|ifModified|offsetTop|evalScript|createElement|setRequestHeader|ctrlKey|callback|metaKey|contentType|ajaxSend|ajaxSuccess|ajaxError|ajaxStop|ajaxStart|serializeArray|init|notmodified|POST|loaded|appendTo|DOMContentLoaded|bindReady|mouseout|not|removeAttr|unbind|unload|Width|keyCode|charCode|onreadystatechange|clientX|pageX|srcElement|join|outerHTML|substr|zoom|parse|textarea|reset|image|odd|even|before|quickClass|quickID|prepend|quickChild|execScript|offset|scroll|processData|uuid|contents|continue|textContent|ajaxComplete|clone|setArray|webkit|nodeValue|fl|_default|100|linear|href|speed|eq|createTextNode|throw|replaceWith|splice|_toggle|xml|colgroup|304|200|alpha|Last|httpData|httpNotModified|httpSuccess|fieldset|beforeSend|getBoundingClientRect|XMLHttpRequest|ActiveXObject|col|br|abbr|pixelLeft|urlencoded|www|application|ajaxSetup|post|getJSON|getScript|elements|serialize|clientWidth|hasClass|scr|clientHeight|write|relative|keyup|keypress|keydown|change|mousemove|mouseup|mousedown|right|dblclick|resize|focus|blur|frames|instanceof|hover|offsetWidth|triggerHandler|ipt|defer|offsetHeight|border|padding|clientY|pageY|Left|Right|toElement|Bottom|Top|cancelBubble|returnValue|detachEvent|attachEvent|substring|line|weight|animated|header|font|enabled|innerText|contains|only|size|gt|lt|uFFFF|u0128|417|inner|Height|toggleClass|removeClass|addClass|replaceAll|noConflict|insertAfter|prependTo|wrap|contentWindow|contentDocument|http|iframe|children|siblings|prevAll|nextAll|wrapInner|prev|Boolean|next|parents|maxLength|maxlength|readOnly|readonly|class|htmlFor|CSS1Compat|compatMode|compatible|borderTopWidth|ie|ra|inline|it|rv|medium|borderWidth|userAgent|522|navigator|with|concat|1px|10000|array|ig|PI|NaN|400|reverse|fast|600|slow|Function|Object|setAttribute|changed|be|can|property|fadeTo|fadeOut|getAttributeNode|fadeIn|slideToggle|method|slideUp|slideDown|action|cssText|stop|responseXML|option|content|300|th|protocol|td|location|send|cap|abort|colg|cos|tfoot|thead|With|leg|Requested|opt|GMT|1970|Jan|01|Thu|area|Since|hr|If|Type|Content|meta|specified|open|link|XMLHTTP|Microsoft|img|onload|row|borderLeftWidth|head|attributes'.split('|'),0,{}); /* Copyright (c) 2004-2007, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ /* This is a compiled version of Dojo, built for deployment and not for development. To get an editable version, please visit: http://dojotoolkit.org for documentation and information on getting the source. */ var decompressedDojo = function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(V z=="1k"){(B(){if(V D["1o"]=="1k"){D.1o={}}if((!D["1z"])||(!1z["ca"])){D.1z={}}A cn=["rA","rz","1K","ry","rx","9f","rw","rv","ru","rt","rs","rr","rq","ro","rn","rm"];A i=0,24;1s(24=cn[i++]){if(!1z[24]){1z[24]=B(){}}}if(V D["z"]=="1k"){D.z={}}z.1W=D;A d3={im:U,rl:U,rk:"",rj:"",ri:"",rh:K,rg:U};R(A 8z in d3){if(V 1o[8z]=="1k"){1o[8z]=d3[8z]}}A jK=["rf","rd","rc","rb"];A t;1s(t=jK.3a()){z["is"+t]=U}})();z.8h=1o.8h;z.cY={jJ:0,jI:9,jH:0,jG:"",jF:2V("$ra: r9 $".1f(/[0-9]+/)[0]),2i:B(){4G(z.cY){C jJ+"."+jI+"."+jH+jG+" ("+jF+")"}}};z.d1=B(jE,jD,1V){A 2h=1V||z.1W;R(A i=0,p;2h&&(p=jE[i]);i++){2h=(p in 2h?2h[p]:(jD?2h[p]={}:1k))}C 2h};z.88=B(jC,jA,jB){A d2=jC.1A("."),p=d2.8q(),M=z.d1(d2,K,jB);C(M&&p?(M[p]=jA):1k)};z.6q=B(jz,jy,jx){C z.d1(jz.1A("."),jy,jx)};z.r8=B(jw,M){C!!z.6q(jw,U,M)};z["3u"]=B(d0){C z.1W.3u?z.1W.3u(d0):3u(d0)};z.ia=B(jv,cZ,cX){A 8y="r7: "+jv;if(cZ){8y+=" "+cZ}if(cX){8y+=" -- r6 be r5 in cY: "+cX}1z.1K(8y)};z.r4=B(ju,cW){A cV="r3: "+ju+" -- r2 r1 4F r0 qZ qY.";if(cW){cV+=" "+cW}1z.1K(cV)};(B(){A cR={53:{},6p:0,1h:{},8k:{z:{1p:"z",1Z:"."},cU:{1p:"cU",1Z:"../qX/cU"},cT:{1p:"cT",1Z:"cT"}},cN:B(cS){A mp=D.8k;C jp(mp[cS]&&mp[cS].1Z)},jk:B(8x){A mp=D.8k;if(D.cN(8x)){C mp[8x].1Z}C 8x},8v:[],6t:U,56:[],8t:[],8u:U};R(A cQ in cR){z[cQ]=cR[cQ]}})();z.jg=B(8w,cP,cb){A 1g=(((8w.2s(0)=="/"||8w.1f(/^\\w+:/)))?"":D.51)+8w;if(1o.jt&&z.c8){1g+="?"+67(1o.jt).2f(/\\W+/g,"")}1u{C!cP?D.cO(1g,cb):D.jq(1g,cP,cb)}1y(e){1z.1K(e);C U}};z.cO=B(1g,cb){if(D.8v[1g]){C K}A 6u=D.iR(1g,K);if(!6u){C U}D.8v[1g]=K;D.8v.Y(1g);if(cb){6u="("+6u+")"}A jr=z["3u"](6u+"\\r\\n//@ qW="+1g);if(cb){cb(jr)}C K};z.jq=B(1g,jo,cb){A ok=U;1u{ok=D.cO(1g,cb)}1y(e){1z.1K("qV je ",1g," 4G 9f: ",e)}C jp(ok&&D.53[jo])};z.6m=B(){D.8u=K;D.6t=K;A 57=D.56;D.56=[];R(A x=0;x<57.G;x++){57[x]()}D.8u=U;if(z.6t&&z.6p==0&&D.56.G>0){z.8s()}};z.ck=B(){A 57=D.8t;1s(57.G){(57.8q())()}};z.qU=B(M,jn){A d=z;if(P.G==1){d.56.Y(M)}I{if(P.G>1){d.56.Y(B(){M[jn]()})}}if(d.6t&&d.6p==0&&!d.8u){d.8s()}};z.dW=B(M,jm){A d=z;if(P.G==1){d.8t.Y(M)}I{if(P.G>1){d.8t.Y(B(){M[jm]()})}}};z.iM=B(){if(D.6t){C}if(D.6p>0){1z.1K("qT qS in qR!");C}z.8s()};z.8s=B(){if(V 5c=="8b"||(1o["qQ"]&&z.2M)){5c("z.6m();",0)}I{z.6m()}};z.cF=B(jl){A 4v=jl.1A(".");R(A i=4v.G;i>0;i--){A 8r=4v.2w(0,i).22(".");if((i==1)&&!D.cN(8r)){4v[0]="../"+4v[0]}I{A cM=D.jk(8r);if(cM!=8r){4v.3S(0,i,cM);3f}}}C 4v};z.jj=U;z.8m=B(2T,qP,55){55=D.jj||55;A 54=D.53[2T];if(54){C 54}A cL=2T.1A(".");A 3L=D.cF(2T);A jh=((3L[0].2s(0)!="/")&&!3L[0].1f(/^\\w+:/));A ji=3L[3L.G-1];A 3m;if(ji=="*"){2T=cL.2w(0,-1).22(".");3L.8q();3m=3L.22("/")+"/"+(1o["qO"]||"qN")+".js";if(jh&&3m.2s(0)=="/"){3m=3m.2w(1)}}I{3m=3L.22("/")+".js";2T=cL.22(".")}A jf=(!55)?2T:L;A ok=D.jg(3m,jf);if((!ok)&&(!55)){2m S 1O("qM 3O 4E \'"+2T+"\'; 72 qL \'"+3m+"\'")}if((!55)&&(!D["qK"])){54=D.53[2T];if(!54){2m S 1O("qJ \'"+2T+"\' is 3O qI a8 je \'"+3m+"\'")}}C 54};z.8c=z.8m;z.1Q=B(cK){A cJ=cK+"";A 8p=cJ;A 6s=cK.1A(/\\./);if(6s[6s.G-1]=="*"){6s.8q();8p=6s.22(".")}A 8o=z.6q(8p,K);D.53[cJ]=8o;D.53[8p]=8o;C 8o};z.qH=B(8n){A jd=8n["qG"]||[];A cI=jd.3U(8n[z.j4]||8n["aY"]||[]);R(A x=0;x0&&!(j==1&&1X[0]=="")&&1X[j]==".."&&1X[j-1]!=".."){if(j==(1X.G-1)){1X.3S(j,1);1X[j-1]=""}I{1X.3S(j-1,2);j-=2}}}}1t.28=1X.22("/")}}}}1g="";if(1t.4t){1g+=1t.4t+":"}if(1t.3l){1g+="//"+1t.3l}1g+=1t.28;if(1t.1r){1g+="?"+1t.1r}if(1t.52){1g+="#"+1t.52}}D.1g=1g.2i();A r=D.1g.1f(j7);D.4t=r[2]||(r[1]?"":n);D.3l=r[4]||(r[3]?"":n);D.28=r[5];D.1r=r[7]||(r[6]?"":n);D.52=r[9]||(r[8]?"":n);if(D.3l!=n){r=D.3l.1f(j6);D.8X=r[3]||n;D.8W=r[4]||n;D.qw=r[5];D.qv=r[7]||n}};z.4r.1C.2i=B(){C D.1g}})();z.qu=B(j5,2E){A 2B=z.cF(j5).22("/");if(!2B){C L}if(2B.31("/")!=2B.G-1){2B+="/"}A cE=2B.T(":");if(2B.2s(0)!="/"&&(cE==-1||cE>2B.T("/"))){2B=z.51+2B}C S z.4r(2B,2E)};if(V 26!="1k"){z.c8=K;z.j4="qt";(B(){A d=z;if(1q&&1q.4I){A 8j=1q.4I("ak");A j3=/z(\\.qs)?\\.js([\\?\\.]|$)/i;R(A i=0;i<8j.G;i++){A 4X=8j[i].5t("4X");if(!4X){6c}A m=4X.1f(j3);if(m){if(!1o["51"]){1o["51"]=4X.21(0,m.hK)}A cD=8j[i].5t("1o");if(cD){A cC=3u("({ "+cD+" })");R(A x in cC){1o[x]=cC[x]}}3f}}}d.51=1o["51"];A n=cq;A 8i=n.iL;A 4Z=n.qr;A 6r=2k(4Z);d.2M=(8i.T("qq")>=0)?6r:0;d.6B=(4Z.T("qo")>=0)||(4Z.T("j2")>=0)?6r:0;d.3o=(4Z.T("j2")>=0)?6r:0;A j1=8i.T("qn");d.gu=d.7B=((j1>=0)&&(!d.6B))?6r:0;d.j0=0;d.1l=0;d.iV=0;1u{if(d.7B){d.j0=2k(8i.1A("qm/")[1].1A(" ")[0])}if((1q.gx)&&(!d.2M)){d.1l=2k(4Z.1A("qk ")[1].1A(";")[0])}}1y(e){}if(z.1l&&(26.8f.cu==="9q:")){1o.iT=K}d.iX=B(){A 2A;A qj;A cB=d.6q("cz.cy");if(cB){C cB}if(V iZ!="1k"){2A=S iZ()}I{if(d.1l){1u{2A=S 9j("qi.qh")}1y(e){}}I{if(cq.qg["8Z/x-iY"]){2A=1q.a9("8b");2A.cA("Z","8Z/x-iY");2A.cA("3n",0);2A.cA("58",0);2A.1c.gq="7C";1q.5K.4c(2A)}}}if(!2A){C L}z.88("cz.cy.qf",2A);C z.6q("cz.cy")};A iW=d.iX();if(iW){d.iV=K}A cm=1q["aX"];d.qe=(cm=="aW")||(cm=="gr")||(d.1l<6);d.8h=1o.8h||(d.1l?n.qd:n.qc).1M();d.qb=1z.1K;d.cx=["iU.8g","em.8g","iU.8g.4.0"];d.9b=B(){A 4s=L;A cv=L;if(!z.1l||!1o.iT){1u{4s=S qa()}1y(e){}}if(!4s){R(A i=0;i<3;++i){A cw=z.cx[i];1u{4s=S 9j(cw)}1y(e){cv=e}if(4s){z.cx=[cw];3f}}}if(!4s){2m S 1O("8g 3O q9: "+cv)}C 4s};d.8Y=B(iS){A 4Y=iS.3N||0;C((4Y>=q8)&&(4Y0);d.iR=B(1g,iP){A 3K=D.9b();if(!iQ&&z.4r){1g=(S z.4r(26.8f,1g)).2i()}3K.dL("dD",1g,U);1u{3K.dI(L);if(!d.8Y(3K)){A 1G=1O("q2 4F 4E "+1g+" 3N:"+3K.3N);1G.3N=3K.3N;1G.2G=3K.2G;2m 1G}}1y(e){if(iP){C L}2m e}C 3K.2G}})();z.iO=U;z.6o=B(e){z.iO=K;A cr=(e&&e.Z)?e.Z.1M():"4E";if(P.2O.iN||(cr!="q1"&&cr!="4E")){C}P.2O.iN=K;if(V z["8e"]!="1k"){dX(z.8e);63 z.8e}if(z.6p==0){z.iM()}};if(1q.66){if(z.2M||(z.7B&&(1o["q0"]===K))){1q.66("pZ",z.6o,L)}26.66("4E",z.6o,L)}if(/(pY|pX)/i.6Z(cq.iL)){z.8e=dN(B(){if(/6m|iJ/.6Z(1q.6F)){z.6o()}},10)}(B(){A 3g=26;A 8d=B(cp,fp){A iK=3g[cp]||B(){};3g[cp]=B(){fp.14(3g,P);iK.14(3g,P)}};if(z.1l){1q.fJ(""+"");A co=K;8d("iG",B(){3g.5c(B(){co=U},0)});8d("pU",B(){if(co){z.ck()}});1u{1q.pT.2P("v","pS:pR-pQ-pP:pO");1q.pN().pM("v\\\\:*","pL:2E(#aY#pK)")}1y(e){}}I{8d("iG",B(){z.ck()})}})();z.pJ=B(){};z.1e=26["1q"]||L;z.3E=B(){C z.1e.3E||z.1e.4I("3E")[0]};z.ch=B(iF,iE){z.1W=iF;z.1e=iE};z.cf=B(4q,6n,iD){if((6n)&&((V 4q=="3c")||(4q 1N 67))){4q=6n[4q]}C(6n?4q.14(6n,iD||[]):4q())};z.pI=B(cj,iC,iB,iA){A cg;A iz=z.1W;A iy=z.1e;1u{z.ch(cj,cj.1q);cg=z.cf(iC,iB,iA)}ir{z.ch(iz,iy)}C cg};z.pH=B(ix,iw,iv,iu){A ce;A ip=z.1e;1u{z.1e=ix;ce=z.cf(iw,iv,iu)}ir{z.1e=ip}C ce};if(1o["cd"]){R(A cc in 1o["cd"]){z.io(cc,1o["cd"][cc])}}}if(1o.im){if(!1z.ca){z.8c("z.pG.ca")}}}if(!z.1h["z.X.c9"]){z.1h["z.X.c9"]=K;z.1Q("z.X.c9");z.1R=B(it){C(V it=="3c"||it 1N 67)};z.2l=B(it){C(it&&it 1N 4e||V it=="6a"||((V z["1H"]!="1k")&&(it 1N z.1H)))};if(z.c8&&z.3o){z.1Y=B(it){if((V(it)=="B")&&(it=="[8b 1H]")){C U}C(V it=="B"||it 1N bI)}}I{z.1Y=B(it){C(V it=="B"||it 1N bI)}}z.ib=B(it){if(V it=="1k"){C U}C(it===L||V it=="8b"||z.2l(it)||z.1Y(it))};z.pF=B(it){A d=z;if((!it)||(V it=="1k")){C U}if(d.1R(it)){C U}if(d.1Y(it)){C U}if(d.2l(it)){C K}if((it.5w)&&(it.5w.1M()=="3R")){C U}if(pE(it.G)){C K}C U};z.pD=B(it){if(!it){C U}C!z.1Y(it)&&/\\{\\s*\\[il 5h\\]\\s*\\}/.6Z(67(it))};z.c7=B(M,4W){A 8a={};R(A x in 4W){if((V 8a[x]=="1k")||(8a[x]!=4W[x])){M[x]=4W[x]}}if(z.1l){A p=4W.2i;if((V(p)=="B")&&(p!=M.2i)&&(p!=8a.2i)&&(p!="\\pC 2i() {\\n [il 5h]\\n}\\n")){M.2i=4W.2i}}C M};z.1x=B(M,pB){R(A i=1,l=P.G;i2){C z.ig.14(z,P)}if(!3k){3k=2z;2z=L}if(z.1R(3k)){2z=2z||z.1W;if(!2z[3k]){2m(["z.2p: ie[\\"",3k,"\\"] is L (ie=\\"",2z,"\\")"].22(""))}C B(){C 2z[3k].14(2z,P||[])}}I{C(!2z?3k:B(){C 3k.14(2z,P||[])})}};z.6j=B(M,c3){B c4(){};c4.1C=M;A c2=S c4();if(c3){z.1x(c2,c3)}C c2};z.7X=B(pz){A Q=[L];C z.2p.14(z,Q.3U(z.4d(P)))};z.4d=B(M,ic){A Q=[];R(A x=ic||0;x3)){z.ia("z.2r: R 9P \'"+6l+"\' py pw B as \'1P\' pv pu of as a pt i3.","","1.0");A c=3j;3j=P[3]||{};3j.1P=c}A dd=P.2O,4V=L;if(z.2l(4p)){4V=4p;4p=4V.3a()}if(4V){R(A i=0,m;i<4V.G;i++){m=4V[i];if(!m){2m("ps #"+i+" 4F pr of "+6l+" is L. pq\'s pp a po pl is 3O 6m.")}4p=dd.6j(4p,m)}}A i9=(3j||0).1P,6k=dd.6j(4p),fn;R(A i in 3j){if(z.1Y(fn=3j[i])&&(!0[i])){fn.i4=i}}z.4M(6k,{4o:6l,bY:i9,bZ:L},3j||0);6k.1C.1P=6k;C z.88(6l,6k)};z.1x(z.2r,{6j:B(c0,i8){A bp=(c0||0).1C,mp=(i8||0).1C;A 2S=z.2r.i7();z.1x(2S,{84:bp,1x:mp});if(c0){2S.1C=z.6j(bp)}z.4M(2S,z.2r.i6,mp||0,{bY:L});2S.1C.1P=2S;2S.1C.4o=(bp||0).4o+"pk"+(mp||0).4o;z.88(2S.1C.4o,2S);C 2S},i7:B(){C B(){D.i5(P)}},i6:{i5:B(86){A c=86.2O,s=c.84,ct=s&&s.1P,m=c.1x,87=m&&m.1P,a=86,ii,fn;if(a[0]){if((fn=a[0]["bZ"])){a=fn.14(D,a)||a}}if(fn=c.1C.bZ){a=fn.14(D,a)||a}if(ct&&ct.14){ct.14(D,a)}if(87&&87.14){87.14(D,a)}if(ii=c.1C.bY){ii.14(D,86)}},bX:B(85){A c=D.1P,p,m;1s(c){p=c.84;m=c.1x;if(m==85||(m 1N 85.1P)){C p}if(m&&(m=m.bX(85))){C m}c=p&&p.1P}},6h:B(83,82,bW,6i){A p=bW,c,m,f;do{c=p.1P;m=c.1x;if(m&&(m=D.6h(83,82,m,6i))){C m}if((f=p[83])&&(6i==(f==82))){C p}p=c.84}1s(p);C!6i&&(p=D.bX(bW))&&D.6h(83,82,p,6i)},bU:B(2R,4U,bV){A a=P;if(!z.1R(a[0])){bV=4U;4U=2R;2R=4U.2O.i4}A c=4U.2O,p=D.1P.1C,a=bV||4U,fn,mp;if(D[2R]!=c||p[2R]==c){mp=D.6h(2R,c,p,K);if(!mp){2m(D.4o+": 1p i3 (\\""+2R+"\\") 4F bU pj 1f 2O (2r.js)")}p=D.6h(2R,c,mp,U)}fn=p&&p[2R];if(!fn){1z.1K(mp.4o+": no bU \\""+2R+"\\" ph pg (2r.js)");C}C fn.14(D,a)}}})}if(!z.1h["z.X.2c"]){z.1h["z.X.2c"]=K;z.1Q("z.X.2c");z.3i={i2:B(){C B(){A ap=4e.1C,c=P.2O,ls=c.2b,t=c.5V;A r=t&&t.14(D,P);R(A i in ls){if(!(i in ap)){ls[i].14(D,P)}}C r}},2P:B(6g,bT,i1){6g=6g||z.1W;A f=6g[bT];if(!f||!f.2b){A d=z.3i.i2();d.5V=f;d.2b=[];f=6g[bT]=d}C f.2b.Y(i1)},3J:B(i0,hZ,bS){A f=(i0||z.1W)[hZ];if(f&&f.2b&&bS--){63 f.2b[bS]}}};z.2c=B(M,pd,pc,pa,p9){A a=P,F=[],i=0;F.Y(z.1R(a[0])?L:a[i++],a[i++]);A a1=a[i+1];F.Y(z.1R(a1)||z.1Y(a1)?a[i++]:L,a[i++]);R(A l=a.G;i2){6e=z.7X(6e,P,2)}C D.5k(6e,6e)},ef:B(cb,4T){A 7Y=z.2p(cb,4T);if(P.G>2){7Y=z.7X(7Y,P,2)}C D.5k(7Y,L)},ed:B(cb,4T){A 7W=z.2p(cb,4T);if(P.G>2){7W=z.7X(7W,P,2)}C D.5k(L,7W)},5k:B(cb,eb){D.bM.Y([cb,eb]);if(D.2y>=0){D.7U()}C D},7U:B(){A bL=D.bM;A 4n=D.2y;A 1v=D.4R[4n];A 4S=D;A cb=L;1s((bL.G>0)&&(D.3M==0)){A f=bL.3a()[4n];if(!f){6c}1u{1v=f(1v);4n=((1v 1N 1O)?1:0);if(1v 1N z.30){cb=B(1v){4S.7V(1v);4S.3M--;if((4S.3M==0)&&(4S.2y>=0)){4S.7U()}};D.3M++}}1y(1G){1z.1K(1G);4n=1;1v=1G}}D.2y=4n;D.4R[4n]=1v;if((cb)&&(D.3M)){1v.9e(cb)}}})}if(!z.1h["z.X.2e"]){z.1h["z.X.2e"]=K;z.1Q("z.X.2e");z.5m=B(2e){1u{C 3u("("+2e+")")}1y(e){1z.1K(e);C 2e}};z.bK=B(2H){C("\\""+2H.2f(/(["\\\\])/g,"\\\\$1")+"\\"").2f(/[\\f]/g,"\\\\f").2f(/[\\b]/g,"\\\\b").2f(/[\\n]/g,"\\\\n").2f(/[\\t]/g,"\\\\t").2f(/[\\r]/g,"\\\\r")};z.hM="\\t";z.eq=B(it,4l,4P){4P=4P||"";A 4k=(4l?4P+z.hM:"");A 6b=(4l?"\\n":"");A 4Q=V(it);if(4Q=="1k"){C"1k"}I{if((4Q=="4J")||(4Q=="p1")){C it+""}I{if(it===L){C"L"}}}if(4Q=="3c"){C z.bK(it)}A 6d=P.2O;A 4m;if(V it.hL=="B"){4m=it.hL();if(it!==4m){C 6d(4m,4l,4k)}}if(V it.2e=="B"){4m=it.2e();if(it!==4m){C 6d(4m,4l,4k)}}if(z.2l(it)){A 1v=[];R(A i=0;i>=7R;t[x]=7R==4?17*c:c});t.a=1;C t};z.7P=B(a,M){A t=M||S z.1J();t.bz(2V(a[0]),2V(a[1]),2V(a[2]),2V(a[3]));if(2L(t.a)){t.a=1}C t.7Q()};z.hq=B(2H,M){A a=z.1J.hp[2H];C a&&z.7P(a,M)||z.ho(2H,M)||z.hn(2H,M)}}if(!z.1h["z.X"]){z.1h["z.X"]=K;z.1Q("z.X")}if(!z.1h["z.X.5Z"]){z.1h["z.X.5Z"]=K;z.1Q("z.X.5Z");(B(){A 1j=z.b2={2P:B(E,68,fp){if(!E){C}68=1j.4O(68);fp=1j.7G(68,fp);E.66(68,fp,U);C fp},3J:B(E,hm,hl){(E)&&(E.oF(1j.4O(hm),hl,U))},4O:B(1p){C(1p.2w(0,2)=="on"?1p.2w(2):1p)},7G:B(1p,fp){C(1p!="4b"?fp:B(e){C fp.2d(D,1j.4i(e,D))})},4i:B(H,oE){4w(H.Z){2X"4b":1j.7K(H);3f}C H},7K:B(H){H.oD=(H.3h?67.oC(H.3h):"")}};z.oB=B(H,hk){C 1j.4i(H,hk)};z.gY=B(H){H.7J();H.7I()};A 7O=z.3i;z.by=B(M,bx,hh,hg,hi){A hj=M&&(M.2t||M.oA||M.66);A bw=!hj?0:(!hi?1:2),l=[z.3i,1j,7O][bw];A h=l.2P(M,bx,z.2p(hh,hg));C[M,bx,h,bw]};z.bv=B(M,he,hd,hf){([z.3i,1j,7O][hf]).3J(M,he,hd)};z.5W={oz:8,gV:9,oy:12,ox:13,ow:16,ov:17,ou:18,gG:19,ot:20,os:27,or:32,b5:33,b4:34,gE:35,gF:36,b7:37,b9:38,b6:39,b8:40,gD:45,8S:46,oq:47,oo:91,om:92,ol:93,oj:96,oi:97,oh:98,og:99,oe:6D,od:oc,ob:oa,o9:o8,o7:o6,o5:o4,o3:bi,o2:o1,o0:nZ,nY:nX,nW:nV,nU:bk,gS:nT,gR:nS,gQ:nR,gP:nQ,gO:nP,gN:nO,gM:nN,gL:nM,gK:nL,gJ:nK,gI:nJ,gH:nI,nH:nG,nF:nE,nD:nC,gB:nB,gC:nA};if(z.1l){bf=B(e,5h){1u{C(e.3I=5h)}1y(e){C 0}};A 61=z.3i;if(!1o.nz){7O=61=z.gy={b3:[],2P:B(64,bu,hc){64=64||z.1W;A f=64[bu];if(!f||!f.2b){A d=z.gz();d.5V=f&&(7M.Y(f)-1);d.2b=[];f=64[bu]=d}C f.2b.Y(7M.Y(hc)-1)},3J:B(hb,ha,7N){A f=(hb||z.1W)[ha],l=f&&f.2b;if(f&&l&&7N--){63 7M[l[7N]];63 l[7N]}}};A 7M=61.b3}z.1x(1j,{2P:B(E,62,fp){if(!E){C}62=1j.4O(62);if(62=="h3"){A kd=E.bs;if(!kd||!kd.2b||!kd.h9){1j.2P(E,"bs",1j.h4);E.bs.h9=K}}C 61.2P(E,62,1j.7G(fp))},3J:B(E,h8,h7){61.3J(E,1j.4O(h8),h7)},4O:B(7L){C(7L.2w(0,2)!="on"?"on"+7L:7L)},ny:B(){},4i:B(H,4N){if(!H){A w=(4N)&&((4N.aD||4N.1q||4N).nx)||26;H=w.5Z}if(!H){C(H)}H.5V=H.br;H.bh=(4N||H.br);H.nw=H.nv;H.nu=H.nr;A bq=H.br,1e=(bq&&bq.aD)||1q;A bn=((z.1l<6)||(1e["aX"]=="aW"))?1e.3E:1e.5K;A bm=z.aB();H.nq=H.np+z.aH(bn.5I||0)-bm.x;H.nn=H.nm+(bn.5G||0)-bm.y;if(H.Z=="fk"){H.h6=H.nl}if(H.Z=="fj"){H.h6=H.nk}H.7I=1j.bc;H.7J=1j.ba;C 1j.h5(H)},h5:B(H){4w(H.Z){2X"4b":A c=("3h"in H?H.3h:H.3I);if(c==10){c=0;H.3I=13}I{if(c==13||c==27){c=0}I{if(c==3){c=99}}}H.3h=c;1j.7K(H);3f}C H},gZ:{bi:42,bk:47,h2:59,nj:43,ni:44,nh:45,ng:46,nf:47,60:96,h1:91,nb:92,na:93,h0:39},h4:B(H){A kp=H.bh.h3;if(!kp||!kp.2b){C}A k=H.3I;A bj=(k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>bk)&&(k60)&&(kh0);if(bj||H.5Y){A c=(bj?0:k);if(H.5Y){if(k==3||k==13){C}I{if(c>95&&c=65&&c<=90)){c+=32}I{c=1j.gZ[c]||c}}}}A 2x=1j.7H(H,{Z:"4b",2x:K,3h:c});kp.2d(H.bh,2x);H.bg=2x.bg;H.bd=2x.bd;bf(H,2x.3I)}},bc:B(){D.bg=K},ba:B(){D.n9=D.3I;if(D.5Y){bf(D,0)}D.bd=U}});z.gY=B(H){H=H||26.5Z;1j.bc.2d(H);1j.ba.2d(H)}}1j.7H=B(H,gX){A 2x=z.1x({},H,gX);1j.7K(2x);2x.7J=B(){H.7J()};2x.7I=B(){H.7I()};C 2x};if(z.2M){z.1x(1j,{4i:B(H,n8){4w(H.Z){2X"4b":A c=H.n7;if(c==3){c=99}c=((c<41)&&(!H.5X)?0:c);if((H.5Y)&&(!H.5X)&&(c>=65)&&(c<=90)){c+=32}C 1j.7H(H,{3h:c})}C H}})}if(z.3o){z.1x(1j,{4i:B(H,n6){4w(H.Z){2X"4b":A c=H.3h,s=H.5X,k=H.3I;k=k||gA[H.gW]||0;if(H.gW=="n5"){c=0}I{if((H.5Y)&&(c>0)&&(c<27)){c+=96}I{if(c==z.5W.gU){c=z.5W.gV;s=K}I{c=(c>=32&&c gh",E).1n(B(i){i.1c.7E=i.1c.7E.2f(/gk:[^;]*;/i,"")})}}I{A o="mh(mg="+(7D*6D)+")";E.1c.3T=o}if(E.gj.1M()=="gi"){z.1r("> gh",E).1n(B(i){i.1c.3T=o})}C 7D}:B(E,gg){C E.1c.2W=gg});A 5Q={3n:K,58:K,2g:K,5J:K};A gd=B(E,Z,5P){Z=Z.1M();if(5Q[Z]===K){C z.4g(E,5P)}I{if(5Q[Z]===U){C 5P}I{if((Z.T("mf")>=0)||(Z.T("md")>=0)||(Z.T("3n")>=0)||(Z.T("58")>=0)||(Z.T("5q")>=0)||(Z.T("mc")>=0)||(Z.T("ma")>=0)){5Q[Z]=K;C z.4g(E,5P)}I{5Q[Z]=U;C 5P}}}};z.1c=B(E,5O,aT){A n=z.1D(E),F=P.G,op=(5O=="2W");if(F==3){C op?z.gf(n,aT):n.1c[5O]=aT}if(F==2&&op){C z.ge(n)}A s=z.3F(n);C(F==1)?s:gd(n,5O,s[5O])};z.7A=B(n,gc){A s=gc||1E(n),px=z.4g,l=px(n,s.m9),t=px(n,s.m8);C{l:l,t:t,w:l+px(n,s.m7),h:t+px(n,s.m6)}};z.5N=B(n,gb){A ne="7C",px=z.4g,s=gb||1E(n),bl=(s.m5!=ne?px(n,s.m4):0),bt=(s.m3!=ne?px(n,s.m2):0);C{l:bl,t:bt,w:bl+(s.m1!=ne?px(n,s.m0):0),h:bt+(s.lZ!=ne?px(n,s.lY):0)}};z.aN=B(n,ga){A s=ga||1E(n),p=z.7A(n,s),b=z.5N(n,s);C{l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h}};z.aM=B(n,g9){A s=g9||1E(n),px=z.4g,l=px(n,s.lX),t=px(n,s.lW),r=px(n,s.lV),b=px(n,s.lU);if(z.3o&&(s.ax!="fU")){r=l}C{l:l,t:t,w:l+r,h:t+b}};z.au=B(E,g8){A s=g8||1E(E),me=z.aM(E,s);A l=E.fT-me.l,t=E.fS-me.t;if(z.7B){A aS=2k(s.2g),aR=2k(s.5J);if(!2L(aS)&&!2L(aR)){l=aS,t=aR}I{A p=E.1L;if(p&&p.1c){A aQ=1E(p);if(aQ.lT!="lS"){A be=z.5N(p,aQ);l+=be.l,t+=be.t}}}}I{if(z.2M){A p=E.1L;if(p){A be=z.5N(p);l-=be.l,t-=be.t}}}C{l:l,t:t,w:E.6v+me.w,h:E.8D+me.h}};z.aK=B(E,g7){A s=g7||1E(E),pe=z.7A(E,s),be=z.5N(E,s),w=E.aF,h;if(!w){w=E.6v,h=E.8D}I{h=E.lR,be.w=be.h=0}if(z.2M){pe.l+=be.l;pe.t+=be.t}C{l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h}};z.lQ=B(E,g6){A s=g6||1E(E),pe=z.7A(E,s),cb=z.aK(E,s);C{l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h}};z.aL=B(E,l,t,w,h,u){u=u||"px";4G(E.1c){if(!2L(l)){2g=l+u}if(!2L(t)){5J=t+u}if(w>=0){3n=w+u}if(h>=0){58=h+u}}};z.aO=B(E){A n=E.5w;C(z.aP=="g5-3G")||(n=="lP")||(n=="lO")};z.fX=B(E,7z,7y,g4){A bb=z.aO(E);if(bb){A pb=z.aN(E,g4);if(7z>=0){7z+=pb.w}if(7y>=0){7y+=pb.h}}z.aL(E,g3,g3,7z,7y)};z.fY=B(E,g1,g0,5M,5L,g2){A s=g2||z.3F(E);A bb=z.aO(E),pb=bb?fZ:z.aN(E,s),mb=z.aM(E,s);if(5M>=0){5M=2Y.5q(5M-pb.w-mb.w,0)}if(5L>=0){5L=2Y.5q(5L-pb.h-mb.h,0)}z.aL(E,g1,g0,5M,5L)};A fZ={l:0,t:0,w:0,h:0};z.lN=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.au(n,s):z.fY(n,b.l,b.t,b.w,b.h,s)};z.lM=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.aK(n,s):z.fX(n,b.w,b.h,s)};A 5H=B(E,1a){if(!(E=(E||0).1L)){C 0}A 1U,aJ=0,2h=z.3E();1s(E&&E.1c){if(1E(E).ax=="lL"){C 0}1U=E[1a];if(1U){aJ+=1U-0;if(E==2h){3f}}E=E.1L}C aJ};z.fQ=B(){A 2h=z.3E();A 3g=z.1W;A de=z.1e.5K;C{y:(3g.lK||de.5G||2h.5G||0),x:(3g.lJ||z.aH(de.5I)||2h.5I||0)}};z.aG=B(){C V z.aI=="1k"?(z.aI=z.3F(z.3E()).lI=="lH"):z.aI};z.aB=B(){A de=z.1e.5K;if(z.1l>=7){C{x:de.aC().2g,y:de.aC().5J}}I{C{x:z.aG()||26.am==26?de.fW:de.6v-de.aF-de.fW,y:de.lG}}};z.aH=B(aE){if(z.1l&&!z.aG()){A de=z.1e.5K;C aE+de.aF-de.lF}C aE};z.fP=B(E,aw){A ay=E.aD;A J={x:0,y:0};A 7w=U;A db=z.3E();if(z.1l){A aA=E.aC();A az=z.aB();J.x=aA.2g-az.x;J.y=aA.5J-az.y}I{if(ay["fV"]){A bo=ay.fV(E);J.x=bo.x-5H(E,"5I");J.y=bo.y-5H(E,"5G")}I{if(E["fR"]){7w=K;A 7x;if(z.3o&&(1E(E).ax=="fU")&&(E.1L==db)){7x=db}I{7x=db.1L}if(E.1L!=db){A nd=E;if(z.2M){nd=db}J.x-=5H(nd,"5I");J.y-=5H(nd,"5G")}A 4f=E;do{A n=4f["fT"];if(!z.2M||n>0){J.x+=2L(n)?0:n}A m=4f["fS"];J.y+=2L(m)?0:m;4f=4f.fR}1s((4f!=7x)&&4f)}I{if(E["x"]&&E["y"]){J.x+=2L(E.x)?0:E.x;J.y+=2L(E.y)?0:E.y}}}}if(7w||aw){A av=z.fQ();A m=7w?(!aw?-1:0):1;J.y+=m*av.y;J.x+=m*av.x}C J};z.af=B(E,fO){A n=z.1D(E),s=1E(n),mb=z.au(n,s);A at=z.fP(n,fO);mb.x=at.x;mb.y=at.y;C mb}})();z.fL=B(E,fN){C((" "+E.3A+" ").T(" "+fN+" ")>=0)};z.7s=B(E,ar){A 7v=E.3A;if((" "+7v+" ").T(" "+ar+" ")<0){E.3A=7v+(7v?" ":"")+ar}};z.7r=B(E,fM){A t=z.7g((" "+E.3A+" ").2f(" "+fM+" "," "));if(E.3A!=t){E.3A=t}};z.lE=B(E,aq,7u){if(V 7u=="1k"){7u=!z.fL(E,aq)}z[7u?"7s":"7r"](E,aq)}}if(!z.1h["z.X.1H"]){z.1h["z.X.1H"]=K;z.1Q("z.X.1H");(B(){A d=z;z.1H=B(){A F=P;if((F.G==1)&&(V F[0]=="4J")){D.G=eK(F[0])}I{if(F.G){d.1n(F,B(i){D.Y(i)},D)}}};z.1H.1C=S 4e;if(d.1l){A fK=B(al){C("A a2 = am."+al+"; "+"A ap = 4e.1C; "+"A ao = a2.1C; "+"R(A x in ao){ ap[x] = ao[x]; } "+"am."+al+" = 4e; ")};A fI=fK("z.1H");A aj=26.lD();aj.1q.fJ(""+fI+"");aj.lC(1,1,1,1)}z.4M(z.1H,{T:B(fH,fG){C d.T(D,fH,fG)},31:B(lB,lA){A aa=d.4d(P);aa.ae(D);C d.31.14(d,aa)},ah:B(fF,fE){C d.ah(D,fF,fE)},ag:B(fD,fC){C d.ag(D,fD,fC)},1n:B(fB,fA){d.1n(D,fB,fA);C D},23:B(7t,M){C d.23(D,7t,M,d.1H)},af:B(){C d.23(D,d.af)},1c:B(lz,ly){A aa=d.4d(P);aa.ae(D[0]);A s=d.1c.14(d,aa);C(P.G>1)?D:s},lx:B(lw,lv){A aa=d.4d(P);aa.ae(L);A s=D.23(B(i){aa[0]=i;C d.1c.14(d,aa)});C(P.G>1)?D:s},7s:B(fz){C D.1n(B(i){z.7s(i,fz)})},7r:B(fy){C D.1n(B(i){z.7r(i,fy)})},5E:B(fw,7q){A 1m=d.1r(fw)[0];7q=7q||"72";R(A x=0;x=0){if(i[x]<1d){1d=i[x]}}}C(1d<0)?ql:1d};A 6X=B(7l){A i=2I(7l);if(i[0]!=-1){C 7l.21(i[0]+1,a0(7l,1))}I{C""}};A 5r=B(7k){A 5D;A i=2I(7k);if((i[0]==0)||(i[1]==0)){5D=0}I{5D=a0(7k,0)}C((5D>0)?7k.3b(0,5D).1M():"*")};A fg=B(Q){A J=-1;R(A x=0;x=0){if((1S>J)||(J==-1)){J=1S}}}C J};A 9H=B(7i){A i=2I(7i);if(-1==i[1]){C""}A di=i[1]+1;A 7j=fg(i.2w(2));if(di<7j){C 7i.21(di,7j)}I{if(-1==7j){C 7i.3b(di)}I{C""}}};A f3=[{1i:"|=",1f:B(15,fe){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fe+"-\')]"}},{1i:"~=",1f:B(15,fd){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fd+" \')]"}},{1i:"^=",1f:B(15,fb){C"[li-4G(@"+15+", \'"+fb+"\')]"}},{1i:"*=",1f:B(15,fa){C"[5z(@"+15+", \'"+fa+"\')]"}},{1i:"$=",1f:B(15,9Z){C"[21(@"+15+", 3c-G(@"+15+")-"+(9Z.G-1)+")=\'"+9Z+"\']"}},{1i:"!=",1f:B(15,f9){C"[3O(@"+15+"=\'"+f9+"\')]"}},{1i:"=",1f:B(15,f8){C"[@"+15+"=\'"+f8+"\']"}}];A 9C=B(9Y,3Z,f7,f6){A 49;A i=2I(3Z);if(i[2]>=0){A 4L=3Z.T("]",i[2]);A 29=3Z.21(i[2]+1,4L);1s(29&&29.G){if(29.2s(0)=="@"){29=29.2w(1)}49=L;R(A x=0;x<9Y.G;x++){A 1S=9Y[x];A 7h=29.T(1S.1i);if(7h>=0){A 15=29.21(0,7h);A 4a=29.21(7h+1S.1i.G);if((4a.2s(0)=="\\"")||(4a.2s(0)=="\'")){4a=4a.21(1,4a.G-1)}49=1S.1f(d.7g(15),d.7g(4a));3f}}if((!49)&&(29.G)){49=f7(29)}if(49){f6(49)}29=L;A 7f=3Z.T("[",4L);if(0<=7f){4L=3Z.T("]",7f);if(0<=4L){29=3Z.21(7f+1,4L)}}}}};A f0=B(f5){A 4K=".";A 7e=f5.1A(" ");1s(7e.G){A 2K=7e.3a();A 7d;if(2K==">"){7d="/";2K=7e.3a()}I{7d="//"}A f4=5r(2K);4K+=7d+f4;A id=6X(2K);if(id.G){4K+="[@id=\'"+id+"\'][1]"}A cn=9H(2K);if(cn.G){A 9X=" ";if(cn.2s(cn.G-1)=="*"){9X="";cn=cn.3b(0,cn.G-1)}4K+="[5z(3U(\' \',@9P,\' \'), \' "+cn+9X+"\')]"}9C(f3,2K,B(f2){C"[@"+f2+"]"},B(f1){4K+=f1})}C 4K};A 7a={};A eC=B(28){if(7a[28]){C 7a[28]}A 1e=d.1e;A 9W=f0(28);A 4H=B(9V){A J=[];A 7b;1u{7b=1e.9x(9W,9V,L,lh.lg,L)}1y(e){1z.1K("lf in le:",9W,"lc:",9V);1z.1K(e)}A 7c=7b.eZ();1s(7c){J.Y(7c);7c=7b.eZ()}C J};C 7a[28]=4H};A 5x={};A 9B={};A 3y=B(79,78){if(!79){C 78}if(!78){C 79}C B(){C 79.14(26,P)&&78.14(26,P)}};A 75=B(9U,3Y,5B,2J){A 2v=2J+1;A 76=(3Y.G==2v);A 2K=3Y[2J];if(2K==">"){A 77=9U.3W;if(!77.G){C}2v++;76=(3Y.G==2v);A 4H=6O(3Y[2J+1]);R(A x=0,11;x<77.G,11=77[x];x++){if(4H(11)){if(76){5B.Y(11)}I{75(11,3Y,5B,2v)}}}}A 5C=6U(2K)(9U);if(76){1s(5C.G){5B.Y(5C.3a())}}I{1s(5C.G){75(5C.3a(),3Y,5B,2v)}}};A eE=B(9T,eY){A J=[];A x=9T.G-1,11;1s(11=9T[x--]){75(11,eY,J,0)}C J};A 6O=B(3D){if(5x[3D]){C 5x[3D]}A ff=L;A 9S=5r(3D);if(9S!="*"){ff=3y(ff,B(N){C((N.2t==1)&&(9S==N.5w.1M()))})}A 9R=6X(3D);if(9R.G){ff=3y(ff,B(N){C((N.2t==1)&&(N.id==9R))})}if(2Y.5q.14(D,2I(3D).2w(1))>=0){ff=3y(ff,9z(3D))}C 5x[3D]=ff};A 5y=B(E){A pn=E.1L;A 9Q=pn.3W;A 2v=-1;A 3C=pn.5A;if(!3C){C 2v}A ci=E["eW"];A cl=pn["eX"];if(((V cl=="4J")&&(cl!=9Q.G))||(V ci!="4J")){pn["eX"]=9Q.G;A 2J=1;do{if(3C===E){2v=2J}if(3C.2t==1){3C["eW"]=2J;2J++}3C=3C.71}1s(3C)}I{2v=ci}C 2v};A lb=0;A 3X=B(N,15){A 74="";if(15=="9P"){C N.3A||74}if(15=="R"){C N.la||74}C N.5t(15,2)||74};A eH=[{1i:"|=",1f:B(15,9O){A eV=" "+9O+"-";C B(N){A ea=" "+(N.5t(15,2)||"");C((ea==9O)||(ea.T(eV)==0))}}},{1i:"^=",1f:B(15,eU){C B(N){C(3X(N,15).T(eU)==0)}}},{1i:"*=",1f:B(15,eT){C B(N){C(3X(N,15).T(eT)>=0)}}},{1i:"~=",1f:B(15,eS){A 9N=" "+eS+" ";C B(N){A ea=" "+3X(N,15)+" ";C(ea.T(9N)>=0)}}},{1i:"$=",1f:B(15,73){A 9N=" "+73;C B(N){A ea=" "+3X(N,15);C(ea.31(73)==(ea.G-73.G))}}},{1i:"!=",1f:B(15,eR){C B(N){C(3X(N,15)!=eR)}}},{1i:"=",1f:B(15,eQ){C B(N){C(3X(N,15)==eQ)}}}];A 9E=[{1i:"9M-9K",1f:B(1p,l9){C B(N){if(N.2t!=1){C U}A fc=N.eP;1s(fc&&(fc.2t!=1)){fc=fc.eP}C(!fc)}}},{1i:"72-9K",1f:B(1p,l8){C B(N){if(N.2t!=1){C U}A nc=N.71;1s(nc&&(nc.2t!=1)){nc=nc.71}C(!nc)}}},{1i:"l7",1f:B(1p,l6){C B(N){A cn=N.3W;A eO=N.3W.G;R(A x=eO-1;x>=0;x--){A nt=cn[x].2t;if((nt==1)||(nt==3)){C U}}C K}}},{1i:"5z",1f:B(1p,eN){C B(N){C(N.9L.T(eN)>=0)}}},{1i:"3O",1f:B(1p,eM){A eL=6O(eM);C B(N){C(!eL(N))}}},{1i:"l5-9K",1f:B(1p,2u){A pi=eK;if(2u=="l4"){C B(N){C(((5y(N))%2)==1)}}I{if((2u=="2n")||(2u=="l3")){C B(N){C((5y(N)%2)==0)}}I{if(2u.T("l2+")==0){A 70=pi(2u.3b(3));C B(N){C(N.1L.3W[70-1]===N)}}I{if((2u.T("n+")>0)&&(2u.G>3)){A 9J=2u.1A("n+",2);A eJ=pi(9J[0]);A 2J=pi(9J[1]);C B(N){C((5y(N)%eJ)==2J)}}I{if(2u.T("n")==-1){A 70=pi(2u);C B(N){C(5y(N)==70)}}}}}}}}];A 9z=B(3e){A 9I=(9B[3e]||5x[3e]);if(9I){C 9I}A ff=L;A i=2I(3e);if(i[0]>=0){A 24=5r(3e);if(24!="*"){ff=3y(ff,B(N){C(N.5w.1M()==24)})}}A 5u;A 3B=9H(3e);if(3B.G){A 9F=3B.2s(3B.G-1)=="*";if(9F){3B=3B.3b(0,3B.G-1)}A re=S 9G("(?:^|\\\\s)"+3B+(9F?".*":"")+"(?:\\\\s|$)");ff=3y(ff,B(N){C re.6Z(N.3A)})}if(i[3]>=0){A 3z=3e.3b(i[3]+1);A 9D="";A 5v=3z.T("(");A 6Y=3z.31(")");if((0<=5v)&&(0<=6Y)&&(6Y>5v)){9D=3z.21(5v+1,6Y);3z=3z.3b(0,5v)}5u=L;R(A x=0;x<9E.G;x++){A 1S=9E[x];if(1S.1i==3z){5u=1S.1f(3z,9D);3f}}if(5u){ff=3y(ff,5u)}}A eG=(d.1l)?B(5s){A eI=5s.1M();C B(N){C N[5s]||N[eI]}}:B(5s){C B(N){C(N&&N.5t&&N.l1(5s))}};9C(eH,3e,eG,B(eF){ff=3y(ff,eF)});if(!ff){ff=B(){C K}}C 9B[3e]=ff};A 6W={};A 6U=B(3d,1B){A 9A=6W[3d];if(9A){C 9A}A i=2I(3d);A id=6X(3d);if(i[0]==0){C 6W[3d]=B(1B){C[d.1D(id)]}}A 9y=9z(3d);A 5p;if(i[0]>=0){5p=B(1B){A 11=d.1D(id);if(9y(11)){C[11]}}}I{A 3V;A 24=5r(3d);if(2Y.5q.14(D,2I(3d))==-1){5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){J.Y(11)}C J}}I{5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){if(9y(11)){J.Y(11)}}C J}}}C 6W[3d]=5p};A l0={};A 5o={">":B(1B){A J=[];A 11,x=0,3V=1B.3W;1s(11=3V[x++]){if(11.2t==1){J.Y(11)}}C J}};A 9w=B(6V){if(0>6V.T(" ")){C 6U(6V)}A eD=B(1B){A 6S=6V.1A(" ");A 6T;if(6S[0]==">"){6T=[1B]}I{6T=6U(6S.3a())(1B)}C eE(6T,6S)};C eD};A 9v=((1q["9x"]&&!d.3o)?B(3x){A 6R=3x.1A(" ");if((1q["9x"])&&(3x.T(":")==-1)&&((K))){if(((6R.G>2)&&(3x.T(">")==-1))||(6R.G>3)||(3x.T("[")>=0)||((1==6R.G)&&(0<=3x.T(".")))){C eC(3x)}}C 9w(3x)}:9w);A ey=B(3w){if(5o[3w]){C 5o[3w]}if(0>3w.T(",")){C 5o[3w]=9v(3w)}I{A eB=3w.1A(/\\s*,\\s*/);A 4H=B(1B){A eA=0;A J=[];A 6Q;1s(6Q=eB[eA++]){J=J.3U(9v(6Q,6Q.T(" "))(1B))}C J};C 5o[3w]=4H}};A 5n=0;A ez=B(Q){A J=S d.1H();if(!Q){C J}if(Q[0]){J.Y(Q[0])}if(Q.G<2){C J}5n++;Q[0]["9u"]=5n;R(A x=1,11;11=Q[x];x++){if(Q[x]["9u"]!=5n){J.Y(11)}11["9u"]=5n}C J};d.1r=B(6P,1B){if(V 6P!="3c"){C S d.1H(6P)}if(V 1B=="3c"){1B=d.1D(1B)}C ez(ey(6P)(1B||d.1e))};d.9t=B(ex,9s){A 9r=S d.1H();A ff=(9s)?6O(9s):B(){C K};R(A x=0,11;11=ex[x];x++){if(ff(11)){9r.Y(11)}}C 9r}})()}if(!z.1h["z.X.1b"]){z.1h["z.X.1b"]=K;z.1Q("z.X.1b");z.6K=B(ew){A J={};A iq="kZ[Z!=9q][Z!=kY][Z!=et][Z!=kX][Z!=kW], kV, kU";z.1r(iq,ew).3T(B(E){C(!E.kT)}).1n(B(1m){A 3v=1m.1p;A Z=(1m.Z||"").1M();if((Z=="kS")||(Z=="kR")){if(1m.kQ){J[3v]=1m.1Z}}I{if(1m.kP){A ev=J[3v]=[];z.1r("kO[kN]",1m).1n(B(eu){ev.Y(eu.1Z)})}I{J[3v]=1m.1Z;if(Z=="et"){J[3v+".x"]=J[3v+".y"]=J[3v].x=J[3v].y=0}}}});C J};z.9h=B(23){A ec=kM;A J="";A es={};R(A x in 23){if(23[x]!=es[x]){if(z.2l(23[x])){R(A y=0;y<23[x].G;y++){J+=ec(x)+"="+ec(23[x][y])+"&"}}I{J+=ec(x)+"="+ec(23[x])+"&"}}}if((J.G)&&(J.2s(J.G-1)=="&")){J=J.3b(0,J.G-1)}C J};z.kL=B(er){C z.9h(z.6K(er))};z.kK=B(ep){C z.eq(z.6K(ep))};z.kJ=B(2H){A J={};A qp=2H.1A("&");A dc=kI;z.1n(qp,B(1m){if(1m.G){A 9p=1m.1A("=");A 1p=dc(9p.3a());A 1U=dc(9p.22("="));if(z.1R(J[1p])){J[1p]=[J[1p]]}if(z.2l(J[1p])){J[1p].Y(1U)}I{J[1p]=1U}}});C J};z.e1=U;z.e6={"9g":B(1b){C 1b.2G},"2e":B(1b){if(!1o.eo){1z.1K("kH kG kF a kE of 9g/2e-6M-9m"+" 4F kD kC kB kA 4G en kz"+" (ky 1o.eo=K 4F kx kw D kv)")}C z.5m(1b.2G)},"2e-6M-ku":B(1b){A 6N=1b.2G;A 9o=6N.T("/*");A 9n=6N.31("*/");if((9o==-1)||(9n==-1)){C z.5m(1b.2G)}C z.5m(6N.21(9o+2,9n))},"2e-6M-9m":B(1b){A 6L=1b.2G;A 9l=6L.T("/*");A 9k=6L.31("*/");if((9l==-1)||(9k==-1)){1z.1K("kt en ks\'t 6M 9m!");C""}C z.5m(6L.21(9l+2,9k))},"kr":B(1b){C z.3u(1b.2G)},"kq":B(1b){if(z.1l&&!1b.el){z.1n(["ko","em","kn","km"],B(i){1u{A 1e=S 9j(kl[i]+".kk");1e.kj=U;1e.ki(1b.2G);C 1e}1y(e){}})}I{C 1b.el}}};(B(){z.e5=B(F,ej,ei,eh){A 2F={};2F.F=F;A 6J=L;if(F.3R){A 3R=z.1D(F.3R);A 9i=3R.kh("kg");2F.2E=F.2E||(9i?9i.1Z:L);6J=z.6K(3R)}I{2F.2E=F.2E}A 5l=[{}];if(6J){5l.Y(6J)}if(F.5g){5l.Y(F.5g)}if(F.ek){5l.Y({"z.ek":S 5d().8O()})}2F.1r=z.9h(z.1x.14(L,5l));2F.9d=F.9d||"9g";A d=S z.30(ej);d.5k(ei,B(eg){C eh(eg,d)});A ld=F.4E;if(ld&&z.1Y(ld)){d.ef(B(ee){C ld.2d(F,ee,2F)})}A 1G=F.9f;if(1G&&z.1Y(1G)){d.ed(B(e9){C 1G.2d(F,e9,2F)})}A 6I=F.kf;if(6I&&z.1Y(6I)){d.9e(B(e8){C 6I.2d(F,e8,2F)})}d.1F=2F;C d};A e4=B(O){O.e0=K;A 1b=O.1F.1b;if(V 1b.e7=="B"){1b.e7()}};A e3=B(O){C z.e6[O.1F.9d](O.1F.1b)};A e2=B(9c,O){1z.1K(9c);C 9c};A 3Q=B(F){A O=z.e5(F,e4,e3,e2);O.1F.1b=z.9b(O.1F.F);C O};A 5j=L;A 3t=[];A 94=B(){A dZ=(S 5d()).dU();if(!z.e1){z.1n(3t,B(4D,6H){if(!4D){C}A O=4D.O;1u{if(!O||O.e0||!4D.dT(O)){3t.3S(6H,1);C}if(4D.dR(O)){3t.3S(6H,1);4D.dP(O)}I{if(O.9a){if(O.9a+(O.1F.F.6G||0)0){5c(z.2p(D,B(){D.5b(L,8R)}),d);C D}D.4A=S 5d().8O();if(D.2Z){D.4A-=D.8Q*D.2o}D.8N=D.4A+D.8Q;D.2D=K;D.2Z=U;A 8P=D.2C.4x(D.2o);if(!D.2o){if(!D.4y){D.4y=D.4z}D.3q("dt",[8P])}D.3q("ds",[8P]);D.8M();C D},jS:B(){5e(D.3r);if(!D.2D){C D}D.2Z=K;D.3q("dr",[D.2C.4x(D.2o)]);C D},jR:B(dq,dp){5e(D.3r);D.2D=D.2Z=K;D.2o=dq*6D;if(dp){D.5b()}C D},jQ:B(dn){if(!D.3r){C}5e(D.3r);if(dn){D.2o=1}D.3q("dm",[D.2C.4x(D.2o)]);D.2D=D.2Z=U;C D},3N:B(){if(D.2D){C D.2Z?"3M":"jP"}C"jO"},8M:B(){5e(D.3r);if(D.2D){A dl=S 5d().8O();A 2q=(dl-D.4A)/(D.8N-D.4A);if(2q>=1){2q=1}D.2o=2q;if(D.5a){2q=D.5a(2q)}D.3q("8B",[D.2C.4x(2q)]);if(2q<1){D.3r=5c(z.2p(D,"8M"),D.dj)}I{D.2D=U;if(D.4z>0){D.4z--;D.5b(L,K)}I{if(D.4z==-1){D.5b(L,K)}I{if(D.4y){D.4z=D.4y;D.4y=0}}}D.2o=0;D.3q("dh")}}C D}});(B(){A df=B(E){if(z.1l){A ns=E.1c;if(!ns.8L.G&&z.1c(E,"8L")=="dg"){ns.8L="1"}if(!ns.3n.G&&z.1c(E,"3n")=="8K"){ns.3n="8K"}}};z.6C=B(F){if(V F.1d=="1k"){2m S 1O("z.6C jN an 1d 1Z")}F.E=z.1D(F.E);A 3p=z.1x({6w:{}},F);A 8J=(3p.6w.2W={});8J.1w=(V 3p.1w=="1k")?B(){C 2V(z.1c(3p.E,"2W"))}:3p.1w;8J.1d=3p.1d;A 2U=z.6y(3p);z.2c(2U,"6x",L,B(){df(3p.E)});C 2U};z.8I=B(F){C z.6C(z.1x({1d:1},F))};z.8H=B(F){C z.6C(z.1x({1d:0},F))};if(z.6B&&!z.3o){z.8E=B(n){C 2k("0.5")+((2Y.da((n+2k("1.5"))*2Y.d9))/2)}}I{z.8E=B(n){C 0.5+((2Y.da((n+1.5)*2Y.d9))/2)}}A d4=B(6A){D.8G=6A;R(A p in 6A){A 1a=6A[p];if(1a.1w 1N z.1J){1a.d7=S z.1J()}}D.4x=B(r){A J={};R(A p in D.8G){A 1a=D.8G[p];A 6z=L;if(1a.1w 1N z.1J){6z=z.d8(1a.1w,1a.1d,r,1a.d7).8F()}I{if(!z.2l(1a.1w)){6z=((1a.1d-1a.1w)*r)+1a.1w+(p!="2W"?1a.jM||"px":"")}}J[p]=6z}C J}};z.6y=B(F){F.E=z.1D(F.E);if(!F.5a){F.5a=z.8E}A 2U=S z.d6(F);z.2c(2U,"6x",2U,B(){A pm={};R(A p in D.6w){A 1a=pm[p]=z.1x({},D.6w[p]);if(z.1Y(1a.1w)){1a.1w=1a.1w()}if(z.1Y(1a.1d)){1a.1d=1a.1d()}A d5=(p.1M().T("jL")>=0);B 8C(E,p){4w(p){2X"58":C E.8D;2X"3n":C E.6v}A v=z.1c(E,p);C(p=="2W")?2V(v):2k(v)};if(V 1a.1d=="1k"){1a.1d=8C(D.E,p)}I{if(V 1a.1w=="1k"){1a.1w=8C(D.E,p)}}if(d5){1a.1w=S z.1J(1a.1w);1a.1d=S z.1J(1a.1d)}I{1a.1w=(p=="2W")?2V(1a.1w):2k(1a.1w)}}D.2C=S d4(pm)});z.2c(2U,"8B",2U,B(8A){R(A s in 8A){z.1c(D.E,s,8A[s])}});C 2U}})()}',62,1711,'|||||||||||||||||||||||||||||||||||dojo|var|function|return|this|node|args|length|evt|else|ret|true|null|obj|elem|dfd|arguments|arr|for|new|indexOf|false|typeof||_base|push|type||te|||apply|attr|||||prop|xhr|style|end|doc|match|uri|_hasResource|key|del|undefined|isIE|item|forEach|djConfig|name|document|query|while|_66|try|res|start|mixin|catch|console|split|root|prototype|byId|gcs|ioArgs|err|NodeList|_p|Color|debug|parentNode|toLowerCase|instanceof|Error|constructor|provide|isString|ta|255|val|_a|global|_69|isFunction|value||substring|join|map|tn||window||path|_343|_220|_listeners|connect|call|json|replace|left|_b|toString|128|parseFloat|isArray|throw||_percent|hitch|step|declare|charAt|nodeType|_3c3|nidx|slice|faux|fired|_c4|_7e|loc|curve|_active|url|_44c|responseText|str|_312|idx|tqp|isNaN|isOpera|_22d|callee|add|_18b|_f8|_e2|_41|anim|Number|opacity|case|Math|_paused|Deferred|lastIndexOf|||||||||shift|substr|string|_3e7|_3ce|break|_w|charCode|_listener|_d5|_c5|authority|_49|width|isSafari|_49e|fire|_timer|_47b|_465|eval|_in|_40c|_409|_362|_3d9|className|_3d5|_386|_37a|body|getComputedStyle|box|_221|keyCode|remove|_8d|_46|paused|status|not|_478|_461|form|splice|filter|concat|tret|childNodes|_38b|_367|_33d||||||||||_340|_348|keypress|appendChild|_toArray|Array|_2b0|_toPixelValue|ref|_fixEvent|_19f|_14c|_14a|_150|_141|declaredClass|_d4|_99|_Url|_83|scheme|_67|_3d|switch|getValue|_startRepeatCount|repeat|_startTime|_47e|cancel|tif|load|to|with|tf|getElementsByTagName|number|_34c|_342|extend|_1e3|_normalizeEventName|_14b|_14e|results|self|cbfn|_f9|_d8|_b2|src|_88|dav||baseUrl|fragment|_loadedModules|_44|_43|_loaders|mll|height||easing|play|setTimeout|Date|clearTimeout|hdr|content|code|errback|_464|addCallbacks|_450|fromJson|_413|_3fc|_3ee|max|_31e|cond|getAttribute|_3d4|obi|tagName|_360|_381|contains|firstChild|_368|_372|_320|place|_2fa|scrollTop|_299|scrollLeft|top|documentElement|_288|_287|_getBorderExtents|_23f|_23d|_239|_218|_216|_211|eles|target|keys|shiftKey|ctrlKey|event|192|iel|_1db|delete|_1cf||addEventListener|String|_1af|_157|array|_14d|continue|_14f|_137|_11f|_106|_findMethod|has|_delegate|_dc|_d3|loaded|_9a|_loadInit|_inFlightCount|getObject|tv|_4f|_postLoad|_2d|offsetWidth|properties|beforeBegin|animateProperty|_4ad|_4a6|isKhtml|_fade|100|headers|readyState|timeout|_469|_457|_44d|formToObject|_441|comment|_43d|_36f|_419|tp|_40a|_406|_407|_373|_403|_3e6|_31b|cbi|test|_3c7|nextSibling|last|_3a1|_38e|_365|_36b|ecn|_364|_363|_356|_35e|_35f|_34f|_34d|_349|trim|tci|_328|_32b|_31f|_31c|_anim|_300|_2ff|_2f5|_2e7|removeClass|addClass|func|_2c4|cls|_2a9|_2ae|_280|_27f|_getPadExtents|isMoz|none|_233|cssText|_214|_fixCallback|_synthesizeEvent|stopPropagation|preventDefault|_setKeyChar|_1e1|ieh|_1d7|_1be|colorFromArray|sanitize|bits|rgb|_156|_fire|_resback|_13d|partial|_13a|silentlyCancelled|_topics|_127|_f1|_f0|superclass|_ec|_e3|mct|setObject|_bf|_b3|object|require|_92|_khtmlTimer|location|XMLHTTP|locale|dua|_71|_modulePrefixes|_55|_loadModule|_51|_50|_4e|pop|_3f|_callLoaded|_unloaders|_loadNotifying|_loadedUrls|_27|_24|_1d|_5|_4b7|onAnimate|getStyle|offsetHeight|_defaultEasing|toCss|_properties|fadeOut|fadeIn|_49f|auto|zoom|_cycle|_endTime|valueOf|_494|duration|_492|DELETE|_ioAddQueryToUrl|putData|contentType|password|user|_isDocumentOk|application|||||_466||||||startTime|_xhrObj|_45f|handleAs|addBoth|error|text|objectToQuery|_44f|ActiveXObject|_443|_442|filtered|_43f|_43e|_437|file|tnl|_41c|_filterQueryResult|_zipIdx|_408|_402|evaluate|_3ed|_380|fHit|_361|_33b|_3da|_3ab|_3d6|RegExp|_327|_3cf|_3c9|child|innerHTML|first|tval|_391|class|pnc|_37e|_37c|_375|_366|_35c|_35a|_353|_33c|_336|_314|||_315|_oe|_307|_309|cloneNode|after|createElement||_2f8|_2ef|_2ee|unshift|coords|some|every||_2cb|script|_2c9|parent||a2p||_2c3|_2bd||abs|_getMarginBox|_2b3|_2a6|position|_2a7|_2ac|_2ab|_getIeDocumentElementOffset|getBoundingClientRect|ownerDocument|_2a3|clientWidth|_isBodyLtr|_fixIeBiDiScrollLeft|_bodyLtr|_29d|_getContentBox|_setBox|_getMarginExtents|_getPadBorderExtents|_usesBorderBox|boxModel|pcs|st|sl|_240|runtimeStyle|_dcm|BackCompat|compatMode|default|_21b|_d|html|_event_listener|handlers|PAGE_DOWN|PAGE_UP|RIGHT_ARROW|LEFT_ARROW|DOWN_ARROW|UP_ARROW|_preventDefault||_stopPropagation|returnValue||_trySetKeyCode|cancelBubble|currentTarget|106|_1ee|111||_1e8|_1e7|||se|srcElement|onkeydown||_1d0|_disconnect|lid|_1c0|_connect|_set|_195|_185|_183|_17d|_everyOrSome|_16b|_172|_15b|Function|_154|_escapeString|_140|chain|_check|canceller|_12d|_124|_11a|_10d|_107|inherited|_fa|_f2|_findMixin|_constructor|preamble|_de|clone|tmp|_c7|TMP|_be|_ba|_mixin|isBrowser|lang|firebug||param|modulePaths|_a7|_fireCallback|_a0|setContext||_9c|unloaded||||_96|_93|navigator|_90|_89||protocol|_84|_86|_XMLHTTP_PROGIDS|gears|google|setAttribute|_80|_77|cfg|_6f|_getModuleSymbols|_5a|_58|_53|_4d|_4c|_45|_40|_moduleHasPrefix|_loadUri|_28|_26|_21|_22|tests|doh|_20|_1f|_1c|version|_1b|_19|_getProp|_11|_4|_4a5|_4b3|_Animation|tempColor|blendColors|PI|sin|||||_49a|normal|onEnd||rate||curr|onStop|_497||_496|pct|onPause|onPlay|onBegin|delay||_491|_Line|_48b|wrapForm|PUT|_487|POST|GET|_476|_474|_472|_ioWatch|send|_471|setRequestHeader|open|callback|setInterval|_470|resHandle|_46f|ioCheck|_46e|validCheck|getTime|_ioCancelAll|addOnUnload|clearInterval|dojoType|now|canceled|_blockAsync|_45e|_45c|_459|_ioSetArgs|_contentHandlers|abort|_458|_456||||addErrback|_454|addCallback|_452|_44b|_44a|_449|preventCache|responseXML|Microsoft|JSON|usePlainJson|_431|toJson|_430|_42d|image|opt|ria|_421|_41b|_40b|_zip|_410|_40d|_357|sqf|_374|_3e5|_3df|_38f|clc|pred|parseInt|ntf|_3bf|_3bc|cnl|previousSibling|_3a9|_3a6|_39c|_399|_396|_392|__cachedIndex|__cachedLength|_376|iterateNext|_34a|_355|_354|_32c|_350|_34b|_33f|_33e|_33a|_338|_334|_332||_330|_32e||_322|_316|mousemove|mouseout|mouseover|_305|lastChild||_2f9||_2f2|_2f1|removeChild|_2ec|_2eb|_2ea|_2e6||_2e4|_2e2|_2d6|_2d5|_2d4|_2d3|_2d2|_2d1|_2cd|_2cc|scs|write|_2c8|hasClass|_2c0|_2bb|_2b5|_abs|_docScroll|offsetParent|offsetTop|offsetLeft|absolute|getBoxObjectFor|clientLeft|_setContentSize|_setMarginBox|_28d|_286|_285|_289|NaN|_281|border|_272|_26b|_260|_258|_253|_24c|_246|_23a|_getOpacity|_setOpacity|_238|td|tr|nodeName|FILTER|_22f|_22e|currentStyle|_22c|_22b|display|QuirksMode|unselectable|_217|isMozilla|getElementById|attributes|all|_ie_listener|_getIeDispatcher|_1fd|NUM_LOCK|SCROLL_LOCK|INSERT|END|HOME|PAUSE|F12|F11|F10|F9|F8|F7|F6|F5|F4|F3|F2|F1|63232|SHIFT_TAB|TAB|keyIdentifier|_1f3|stopEvent|_punctMap|222|219|186|onkeypress|_stealthKeyDown|_fixKeys|relatedTarget|_1e0|_1df|_stealthKeydown|_1d6|_1d5|_1d1|_1ca|_1c9|_1cb|_1c2|_1c1|_1c3|_1c4|_1bc|_1b3|_1b2|colorFromHex|colorFromRgb|named|colorFromString|mask|rgba|_19c|_197|_192|setColor|_180|_178|_177|_175|_174|_16d|_166|_164|_163|_162|_15c|_15d|_15e|index|__json__|toJsonIndentStr|_nextId|_12f|_12b|publish|_128|_126|_125|_122|_121|_123|_11c|_11b|_10c|_10b|_108|getDispatcher|argument|nom|_construct|_core|_makeCtor|_df|_db|deprecated|isObject|_cc||scope||_hitchArgs|_c2||pre|_c1|native|isDebug||registerModulePath|_a8||finally|||_a6|_a5|_a4|_a3|_a2|_a1|_9f|_9e|_9d|_9b|_98|_97|onbeforeunload|ipt|scr|complete|_95|userAgent|_modulesLoaded|initialized|_initFired|_8c|_8a|_getText|_87|ieForceActiveXXhr|Msxml2|isGears|_81|_gearsObject|googlegears|GearsFactory|isFF|_7d|Safari|_72|_name|_6c|ire|ore|_68|i18n|_5b|requireIf|_56|_52|loading|_4a|_loadPath|_47|_48|_global_omit_module_check|_getModulePrefix|_3c|_3a|_37|_30|Boolean|_loadUriAndCheck|_2e||cacheBust|_1e|_1a|_17|_16|_15|_14|_f|_10|_e|_9|_8|revision|flag|patch|minor|major|_6|color|units|needs|stopped|playing|stop|gotoPercent|pause|1000|implemented|yet|_48a|xhrDelete|rawXhrPut|xhrPut|postData|rawXhrPost|xhrPost|xhrGet|Type|Content|sync|response|http|bad|urlencoded|www|_watchInFlightError||exceeded|handle|action|getAttributeNode|loadXML|async|XMLDOM|prefixes|MSXML3|MSXML|MSXML2||xml|javascript|wasn|your|optional|message|off|turn|use|endpoints|issues|security|potential|avoid|mimetype|using|consider|please|decodeURIComponent|queryToObject|formToJson|formToQuery|encodeURIComponent|selected|option|multiple|checked|checkbox|radio|disabled|textarea|select|button|reset|submit|input|_3fb|hasAttribute|0n|even|odd|nth|_3b5|empty|_3b1|_3ad|htmlFor|_38a|under||exprssion|failure|ANY_TYPE|XPathResult|starts|keyup|keydown|mouseup|mousedown|blur|click|combine|span|addContent||adopt|orphan|_2de|_2dd|styles|_2da|_2d9|_2cf|_2ce|show|createPopup|toggleClass|scrollWidth|clientTop|ltr|direction|pageXOffset|pageYOffset|fixed|contentBox|marginBox|BUTTON|TABLE|_getBorderBox|clientHeight|visible|overflow|marginBottom|marginRight|marginTop|marginLeft|borderBottomWidth|borderBottomStyle|borderRightWidth|borderRightStyle|borderTopWidth|borderTopStyle|borderLeftWidth|borderLeftStyle|paddingBottom|paddingRight|paddingTop|paddingLeft|offset||min|padding||margin|Opacity|Alpha|alpha|filters|pixelLeft|medium|_22a|defaultView|before||insertBefore|KhtmlUserSelect|MozUserSelect|setSelectable|isDescendant|div|_destroyElement|BackgroundImageCache|execCommand|PageDown|PageUp|Right|Left|Down|Up|63289|63249|63248|PRINT_SCREEN|63302|63277|63276|63275|63273|63272|63250|63247|63246|63245|63244|63243|63242|63241|63240|63239|63238|63237|63236|63235|63234|63233|Enter|_1f9|which|_1f6|bubbledKeyCode|221|220||||191|190|189|188|187|toElement|fromElement|clientY|pageY||clientX|pageX|offsetY|||layerY|offsetX|layerX|parentWindow|_nop|_allow_leaks|145|144|126|F15|125|F14|124|F13|123|122|121|120|119|118|117|116|115|114|113|112|NUMPAD_DIVIDE|110|NUMPAD_PERIOD|109|NUMPAD_MINUS|108|NUMPAD_ENTER|107|NUMPAD_PLUS|NUMPAD_MULTIPLY|105|NUMPAD_9|104|NUMPAD_8|103|NUMPAD_7|102|NUMPAD_6|101|NUMPAD_5|NUMPAD_4||NUMPAD_3|NUMPAD_2|NUMPAD_1|NUMPAD_0||SELECT|RIGHT_WINDOW||LEFT_WINDOW||HELP|SPACE|ESCAPE|CAPS_LOCK|ALT|CTRL|SHIFT|ENTER|CLEAR|BACKSPACE|attachEvent|fixEvent|fromCharCode|keyChar|_1b9|removeEventListener|0x|round|toHex|toRgba|toRgb|aqua|teal|blue|navy|yellow|olive|lime|green|fuchsia|purple|red|maroon|white|gray|silver|black|boolean|called|already|Cancelled|connectPublisher|unsubscribe|subscribe|disconnect|_113|_112||_111|_110|||found|was||must|_|module|||required|likely|It|declaration|Mixin|separate|instead|property|initializer||pass|_c9|_bb|_b7|nfunction|isAlien|isFinite|isArrayLike|_firebug|withDoc|withGlobal|_writeIncludes|VML|behavior|addRule|createStyleSheet|vml|com|microsoft|schemas|urn|namespaces|onunload|onreadystatechange|defer|khtml|WebKit|DOMContentLoaded|enableMozDomContentLoaded|domcontentloaded|Unable|base|chrome|1223|304|300|200|available|XMLHttpRequest|_println|language|userLanguage|isQuirks|factory|mimeTypes|Factory|Gears|_7f|MSIE||Firefox|Gecko|Konqueror||Opera|appVersion|xd|browser|moduleUrl|port|host|hostenv|_requireLocalization|_5f|_5e|_5d|_5c|requireLocalization|requireAfterIf|_57|common|platformRequire|defined|symbol|_isXDomain|tried|Could|__package__|packageFileName|_42|useXDomain|flight|still|files|addOnLoad|failed|sourceURL|util|notice|without|change|subject|APIs|EXPERIMENTAL|experimental|removed|will|DEPRECATED|exists|10315|Rev|Mobile|Spidermonkey|Rhino||Browser|delayMozLoadingFix|preventBackButtonFix|libraryScriptUri|baseRelativePath|baseScriptUri|allowQueryConfig|warn|trace|timeEnd||time|profileEnd|profile|log|info|groupEnd|group|dirxml|dir|count|assert'.split('|'),0,{}); /* Prototype 1.5 rc0 - Adapted from Ruby on Rails - http://dev.rubyonrails.org/browser/spinoffs/prototype/src - By Lunarmedia, 06 August, 2006 - Available at (and packed with) JavascriptCompressor.com Please note this version is missing the selector.js component of the full Prototype library. You can get the compressed version of selector at JavascriptCompressor.com */ var decompressedPrototype = function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[(function(e){return d[e]})];e=(function(){return'\\w+'});c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('d T={4l:\'1.5.8P\',3E:\'(?:<3G.*?>)((\\n|\\r|.)*?)(?:<\\/3G>)\',2v:7(){},K:7(x){c x}};d 1b={17:7(){c 7(){6.1I.2n(6,N)}}};d 1e=z q();q.u=7(5d,O){G(d 1G 2M O){5d[1G]=O[1G]}c 5d};q.1U=7(U){1j{f(U==1v)c\'1v\';f(U==1L)c\'1L\';c U.1U?U.1U():U.2C()}1s(e){f(e 8R 9l)c\'...\';25 e}};7j.v.1d=7(){d 43=6,23=$A(N),U=23.8S();c 7(){c 43.2n(U,23.3s($A(N)))}};7j.v.8U=7(U){d 43=6;c 7(C){c 43.8V(U,C||1W.C)}};q.u(8Q.v,{8W:7(){d 4Z=6.2C(16);f(6<16)c\'0\'+4Z;c 4Z},5j:7(){c 6+1},8Y:7(o){$R(0,6,11).V(o);c 6}});d 6s={6j:7(){d 48;G(d i=0;i0){f(I=O.I(1A)){L+=O.47(0,I.w);L+=(1z(I)||\'\').2C();O=O.47(I.w+I[0].t)}1D{L+=O,O=\'\'}}c L},92:7(1A,1z,3i){1z=6.2T.52(1z);3i=3i===1v?1:3i;c 6.2T(1A,7(I){f(--3i<0)c I[0];c 1z(I)})},93:7(1A,o){6.2T(1A,o);c 6},94:7(t,2S){t=t||30;2S=2S===1v?\'...\':2S;c 6.t>t?6.47(0,t-2S.t)+2S:6},9F:7(){c 6.2y(/^\\s+/,\'\').2y(/\\s+$/,\'\')},71:7(){c 6.2y(/<\\/?[^>]+>/7Y,\'\')},2Q:7(){c 6.2y(z 3O(T.3E,\'5P\'),\'\')},70:7(){d 6Y=z 3O(T.3E,\'5P\');d 5p=z 3O(T.3E,\'98\');c(6.I(6Y)||[]).1C(7(5o){c(5o.I(5p)||[\'\',\'\'])[1]})},3q:7(){c 6.70().1C(7(3G){c 4q(3G)})},9E:7(){d 1q=J.4Y(\'1q\');d 1Y=J.9D(6);1q.75(1Y);c 1q.3h},9c:7(){d 1q=J.4Y(\'1q\');1q.3h=6.71();c 1q.2z[0]?1q.2z[0].6q:\'\'},78:7(){d 7i=6.I(/^\\??(.*)$/)[1].3j(\'&\');c 7i.36({},7(5b,72){d 1i=72.3j(\'=\');5b[1i[0]]=1i[1];c 5b})},1Z:7(){c 6.3j(\'\')},3P:7(){d 2l=6.3j(\'-\');f(2l.t==1)c 2l[0];d 54=6.5g(\'-\')==0?2l[0].7e(0).3Y()+2l[0].7g(1):2l[0];G(d i=1,73=2l.t;i<73;i++){d s=2l[i];54+=s.7e(0).3Y()+s.7g(1)}c 54},1U:7(){c"\'"+6.2y(/\\\\/g,\'\\\\\\\\\').2y(/\'/g,\'\\\\\\\'\')+"\'"}});4b.v.2T.52=7(1z){f(2i 1z==\'7\')c 1z;d 2U=z 3n(1z);c 7(I){c 2U.7a(I)}};4b.v.9h=4b.v.78;d 3n=1b.17();3n.79=/(^|.|\\r|\\n)(#\\{(.*?)\\})/;3n.v={1I:7(2U,1A){6.2U=2U.2C();6.1A=1A||3n.79},7a:7(U){c 6.2U.2T(6.1A,7(I){d 53=I[1];f(53==\'\\\\\')c I[2];c 53+(U[I[3]]||\'\').2C()})}};d $1y=z q();d $49=z q();d 1p={V:7(o){d w=0;1j{6.2m(7(h){1j{o(h,w++)}1s(e){f(e!=$49)25 e}})}1s(e){f(e!=$1y)25 e}},9n:7(o){d L=11;6.V(7(h,w){L=L&&!!(o||T.K)(h,w);f(!L)25 $1y});c L},9o:7(o){d L=11;6.V(7(h,w){f(L=!!(o||T.K)(h,w))25 $1y});c L},3e:7(o){d P=[];6.V(7(h,w){P.W(o(h,w))});c P},7n:7(o){d L;6.V(7(h,w){f(o(h,w)){L=h;25 $1y}});c L},7o:7(o){d P=[];6.V(7(h,w){f(o(h,w))P.W(h)});c P},9p:7(1A,o){d P=[];6.V(7(h,w){d 7c=h.2C();f(7c.I(1A))P.W((o||T.K)(h,w))});c P},1M:7(U){d 51=Y;6.V(7(h){f(h==U){51=11;25 $1y}});c 51},36:7(45,o){6.V(7(h,w){45=o(45,h,w)});c 45},9q:7(1F){d 23=$A(N).47(1);c 6.3e(7(h){c h[1F].2n(h,23)})},9s:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||h>=L)L=h});c L},9u:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||hb?1:0}).3r(\'h\')},1Z:7(){c 6.3e(T.K)},9B:7(){d o=T.K,23=$A(N);f(2i 23.5e()==\'7\')o=23.9C();d 7l=[6].3s(23).1C($A);c 6.1C(7(h,w){c o(7l.3r(w))})},1U:7(){c\'#<1p:\'+6.1Z().1U()+\'>\'}};q.u(1p,{1C:1p.3e,5v:1p.7n,1k:1p.7o,8M:1p.1M,7p:1p.1Z});d $A=1E.7q=7(2R){f(!2R)c[];f(2R.1Z){c 2R.1Z()}1D{d P=[];G(d i=0;i<2R.t;i++)P.W(2R[i]);c P}};q.u(1E.v,1p);f(!1E.v.4d)1E.v.4d=1E.v.4m;q.u(1E.v,{2m:7(o){G(d i=0;i<6.t;i++)o(6[i])},5i:7(){6.t=0;c 6},7r:7(){c 6[0]},5e:7(){c 6[6.t-1]},7s:7(){c 6.1k(7(h){c h!=1v||h!=1L})},6J:7(){c 6.36([],7(6H,h){c 6H.3s(h&&h.5D==1E?h.6J():[h])})},5s:7(){d 4N=$A(N);c 6.1k(7(h){c!4N.1M(h)})},5g:7(U){G(d i=0;i<6.t;i++)f(6[i]==U)c i;c-1},4m:7(5h){c(5h!==Y?6:6.1Z()).4d()},1U:7(){c\'[\'+6.1C(q.1U).1N(\', \')+\']\'}});d 4h={2m:7(o){G(d 1O 2M 6){d h=6[1O];f(2i h==\'7\')49;d 1i=[1O,h];1i.1O=1O;1i.h=h;o(1i)}},7t:7(){c 6.3r(\'1O\')},4N:7(){c 6.3r(\'h\')},7u:7(2N){c $H(2N).36($H(6),7(4Q,1i){4Q[1i.1O]=1i.h;c 4Q})},7w:7(){c 6.1C(7(1i){c 1i.1C(4n).1N(\'=\')}).1N(\'&\')},1U:7(){c\'#<4h:{\'+6.1C(7(1i){c 1i.1C(q.1U).1N(\': \')}).1N(\', \')+\'}>\'}};7 $H(U){d 2N=q.u({},U||{});q.u(2N,1p);q.u(2N,4h);c 2N};3L=1b.17();q.u(3L.v,1p);q.u(3L.v,{1I:7(22,2x,2H){6.22=22;6.2x=2x;6.2H=2H},2m:7(o){d h=6.22;2q{o(h);h=h.5j()}1H(6.1M(h))},1M:7(h){f(h<6.22)c Y;f(6.2H)c h<6.2x;c h<=6.2x}});d $R=7(22,2x,2H){c z 3L(22,2x,2H)};d M={4w:7(){c 6s.6j(7(){c z 5C()},7(){c z 5n(\'7y.6d\')},7(){c z 5n(\'7z.6d\')})||Y},4s:0};M.2W={3b:[],2m:7(o){6.3b.2m(o)},69:7(4F){f(!6.1M(4F))6.3b.W(4F)},7A:7(5t){6.3b=6.3b.5s(5t)},3y:7(1a,26,E,2Z){6.V(7(3o){f(3o[1a]&&2i 3o[1a]==\'7\'){1j{3o[1a].2n(3o,[26,E,2Z])}1s(e){}}})}};q.u(M.2W,1p);M.2W.69({5G:7(){M.4s++},1B:7(){M.4s--}});M.44=7(){};M.44.v={4a:7(m){6.m={1F:\'4j\',4p:11,5H:\'5E/x-86-Q-7C\',28:\'\'};q.u(6.m,m||{})},3l:7(){c 6.E.32==1v||6.E.32==0||(6.E.32>=84&&6.E.32<7E)},7G:7(){c!6.3l()}};M.3t=1b.17();M.3t.5L=[\'7H\',\'80\',\'7I\',\'7J\',\'4t\'];M.3t.v=q.u(z M.44(),{1I:7(1l,m){6.E=M.4w();6.4a(m);6.26(1l)},26:7(1l){d 28=6.m.28||\'\';f(28.t>0)28+=\'&7K=\';1j{6.1l=1l;f(6.m.1F==\'7L\'&&28.t>0)6.1l+=(6.1l.I(/\\?/)?\'&\':\'?\')+28;M.2W.3y(\'5G\',6,6.E);6.E.7N(6.m.1F,6.1l,6.m.4p);f(6.m.4p){6.E.5T=6.5J.1d(6);2Y((7(){6.4r(1)}).1d(6),10)}6.5A();d 1c=6.m.5V?6.m.5V:28;6.E.7O(6.m.1F==\'4j\'?1c:1L)}1s(e){6.3p(e)}},5A:7(){d 1P=[\'X-7P-7Q\',\'5C\',\'X-T-4l\',T.4l,\'7R\',\'1Y/7m, 1Y/2e, 5E/5F, 1Y/5F, */*\'];f(6.m.1F==\'4j\'){1P.W(\'5Q-2g\',6.m.5H);f(6.E.7S)1P.W(\'7T\',\'7U\')}f(6.m.1P)1P.W.2n(1P,6.m.1P);G(d i=0;i<1P.t;i+=2)6.E.7V(1P[i],1P[i+1])},5J:7(){d 2F=6.E.2F;f(2F!=1)6.4r(6.E.2F)},4A:7(B){1j{c 6.E.7W(B)}1s(e){}},5M:7(){1j{c 4q(\'(\'+6.4A(\'X-7X\')+\')\')}1s(e){}},5R:7(){1j{c 4q(6.E.3F)}1s(e){6.3p(e)}},4r:7(2F){d C=M.3t.5L[2F];d E=6.E,2Z=6.5M();f(C==\'4t\'){1j{(6.m[\'2I\'+6.E.32]||6.m[\'2I\'+(6.3l()?\'81\':\'82\')]||T.2v)(E,2Z)}1s(e){6.3p(e)}f((6.4A(\'5Q-2g\')||\'\').I(/^1Y\\/7m/i))6.5R()}1j{(6.m[\'2I\'+C]||T.2v)(E,2Z);M.2W.3y(\'2I\'+C,6,E,2Z)}1s(e){6.3p(e)}f(C==\'4t\')6.E.5T=T.2v},3p:7(57){(6.m.5W||T.2v)(6,57);M.2W.3y(\'5W\',6,57)}});M.4C=1b.17();q.u(q.u(M.4C.v,M.3t.v),{1I:7(1w,1l,m){6.4x={3m:1w.3m?$(1w.3m):$(1w),3z:1w.3z?$(1w.3z):(1w.3m?1L:$(1w))};6.E=M.4w();6.4a(m);d 1B=6.m.1B||T.2v;6.m.1B=(7(E,U){6.5Y();1B(E,U)}).1d(6);6.26(1l)},5Y:7(){d 3A=6.3l()?6.4x.3m:6.4x.3z;d 3k=6.E.3F;f(!6.m.3q)3k=3k.2Q();f(3A){f(6.m.60){z 6.m.60(3A,3k)}1D{k.6h(3A,3k)}}f(6.3l()){f(6.1B)2Y(6.1B.1d(6),10)}}});M.61=1b.17();M.61.v=q.u(z M.44(),{1I:7(1w,1l,m){6.4a(m);6.1B=6.m.1B;6.1J=(6.m.1J||2);6.2s=(6.m.2s||1);6.4B={};6.1w=1w;6.1l=1l;6.22()},22:7(){6.m.1B=6.63.1d(6);6.2D()},7b:7(){6.4B.1B=1v;89(6.65);(6.1B||T.2v).2n(6,N)},63:7(26){f(6.m.2s){6.2s=(26.3F==6.64?6.2s*6.m.2s:1);6.64=26.3F}6.65=2Y(6.2D.1d(6),6.2s*6.1J*4z)},2D:7(){6.4B=z M.4C(6.1w,6.1l,6.m)}});7 $(){d P=[],4;G(d i=0;i<4V>\'+6.2t+\'\';c $A(1q.2z[0].2z[0].2z)}};d 1g=z q();1g.6W=1b.17();1g.6W.v=q.u(z 1e.1g(\'96\'),{2V:7(){6.1K.97(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4)}).1d(6))}});1g.5m=1b.17();1g.5m.v=q.u(z 1e.1g(\'99\'),{2V:7(){6.1K.56(6.4);6.1K.74(11)},2X:7(2h){2h.4m(Y).V((7(2j){6.4.55(2j,6.4.9a)}).1d(6))}});1g.7h=1b.17();1g.7h.v=q.u(z 1e.1g(\'9d\'),{2V:7(){6.1K.56(6.4);6.1K.74(6.4)},2X:7(2h){2h.V((7(2j){6.4.75(2j)}).1d(6))}});1g.76=1b.17();1g.76.v=q.u(z 1e.1g(\'9i\'),{2V:7(){6.1K.9m(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4.9t)}).1d(6))}});k.3S=1b.17();k.3S.v={1I:7(4){6.4=$(4)},2m:7(o){6.4.1f.3j(/\\s+/).1k(7(B){c B.t>0}).2m(o)},5c:7(1f){6.4.1f=1f},7k:7(5a){f(6.1M(5a))c;6.5c(6.1Z().3s(5a).1N(\' \'))},42:7(4c){f(!6.1M(4c))c;6.5c(6.1k(7(1f){c 1f!=4c}).1N(\' \'))},2C:7(){c 6.1Z().1N(\' \')}};q.u(k.3S.v,1p);d 5I={5i:7(){G(d i=0;i=0){2b=4.m[w];h=2b.h||2b.1Y}c[4.B,h]},5X:7(4){d h=[];G(d i=0;i<4.t;i++){d 2b=4.m[i];f(2b.87)h.W(2b.h||2b.1Y)}c[4.B,h]}};d $F=D.k.1x;1e.3D=7(){};1e.3D.v={1I:7(4,1J,1a){6.1J=1J;6.4=$(4);6.1a=1a;6.2K=6.1x();6.2A()},2A:7(){5Z(6.2D.1d(6),6.1J*4z)},2D:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}}};D.k.3C=1b.17();D.k.3C.v=q.u(z 1e.3D(),{1x:7(){c D.k.1x(6.4)}});D.3C=1b.17();D.3C.v=q.u(z 1e.3D(),{1x:7(){c D.3a(6.4)}});1e.2c=7(){};1e.2c.v={1I:7(4,1a){6.4=$(4);6.1a=1a;6.2K=6.1x();f(6.4.1h.2w()==\'Q\')6.67();1D 6.2A(6.4)},4K:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}},67:7(){d 12=D.2L(6.4);G(d i=0;i<12.t;i++)6.2A(12[i])},2A:7(4){f(4.2g){6c(4.2g.2w()){1r\'6g\':1r\'6i\':1o.3B(4,\'8j\',6.4K.1d(6));1y;1r\'6l\':1r\'1Y\':1r\'3J\':1r\'1k-6n\':1r\'1k-8t\':1o.3B(4,\'8u\',6.4K.1d(6));1y}}}};D.k.2c=1b.17();D.k.2c.v=q.u(z 1e.2c(),{1x:7(){c D.k.1x(6.4)}});D.2c=1b.17();D.2c.v=q.u(z 1e.2c(),{1x:7(){c D.3a(6.4)}});f(!1W.1o){d 1o=z q()}q.u(1o,{8C:8,8F:9,8H:13,8I:27,8J:37,8L:38,8O:39,8T:40,8X:46,4:7(C){c C.Z||C.91},95:7(C){c(((C.6X)&&(C.6X==1))||((C.6Z)&&(C.6Z==1)))},9b:7(C){c C.9e||(C.9f+(J.3R.2G||J.1c.2G))},9g:7(C){c C.9j||(C.9k+(J.3R.2O||J.1c.2O))},7b:7(C){f(C.7d){C.7d();C.9r()}1D{C.48=Y;C.9w=11}},9A:7(C,1h){d 4=1o.4(C);1H(4.1X&&(!4.1h||(4.1h.3Y()!=1h.3Y())))4=4.1X;c 4},1T:Y,5u:7(4,B,1V,1u){f(!6.1T)6.1T=[];f(4.5f){6.1T.W([4,B,1V,1u]);4.5f(B,1V,1u)}1D f(4.4i){6.1T.W([4,B,1V,1u]);4.4i(\'2I\'+B,1V)}},66:7(){f(!1o.1T)c;G(d i=0;i<1o.1T.t;i++){1o.5N.2n(6,1o.1T[i]);1o.1T[i][0]=1L}1o.1T=Y},3B:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4i))B=\'5K\';6.5u(4,B,1V,1u)},5N:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4k))B=\'5K\';f(4.5x){4.5x(B,1V,1u)}1D f(4.4k){1j{4.4k(\'2I\'+B,1V)}1s(e){}}}});f(33.4u.I(/\\88\\b/))1o.3B(1W,\'8a\',1o.66,Y);d 2d={6o:Y,4P:7(){6.6z=1W.8e||J.3R.2G||J.1c.2G||0;6.6F=1W.8g||J.3R.2O||J.1c.2O||0},6u:7(4){d 19=0,15=0;2q{19+=4.2O||0;15+=4.2G||0;4=4.1X}1H(4);c[15,19]},35:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q}1H(4);c[15,19]},68:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q;f(4){p=k.1R(4,\'14\');f(p==\'3T\'||p==\'2o\')1y}}1H(4);c[15,19]},1Q:7(4){f(4.1Q)c 4.1Q;f(4==J.1c)c 4;1H((4=4.1X)&&4!=J.1c)f(k.1R(4,\'14\')!=\'4G\')c 4;c J.1c},8o:7(4,x,y){f(6.6o)c 6.6r(4,x,y);6.3g=x;6.34=y;6.1t=6.35(4);c(y>=6.1t[1]&&y<6.1t[1]+4.2k&&x>=6.1t[0]&&x<6.1t[0]+4.2p)},6r:7(4,x,y){d 4S=6.6u(4);6.3g=x+4S[0]-6.6z;6.34=y+4S[1]-6.6F;6.1t=6.35(4);c(6.34>=6.1t[1]&&6.34<6.1t[1]+4.2k&&6.3g>=6.1t[0]&&6.3g<6.1t[0]+4.2p)},8E:7(3Z,4){f(!3Z)c 0;f(3Z==\'8G\')c((6.1t[1]+4.2k)-6.34)/4.2k;f(3Z==\'8K\')c((6.1t[0]+4.2p)-6.3g)/4.2p},77:7(O,Z){O=$(O);Z=$(Z);Z.l.14=\'2o\';d 2P=6.35(O);Z.l.1n=2P[1]+\'1m\';Z.l.18=2P[0]+\'1m\';Z.l.21=O.2p+\'1m\';Z.l.24=O.2k+\'1m\'},4e:7(4M){d 19=0,15=0;d 4=4M;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y}1H(4=4.1Q);4=4M;2q{19-=4.2O||0;15-=4.2G||0}1H(4=4.1X);c[15,19]},77:7(O,Z){d m=q.u({5l:11,5r:11,5B:11,5q:11,29:0,2f:0},N[2]||{});O=$(O);d p=2d.4e(O);Z=$(Z);d 2J=[0,0];d 3v=1L;f(k.1R(Z,\'14\')==\'2o\'){3v=2d.1Q(Z);2J=2d.4e(3v)}f(3v==J.1c){2J[0]-=J.1c.2f;2J[1]-=J.1c.29}f(m.5l)Z.l.18=(p[0]-2J[0]+m.2f)+\'1m\';f(m.5r)Z.l.1n=(p[1]-2J[1]+m.29)+\'1m\';f(m.5B)Z.l.21=O.2p+\'1m\';f(m.5q)Z.l.24=O.2k+\'1m\'},8b:7(4){4=$(4);f(4.l.14==\'2o\')c;2d.4P();d 2P=2d.68(4);d 1n=2P[1];d 18=2P[0];d 21=4.6m;d 24=4.6p;4.6P=18-3X(4.l.18||0);4.6I=1n-3X(4.l.1n||0);4.5k=4.l.21;4.7f=4.l.24;4.l.14=\'2o\';4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.21=21+\'1m\';4.l.24=24+\'1m\'},8w:7(4){4=$(4);f(4.l.14==\'3T\')c;2d.4P();4.l.14=\'3T\';d 1n=3X(4.l.1n||0)-(4.6I||0);d 18=3X(4.l.18||0)-(4.6P||0);4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.24=4.7f;4.l.21=4.5k}};f(/3x|3w|3u/.4v(33.62)){2d.35=7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y;4=4.1Q}1H(4);c[15,19]}};',62,600,'||||element||this|function|||||return|var||if||value|||Element|style|options||iterator||Object|||length|extend|prototype|index|||new||name|event|Form|transport||for||match|document||result|Ajax|arguments|source|results|form|||Prototype|object|each|push||false|target||true|elements||position|valueL||create|left|valueT|callback|Class|body|bind|Abstract|className|Insertion|tagName|pair|try|select|url|px|top|Event|Enumerable|div|case|catch|offset|useCapture|undefined|container|getValue|break|replacement|pattern|onComplete|map|else|Array|method|property|while|initialize|frequency|range|null|include|join|key|requestHeaders|offsetParent|getStyle|parameter|observers|inspect|observer|window|parentNode|text|toArray|els|width|start|args|height|throw|request||parameters|offsetTop|methods|opt|EventObserver|Position|html|offsetLeft|type|fragments|typeof|fragment|offsetHeight|oStringList|_each|apply|absolute|offsetWidth|do|cache|decay|content|input|emptyFunction|toLowerCase|end|replace|childNodes|registerCallback|display|toString|onTimerEvent|Serializers|readyState|scrollLeft|exclusive|on|delta|lastValue|getElements|in|hash|scrollTop|offsets|stripScripts|iterable|truncation|gsub|template|initializeRange|Responders|insertContent|setTimeout|json||hidden|status|navigator|ycomp|cumulativeOffset|inject||||serialize|responders|_overflow|Methods|collect|adjacency|xcomp|innerHTML|count|split|response|responseIsSuccess|success|Template|responder|dispatchException|evalScripts|pluck|concat|Request|KHTML|parent|Safari|Konqueror|dispatch|failure|receiver|observe|Observer|TimedObserver|ScriptFragment|responseText|script|inputs|ancestor|textarea|classNames|ObjectRange|node|typeName|RegExp|camelize|none|documentElement|ClassNames|relative|right|overflow|HTMLElement|parseFloat|toUpperCase|mode||currentlyExecuting|remove|__method|Base|memo||slice|returnValue|continue|setOptions|String|classNameToRemove|_reverse|page|focus|queryComponent|Hash|attachEvent|post|detachEvent|Version|reverse|encodeURIComponent|disabled|asynchronous|eval|respondToReadyState|activeRequestCount|Complete|appVersion|test|getTransport|containers|matchingInputs|1000|header|updater|Updater|getElementsByTagName|child|responderToAdd|static|tagElements|queryComponents|defaultView|onElementEvent|css|forElement|values|visibility|prepare|mergedHash|pos|offsetcache|_madePositioned|visible|tbody|findOrStore|_nativeExtensions|createElement|digits|trues|found|prepareReplacement|before|camelizedString|insertBefore|selectNodeContents|exception|falses|criteria|classNameToAdd|params|set|destination|last|addEventListener|indexOf|inline|clear|succ|_originalWidth|setLeft|Top|ActiveXObject|scriptTag|matchOne|setHeight|setTop|without|responderToRemove|_observeAndCache|find|reset|removeEventListener|activate|findFirstElement|setRequestHeaders|setWidth|XMLHttpRequest|constructor|application|xml|onCreate|contentType|Field|onStateChange|keydown|Events|evalJSON|stopObserving|inputSelector|img|Content|evalResponse|selectOne|onreadystatechange|keypress|postBody|onException|selectMany|updateContent|setInterval|insertion|PeriodicalUpdater|userAgent|updateComplete|lastText|timer|unloadCache|registerFormCallbacks|positionedOffset|register|parentElement|children|switch|XMLHTTP|_extended|hide|checkbox|update|radio|these|outerHTML|password|clientWidth|one|includeScrollOffsets|clientHeight|nodeValue|withinIncludingScrolloffsets|Try|scrollTo|realOffset|getComputedStyle|show|currentStyle|auto|deltaX|originalPosition|originalVisibility|originalWidth|originalHeight|opera|deltaY|bottom|array|_originalTop|flatten|addMethods|lambda|Toggle|toggle|insertAdjacentHTML|_originalLeft|PeriodicalExecuter|ownerDocument|createRange|createContextualFragment|contentFromAnonymousTable|table|Before|which|matchAll|button|extractScripts|stripTags|pairString|len|collapse|appendChild|After|clone|toQueryParams|Pattern|evaluate|stop|stringValue|preventDefault|charAt|_originalHeight|substring|Bottom|pairs|Function|add|collections|javascript|detect|findAll|entries|from|first|compact|keys|merge|present|toQueryString|getInputs|Msxml2|Microsoft|unregister|disable|urlencoded|blur|300|enable|responseIsFailure|Uninitialized|Loaded|Interactive|_|get|focusFirstElement|open|send|Requested|With|Accept|overrideMimeType|Connection|close|setRequestHeader|getResponseHeader|JSON|gi|submit|Loading|Success|Failure|checked|200|selectedIndex|www|selected|bMSIE|clearTimeout|unload|absolutize|string|getElementById|pageXOffset|getElementsByClassName|pageYOffset|removeChild|replaceChild|click|getHeight|hasClassName|addClassName|removeClassName|within|cleanWhitespace|nodeType|empty|childOf|multiple|change|getPropertyValue|relativize|setStyle|getDimensions|makePositioned|undoPositioned|makeClipping|KEY_BACKSPACE|undoClipping|overlap|KEY_TAB|vertical|KEY_RETURN|KEY_ESC|KEY_LEFT|horizontal|KEY_UP|member|tr|KEY_RIGHT|0_RC_0|Number|instanceof|shift|KEY_DOWN|bindAsEventListener|call|toColorPart|KEY_DELETE|times|finally|callee|srcElement|sub|scan|truncate|isLeftClick|beforeBegin|setStartBefore|im|afterBegin|firstChild|pointerX|unescapeHTML|beforeEnd|pageX|clientX|pointerY|parseQuery|afterEnd|pageY|clientY|RangeError|setStartAfter|all|any|grep|invoke|stopPropagation|max|nextSibling|min|partition|cancelBubble|reject|sortBy|sort|findElement|zip|pop|createTextNode|escapeHTML|strip'.split('|'),0,{}) }closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/sunspider-0.9.1/math-cordic.js0000644000175000017500000000514014433667662026533 0ustar apoapo/* * Copyright (C) Rich Moore. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /////. Start CORDIC var AG_CONST = 0.6072529350; function FIXED(X) { return X * 65536.0; } function FLOAT(X) { return X / 65536.0; } function DEG2RAD(X) { return 0.017453 * (X); } var Angles = [ FIXED(45.0), FIXED(26.565), FIXED(14.0362), FIXED(7.12502), FIXED(3.57633), FIXED(1.78991), FIXED(0.895174), FIXED(0.447614), FIXED(0.223811), FIXED(0.111906), FIXED(0.055953), FIXED(0.027977) ]; function cordicsincos() { var X; var Y; var TargetAngle; var CurrAngle; var Step; X = FIXED(AG_CONST); /* AG_CONST * cos(0) */ Y = 0; /* AG_CONST * sin(0) */ TargetAngle = FIXED(28.027); CurrAngle = 0; for (Step = 0; Step < 12; Step++) { var NewX; if (TargetAngle > CurrAngle) { NewX = X - (Y >> Step); Y = (X >> Step) + Y; X = NewX; CurrAngle += Angles[Step]; } else { NewX = X + (Y >> Step); Y = -(X >> Step) + Y; X = NewX; CurrAngle -= Angles[Step]; } } } ///// End CORDIC function cordic( runs ) { var start = new Date(); for ( var i = 0 ; i < runs ; i++ ) { cordicsincos(); } var end = new Date(); return end.getTime() - start.getTime(); } cordic(25000); closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/caliper/0000755000175000017500000000000014433667662022743 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/caliper/fieldTests.js0000644000175000017500000000254114433667662025411 0ustar apoapofunction createObject(iterations, strings, ints) { var o; for (var ct = 0; ct < iterations; ct++) { o = {}; var s = 0; var i = 0; while ((s < strings.length) && (i < ints.length)) { if (s < strings.length) { o[strings[s]] = strings[s]; s++; } if (i < ints.length) { o[ints[i]] = ints[i]; i++; } } } return o; } function iterateObject(iterations, o) { var x; for (var ct = 0; ct < iterations; ct++) { for (var k in o) { x = o[k]; } } return x; } function iterateOwnKeysObject(iterations, o) { var pn; for (var ct = 0; ct < iterations; ct++) { pn = Object.getOwnPropertyNames(o); } return pn; } function accessObject(iterations, o, strings, ints) { var s = 0; var i = 0; for (var ct = 0; ct < iterations; ct++) { if (strings.length > 0) { var x = o[strings[s]]; s++; if (s === strings.length) { s = 0; } } if (ints.length > 0) { var x = o[ints[i]]; i++; if (i === ints.length) { i = 0; } } } } function deleteObject(iterations, o, strings, ints) { var s = 0; var i = 0; for (var ct = 0; ct < iterations; ct++) { if (s < strings.length) { delete o[strings[s]]; s++; } if (i < ints.length) { delete o[ints[i]]; i++; } } }closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/0000755000175000017500000000000014433667662024325 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/base.js0000644000175000017500000002226214433667662025601 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Simple framework for running the benchmark suites and // computing a score based on the timing measurements. // A benchmark has a name (string) and a function that will be run to // do the performance measurement. The optional setup and tearDown // arguments are functions that will be invoked before and after // running the benchmark, but the running time of these functions will // not be accounted for in the benchmark score. function Benchmark(name, run, setup, tearDown) { this.name = name; this.run = run; this.Setup = setup ? setup : function() { }; this.TearDown = tearDown ? tearDown : function() { }; } // Benchmark results hold the benchmark and the measured time used to // run the benchmark. The benchmark score is computed later once a // full benchmark suite has run to completion. function BenchmarkResult(benchmark, time) { this.benchmark = benchmark; this.time = time; } // Automatically convert results to numbers. Used by the geometric // mean computation. BenchmarkResult.prototype.valueOf = function() { return this.time; } // Suites of benchmarks consist of a name and the set of benchmarks in // addition to the reference timing that the final score will be based // on. This way, all scores are relative to a reference run and higher // scores implies better performance. function BenchmarkSuite(name, reference, benchmarks) { this.name = name; this.reference = reference; this.benchmarks = benchmarks; BenchmarkSuite.suites.push(this); } // Keep track of all declared benchmark suites. BenchmarkSuite.suites = []; // Scores are not comparable across versions. Bump the version if // you're making changes that will affect that scores, e.g. if you add // a new benchmark or change an existing one. BenchmarkSuite.version = '6'; // To make the benchmark results predictable, we replace Math.random // with a 100% deterministic alternative. Math.random = (function() { var seed = 49734321; return function() { // Robert Jenkins' 32 bit integer hash function. seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; return (seed & 0xfffffff) / 0x10000000; }; })(); // Runs all registered benchmark suites and optionally yields between // each individual benchmark to avoid running for too long in the // context of browsers. Once done, the final score is reported to the // runner. BenchmarkSuite.RunSuites = function(runner) { var continuation = null; var suites = BenchmarkSuite.suites; var length = suites.length; BenchmarkSuite.scores = []; var index = 0; function RunStep() { while (continuation || index < length) { if (continuation) { continuation = continuation(); } else { var suite = suites[index++]; if (runner.NotifyStart) runner.NotifyStart(suite.name); continuation = suite.RunStep(runner); } if (continuation && typeof window != 'undefined' && window.setTimeout) { window.setTimeout(RunStep, 25); return; } } if (runner.NotifyScore) { var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores); var formatted = BenchmarkSuite.FormatScore(100 * score); runner.NotifyScore(formatted); } } RunStep(); } // Counts the total number of registered benchmarks. Useful for // showing progress as a percentage. BenchmarkSuite.CountBenchmarks = function() { var result = 0; var suites = BenchmarkSuite.suites; for (var i = 0; i < suites.length; i++) { result += suites[i].benchmarks.length; } return result; } // Computes the geometric mean of a set of numbers. BenchmarkSuite.GeometricMean = function(numbers) { var log = 0; for (var i = 0; i < numbers.length; i++) { log += Math.log(numbers[i]); } return Math.pow(Math.E, log / numbers.length); } // Converts a score value to a string with at least three significant // digits. BenchmarkSuite.FormatScore = function(value) { if (value > 100) { return value.toFixed(0); } else { return value.toPrecision(3); } } // Notifies the runner that we're done running a single benchmark in // the benchmark suite. This can be useful to report progress. BenchmarkSuite.prototype.NotifyStep = function(result) { this.results.push(result); if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name); } // Notifies the runner that we're done with running a suite and that // we have a result which can be reported to the user if needed. BenchmarkSuite.prototype.NotifyResult = function() { var mean = BenchmarkSuite.GeometricMean(this.results); var score = this.reference / mean; BenchmarkSuite.scores.push(score); if (this.runner.NotifyResult) { var formatted = BenchmarkSuite.FormatScore(100 * score); this.runner.NotifyResult(this.name, formatted); } } // Notifies the runner that running a benchmark resulted in an error. BenchmarkSuite.prototype.NotifyError = function(error) { if (this.runner.NotifyError) { this.runner.NotifyError(this.name, error); } if (this.runner.NotifyStep) { this.runner.NotifyStep(this.name); } } var MIN_TIME = 10000; // Runs a single benchmark for at least MIN_TIME milliseconds and computes the // average time it takes to run a single iteration. BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) { function Measure(data) { var elapsed = 0; var start = new Date(); for (var n = 0; elapsed < MIN_TIME; n++) { benchmark.run(); elapsed = new Date() - start; } if (data != null) { data.runs += n; data.elapsed += elapsed; } } if (data == null) { // Measure the benchmark once for warm up and throw the result // away. Return a fresh data object. Measure(null); return { runs: 0, elapsed: 0 }; } else { Measure(data); // If we've run too few iterations, we continue for another second. if (data.runs < 32) return data; var usec = (data.elapsed * MIN_TIME) / data.runs; this.NotifyStep(new BenchmarkResult(benchmark, usec)); return null; } } // This function starts running a suite, but stops between each // individual benchmark in the suite and returns a continuation // function which can be invoked to run the next benchmark. Once the // last benchmark has been executed, null is returned. BenchmarkSuite.prototype.RunStep = function(runner) { this.results = []; this.runner = runner; var length = this.benchmarks.length; var index = 0; var suite = this; var data; // Run the setup, the actual benchmark, and the tear down in three // separate steps to allow the framework to yield between any of the // steps. function RunNextSetup() { if (index < length) { try { suite.benchmarks[index].Setup(); } catch (e) { suite.NotifyError(e); return null; } return RunNextBenchmark; } suite.NotifyResult(); return null; } function RunNextBenchmark() { try { data = suite.RunSingleBenchmark(suite.benchmarks[index], data); } catch (e) { suite.NotifyError(e); return null; } // If data is null, we're done with this benchmark. return (data == null) ? RunNextTearDown : RunNextBenchmark(); } function RunNextTearDown() { try { suite.benchmarks[index++].TearDown(); } catch (e) { suite.NotifyError(e); return null; } return RunNextSetup; } // Start out running the setup. return RunNextSetup(); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/raytrace.js0000644000175000017500000006653414433667662026513 0ustar apoapo// The ray tracer code in this file is written by Adam Burmister. It // is available in its original form from: // // http://labs.flog.nz.co/raytracer/ // // It has been modified slightly by Google to work as a standalone // benchmark, but the all the computational code remains // untouched. This file also contains a copy of parts of the Prototype // JavaScript framework which is used by the ray tracer. var RayTrace = new BenchmarkSuite('RayTrace', 739989, [ new Benchmark('RayTrace', renderScene) ]); // Variable used to hold a number that can be used to verify that // the scene was ray traced correctly. var checkNumber; // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // The following is a copy of parts of the Prototype JavaScript library: // Prototype JavaScript framework, version 1.5.0 // (c) 2005-2007 Sam Stephenson // // Prototype is freely distributable under the terms of an MIT-style license. // For details, see the Prototype web site: http://prototype.conio.net/ var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } }; Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; }; // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // The rest of this file is the actual ray tracer written by Adam // Burmister. It's a concatenation of the following files: // // flog/color.js // flog/light.js // flog/vector.js // flog/ray.js // flog/scene.js // flog/material/basematerial.js // flog/material/solid.js // flog/material/chessboard.js // flog/shape/baseshape.js // flog/shape/sphere.js // flog/shape/plane.js // flog/intersectioninfo.js // flog/camera.js // flog/background.js // flog/engine.js /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Color = Class.create(); Flog.RayTracer.Color.prototype = { red : 0.0, green : 0.0, blue : 0.0, initialize : function(r, g, b) { if(!r) r = 0.0; if(!g) g = 0.0; if(!b) b = 0.0; this.red = r; this.green = g; this.blue = b; }, add : function(c1, c2){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red + c2.red; result.green = c1.green + c2.green; result.blue = c1.blue + c2.blue; return result; }, addScalar: function(c1, s){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red + s; result.green = c1.green + s; result.blue = c1.blue + s; result.limit(); return result; }, subtract: function(c1, c2){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red - c2.red; result.green = c1.green - c2.green; result.blue = c1.blue - c2.blue; return result; }, multiply : function(c1, c2) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red * c2.red; result.green = c1.green * c2.green; result.blue = c1.blue * c2.blue; return result; }, multiplyScalar : function(c1, f) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red * f; result.green = c1.green * f; result.blue = c1.blue * f; return result; }, divideFactor : function(c1, f) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red / f; result.green = c1.green / f; result.blue = c1.blue / f; return result; }, limit: function(){ this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0; this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0; this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0; }, distance : function(color) { var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue); return d; }, blend: function(c1, c2, w){ var result = new Flog.RayTracer.Color(0,0,0); result = Flog.RayTracer.Color.prototype.add( Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w), Flog.RayTracer.Color.prototype.multiplyScalar(c2, w) ); return result; }, brightness : function() { var r = Math.floor(this.red*255); var g = Math.floor(this.green*255); var b = Math.floor(this.blue*255); return (r * 77 + g * 150 + b * 29) >> 8; }, toString : function () { var r = Math.floor(this.red*255); var g = Math.floor(this.green*255); var b = Math.floor(this.blue*255); return "rgb("+ r +","+ g +","+ b +")"; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Light = Class.create(); Flog.RayTracer.Light.prototype = { position: null, color: null, intensity: 10.0, initialize : function(pos, color, intensity) { this.position = pos; this.color = color; this.intensity = (intensity ? intensity : 10.0); }, toString : function () { return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Vector = Class.create(); Flog.RayTracer.Vector.prototype = { x : 0.0, y : 0.0, z : 0.0, initialize : function(x, y, z) { this.x = (x ? x : 0); this.y = (y ? y : 0); this.z = (z ? z : 0); }, copy: function(vector){ this.x = vector.x; this.y = vector.y; this.z = vector.z; }, normalize : function() { var m = this.magnitude(); return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m); }, magnitude : function() { return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z)); }, cross : function(w) { return new Flog.RayTracer.Vector( -this.z * w.y + this.y * w.z, this.z * w.x - this.x * w.z, -this.y * w.x + this.x * w.y); }, dot : function(w) { return this.x * w.x + this.y * w.y + this.z * w.z; }, add : function(v, w) { return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z); }, subtract : function(v, w) { if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']'; return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z); }, multiplyVector : function(v, w) { return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z); }, multiplyScalar : function(v, w) { return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w); }, toString : function () { return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Ray = Class.create(); Flog.RayTracer.Ray.prototype = { position : null, direction : null, initialize : function(pos, dir) { this.position = pos; this.direction = dir; }, toString : function () { return 'Ray [' + this.position + ',' + this.direction + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Scene = Class.create(); Flog.RayTracer.Scene.prototype = { camera : null, shapes : [], lights : [], background : null, initialize : function() { this.camera = new Flog.RayTracer.Camera( new Flog.RayTracer.Vector(0,0,-5), new Flog.RayTracer.Vector(0,0,1), new Flog.RayTracer.Vector(0,1,0) ); this.shapes = new Array(); this.lights = new Array(); this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2); } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {}; Flog.RayTracer.Material.BaseMaterial = Class.create(); Flog.RayTracer.Material.BaseMaterial.prototype = { gloss: 2.0, // [0...infinity] 0 = matt transparency: 0.0, // 0=opaque reflection: 0.0, // [0...infinity] 0 = no reflection refraction: 0.50, hasTexture: false, initialize : function() { }, getColor: function(u, v){ }, wrapUp: function(t){ t = t % 2.0; if(t < -1) t += 2.0; if(t >= 1) t -= 2.0; return t; }, toString : function () { return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Material.Solid = Class.create(); Flog.RayTracer.Material.Solid.prototype = Object.extend( new Flog.RayTracer.Material.BaseMaterial(), { initialize : function(color, reflection, refraction, transparency, gloss) { this.color = color; this.reflection = reflection; this.transparency = transparency; this.gloss = gloss; this.hasTexture = false; }, getColor: function(u, v){ return this.color; }, toString : function () { return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } ); /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Material.Chessboard = Class.create(); Flog.RayTracer.Material.Chessboard.prototype = Object.extend( new Flog.RayTracer.Material.BaseMaterial(), { colorEven: null, colorOdd: null, density: 0.5, initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) { this.colorEven = colorEven; this.colorOdd = colorOdd; this.reflection = reflection; this.transparency = transparency; this.gloss = gloss; this.density = density; this.hasTexture = true; }, getColor: function(u, v){ var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density); if(t < 0.0) return this.colorEven; else return this.colorOdd; }, toString : function () { return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } ); /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; Flog.RayTracer.Shape.Sphere = Class.create(); Flog.RayTracer.Shape.Sphere.prototype = { initialize : function(pos, radius, material) { this.radius = radius; this.position = pos; this.material = material; }, intersect: function(ray){ var info = new Flog.RayTracer.IntersectionInfo(); info.shape = this; var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position); var B = dst.dot(ray.direction); var C = dst.dot(dst) - (this.radius * this.radius); var D = (B * B) - C; if(D > 0){ // intersection! info.isHit = true; info.distance = (-B) - Math.sqrt(D); info.position = Flog.RayTracer.Vector.prototype.add( ray.position, Flog.RayTracer.Vector.prototype.multiplyScalar( ray.direction, info.distance ) ); info.normal = Flog.RayTracer.Vector.prototype.subtract( info.position, this.position ).normalize(); info.color = this.material.getColor(0,0); } else { info.isHit = false; } return info; }, toString : function () { return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; Flog.RayTracer.Shape.Plane = Class.create(); Flog.RayTracer.Shape.Plane.prototype = { d: 0.0, initialize : function(pos, d, material) { this.position = pos; this.d = d; this.material = material; }, intersect: function(ray){ var info = new Flog.RayTracer.IntersectionInfo(); var Vd = this.position.dot(ray.direction); if(Vd == 0) return info; // no intersection var t = -(this.position.dot(ray.position) + this.d) / Vd; if(t <= 0) return info; info.shape = this; info.isHit = true; info.position = Flog.RayTracer.Vector.prototype.add( ray.position, Flog.RayTracer.Vector.prototype.multiplyScalar( ray.direction, t ) ); info.normal = this.position; info.distance = t; if(this.material.hasTexture){ var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x); var vV = vU.cross(this.position); var u = info.position.dot(vU); var v = info.position.dot(vV); info.color = this.material.getColor(u,v); } else { info.color = this.material.getColor(0,0); } return info; }, toString : function () { return 'Plane [' + this.position + ', d=' + this.d + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.IntersectionInfo = Class.create(); Flog.RayTracer.IntersectionInfo.prototype = { isHit: false, hitCount: 0, shape: null, position: null, normal: null, color: null, distance: null, initialize : function() { this.color = new Flog.RayTracer.Color(0,0,0); }, toString : function () { return 'Intersection [' + this.position + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Camera = Class.create(); Flog.RayTracer.Camera.prototype = { position: null, lookAt: null, equator: null, up: null, screen: null, initialize : function(pos, lookAt, up) { this.position = pos; this.lookAt = lookAt; this.up = up; this.equator = lookAt.normalize().cross(this.up); this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt); }, getRay: function(vx, vy){ var pos = Flog.RayTracer.Vector.prototype.subtract( this.screen, Flog.RayTracer.Vector.prototype.subtract( Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx), Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy) ) ); pos.y = pos.y * -1; var dir = Flog.RayTracer.Vector.prototype.subtract( pos, this.position ); var ray = new Flog.RayTracer.Ray(pos, dir.normalize()); return ray; }, toString : function () { return 'Ray []'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Background = Class.create(); Flog.RayTracer.Background.prototype = { color : null, ambience : 0.0, initialize : function(color, ambience) { this.color = color; this.ambience = ambience; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Engine = Class.create(); Flog.RayTracer.Engine.prototype = { canvas: null, /* 2d context we can render to */ initialize: function(options){ this.options = Object.extend({ canvasHeight: 100, canvasWidth: 100, pixelWidth: 2, pixelHeight: 2, renderDiffuse: false, renderShadows: false, renderHighlights: false, renderReflections: false, rayDepth: 2 }, options || {}); this.options.canvasHeight /= this.options.pixelHeight; this.options.canvasWidth /= this.options.pixelWidth; /* TODO: dynamically include other scripts */ }, setPixel: function(x, y, color){ var pxW, pxH; pxW = this.options.pixelWidth; pxH = this.options.pixelHeight; if (this.canvas) { this.canvas.fillStyle = color.toString(); this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH); } else { if (x === y) { checkNumber += color.brightness(); } // print(x * pxW, y * pxH, pxW, pxH); } }, renderScene: function(scene, canvas){ checkNumber = 0; /* Get canvas */ if (canvas) { this.canvas = canvas.getContext("2d"); } else { this.canvas = null; } var canvasHeight = this.options.canvasHeight; var canvasWidth = this.options.canvasWidth; for(var y=0; y < canvasHeight; y++){ for(var x=0; x < canvasWidth; x++){ var yp = y * 1.0 / canvasHeight * 2 - 1; var xp = x * 1.0 / canvasWidth * 2 - 1; var ray = scene.camera.getRay(xp, yp); var color = this.getPixelColor(ray, scene); this.setPixel(x, y, color); } } if (checkNumber !== 2321) { throw new Error("Scene rendered incorrectly"); } }, getPixelColor: function(ray, scene){ var info = this.testIntersection(ray, scene, null); if(info.isHit){ var color = this.rayTrace(info, ray, scene, 0); return color; } return scene.background.color; }, testIntersection: function(ray, scene, exclude){ var hits = 0; var best = new Flog.RayTracer.IntersectionInfo(); best.distance = 2000; for(var i=0; i= 0 && info.distance < best.distance){ best = info; hits++; } } } best.hitCount = hits; return best; }, getReflectionRay: function(P,N,V){ var c1 = -N.dot(V); var R1 = Flog.RayTracer.Vector.prototype.add( Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1), V ); return new Flog.RayTracer.Ray(P, R1); }, rayTrace: function(info, ray, scene, depth){ // Calc ambient var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience); var oldColor = color; var shininess = Math.pow(10, info.shape.material.gloss + 1); for(var i=0; i 0.0){ color = Flog.RayTracer.Color.prototype.add( color, Flog.RayTracer.Color.prototype.multiply( info.color, Flog.RayTracer.Color.prototype.multiplyScalar( light.color, L ) ) ); } } // The greater the depth the more accurate the colours, but // this is exponentially (!) expensive if(depth <= this.options.rayDepth){ // calculate reflection ray if(this.options.renderReflections && info.shape.material.reflection > 0) { var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction); var refl = this.testIntersection(reflectionRay, scene, info.shape); if (refl.isHit && refl.distance > 0){ refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1); } else { refl.color = scene.background.color; } color = Flog.RayTracer.Color.prototype.blend( color, refl.color, info.shape.material.reflection ); } // Refraction /* TODO */ } /* Render shadows and highlights */ var shadowInfo = new Flog.RayTracer.IntersectionInfo(); if(this.options.renderShadows){ var shadowRay = new Flog.RayTracer.Ray(info.position, v); shadowInfo = this.testIntersection(shadowRay, scene, info.shape); if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){ var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5); var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5)); color = Flog.RayTracer.Color.prototype.addScalar(vA,dB); } } // Phong specular highlights if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){ var Lv = Flog.RayTracer.Vector.prototype.subtract( info.shape.position, light.position ).normalize(); var E = Flog.RayTracer.Vector.prototype.subtract( scene.camera.position, info.shape.position ).normalize(); var H = Flog.RayTracer.Vector.prototype.subtract( E, Lv ).normalize(); var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess); color = Flog.RayTracer.Color.prototype.add( Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight), color ); } } color.limit(); return color; } }; function renderScene(){ var scene = new Flog.RayTracer.Scene(); scene.camera = new Flog.RayTracer.Camera( new Flog.RayTracer.Vector(0, 0, -15), new Flog.RayTracer.Vector(-0.2, 0, 5), new Flog.RayTracer.Vector(0, 1, 0) ); scene.background = new Flog.RayTracer.Background( new Flog.RayTracer.Color(0.5, 0.5, 0.5), 0.4 ); var sphere = new Flog.RayTracer.Shape.Sphere( new Flog.RayTracer.Vector(-1.5, 1.5, 2), 1.5, new Flog.RayTracer.Material.Solid( new Flog.RayTracer.Color(0,0.5,0.5), 0.3, 0.0, 0.0, 2.0 ) ); var sphere1 = new Flog.RayTracer.Shape.Sphere( new Flog.RayTracer.Vector(1, 0.25, 1), 0.5, new Flog.RayTracer.Material.Solid( new Flog.RayTracer.Color(0.9,0.9,0.9), 0.1, 0.0, 0.0, 1.5 ) ); var plane = new Flog.RayTracer.Shape.Plane( new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(), 1.2, new Flog.RayTracer.Material.Chessboard( new Flog.RayTracer.Color(1,1,1), new Flog.RayTracer.Color(0,0,0), 0.2, 0.0, 1.0, 0.7 ) ); scene.shapes.push(plane); scene.shapes.push(sphere); scene.shapes.push(sphere1); var light = new Flog.RayTracer.Light( new Flog.RayTracer.Vector(5, 10, -1), new Flog.RayTracer.Color(0.8, 0.8, 0.8) ); var light1 = new Flog.RayTracer.Light( new Flog.RayTracer.Vector(-3, 5, -15), new Flog.RayTracer.Color(0.8, 0.8, 0.8), 100 ); scene.lights.push(light); scene.lights.push(light1); var imageWidth = 100; // $F('imageWidth'); var imageHeight = 100; // $F('imageHeight'); var pixelSize = "5,5".split(','); // $F('pixelSize').split(','); var renderDiffuse = true; // $F('renderDiffuse'); var renderShadows = true; // $F('renderShadows'); var renderHighlights = true; // $F('renderHighlights'); var renderReflections = true; // $F('renderReflections'); var rayDepth = 2;//$F('rayDepth'); var raytracer = new Flog.RayTracer.Engine( { canvasWidth: imageWidth, canvasHeight: imageHeight, pixelWidth: pixelSize[0], pixelHeight: pixelSize[1], "renderDiffuse": renderDiffuse, "renderHighlights": renderHighlights, "renderShadows": renderShadows, "renderReflections": renderReflections, "rayDepth": rayDepth } ); raytracer.renderScene(scene, null, 0); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/regexp.js0000644000175000017500000033130714433667662026164 0ustar apoapo// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Automatically generated on 2009-01-30. Manually updated on 2010-09-17. // This benchmark is generated by loading 50 of the most popular pages // on the web and logging all regexp operations performed. Each // operation is given a weight that is calculated from an estimate of // the popularity of the pages where it occurs and the number of times // it is executed while loading each page. Furthermore the literal // letters in the data are encoded using ROT13 in a way that does not // affect how the regexps match their input. Finally the strings are // scrambled to exercise the regexp engine on different input strings. var RegExp = new BenchmarkSuite('RegExp', 910985, [ new Benchmark("RegExp", RegExpRun, RegExpSetup, RegExpTearDown) ]); var regExpBenchmark = null; function RegExpSetup() { regExpBenchmark = new RegExpBenchmark(); RegExpRun(); // run once to get system initialized } function RegExpRun() { regExpBenchmark.run(); } function RegExpTearDown() { regExpBenchmark = null; } // Returns an array of n different variants of the input string str. // The variants are computed by randomly rotating one random // character. function computeInputVariants(str, n) { var variants = [ str ]; for (var i = 1; i < n; i++) { var pos = Math.floor(Math.random() * str.length); var chr = String.fromCharCode((str.charCodeAt(pos) + Math.floor(Math.random() * 128)) % 128); variants[i] = str.substring(0, pos) + chr + str.substring(pos + 1, str.length); } return variants; } function RegExpBenchmark() { var re0 = /^ba/; var re1 = /(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/; var re2 = /^\s*|\s*$/g; var re3 = /\bQBZPbageby_cynprubyqre\b/; var re4 = /,/; var re5 = /\bQBZPbageby_cynprubyqre\b/g; var re6 = /^[\s\xa0]+|[\s\xa0]+$/g; var re7 = /(\d*)(\D*)/g; var re8 = /=/; var re9 = /(^|\s)lhv\-h(\s|$)/; var str0 = 'Zbmvyyn/5.0 (Jvaqbjf; H; Jvaqbjf AG 5.1; ra-HF) NccyrJroXvg/528.9 (XUGZY, yvxr Trpxb) Puebzr/2.0.157.0 Fnsnev/528.9'; var re10 = /\#/g; var re11 = /\./g; var re12 = /'/g; var re13 = /\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i; var str1 = 'Fubpxjnir Synfu 9.0 e115'; var re14 = /\s+/g; var re15 = /^\s*(\S*(\s+\S+)*)\s*$/; var re16 = /(-[a-z])/i; var s0 = computeInputVariants('pyvpx', 6511); var s1 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 1844); var s2 = computeInputVariants('QBZPbageby_cynprubyqre', 739); var s3 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 598); var s4 = computeInputVariants('uggc://jjj.snprobbx.pbz/fepu.cuc', 454); var s5 = computeInputVariants('qqqq, ZZZ q, llll', 352); var s6 = computeInputVariants('vachggrkg QBZPbageby_cynprubyqre', 312); var s7 = computeInputVariants('/ZlFcnprUbzrcntr/Vaqrk-FvgrUbzr,10000000', 282); var s8 = computeInputVariants('vachggrkg', 177); var s9 = computeInputVariants('528.9', 170); var s10 = computeInputVariants('528', 170); var s11 = computeInputVariants('VCPhygher=ra-HF', 156); var s12 = computeInputVariants('CersreerqPhygher=ra-HF', 156); var s13 = computeInputVariants('xrlcerff', 144); var s14 = computeInputVariants('521', 139); var s15 = computeInputVariants(str0, 139); var s16 = computeInputVariants('qvi .so_zrah', 137); var s17 = computeInputVariants('qvi.so_zrah', 137); var s18 = computeInputVariants('uvqqra_ryrz', 117); var s19 = computeInputVariants('sevraqfgre_naba=nvq%3Qn6ss9p85n868ro9s059pn854735956o3%26ers%3Q%26df%3Q%26vpgl%3QHF', 95); var s20 = computeInputVariants('uggc://ubzr.zlfcnpr.pbz/vaqrk.psz', 93); var s21 = computeInputVariants(str1, 92); var s22 = computeInputVariants('svefg', 85); var s23 = computeInputVariants('uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz', 85); var s24 = computeInputVariants('ynfg', 85); var s25 = computeInputVariants('qvfcynl', 85); function runBlock0() { for (var i = 0; i < 6511; i++) { re0.exec(s0[i]); } for (var i = 0; i < 1844; i++) { re1.exec(s1[i]); } for (var i = 0; i < 739; i++) { s2[i].replace(re2, ''); } for (var i = 0; i < 598; i++) { re1.exec(s3[i]); } for (var i = 0; i < 454; i++) { re1.exec(s4[i]); } for (var i = 0; i < 352; i++) { /qqqq|qqq|qq|q|ZZZZ|ZZZ|ZZ|Z|llll|ll|l|uu|u|UU|U|zz|z|ff|f|gg|g|sss|ss|s|mmm|mm|m/g.exec(s5[i]); } for (var i = 0; i < 312; i++) { re3.exec(s6[i]); } for (var i = 0; i < 282; i++) { re4.exec(s7[i]); } for (var i = 0; i < 177; i++) { s8[i].replace(re5, ''); } for (var i = 0; i < 170; i++) { s9[i].replace(re6, ''); re7.exec(s10[i]); } for (var i = 0; i < 156; i++) { re8.exec(s11[i]); re8.exec(s12[i]); } for (var i = 0; i < 144; i++) { re0.exec(s13[i]); } for (var i = 0; i < 139; i++) { s14[i].replace(re6, ''); re7.exec(s14[i]); re9.exec(''); /JroXvg\/(\S+)/.exec(s15[i]); } for (var i = 0; i < 137; i++) { s16[i].replace(re10, ''); s16[i].replace(/\[/g, ''); s17[i].replace(re11, ''); } for (var i = 0; i < 117; i++) { s18[i].replace(re2, ''); } for (var i = 0; i < 95; i++) { /(?:^|;)\s*sevraqfgre_ynat=([^;]*)/.exec(s19[i]); } for (var i = 0; i < 93; i++) { s20[i].replace(re12, ''); re13.exec(s20[i]); } for (var i = 0; i < 92; i++) { s21[i].replace(/([a-zA-Z]|\s)+/, ''); } for (var i = 0; i < 85; i++) { s22[i].replace(re14, ''); s22[i].replace(re15, ''); s23[i].replace(re12, ''); s24[i].replace(re14, ''); s24[i].replace(re15, ''); re16.exec(s25[i]); re13.exec(s23[i]); } } var re17 = /(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g; var str2 = '{"anzr":"","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":gehr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"\xa4","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":gehr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, qq ZZZZ llll UU:zz:ff","YbatQngrCnggrea":"qqqq, qq ZZZZ llll","YbatGvzrCnggrea":"UU:zz:ff","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"ZZ/qq/llll","FubegGvzrCnggrea":"UU:zz","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"llll ZZZZ","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":gehr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; var str3 = '{"anzr":"ra-HF","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":snyfr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"$","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":snyfr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, ZZZZ qq, llll u:zz:ff gg","YbatQngrCnggrea":"qqqq, ZZZZ qq, llll","YbatGvzrCnggrea":"u:zz:ff gg","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"Z/q/llll","FubegGvzrCnggrea":"u:zz gg","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"ZZZZ, llll","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":snyfr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; var str4 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str5 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var re18 = /^\s+|\s+$/g; var str6 = 'uggc://jjj.snprobbx.pbz/vaqrk.cuc'; var re19 = /(?:^|\s+)ba(?:\s+|$)/; var re20 = /[+, ]/; var re21 = /ybnqrq|pbzcyrgr/; var str7 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(d1)c=d1.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+d1.Cnaryf[c].Jvqgu,o=g+d1.Cnaryf[c].Urvtug;vs((pheKe)||(pheLo)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(d1&&d1.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(d1)d1.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(d1)d1.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;d1.IjTc=d2(n,c){ine nq=d1;vs(vfAnA(c)){sbe(ine v=0;v0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;d1.IjYvzvg0=d2(n,f){ine nq=d1,vh=f.fcyvg("/");sbe(ine v=0;v0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;d1.IjRVST=d2(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",d1.rvsg);};;d1.IjNavzSHC=d2(n,c){ine nq=d1;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ 0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;d1.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;d1.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c]/g; var str15 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=44132r503660'; var str16 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; AFP_zp_dfctwzs-aowb_80=44132r503660; __hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1; __hgzo=144631658.0.10.1231363638; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str17 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363621014&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363621014&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=348699119.1231363624&tn_fvq=1231363624&tn_uvq=895511034&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str18 = 'uggc://jjj.yrobapbva.se/yv'; var str19 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str20 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var s67 = computeInputVariants('e115', 27); var s68 = computeInputVariants('qvfcynl', 27); var s69 = computeInputVariants('cbfvgvba', 27); var s70 = computeInputVariants('uggc://jjj.zlfcnpr.pbz/', 27); var s71 = computeInputVariants('cntrivrj', 27); var s72 = computeInputVariants('VC=74.125.75.3', 27); var s73 = computeInputVariants('ra', 27); var s74 = computeInputVariants(str10, 27); var s75 = computeInputVariants(str11, 27); var s76 = computeInputVariants(str12, 27); var s77 = computeInputVariants(str17, 27); var s78 = computeInputVariants(str18, 27); function runBlock3() { for (var i = 0; i < 27; i++) { s67[i].replace(/[A-Za-z]/g, ''); } for (var i = 0; i < 23; i++) { s68[i].replace(re27, ''); s69[i].replace(re27, ''); } for (var i = 0; i < 22; i++) { 'unaqyr'.replace(re14, ''); 'unaqyr'.replace(re15, ''); 'yvar'.replace(re14, ''); 'yvar'.replace(re15, ''); 'cnerag puebzr6 fvatyr1 gno'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno'.replace(re15, ''); 'fyvqre'.replace(re14, ''); 'fyvqre'.replace(re15, ''); re28.exec(''); } for (var i = 0; i < 21; i++) { s70[i].replace(re12, ''); re13.exec(s70[i]); } for (var i = 0; i < 20; i++) { s71[i].replace(re29, ''); s71[i].replace(re30, ''); re19.exec('ynfg'); re19.exec('ba svefg'); re8.exec(s72[i]); } for (var i = 0; i < 19; i++) { re31.exec(s73[i]); } for (var i = 0; i < 18; i++) { s74[i].split(re32); s75[i].split(re32); s76[i].replace(re33, ''); re8.exec('144631658.0.10.1231363570'); re8.exec('144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); re8.exec(str13); re8.exec(str14); re8.exec('__hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); re8.exec('__hgzo=144631658.0.10.1231363570'); re8.exec('__hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(s74[i]); re34.exec(s75[i]); } for (var i = 0; i < 17; i++) { s15[i].match(/zfvr/gi); s15[i].match(/bcren/gi); str15.split(re32); str16.split(re32); 'ohggba'.replace(re14, ''); 'ohggba'.replace(re15, ''); 'puvyq p1 svefg sylbhg pybfrq'.replace(re14, ''); 'puvyq p1 svefg sylbhg pybfrq'.replace(re15, ''); 'pvgvrf'.replace(re14, ''); 'pvgvrf'.replace(re15, ''); 'pybfrq'.replace(re14, ''); 'pybfrq'.replace(re15, ''); 'qry'.replace(re14, ''); 'qry'.replace(re15, ''); 'uqy_zba'.replace(re14, ''); 'uqy_zba'.replace(re15, ''); s77[i].replace(re33, ''); s78[i].replace(/%3P/g, ''); s78[i].replace(/%3R/g, ''); s78[i].replace(/%3q/g, ''); s78[i].replace(re35, ''); 'yvaxyvfg16'.replace(re14, ''); 'yvaxyvfg16'.replace(re15, ''); 'zvahf'.replace(re14, ''); 'zvahf'.replace(re15, ''); 'bcra'.replace(re14, ''); 'bcra'.replace(re15, ''); 'cnerag puebzr5 fvatyr1 ps NU'.replace(re14, ''); 'cnerag puebzr5 fvatyr1 ps NU'.replace(re15, ''); 'cynlre'.replace(re14, ''); 'cynlre'.replace(re15, ''); 'cyhf'.replace(re14, ''); 'cyhf'.replace(re15, ''); 'cb_uqy'.replace(re14, ''); 'cb_uqy'.replace(re15, ''); 'hyJVzt'.replace(re14, ''); 'hyJVzt'.replace(re15, ''); re8.exec('144631658.0.10.1231363638'); re8.exec('144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.965867047679498800.1231363638.1231363638.1231363638.1'); re8.exec('4413268q3660'); re8.exec('4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); re8.exec('SbeprqRkcvengvba=633669321699093060'); re8.exec('VC=74.125.75.20'); re8.exec(str19); re8.exec(str20); re8.exec('AFP_zp_tfwsbrg-aowb_80=4413268q3660'); re8.exec('FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); re8.exec('__hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1'); re8.exec('__hgzo=144631658.0.10.1231363638'); re8.exec('__hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str15); re34.exec(str16); } } var re36 = /uers|fep|fryrpgrq/; var re37 = /\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g; var re38 = /^(\w+|\*)$/; var str21 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str22 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; __hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1; __hgzo=144631658.0.10.1231367822; __hgzp=144631658; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str23 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367803797&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367803797&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Szrffntvat.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1192552091.1231367807&tn_fvq=1231367807&tn_uvq=1155446857&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str24 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str25 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str26 = 'hy.ynat-fryrpgbe'; var re39 = /\\/g; var re40 = / /g; var re41 = /\/\xc4\/t/; var re42 = /\/\xd6\/t/; var re43 = /\/\xdc\/t/; var re44 = /\/\xdf\/t/; var re45 = /\/\xe4\/t/; var re46 = /\/\xf6\/t/; var re47 = /\/\xfc\/t/; var re48 = /\W/g; var re49 = /uers|fep|fglyr/; var s79 = computeInputVariants(str21, 16); var s80 = computeInputVariants(str22, 16); var s81 = computeInputVariants(str23, 16); var s82 = computeInputVariants(str26, 16); function runBlock4() { for (var i = 0; i < 16; i++) { ''.replace(/\*/g, ''); /\bnpgvir\b/.exec('npgvir'); /sversbk/i.exec(s15[i]); re36.exec('glcr'); /zfvr/i.exec(s15[i]); /bcren/i.exec(s15[i]); } for (var i = 0; i < 15; i++) { s79[i].split(re32); s80[i].split(re32); 'uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); s81[i].replace(re33, ''); 'yv'.replace(re37, ''); 'yv'.replace(re18, ''); re8.exec('144631658.0.10.1231367822'); re8.exec('144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); re8.exec(str24); re8.exec(str25); re8.exec('__hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); re8.exec('__hgzo=144631658.0.10.1231367822'); re8.exec('__hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(s79[i]); re34.exec(s80[i]); /\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g.exec(s82[i]); re13.exec('uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'); re38.exec('yv'); } for (var i = 0; i < 14; i++) { ''.replace(re18, ''); '9.0 e115'.replace(/(\s+e|\s+o[0-9]+)/, ''); 'Funer guvf tnqtrg'.replace(//g, ''); 'Funer guvf tnqtrg'.replace(re39, ''); 'uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); 'grnfre'.replace(re40, ''); 'grnfre'.replace(re41, ''); 'grnfre'.replace(re42, ''); 'grnfre'.replace(re43, ''); 'grnfre'.replace(re44, ''); 'grnfre'.replace(re45, ''); 'grnfre'.replace(re46, ''); 'grnfre'.replace(re47, ''); 'grnfre'.replace(re48, ''); re16.exec('znetva-gbc'); re16.exec('cbfvgvba'); re19.exec('gno1'); re9.exec('qz'); re9.exec('qg'); re9.exec('zbqobk'); re9.exec('zbqobkva'); re9.exec('zbqgvgyr'); re13.exec('uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'); re26.exec('/vt/znvytnqtrg'); re49.exec('glcr'); } } var re50 = /(?:^|\s+)fryrpgrq(?:\s+|$)/; var re51 = /\&/g; var re52 = /\+/g; var re53 = /\?/g; var re54 = /\t/g; var re55 = /(\$\{nqiHey\})|(\$nqiHey\b)/g; var re56 = /(\$\{cngu\})|(\$cngu\b)/g; function runBlock5() { for (var i = 0; i < 13; i++) { 'purpx'.replace(re14, ''); 'purpx'.replace(re15, ''); 'pvgl'.replace(re14, ''); 'pvgl'.replace(re15, ''); 'qrpe fyvqrgrkg'.replace(re14, ''); 'qrpe fyvqrgrkg'.replace(re15, ''); 'svefg fryrpgrq'.replace(re14, ''); 'svefg fryrpgrq'.replace(re15, ''); 'uqy_rag'.replace(re14, ''); 'uqy_rag'.replace(re15, ''); 'vape fyvqrgrkg'.replace(re14, ''); 'vape fyvqrgrkg'.replace(re15, ''); 'vachggrkg QBZPbageby_cynprubyqre'.replace(re5, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re15, ''); 'cb_guz'.replace(re14, ''); 'cb_guz'.replace(re15, ''); 'fhozvg'.replace(re14, ''); 'fhozvg'.replace(re15, ''); re50.exec(''); /NccyrJroXvg\/([^\s]*)/.exec(s15[i]); /XUGZY/.exec(s15[i]); } for (var i = 0; i < 12; i++) { '${cebg}://${ubfg}${cngu}/${dz}'.replace(/(\$\{cebg\})|(\$cebg\b)/g, ''); '1'.replace(re40, ''); '1'.replace(re10, ''); '1'.replace(re51, ''); '1'.replace(re52, ''); '1'.replace(re53, ''); '1'.replace(re39, ''); '1'.replace(re54, ''); '9.0 e115'.replace(/^(.*)\..*$/, ''); '9.0 e115'.replace(/^.*e(.*)$/, ''); ''.replace(re55, ''); ''.replace(re55, ''); s21[i].replace(/^.*\s+(\S+\s+\S+$)/, ''); 'tzk%2Subzrcntr%2Sfgneg%2Sqr%2S'.replace(re30, ''); 'tzk'.replace(re30, ''); 'uggc://${ubfg}${cngu}/${dz}'.replace(/(\$\{ubfg\})|(\$ubfg\b)/g, ''); 'uggc://nqpyvrag.hvzfrei.arg${cngu}/${dz}'.replace(re56, ''); 'uggc://nqpyvrag.hvzfrei.arg/wf.at/${dz}'.replace(/(\$\{dz\})|(\$dz\b)/g, ''); 'frpgvba'.replace(re29, ''); 'frpgvba'.replace(re30, ''); 'fvgr'.replace(re29, ''); 'fvgr'.replace(re30, ''); 'fcrpvny'.replace(re29, ''); 'fcrpvny'.replace(re30, ''); re36.exec('anzr'); /e/.exec('9.0 e115'); } } var re57 = /##yv4##/gi; var re58 = /##yv16##/gi; var re59 = /##yv19##/gi; var str27 = '##yv4##Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str28 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str29 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str30 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str31 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. ##N##Yrnea zber##/N##'; var str32 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. Yrnea zber##/N##'; var str33 = 'Bar Jvaqbjf Yvir VQ trgf lbh vagb Ubgznvy, Zrffratre, Kobk YVIR \u2014 naq bgure cynprf lbh frr #~#argjbexybtb#~#'; var re60 = /(?:^|\s+)bss(?:\s+|$)/; var re61 = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; var re62 = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; var str34 = '${1}://${2}${3}${4}${5}'; var str35 = ' O=6gnyg0g4znrrn&o=3&f=gc; Q=_lyu=K3bQZGSxnT4lZzD3OS9GNmV3ZGLkAQxRpTyxNmRlZmRmAmNkAQLRqTImqNZjOUEgpTjQnJ5xMKtgoN--; SCF=qy'; var s83 = computeInputVariants(str27, 11); var s84 = computeInputVariants(str28, 11); var s85 = computeInputVariants(str29, 11); var s86 = computeInputVariants(str30, 11); var s87 = computeInputVariants(str31, 11); var s88 = computeInputVariants(str32, 11); var s89 = computeInputVariants(str33, 11); var s90 = computeInputVariants(str34, 11); function runBlock6() { for (var i = 0; i < 11; i++) { s83[i].replace(/##yv0##/gi, ''); s83[i].replace(re57, ''); s84[i].replace(re58, ''); s85[i].replace(re59, ''); s86[i].replace(/##\/o##/gi, ''); s86[i].replace(/##\/v##/gi, ''); s86[i].replace(/##\/h##/gi, ''); s86[i].replace(/##o##/gi, ''); s86[i].replace(/##oe##/gi, ''); s86[i].replace(/##v##/gi, ''); s86[i].replace(/##h##/gi, ''); s87[i].replace(/##n##/gi, ''); s88[i].replace(/##\/n##/gi, ''); s89[i].replace(/#~#argjbexybtb#~#/g, ''); / Zbovyr\//.exec(s15[i]); /##yv1##/gi.exec(s83[i]); /##yv10##/gi.exec(s84[i]); /##yv11##/gi.exec(s84[i]); /##yv12##/gi.exec(s84[i]); /##yv13##/gi.exec(s84[i]); /##yv14##/gi.exec(s84[i]); /##yv15##/gi.exec(s84[i]); re58.exec(s84[i]); /##yv17##/gi.exec(s85[i]); /##yv18##/gi.exec(s85[i]); re59.exec(s85[i]); /##yv2##/gi.exec(s83[i]); /##yv20##/gi.exec(s86[i]); /##yv21##/gi.exec(s86[i]); /##yv22##/gi.exec(s86[i]); /##yv23##/gi.exec(s86[i]); /##yv3##/gi.exec(s83[i]); re57.exec(s83[i]); /##yv5##/gi.exec(s84[i]); /##yv6##/gi.exec(s84[i]); /##yv7##/gi.exec(s84[i]); /##yv8##/gi.exec(s84[i]); /##yv9##/gi.exec(s84[i]); re8.exec('473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); re8.exec('SbeprqRkcvengvba=633669325184628362'); re8.exec('FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); /AbxvnA[^\/]*/.exec(s15[i]); } for (var i = 0; i < 10; i++) { ' bss'.replace(/(?:^|\s+)bss(?:\s+|$)/g, ''); s90[i].replace(/(\$\{0\})|(\$0\b)/g, ''); s90[i].replace(/(\$\{1\})|(\$1\b)/g, ''); s90[i].replace(/(\$\{pbzcyrgr\})|(\$pbzcyrgr\b)/g, ''); s90[i].replace(/(\$\{sentzrag\})|(\$sentzrag\b)/g, ''); s90[i].replace(/(\$\{ubfgcbeg\})|(\$ubfgcbeg\b)/g, ''); s90[i].replace(re56, ''); s90[i].replace(/(\$\{cebgbpby\})|(\$cebgbpby\b)/g, ''); s90[i].replace(/(\$\{dhrel\})|(\$dhrel\b)/g, ''); 'nqfvmr'.replace(re29, ''); 'nqfvmr'.replace(re30, ''); 'uggc://${2}${3}${4}${5}'.replace(/(\$\{2\})|(\$2\b)/g, ''); 'uggc://wf.hv-cbegny.qr${3}${4}${5}'.replace(/(\$\{3\})|(\$3\b)/g, ''); 'arjf'.replace(re40, ''); 'arjf'.replace(re41, ''); 'arjf'.replace(re42, ''); 'arjf'.replace(re43, ''); 'arjf'.replace(re44, ''); 'arjf'.replace(re45, ''); 'arjf'.replace(re46, ''); 'arjf'.replace(re47, ''); 'arjf'.replace(re48, ''); / PC=i=(\d+)&oe=(.)/.exec(str35); re60.exec(' '); re60.exec(' bss'); re60.exec(''); re19.exec(' '); re19.exec('svefg ba'); re19.exec('ynfg vtaber'); re19.exec('ba'); re9.exec('scnq so '); re9.exec('zrqvgobk'); re9.exec('hsgy'); re9.exec('lhv-h'); /Fnsnev|Xbadhrebe|XUGZY/gi.exec(s15[i]); re61.exec('uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); re62.exec('#Ybtva_rznvy'); } } var re63 = /\{0\}/g; var str36 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_tfwsbrg-aowb_80=4413268q3660'; var str37 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; AFP_zp_tfwsbrg-aowb_80=4413268q3660; __hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1; __hgzo=144631658.0.10.1231364074; __hgzp=144631658; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str38 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231364057761&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364057761&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Ssevraqf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1667363813.1231364061&tn_fvq=1231364061&tn_uvq=1917563877&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str39 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str40 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var s91 = computeInputVariants(str36, 9); var s92 = computeInputVariants(str37, 9); var s93 = computeInputVariants(str38, 9); function runBlock7() { for (var i = 0; i < 9; i++) { '0'.replace(re40, ''); '0'.replace(re10, ''); '0'.replace(re51, ''); '0'.replace(re52, ''); '0'.replace(re53, ''); '0'.replace(re39, ''); '0'.replace(re54, ''); 'Lrf'.replace(re40, ''); 'Lrf'.replace(re10, ''); 'Lrf'.replace(re51, ''); 'Lrf'.replace(re52, ''); 'Lrf'.replace(re53, ''); 'Lrf'.replace(re39, ''); 'Lrf'.replace(re54, ''); } for (var i = 0; i < 8; i++) { 'Pybfr {0}'.replace(re63, ''); 'Bcra {0}'.replace(re63, ''); s91[i].split(re32); s92[i].split(re32); 'puvyq p1 svefg gnournqref'.replace(re14, ''); 'puvyq p1 svefg gnournqref'.replace(re15, ''); 'uqy_fcb'.replace(re14, ''); 'uqy_fcb'.replace(re15, ''); 'uvag'.replace(re14, ''); 'uvag'.replace(re15, ''); s93[i].replace(re33, ''); 'yvfg'.replace(re14, ''); 'yvfg'.replace(re15, ''); 'at_bhgre'.replace(re30, ''); 'cnerag puebzr5 qbhoyr2 NU'.replace(re14, ''); 'cnerag puebzr5 qbhoyr2 NU'.replace(re15, ''); 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re14, ''); 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re15, ''); 'cnerag puebzr6 fvatyr1'.replace(re14, ''); 'cnerag puebzr6 fvatyr1'.replace(re15, ''); 'cb_qrs'.replace(re14, ''); 'cb_qrs'.replace(re15, ''); 'gnopbagrag'.replace(re14, ''); 'gnopbagrag'.replace(re15, ''); 'iv_svefg_gvzr'.replace(re30, ''); /(^|.)(ronl|qri-ehf3.wbg)(|fgberf|zbgbef|yvirnhpgvbaf|jvxv|rkcerff|punggre).(pbz(|.nh|.pa|.ux|.zl|.ft|.oe|.zk)|pb(.hx|.xe|.am)|pn|qr|se|vg|ay|or|ng|pu|vr|va|rf|cy|cu|fr)$/i.exec('cntrf.ronl.pbz'); re8.exec('144631658.0.10.1231364074'); re8.exec('144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); re8.exec('4413241q3660'); re8.exec('SbeprqRkcvengvba=633669357391353591'); re8.exec(str39); re8.exec(str40); re8.exec('AFP_zp_kkk-gdzogv_80=4413241q3660'); re8.exec('FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); re8.exec('__hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); re8.exec('__hgzo=144631658.0.10.1231364074'); re8.exec('__hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); re34.exec(s91[i]); re34.exec(s92[i]); } } var re64 = /\b[a-z]/g; var re65 = /^uggc:\/\//; var re66 = /(?:^|\s+)qvfnoyrq(?:\s+|$)/; var str41 = 'uggc://cebsvyr.zlfcnpr.pbz/Zbqhyrf/Nccyvpngvbaf/Cntrf/Pnainf.nfck'; function runBlock8() { for (var i = 0; i < 7; i++) { s21[i].match(/\d+/g); 'nsgre'.replace(re64, ''); 'orsber'.replace(re64, ''); 'obggbz'.replace(re64, ''); 'ohvygva_jrngure.kzy'.replace(re65, ''); 'ohggba'.replace(re37, ''); 'ohggba'.replace(re18, ''); 'qngrgvzr.kzy'.replace(re65, ''); 'uggc://eff.paa.pbz/eff/paa_gbcfgbevrf.eff'.replace(re65, ''); 'vachg'.replace(re37, ''); 'vachg'.replace(re18, ''); 'vafvqr'.replace(re64, ''); 'cbvagre'.replace(re27, ''); 'cbfvgvba'.replace(/[A-Z]/g, ''); 'gbc'.replace(re27, ''); 'gbc'.replace(re64, ''); 'hy'.replace(re37, ''); 'hy'.replace(re18, ''); str26.replace(re37, ''); str26.replace(re18, ''); 'lbhghor_vtbbtyr/i2/lbhghor.kzy'.replace(re65, ''); 'm-vaqrk'.replace(re27, ''); /#([\w-]+)/.exec(str26); re16.exec('urvtug'); re16.exec('znetvaGbc'); re16.exec('jvqgu'); re19.exec('gno0 svefg ba'); re19.exec('gno0 ba'); re19.exec('gno4 ynfg'); re19.exec('gno4'); re19.exec('gno5'); re19.exec('gno6'); re19.exec('gno7'); re19.exec('gno8'); /NqborNVE\/([^\s]*)/.exec(s15[i]); /NccyrJroXvg\/([^ ]*)/.exec(s15[i]); /XUGZY/gi.exec(s15[i]); /^(?:obql|ugzy)$/i.exec('YV'); re38.exec('ohggba'); re38.exec('vachg'); re38.exec('hy'); re38.exec(str26); /^(\w+|\*)/.exec(str26); /znp|jva|yvahk/i.exec('Jva32'); /eton?\([\d\s,]+\)/.exec('fgngvp'); } for (var i = 0; i < 6; i++) { ''.replace(/\r/g, ''); '/'.replace(re40, ''); '/'.replace(re10, ''); '/'.replace(re51, ''); '/'.replace(re52, ''); '/'.replace(re53, ''); '/'.replace(re39, ''); '/'.replace(re54, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/{0}?[NDO]&{1}&{2}&[NDR]'.replace(re63, ''); str41.replace(re12, ''); 'uggc://jjj.snprobbx.pbz/fepu.cuc'.replace(re23, ''); 'freivpr'.replace(re40, ''); 'freivpr'.replace(re41, ''); 'freivpr'.replace(re42, ''); 'freivpr'.replace(re43, ''); 'freivpr'.replace(re44, ''); 'freivpr'.replace(re45, ''); 'freivpr'.replace(re46, ''); 'freivpr'.replace(re47, ''); 'freivpr'.replace(re48, ''); /((ZFVR\s+([6-9]|\d\d)\.))/.exec(s15[i]); re66.exec(''); re50.exec('fryrpgrq'); re8.exec('8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); re8.exec('SbeprqRkcvengvba=633669340386893867'); re8.exec('VC=74.125.75.17'); re8.exec('FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); /Xbadhrebe|Fnsnev|XUGZY/.exec(s15[i]); re13.exec(str41); re49.exec('unfsbphf'); } } var re67 = /zrah_byq/g; var str42 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str43 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; __hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1; __hgzo=144631658.0.10.1231364380; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str44 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_vzntrf_wf&qg=1231364373088&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364373088&punaary=svz_zlfcnpr_hfre-ivrj-pbzzragf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Spbzzrag.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1158737789.1231364375&tn_fvq=1231364375&tn_uvq=415520832&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str45 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str46 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var re68 = /^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/; var re69 = /\{1\}/g; var re70 = /\s+/; var re71 = /(\$\{4\})|(\$4\b)/g; var re72 = /(\$\{5\})|(\$5\b)/g; var re73 = /\{2\}/g; var re74 = /[^+>] [^+>]/; var re75 = /\bucpyv\s*=\s*([^;]*)/i; var re76 = /\bucuvqr\s*=\s*([^;]*)/i; var re77 = /\bucfie\s*=\s*([^;]*)/i; var re78 = /\bhfucjrn\s*=\s*([^;]*)/i; var re79 = /\bmvc\s*=\s*([^;]*)/i; var re80 = /^((?:[\w\u0128-\uffff*_-]|\\.)+)(#)((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re81 = /^([>+~])\s*(\w*)/i; var re82 = /^>\s*((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re83 = /^[\s[]?shapgvba/; var re84 = /v\/g.tvs#(.*)/i; var str47 = '#Zbq-Vasb-Vasb-WninFpevcgUvag'; var str48 = ',n.svryqOgaPnapry'; var str49 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_kkk-gdzogv_80=4413241q3660'; var str50 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; AFP_zp_kkk-gdzogv_80=4413241q3660; AFP_zp_kkk-aowb_80=4413235p3660; __hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1; __hgzo=144631658.0.10.1231367708; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str51 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367691141&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367691141&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sjjj.zlfcnpr.pbz%2S&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=320757904.1231367694&tn_fvq=1231367694&tn_uvq=1758792003&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str52 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N38%3N42%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=1024k768&p=24&x=L&oj=994&ou=634&uc=A&{2}&[NDR]'; var str53 = 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq qbhoyr2 ps'; var str54 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str55 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str56 = 'ne;ng;nh;or;oe;pn;pu;py;pa;qr;qx;rf;sv;se;to;ux;vq;vr;va;vg;wc;xe;zk;zl;ay;ab;am;cu;cy;cg;eh;fr;ft;gu;ge;gj;mn;'; var str57 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886&GHVQ=1'; var str58 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886'; var str59 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'; var str60 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF'; var str61 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/29/4RQP4969777N048NPS4RRR3PO2S7S.wct'; var str62 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/OQ/63NP9O94NS5OQP1249Q9S1ROP7NS3.wct'; var str63 = 'zbmvyyn/5.0 (jvaqbjf; h; jvaqbjf ag 5.1; ra-hf) nccyrjroxvg/528.9 (xugzy, yvxr trpxb) puebzr/2.0.157.0 fnsnev/528.9'; var s94 = computeInputVariants(str42, 5); var s95 = computeInputVariants(str43, 5); var s96 = computeInputVariants(str44, 5); var s97 = computeInputVariants(str47, 5); var s98 = computeInputVariants(str48, 5); var s99 = computeInputVariants(str49, 5); var s100 = computeInputVariants(str50, 5); var s101 = computeInputVariants(str51, 5); var s102 = computeInputVariants(str52, 5); var s103 = computeInputVariants(str53, 5); function runBlock9() { for (var i = 0; i < 5; i++) { s94[i].split(re32); s95[i].split(re32); 'svz_zlfcnpr_hfre-ivrj-pbzzragf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); s96[i].replace(re33, ''); 'zrah_arj zrah_arj_gbttyr zrah_gbttyr'.replace(re67, ''); 'zrah_byq zrah_byq_gbttyr zrah_gbttyr'.replace(re67, ''); re8.exec('102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); re8.exec('144631658.0.10.1231364380'); re8.exec('144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); re8.exec('441326q33660'); re8.exec('SbeprqRkcvengvba=633669341278771470'); re8.exec(str45); re8.exec(str46); re8.exec('AFP_zp_dfctwzssrwh-aowb_80=441326q33660'); re8.exec('FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); re8.exec('__hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); re8.exec('__hgzo=144631658.0.10.1231364380'); re8.exec('__hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); } for (var i = 0; i < 4; i++) { ' yvfg1'.replace(re14, ''); ' yvfg1'.replace(re15, ''); ' yvfg2'.replace(re14, ''); ' yvfg2'.replace(re15, ''); ' frneputebhc1'.replace(re14, ''); ' frneputebhc1'.replace(re15, ''); s97[i].replace(re68, ''); s97[i].replace(re18, ''); ''.replace(/&/g, ''); ''.replace(re35, ''); '(..-{0})(\|(\d+)|)'.replace(re63, ''); s98[i].replace(re18, ''); '//vzt.jro.qr/vij/FC/${cngu}/${anzr}/${inyhr}?gf=${abj}'.replace(re56, ''); '//vzt.jro.qr/vij/FC/tzk_uc/${anzr}/${inyhr}?gf=${abj}'.replace(/(\$\{anzr\})|(\$anzr\b)/g, ''); 'Jvaqbjf Yvir Ubgznvy{1}'.replace(re69, ''); '{0}{1}'.replace(re63, ''); '{1}'.replace(re69, ''); '{1}'.replace(re63, ''); 'Vzntrf'.replace(re15, ''); 'ZFA'.replace(re15, ''); 'Zncf'.replace(re15, ''); 'Zbq-Vasb-Vasb-WninFpevcgUvag'.replace(re39, ''); 'Arjf'.replace(re15, ''); s99[i].split(re32); s100[i].split(re32); 'Ivqrb'.replace(re15, ''); 'Jro'.replace(re15, ''); 'n'.replace(re39, ''); 'nwnkFgneg'.split(re70); 'nwnkFgbc'.split(re70); 'ovaq'.replace(re14, ''); 'ovaq'.replace(re15, ''); 'oevatf lbh zber. Zber fcnpr (5TO), zber frphevgl, fgvyy serr.'.replace(re63, ''); 'puvyq p1 svefg qrpx'.replace(re14, ''); 'puvyq p1 svefg qrpx'.replace(re15, ''); 'puvyq p1 svefg qbhoyr2'.replace(re14, ''); 'puvyq p1 svefg qbhoyr2'.replace(re15, ''); 'puvyq p2 ynfg'.replace(re14, ''); 'puvyq p2 ynfg'.replace(re15, ''); 'puvyq p2'.replace(re14, ''); 'puvyq p2'.replace(re15, ''); 'puvyq p3'.replace(re14, ''); 'puvyq p3'.replace(re15, ''); 'puvyq p4 ynfg'.replace(re14, ''); 'puvyq p4 ynfg'.replace(re15, ''); 'pbclevtug'.replace(re14, ''); 'pbclevtug'.replace(re15, ''); 'qZFAZR_1'.replace(re14, ''); 'qZFAZR_1'.replace(re15, ''); 'qbhoyr2 ps'.replace(re14, ''); 'qbhoyr2 ps'.replace(re15, ''); 'qbhoyr2'.replace(re14, ''); 'qbhoyr2'.replace(re15, ''); 'uqy_arj'.replace(re14, ''); 'uqy_arj'.replace(re15, ''); 'uc_fubccvatobk'.replace(re30, ''); 'ugzy%2Rvq'.replace(re29, ''); 'ugzy%2Rvq'.replace(re30, ''); s101[i].replace(re33, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${5}'.replace(re72, ''); s102[i].replace(re73, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); 'vztZFSG'.replace(re14, ''); 'vztZFSG'.replace(re15, ''); 'zfasbbg1 ps'.replace(re14, ''); 'zfasbbg1 ps'.replace(re15, ''); s103[i].replace(re14, ''); s103[i].replace(re15, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re15, ''); 'cevznel'.replace(re14, ''); 'cevznel'.replace(re15, ''); 'erpgnatyr'.replace(re30, ''); 'frpbaqnel'.replace(re14, ''); 'frpbaqnel'.replace(re15, ''); 'haybnq'.split(re70); '{0}{1}1'.replace(re63, ''); '|{1}1'.replace(re69, ''); /(..-HF)(\|(\d+)|)/i.exec('xb-xe,ra-va,gu-gu'); re4.exec('/ZlFcnprNccf/NccPnainf,45000012'); re8.exec('144631658.0.10.1231367708'); re8.exec('144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); re8.exec('4413235p3660'); re8.exec('441327q73660'); re8.exec('9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); re8.exec('SbeprqRkcvengvba=633669350559478880'); re8.exec(str54); re8.exec(str55); re8.exec('AFP_zp_dfctwzs-aowb_80=441327q73660'); re8.exec('AFP_zp_kkk-aowb_80=4413235p3660'); re8.exec('FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); re8.exec('__hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); re8.exec('__hgzo=144631658.0.10.1231367708'); re8.exec('__hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(s99[i]); re34.exec(s100[i]); /ZFVR\s+5[.]01/.exec(s15[i]); /HF(?=;)/i.exec(str56); re74.exec(s97[i]); re28.exec('svefg npgvir svefgNpgvir'); re28.exec('ynfg'); /\bp:(..)/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF'); re75.exec(str57); re75.exec(str58); re76.exec(str57); re76.exec(str58); re77.exec(str57); re77.exec(str58); /\bhfucce\s*=\s*([^;]*)/i.exec(str59); re78.exec(str57); re78.exec(str58); /\bjci\s*=\s*([^;]*)/i.exec(str59); re79.exec(str58); re79.exec(str60); re79.exec(str59); /\|p:([a-z]{2})/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'); re80.exec(s97[i]); re61.exec('cebgbglcr.wf'); re68.exec(s97[i]); re81.exec(s97[i]); re82.exec(s97[i]); /^Fubpxjnir Synfu (\d)/.exec(s21[i]); /^Fubpxjnir Synfu (\d+)/.exec(s21[i]); re83.exec('[bowrpg tybony]'); re62.exec(s97[i]); re84.exec(str61); re84.exec(str62); /jroxvg/.exec(str63); } } var re85 = /eaq_zbqobkva/; var str64 = '1231365729213'; var str65 = '74.125.75.3-1057165600.29978900'; var str66 = '74.125.75.3-1057165600.29978900.1231365730214'; var str67 = 'Frnepu%20Zvpebfbsg.pbz'; var str68 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str69 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; __hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1; __hgzo=144631658.0.10.1231365779; __hgzp=144631658; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str70 = 'I=3%26THVQ=757q3ss871q44o7o805n8113n5p72q52'; var str71 = 'I=3&THVQ=757q3ss871q44o7o805n8113n5p72q52'; var str72 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365765292&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365765292&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sohyyrgvaf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1579793869.1231365768&tn_fvq=1231365768&tn_uvq=2056210897&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str73 = 'frnepu.zvpebfbsg.pbz'; var str74 = 'frnepu.zvpebfbsg.pbz/'; var str75 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str76 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; function runBlock10() { for (var i = 0; i < 3; i++) { '%3Szxg=ra-HF'.replace(re39, ''); '-8'.replace(re40, ''); '-8'.replace(re10, ''); '-8'.replace(re51, ''); '-8'.replace(re52, ''); '-8'.replace(re53, ''); '-8'.replace(re39, ''); '-8'.replace(re54, ''); '1.5'.replace(re40, ''); '1.5'.replace(re10, ''); '1.5'.replace(re51, ''); '1.5'.replace(re52, ''); '1.5'.replace(re53, ''); '1.5'.replace(re39, ''); '1.5'.replace(re54, ''); '1024k768'.replace(re40, ''); '1024k768'.replace(re10, ''); '1024k768'.replace(re51, ''); '1024k768'.replace(re52, ''); '1024k768'.replace(re53, ''); '1024k768'.replace(re39, ''); '1024k768'.replace(re54, ''); str64.replace(re40, ''); str64.replace(re10, ''); str64.replace(re51, ''); str64.replace(re52, ''); str64.replace(re53, ''); str64.replace(re39, ''); str64.replace(re54, ''); '14'.replace(re40, ''); '14'.replace(re10, ''); '14'.replace(re51, ''); '14'.replace(re52, ''); '14'.replace(re53, ''); '14'.replace(re39, ''); '14'.replace(re54, ''); '24'.replace(re40, ''); '24'.replace(re10, ''); '24'.replace(re51, ''); '24'.replace(re52, ''); '24'.replace(re53, ''); '24'.replace(re39, ''); '24'.replace(re54, ''); str65.replace(re40, ''); str65.replace(re10, ''); str65.replace(re51, ''); str65.replace(re52, ''); str65.replace(re53, ''); str65.replace(re39, ''); str65.replace(re54, ''); str66.replace(re40, ''); str66.replace(re10, ''); str66.replace(re51, ''); str66.replace(re52, ''); str66.replace(re53, ''); str66.replace(re39, ''); str66.replace(re54, ''); '9.0'.replace(re40, ''); '9.0'.replace(re10, ''); '9.0'.replace(re51, ''); '9.0'.replace(re52, ''); '9.0'.replace(re53, ''); '9.0'.replace(re39, ''); '9.0'.replace(re54, ''); '994k634'.replace(re40, ''); '994k634'.replace(re10, ''); '994k634'.replace(re51, ''); '994k634'.replace(re52, ''); '994k634'.replace(re53, ''); '994k634'.replace(re39, ''); '994k634'.replace(re54, ''); '?zxg=ra-HF'.replace(re40, ''); '?zxg=ra-HF'.replace(re10, ''); '?zxg=ra-HF'.replace(re51, ''); '?zxg=ra-HF'.replace(re52, ''); '?zxg=ra-HF'.replace(re53, ''); '?zxg=ra-HF'.replace(re54, ''); 'PAA.pbz'.replace(re25, ''); 'PAA.pbz'.replace(re12, ''); 'PAA.pbz'.replace(re39, ''); 'Qngr & Gvzr'.replace(re25, ''); 'Qngr & Gvzr'.replace(re12, ''); 'Qngr & Gvzr'.replace(re39, ''); 'Frnepu Zvpebfbsg.pbz'.replace(re40, ''); 'Frnepu Zvpebfbsg.pbz'.replace(re54, ''); str67.replace(re10, ''); str67.replace(re51, ''); str67.replace(re52, ''); str67.replace(re53, ''); str67.replace(re39, ''); str68.split(re32); str69.split(re32); str70.replace(re52, ''); str70.replace(re53, ''); str70.replace(re39, ''); str71.replace(re40, ''); str71.replace(re10, ''); str71.replace(re51, ''); str71.replace(re54, ''); 'Jrngure'.replace(re25, ''); 'Jrngure'.replace(re12, ''); 'Jrngure'.replace(re39, ''); 'LbhGhor'.replace(re25, ''); 'LbhGhor'.replace(re12, ''); 'LbhGhor'.replace(re39, ''); str72.replace(re33, ''); 'erzbgr_vsenzr_1'.replace(/^erzbgr_vsenzr_/, ''); str73.replace(re40, ''); str73.replace(re10, ''); str73.replace(re51, ''); str73.replace(re52, ''); str73.replace(re53, ''); str73.replace(re39, ''); str73.replace(re54, ''); str74.replace(re40, ''); str74.replace(re10, ''); str74.replace(re51, ''); str74.replace(re52, ''); str74.replace(re53, ''); str74.replace(re39, ''); str74.replace(re54, ''); 'lhv-h'.replace(/\-/g, ''); re9.exec('p'); re9.exec('qz p'); re9.exec('zbqynory'); re9.exec('lhv-h svefg'); re8.exec('144631658.0.10.1231365779'); re8.exec('144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); re8.exec(str75); re8.exec(str76); re8.exec('__hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); re8.exec('__hgzo=144631658.0.10.1231365779'); re8.exec('__hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str68); re34.exec(str69); /^$/.exec(''); re31.exec('qr'); /^znk\d+$/.exec(''); /^zva\d+$/.exec(''); /^erfgber$/.exec(''); re85.exec('zbqobkva zbqobk_abcnqqvat '); re85.exec('zbqgvgyr'); re85.exec('eaq_zbqobkva '); re85.exec('eaq_zbqgvgyr '); /frpgvba\d+_pbagragf/.exec('obggbz_ani'); } } var re86 = /;\s*/; var re87 = /(\$\{inyhr\})|(\$inyhr\b)/g; var re88 = /(\$\{abj\})|(\$abj\b)/g; var re89 = /\s+$/; var re90 = /^\s+/; var re91 = /(\\\"|\x00-|\x1f|\x7f-|\x9f|\u00ad|\u0600-|\u0604|\u070f|\u17b4|\u17b5|\u200c-|\u200f|\u2028-|\u202f|\u2060-|\u206f|\ufeff|\ufff0-|\uffff)/g; var re92 = /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/; var re93 = /^([:.#]*)((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re94 = /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/; var str77 = '#fubhgobk .pybfr'; var str78 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzssrwh-aowb_80=441326q33660'; var str79 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; AFP_zp_dfctwzssrwh-aowb_80=441326q33660; __hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1; __hgzo=144631658.0.10.1231365869; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str80 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=441327q73660'; var str81 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; AFP_zp_dfctwzs-aowb_80=441327q73660; __hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1; __hgzo=144631658.0.10.1231367054; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str82 = '[glcr=fhozvg]'; var str83 = 'n.svryqOga,n.svryqOgaPnapry'; var str84 = 'n.svryqOgaPnapry'; var str85 = 'oyvpxchaxg'; var str86 = 'qvi.bow-nppbeqvba qg'; var str87 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_nccf_wf&qg=1231367052227&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367052227&punaary=svz_zlfcnpr_nccf-pnainf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2SZbqhyrf%2SNccyvpngvbaf%2SCntrf%2SPnainf.nfck&nq_glcr=grkg&rvq=6083027&rn=0&sez=1&tn_ivq=716357910.1231367056&tn_fvq=1231367056&tn_uvq=1387206491&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str88 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365851658&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365851658&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyrrqvg.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1979828129.1231365855&tn_fvq=1231365855&tn_uvq=2085229649&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str89 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N12%3N47%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=0k0&p=43835816&x=A&oj=994&ou=634&uc=A&{2}&[NDR]'; var str90 = 'zrgn[anzr=nwnkHey]'; var str91 = 'anpuevpugra'; var str92 = 'b oS={\'oT\':1.1};x $8n(B){z(B!=o9)};x $S(B){O(!$8n(B))z A;O(B.4L)z\'T\';b S=7t B;O(S==\'2P\'&&B.p4){23(B.7f){12 1:z\'T\';12 3:z/\S/.2g(B.8M)?\'ox\':\'oh\'}}O(S==\'2P\'||S==\'x\'){23(B.nE){12 2V:z\'1O\';12 7I:z\'5a\';12 18:z\'4B\'}O(7t B.I==\'4F\'){O(B.3u)z\'pG\';O(B.8e)z\'1p\'}}z S};x $2p(){b 4E={};Z(b v=0;v<1p.I;v++){Z(b X 1o 1p[v]){b nc=1p[v][X];b 6E=4E[X];O(6E&&$S(nc)==\'2P\'&&$S(6E)==\'2P\')4E[X]=$2p(6E,nc);17 4E[X]=nc}}z 4E};b $E=7p.E=x(){b 1d=1p;O(!1d[1])1d=[p,1d[0]];Z(b X 1o 1d[1])1d[0][X]=1d[1][X];z 1d[0]};b $4D=7p.pJ=x(){Z(b v=0,y=1p.I;v-1:p.3F(2R)>-1},nX:x(){z p.3y(/([.*+?^${}()|[\]\/\\])/t,\'\\$1\')}});2V.E({5V:x(1O){O(p.I<3)z A;O(p.I==4&&p[3]==0&&!1O)z\'p5\';b 3P=[];Z(b v=0;v<3;v++){b 52=(p[v]-0).4h(16);3P.1x((52.I==1)?\'0\'+52:52)}z 1O?3P:\'#\'+3P.2u(\'\')},5U:x(1O){O(p.I!=3)z A;b 1i=[];Z(b v=0;v<3;v++){1i.1x(5K((p[v].I==1)?p[v]+p[v]:p[v],16))}z 1O?1i:\'1i(\'+1i.2u(\',\')+\')\'}});7F.E({3n:x(P){b J=p;P=$2p({\'L\':J,\'V\':A,\'1p\':1S,\'2x\':A,\'4s\':A,\'6W\':A},P);O($2O(P.1p)&&$S(P.1p)!=\'1O\')P.1p=[P.1p];z x(V){b 1d;O(P.V){V=V||H.V;1d=[(P.V===1r)?V:Y P.V(V)];O(P.1p)1d.E(P.1p)}17 1d=P.1p||1p;b 3C=x(){z J.3H($5S(P'; var str93 = 'hagreunyghat'; var str94 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str95 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str96 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str97 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str98 = 'shapgvba (){Cuk.Nccyvpngvba.Frghc.Pber();Cuk.Nccyvpngvba.Frghc.Nwnk();Cuk.Nccyvpngvba.Frghc.Synfu();Cuk.Nccyvpngvba.Frghc.Zbqhyrf()}'; function runBlock11() { for (var i = 0; i < 2; i++) { ' .pybfr'.replace(re18, ''); ' n.svryqOgaPnapry'.replace(re18, ''); ' qg'.replace(re18, ''); str77.replace(re68, ''); str77.replace(re18, ''); ''.replace(re39, ''); ''.replace(/^/, ''); ''.split(re86); '*'.replace(re39, ''); '*'.replace(re68, ''); '*'.replace(re18, ''); '.pybfr'.replace(re68, ''); '.pybfr'.replace(re18, ''); '//vzt.jro.qr/vij/FC/tzk_uc/fperra/${inyhr}?gf=${abj}'.replace(re87, ''); '//vzt.jro.qr/vij/FC/tzk_uc/fperra/1024?gf=${abj}'.replace(re88, ''); '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/${inyhr}?gf=${abj}'.replace(re87, ''); '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/992/608?gf=${abj}'.replace(re88, ''); '300k120'.replace(re30, ''); '300k250'.replace(re30, ''); '310k120'.replace(re30, ''); '310k170'.replace(re30, ''); '310k250'.replace(re30, ''); '9.0 e115'.replace(/^.*\.(.*)\s.*$/, ''); 'Nppbeqvba'.replace(re2, ''); 'Nxghryy\x0a'.replace(re89, ''); 'Nxghryy\x0a'.replace(re90, ''); 'Nccyvpngvba'.replace(re2, ''); 'Oyvpxchaxg\x0a'.replace(re89, ''); 'Oyvpxchaxg\x0a'.replace(re90, ''); 'Svanamra\x0a'.replace(re89, ''); 'Svanamra\x0a'.replace(re90, ''); 'Tnzrf\x0a'.replace(re89, ''); 'Tnzrf\x0a'.replace(re90, ''); 'Ubebfxbc\x0a'.replace(re89, ''); 'Ubebfxbc\x0a'.replace(re90, ''); 'Xvab\x0a'.replace(re89, ''); 'Xvab\x0a'.replace(re90, ''); 'Zbqhyrf'.replace(re2, ''); 'Zhfvx\x0a'.replace(re89, ''); 'Zhfvx\x0a'.replace(re90, ''); 'Anpuevpugra\x0a'.replace(re89, ''); 'Anpuevpugra\x0a'.replace(re90, ''); 'Cuk'.replace(re2, ''); 'ErdhrfgSvavfu'.split(re70); 'ErdhrfgSvavfu.NWNK.Cuk'.split(re70); 'Ebhgr\x0a'.replace(re89, ''); 'Ebhgr\x0a'.replace(re90, ''); str78.split(re32); str79.split(re32); str80.split(re32); str81.split(re32); 'Fcbeg\x0a'.replace(re89, ''); 'Fcbeg\x0a'.replace(re90, ''); 'GI-Fcbg\x0a'.replace(re89, ''); 'GI-Fcbg\x0a'.replace(re90, ''); 'Gbhe\x0a'.replace(re89, ''); 'Gbhe\x0a'.replace(re90, ''); 'Hagreunyghat\x0a'.replace(re89, ''); 'Hagreunyghat\x0a'.replace(re90, ''); 'Ivqrb\x0a'.replace(re89, ''); 'Ivqrb\x0a'.replace(re90, ''); 'Jrggre\x0a'.replace(re89, ''); 'Jrggre\x0a'.replace(re90, ''); str82.replace(re68, ''); str82.replace(re18, ''); str83.replace(re68, ''); str83.replace(re18, ''); str84.replace(re68, ''); str84.replace(re18, ''); 'nqiFreivprObk'.replace(re30, ''); 'nqiFubccvatObk'.replace(re30, ''); 'nwnk'.replace(re39, ''); 'nxghryy'.replace(re40, ''); 'nxghryy'.replace(re41, ''); 'nxghryy'.replace(re42, ''); 'nxghryy'.replace(re43, ''); 'nxghryy'.replace(re44, ''); 'nxghryy'.replace(re45, ''); 'nxghryy'.replace(re46, ''); 'nxghryy'.replace(re47, ''); 'nxghryy'.replace(re48, ''); str85.replace(re40, ''); str85.replace(re41, ''); str85.replace(re42, ''); str85.replace(re43, ''); str85.replace(re44, ''); str85.replace(re45, ''); str85.replace(re46, ''); str85.replace(re47, ''); str85.replace(re48, ''); 'pngrtbel'.replace(re29, ''); 'pngrtbel'.replace(re30, ''); 'pybfr'.replace(re39, ''); 'qvi'.replace(re39, ''); str86.replace(re68, ''); str86.replace(re18, ''); 'qg'.replace(re39, ''); 'qg'.replace(re68, ''); 'qg'.replace(re18, ''); 'rzorq'.replace(re39, ''); 'rzorq'.replace(re68, ''); 'rzorq'.replace(re18, ''); 'svryqOga'.replace(re39, ''); 'svryqOgaPnapry'.replace(re39, ''); 'svz_zlfcnpr_nccf-pnainf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); 'svanamra'.replace(re40, ''); 'svanamra'.replace(re41, ''); 'svanamra'.replace(re42, ''); 'svanamra'.replace(re43, ''); 'svanamra'.replace(re44, ''); 'svanamra'.replace(re45, ''); 'svanamra'.replace(re46, ''); 'svanamra'.replace(re47, ''); 'svanamra'.replace(re48, ''); 'sbphf'.split(re70); 'sbphf.gno sbphfva.gno'.split(re70); 'sbphfva'.split(re70); 'sbez'.replace(re39, ''); 'sbez.nwnk'.replace(re68, ''); 'sbez.nwnk'.replace(re18, ''); 'tnzrf'.replace(re40, ''); 'tnzrf'.replace(re41, ''); 'tnzrf'.replace(re42, ''); 'tnzrf'.replace(re43, ''); 'tnzrf'.replace(re44, ''); 'tnzrf'.replace(re45, ''); 'tnzrf'.replace(re46, ''); 'tnzrf'.replace(re47, ''); 'tnzrf'.replace(re48, ''); 'ubzrcntr'.replace(re30, ''); 'ubebfxbc'.replace(re40, ''); 'ubebfxbc'.replace(re41, ''); 'ubebfxbc'.replace(re42, ''); 'ubebfxbc'.replace(re43, ''); 'ubebfxbc'.replace(re44, ''); 'ubebfxbc'.replace(re45, ''); 'ubebfxbc'.replace(re46, ''); 'ubebfxbc'.replace(re47, ''); 'ubebfxbc'.replace(re48, ''); 'uc_cebzbobk_ugzy%2Puc_cebzbobk_vzt'.replace(re30, ''); 'uc_erpgnatyr'.replace(re30, ''); str87.replace(re33, ''); str88.replace(re33, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${5}'.replace(re72, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${5}'.replace(re72, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${5}'.replace(re72, ''); str89.replace(re73, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); str6.replace(re23, ''); 'xvab'.replace(re40, ''); 'xvab'.replace(re41, ''); 'xvab'.replace(re42, ''); 'xvab'.replace(re43, ''); 'xvab'.replace(re44, ''); 'xvab'.replace(re45, ''); 'xvab'.replace(re46, ''); 'xvab'.replace(re47, ''); 'xvab'.replace(re48, ''); 'ybnq'.split(re70); 'zrqvnzbqgno lhv-anifrg lhv-anifrg-gbc'.replace(re18, ''); 'zrgn'.replace(re39, ''); str90.replace(re68, ''); str90.replace(re18, ''); 'zbhfrzbir'.split(re70); 'zbhfrzbir.gno'.split(re70); str63.replace(/^.*jroxvg\/(\d+(\.\d+)?).*$/, ''); 'zhfvx'.replace(re40, ''); 'zhfvx'.replace(re41, ''); 'zhfvx'.replace(re42, ''); 'zhfvx'.replace(re43, ''); 'zhfvx'.replace(re44, ''); 'zhfvx'.replace(re45, ''); 'zhfvx'.replace(re46, ''); 'zhfvx'.replace(re47, ''); 'zhfvx'.replace(re48, ''); 'zlfcnpr_nccf_pnainf'.replace(re52, ''); str91.replace(re40, ''); str91.replace(re41, ''); str91.replace(re42, ''); str91.replace(re43, ''); str91.replace(re44, ''); str91.replace(re45, ''); str91.replace(re46, ''); str91.replace(re47, ''); str91.replace(re48, ''); 'anzr'.replace(re39, ''); str92.replace(/\b\w+\b/g, ''); 'bow-nppbeqvba'.replace(re39, ''); 'bowrpg'.replace(re39, ''); 'bowrpg'.replace(re68, ''); 'bowrpg'.replace(re18, ''); 'cnenzf%2Rfglyrf'.replace(re29, ''); 'cnenzf%2Rfglyrf'.replace(re30, ''); 'cbchc'.replace(re30, ''); 'ebhgr'.replace(re40, ''); 'ebhgr'.replace(re41, ''); 'ebhgr'.replace(re42, ''); 'ebhgr'.replace(re43, ''); 'ebhgr'.replace(re44, ''); 'ebhgr'.replace(re45, ''); 'ebhgr'.replace(re46, ''); 'ebhgr'.replace(re47, ''); 'ebhgr'.replace(re48, ''); 'freivprobk_uc'.replace(re30, ''); 'fubccvatobk_uc'.replace(re30, ''); 'fubhgobk'.replace(re39, ''); 'fcbeg'.replace(re40, ''); 'fcbeg'.replace(re41, ''); 'fcbeg'.replace(re42, ''); 'fcbeg'.replace(re43, ''); 'fcbeg'.replace(re44, ''); 'fcbeg'.replace(re45, ''); 'fcbeg'.replace(re46, ''); 'fcbeg'.replace(re47, ''); 'fcbeg'.replace(re48, ''); 'gbhe'.replace(re40, ''); 'gbhe'.replace(re41, ''); 'gbhe'.replace(re42, ''); 'gbhe'.replace(re43, ''); 'gbhe'.replace(re44, ''); 'gbhe'.replace(re45, ''); 'gbhe'.replace(re46, ''); 'gbhe'.replace(re47, ''); 'gbhe'.replace(re48, ''); 'gi-fcbg'.replace(re40, ''); 'gi-fcbg'.replace(re41, ''); 'gi-fcbg'.replace(re42, ''); 'gi-fcbg'.replace(re43, ''); 'gi-fcbg'.replace(re44, ''); 'gi-fcbg'.replace(re45, ''); 'gi-fcbg'.replace(re46, ''); 'gi-fcbg'.replace(re47, ''); 'gi-fcbg'.replace(re48, ''); 'glcr'.replace(re39, ''); 'haqrsvarq'.replace(/\//g, ''); str93.replace(re40, ''); str93.replace(re41, ''); str93.replace(re42, ''); str93.replace(re43, ''); str93.replace(re44, ''); str93.replace(re45, ''); str93.replace(re46, ''); str93.replace(re47, ''); str93.replace(re48, ''); 'ivqrb'.replace(re40, ''); 'ivqrb'.replace(re41, ''); 'ivqrb'.replace(re42, ''); 'ivqrb'.replace(re43, ''); 'ivqrb'.replace(re44, ''); 'ivqrb'.replace(re45, ''); 'ivqrb'.replace(re46, ''); 'ivqrb'.replace(re47, ''); 'ivqrb'.replace(re48, ''); 'ivfvgf=1'.split(re86); 'jrggre'.replace(re40, ''); 'jrggre'.replace(re41, ''); 'jrggre'.replace(re42, ''); 'jrggre'.replace(re43, ''); 'jrggre'.replace(re44, ''); 'jrggre'.replace(re45, ''); 'jrggre'.replace(re46, ''); 'jrggre'.replace(re47, ''); 'jrggre'.replace(re48, ''); /#[a-z0-9]+$/i.exec('uggc://jjj.fpuhryreim.arg/Qrsnhyg'); re66.exec('fryrpgrq'); /(?:^|\s+)lhv-ani(?:\s+|$)/.exec('sff lhv-ani'); /(?:^|\s+)lhv-anifrg(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); /(?:^|\s+)lhv-anifrg-gbc(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); re91.exec('GnoThvq'); re91.exec('thvq'); /(pbzcngvoyr|jroxvg)/.exec(str63); /.+(?:ei|vg|en|vr)[\/: ]([\d.]+)/.exec(str63); re8.exec('144631658.0.10.1231365869'); re8.exec('144631658.0.10.1231367054'); re8.exec('144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); re8.exec('144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); re8.exec(str94); re8.exec(str95); re8.exec(str96); re8.exec(str97); re8.exec('__hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); re8.exec('__hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); re8.exec('__hgzo=144631658.0.10.1231365869'); re8.exec('__hgzo=144631658.0.10.1231367054'); re8.exec('__hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('__hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str78); re34.exec(str79); re34.exec(str81); re74.exec(str77); re74.exec('*'); re74.exec(str82); re74.exec(str83); re74.exec(str86); re74.exec('rzorq'); re74.exec('sbez.nwnk'); re74.exec(str90); re74.exec('bowrpg'); /\/onfr.wf(\?.+)?$/.exec('/uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); re28.exec('uvag ynfgUvag ynfg'); re75.exec(''); re76.exec(''); re77.exec(''); re78.exec(''); re80.exec(str77); re80.exec('*'); re80.exec('.pybfr'); re80.exec(str82); re80.exec(str83); re80.exec(str84); re80.exec(str86); re80.exec('qg'); re80.exec('rzorq'); re80.exec('sbez.nwnk'); re80.exec(str90); re80.exec('bowrpg'); re61.exec('qlaYvo.wf'); re61.exec('rssrpgYvo.wf'); re61.exec('uggc://jjj.tzk.arg/qr/?fgnghf=uvajrvf'); re92.exec(' .pybfr'); re92.exec(' n.svryqOgaPnapry'); re92.exec(' qg'); re92.exec(str48); re92.exec('.nwnk'); re92.exec('.svryqOga,n.svryqOgaPnapry'); re92.exec('.svryqOgaPnapry'); re92.exec('.bow-nppbeqvba qg'); re68.exec(str77); re68.exec('*'); re68.exec('.pybfr'); re68.exec(str82); re68.exec(str83); re68.exec(str84); re68.exec(str86); re68.exec('qg'); re68.exec('rzorq'); re68.exec('sbez.nwnk'); re68.exec(str90); re68.exec('bowrpg'); re93.exec(' .pybfr'); re93.exec(' n.svryqOgaPnapry'); re93.exec(' qg'); re93.exec(str48); re93.exec('.nwnk'); re93.exec('.svryqOga,n.svryqOgaPnapry'); re93.exec('.svryqOgaPnapry'); re93.exec('.bow-nppbeqvba qg'); re81.exec(str77); re81.exec('*'); re81.exec(str48); re81.exec('.pybfr'); re81.exec(str82); re81.exec(str83); re81.exec(str84); re81.exec(str86); re81.exec('qg'); re81.exec('rzorq'); re81.exec('sbez.nwnk'); re81.exec(str90); re81.exec('bowrpg'); re94.exec(' .pybfr'); re94.exec(' n.svryqOgaPnapry'); re94.exec(' qg'); re94.exec(str48); re94.exec('.nwnk'); re94.exec('.svryqOga,n.svryqOgaPnapry'); re94.exec('.svryqOgaPnapry'); re94.exec('.bow-nppbeqvba qg'); re94.exec('[anzr=nwnkHey]'); re94.exec(str82); re31.exec('rf'); re31.exec('wn'); re82.exec(str77); re82.exec('*'); re82.exec(str48); re82.exec('.pybfr'); re82.exec(str82); re82.exec(str83); re82.exec(str84); re82.exec(str86); re82.exec('qg'); re82.exec('rzorq'); re82.exec('sbez.nwnk'); re82.exec(str90); re82.exec('bowrpg'); re83.exec(str98); re83.exec('shapgvba sbphf() { [angvir pbqr] }'); re62.exec('#Ybtva'); re62.exec('#Ybtva_cnffjbeq'); re62.exec(str77); re62.exec('#fubhgobkWf'); re62.exec('#fubhgobkWfReebe'); re62.exec('#fubhgobkWfFhpprff'); re62.exec('*'); re62.exec(str82); re62.exec(str83); re62.exec(str86); re62.exec('rzorq'); re62.exec('sbez.nwnk'); re62.exec(str90); re62.exec('bowrpg'); re49.exec('pbagrag'); re24.exec(str6); /xbadhrebe/.exec(str63); /znp/.exec('jva32'); /zbmvyyn/.exec(str63); /zfvr/.exec(str63); /ag\s5\.1/.exec(str63); /bcren/.exec(str63); /fnsnev/.exec(str63); /jva/.exec('jva32'); /jvaqbjf/.exec(str63); } } function run() { for (var i = 0; i < 5; i++) { runBlock0(); runBlock1(); runBlock2(); runBlock3(); runBlock4(); runBlock5(); runBlock6(); runBlock7(); runBlock8(); runBlock9(); runBlock10(); runBlock11(); } } this.run = run; } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/run.js0000644000175000017500000000523214433667662025471 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. load('v8-benchmarks-v6/base.js'); load('v8-benchmarks-v6/richards.js'); load('v8-benchmarks-v6/deltablue.js'); load('v8-benchmarks-v6/crypto.js'); load('v8-benchmarks-v6/raytrace.js'); load('v8-benchmarks-v6/earley-boyer.js'); load('v8-benchmarks-v6/regexp.js'); load('v8-benchmarks-v6/splay.js'); var success = true; var lastScore; function printMeasurement(name, value) { print("" + name + "" + value + ""); } function PrintResult(name, result) { //printMeasurement(RUN_NAME + ' ' + name, result); print(name + ': ' + result); } function PrintError(name, error) { PrintResult(name, error); success = false; } function PrintScore(score) { lastScore = score; if (success) { print('Score (version ' + BenchmarkSuite.version + '): ' + score); } } // Run more than once to warm up JVM var runs = 1; for (var i = 1; i <= runs; i++) { BenchmarkSuite.RunSuites({ NotifyResult: PrintResult, NotifyError: PrintError, NotifyScore: PrintScore }); gc(); } lastScore; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/richards.js0000644000175000017500000003664014433667662026473 0ustar apoapo// Copyright 2006-2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This is a JavaScript implementation of the Richards // benchmark from: // // http://www.cl.cam.ac.uk/~mr10/Bench.html // // The benchmark was originally implemented in BCPL by // Martin Richards. var Richards = new BenchmarkSuite('Richards', 35302, [ new Benchmark("Richards", runRichards) ]); /** * The Richards benchmark simulates the task dispatcher of an * operating system. **/ function runRichards() { var scheduler = new Scheduler(); scheduler.addIdleTask(ID_IDLE, 0, null, COUNT); var queue = new Packet(null, ID_WORKER, KIND_WORK); queue = new Packet(queue, ID_WORKER, KIND_WORK); scheduler.addWorkerTask(ID_WORKER, 1000, queue); queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue); queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue); scheduler.addDeviceTask(ID_DEVICE_A, 4000, null); scheduler.addDeviceTask(ID_DEVICE_B, 5000, null); scheduler.schedule(); if (scheduler.queueCount != EXPECTED_QUEUE_COUNT || scheduler.holdCount != EXPECTED_HOLD_COUNT) { var msg = "Error during execution: queueCount = " + scheduler.queueCount + ", holdCount = " + scheduler.holdCount + "."; throw new Error(msg); } } var COUNT = 1000; /** * These two constants specify how many times a packet is queued and * how many times a task is put on hold in a correct run of richards. * They don't have any meaning a such but are characteristic of a * correct run so if the actual queue or hold count is different from * the expected there must be a bug in the implementation. **/ var EXPECTED_QUEUE_COUNT = 2322; var EXPECTED_HOLD_COUNT = 928; /** * A scheduler can be used to schedule a set of tasks based on their relative * priorities. Scheduling is done by maintaining a list of task control blocks * which holds tasks and the data queue they are processing. * @constructor */ function Scheduler() { this.queueCount = 0; this.holdCount = 0; this.blocks = new Array(NUMBER_OF_IDS); this.list = null; this.currentTcb = null; this.currentId = null; } var ID_IDLE = 0; var ID_WORKER = 1; var ID_HANDLER_A = 2; var ID_HANDLER_B = 3; var ID_DEVICE_A = 4; var ID_DEVICE_B = 5; var NUMBER_OF_IDS = 6; var KIND_DEVICE = 0; var KIND_WORK = 1; /** * Add an idle task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {int} count the number of times to schedule the task */ Scheduler.prototype.addIdleTask = function (id, priority, queue, count) { this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count)); }; /** * Add a work task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addWorkerTask = function (id, priority, queue) { this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0)); }; /** * Add a handler task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addHandlerTask = function (id, priority, queue) { this.addTask(id, priority, queue, new HandlerTask(this)); }; /** * Add a handler task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addDeviceTask = function (id, priority, queue) { this.addTask(id, priority, queue, new DeviceTask(this)) }; /** * Add the specified task and mark it as running. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {Task} task the task to add */ Scheduler.prototype.addRunningTask = function (id, priority, queue, task) { this.addTask(id, priority, queue, task); this.currentTcb.setRunning(); }; /** * Add the specified task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {Task} task the task to add */ Scheduler.prototype.addTask = function (id, priority, queue, task) { this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task); this.list = this.currentTcb; this.blocks[id] = this.currentTcb; }; /** * Execute the tasks managed by this scheduler. */ Scheduler.prototype.schedule = function () { this.currentTcb = this.list; while (this.currentTcb != null) { if (this.currentTcb.isHeldOrSuspended()) { this.currentTcb = this.currentTcb.link; } else { this.currentId = this.currentTcb.id; this.currentTcb = this.currentTcb.run(); } } }; /** * Release a task that is currently blocked and return the next block to run. * @param {int} id the id of the task to suspend */ Scheduler.prototype.release = function (id) { var tcb = this.blocks[id]; if (tcb == null) return tcb; tcb.markAsNotHeld(); if (tcb.priority > this.currentTcb.priority) { return tcb; } else { return this.currentTcb; } }; /** * Block the currently executing task and return the next task control block * to run. The blocked task will not be made runnable until it is explicitly * released, even if new work is added to it. */ Scheduler.prototype.holdCurrent = function () { this.holdCount++; this.currentTcb.markAsHeld(); return this.currentTcb.link; }; /** * Suspend the currently executing task and return the next task control block * to run. If new work is added to the suspended task it will be made runnable. */ Scheduler.prototype.suspendCurrent = function () { this.currentTcb.markAsSuspended(); return this.currentTcb; }; /** * Add the specified packet to the end of the worklist used by the task * associated with the packet and make the task runnable if it is currently * suspended. * @param {Packet} packet the packet to add */ Scheduler.prototype.queue = function (packet) { var t = this.blocks[packet.id]; if (t == null) return t; this.queueCount++; packet.link = null; packet.id = this.currentId; return t.checkPriorityAdd(this.currentTcb, packet); }; /** * A task control block manages a task and the queue of work packages associated * with it. * @param {TaskControlBlock} link the preceding block in the linked block list * @param {int} id the id of this block * @param {int} priority the priority of this block * @param {Packet} queue the queue of packages to be processed by the task * @param {Task} task the task * @constructor */ function TaskControlBlock(link, id, priority, queue, task) { this.link = link; this.id = id; this.priority = priority; this.queue = queue; this.task = task; if (queue == null) { this.state = STATE_SUSPENDED; } else { this.state = STATE_SUSPENDED_RUNNABLE; } } /** * The task is running and is currently scheduled. */ var STATE_RUNNING = 0; /** * The task has packets left to process. */ var STATE_RUNNABLE = 1; /** * The task is not currently running. The task is not blocked as such and may * be started by the scheduler. */ var STATE_SUSPENDED = 2; /** * The task is blocked and cannot be run until it is explicitly released. */ var STATE_HELD = 4; var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE; var STATE_NOT_HELD = ~STATE_HELD; TaskControlBlock.prototype.setRunning = function () { this.state = STATE_RUNNING; }; TaskControlBlock.prototype.markAsNotHeld = function () { this.state = this.state & STATE_NOT_HELD; }; TaskControlBlock.prototype.markAsHeld = function () { this.state = this.state | STATE_HELD; }; TaskControlBlock.prototype.isHeldOrSuspended = function () { return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED); }; TaskControlBlock.prototype.markAsSuspended = function () { this.state = this.state | STATE_SUSPENDED; }; TaskControlBlock.prototype.markAsRunnable = function () { this.state = this.state | STATE_RUNNABLE; }; /** * Runs this task, if it is ready to be run, and returns the next task to run. */ TaskControlBlock.prototype.run = function () { var packet; if (this.state == STATE_SUSPENDED_RUNNABLE) { packet = this.queue; this.queue = packet.link; if (this.queue == null) { this.state = STATE_RUNNING; } else { this.state = STATE_RUNNABLE; } } else { packet = null; } return this.task.run(packet); }; /** * Adds a packet to the worklist of this block's task, marks this as runnable if * necessary, and returns the next runnable object to run (the one * with the highest priority). */ TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) { if (this.queue == null) { this.queue = packet; this.markAsRunnable(); if (this.priority > task.priority) return this; } else { this.queue = packet.addTo(this.queue); } return task; }; TaskControlBlock.prototype.toString = function () { return "tcb { " + this.task + "@" + this.state + " }"; }; /** * An idle task doesn't do any work itself but cycles control between the two * device tasks. * @param {Scheduler} scheduler the scheduler that manages this task * @param {int} v1 a seed value that controls how the device tasks are scheduled * @param {int} count the number of times this task should be scheduled * @constructor */ function IdleTask(scheduler, v1, count) { this.scheduler = scheduler; this.v1 = v1; this.count = count; } IdleTask.prototype.run = function (packet) { this.count--; if (this.count == 0) return this.scheduler.holdCurrent(); if ((this.v1 & 1) == 0) { this.v1 = this.v1 >> 1; return this.scheduler.release(ID_DEVICE_A); } else { this.v1 = (this.v1 >> 1) ^ 0xD008; return this.scheduler.release(ID_DEVICE_B); } }; IdleTask.prototype.toString = function () { return "IdleTask" }; /** * A task that suspends itself after each time it has been run to simulate * waiting for data from an external device. * @param {Scheduler} scheduler the scheduler that manages this task * @constructor */ function DeviceTask(scheduler) { this.scheduler = scheduler; this.v1 = null; } DeviceTask.prototype.run = function (packet) { if (packet == null) { if (this.v1 == null) return this.scheduler.suspendCurrent(); var v = this.v1; this.v1 = null; return this.scheduler.queue(v); } else { this.v1 = packet; return this.scheduler.holdCurrent(); } }; DeviceTask.prototype.toString = function () { return "DeviceTask"; }; /** * A task that manipulates work packets. * @param {Scheduler} scheduler the scheduler that manages this task * @param {int} v1 a seed used to specify how work packets are manipulated * @param {int} v2 another seed used to specify how work packets are manipulated * @constructor */ function WorkerTask(scheduler, v1, v2) { this.scheduler = scheduler; this.v1 = v1; this.v2 = v2; } WorkerTask.prototype.run = function (packet) { if (packet == null) { return this.scheduler.suspendCurrent(); } else { if (this.v1 == ID_HANDLER_A) { this.v1 = ID_HANDLER_B; } else { this.v1 = ID_HANDLER_A; } packet.id = this.v1; packet.a1 = 0; for (var i = 0; i < DATA_SIZE; i++) { this.v2++; if (this.v2 > 26) this.v2 = 1; packet.a2[i] = this.v2; } return this.scheduler.queue(packet); } }; WorkerTask.prototype.toString = function () { return "WorkerTask"; }; /** * A task that manipulates work packets and then suspends itself. * @param {Scheduler} scheduler the scheduler that manages this task * @constructor */ function HandlerTask(scheduler) { this.scheduler = scheduler; this.v1 = null; this.v2 = null; } HandlerTask.prototype.run = function (packet) { if (packet != null) { if (packet.kind == KIND_WORK) { this.v1 = packet.addTo(this.v1); } else { this.v2 = packet.addTo(this.v2); } } if (this.v1 != null) { var count = this.v1.a1; var v; if (count < DATA_SIZE) { if (this.v2 != null) { v = this.v2; this.v2 = this.v2.link; v.a1 = this.v1.a2[count]; this.v1.a1 = count + 1; return this.scheduler.queue(v); } } else { v = this.v1; this.v1 = this.v1.link; return this.scheduler.queue(v); } } return this.scheduler.suspendCurrent(); }; HandlerTask.prototype.toString = function () { return "HandlerTask"; }; /* --- * * P a c k e t * --- */ var DATA_SIZE = 4; /** * A simple package of data that is manipulated by the tasks. The exact layout * of the payload data carried by a packet is not importaint, and neither is the * nature of the work performed on packets by the tasks. * * Besides carrying data, packets form linked lists and are hence used both as * data and worklists. * @param {Packet} link the tail of the linked list of packets * @param {int} id an ID for this packet * @param {int} kind the type of this packet * @constructor */ function Packet(link, id, kind) { this.link = link; this.id = id; this.kind = kind; this.a1 = 0; this.a2 = new Array(DATA_SIZE); } /** * Add this packet to the end of a worklist, and return the worklist. * @param {Packet} queue the worklist to add this packet to */ Packet.prototype.addTo = function (queue) { this.link = null; if (queue == null) return this; var peek, next = queue; while ((peek = next.link) != null) next = peek; next.link = this; return queue; }; Packet.prototype.toString = function () { return "Packet"; }; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/crypto.js0000644000175000017500000013561614433667662026217 0ustar apoapo/* * Copyright (c) 2003-2005 Tom Wu * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * In addition, the following condition applies: * * All redistributions must retain an intact copy of this copyright notice * and disclaimer. */ // The code has been adapted for use as a benchmark by Google. var Crypto = new BenchmarkSuite('Crypto', 266181, [ new Benchmark("Encrypt", encrypt), new Benchmark("Decrypt", decrypt) ]); // Basic JavaScript BN library - subset useful for RSA encryption. // Bits per digit var dbits; var BI_DB; var BI_DM; var BI_DV; var BI_FP; var BI_FV; var BI_F1; var BI_F2; // JavaScript engine analysis var canary = 0xdeadbeefcafe; var j_lm = ((canary&0xffffff)==0xefcafe); // (public) Constructor function BigInteger(a,b,c) { this.array = new Array(); if(a != null) if("number" == typeof a) this.fromNumber(a,b,c); else if(b == null && "string" != typeof a) this.fromString(a,256); else this.fromString(a,b); } // return new, unset BigInteger function nbi() { return new BigInteger(null); } // am: Compute w_j += (x*this_i), propagate carries, // c is initial carry, returns final carry. // c < 3*dvalue, x < 2*dvalue, this_i < dvalue // We need to select the fastest one that works in this environment. // am1: use a single mult and divide to get the high bits, // max digit bits should be 26 because // max internal value = 2*dvalue^2-2*dvalue (< 2^53) function am1(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; while(--n >= 0) { var v = x*this_array[i++]+w_array[j]+c; c = Math.floor(v/0x4000000); w_array[j++] = v&0x3ffffff; } return c; } // am2 avoids a big mult-and-extract completely. // Max digit bits should be <= 30 because we do bitwise ops // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) function am2(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x7fff, xh = x>>15; while(--n >= 0) { var l = this_array[i]&0x7fff; var h = this_array[i++]>>15; var m = xh*l+h*xl; l = xl*l+((m&0x7fff)<<15)+w_array[j]+(c&0x3fffffff); c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); w_array[j++] = l&0x3fffffff; } return c; } // Alternately, set max digit bits to 28 since some // browsers slow down when dealing with 32-bit numbers. function am3(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x3fff, xh = x>>14; while(--n >= 0) { var l = this_array[i]&0x3fff; var h = this_array[i++]>>14; var m = xh*l+h*xl; l = xl*l+((m&0x3fff)<<14)+w_array[j]+c; c = (l>>28)+(m>>14)+xh*h; w_array[j++] = l&0xfffffff; } return c; } // This is tailored to VMs with 2-bit tagging. It makes sure // that all the computations stay within the 29 bits available. function am4(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x1fff, xh = x>>13; while(--n >= 0) { var l = this_array[i]&0x1fff; var h = this_array[i++]>>13; var m = xh*l+h*xl; l = xl*l+((m&0x1fff)<<13)+w_array[j]+c; c = (l>>26)+(m>>13)+xh*h; w_array[j++] = l&0x3ffffff; } return c; } // am3/28 is best for SM, Rhino, but am4/26 is best for v8. // Kestrel (Opera 9.5) gets its best result with am4/26. // IE7 does 9% better with am3/28 than with am4/26. // Firefox (SM) gets 10% faster with am3/28 than with am4/26. setupEngine = function(fn, bits) { BigInteger.prototype.am = fn; dbits = bits; BI_DB = dbits; BI_DM = ((1<= 0; --i) r_array[i] = this_array[i]; r.t = this.t; r.s = this.s; } // (protected) set from integer value x, -DV <= x < DV function bnpFromInt(x) { var this_array = this.array; this.t = 1; this.s = (x<0)?-1:0; if(x > 0) this_array[0] = x; else if(x < -1) this_array[0] = x+DV; else this.t = 0; } // return bigint initialized to value function nbv(i) { var r = nbi(); r.fromInt(i); return r; } // (protected) set from string and radix function bnpFromString(s,b) { var this_array = this.array; var k; if(b == 16) k = 4; else if(b == 8) k = 3; else if(b == 256) k = 8; // byte array else if(b == 2) k = 1; else if(b == 32) k = 5; else if(b == 4) k = 2; else { this.fromRadix(s,b); return; } this.t = 0; this.s = 0; var i = s.length, mi = false, sh = 0; while(--i >= 0) { var x = (k==8)?s[i]&0xff:intAt(s,i); if(x < 0) { if(s.charAt(i) == "-") mi = true; continue; } mi = false; if(sh == 0) this_array[this.t++] = x; else if(sh+k > BI_DB) { this_array[this.t-1] |= (x&((1<<(BI_DB-sh))-1))<>(BI_DB-sh)); } else this_array[this.t-1] |= x<= BI_DB) sh -= BI_DB; } if(k == 8 && (s[0]&0x80) != 0) { this.s = -1; if(sh > 0) this_array[this.t-1] |= ((1<<(BI_DB-sh))-1)< 0 && this_array[this.t-1] == c) --this.t; } // (public) return string representation in given radix function bnToString(b) { var this_array = this.array; if(this.s < 0) return "-"+this.negate().toString(b); var k; if(b == 16) k = 4; else if(b == 8) k = 3; else if(b == 2) k = 1; else if(b == 32) k = 5; else if(b == 4) k = 2; else return this.toRadix(b); var km = (1< 0) { if(p < BI_DB && (d = this_array[i]>>p) > 0) { m = true; r = int2char(d); } while(i >= 0) { if(p < k) { d = (this_array[i]&((1<>(p+=BI_DB-k); } else { d = (this_array[i]>>(p-=k))&km; if(p <= 0) { p += BI_DB; --i; } } if(d > 0) m = true; if(m) r += int2char(d); } } return m?r:"0"; } // (public) -this function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } // (public) |this| function bnAbs() { return (this.s<0)?this.negate():this; } // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a) { var this_array = this.array; var a_array = a.array; var r = this.s-a.s; if(r != 0) return r; var i = this.t; r = i-a.t; if(r != 0) return r; while(--i >= 0) if((r=this_array[i]-a_array[i]) != 0) return r; return 0; } // returns bit length of the integer x function nbits(x) { var r = 1, t; if((t=x>>>16) != 0) { x = t; r += 16; } if((t=x>>8) != 0) { x = t; r += 8; } if((t=x>>4) != 0) { x = t; r += 4; } if((t=x>>2) != 0) { x = t; r += 2; } if((t=x>>1) != 0) { x = t; r += 1; } return r; } // (public) return the number of bits in "this" function bnBitLength() { var this_array = this.array; if(this.t <= 0) return 0; return BI_DB*(this.t-1)+nbits(this_array[this.t-1]^(this.s&BI_DM)); } // (protected) r = this << n*DB function bnpDLShiftTo(n,r) { var this_array = this.array; var r_array = r.array; var i; for(i = this.t-1; i >= 0; --i) r_array[i+n] = this_array[i]; for(i = n-1; i >= 0; --i) r_array[i] = 0; r.t = this.t+n; r.s = this.s; } // (protected) r = this >> n*DB function bnpDRShiftTo(n,r) { var this_array = this.array; var r_array = r.array; for(var i = n; i < this.t; ++i) r_array[i-n] = this_array[i]; r.t = Math.max(this.t-n,0); r.s = this.s; } // (protected) r = this << n function bnpLShiftTo(n,r) { var this_array = this.array; var r_array = r.array; var bs = n%BI_DB; var cbs = BI_DB-bs; var bm = (1<= 0; --i) { r_array[i+ds+1] = (this_array[i]>>cbs)|c; c = (this_array[i]&bm)<= 0; --i) r_array[i] = 0; r_array[ds] = c; r.t = this.t+ds+1; r.s = this.s; r.clamp(); } // (protected) r = this >> n function bnpRShiftTo(n,r) { var this_array = this.array; var r_array = r.array; r.s = this.s; var ds = Math.floor(n/BI_DB); if(ds >= this.t) { r.t = 0; return; } var bs = n%BI_DB; var cbs = BI_DB-bs; var bm = (1<>bs; for(var i = ds+1; i < this.t; ++i) { r_array[i-ds-1] |= (this_array[i]&bm)<>bs; } if(bs > 0) r_array[this.t-ds-1] |= (this.s&bm)<>= BI_DB; } if(a.t < this.t) { c -= a.s; while(i < this.t) { c += this_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += this.s; } else { c += this.s; while(i < a.t) { c -= a_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c -= a.s; } r.s = (c<0)?-1:0; if(c < -1) r_array[i++] = BI_DV+c; else if(c > 0) r_array[i++] = c; r.t = i; r.clamp(); } // (protected) r = this * a, r != this,a (HAC 14.12) // "this" should be the larger one if appropriate. function bnpMultiplyTo(a,r) { var this_array = this.array; var r_array = r.array; var x = this.abs(), y = a.abs(); var y_array = y.array; var i = x.t; r.t = i+y.t; while(--i >= 0) r_array[i] = 0; for(i = 0; i < y.t; ++i) r_array[i+x.t] = x.am(0,y_array[i],r,i,0,x.t); r.s = 0; r.clamp(); if(this.s != a.s) BigInteger.ZERO.subTo(r,r); } // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { var x = this.abs(); var x_array = x.array; var r_array = r.array; var i = r.t = 2*x.t; while(--i >= 0) r_array[i] = 0; for(i = 0; i < x.t-1; ++i) { var c = x.am(i,x_array[i],r,2*i,0,1); if((r_array[i+x.t]+=x.am(i+1,2*x_array[i],r,2*i+1,c,x.t-i-1)) >= BI_DV) { r_array[i+x.t] -= BI_DV; r_array[i+x.t+1] = 1; } } if(r.t > 0) r_array[r.t-1] += x.am(i,x_array[i],r,2*i,0,1); r.s = 0; r.clamp(); } // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { var pm = m.abs(); if(pm.t <= 0) return; var pt = this.abs(); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); return; } if(r == null) r = nbi(); var y = nbi(), ts = this.s, ms = m.s; var pm_array = pm.array; var nsh = BI_DB-nbits(pm_array[pm.t-1]); // normalize modulus if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; var y_array = y.array; var y0 = y_array[ys-1]; if(y0 == 0) return; var yt = y0*(1<1)?y_array[ys-2]>>BI_F2:0); var d1 = BI_FV/yt, d2 = (1<= 0) { r_array[r.t++] = 1; r.subTo(t,r); } BigInteger.ONE.dlShiftTo(ys,t); t.subTo(y,y); // "negative" y so we can replace sub with am later while(y.t < ys) y_array[y.t++] = 0; while(--j >= 0) { // Estimate quotient digit var qd = (r_array[--i]==y0)?BI_DM:Math.floor(r_array[i]*d1+(r_array[i-1]+e)*d2); if((r_array[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out y.dlShiftTo(j,t); r.subTo(t,r); while(r_array[i] < --qd) r.subTo(t,r); } } if(q != null) { r.drShiftTo(ys,q); if(ts != ms) BigInteger.ZERO.subTo(q,q); } r.t = ys; r.clamp(); if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder if(ts < 0) BigInteger.ZERO.subTo(r,r); } // (public) this mod a function bnMod(a) { var r = nbi(); this.abs().divRemTo(a,null,r); if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); return r; } // Modular reduction using "classic" algorithm function Classic(m) { this.m = m; } function cConvert(x) { if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); else return x; } function cRevert(x) { return x; } function cReduce(x) { x.divRemTo(this.m,null,x); } function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } Classic.prototype.convert = cConvert; Classic.prototype.revert = cRevert; Classic.prototype.reduce = cReduce; Classic.prototype.mulTo = cMulTo; Classic.prototype.sqrTo = cSqrTo; // (protected) return "-1/this % 2^DB"; useful for Mont. reduction // justification: // xy == 1 (mod m) // xy = 1+km // xy(2-xy) = (1+km)(1-km) // x[y(2-xy)] = 1-k^2m^2 // x[y(2-xy)] == 1 (mod m^2) // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. // JS multiply "overflows" differently from C/C++, so care is needed here. function bnpInvDigit() { var this_array = this.array; if(this.t < 1) return 0; var x = this_array[0]; if((x&1) == 0) return 0; var y = x&3; // y == 1/x mod 2^2 y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 // last step - calculate inverse mod DV directly; // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints y = (y*(2-x*y%BI_DV))%BI_DV; // y == 1/x mod 2^dbits // we really want the negative inverse, and -DV < y < DV return (y>0)?BI_DV-y:-y; } // Montgomery reduction function Montgomery(m) { this.m = m; this.mp = m.invDigit(); this.mpl = this.mp&0x7fff; this.mph = this.mp>>15; this.um = (1<<(BI_DB-15))-1; this.mt2 = 2*m.t; } // xR mod m function montConvert(x) { var r = nbi(); x.abs().dlShiftTo(this.m.t,r); r.divRemTo(this.m,null,r); if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); return r; } // x/R mod m function montRevert(x) { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } // x = x/R mod m (HAC 14.32) function montReduce(x) { var x_array = x.array; while(x.t <= this.mt2) // pad x so am has enough room later x_array[x.t++] = 0; for(var i = 0; i < this.m.t; ++i) { // faster way of calculating u0 = x[i]*mp mod DV var j = x_array[i]&0x7fff; var u0 = (j*this.mpl+(((j*this.mph+(x_array[i]>>15)*this.mpl)&this.um)<<15))&BI_DM; // use am to combine the multiply-shift-add into one call j = i+this.m.t; x_array[j] += this.m.am(0,u0,x,i,0,this.m.t); // propagate carry while(x_array[j] >= BI_DV) { x_array[j] -= BI_DV; x_array[++j]++; } } x.clamp(); x.drShiftTo(this.m.t,x); if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); } // r = "x^2/R mod m"; x != r function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } // r = "xy/R mod m"; x,y != r function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } Montgomery.prototype.convert = montConvert; Montgomery.prototype.revert = montRevert; Montgomery.prototype.reduce = montReduce; Montgomery.prototype.mulTo = montMulTo; Montgomery.prototype.sqrTo = montSqrTo; // (protected) true iff this is even function bnpIsEven() { var this_array = this.array; return ((this.t>0)?(this_array[0]&1):this.s) == 0; } // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) function bnpExp(e,z) { if(e > 0xffffffff || e < 1) return BigInteger.ONE; var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; g.copyTo(r); while(--i >= 0) { z.sqrTo(r,r2); if((e&(1< 0) z.mulTo(r2,g,r); else { var t = r; r = r2; r2 = t; } } return z.revert(r); } // (public) this^e % m, 0 <= e < 2^32 function bnModPowInt(e,m) { var z; if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); return this.exp(e,z); } // protected BigInteger.prototype.copyTo = bnpCopyTo; BigInteger.prototype.fromInt = bnpFromInt; BigInteger.prototype.fromString = bnpFromString; BigInteger.prototype.clamp = bnpClamp; BigInteger.prototype.dlShiftTo = bnpDLShiftTo; BigInteger.prototype.drShiftTo = bnpDRShiftTo; BigInteger.prototype.lShiftTo = bnpLShiftTo; BigInteger.prototype.rShiftTo = bnpRShiftTo; BigInteger.prototype.subTo = bnpSubTo; BigInteger.prototype.multiplyTo = bnpMultiplyTo; BigInteger.prototype.squareTo = bnpSquareTo; BigInteger.prototype.divRemTo = bnpDivRemTo; BigInteger.prototype.invDigit = bnpInvDigit; BigInteger.prototype.isEven = bnpIsEven; BigInteger.prototype.exp = bnpExp; // public BigInteger.prototype.toString = bnToString; BigInteger.prototype.negate = bnNegate; BigInteger.prototype.abs = bnAbs; BigInteger.prototype.compareTo = bnCompareTo; BigInteger.prototype.bitLength = bnBitLength; BigInteger.prototype.mod = bnMod; BigInteger.prototype.modPowInt = bnModPowInt; // "constants" BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1); // Copyright (c) 2005 Tom Wu // All Rights Reserved. // See "LICENSE" for details. // Extended JavaScript BN functions, required for RSA private ops. // (public) function bnClone() { var r = nbi(); this.copyTo(r); return r; } // (public) return value as integer function bnIntValue() { var this_array = this.array; if(this.s < 0) { if(this.t == 1) return this_array[0]-BI_DV; else if(this.t == 0) return -1; } else if(this.t == 1) return this_array[0]; else if(this.t == 0) return 0; // assumes 16 < DB < 32 return ((this_array[1]&((1<<(32-BI_DB))-1))<>24; } // (public) return value as short (assumes DB>=16) function bnShortValue() { var this_array = this.array; return (this.t==0)?this.s:(this_array[0]<<16)>>16; } // (protected) return x s.t. r^x < DV function bnpChunkSize(r) { return Math.floor(Math.LN2*BI_DB/Math.log(r)); } // (public) 0 if this == 0, 1 if this > 0 function bnSigNum() { var this_array = this.array; if(this.s < 0) return -1; else if(this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0; else return 1; } // (protected) convert to radix string function bnpToRadix(b) { if(b == null) b = 10; if(this.signum() == 0 || b < 2 || b > 36) return "0"; var cs = this.chunkSize(b); var a = Math.pow(b,cs); var d = nbv(a), y = nbi(), z = nbi(), r = ""; this.divRemTo(d,y,z); while(y.signum() > 0) { r = (a+z.intValue()).toString(b).substr(1) + r; y.divRemTo(d,y,z); } return z.intValue().toString(b) + r; } // (protected) convert from radix string function bnpFromRadix(s,b) { this.fromInt(0); if(b == null) b = 10; var cs = this.chunkSize(b); var d = Math.pow(b,cs), mi = false, j = 0, w = 0; for(var i = 0; i < s.length; ++i) { var x = intAt(s,i); if(x < 0) { if(s.charAt(i) == "-" && this.signum() == 0) mi = true; continue; } w = b*w+x; if(++j >= cs) { this.dMultiply(d); this.dAddOffset(w,0); j = 0; w = 0; } } if(j > 0) { this.dMultiply(Math.pow(b,j)); this.dAddOffset(w,0); } if(mi) BigInteger.ZERO.subTo(this,this); } // (protected) alternate constructor function bnpFromNumber(a,b,c) { if("number" == typeof b) { // new BigInteger(int,int,RNG) if(a < 2) this.fromInt(1); else { this.fromNumber(a,c); if(!this.testBit(a-1)) // force MSB set this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); if(this.isEven()) this.dAddOffset(1,0); // force odd while(!this.isProbablePrime(b)) { this.dAddOffset(2,0); if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); } } } else { // new BigInteger(int,RNG) var x = new Array(), t = a&7; x.length = (a>>3)+1; b.nextBytes(x); if(t > 0) x[0] &= ((1< 0) { if(p < BI_DB && (d = this_array[i]>>p) != (this.s&BI_DM)>>p) r[k++] = d|(this.s<<(BI_DB-p)); while(i >= 0) { if(p < 8) { d = (this_array[i]&((1<>(p+=BI_DB-8); } else { d = (this_array[i]>>(p-=8))&0xff; if(p <= 0) { p += BI_DB; --i; } } if((d&0x80) != 0) d |= -256; if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; if(k > 0 || d != this.s) r[k++] = d; } } return r; } function bnEquals(a) { return(this.compareTo(a)==0); } function bnMin(a) { return(this.compareTo(a)<0)?this:a; } function bnMax(a) { return(this.compareTo(a)>0)?this:a; } // (protected) r = this op a (bitwise) function bnpBitwiseTo(a,op,r) { var this_array = this.array; var a_array = a.array; var r_array = r.array; var i, f, m = Math.min(a.t,this.t); for(i = 0; i < m; ++i) r_array[i] = op(this_array[i],a_array[i]); if(a.t < this.t) { f = a.s&BI_DM; for(i = m; i < this.t; ++i) r_array[i] = op(this_array[i],f); r.t = this.t; } else { f = this.s&BI_DM; for(i = m; i < a.t; ++i) r_array[i] = op(f,a_array[i]); r.t = a.t; } r.s = op(this.s,a.s); r.clamp(); } // (public) this & a function op_and(x,y) { return x&y; } function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } // (public) this | a function op_or(x,y) { return x|y; } function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } // (public) this ^ a function op_xor(x,y) { return x^y; } function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } // (public) this & ~a function op_andnot(x,y) { return x&~y; } function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } // (public) ~this function bnNot() { var this_array = this.array; var r = nbi(); var r_array = r.array; for(var i = 0; i < this.t; ++i) r_array[i] = BI_DM&~this_array[i]; r.t = this.t; r.s = ~this.s; return r; } // (public) this << n function bnShiftLeft(n) { var r = nbi(); if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); return r; } // (public) this >> n function bnShiftRight(n) { var r = nbi(); if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); return r; } // return index of lowest 1-bit in x, x < 2^31 function lbit(x) { if(x == 0) return -1; var r = 0; if((x&0xffff) == 0) { x >>= 16; r += 16; } if((x&0xff) == 0) { x >>= 8; r += 8; } if((x&0xf) == 0) { x >>= 4; r += 4; } if((x&3) == 0) { x >>= 2; r += 2; } if((x&1) == 0) ++r; return r; } // (public) returns index of lowest 1-bit (or -1 if none) function bnGetLowestSetBit() { var this_array = this.array; for(var i = 0; i < this.t; ++i) if(this_array[i] != 0) return i*BI_DB+lbit(this_array[i]); if(this.s < 0) return this.t*BI_DB; return -1; } // return number of 1 bits in x function cbit(x) { var r = 0; while(x != 0) { x &= x-1; ++r; } return r; } // (public) return number of set bits function bnBitCount() { var r = 0, x = this.s&BI_DM; for(var i = 0; i < this.t; ++i) r += cbit(this_array[i]^x); return r; } // (public) true iff nth bit is set function bnTestBit(n) { var this_array = this.array; var j = Math.floor(n/BI_DB); if(j >= this.t) return(this.s!=0); return((this_array[j]&(1<<(n%BI_DB)))!=0); } // (protected) this op (1<>= BI_DB; } if(a.t < this.t) { c += a.s; while(i < this.t) { c += this_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += this.s; } else { c += this.s; while(i < a.t) { c += a_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += a.s; } r.s = (c<0)?-1:0; if(c > 0) r_array[i++] = c; else if(c < -1) r_array[i++] = BI_DV+c; r.t = i; r.clamp(); } // (public) this + a function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } // (public) this - a function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } // (public) this * a function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } // (public) this / a function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } // (public) this % a function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } // (public) [this/a,this%a] function bnDivideAndRemainder(a) { var q = nbi(), r = nbi(); this.divRemTo(a,q,r); return new Array(q,r); } // (protected) this *= n, this >= 0, 1 < n < DV function bnpDMultiply(n) { var this_array = this.array; this_array[this.t] = this.am(0,n-1,this,0,0,this.t); ++this.t; this.clamp(); } // (protected) this += n << w words, this >= 0 function bnpDAddOffset(n,w) { var this_array = this.array; while(this.t <= w) this_array[this.t++] = 0; this_array[w] += n; while(this_array[w] >= BI_DV) { this_array[w] -= BI_DV; if(++w >= this.t) this_array[this.t++] = 0; ++this_array[w]; } } // A "null" reducer function NullExp() {} function nNop(x) { return x; } function nMulTo(x,y,r) { x.multiplyTo(y,r); } function nSqrTo(x,r) { x.squareTo(r); } NullExp.prototype.convert = nNop; NullExp.prototype.revert = nNop; NullExp.prototype.mulTo = nMulTo; NullExp.prototype.sqrTo = nSqrTo; // (public) this^e function bnPow(e) { return this.exp(e,new NullExp()); } // (protected) r = lower n words of "this * a", a.t <= n // "this" should be the larger one if appropriate. function bnpMultiplyLowerTo(a,n,r) { var r_array = r.array; var a_array = a.array; var i = Math.min(this.t+a.t,n); r.s = 0; // assumes a,this >= 0 r.t = i; while(i > 0) r_array[--i] = 0; var j; for(j = r.t-this.t; i < j; ++i) r_array[i+this.t] = this.am(0,a_array[i],r,i,0,this.t); for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a_array[i],r,i,0,n-i); r.clamp(); } // (protected) r = "this * a" without lower n words, n > 0 // "this" should be the larger one if appropriate. function bnpMultiplyUpperTo(a,n,r) { var r_array = r.array; var a_array = a.array; --n; var i = r.t = this.t+a.t-n; r.s = 0; // assumes a,this >= 0 while(--i >= 0) r_array[i] = 0; for(i = Math.max(n-this.t,0); i < a.t; ++i) r_array[this.t+i-n] = this.am(n-i,a_array[i],r,0,0,this.t+i-n); r.clamp(); r.drShiftTo(1,r); } // Barrett modular reduction function Barrett(m) { // setup Barrett this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2*m.t,this.r2); this.mu = this.r2.divide(m); this.m = m; } function barrettConvert(x) { if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); else if(x.compareTo(this.m) < 0) return x; else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } } function barrettRevert(x) { return x; } // x = x mod m (HAC 14.42) function barrettReduce(x) { x.drShiftTo(this.m.t-1,this.r2); if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); x.subTo(this.r2,x); while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); } // r = x^2 mod m; x != r function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } // r = x*y mod m; x,y != r function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } Barrett.prototype.convert = barrettConvert; Barrett.prototype.revert = barrettRevert; Barrett.prototype.reduce = barrettReduce; Barrett.prototype.mulTo = barrettMulTo; Barrett.prototype.sqrTo = barrettSqrTo; // (public) this^e % m (HAC 14.85) function bnModPow(e,m) { var e_array = e.array; var i = e.bitLength(), k, r = nbv(1), z; if(i <= 0) return r; else if(i < 18) k = 1; else if(i < 48) k = 3; else if(i < 144) k = 4; else if(i < 768) k = 5; else k = 6; if(i < 8) z = new Classic(m); else if(m.isEven()) z = new Barrett(m); else z = new Montgomery(m); // precomputation var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { var g2 = nbi(); z.sqrTo(g[1],g2); while(n <= km) { g[n] = nbi(); z.mulTo(g2,g[n-2],g[n]); n += 2; } } var j = e.t-1, w, is1 = true, r2 = nbi(), t; i = nbits(e_array[j])-1; while(j >= 0) { if(i >= k1) w = (e_array[j]>>(i-k1))&km; else { w = (e_array[j]&((1<<(i+1))-1))<<(k1-i); if(j > 0) w |= e_array[j-1]>>(BI_DB+i-k1); } n = k; while((w&1) == 0) { w >>= 1; --n; } if((i -= n) < 0) { i += BI_DB; --j; } if(is1) { // ret == 1, don't bother squaring or multiplying it g[w].copyTo(r); is1 = false; } else { while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } z.mulTo(r2,g[w],r); } while(j >= 0 && (e_array[j]&(1< 0) { x.rShiftTo(g,x); y.rShiftTo(g,y); } while(x.signum() > 0) { if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); if(x.compareTo(y) >= 0) { x.subTo(y,x); x.rShiftTo(1,x); } else { y.subTo(x,y); y.rShiftTo(1,y); } } if(g > 0) y.lShiftTo(g,y); return y; } // (protected) this % n, n < 2^26 function bnpModInt(n) { var this_array = this.array; if(n <= 0) return 0; var d = BI_DV%n, r = (this.s<0)?n-1:0; if(this.t > 0) if(d == 0) r = this_array[0]%n; else for(var i = this.t-1; i >= 0; --i) r = (d*r+this_array[i])%n; return r; } // (public) 1/this % m (HAC 14.61) function bnModInverse(m) { var ac = m.isEven(); if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; var u = m.clone(), v = this.clone(); var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); while(u.signum() != 0) { while(u.isEven()) { u.rShiftTo(1,u); if(ac) { if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } a.rShiftTo(1,a); } else if(!b.isEven()) b.subTo(m,b); b.rShiftTo(1,b); } while(v.isEven()) { v.rShiftTo(1,v); if(ac) { if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } c.rShiftTo(1,c); } else if(!d.isEven()) d.subTo(m,d); d.rShiftTo(1,d); } if(u.compareTo(v) >= 0) { u.subTo(v,u); if(ac) a.subTo(c,a); b.subTo(d,b); } else { v.subTo(u,v); if(ac) c.subTo(a,c); d.subTo(b,d); } } if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; if(d.compareTo(m) >= 0) return d.subtract(m); if(d.signum() < 0) d.addTo(m,d); else return d; if(d.signum() < 0) return d.add(m); else return d; } var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; var lplim = (1<<26)/lowprimes[lowprimes.length-1]; // (public) test primality with certainty >= 1-.5^t function bnIsProbablePrime(t) { var i, x = this.abs(); var x_array = x.array; if(x.t == 1 && x_array[0] <= lowprimes[lowprimes.length-1]) { for(i = 0; i < lowprimes.length; ++i) if(x_array[0] == lowprimes[i]) return true; return false; } if(x.isEven()) return false; i = 1; while(i < lowprimes.length) { var m = lowprimes[i], j = i+1; while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; m = x.modInt(m); while(i < j) if(m%lowprimes[i++] == 0) return false; } return x.millerRabin(t); } // (protected) true if probably prime (HAC 4.24, Miller-Rabin) function bnpMillerRabin(t) { var n1 = this.subtract(BigInteger.ONE); var k = n1.getLowestSetBit(); if(k <= 0) return false; var r = n1.shiftRight(k); t = (t+1)>>1; if(t > lowprimes.length) t = lowprimes.length; var a = nbi(); for(var i = 0; i < t; ++i) { a.fromInt(lowprimes[i]); var y = a.modPow(r,this); if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { var j = 1; while(j++ < k && y.compareTo(n1) != 0) { y = y.modPowInt(2,this); if(y.compareTo(BigInteger.ONE) == 0) return false; } if(y.compareTo(n1) != 0) return false; } } return true; } // protected BigInteger.prototype.chunkSize = bnpChunkSize; BigInteger.prototype.toRadix = bnpToRadix; BigInteger.prototype.fromRadix = bnpFromRadix; BigInteger.prototype.fromNumber = bnpFromNumber; BigInteger.prototype.bitwiseTo = bnpBitwiseTo; BigInteger.prototype.changeBit = bnpChangeBit; BigInteger.prototype.addTo = bnpAddTo; BigInteger.prototype.dMultiply = bnpDMultiply; BigInteger.prototype.dAddOffset = bnpDAddOffset; BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; BigInteger.prototype.modInt = bnpModInt; BigInteger.prototype.millerRabin = bnpMillerRabin; // public BigInteger.prototype.clone = bnClone; BigInteger.prototype.intValue = bnIntValue; BigInteger.prototype.byteValue = bnByteValue; BigInteger.prototype.shortValue = bnShortValue; BigInteger.prototype.signum = bnSigNum; BigInteger.prototype.toByteArray = bnToByteArray; BigInteger.prototype.equals = bnEquals; BigInteger.prototype.min = bnMin; BigInteger.prototype.max = bnMax; BigInteger.prototype.and = bnAnd; BigInteger.prototype.or = bnOr; BigInteger.prototype.xor = bnXor; BigInteger.prototype.andNot = bnAndNot; BigInteger.prototype.not = bnNot; BigInteger.prototype.shiftLeft = bnShiftLeft; BigInteger.prototype.shiftRight = bnShiftRight; BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; BigInteger.prototype.bitCount = bnBitCount; BigInteger.prototype.testBit = bnTestBit; BigInteger.prototype.setBit = bnSetBit; BigInteger.prototype.clearBit = bnClearBit; BigInteger.prototype.flipBit = bnFlipBit; BigInteger.prototype.add = bnAdd; BigInteger.prototype.subtract = bnSubtract; BigInteger.prototype.multiply = bnMultiply; BigInteger.prototype.divide = bnDivide; BigInteger.prototype.remainder = bnRemainder; BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; BigInteger.prototype.modPow = bnModPow; BigInteger.prototype.modInverse = bnModInverse; BigInteger.prototype.pow = bnPow; BigInteger.prototype.gcd = bnGCD; BigInteger.prototype.isProbablePrime = bnIsProbablePrime; // BigInteger interfaces not implemented in jsbn: // BigInteger(int signum, byte[] magnitude) // double doubleValue() // float floatValue() // int hashCode() // long longValue() // static BigInteger valueOf(long val) // prng4.js - uses Arcfour as a PRNG function Arcfour() { this.i = 0; this.j = 0; this.S = new Array(); } // Initialize arcfour context from key, an array of ints, each from [0..255] function ARC4init(key) { var i, j, t; for(i = 0; i < 256; ++i) this.S[i] = i; j = 0; for(i = 0; i < 256; ++i) { j = (j + this.S[i] + key[i % key.length]) & 255; t = this.S[i]; this.S[i] = this.S[j]; this.S[j] = t; } this.i = 0; this.j = 0; } function ARC4next() { var t; this.i = (this.i + 1) & 255; this.j = (this.j + this.S[this.i]) & 255; t = this.S[this.i]; this.S[this.i] = this.S[this.j]; this.S[this.j] = t; return this.S[(t + this.S[this.i]) & 255]; } Arcfour.prototype.init = ARC4init; Arcfour.prototype.next = ARC4next; // Plug in your RNG constructor here function prng_newstate() { return new Arcfour(); } // Pool size must be a multiple of 4 and greater than 32. // An array of bytes the size of the pool will be passed to init() var rng_psize = 256; // Random number generator - requires a PRNG backend, e.g. prng4.js // For best results, put code like // // in your main HTML document. var rng_state; var rng_pool; var rng_pptr; // Mix in a 32-bit integer into the pool function rng_seed_int(x) { rng_pool[rng_pptr++] ^= x & 255; rng_pool[rng_pptr++] ^= (x >> 8) & 255; rng_pool[rng_pptr++] ^= (x >> 16) & 255; rng_pool[rng_pptr++] ^= (x >> 24) & 255; if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; } // Mix in the current time (w/milliseconds) into the pool function rng_seed_time() { // Use pre-computed date to avoid making the benchmark // results dependent on the current date. rng_seed_int(1122926989487); } // Initialize the pool with junk if needed. if(rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; while(rng_pptr < rng_psize) { // extract some randomness from Math.random() t = Math.floor(65536 * Math.random()); rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t & 255; } rng_pptr = 0; rng_seed_time(); //rng_seed_int(window.screenX); //rng_seed_int(window.screenY); } function rng_get_byte() { if(rng_state == null) { rng_seed_time(); rng_state = prng_newstate(); rng_state.init(rng_pool); for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) rng_pool[rng_pptr] = 0; rng_pptr = 0; //rng_pool = null; } // TODO: allow reseeding after first request return rng_state.next(); } function rng_get_bytes(ba) { var i; for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); } function SecureRandom() {} SecureRandom.prototype.nextBytes = rng_get_bytes; // Depends on jsbn.js and rng.js // convert a (hex) string to a bignum object function parseBigInt(str,r) { return new BigInteger(str,r); } function linebrk(s,n) { var ret = ""; var i = 0; while(i + n < s.length) { ret += s.substring(i,i+n) + "\n"; i += n; } return ret + s.substring(i,s.length); } function byte2Hex(b) { if(b < 0x10) return "0" + b.toString(16); else return b.toString(16); } // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint function pkcs1pad2(s,n) { if(n < s.length + 11) { alert("Message too long for RSA"); return null; } var ba = new Array(); var i = s.length - 1; while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--); ba[--n] = 0; var rng = new SecureRandom(); var x = new Array(); while(n > 2) { // random non-zero pad x[0] = 0; while(x[0] == 0) rng.nextBytes(x); ba[--n] = x[0]; } ba[--n] = 2; ba[--n] = 0; return new BigInteger(ba); } // "empty" RSA key constructor function RSAKey() { this.n = null; this.e = 0; this.d = null; this.p = null; this.q = null; this.dmp1 = null; this.dmq1 = null; this.coeff = null; } // Set the public key fields N and e from hex strings function RSASetPublic(N,E) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); } else alert("Invalid RSA public key"); } // Perform raw public operation on "x": return x^e (mod n) function RSADoPublic(x) { return x.modPowInt(this.e, this.n); } // Return the PKCS#1 RSA encryption of "text" as an even-length hex string function RSAEncrypt(text) { var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); if(m == null) return null; var c = this.doPublic(m); if(c == null) return null; var h = c.toString(16); if((h.length & 1) == 0) return h; else return "0" + h; } // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string //function RSAEncryptB64(text) { // var h = this.encrypt(text); // if(h) return hex2b64(h); else return null; //} // protected RSAKey.prototype.doPublic = RSADoPublic; // public RSAKey.prototype.setPublic = RSASetPublic; RSAKey.prototype.encrypt = RSAEncrypt; //RSAKey.prototype.encrypt_b64 = RSAEncryptB64; // Depends on rsa.js and jsbn2.js // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext function pkcs1unpad2(d,n) { var b = d.toByteArray(); var i = 0; while(i < b.length && b[i] == 0) ++i; if(b.length-i != n-1 || b[i] != 2) return null; ++i; while(b[i] != 0) if(++i >= b.length) return null; var ret = ""; while(++i < b.length) ret += String.fromCharCode(b[i]); return ret; } // Set the private key fields N, e, and d from hex strings function RSASetPrivate(N,E,D) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); this.d = parseBigInt(D,16); } else alert("Invalid RSA private key"); } // Set the private key fields N, e, d and CRT params from hex strings function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); this.d = parseBigInt(D,16); this.p = parseBigInt(P,16); this.q = parseBigInt(Q,16); this.dmp1 = parseBigInt(DP,16); this.dmq1 = parseBigInt(DQ,16); this.coeff = parseBigInt(C,16); } else alert("Invalid RSA private key"); } // Generate a new random private key B bits long, using public expt E function RSAGenerate(B,E) { var rng = new SecureRandom(); var qs = B>>1; this.e = parseInt(E,16); var ee = new BigInteger(E,16); for(;;) { for(;;) { this.p = new BigInteger(B-qs,1,rng); if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; } for(;;) { this.q = new BigInteger(qs,1,rng); if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; } if(this.p.compareTo(this.q) <= 0) { var t = this.p; this.p = this.q; this.q = t; } var p1 = this.p.subtract(BigInteger.ONE); var q1 = this.q.subtract(BigInteger.ONE); var phi = p1.multiply(q1); if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { this.n = this.p.multiply(this.q); this.d = ee.modInverse(phi); this.dmp1 = this.d.mod(p1); this.dmq1 = this.d.mod(q1); this.coeff = this.q.modInverse(this.p); break; } } } // Perform raw private operation on "x": return x^d (mod n) function RSADoPrivate(x) { if(this.p == null || this.q == null) return x.modPow(this.d, this.n); // TODO: re-calculate any missing CRT params var xp = x.mod(this.p).modPow(this.dmp1, this.p); var xq = x.mod(this.q).modPow(this.dmq1, this.q); while(xp.compareTo(xq) < 0) xp = xp.add(this.p); return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); } // Return the PKCS#1 RSA decryption of "ctext". // "ctext" is an even-length hex string and the output is a plain string. function RSADecrypt(ctext) { var c = parseBigInt(ctext, 16); var m = this.doPrivate(c); if(m == null) return null; return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); } // Return the PKCS#1 RSA decryption of "ctext". // "ctext" is a Base64-encoded string and the output is a plain string. //function RSAB64Decrypt(ctext) { // var h = b64tohex(ctext); // if(h) return this.decrypt(h); else return null; //} // protected RSAKey.prototype.doPrivate = RSADoPrivate; // public RSAKey.prototype.setPrivate = RSASetPrivate; RSAKey.prototype.setPrivateEx = RSASetPrivateEx; RSAKey.prototype.generate = RSAGenerate; RSAKey.prototype.decrypt = RSADecrypt; //RSAKey.prototype.b64_decrypt = RSAB64Decrypt; nValue="a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3"; eValue="10001"; dValue="8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161"; pValue="d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d"; qValue="cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f"; dmp1Value="1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25"; dmq1Value="3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd"; coeffValue="3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250"; setupEngine(am3, 28); var TEXT = "The quick brown fox jumped over the extremely lazy frog! " + "Now is the time for all good men to come to the party."; var encrypted; function encrypt() { var RSA = new RSAKey(); RSA.setPublic(nValue, eValue); RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); encrypted = RSA.encrypt(TEXT); } function decrypt() { var RSA = new RSAKey(); RSA.setPublic(nValue, eValue); RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); var decrypted = RSA.decrypt(encrypted); if (decrypted != TEXT) { throw new Error("Crypto operation failed"); } } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/README.txt0000644000175000017500000000621314433667662026025 0ustar apoapoV8 Benchmark Suite ================== This is the V8 benchmark suite: A collection of pure JavaScript benchmarks that we have used to tune V8. The licenses for the individual benchmarks are included in the JavaScript files. In addition to the benchmarks, the suite consists of the benchmark framework (base.js), which must be loaded before any of the individual benchmark files, and two benchmark runners: An HTML version (run.html) and a standalone JavaScript version (run.js). Changes From Version 1 To Version 2 =================================== For version 2 the crypto benchmark was fixed. Previously, the decryption stage was given plaintext as input, which resulted in an error. Now, the decryption stage is given the output of the encryption stage as input. The result is checked against the original plaintext. For this to give the correct results the crypto objects are reset for each iteration of the benchmark. In addition, the size of the plain text has been increased a little and the use of Math.random() and new Date() to build an RNG pool has been removed. Other benchmarks were fixed to do elementary verification of the results of their calculations. This is to avoid accidentally obtaining scores that are the result of an incorrect JavaScript engine optimization. Changes From Version 2 To Version 3 =================================== Version 3 adds a new benchmark, RegExp. The RegExp benchmark is generated by loading 50 of the most popular pages on the web and logging all regexp operations performed. Each operation is given a weight that is calculated from an estimate of the popularity of the pages where it occurs and the number of times it is executed while loading each page. Finally the literal letters in the data are encoded using ROT13 in a way that does not affect how the regexps match their input. Changes from Version 3 to Version 4 =================================== The Splay benchmark is a newcomer in version 4. It manipulates a splay tree by adding and removing data nodes, thus exercising the memory management subsystem of the JavaScript engine. Furthermore, all the unused parts of the Prototype library were removed from the RayTrace benchmark. This does not affect the running of the benchmark. Changes from Version 4 to Version 5 =================================== Removed duplicate line in random seed code, and changed the name of the Object.prototype.inherits function in the DeltaBlue benchmark to inheritsFrom to avoid name clashes when running in Chromium with extensions enabled. Changes from Version 5 to Version 6 =================================== Removed dead code from the RayTrace benchmark and fixed a couple of typos in the DeltaBlue implementation. Changed the Splay benchmark to avoid converting the same numeric key to a string over and over again and to avoid inserting and removing the same element repeatedly thus increasing pressure on the memory subsystem. Changed the RegExp benchmark to exercise the regular expression engine on different input strings. Furthermore, the benchmark runner was changed to run the benchmarks for at least a few times to stabilize the reported numbers on slower machines. closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/splay.js0000644000175000017500000002477214433667662026027 0ustar apoapo// Copyright 2009 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This benchmark is based on a JavaScript log processing module used // by the V8 profiler to generate execution time profiles for runs of // JavaScript applications, and it effectively measures how fast the // JavaScript engine is at allocating nodes and reclaiming the memory // used for old nodes. Because of the way splay trees work, the engine // also has to deal with a lot of changes to the large tree object // graph. var Splay = new BenchmarkSuite('Splay', 81491, [ new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown) ]); // Configuration. var kSplayTreeSize = 8000; var kSplayTreeModifications = 80; var kSplayTreePayloadDepth = 5; var splayTree = null; function GeneratePayloadTree(depth, tag) { if (depth == 0) { return { array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], string : 'String for key ' + tag + ' in leaf node' }; } else { return { left: GeneratePayloadTree(depth - 1, tag), right: GeneratePayloadTree(depth - 1, tag) }; } } function GenerateKey() { // The benchmark framework guarantees that Math.random is // deterministic; see base.js. return Math.random(); } function InsertNewNode() { // Insert new node with a unique key. var key; do { key = GenerateKey(); } while (splayTree.find(key) != null); var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key)); splayTree.insert(key, payload); return key; } function SplaySetup() { splayTree = new SplayTree(); for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode(); } function SplayTearDown() { // Allow the garbage collector to reclaim the memory // used by the splay tree no matter how we exit the // tear down function. var keys = splayTree.exportKeys(); splayTree = null; // Verify that the splay tree has the right size. var length = keys.length; if (length != kSplayTreeSize) { throw new Error("Splay tree has wrong size"); } // Verify that the splay tree has sorted, unique keys. for (var i = 0; i < length - 1; i++) { if (keys[i] >= keys[i + 1]) { throw new Error("Splay tree not sorted"); } } } function SplayRun() { // Replace a few nodes in the splay tree. for (var i = 0; i < kSplayTreeModifications; i++) { var key = InsertNewNode(); var greatest = splayTree.findGreatestLessThan(key); if (greatest == null) splayTree.remove(key); else splayTree.remove(greatest.key); } } /** * Constructs a Splay tree. A splay tree is a self-balancing binary * search tree with the additional property that recently accessed * elements are quick to access again. It performs basic operations * such as insertion, look-up and removal in O(log(n)) amortized time. * * @constructor */ function SplayTree() { }; /** * Pointer to the root node of the tree. * * @type {SplayTree.Node} * @private */ SplayTree.prototype.root_ = null; /** * @return {boolean} Whether the tree is empty. */ SplayTree.prototype.isEmpty = function() { return !this.root_; }; /** * Inserts a node into the tree with the specified key and value if * the tree does not already contain a node with the specified key. If * the value is inserted, it becomes the root of the tree. * * @param {number} key Key to insert into the tree. * @param {*} value Value to insert into the tree. */ SplayTree.prototype.insert = function(key, value) { if (this.isEmpty()) { this.root_ = new SplayTree.Node(key, value); return; } // Splay on the key to move the last node on the search path for // the key to the root of the tree. this.splay_(key); if (this.root_.key == key) { return; } var node = new SplayTree.Node(key, value); if (key > this.root_.key) { node.left = this.root_; node.right = this.root_.right; this.root_.right = null; } else { node.right = this.root_; node.left = this.root_.left; this.root_.left = null; } this.root_ = node; }; /** * Removes a node with the specified key from the tree if the tree * contains a node with this key. The removed node is returned. If the * key is not found, an exception is thrown. * * @param {number} key Key to find and remove from the tree. * @return {SplayTree.Node} The removed node. */ SplayTree.prototype.remove = function(key) { if (this.isEmpty()) { throw Error('Key not found: ' + key); } this.splay_(key); if (this.root_.key != key) { throw Error('Key not found: ' + key); } var removed = this.root_; if (!this.root_.left) { this.root_ = this.root_.right; } else { var right = this.root_.right; this.root_ = this.root_.left; // Splay to make sure that the new root has an empty right child. this.splay_(key); // Insert the original right child as the right child of the new // root. this.root_.right = right; } return removed; }; /** * Returns the node having the specified key or null if the tree doesn't contain * a node with the specified key. * * @param {number} key Key to find in the tree. * @return {SplayTree.Node} Node having the specified key. */ SplayTree.prototype.find = function(key) { if (this.isEmpty()) { return null; } this.splay_(key); return this.root_.key == key ? this.root_ : null; }; /** * @return {SplayTree.Node} Node having the maximum key value. */ SplayTree.prototype.findMax = function(opt_startNode) { if (this.isEmpty()) { return null; } var current = opt_startNode || this.root_; while (current.right) { current = current.right; } return current; }; /** * @return {SplayTree.Node} Node having the maximum key value that * is less than the specified key value. */ SplayTree.prototype.findGreatestLessThan = function(key) { if (this.isEmpty()) { return null; } // Splay on the key to move the node with the given key or the last // node on the search path to the top of the tree. this.splay_(key); // Now the result is either the root node or the greatest node in // the left subtree. if (this.root_.key < key) { return this.root_; } else if (this.root_.left) { return this.findMax(this.root_.left); } else { return null; } }; /** * @return {Array<*>} An array containing all the keys of tree's nodes. */ SplayTree.prototype.exportKeys = function() { var result = []; if (!this.isEmpty()) { this.root_.traverse_(function(node) { result.push(node.key); }); } return result; }; /** * Perform the splay operation for the given key. Moves the node with * the given key to the top of the tree. If no node has the given * key, the last node on the search path is moved to the top of the * tree. This is the simplified top-down splaying algorithm from: * "Self-adjusting Binary Search Trees" by Sleator and Tarjan * * @param {number} key Key to splay the tree on. * @private */ SplayTree.prototype.splay_ = function(key) { if (this.isEmpty()) { return; } // Create a dummy node. The use of the dummy node is a bit // counter-intuitive: The right child of the dummy node will hold // the L tree of the algorithm. The left child of the dummy node // will hold the R tree of the algorithm. Using a dummy node, left // and right will always be nodes and we avoid special cases. var dummy, left, right; dummy = left = right = new SplayTree.Node(null, null); var current = this.root_; while (true) { if (key < current.key) { if (!current.left) { break; } if (key < current.left.key) { // Rotate right. var tmp = current.left; current.left = tmp.right; tmp.right = current; current = tmp; if (!current.left) { break; } } // Link right. right.left = current; right = current; current = current.left; } else if (key > current.key) { if (!current.right) { break; } if (key > current.right.key) { // Rotate left. var tmp = current.right; current.right = tmp.left; tmp.left = current; current = tmp; if (!current.right) { break; } } // Link left. left.right = current; left = current; current = current.right; } else { break; } } // Assemble. left.right = current.left; right.left = current.right; current.left = dummy.right; current.right = dummy.left; this.root_ = current; }; /** * Constructs a Splay tree node. * * @param {number} key Key. * @param {*} value Value. */ SplayTree.Node = function(key, value) { this.key = key; this.value = value; }; /** * @type {SplayTree.Node} */ SplayTree.Node.prototype.left = null; /** * @type {SplayTree.Node} */ SplayTree.Node.prototype.right = null; /** * Performs an ordered traversal of the subtree starting at * this SplayTree.Node. * * @param {function(SplayTree.Node)} f Visitor function. * @private */ SplayTree.Node.prototype.traverse_ = function(f) { var current = this; while (current) { var left = current.left; if (left) left.traverse_(f); f(current); current = current.right; } }; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/earley-boyer.js0000644000175000017500000057534114433667662027301 0ustar apoapo// This file is automatically generated by scheme2js, except for the // benchmark harness code at the beginning and end of the file. var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 666463, [ new Benchmark("Earley", function () { BgL_earleyzd2benchmarkzd2(); }), new Benchmark("Boyer", function () { BgL_nboyerzd2benchmarkzd2(); }) ]); /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /* * To use write/prints/... the default-output port has to be set first. * Simply setting SC_DEFAULT_OUT and SC_ERROR_OUT to the desired values * should do the trick. * In the following example the std-out and error-port are redirected to * a DIV. function initRuntime() { function escapeHTML(s) { var tmp = s; tmp = tmp.replace(/&/g, "&"); tmp = tmp.replace(//g, ">"); tmp = tmp.replace(/ /g, " "); tmp = tmp.replace(/\n/g, "
      "); tmp = tmp.replace(/\t/g, "    "); return tmp; } document.write("
      "); SC_DEFAULT_OUT = new sc_GenericOutputPort( function(s) { var stdout = document.getElementById('stdout'); stdout.innerHTML = stdout.innerHTML + escapeHTML(s); }); SC_ERROR_OUT = SC_DEFAULT_OUT; } */ function sc_print_debug() { sc_print.apply(null, arguments); } /*** META ((export *js*)) */ var sc_JS_GLOBALS = this; var __sc_LINE=-1; var __sc_FILE=""; /*** META ((export #t)) */ function sc_alert() { var len = arguments.length; var s = ""; var i; for( i = 0; i < len; i++ ) { s += sc_toDisplayString(arguments[ i ]); } return alert( s ); } /*** META ((export #t)) */ function sc_typeof( x ) { return typeof x; } /*** META ((export #t)) */ function sc_error() { var a = [sc_jsstring2symbol("*error*")]; for (var i = 0; i < arguments.length; i++) { a[i+1] = arguments[i]; } throw a; } /*** META ((export #t) (peephole (prefix "throw "))) */ function sc_raise(obj) { throw obj; } /*** META ((export with-handler-lambda)) */ function sc_withHandlerLambda(handler, body) { try { return body(); } catch(e) { if (!e._internalException) return handler(e); else throw e; } } var sc_properties = new Object(); /*** META ((export #t)) */ function sc_putpropBang(sym, key, val) { var ht = sc_properties[sym]; if (!ht) { ht = new Object(); sc_properties[sym] = ht; } ht[key] = val; } /*** META ((export #t)) */ function sc_getprop(sym, key) { var ht = sc_properties[sym]; if (ht) { if (key in ht) return ht[key]; else return false; } else return false; } /*** META ((export #t)) */ function sc_rempropBang(sym, key) { var ht = sc_properties[sym]; if (ht) delete ht[key]; } /*** META ((export #t)) */ function sc_any2String(o) { return jsstring2string(sc_toDisplayString(o)); } /*** META ((export #t) (peephole (infix 2 2 "===")) (type bool)) */ function sc_isEqv(o1, o2) { return (o1 === o2); } /*** META ((export #t) (peephole (infix 2 2 "===")) (type bool)) */ function sc_isEq(o1, o2) { return (o1 === o2); } /*** META ((export #t) (type bool)) */ function sc_isNumber(n) { return (typeof n === "number"); } /*** META ((export #t) (type bool)) */ function sc_isComplex(n) { return sc_isNumber(n); } /*** META ((export #t) (type bool)) */ function sc_isReal(n) { return sc_isNumber(n); } /*** META ((export #t) (type bool)) */ function sc_isRational(n) { return sc_isReal(n); } /*** META ((export #t) (type bool)) */ function sc_isInteger(n) { return (parseInt(n) === n); } /*** META ((export #t) (type bool) (peephole (postfix ", false"))) */ // we don't have exact numbers... function sc_isExact(n) { return false; } /*** META ((export #t) (peephole (postfix ", true")) (type bool)) */ function sc_isInexact(n) { return true; } /*** META ((export = =fx =fl) (type bool) (peephole (infix 2 2 "==="))) */ function sc_equal(x) { for (var i = 1; i < arguments.length; i++) if (x !== arguments[i]) return false; return true; } /*** META ((export < = arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export > >fx >fl) (type bool) (peephole (infix 2 2 ">"))) */ function sc_greater(x, y) { for (var i = 1; i < arguments.length; i++) { if (x <= arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export <= <=fx <=fl) (type bool) (peephole (infix 2 2 "<="))) */ function sc_lessEqual(x, y) { for (var i = 1; i < arguments.length; i++) { if (x > arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export >= >=fl >=fx) (type bool) (peephole (infix 2 2 ">="))) */ function sc_greaterEqual(x, y) { for (var i = 1; i < arguments.length; i++) { if (x < arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export #t) (type bool) (peephole (postfix "=== 0"))) */ function sc_isZero(x) { return (x === 0); } /*** META ((export #t) (type bool) (peephole (postfix "> 0"))) */ function sc_isPositive(x) { return (x > 0); } /*** META ((export #t) (type bool) (peephole (postfix "< 0"))) */ function sc_isNegative(x) { return (x < 0); } /*** META ((export #t) (type bool) (peephole (postfix "%2===1"))) */ function sc_isOdd(x) { return (x % 2 === 1); } /*** META ((export #t) (type bool) (peephole (postfix "%2===0"))) */ function sc_isEven(x) { return (x % 2 === 0); } /*** META ((export #t)) */ var sc_max = Math.max; /*** META ((export #t)) */ var sc_min = Math.min; /*** META ((export + +fx +fl) (peephole (infix 0 #f "+" "0"))) */ function sc_plus() { var sum = 0; for (var i = 0; i < arguments.length; i++) sum += arguments[i]; return sum; } /*** META ((export * *fx *fl) (peephole (infix 0 #f "*" "1"))) */ function sc_multi() { var product = 1; for (var i = 0; i < arguments.length; i++) product *= arguments[i]; return product; } /*** META ((export - -fx -fl) (peephole (minus))) */ function sc_minus(x) { if (arguments.length === 1) return -x; else { var res = x; for (var i = 1; i < arguments.length; i++) res -= arguments[i]; return res; } } /*** META ((export / /fl) (peephole (div))) */ function sc_div(x) { if (arguments.length === 1) return 1/x; else { var res = x; for (var i = 1; i < arguments.length; i++) res /= arguments[i]; return res; } } /*** META ((export #t)) */ var sc_abs = Math.abs; /*** META ((export quotient /fx) (peephole (hole 2 "parseInt(" x "/" y ")"))) */ function sc_quotient(x, y) { return parseInt(x / y); } /*** META ((export #t) (peephole (infix 2 2 "%"))) */ function sc_remainder(x, y) { return x % y; } /*** META ((export #t) (peephole (modulo))) */ function sc_modulo(x, y) { var remainder = x % y; // if they don't have the same sign if ((remainder * y) < 0) return remainder + y; else return remainder; } function sc_euclid_gcd(a, b) { var temp; if (a === 0) return b; if (b === 0) return a; if (a < 0) {a = -a;}; if (b < 0) {b = -b;}; if (b > a) {temp = a; a = b; b = temp;}; while (true) { a %= b; if(a === 0) {return b;}; b %= a; if(b === 0) {return a;}; }; return b; } /*** META ((export #t)) */ function sc_gcd() { var gcd = 0; for (var i = 0; i < arguments.length; i++) gcd = sc_euclid_gcd(gcd, arguments[i]); return gcd; } /*** META ((export #t)) */ function sc_lcm() { var lcm = 1; for (var i = 0; i < arguments.length; i++) { var f = Math.round(arguments[i] / sc_euclid_gcd(arguments[i], lcm)); lcm *= Math.abs(f); } return lcm; } // LIMITATION: numerator and denominator don't make sense in floating point world. //var SC_MAX_DECIMALS = 1000000 // // function sc_numerator(x) { // var rounded = Math.round(x * SC_MAX_DECIMALS); // return Math.round(rounded / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); // } // function sc_denominator(x) { // var rounded = Math.round(x * SC_MAX_DECIMALS); // return Math.round(SC_MAX_DECIMALS / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); // } /*** META ((export #t)) */ var sc_floor = Math.floor; /*** META ((export #t)) */ var sc_ceiling = Math.ceil; /*** META ((export #t)) */ var sc_truncate = parseInt; /*** META ((export #t)) */ var sc_round = Math.round; // LIMITATION: sc_rationalize doesn't make sense in a floating point world. /*** META ((export #t)) */ var sc_exp = Math.exp; /*** META ((export #t)) */ var sc_log = Math.log; /*** META ((export #t)) */ var sc_sin = Math.sin; /*** META ((export #t)) */ var sc_cos = Math.cos; /*** META ((export #t)) */ var sc_tan = Math.tan; /*** META ((export #t)) */ var sc_asin = Math.asin; /*** META ((export #t)) */ var sc_acos = Math.acos; /*** META ((export #t)) */ var sc_atan = Math.atan; /*** META ((export #t)) */ var sc_sqrt = Math.sqrt; /*** META ((export #t)) */ var sc_expt = Math.pow; // LIMITATION: we don't have complex numbers. // LIMITATION: the following functions are hence not implemented. // LIMITATION: make-rectangular, make-polar, real-part, imag-part, magnitude, angle // LIMITATION: 2 argument atan /*** META ((export #t) (peephole (id))) */ function sc_exact2inexact(x) { return x; } /*** META ((export #t) (peephole (id))) */ function sc_inexact2exact(x) { return x; } function sc_number2jsstring(x, radix) { if (radix) return x.toString(radix); else return x.toString(); } function sc_jsstring2number(s, radix) { if (s === "") return false; if (radix) { var t = parseInt(s, radix); if (!t && t !== 0) return false; // verify that each char is in range. (parseInt ignores leading // white and trailing chars) var allowedChars = "01234567890abcdefghijklmnopqrstuvwxyz".substring(0, radix+1); if ((new RegExp("^["+allowedChars+"]*$", "i")).test(s)) return t; else return false; } else { var t = +s; // does not ignore trailing chars. if (!t && t !== 0) return false; // simply verify that first char is not whitespace. var c = s.charAt(0); // if +c is 0, but the char is not "0", then we have a whitespace. if (+c === 0 && c !== "0") return false; return t; } } /*** META ((export #t) (type bool) (peephole (not))) */ function sc_not(b) { return b === false; } /*** META ((export #t) (type bool)) */ function sc_isBoolean(b) { return (b === true) || (b === false); } function sc_Pair(car, cdr) { this.car = car; this.cdr = cdr; } sc_Pair.prototype.toString = function() { return sc_toDisplayString(this); }; sc_Pair.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { var current = this; var res = "("; while(true) { res += writeOrDisplay(current.car); if (sc_isPair(current.cdr)) { res += " "; current = current.cdr; } else if (current.cdr !== null) { res += " . " + writeOrDisplay(current.cdr); break; } else // current.cdr == null break; } res += ")"; return res; }; sc_Pair.prototype.sc_toDisplayString = function() { return this.sc_toWriteOrDisplayString(sc_toDisplayString); }; sc_Pair.prototype.sc_toWriteString = function() { return this.sc_toWriteOrDisplayString(sc_toWriteString); }; // sc_Pair.prototype.sc_toWriteCircleString in IO.js /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_Pair"))) */ function sc_isPair(p) { return (p instanceof sc_Pair); } function sc_isPairEqual(p1, p2, comp) { return (comp(p1.car, p2.car) && comp(p1.cdr, p2.cdr)); } /*** META ((export #t) (peephole (hole 2 "new sc_Pair(" car ", " cdr ")"))) */ function sc_cons(car, cdr) { return new sc_Pair(car, cdr); } /*** META ((export cons*)) */ function sc_consStar() { var res = arguments[arguments.length - 1]; for (var i = arguments.length-2; i >= 0; i--) res = new sc_Pair(arguments[i], res); return res; } /*** META ((export #t) (peephole (postfix ".car"))) */ function sc_car(p) { return p.car; } /*** META ((export #t) (peephole (postfix ".cdr"))) */ function sc_cdr(p) { return p.cdr; } /*** META ((export #t) (peephole (hole 2 p ".car = " val))) */ function sc_setCarBang(p, val) { p.car = val; } /*** META ((export #t) (peephole (hole 2 p ".cdr = " val))) */ function sc_setCdrBang(p, val) { p.cdr = val; } /*** META ((export #t) (peephole (postfix ".car.car"))) */ function sc_caar(p) { return p.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.car"))) */ function sc_cadr(p) { return p.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.cdr"))) */ function sc_cdar(p) { return p.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr"))) */ function sc_cddr(p) { return p.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.car"))) */ function sc_caaar(p) { return p.car.car.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.car"))) */ function sc_cadar(p) { return p.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.car"))) */ function sc_caadr(p) { return p.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car"))) */ function sc_caddr(p) { return p.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.car.cdr"))) */ function sc_cdaar(p) { return p.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr"))) */ function sc_cdadr(p) { return p.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr"))) */ function sc_cddar(p) { return p.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr"))) */ function sc_cdddr(p) { return p.cdr.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.car.car"))) */ function sc_caaaar(p) { return p.car.car.car.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.car.car"))) */ function sc_caadar(p) { return p.car.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.car.car"))) */ function sc_caaadr(p) { return p.cdr.car.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car.car"))) */ function sc_caaddr(p) { return p.cdr.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".car.car.car.cdr"))) */ function sc_cdaaar(p) { return p.car.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.car.cdr"))) */ function sc_cdadar(p) { return p.car.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.car.cdr"))) */ function sc_cdaadr(p) { return p.cdr.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car.cdr"))) */ function sc_cdaddr(p) { return p.cdr.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.cdr.car"))) */ function sc_cadaar(p) { return p.car.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr.car"))) */ function sc_caddar(p) { return p.car.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr.car"))) */ function sc_cadadr(p) { return p.cdr.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr.car"))) */ function sc_cadddr(p) { return p.cdr.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.car.cdr.cdr"))) */ function sc_cddaar(p) { return p.car.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr.cdr"))) */ function sc_cdddar(p) { return p.car.cdr.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr.cdr"))) */ function sc_cddadr(p) { return p.cdr.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr.cdr"))) */ function sc_cddddr(p) { return p.cdr.cdr.cdr.cdr; } /*** META ((export #t)) */ function sc_lastPair(l) { if (!sc_isPair(l)) sc_error("sc_lastPair: pair expected"); var res = l; var cdr = l.cdr; while (sc_isPair(cdr)) { res = cdr; cdr = res.cdr; } return res; } /*** META ((export #t) (type bool) (peephole (postfix " === null"))) */ function sc_isNull(o) { return (o === null); } /*** META ((export #t) (type bool)) */ function sc_isList(o) { var rabbit; var turtle; var rabbit = o; var turtle = o; while (true) { if (rabbit === null || (rabbit instanceof sc_Pair && rabbit.cdr === null)) return true; // end of list else if ((rabbit instanceof sc_Pair) && (rabbit.cdr instanceof sc_Pair)) { rabbit = rabbit.cdr.cdr; turtle = turtle.cdr; if (rabbit === turtle) return false; // cycle } else return false; // not pair } } /*** META ((export #t)) */ function sc_list() { var res = null; var a = arguments; for (var i = a.length-1; i >= 0; i--) res = new sc_Pair(a[i], res); return res; } /*** META ((export #t)) */ function sc_iota(num, init) { var res = null; if (!init) init = 0; for (var i = num - 1; i >= 0; i--) res = new sc_Pair(i + init, res); return res; } /*** META ((export #t)) */ function sc_makeList(nbEls, fill) { var res = null; for (var i = 0; i < nbEls; i++) res = new sc_Pair(fill, res); return res; } /*** META ((export #t)) */ function sc_length(l) { var res = 0; while (l !== null) { res++; l = l.cdr; } return res; } /*** META ((export #t)) */ function sc_remq(o, l) { var dummy = { cdr : null }; var tail = dummy; while (l !== null) { if (l.car !== o) { tail.cdr = sc_cons(l.car, null); tail = tail.cdr; } l = l.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_remqBang(o, l) { var dummy = { cdr : null }; var tail = dummy; var needsAssig = true; while (l !== null) { if (l.car === o) { needsAssig = true; } else { if (needsAssig) { tail.cdr = l; needsAssig = false; } tail = l; } l = l.cdr; } tail.cdr = null; return dummy.cdr; } /*** META ((export #t)) */ function sc_delete(o, l) { var dummy = { cdr : null }; var tail = dummy; while (l !== null) { if (!sc_isEqual(l.car, o)) { tail.cdr = sc_cons(l.car, null); tail = tail.cdr; } l = l.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_deleteBang(o, l) { var dummy = { cdr : null }; var tail = dummy; var needsAssig = true; while (l !== null) { if (sc_isEqual(l.car, o)) { needsAssig = true; } else { if (needsAssig) { tail.cdr = l; needsAssig = false; } tail = l; } l = l.cdr; } tail.cdr = null; return dummy.cdr; } function sc_reverseAppendBang(l1, l2) { var res = l2; while (l1 !== null) { var tmp = res; res = l1; l1 = l1.cdr; res.cdr = tmp; } return res; } function sc_dualAppend(l1, l2) { if (l1 === null) return l2; if (l2 === null) return l1; var rev = sc_reverse(l1); return sc_reverseAppendBang(rev, l2); } /*** META ((export #t)) */ function sc_append() { if (arguments.length === 0) return null; var res = arguments[arguments.length - 1]; for (var i = arguments.length - 2; i >= 0; i--) res = sc_dualAppend(arguments[i], res); return res; } function sc_dualAppendBang(l1, l2) { if (l1 === null) return l2; if (l2 === null) return l1; var tmp = l1; while (tmp.cdr !== null) tmp=tmp.cdr; tmp.cdr = l2; return l1; } /*** META ((export #t)) */ function sc_appendBang() { var res = null; for (var i = 0; i < arguments.length; i++) res = sc_dualAppendBang(res, arguments[i]); return res; } /*** META ((export #t)) */ function sc_reverse(l1) { var res = null; while (l1 !== null) { res = sc_cons(l1.car, res); l1 = l1.cdr; } return res; } /*** META ((export #t)) */ function sc_reverseBang(l) { return sc_reverseAppendBang(l, null); } /*** META ((export #t)) */ function sc_listTail(l, k) { var res = l; for (var i = 0; i < k; i++) { res = res.cdr; } return res; } /*** META ((export #t)) */ function sc_listRef(l, k) { return sc_listTail(l, k).car; } /* // unoptimized generic versions function sc_memX(o, l, comp) { while (l != null) { if (comp(l.car, o)) return l; l = l.cdr; } return false; } function sc_memq(o, l) { return sc_memX(o, l, sc_isEq); } function sc_memv(o, l) { return sc_memX(o, l, sc_isEqv); } function sc_member(o, l) { return sc_memX(o, l, sc_isEqual); } */ /* optimized versions */ /*** META ((export #t)) */ function sc_memq(o, l) { while (l !== null) { if (l.car === o) return l; l = l.cdr; } return false; } /*** META ((export #t)) */ function sc_memv(o, l) { while (l !== null) { if (l.car === o) return l; l = l.cdr; } return false; } /*** META ((export #t)) */ function sc_member(o, l) { while (l !== null) { if (sc_isEqual(l.car,o)) return l; l = l.cdr; } return false; } /* // generic unoptimized versions function sc_assX(o, al, comp) { while (al != null) { if (comp(al.car.car, o)) return al.car; al = al.cdr; } return false; } function sc_assq(o, al) { return sc_assX(o, al, sc_isEq); } function sc_assv(o, al) { return sc_assX(o, al, sc_isEqv); } function sc_assoc(o, al) { return sc_assX(o, al, sc_isEqual); } */ // optimized versions /*** META ((export #t)) */ function sc_assq(o, al) { while (al !== null) { if (al.car.car === o) return al.car; al = al.cdr; } return false; } /*** META ((export #t)) */ function sc_assv(o, al) { while (al !== null) { if (al.car.car === o) return al.car; al = al.cdr; } return false; } /*** META ((export #t)) */ function sc_assoc(o, al) { while (al !== null) { if (sc_isEqual(al.car.car, o)) return al.car; al = al.cdr; } return false; } /* can be used for mutable strings and characters */ function sc_isCharStringEqual(cs1, cs2) { return cs1.val === cs2.val; } function sc_isCharStringLess(cs1, cs2) { return cs1.val < cs2.val; } function sc_isCharStringGreater(cs1, cs2) { return cs1.val > cs2.val; } function sc_isCharStringLessEqual(cs1, cs2) { return cs1.val <= cs2.val; } function sc_isCharStringGreaterEqual(cs1, cs2) { return cs1.val >= cs2.val; } function sc_isCharStringCIEqual(cs1, cs2) { return cs1.val.toLowerCase() === cs2.val.toLowerCase(); } function sc_isCharStringCILess(cs1, cs2) { return cs1.val.toLowerCase() < cs2.val.toLowerCase(); } function sc_isCharStringCIGreater(cs1, cs2) { return cs1.val.toLowerCase() > cs2.val.toLowerCase(); } function sc_isCharStringCILessEqual(cs1, cs2) { return cs1.val.toLowerCase() <= cs2.val.toLowerCase(); } function sc_isCharStringCIGreaterEqual(cs1, cs2) { return cs1.val.toLowerCase() >= cs2.val.toLowerCase(); } function sc_Char(c) { var cached = sc_Char.lazy[c]; if (cached) return cached; this.val = c; sc_Char.lazy[c] = this; // add return, so FF does not complain. return undefined; } sc_Char.lazy = new Object(); // thanks to Eric sc_Char.char2readable = { "\000": "#\\null", "\007": "#\\bell", "\010": "#\\backspace", "\011": "#\\tab", "\012": "#\\newline", "\014": "#\\page", "\015": "#\\return", "\033": "#\\escape", "\040": "#\\space", "\177": "#\\delete", /* poeticless names */ "\001": "#\\soh", "\002": "#\\stx", "\003": "#\\etx", "\004": "#\\eot", "\005": "#\\enq", "\006": "#\\ack", "\013": "#\\vt", "\016": "#\\so", "\017": "#\\si", "\020": "#\\dle", "\021": "#\\dc1", "\022": "#\\dc2", "\023": "#\\dc3", "\024": "#\\dc4", "\025": "#\\nak", "\026": "#\\syn", "\027": "#\\etb", "\030": "#\\can", "\031": "#\\em", "\032": "#\\sub", "\033": "#\\esc", "\034": "#\\fs", "\035": "#\\gs", "\036": "#\\rs", "\037": "#\\us"}; sc_Char.readable2char = { "null": "\000", "bell": "\007", "backspace": "\010", "tab": "\011", "newline": "\012", "page": "\014", "return": "\015", "escape": "\033", "space": "\040", "delete": "\000", "soh": "\001", "stx": "\002", "etx": "\003", "eot": "\004", "enq": "\005", "ack": "\006", "bel": "\007", "bs": "\010", "ht": "\011", "nl": "\012", "vt": "\013", "np": "\014", "cr": "\015", "so": "\016", "si": "\017", "dle": "\020", "dc1": "\021", "dc2": "\022", "dc3": "\023", "dc4": "\024", "nak": "\025", "syn": "\026", "etb": "\027", "can": "\030", "em": "\031", "sub": "\032", "esc": "\033", "fs": "\034", "gs": "\035", "rs": "\036", "us": "\037", "sp": "\040", "del": "\177"}; sc_Char.prototype.toString = function() { return this.val; }; // sc_toDisplayString == toString sc_Char.prototype.sc_toWriteString = function() { var entry = sc_Char.char2readable[this.val]; if (entry) return entry; else return "#\\" + this.val; }; /*** META ((export #t) (type bool) (peephole (postfix "instanceof sc_Char"))) */ function sc_isChar(c) { return (c instanceof sc_Char); } /*** META ((export char=?) (type bool) (peephole (hole 2 c1 ".val === " c2 ".val"))) */ var sc_isCharEqual = sc_isCharStringEqual; /*** META ((export char?) (type bool) (peephole (hole 2 c1 ".val > " c2 ".val"))) */ var sc_isCharGreater = sc_isCharStringGreater; /*** META ((export char<=?) (type bool) (peephole (hole 2 c1 ".val <= " c2 ".val"))) */ var sc_isCharLessEqual = sc_isCharStringLessEqual; /*** META ((export char>=?) (type bool) (peephole (hole 2 c1 ".val >= " c2 ".val"))) */ var sc_isCharGreaterEqual = sc_isCharStringGreaterEqual; /*** META ((export char-ci=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() === " c2 ".val.toLowerCase()"))) */ var sc_isCharCIEqual = sc_isCharStringCIEqual; /*** META ((export char-ci?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() > " c2 ".val.toLowerCase()"))) */ var sc_isCharCIGreater = sc_isCharStringCIGreater; /*** META ((export char-ci<=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() <= " c2 ".val.toLowerCase()"))) */ var sc_isCharCILessEqual = sc_isCharStringCILessEqual; /*** META ((export char-ci>=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() >= " c2 ".val.toLowerCase()"))) */ var sc_isCharCIGreaterEqual = sc_isCharStringCIGreaterEqual; var SC_NUMBER_CLASS = "0123456789"; var SC_WHITESPACE_CLASS = ' \r\n\t\f'; var SC_LOWER_CLASS = 'abcdefghijklmnopqrstuvwxyz'; var SC_UPPER_CLASS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; function sc_isCharOfClass(c, cl) { return (cl.indexOf(c) != -1); } /*** META ((export #t) (type bool)) */ function sc_isCharAlphabetic(c) { return sc_isCharOfClass(c.val, SC_LOWER_CLASS) || sc_isCharOfClass(c.val, SC_UPPER_CLASS); } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_NUMBER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharNumeric(c) { return sc_isCharOfClass(c.val, SC_NUMBER_CLASS); } /*** META ((export #t) (type bool)) */ function sc_isCharWhitespace(c) { var tmp = c.val; return tmp === " " || tmp === "\r" || tmp === "\n" || tmp === "\t" || tmp === "\f"; } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_UPPER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharUpperCase(c) { return sc_isCharOfClass(c.val, SC_UPPER_CLASS); } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_LOWER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharLowerCase(c) { return sc_isCharOfClass(c.val, SC_LOWER_CLASS); } /*** META ((export #t) (peephole (postfix ".val.charCodeAt(0)"))) */ function sc_char2integer(c) { return c.val.charCodeAt(0); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(String.fromCharCode(" n "))"))) */ function sc_integer2char(n) { return new sc_Char(String.fromCharCode(n)); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(" c ".val.toUpperCase())"))) */ function sc_charUpcase(c) { return new sc_Char(c.val.toUpperCase()); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(" c ".val.toLowerCase())"))) */ function sc_charDowncase(c) { return new sc_Char(c.val.toLowerCase()); } function sc_makeJSStringOfLength(k, c) { var fill; if (c === undefined) fill = " "; else fill = c; var res = ""; var len = 1; // every round doubles the size of fill. while (k >= len) { if (k & len) res = res.concat(fill); fill = fill.concat(fill); len *= 2; } return res; } function sc_makejsString(k, c) { var fill; if (c) fill = c.val; else fill = " "; return sc_makeJSStringOfLength(k, fill); } function sc_jsstring2list(s) { var res = null; for (var i = s.length - 1; i >= 0; i--) res = sc_cons(new sc_Char(s.charAt(i)), res); return res; } function sc_list2jsstring(l) { var a = new Array(); while(l !== null) { a.push(l.car.val); l = l.cdr; } return "".concat.apply("", a); } var sc_Vector = Array; sc_Vector.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { if (this.length === 0) return "#()"; var res = "#(" + writeOrDisplay(this[0]); for (var i = 1; i < this.length; i++) res += " " + writeOrDisplay(this[i]); res += ")"; return res; }; sc_Vector.prototype.sc_toDisplayString = function() { return this.sc_toWriteOrDisplayString(sc_toDisplayString); }; sc_Vector.prototype.sc_toWriteString = function() { return this.sc_toWriteOrDisplayString(sc_toWriteString); }; /*** META ((export vector? array?) (type bool) (peephole (postfix " instanceof sc_Vector"))) */ function sc_isVector(v) { return (v instanceof sc_Vector); } // only applies to vectors function sc_isVectorEqual(v1, v2, comp) { if (v1.length !== v2.length) return false; for (var i = 0; i < v1.length; i++) if (!comp(v1[i], v2[i])) return false; return true; } /*** META ((export make-vector make-array)) */ function sc_makeVector(size, fill) { var a = new sc_Vector(size); if (fill !== undefined) sc_vectorFillBang(a, fill); return a; } /*** META ((export vector array) (peephole (vector))) */ function sc_vector() { var a = new sc_Vector(); for (var i = 0; i < arguments.length; i++) a.push(arguments[i]); return a; } /*** META ((export vector-length array-length) (peephole (postfix ".length"))) */ function sc_vectorLength(v) { return v.length; } /*** META ((export vector-ref array-ref) (peephole (hole 2 v "[" pos "]"))) */ function sc_vectorRef(v, pos) { return v[pos]; } /*** META ((export vector-set! array-set!) (peephole (hole 3 v "[" pos "] = " val))) */ function sc_vectorSetBang(v, pos, val) { v[pos] = val; } /*** META ((export vector->list array->list)) */ function sc_vector2list(a) { var res = null; for (var i = a.length-1; i >= 0; i--) res = sc_cons(a[i], res); return res; } /*** META ((export list->vector list->array)) */ function sc_list2vector(l) { var a = new sc_Vector(); while(l !== null) { a.push(l.car); l = l.cdr; } return a; } /*** META ((export vector-fill! array-fill!)) */ function sc_vectorFillBang(a, fill) { for (var i = 0; i < a.length; i++) a[i] = fill; } /*** META ((export #t)) */ function sc_copyVector(a, len) { if (len <= a.length) return a.slice(0, len); else { var tmp = a.concat(); tmp.length = len; return tmp; } } /*** META ((export #t) (peephole (hole 3 a ".slice(" start "," end ")"))) */ function sc_vectorCopy(a, start, end) { return a.slice(start, end); } /*** META ((export #t)) */ function sc_vectorCopyBang(target, tstart, source, sstart, send) { if (!sstart) sstart = 0; if (!send) send = source.length; // if target == source we don't want to overwrite not yet copied elements. if (tstart <= sstart) { for (var i = tstart, j = sstart; j < send; i++, j++) { target[i] = source[j]; } } else { var diff = send - sstart; for (var i = tstart + diff - 1, j = send - 1; j >= sstart; i--, j--) { target[i] = source[j]; } } return target; } /*** META ((export #t) (type bool) (peephole (hole 1 "typeof " o " === 'function'"))) */ function sc_isProcedure(o) { return (typeof o === "function"); } /*** META ((export #t)) */ function sc_apply(proc) { var args = new Array(); // first part of arguments are not in list-form. for (var i = 1; i < arguments.length - 1; i++) args.push(arguments[i]); var l = arguments[arguments.length - 1]; while (l !== null) { args.push(l.car); l = l.cdr; } return proc.apply(null, args); } /*** META ((export #t)) */ function sc_map(proc, l1) { if (l1 === undefined) return null; // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); var revres = null; while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } revres = sc_cons(proc.apply(null, applyArgs), revres); } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_mapBang(proc, l1) { if (l1 === undefined) return null; // else var l1_orig = l1; var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); while (l1 !== null) { var tmp = l1; for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } tmp.car = proc.apply(null, applyArgs); } return l1_orig; } /*** META ((export #t)) */ function sc_forEach(proc, l1) { if (l1 === undefined) return undefined; // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } proc.apply(null, applyArgs); } // add return so FF does not complain. return undefined; } /*** META ((export #t)) */ function sc_filter(proc, l1) { var dummy = { cdr : null }; var tail = dummy; while (l1 !== null) { if (proc(l1.car) !== false) { tail.cdr = sc_cons(l1.car, null); tail = tail.cdr; } l1 = l1.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_filterBang(proc, l1) { var head = sc_cons("dummy", l1); var it = head; var next = l1; while (next !== null) { if (proc(next.car) !== false) { it.cdr = next it = next; } next = next.cdr; } it.cdr = null; return head.cdr; } function sc_filterMap1(proc, l1) { var revres = null; while (l1 !== null) { var tmp = proc(l1.car) if (tmp !== false) revres = sc_cons(tmp, revres); l1 = l1.cdr; } return sc_reverseAppendBang(revres, null); } function sc_filterMap2(proc, l1, l2) { var revres = null; while (l1 !== null) { var tmp = proc(l1.car, l2.car); if(tmp !== false) revres = sc_cons(tmp, revres); l1 = l1.cdr; l2 = l2.cdr } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_filterMap(proc, l1, l2, l3) { if (l2 === undefined) return sc_filterMap1(proc, l1); else if (l3 === undefined) return sc_filterMap2(proc, l1, l2); // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); var revres = null; while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } var tmp = proc.apply(null, applyArgs); if(tmp !== false) revres = sc_cons(tmp, revres); } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_any(proc, l) { var revres = null; while (l !== null) { var tmp = proc(l.car); if(tmp !== false) return tmp; l = l.cdr; } return false; } /*** META ((export any?) (peephole (hole 2 "sc_any(" proc "," l ") !== false"))) */ function sc_anyPred(proc, l) { return sc_any(proc, l)!== false; } /*** META ((export #t)) */ function sc_every(proc, l) { var revres = null; var tmp = true; while (l !== null) { tmp = proc(l.car); if (tmp === false) return false; l = l.cdr; } return tmp; } /*** META ((export every?) (peephole (hole 2 "sc_every(" proc "," l ") !== false"))) */ function sc_everyPred(proc, l) { var tmp = sc_every(proc, l); if (tmp !== false) return true; return false; } /*** META ((export #t) (peephole (postfix "()"))) */ function sc_force(o) { return o(); } /*** META ((export #t)) */ function sc_makePromise(proc) { var isResultReady = false; var result = undefined; return function() { if (!isResultReady) { var tmp = proc(); if (!isResultReady) { isResultReady = true; result = tmp; } } return result; }; } function sc_Values(values) { this.values = values; } /*** META ((export #t) (peephole (values))) */ function sc_values() { if (arguments.length === 1) return arguments[0]; else return new sc_Values(arguments); } /*** META ((export #t)) */ function sc_callWithValues(producer, consumer) { var produced = producer(); if (produced instanceof sc_Values) return consumer.apply(null, produced.values); else return consumer(produced); } /*** META ((export #t)) */ function sc_dynamicWind(before, thunk, after) { before(); try { var res = thunk(); return res; } finally { after(); } } // TODO: eval/scheme-report-environment/null-environment/interaction-environment // LIMITATION: 'load' doesn't exist without files. // LIMITATION: transcript-on/transcript-off doesn't exist without files. function sc_Struct(name) { this.name = name; } sc_Struct.prototype.sc_toDisplayString = function() { return "#"; }; sc_Struct.prototype.sc_toWriteString = sc_Struct.prototype.sc_toDisplayString; /*** META ((export #t) (peephole (hole 1 "new sc_Struct(" name ")"))) */ function sc_makeStruct(name) { return new sc_Struct(name); } /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_Struct"))) */ function sc_isStruct(o) { return (o instanceof sc_Struct); } /*** META ((export #t) (type bool) (peephole (hole 2 "(" 1 " instanceof sc_Struct) && ( " 1 ".name === " 0 ")"))) */ function sc_isStructNamed(name, s) { return ((s instanceof sc_Struct) && (s.name === name)); } /*** META ((export struct-field) (peephole (hole 3 0 "[" 2 "]"))) */ function sc_getStructField(s, name, field) { return s[field]; } /*** META ((export struct-field-set!) (peephole (hole 4 0 "[" 2 "] = " 3))) */ function sc_setStructFieldBang(s, name, field, val) { s[field] = val; } /*** META ((export #t) (peephole (prefix "~"))) */ function sc_bitNot(x) { return ~x; } /*** META ((export #t) (peephole (infix 2 2 "&"))) */ function sc_bitAnd(x, y) { return x & y; } /*** META ((export #t) (peephole (infix 2 2 "|"))) */ function sc_bitOr(x, y) { return x | y; } /*** META ((export #t) (peephole (infix 2 2 "^"))) */ function sc_bitXor(x, y) { return x ^ y; } /*** META ((export #t) (peephole (infix 2 2 "<<"))) */ function sc_bitLsh(x, y) { return x << y; } /*** META ((export #t) (peephole (infix 2 2 ">>"))) */ function sc_bitRsh(x, y) { return x >> y; } /*** META ((export #t) (peephole (infix 2 2 ">>>"))) */ function sc_bitUrsh(x, y) { return x >>> y; } /*** META ((export js-field js-property) (peephole (hole 2 o "[" field "]"))) */ function sc_jsField(o, field) { return o[field]; } /*** META ((export js-field-set! js-property-set!) (peephole (hole 3 o "[" field "] = " val))) */ function sc_setJsFieldBang(o, field, val) { return o[field] = val; } /*** META ((export js-field-delete! js-property-delete!) (peephole (hole 2 "delete" o "[" field "]"))) */ function sc_deleteJsFieldBang(o, field) { delete o[field]; } /*** META ((export #t) (peephole (jsCall))) */ function sc_jsCall(o, fun) { var args = new Array(); for (var i = 2; i < arguments.length; i++) args[i-2] = arguments[i]; return fun.apply(o, args); } /*** META ((export #t) (peephole (jsMethodCall))) */ function sc_jsMethodCall(o, field) { var args = new Array(); for (var i = 2; i < arguments.length; i++) args[i-2] = arguments[i]; return o[field].apply(o, args); } /*** META ((export new js-new) (peephole (jsNew))) */ function sc_jsNew(c) { var evalStr = "new c("; evalStr +=arguments.length > 1? "arguments[1]": ""; for (var i = 2; i < arguments.length; i++) evalStr += ", arguments[" + i + "]"; evalStr +=")"; return eval(evalStr); } // ======================== RegExp ==================== /*** META ((export #t)) */ function sc_pregexp(re) { return new RegExp(sc_string2jsstring(re)); } /*** META ((export #t)) */ function sc_pregexpMatch(re, s) { var reg = (re instanceof RegExp) ? re : sc_pregexp(re); var tmp = reg.exec(sc_string2jsstring(s)); if (tmp == null) return false; var res = null; for (var i = tmp.length-1; i >= 0; i--) { if (tmp[i] !== null) { res = sc_cons(sc_jsstring2string(tmp[i]), res); } else { res = sc_cons(false, res); } } return res; } /*** META ((export #t)) */ function sc_pregexpReplace(re, s1, s2) { var reg; var jss1 = sc_string2jsstring(s1); var jss2 = sc_string2jsstring(s2); if (re instanceof RegExp) { if (re.global) reg = re; else reg = new RegExp(re.source); } else { reg = new RegExp(sc_string2jsstring(re)); } return jss1.replace(reg, jss2); } /*** META ((export pregexp-replace*)) */ function sc_pregexpReplaceAll(re, s1, s2) { var reg; var jss1 = sc_string2jsstring(s1); var jss2 = sc_string2jsstring(s2); if (re instanceof RegExp) { if (re.global) reg = re; else reg = new RegExp(re.source, "g"); } else { reg = new RegExp(sc_string2jsstring(re), "g"); } return jss1.replace(reg, jss2); } /*** META ((export #t)) */ function sc_pregexpSplit(re, s) { var reg = ((re instanceof RegExp) ? re : new RegExp(sc_string2jsstring(re))); var jss = sc_string2jsstring(s); var tmp = jss.split(reg); if (tmp == null) return false; return sc_vector2list(tmp); } /* =========================================================================== */ /* Other library stuff */ /* =========================================================================== */ /*** META ((export #t) (peephole (hole 1 "Math.floor(Math.random()*" 'n ")"))) */ function sc_random(n) { return Math.floor(Math.random()*n); } /*** META ((export current-date) (peephole (hole 0 "new Date()"))) */ function sc_currentDate() { return new Date(); } function sc_Hashtable() { } sc_Hashtable.prototype.toString = function() { return "#{%hashtable}"; }; // sc_toWriteString == sc_toDisplayString == toString function sc_HashtableElement(key, val) { this.key = key; this.val = val; } /*** META ((export #t) (peephole (hole 0 "new sc_Hashtable()"))) */ function sc_makeHashtable() { return new sc_Hashtable(); } /*** META ((export #t)) */ function sc_hashtablePutBang(ht, key, val) { var hash = sc_hash(key); ht[hash] = new sc_HashtableElement(key, val); } /*** META ((export #t)) */ function sc_hashtableGet(ht, key) { var hash = sc_hash(key); if (hash in ht) return ht[hash].val; else return false; } /*** META ((export #t)) */ function sc_hashtableForEach(ht, f) { for (var v in ht) { if (ht[v] instanceof sc_HashtableElement) f(ht[v].key, ht[v].val); } } /*** META ((export hashtable-contains?) (peephole (hole 2 "sc_hash(" 1 ") in " 0))) */ function sc_hashtableContains(ht, key) { var hash = sc_hash(key); if (hash in ht) return true; else return false; } var SC_HASH_COUNTER = 0; function sc_hash(o) { if (o === null) return "null"; else if (o === undefined) return "undefined"; else if (o === true) return "true"; else if (o === false) return "false"; else if (typeof o === "number") return "num-" + o; else if (typeof o === "string") return "jsstr-" + o; else if (o.sc_getHash) return o.sc_getHash(); else return sc_counterHash.call(o); } function sc_counterHash() { if (!this.sc_hash) { this.sc_hash = "hash-" + SC_HASH_COUNTER; SC_HASH_COUNTER++; } return this.sc_hash; } function sc_Trampoline(args, maxTailCalls) { this['__trampoline return__'] = true; this.args = args; this.MAX_TAIL_CALLs = maxTailCalls; } // TODO: call/cc stuff sc_Trampoline.prototype.restart = function() { var o = this; while (true) { // set both globals. SC_TAIL_OBJECT.calls = o.MAX_TAIL_CALLs-1; var fun = o.args.callee; var res = fun.apply(SC_TAIL_OBJECT, o.args); if (res instanceof sc_Trampoline) o = res; else return res; } } /*** META ((export bind-exit-lambda)) */ function sc_bindExitLambda(proc) { var escape_obj = new sc_BindExitException(); var escape = function(res) { escape_obj.res = res; throw escape_obj; }; try { return proc(escape); } catch(e) { if (e === escape_obj) { return e.res; } throw e; } } function sc_BindExitException() { this._internalException = true; } var SC_SCM2JS_GLOBALS = new Object(); // default tail-call depth. // normally the program should set it again. but just in case... var SC_TAIL_OBJECT = new Object(); SC_SCM2JS_GLOBALS.TAIL_OBJECT = SC_TAIL_OBJECT; // ======================== I/O ======================= /*------------------------------------------------------------------*/ function sc_EOF() { } var SC_EOF_OBJECT = new sc_EOF(); function sc_Port() { } /* --------------- Input ports -------------------------------------*/ function sc_InputPort() { } sc_InputPort.prototype = new sc_Port(); sc_InputPort.prototype.peekChar = function() { if (!("peeked" in this)) this.peeked = this.getNextChar(); return this.peeked; } sc_InputPort.prototype.readChar = function() { var tmp = this.peekChar(); delete this.peeked; return tmp; } sc_InputPort.prototype.isCharReady = function() { return true; } sc_InputPort.prototype.close = function() { // do nothing } /* .............. String port ..........................*/ function sc_ErrorInputPort() { }; sc_ErrorInputPort.prototype = new sc_InputPort(); sc_ErrorInputPort.prototype.getNextChar = function() { throw "can't read from error-port."; }; sc_ErrorInputPort.prototype.isCharReady = function() { return false; }; /* .............. String port ..........................*/ function sc_StringInputPort(jsStr) { // we are going to do some charAts on the str. // instead of recreating all the time a String-object, we // create one in the beginning. (not sure, if this is really an optim) this.str = new String(jsStr); this.pos = 0; } sc_StringInputPort.prototype = new sc_InputPort(); sc_StringInputPort.prototype.getNextChar = function() { if (this.pos >= this.str.length) return SC_EOF_OBJECT; return this.str.charAt(this.pos++); }; /* ------------- Read and other lib-funs -------------------------------*/ function sc_Token(type, val, pos) { this.type = type; this.val = val; this.pos = pos; } sc_Token.EOF = 0/*EOF*/; sc_Token.OPEN_PAR = 1/*OPEN_PAR*/; sc_Token.CLOSE_PAR = 2/*CLOSE_PAR*/; sc_Token.OPEN_BRACE = 3/*OPEN_BRACE*/; sc_Token.CLOSE_BRACE = 4/*CLOSE_BRACE*/; sc_Token.OPEN_BRACKET = 5/*OPEN_BRACKET*/; sc_Token.CLOSE_BRACKET = 6/*CLOSE_BRACKET*/; sc_Token.WHITESPACE = 7/*WHITESPACE*/; sc_Token.QUOTE = 8/*QUOTE*/; sc_Token.ID = 9/*ID*/; sc_Token.DOT = 10/*DOT*/; sc_Token.STRING = 11/*STRING*/; sc_Token.NUMBER = 12/*NUMBER*/; sc_Token.ERROR = 13/*ERROR*/; sc_Token.VECTOR_BEGIN = 14/*VECTOR_BEGIN*/; sc_Token.TRUE = 15/*TRUE*/; sc_Token.FALSE = 16/*FALSE*/; sc_Token.UNSPECIFIED = 17/*UNSPECIFIED*/; sc_Token.REFERENCE = 18/*REFERENCE*/; sc_Token.STORE = 19/*STORE*/; sc_Token.CHAR = 20/*CHAR*/; var SC_ID_CLASS = SC_LOWER_CLASS + SC_UPPER_CLASS + "!$%*+-./:<=>?@^_~"; function sc_Tokenizer(port) { this.port = port; } sc_Tokenizer.prototype.peekToken = function() { if (this.peeked) return this.peeked; var newToken = this.nextToken(); this.peeked = newToken; return newToken; }; sc_Tokenizer.prototype.readToken = function() { var tmp = this.peekToken(); delete this.peeked; return tmp; }; sc_Tokenizer.prototype.nextToken = function() { var port = this.port; function isNumberChar(c) { return (c >= "0" && c <= "9"); }; function isIdOrNumberChar(c) { return SC_ID_CLASS.indexOf(c) != -1 || // ID-char (c >= "0" && c <= "9"); } function isWhitespace(c) { return c === " " || c === "\r" || c === "\n" || c === "\t" || c === "\f"; }; function isWhitespaceOrEOF(c) { return isWhitespace(c) || c === SC_EOF_OBJECT; }; function readString() { res = ""; while (true) { var c = port.readChar(); switch (c) { case '"': return new sc_Token(11/*STRING*/, res); case "\\": var tmp = port.readChar(); switch (tmp) { case '0': res += "\0"; break; case 'a': res += "\a"; break; case 'b': res += "\b"; break; case 'f': res += "\f"; break; case 'n': res += "\n"; break; case 'r': res += "\r"; break; case 't': res += "\t"; break; case 'v': res += "\v"; break; case '"': res += '"'; break; case '\\': res += '\\'; break; case 'x': /* hexa-number */ var nb = 0; while (true) { var hexC = port.peekChar(); if (hexC >= '0' && hexC <= '9') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - '0'.charCodeAt(0); } else if (hexC >= 'a' && hexC <= 'f') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - 'a'.charCodeAt(0); } else if (hexC >= 'A' && hexC <= 'F') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - 'A'.charCodeAt(0); } else { // next char isn't part of hex. res += String.fromCharCode(nb); break; } } break; default: if (tmp === SC_EOF_OBJECT) { return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); } res += tmp; } break; default: if (c === SC_EOF_OBJECT) { return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); } res += c; } } }; function readIdOrNumber(firstChar) { var res = firstChar; while (isIdOrNumberChar(port.peekChar())) res += port.readChar(); if (isNaN(res)) return new sc_Token(9/*ID*/, res); else return new sc_Token(12/*NUMBER*/, res - 0); }; function skipWhitespaceAndComments() { var done = false; while (!done) { done = true; while (isWhitespace(port.peekChar())) port.readChar(); if (port.peekChar() === ';') { port.readChar(); done = false; while (true) { curChar = port.readChar(); if (curChar === SC_EOF_OBJECT || curChar === '\n') break; } } } }; function readDot() { if (isWhitespace(port.peekChar())) return new sc_Token(10/*DOT*/); else return readIdOrNumber("."); }; function readSharp() { var c = port.readChar(); if (isWhitespace(c)) return new sc_Token(13/*ERROR*/, "bad #-pattern0."); // reference if (isNumberChar(c)) { var nb = c - 0; while (isNumberChar(port.peekChar())) nb = nb*10 + (port.readChar() - 0); switch (port.readChar()) { case '#': return new sc_Token(18/*REFERENCE*/, nb); case '=': return new sc_Token(19/*STORE*/, nb); default: return new sc_Token(13/*ERROR*/, "bad #-pattern1." + nb); } } if (c === "(") return new sc_Token(14/*VECTOR_BEGIN*/); if (c === "\\") { // character var tmp = "" while (!isWhitespaceOrEOF(port.peekChar())) tmp += port.readChar(); switch (tmp.length) { case 0: // it's escaping a whitespace char: if (sc_isEOFObject(port.peekChar)) return new sc_Token(13/*ERROR*/, "bad #-pattern2."); else return new sc_Token(20/*CHAR*/, port.readChar()); case 1: return new sc_Token(20/*CHAR*/, tmp); default: var entry = sc_Char.readable2char[tmp.toLowerCase()]; if (entry) return new sc_Token(20/*CHAR*/, entry); else return new sc_Token(13/*ERROR*/, "unknown character description: #\\" + tmp); } } // some constants (#t, #f, #unspecified) var res; var needing; switch (c) { case 't': res = new sc_Token(15/*TRUE*/, true); needing = ""; break; case 'f': res = new sc_Token(16/*FALSE*/, false); needing = ""; break; case 'u': res = new sc_Token(17/*UNSPECIFIED*/, undefined); needing = "nspecified"; break; default: return new sc_Token(13/*ERROR*/, "bad #-pattern3: " + c); } while(true) { c = port.peekChar(); if ((isWhitespaceOrEOF(c) || c === ')') && needing == "") return res; else if (isWhitespace(c) || needing == "") return new sc_Token(13/*ERROR*/, "bad #-pattern4 " + c + " " + needing); else if (needing.charAt(0) == c) { port.readChar(); // consume needing = needing.slice(1); } else return new sc_Token(13/*ERROR*/, "bad #-pattern5"); } }; skipWhitespaceAndComments(); var curChar = port.readChar(); if (curChar === SC_EOF_OBJECT) return new sc_Token(0/*EOF*/, curChar); switch (curChar) { case " ": case "\n": case "\t": return readWhitespace(); case "(": return new sc_Token(1/*OPEN_PAR*/); case ")": return new sc_Token(2/*CLOSE_PAR*/); case "{": return new sc_Token(3/*OPEN_BRACE*/); case "}": return new sc_Token(4/*CLOSE_BRACE*/); case "[": return new sc_Token(5/*OPEN_BRACKET*/); case "]": return new sc_Token(6/*CLOSE_BRACKET*/); case "'": return new sc_Token(8/*QUOTE*/); case "#": return readSharp(); case ".": return readDot(); case '"': return readString(); default: if (isIdOrNumberChar(curChar)) return readIdOrNumber(curChar); throw "unexpected character: " + curChar; } }; function sc_Reader(tokenizer) { this.tokenizer = tokenizer; this.backref = new Array(); } sc_Reader.prototype.read = function() { function readList(listBeginType) { function matchesPeer(open, close) { return open === 1/*OPEN_PAR*/ && close === 2/*CLOSE_PAR*/ || open === 3/*OPEN_BRACE*/ && close === 4/*CLOSE_BRACE*/ || open === 5/*OPEN_BRACKET*/ && close === 6/*CLOSE_BRACKET*/; }; var res = null; while (true) { var token = tokenizer.peekToken(); switch (token.type) { case 2/*CLOSE_PAR*/: case 4/*CLOSE_BRACE*/: case 6/*CLOSE_BRACKET*/: if (matchesPeer(listBeginType, token.type)) { tokenizer.readToken(); // consume token return sc_reverseBang(res); } else throw "closing par doesn't match: " + listBeginType + " " + listEndType; case 0/*EOF*/: throw "unexpected end of file"; case 10/*DOT*/: tokenizer.readToken(); // consume token var cdr = this.read(); var par = tokenizer.readToken(); if (!matchesPeer(listBeginType, par.type)) throw "closing par doesn't match: " + listBeginType + " " + par.type; else return sc_reverseAppendBang(res, cdr); default: res = sc_cons(this.read(), res); } } }; function readQuote() { return sc_cons("quote", sc_cons(this.read(), null)); }; function readVector() { // opening-parenthesis is already consumed var a = new Array(); while (true) { var token = tokenizer.peekToken(); switch (token.type) { case 2/*CLOSE_PAR*/: tokenizer.readToken(); return a; default: a.push(this.read()); } } }; function storeRefence(nb) { var tmp = this.read(); this.backref[nb] = tmp; return tmp; }; function readReference(nb) { if (nb in this.backref) return this.backref[nb]; else throw "bad reference: " + nb; }; var tokenizer = this.tokenizer; var token = tokenizer.readToken(); // handle error if (token.type === 13/*ERROR*/) throw token.val; switch (token.type) { case 1/*OPEN_PAR*/: case 3/*OPEN_BRACE*/: case 5/*OPEN_BRACKET*/: return readList.call(this, token.type); case 8/*QUOTE*/: return readQuote.call(this); case 11/*STRING*/: return sc_jsstring2string(token.val); case 20/*CHAR*/: return new sc_Char(token.val); case 14/*VECTOR_BEGIN*/: return readVector.call(this); case 18/*REFERENCE*/: return readReference.call(this, token.val); case 19/*STORE*/: return storeRefence.call(this, token.val); case 9/*ID*/: return sc_jsstring2symbol(token.val); case 0/*EOF*/: case 12/*NUMBER*/: case 15/*TRUE*/: case 16/*FALSE*/: case 17/*UNSPECIFIED*/: return token.val; default: throw "unexpected token " + token.type + " " + token.val; } }; /*** META ((export #t)) */ function sc_read(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var reader = new sc_Reader(new sc_Tokenizer(port)); return reader.read(); } /*** META ((export #t)) */ function sc_readChar(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var t = port.readChar(); return t === SC_EOF_OBJECT? t: new sc_Char(t); } /*** META ((export #t)) */ function sc_peekChar(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var t = port.peekChar(); return t === SC_EOF_OBJECT? t: new sc_Char(t); } /*** META ((export #t) (type bool)) */ function sc_isCharReady(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... return port.isCharReady(); } /*** META ((export #t) (peephole (postfix ".close()"))) */ function sc_closeInputPort(p) { return p.close(); } /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_InputPort"))) */ function sc_isInputPort(o) { return (o instanceof sc_InputPort); } /*** META ((export eof-object?) (type bool) (peephole (postfix " === SC_EOF_OBJECT"))) */ function sc_isEOFObject(o) { return o === SC_EOF_OBJECT; } /*** META ((export #t) (peephole (hole 0 "SC_DEFAULT_IN"))) */ function sc_currentInputPort() { return SC_DEFAULT_IN; } /* ------------ file operations are not supported -----------*/ /*** META ((export #t)) */ function sc_callWithInputFile(s, proc) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_callWithOutputFile(s, proc) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_withInputFromFile(s, thunk) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_withOutputToFile(s, thunk) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_openInputFile(s) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_openOutputFile(s) { throw "can't open " + s; } /* ----------------------------------------------------------------------------*/ /*** META ((export #t)) */ function sc_basename(p) { var i = p.lastIndexOf('/'); if(i >= 0) return p.substring(i + 1, p.length); else return ''; } /*** META ((export #t)) */ function sc_dirname(p) { var i = p.lastIndexOf('/'); if(i >= 0) return p.substring(0, i); else return ''; } /* ----------------------------------------------------------------------------*/ /*** META ((export #t)) */ function sc_withInputFromPort(p, thunk) { try { var tmp = SC_DEFAULT_IN; // THREAD: shared var. SC_DEFAULT_IN = p; return thunk(); } finally { SC_DEFAULT_IN = tmp; } } /*** META ((export #t)) */ function sc_withInputFromString(s, thunk) { return sc_withInputFromPort(new sc_StringInputPort(sc_string2jsstring(s)), thunk); } /*** META ((export #t)) */ function sc_withOutputToPort(p, thunk) { try { var tmp = SC_DEFAULT_OUT; // THREAD: shared var. SC_DEFAULT_OUT = p; return thunk(); } finally { SC_DEFAULT_OUT = tmp; } } /*** META ((export #t)) */ function sc_withOutputToString(thunk) { var p = new sc_StringOutputPort(); sc_withOutputToPort(p, thunk); return p.close(); } /*** META ((export #t)) */ function sc_withOutputToProcedure(proc, thunk) { var t = function(s) { proc(sc_jsstring2string(s)); }; return sc_withOutputToPort(new sc_GenericOutputPort(t), thunk); } /*** META ((export #t) (peephole (hole 0 "new sc_StringOutputPort()"))) */ function sc_openOutputString() { return new sc_StringOutputPort(); } /*** META ((export #t)) */ function sc_openInputString(str) { return new sc_StringInputPort(sc_string2jsstring(str)); } /* ----------------------------------------------------------------------------*/ function sc_OutputPort() { } sc_OutputPort.prototype = new sc_Port(); sc_OutputPort.prototype.appendJSString = function(obj) { /* do nothing */ } sc_OutputPort.prototype.close = function() { /* do nothing */ } function sc_StringOutputPort() { this.res = ""; } sc_StringOutputPort.prototype = new sc_OutputPort(); sc_StringOutputPort.prototype.appendJSString = function(s) { this.res += s; } sc_StringOutputPort.prototype.close = function() { return sc_jsstring2string(this.res); } /*** META ((export #t)) */ function sc_getOutputString(sp) { return sc_jsstring2string(sp.res); } function sc_ErrorOutputPort() { } sc_ErrorOutputPort.prototype = new sc_OutputPort(); sc_ErrorOutputPort.prototype.appendJSString = function(s) { throw "don't write on ErrorPort!"; } sc_ErrorOutputPort.prototype.close = function() { /* do nothing */ } function sc_GenericOutputPort(appendJSString, close) { this.appendJSString = appendJSString; if (close) this.close = close; } sc_GenericOutputPort.prototype = new sc_OutputPort(); /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_OutputPort"))) */ function sc_isOutputPort(o) { return (o instanceof sc_OutputPort); } /*** META ((export #t) (peephole (postfix ".close()"))) */ function sc_closeOutputPort(p) { return p.close(); } /* ------------------ write ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_write(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toWriteString(o)); } function sc_toWriteString(o) { if (o === null) return "()"; else if (o === true) return "#t"; else if (o === false) return "#f"; else if (o === undefined) return "#unspecified"; else if (typeof o === 'function') return "#"; else if (o.sc_toWriteString) return o.sc_toWriteString(); else return o.toString(); } function sc_escapeWriteString(s) { var res = ""; var j = 0; for (i = 0; i < s.length; i++) { switch (s.charAt(i)) { case "\0": res += s.substring(j, i) + "\\0"; j = i + 1; break; case "\b": res += s.substring(j, i) + "\\b"; j = i + 1; break; case "\f": res += s.substring(j, i) + "\\f"; j = i + 1; break; case "\n": res += s.substring(j, i) + "\\n"; j = i + 1; break; case "\r": res += s.substring(j, i) + "\\r"; j = i + 1; break; case "\t": res += s.substring(j, i) + "\\t"; j = i + 1; break; case "\v": res += s.substring(j, i) + "\\v"; j = i + 1; break; case '"': res += s.substring(j, i) + '\\"'; j = i + 1; break; case "\\": res += s.substring(j, i) + "\\\\"; j = i + 1; break; default: var c = s.charAt(i); if ("\a" !== "a" && c == "\a") { res += s.substring(j, i) + "\\a"; j = i + 1; continue; } if ("\v" !== "v" && c == "\v") { res += s.substring(j, i) + "\\v"; j = i + 1; continue; } //if (s.charAt(i) < ' ' || s.charCodeAt(i) > 127) { // CARE: Manuel is this OK with HOP? if (s.charAt(i) < ' ') { /* non printable character and special chars */ res += s.substring(j, i) + "\\x" + s.charCodeAt(i).toString(16); j = i + 1; } // else just let i increase... } } res += s.substring(j, i); return res; } /* ------------------ display ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_display(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toDisplayString(o)); } function sc_toDisplayString(o) { if (o === null) return "()"; else if (o === true) return "#t"; else if (o === false) return "#f"; else if (o === undefined) return "#unspecified"; else if (typeof o === 'function') return "#"; else if (o.sc_toDisplayString) return o.sc_toDisplayString(); else return o.toString(); } /* ------------------ newline ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_newline(p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString("\n"); } /* ------------------ write-char ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_writeChar(c, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(c.val); } /* ------------------ write-circle ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_writeCircle(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toWriteCircleString(o)); } function sc_toWriteCircleString(o) { var symb = sc_gensym("writeCircle"); var nbPointer = new Object(); nbPointer.nb = 0; sc_prepWriteCircle(o, symb, nbPointer); return sc_genToWriteCircleString(o, symb); } function sc_prepWriteCircle(o, symb, nbPointer) { // TODO sc_Struct if (o instanceof sc_Pair || o instanceof sc_Vector) { if (o[symb] !== undefined) { // not the first visit. o[symb]++; // unless there is already a number, assign one. if (!o[symb + "nb"]) o[symb + "nb"] = nbPointer.nb++; return; } o[symb] = 0; if (o instanceof sc_Pair) { sc_prepWriteCircle(o.car, symb, nbPointer); sc_prepWriteCircle(o.cdr, symb, nbPointer); } else { for (var i = 0; i < o.length; i++) sc_prepWriteCircle(o[i], symb, nbPointer); } } } function sc_genToWriteCircleString(o, symb) { if (!(o instanceof sc_Pair || o instanceof sc_Vector)) return sc_toWriteString(o); return o.sc_toWriteCircleString(symb); } sc_Pair.prototype.sc_toWriteCircleString = function(symb, inList) { if (this[symb + "use"]) { // use-flag is set. Just use it. var nb = this[symb + "nb"]; if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } if (inList) return '. #' + nb + '#'; else return '#' + nb + '#'; } if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } var res = ""; if (this[symb] !== undefined) { // implies > 0 this[symb + "use"] = true; if (inList) res += '. #' + this[symb + "nb"] + '='; else res += '#' + this[symb + "nb"] + '='; inList = false; } if (!inList) res += "("; // print car res += sc_genToWriteCircleString(this.car, symb); if (sc_isPair(this.cdr)) { res += " " + this.cdr.sc_toWriteCircleString(symb, true); } else if (this.cdr !== null) { res += " . " + sc_genToWriteCircleString(this.cdr, symb); } if (!inList) res += ")"; return res; }; sc_Vector.prototype.sc_toWriteCircleString = function(symb) { if (this[symb + "use"]) { // use-flag is set. Just use it. var nb = this[symb + "nb"]; if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } return '#' + nb + '#'; } if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } var res = ""; if (this[symb] !== undefined) { // implies > 0 this[symb + "use"] = true; res += '#' + this[symb + "nb"] + '='; } res += "#("; for (var i = 0; i < this.length; i++) { res += sc_genToWriteCircleString(this[i], symb); if (i < this.length - 1) res += " "; } res += ")"; return res; }; /* ------------------ print ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_print(s) { if (arguments.length === 1) { sc_display(s); sc_newline(); } else { for (var i = 0; i < arguments.length; i++) sc_display(arguments[i]); sc_newline(); } } /* ------------------ format ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_format(s, args) { var len = s.length; var p = new sc_StringOutputPort(); var i = 0, j = 1; while( i < len ) { var i2 = s.indexOf("~", i); if (i2 == -1) { p.appendJSString( s.substring( i, len ) ); return p.close(); } else { if (i2 > i) { if (i2 == (len - 1)) { p.appendJSString(s.substring(i, len)); return p.close(); } else { p.appendJSString(s.substring(i, i2)); i = i2; } } switch(s.charCodeAt(i2 + 1)) { case 65: case 97: // a sc_display(arguments[j], p); i += 2; j++; break; case 83: case 115: // s sc_write(arguments[j], p); i += 2; j++; break; case 86: case 118: // v sc_display(arguments[j], p); p.appendJSString("\n"); i += 2; j++; break; case 67: case 99: // c p.appendJSString(String.fromCharCode(arguments[j])); i += 2; j++; break; case 88: case 120: // x p.appendJSString(arguments[j].toString(6)); i += 2; j++; break; case 79: case 111: // o p.appendJSString(arguments[j].toString(8)); i += 2; j++; break; case 66: case 98: // b p.appendJSString(arguments[j].toString(2)); i += 2; j++; break; case 37: case 110: // %, n p.appendJSString("\n"); i += 2; break; case 114: // r p.appendJSString("\r"); i += 2; break; case 126: // ~ p.appendJSString("~"); i += 2; break; default: sc_error( "format: illegal ~" + String.fromCharCode(s.charCodeAt(i2 + 1)) + " sequence" ); return ""; } } } return p.close(); } /* ------------------ global ports ---------------------------------------------------*/ var SC_DEFAULT_IN = new sc_ErrorInputPort(); var SC_DEFAULT_OUT = new sc_ErrorOutputPort(); var SC_ERROR_OUT = new sc_ErrorOutputPort(); var sc_SYMBOL_PREFIX = "\u1E9C"; var sc_KEYWORD_PREFIX = "\u1E9D"; /*** META ((export #t) (peephole (id))) */ function sc_jsstring2string(s) { return s; } /*** META ((export #t) (peephole (prefix "'\\u1E9C' +"))) */ function sc_jsstring2symbol(s) { return sc_SYMBOL_PREFIX + s; } /*** META ((export #t) (peephole (id))) */ function sc_string2jsstring(s) { return s; } /*** META ((export #t) (peephole (symbol2jsstring_immutable))) */ function sc_symbol2jsstring(s) { return s.slice(1); } /*** META ((export #t) (peephole (postfix ".slice(1)"))) */ function sc_keyword2jsstring(k) { return k.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9D' +"))) */ function sc_jsstring2keyword(s) { return sc_KEYWORD_PREFIX + s; } /*** META ((export #t) (type bool)) */ function sc_isKeyword(s) { return (typeof s === "string") && (s.charAt(0) === sc_KEYWORD_PREFIX); } /*** META ((export #t)) */ var sc_gensym = function() { var counter = 1000; return function(sym) { counter++; if (!sym) sym = sc_SYMBOL_PREFIX; return sym + "s" + counter + "~" + "^sC-GeNsYm "; }; }(); /*** META ((export #t) (type bool)) */ function sc_isEqual(o1, o2) { return ((o1 === o2) || (sc_isPair(o1) && sc_isPair(o2) && sc_isPairEqual(o1, o2, sc_isEqual)) || (sc_isVector(o1) && sc_isVector(o2) && sc_isVectorEqual(o1, o2, sc_isEqual))); } /*** META ((export number->symbol integer->symbol)) */ function sc_number2symbol(x, radix) { return sc_SYMBOL_PREFIX + sc_number2jsstring(x, radix); } /*** META ((export number->string integer->string)) */ var sc_number2string = sc_number2jsstring; /*** META ((export #t)) */ function sc_symbol2number(s, radix) { return sc_jsstring2number(s.slice(1), radix); } /*** META ((export #t)) */ var sc_string2number = sc_jsstring2number; /*** META ((export #t) (peephole (prefix "+" s))) ;; peephole will only apply if no radix is given. */ function sc_string2integer(s, radix) { if (!radix) return +s; return parseInt(s, radix); } /*** META ((export #t) (peephole (prefix "+"))) */ function sc_string2real(s) { return +s; } /*** META ((export #t) (type bool)) */ function sc_isSymbol(s) { return (typeof s === "string") && (s.charAt(0) === sc_SYMBOL_PREFIX); } /*** META ((export #t) (peephole (symbol2string_immutable))) */ function sc_symbol2string(s) { return s.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9C' +"))) */ function sc_string2symbol(s) { return sc_SYMBOL_PREFIX + s; } /*** META ((export symbol-append) (peephole (symbolAppend_immutable))) */ function sc_symbolAppend() { var res = sc_SYMBOL_PREFIX; for (var i = 0; i < arguments.length; i++) res += arguments[i].slice(1); return res; } /*** META ((export #t) (peephole (postfix ".val"))) */ function sc_char2string(c) { return c.val; } /*** META ((export #t) (peephole (hole 1 "'\\u1E9C' + " c ".val"))) */ function sc_char2symbol(c) { return sc_SYMBOL_PREFIX + c.val; } /*** META ((export #t) (type bool)) */ function sc_isString(s) { return (typeof s === "string") && (s.charAt(0) !== sc_SYMBOL_PREFIX); } /*** META ((export #t)) */ var sc_makeString = sc_makejsString; /*** META ((export #t)) */ function sc_string() { for (var i = 0; i < arguments.length; i++) arguments[i] = arguments[i].val; return "".concat.apply("", arguments); } /*** META ((export #t) (peephole (postfix ".length"))) */ function sc_stringLength(s) { return s.length; } /*** META ((export #t)) */ function sc_stringRef(s, k) { return new sc_Char(s.charAt(k)); } /* there's no stringSet in the immutable version function sc_stringSet(s, k, c) */ /*** META ((export string=?) (type bool) (peephole (hole 2 str1 " === " str2))) */ function sc_isStringEqual(s1, s2) { return s1 === s2; } /*** META ((export string?) (type bool) (peephole (hole 2 str1 " > " str2))) */ function sc_isStringGreater(s1, s2) { return s1 > s2; } /*** META ((export string<=?) (type bool) (peephole (hole 2 str1 " <= " str2))) */ function sc_isStringLessEqual(s1, s2) { return s1 <= s2; } /*** META ((export string>=?) (type bool) (peephole (hole 2 str1 " >= " str2))) */ function sc_isStringGreaterEqual(s1, s2) { return s1 >= s2; } /*** META ((export string-ci=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() === " str2 ".toLowerCase()"))) */ function sc_isStringCIEqual(s1, s2) { return s1.toLowerCase() === s2.toLowerCase(); } /*** META ((export string-ci?) (type bool) (peephole (hole 2 str1 ".toLowerCase() > " str2 ".toLowerCase()"))) */ function sc_isStringCIGreater(s1, s2) { return s1.toLowerCase() > s2.toLowerCase(); } /*** META ((export string-ci<=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() <= " str2 ".toLowerCase()"))) */ function sc_isStringCILessEqual(s1, s2) { return s1.toLowerCase() <= s2.toLowerCase(); } /*** META ((export string-ci>=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() >= " str2 ".toLowerCase()"))) */ function sc_isStringCIGreaterEqual(s1, s2) { return s1.toLowerCase() >= s2.toLowerCase(); } /*** META ((export #t) (peephole (hole 3 s ".substring(" start ", " end ")"))) */ function sc_substring(s, start, end) { return s.substring(start, end); } /*** META ((export #t)) */ function sc_isSubstring_at(s1, s2, i) { return s2 == s1.substring(i, i+ s2.length); } /*** META ((export #t) (peephole (infix 0 #f "+" "''"))) */ function sc_stringAppend() { return "".concat.apply("", arguments); } /*** META ((export #t)) */ var sc_string2list = sc_jsstring2list; /*** META ((export #t)) */ var sc_list2string = sc_list2jsstring; /*** META ((export #t) (peephole (id))) */ function sc_stringCopy(s) { return s; } /* there's no string-fill in the immutable version function sc_stringFill(s, c) */ /*** META ((export #t) (peephole (postfix ".slice(1)"))) */ function sc_keyword2string(o) { return o.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9D' +"))) */ function sc_string2keyword(o) { return sc_KEYWORD_PREFIX + o; } String.prototype.sc_toDisplayString = function() { if (this.charAt(0) === sc_SYMBOL_PREFIX) // TODO: care for symbols with spaces (escape-chars symbols). return this.slice(1); else if (this.charAt(0) === sc_KEYWORD_PREFIX) return ":" + this.slice(1); else return this.toString(); }; String.prototype.sc_toWriteString = function() { if (this.charAt(0) === sc_SYMBOL_PREFIX) // TODO: care for symbols with spaces (escape-chars symbols). return this.slice(1); else if (this.charAt(0) === sc_KEYWORD_PREFIX) return ":" + this.slice(1); else return '"' + sc_escapeWriteString(this) + '"'; }; /* Exported Variables */ var BgL_testzd2boyerzd2; var BgL_nboyerzd2benchmarkzd2; var BgL_setupzd2boyerzd2; /* End Exports */ var translate_term_nboyer; var translate_args_nboyer; var untranslate_term_nboyer; var BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer; var BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer; var translate_alist_nboyer; var apply_subst_nboyer; var apply_subst_lst_nboyer; var tautologyp_nboyer; var if_constructor_nboyer; var rewrite_count_nboyer; var rewrite_nboyer; var rewrite_args_nboyer; var unify_subst_nboyer; var one_way_unify1_nboyer; var false_term_nboyer; var true_term_nboyer; var trans_of_implies1_nboyer; var is_term_equal_nboyer; var is_term_member_nboyer; var const_nboyer; var sc_const_3_nboyer; var sc_const_4_nboyer; { (sc_const_4_nboyer = (new sc_Pair("\u1E9Cimplies",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cu",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cw",null)))))),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cw",null)))))),null))))))); (sc_const_3_nboyer = sc_list((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccompile",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Ccodegen",(new sc_Pair((new sc_Pair("\u1E9Coptimize",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreaterp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clesseqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cboolean",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ciff",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceven1",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Codd",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccountps-",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccountps-loop",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfact-",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfact-loop",(new sc_Pair("\u1E9Ci",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdivides",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-true",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-false",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctautology-checker",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctautologyp",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfalsify",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfalsify1",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime1",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair("\u1E9Cp",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))))),(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cc",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cplus-fringe",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair("\u1E9Cenvrn",null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmc-flatten",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cintersect",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Ck",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ck",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Csort-lp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus1",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Ci",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cbase",null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cj",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cj",(new sc_Pair((1),null)))))),null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Ci",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cw",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cz",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnlistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csamefringe",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cz",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cw",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair(sc_list("\u1E9Cand", (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Ca",null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cb",null)))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cl",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cl",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdsort",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx1",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx2",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx3",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx4",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx5",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx6",(new sc_Pair("\u1E9Cx7",null)))))),null)))))),null)))))),null)))))),null)))))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((6),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx7",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cy",(new sc_Pair((2),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csigma",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Ci",null)))),null)))))),(new sc_Pair((2),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cz",null)))),null)))))),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Ca",null)))),null)))),(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Cb",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair("\u1E9Cz",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cassignedp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair((new sc_Pair("\u1E9Cset",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cval",(new sc_Pair("\u1E9Cmem",null)))))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair("\u1E9Cval",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cmem",null)))))),null)))))))),null)))))))); (const_nboyer = (new sc_Pair((new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))))),null))))))))))); BgL_nboyerzd2benchmarkzd2 = function() { var args = null; for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { args = sc_cons(arguments[sc_tmp], args); } var n; return ((n = ((args === null)?(0):(args.car))), (BgL_setupzd2boyerzd2()), (BgL_runzd2benchmarkzd2(("nboyer"+(sc_number2string(n))), (1), function() { return (BgL_testzd2boyerzd2(n)); }, function(rewrites) { if ((sc_isNumber(rewrites))) switch (n) { case (0): return (rewrites===(95024)); break; case (1): return (rewrites===(591777)); break; case (2): return (rewrites===(1813975)); break; case (3): return (rewrites===(5375678)); break; case (4): return (rewrites===(16445406)); break; case (5): return (rewrites===(51507739)); break; default: return true; break; } else return false; }))); }; BgL_setupzd2boyerzd2 = function() { return true; }; BgL_testzd2boyerzd2 = function() { return true; }; translate_term_nboyer = function(term) { var lst; return (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((translate_term_nboyer((lst.car))), (translate_args_nboyer((lst.cdr)))))))))); }; translate_args_nboyer = function(lst) { var sc_lst_5; var term; return ((lst === null)?null:(new sc_Pair(((term = (lst.car)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))), ((sc_lst_5 = (lst.cdr)), ((sc_lst_5 === null)?null:(new sc_Pair((translate_term_nboyer((sc_lst_5.car))), (translate_args_nboyer((sc_lst_5.cdr)))))))))); }; untranslate_term_nboyer = function(term) { var optrOpnd; var tail1131; var L1127; var falseHead1130; var symbol_record; if (!(term instanceof sc_Pair)) return term; else { (falseHead1130 = (new sc_Pair(null, null))); (L1127 = (term.cdr)); (tail1131 = falseHead1130); while (!(L1127 === null)) { { (tail1131.cdr = (new sc_Pair((untranslate_term_nboyer((L1127.car))), null))); (tail1131 = (tail1131.cdr)); (L1127 = (L1127.cdr)); } } (optrOpnd = (falseHead1130.cdr)); return (new sc_Pair(((symbol_record = (term.car)), (symbol_record[(0)])), optrOpnd)); } }; BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer = function(sym) { var r; var x; return ((x = (sc_assq(sym, BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), ((x!== false)?(x.cdr):((r = [sym, null]), (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = (new sc_Pair((new sc_Pair(sym, r)), BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), r))); }; (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); translate_alist_nboyer = function(alist) { var sc_alist_6; var term; return ((alist === null)?null:(new sc_Pair((new sc_Pair((alist.car.car), ((term = (alist.car.cdr)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))))), ((sc_alist_6 = (alist.cdr)), ((sc_alist_6 === null)?null:(new sc_Pair((new sc_Pair((sc_alist_6.car.car), (translate_term_nboyer((sc_alist_6.car.cdr))))), (translate_alist_nboyer((sc_alist_6.cdr)))))))))); }; apply_subst_nboyer = function(alist, term) { var lst; var temp_temp; return (!(term instanceof sc_Pair)?((temp_temp = (sc_assq(term, alist))), ((temp_temp!== false)?(temp_temp.cdr):term)):(new sc_Pair((term.car), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), (apply_subst_lst_nboyer(alist, (lst.cdr)))))))))); }; apply_subst_lst_nboyer = function(alist, lst) { var sc_lst_7; return ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), ((sc_lst_7 = (lst.cdr)), ((sc_lst_7 === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (sc_lst_7.car))), (apply_subst_lst_nboyer(alist, (sc_lst_7.cdr)))))))))); }; tautologyp_nboyer = function(sc_x_11, true_lst, false_lst) { var tmp1125; var x; var tmp1126; var sc_x_8; var sc_tmp1125_9; var sc_tmp1126_10; var sc_x_11; var true_lst; var false_lst; while (true) { if ((((sc_tmp1126_10 = (is_term_equal_nboyer(sc_x_11, true_term_nboyer))), ((sc_tmp1126_10!== false)?sc_tmp1126_10:(is_term_member_nboyer(sc_x_11, true_lst))))!== false)) return true; else if ((((sc_tmp1125_9 = (is_term_equal_nboyer(sc_x_11, false_term_nboyer))), ((sc_tmp1125_9!== false)?sc_tmp1125_9:(is_term_member_nboyer(sc_x_11, false_lst))))!== false)) return false; else if (!(sc_x_11 instanceof sc_Pair)) return false; else if (((sc_x_11.car)===if_constructor_nboyer)) if ((((sc_x_8 = (sc_x_11.cdr.car)), (tmp1126 = (is_term_equal_nboyer(sc_x_8, true_term_nboyer))), ((tmp1126!== false)?tmp1126:(is_term_member_nboyer(sc_x_8, true_lst))))!== false)) (sc_x_11 = (sc_x_11.cdr.cdr.car)); else if ((((x = (sc_x_11.cdr.car)), (tmp1125 = (is_term_equal_nboyer(x, false_term_nboyer))), ((tmp1125!== false)?tmp1125:(is_term_member_nboyer(x, false_lst))))!== false)) (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); else if (((tautologyp_nboyer((sc_x_11.cdr.cdr.car), (new sc_Pair((sc_x_11.cdr.car), true_lst)), false_lst))!== false)) { (false_lst = (new sc_Pair((sc_x_11.cdr.car), false_lst))); (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); } else return false; else return false; } }; (if_constructor_nboyer = "\u1E9C*"); (rewrite_count_nboyer = (0)); rewrite_nboyer = function(term) { var term2; var sc_term_12; var lst; var symbol_record; var sc_lst_13; { (++rewrite_count_nboyer); if (!(term instanceof sc_Pair)) return term; else { (sc_term_12 = (new sc_Pair((term.car), ((sc_lst_13 = (term.cdr)), ((sc_lst_13 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_13.car))), (rewrite_args_nboyer((sc_lst_13.cdr)))))))))); (lst = ((symbol_record = (term.car)), (symbol_record[(1)]))); while (true) { if ((lst === null)) return sc_term_12; else if ((((term2 = ((lst.car).cdr.car)), (unify_subst_nboyer = null), (one_way_unify1_nboyer(sc_term_12, term2)))!== false)) return (rewrite_nboyer((apply_subst_nboyer(unify_subst_nboyer, ((lst.car).cdr.cdr.car))))); else (lst = (lst.cdr)); } } } }; rewrite_args_nboyer = function(lst) { var sc_lst_14; return ((lst === null)?null:(new sc_Pair((rewrite_nboyer((lst.car))), ((sc_lst_14 = (lst.cdr)), ((sc_lst_14 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_14.car))), (rewrite_args_nboyer((sc_lst_14.cdr)))))))))); }; (unify_subst_nboyer = "\u1E9C*"); one_way_unify1_nboyer = function(term1, term2) { var lst1; var lst2; var temp_temp; if (!(term2 instanceof sc_Pair)) { (temp_temp = (sc_assq(term2, unify_subst_nboyer))); if ((temp_temp!== false)) return (is_term_equal_nboyer(term1, (temp_temp.cdr))); else if ((sc_isNumber(term2))) return (sc_isEqual(term1, term2)); else { (unify_subst_nboyer = (new sc_Pair((new sc_Pair(term2, term1)), unify_subst_nboyer))); return true; } } else if (!(term1 instanceof sc_Pair)) return false; else if (((term1.car)===(term2.car))) { (lst1 = (term1.cdr)); (lst2 = (term2.cdr)); while (true) { if ((lst1 === null)) return (lst2 === null); else if ((lst2 === null)) return false; else if (((one_way_unify1_nboyer((lst1.car), (lst2.car)))!== false)) { (lst1 = (lst1.cdr)); (lst2 = (lst2.cdr)); } else return false; } } else return false; }; (false_term_nboyer = "\u1E9C*"); (true_term_nboyer = "\u1E9C*"); trans_of_implies1_nboyer = function(n) { var sc_n_15; return ((sc_isEqual(n, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (n-(1)), n)), ((sc_n_15 = (n-(1))), ((sc_isEqual(sc_n_15, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (sc_n_15-(1)), sc_n_15)), (trans_of_implies1_nboyer((sc_n_15-(1))))))))))); }; is_term_equal_nboyer = function(x, y) { var lst1; var lst2; var r2; var r1; if ((x instanceof sc_Pair)) if ((y instanceof sc_Pair)) if ((((r1 = (x.car)), (r2 = (y.car)), (r1===r2))!== false)) { (lst1 = (x.cdr)); (lst2 = (y.cdr)); while (true) { if ((lst1 === null)) return (lst2 === null); else if ((lst2 === null)) return false; else if (((is_term_equal_nboyer((lst1.car), (lst2.car)))!== false)) { (lst1 = (lst1.cdr)); (lst2 = (lst2.cdr)); } else return false; } } else return false; else return false; else return (sc_isEqual(x, y)); }; is_term_member_nboyer = function(x, lst) { var x; var lst; while (true) { if ((lst === null)) return false; else if (((is_term_equal_nboyer(x, (lst.car)))!== false)) return true; else (lst = (lst.cdr)); } }; BgL_setupzd2boyerzd2 = function() { var symbol_record; var value; var BgL_sc_symbolzd2record_16zd2; var sym; var sc_sym_17; var term; var lst; var sc_term_18; var sc_term_19; { (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); (if_constructor_nboyer = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer("\u1E9Cif"))); (false_term_nboyer = ((sc_term_19 = (new sc_Pair("\u1E9Cf",null))), (!(sc_term_19 instanceof sc_Pair)?sc_term_19:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_19.car))), (translate_args_nboyer((sc_term_19.cdr)))))))); (true_term_nboyer = ((sc_term_18 = (new sc_Pair("\u1E9Ct",null))), (!(sc_term_18 instanceof sc_Pair)?sc_term_18:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_18.car))), (translate_args_nboyer((sc_term_18.cdr)))))))); (lst = sc_const_3_nboyer); while (!(lst === null)) { { (term = (lst.car)); if (((term instanceof sc_Pair)&&(((term.car)==="\u1E9Cequal")&&((term.cdr.car) instanceof sc_Pair)))) { (sc_sym_17 = ((term.cdr.car).car)); (value = (new sc_Pair((!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr)))))), ((sym = ((term.cdr.car).car)), (BgL_sc_symbolzd2record_16zd2 = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sym))), (BgL_sc_symbolzd2record_16zd2[(1)]))))); (symbol_record = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sc_sym_17))); (symbol_record[(1)] = value); } else (sc_error("ADD-LEMMA did not like term: ", term)); (lst = (lst.cdr)); } } return true; } }; BgL_testzd2boyerzd2 = function(n) { var optrOpnd; var term; var sc_n_20; var answer; var sc_term_21; var sc_term_22; { (rewrite_count_nboyer = (0)); (term = sc_const_4_nboyer); (sc_n_20 = n); while (!(sc_n_20=== 0)) { { (term = (sc_list("\u1E9Cor", term, (new sc_Pair("\u1E9Cf",null))))); (--sc_n_20); } } (sc_term_22 = term); if (!(sc_term_22 instanceof sc_Pair)) (optrOpnd = sc_term_22); else (optrOpnd = (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_22.car))), (translate_args_nboyer((sc_term_22.cdr)))))); (sc_term_21 = (apply_subst_nboyer(((const_nboyer === null)?null:(new sc_Pair((new sc_Pair((const_nboyer.car.car), (translate_term_nboyer((const_nboyer.car.cdr))))), (translate_alist_nboyer((const_nboyer.cdr)))))), optrOpnd))); (answer = (tautologyp_nboyer((rewrite_nboyer(sc_term_21)), null, null))); (sc_write(rewrite_count_nboyer)); (sc_display(" rewrites")); (sc_newline()); if ((answer!== false)) return rewrite_count_nboyer; else return false; } }; } /* Exported Variables */ var BgL_parsezd2ze3nbzd2treesze3; var BgL_earleyzd2benchmarkzd2; var BgL_parsezd2ze3parsedzf3zc2; var test; var BgL_parsezd2ze3treesz31; var BgL_makezd2parserzd2; /* End Exports */ var const_earley; { (const_earley = (new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair((new sc_Pair("\u1E9Ca",null)),(new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair("\u1E9Cs",null)))),null)))))),null))); BgL_makezd2parserzd2 = function(grammar, lexer) { var i; var parser_descr; var def_loop; var nb_nts; var names; var steps; var predictors; var enders; var starters; var nts; var sc_names_1; var sc_steps_2; var sc_predictors_3; var sc_enders_4; var sc_starters_5; var nb_confs; var BgL_sc_defzd2loop_6zd2; var BgL_sc_nbzd2nts_7zd2; var sc_nts_8; var BgL_sc_defzd2loop_9zd2; var ind; { ind = function(nt, sc_nts_10) { var i; { (i = ((sc_nts_10.length)-(1))); while (true) { if ((i>=(0))) if ((sc_isEqual((sc_nts_10[i]), nt))) return i; else (--i); else return false; } } }; (sc_nts_8 = ((BgL_sc_defzd2loop_9zd2 = function(defs, sc_nts_11) { var rule_loop; var head; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, sc_nts_12) { var nt; var l; var sc_nts_13; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (l = rule); (sc_nts_13 = sc_nts_12); while ((l instanceof sc_Pair)) { { (nt = (l.car)); (l = (l.cdr)); (sc_nts_13 = (((sc_member(nt, sc_nts_13))!== false)?sc_nts_13:(new sc_Pair(nt, sc_nts_13)))); } } return (rule_loop((rules.cdr), sc_nts_13)); } else return (BgL_sc_defzd2loop_9zd2((defs.cdr), sc_nts_12)); }), (rule_loop((def.cdr), (((sc_member(head, sc_nts_11))!== false)?sc_nts_11:(new sc_Pair(head, sc_nts_11)))))):(sc_list2vector((sc_reverse(sc_nts_11))))); }), (BgL_sc_defzd2loop_9zd2(grammar, null)))); (BgL_sc_nbzd2nts_7zd2 = (sc_nts_8.length)); (nb_confs = (((BgL_sc_defzd2loop_6zd2 = function(defs, BgL_sc_nbzd2confs_14zd2) { var rule_loop; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (rule_loop = function(rules, BgL_sc_nbzd2confs_15zd2) { var l; var BgL_sc_nbzd2confs_16zd2; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (l = rule); (BgL_sc_nbzd2confs_16zd2 = BgL_sc_nbzd2confs_15zd2); while ((l instanceof sc_Pair)) { { (l = (l.cdr)); (++BgL_sc_nbzd2confs_16zd2); } } return (rule_loop((rules.cdr), (BgL_sc_nbzd2confs_16zd2+(1)))); } else return (BgL_sc_defzd2loop_6zd2((defs.cdr), BgL_sc_nbzd2confs_15zd2)); }), (rule_loop((def.cdr), BgL_sc_nbzd2confs_14zd2))):BgL_sc_nbzd2confs_14zd2); }), (BgL_sc_defzd2loop_6zd2(grammar, (0))))+BgL_sc_nbzd2nts_7zd2)); (sc_starters_5 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_enders_4 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_predictors_3 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_steps_2 = (sc_makeVector(nb_confs, false))); (sc_names_1 = (sc_makeVector(nb_confs, false))); (nts = sc_nts_8); (starters = sc_starters_5); (enders = sc_enders_4); (predictors = sc_predictors_3); (steps = sc_steps_2); (names = sc_names_1); (nb_nts = (sc_nts_8.length)); (i = (nb_nts-(1))); while ((i>=(0))) { { (sc_steps_2[i] = (i-nb_nts)); (sc_names_1[i] = (sc_list((sc_nts_8[i]), (0)))); (sc_enders_4[i] = (sc_list(i))); (--i); } } def_loop = function(defs, conf) { var rule_loop; var head; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, conf, rule_num) { var i; var sc_i_17; var nt; var l; var sc_conf_18; var sc_i_19; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (names[conf] = (sc_list(head, rule_num))); (sc_i_19 = (ind(head, nts))); (starters[sc_i_19] = (new sc_Pair(conf, (starters[sc_i_19])))); (l = rule); (sc_conf_18 = conf); while ((l instanceof sc_Pair)) { { (nt = (l.car)); (steps[sc_conf_18] = (ind(nt, nts))); (sc_i_17 = (ind(nt, nts))); (predictors[sc_i_17] = (new sc_Pair(sc_conf_18, (predictors[sc_i_17])))); (l = (l.cdr)); (++sc_conf_18); } } (steps[sc_conf_18] = ((ind(head, nts))-nb_nts)); (i = (ind(head, nts))); (enders[i] = (new sc_Pair(sc_conf_18, (enders[i])))); return (rule_loop((rules.cdr), (sc_conf_18+(1)), (rule_num+(1)))); } else return (def_loop((defs.cdr), conf)); }), (rule_loop((def.cdr), conf, (1)))):undefined); }; (def_loop(grammar, (sc_nts_8.length))); (parser_descr = [lexer, sc_nts_8, sc_starters_5, sc_enders_4, sc_predictors_3, sc_steps_2, sc_names_1]); return function(input) { var optrOpnd; var sc_optrOpnd_20; var sc_optrOpnd_21; var sc_optrOpnd_22; var loop1; var BgL_sc_stateza2_23za2; var toks; var BgL_sc_nbzd2nts_24zd2; var sc_steps_25; var sc_enders_26; var state_num; var BgL_sc_statesza2_27za2; var states; var i; var conf; var l; var tok_nts; var sc_i_28; var sc_i_29; var l1; var l2; var tok; var tail1129; var L1125; var goal_enders; var BgL_sc_statesza2_30za2; var BgL_sc_nbzd2nts_31zd2; var BgL_sc_nbzd2confs_32zd2; var nb_toks; var goal_starters; var sc_states_33; var BgL_sc_nbzd2confs_34zd2; var BgL_sc_nbzd2toks_35zd2; var sc_toks_36; var falseHead1128; var sc_names_37; var sc_steps_38; var sc_predictors_39; var sc_enders_40; var sc_starters_41; var sc_nts_42; var lexer; var sc_ind_43; var make_states; var BgL_sc_confzd2setzd2getza2_44za2; var conf_set_merge_new_bang; var conf_set_adjoin; var BgL_sc_confzd2setzd2adjoinza2_45za2; var BgL_sc_confzd2setzd2adjoinza2za2_46z00; var conf_set_union; var forw; var is_parsed; var deriv_trees; var BgL_sc_derivzd2treesza2_47z70; var nb_deriv_trees; var BgL_sc_nbzd2derivzd2treesza2_48za2; { sc_ind_43 = function(nt, sc_nts_49) { var i; { (i = ((sc_nts_49.length)-(1))); while (true) { if ((i>=(0))) if ((sc_isEqual((sc_nts_49[i]), nt))) return i; else (--i); else return false; } } }; make_states = function(BgL_sc_nbzd2toks_50zd2, BgL_sc_nbzd2confs_51zd2) { var v; var i; var sc_states_52; { (sc_states_52 = (sc_makeVector((BgL_sc_nbzd2toks_50zd2+(1)), false))); (i = BgL_sc_nbzd2toks_50zd2); while ((i>=(0))) { { (v = (sc_makeVector((BgL_sc_nbzd2confs_51zd2+(1)), false))); (v[(0)] = (-1)); (sc_states_52[i] = v); (--i); } } return sc_states_52; } }; BgL_sc_confzd2setzd2getza2_44za2 = function(state, BgL_sc_statezd2num_53zd2, sc_conf_54) { var conf_set; var BgL_sc_confzd2set_55zd2; return ((BgL_sc_confzd2set_55zd2 = (state[(sc_conf_54+(1))])), ((BgL_sc_confzd2set_55zd2!== false)?BgL_sc_confzd2set_55zd2:((conf_set = (sc_makeVector((BgL_sc_statezd2num_53zd2+(6)), false))), (conf_set[(1)] = (-3)), (conf_set[(2)] = (-1)), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)), (state[(sc_conf_54+(1))] = conf_set), conf_set))); }; conf_set_merge_new_bang = function(conf_set) { return ((conf_set[((conf_set[(1)])+(5))] = (conf_set[(4)])), (conf_set[(1)] = (conf_set[(3)])), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1))); }; conf_set_adjoin = function(state, conf_set, sc_conf_56, i) { var tail; return ((tail = (conf_set[(3)])), (conf_set[(i+(5))] = (-1)), (conf_set[(tail+(5))] = i), (conf_set[(3)] = i), ((tail<(0))?((conf_set[(0)] = (state[(0)])), (state[(0)] = sc_conf_56)):undefined)); }; BgL_sc_confzd2setzd2adjoinza2_45za2 = function(sc_states_57, BgL_sc_statezd2num_58zd2, l, i) { var conf_set; var sc_conf_59; var l1; var state; { (state = (sc_states_57[BgL_sc_statezd2num_58zd2])); (l1 = l); while ((l1 instanceof sc_Pair)) { { (sc_conf_59 = (l1.car)); (conf_set = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_58zd2, sc_conf_59))); if (((conf_set[(i+(5))])=== false)) { (conf_set_adjoin(state, conf_set, sc_conf_59, i)); (l1 = (l1.cdr)); } else (l1 = (l1.cdr)); } } return undefined; } }; BgL_sc_confzd2setzd2adjoinza2za2_46z00 = function(sc_states_60, BgL_sc_statesza2_61za2, BgL_sc_statezd2num_62zd2, sc_conf_63, i) { var BgL_sc_confzd2setza2_64z70; var BgL_sc_stateza2_65za2; var conf_set; var state; return ((state = (sc_states_60[BgL_sc_statezd2num_62zd2])), ((((conf_set = (state[(sc_conf_63+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)?((BgL_sc_stateza2_65za2 = (BgL_sc_statesza2_61za2[BgL_sc_statezd2num_62zd2])), (BgL_sc_confzd2setza2_64z70 = (BgL_sc_confzd2setzd2getza2_44za2(BgL_sc_stateza2_65za2, BgL_sc_statezd2num_62zd2, sc_conf_63))), (((BgL_sc_confzd2setza2_64z70[(i+(5))])=== false)?(conf_set_adjoin(BgL_sc_stateza2_65za2, BgL_sc_confzd2setza2_64z70, sc_conf_63, i)):undefined), true):false)); }; conf_set_union = function(state, conf_set, sc_conf_66, other_set) { var i; { (i = (other_set[(2)])); while ((i>=(0))) { if (((conf_set[(i+(5))])=== false)) { (conf_set_adjoin(state, conf_set, sc_conf_66, i)); (i = (other_set[(i+(5))])); } else (i = (other_set[(i+(5))])); } return undefined; } }; forw = function(sc_states_67, BgL_sc_statezd2num_68zd2, sc_starters_69, sc_enders_70, sc_predictors_71, sc_steps_72, sc_nts_73) { var next_set; var next; var conf_set; var ender; var l; var starter_set; var starter; var sc_l_74; var sc_loop1_75; var head; var BgL_sc_confzd2set_76zd2; var BgL_sc_statezd2num_77zd2; var state; var sc_states_78; var preds; var BgL_sc_confzd2set_79zd2; var step; var sc_conf_80; var BgL_sc_nbzd2nts_81zd2; var sc_state_82; { (sc_state_82 = (sc_states_67[BgL_sc_statezd2num_68zd2])); (BgL_sc_nbzd2nts_81zd2 = (sc_nts_73.length)); while (true) { { (sc_conf_80 = (sc_state_82[(0)])); if ((sc_conf_80>=(0))) { (step = (sc_steps_72[sc_conf_80])); (BgL_sc_confzd2set_79zd2 = (sc_state_82[(sc_conf_80+(1))])); (head = (BgL_sc_confzd2set_79zd2[(4)])); (sc_state_82[(0)] = (BgL_sc_confzd2set_79zd2[(0)])); (conf_set_merge_new_bang(BgL_sc_confzd2set_79zd2)); if ((step>=(0))) { (sc_l_74 = (sc_starters_69[step])); while ((sc_l_74 instanceof sc_Pair)) { { (starter = (sc_l_74.car)); (starter_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, starter))); if (((starter_set[(BgL_sc_statezd2num_68zd2+(5))])=== false)) { (conf_set_adjoin(sc_state_82, starter_set, starter, BgL_sc_statezd2num_68zd2)); (sc_l_74 = (sc_l_74.cdr)); } else (sc_l_74 = (sc_l_74.cdr)); } } (l = (sc_enders_70[step])); while ((l instanceof sc_Pair)) { { (ender = (l.car)); if ((((conf_set = (sc_state_82[(ender+(1))])), ((conf_set!== false)?(conf_set[(BgL_sc_statezd2num_68zd2+(5))]):false))!== false)) { (next = (sc_conf_80+(1))); (next_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, next))); (conf_set_union(sc_state_82, next_set, next, BgL_sc_confzd2set_79zd2)); (l = (l.cdr)); } else (l = (l.cdr)); } } } else { (preds = (sc_predictors_71[(step+BgL_sc_nbzd2nts_81zd2)])); (sc_states_78 = sc_states_67); (state = sc_state_82); (BgL_sc_statezd2num_77zd2 = BgL_sc_statezd2num_68zd2); (BgL_sc_confzd2set_76zd2 = BgL_sc_confzd2set_79zd2); sc_loop1_75 = function(l) { var sc_state_83; var BgL_sc_nextzd2set_84zd2; var sc_next_85; var pred_set; var i; var pred; if ((l instanceof sc_Pair)) { (pred = (l.car)); (i = head); while ((i>=(0))) { { (pred_set = ((sc_state_83 = (sc_states_78[i])), (sc_state_83[(pred+(1))]))); if ((pred_set!== false)) { (sc_next_85 = (pred+(1))); (BgL_sc_nextzd2set_84zd2 = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_77zd2, sc_next_85))); (conf_set_union(state, BgL_sc_nextzd2set_84zd2, sc_next_85, pred_set)); } (i = (BgL_sc_confzd2set_76zd2[(i+(5))])); } } return (sc_loop1_75((l.cdr))); } else return undefined; }; (sc_loop1_75(preds)); } } else return undefined; } } } }; is_parsed = function(nt, i, j, sc_nts_86, sc_enders_87, sc_states_88) { var conf_set; var state; var sc_conf_89; var l; var BgL_sc_ntza2_90za2; { (BgL_sc_ntza2_90za2 = (sc_ind_43(nt, sc_nts_86))); if ((BgL_sc_ntza2_90za2!== false)) { (sc_nts_86.length); (l = (sc_enders_87[BgL_sc_ntza2_90za2])); while (true) { if ((l instanceof sc_Pair)) { (sc_conf_89 = (l.car)); if ((((state = (sc_states_88[j])), (conf_set = (state[(sc_conf_89+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) return true; else (l = (l.cdr)); } else return false; } } else return false; } }; deriv_trees = function(sc_conf_91, i, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2) { var sc_loop1_98; var prev; var name; return ((name = (sc_names_94[sc_conf_91])), ((name!== false)?((sc_conf_91=(0))) if (((k>=i)&&(((sc_state_99 = (sc_states_96[k])), (conf_set = (sc_state_99[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) { (prev_trees = (deriv_trees(prev, i, k, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); (ender_trees = (deriv_trees(ender, k, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); loop3 = function(l3, l2) { var l4; var sc_l2_100; var ender_tree; if ((l3 instanceof sc_Pair)) { (ender_tree = (sc_list((l3.car)))); (l4 = prev_trees); (sc_l2_100 = l2); while ((l4 instanceof sc_Pair)) { { (sc_l2_100 = (new sc_Pair((sc_append((l4.car), ender_tree)), sc_l2_100))); (l4 = (l4.cdr)); } } return (loop3((l3.cdr), sc_l2_100)); } else return (loop2((ender_set[(k+(5))]), l2)); }; return (loop3(ender_trees, l2)); } else (k = (ender_set[(k+(5))])); else return (sc_loop1_98((l1.cdr), l2)); } }; return (loop2((ender_set[(2)]), l2)); } else (l1 = (l1.cdr)); } else return l2; } }), (sc_loop1_98((sc_enders_92[(sc_steps_93[prev])]), null))))); }; BgL_sc_derivzd2treesza2_47z70 = function(nt, i, j, sc_nts_101, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106) { var conf_set; var state; var sc_conf_107; var l; var trees; var BgL_sc_nbzd2nts_108zd2; var BgL_sc_ntza2_109za2; { (BgL_sc_ntza2_109za2 = (sc_ind_43(nt, sc_nts_101))); if ((BgL_sc_ntza2_109za2!== false)) { (BgL_sc_nbzd2nts_108zd2 = (sc_nts_101.length)); (l = (sc_enders_102[BgL_sc_ntza2_109za2])); (trees = null); while ((l instanceof sc_Pair)) { { (sc_conf_107 = (l.car)); if ((((state = (sc_states_106[j])), (conf_set = (state[(sc_conf_107+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) { (l = (l.cdr)); (trees = (sc_append((deriv_trees(sc_conf_107, i, j, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106, BgL_sc_nbzd2nts_108zd2)), trees))); } else (l = (l.cdr)); } } return trees; } else return false; } }; nb_deriv_trees = function(sc_conf_110, i, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2) { var sc_loop1_116; var tmp1124; var prev; return ((prev = (sc_conf_110-(1))), ((((tmp1124 = (sc_conf_110=(0))) { if (((k>=i)&&(((state = (sc_states_114[k])), (conf_set = (state[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) { (nb_prev_trees = (nb_deriv_trees(prev, i, k, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); (nb_ender_trees = (nb_deriv_trees(ender, k, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); (k = (ender_set[(k+(5))])); (n +=(nb_prev_trees*nb_ender_trees)); } else (k = (ender_set[(k+(5))])); } return (sc_loop1_116((l.cdr), n)); } else (l = (l.cdr)); } else return sc_n_118; } }), (sc_loop1_116((sc_enders_111[(sc_steps_112[prev])]), (0)))))); }; BgL_sc_nbzd2derivzd2treesza2_48za2 = function(nt, i, j, sc_nts_119, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123) { var conf_set; var state; var sc_conf_124; var l; var nb_trees; var BgL_sc_nbzd2nts_125zd2; var BgL_sc_ntza2_126za2; { (BgL_sc_ntza2_126za2 = (sc_ind_43(nt, sc_nts_119))); if ((BgL_sc_ntza2_126za2!== false)) { (BgL_sc_nbzd2nts_125zd2 = (sc_nts_119.length)); (l = (sc_enders_120[BgL_sc_ntza2_126za2])); (nb_trees = (0)); while ((l instanceof sc_Pair)) { { (sc_conf_124 = (l.car)); if ((((state = (sc_states_123[j])), (conf_set = (state[(sc_conf_124+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) { (l = (l.cdr)); (nb_trees = ((nb_deriv_trees(sc_conf_124, i, j, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123, BgL_sc_nbzd2nts_125zd2))+nb_trees)); } else (l = (l.cdr)); } } return nb_trees; } else return false; } }; (lexer = (parser_descr[(0)])); (sc_nts_42 = (parser_descr[(1)])); (sc_starters_41 = (parser_descr[(2)])); (sc_enders_40 = (parser_descr[(3)])); (sc_predictors_39 = (parser_descr[(4)])); (sc_steps_38 = (parser_descr[(5)])); (sc_names_37 = (parser_descr[(6)])); (falseHead1128 = (new sc_Pair(null, null))); (L1125 = (lexer(input))); (tail1129 = falseHead1128); while (!(L1125 === null)) { { (tok = (L1125.car)); (l1 = (tok.cdr)); (l2 = null); while ((l1 instanceof sc_Pair)) { { (sc_i_29 = (sc_ind_43((l1.car), sc_nts_42))); if ((sc_i_29!== false)) { (l1 = (l1.cdr)); (l2 = (new sc_Pair(sc_i_29, l2))); } else (l1 = (l1.cdr)); } } (sc_optrOpnd_22 = (new sc_Pair((tok.car), (sc_reverse(l2))))); (sc_optrOpnd_21 = (new sc_Pair(sc_optrOpnd_22, null))); (tail1129.cdr = sc_optrOpnd_21); (tail1129 = (tail1129.cdr)); (L1125 = (L1125.cdr)); } } (sc_optrOpnd_20 = (falseHead1128.cdr)); (sc_toks_36 = (sc_list2vector(sc_optrOpnd_20))); (BgL_sc_nbzd2toks_35zd2 = (sc_toks_36.length)); (BgL_sc_nbzd2confs_34zd2 = (sc_steps_38.length)); (sc_states_33 = (make_states(BgL_sc_nbzd2toks_35zd2, BgL_sc_nbzd2confs_34zd2))); (goal_starters = (sc_starters_41[(0)])); (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (0), goal_starters, (0))); (forw(sc_states_33, (0), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42)); (sc_i_28 = (0)); while ((sc_i_28=(0))) { { (states = sc_states_33); (BgL_sc_statesza2_27za2 = BgL_sc_statesza2_30za2); (state_num = i); (sc_enders_26 = sc_enders_40); (sc_steps_25 = sc_steps_38); (BgL_sc_nbzd2nts_24zd2 = BgL_sc_nbzd2nts_31zd2); (toks = sc_toks_36); (BgL_sc_stateza2_23za2 = (BgL_sc_statesza2_30za2[i])); loop1 = function() { var sc_loop1_127; var prev; var BgL_sc_statesza2_128za2; var sc_states_129; var j; var i; var sc_i_130; var head; var conf_set; var sc_conf_131; { (sc_conf_131 = (BgL_sc_stateza2_23za2[(0)])); if ((sc_conf_131>=(0))) { (conf_set = (BgL_sc_stateza2_23za2[(sc_conf_131+(1))])); (head = (conf_set[(4)])); (BgL_sc_stateza2_23za2[(0)] = (conf_set[(0)])); (conf_set_merge_new_bang(conf_set)); (sc_i_130 = head); while ((sc_i_130>=(0))) { { (i = sc_i_130); (j = state_num); (sc_states_129 = states); (BgL_sc_statesza2_128za2 = BgL_sc_statesza2_27za2); (prev = (sc_conf_131-(1))); if (((sc_conf_131>=BgL_sc_nbzd2nts_24zd2)&&((sc_steps_25[prev])>=(0)))) { sc_loop1_127 = function(l) { var k; var ender_set; var state; var ender; var l; while (true) { if ((l instanceof sc_Pair)) { (ender = (l.car)); (ender_set = ((state = (sc_states_129[j])), (state[(ender+(1))]))); if ((ender_set!== false)) { (k = (ender_set[(2)])); while ((k>=(0))) { { if ((k>=i)) if (((BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, k, prev, i))!== false)) (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, j, ender, k)); (k = (ender_set[(k+(5))])); } } return (sc_loop1_127((l.cdr))); } else (l = (l.cdr)); } else return undefined; } }; (sc_loop1_127((sc_enders_26[(sc_steps_25[prev])]))); } (sc_i_130 = (conf_set[(sc_i_130+(5))])); } } return (loop1()); } else return undefined; } }; (loop1()); (--i); } } (optrOpnd = BgL_sc_statesza2_30za2); return [sc_nts_42, sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_names_37, sc_toks_36, optrOpnd, is_parsed, BgL_sc_derivzd2treesza2_47z70, BgL_sc_nbzd2derivzd2treesza2_48za2]; } }; } }; BgL_parsezd2ze3parsedzf3zc2 = function(parse, nt, i, j) { var is_parsed; var states; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (states = (parse[(7)])), (is_parsed = (parse[(8)])), (is_parsed(nt, i, j, nts, enders, states))); }; BgL_parsezd2ze3treesz31 = function(parse, nt, i, j) { var BgL_sc_derivzd2treesza2_132z70; var states; var toks; var names; var steps; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (names = (parse[(5)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_derivzd2treesza2_132z70 = (parse[(9)])), (BgL_sc_derivzd2treesza2_132z70(nt, i, j, nts, enders, steps, names, toks, states))); }; BgL_parsezd2ze3nbzd2treesze3 = function(parse, nt, i, j) { var BgL_sc_nbzd2derivzd2treesza2_133za2; var states; var toks; var steps; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_nbzd2derivzd2treesza2_133za2 = (parse[(10)])), (BgL_sc_nbzd2derivzd2treesza2_133za2(nt, i, j, nts, enders, steps, toks, states))); }; test = function(k) { var x; var p; return ((p = (BgL_makezd2parserzd2(const_earley, function(l) { var sc_x_134; var tail1134; var L1130; var falseHead1133; { (falseHead1133 = (new sc_Pair(null, null))); (tail1134 = falseHead1133); (L1130 = l); while (!(L1130 === null)) { { (tail1134.cdr = (new sc_Pair(((sc_x_134 = (L1130.car)), (sc_list(sc_x_134, sc_x_134))), null))); (tail1134 = (tail1134.cdr)); (L1130 = (L1130.cdr)); } } return (falseHead1133.cdr); } }))), (x = (p((sc_vector2list((sc_makeVector(k, "\u1E9Ca"))))))), (sc_length((BgL_parsezd2ze3treesz31(x, "\u1E9Cs", (0), k))))); }; BgL_earleyzd2benchmarkzd2 = function() { var args = null; for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { args = sc_cons(arguments[sc_tmp], args); } var k; return ((k = ((args === null)?(7):(args.car))), (BgL_runzd2benchmarkzd2("earley", (1), function() { return (test(k)); }, function(result) { return ((sc_display(result)), (sc_newline()), result == 132); }))); }; } /************* END OF GENERATED CODE *************/ // Invoke this function to run a benchmark. // The first argument is a string identifying the benchmark. // The second argument is the number of times to run the benchmark. // The third argument is a function that runs the benchmark. // The fourth argument is a unary function that warns if the result // returned by the benchmark is incorrect. // // Example: // RunBenchmark("new Array()", // 1, // function () { new Array(1000000); } // function (v) { // return (v instanceof Array) && (v.length == 1000000); // }); SC_DEFAULT_OUT = new sc_GenericOutputPort(function(s) {}); SC_ERROR_OUT = SC_DEFAULT_OUT; function RunBenchmark(name, count, run, warn) { for (var n = 0; n < count; ++n) { result = run(); if (!warn(result)) { throw new Error("Earley or Boyer did incorrect number of rewrites"); } } } var BgL_runzd2benchmarkzd2 = RunBenchmark; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v6/deltablue.js0000644000175000017500000006206514433667662026635 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Copyright 1996 John Maloney and Mario Wolczko. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // This implementation of the DeltaBlue benchmark is derived // from the Smalltalk implementation by John Maloney and Mario // Wolczko. Some parts have been translated directly, whereas // others have been modified more aggresively to make it feel // more like a JavaScript program. var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [ new Benchmark('DeltaBlue', deltaBlue) ]); /** * A JavaScript implementation of the DeltaBlue constraint-solving * algorithm, as described in: * * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver" * Bjorn N. Freeman-Benson and John Maloney * January 1990 Communications of the ACM, * also available as University of Washington TR 89-08-06. * * Beware: this benchmark is written in a grotesque style where * the constraint model is built by side-effects from constructors. * I've kept it this way to avoid deviating too much from the original * implementation. */ /* --- O b j e c t M o d e l --- */ Object.prototype.inheritsFrom = function (shuper) { function Inheriter() { } Inheriter.prototype = shuper.prototype; this.prototype = new Inheriter(); this.superConstructor = shuper; } function OrderedCollection() { this.elms = new Array(); } OrderedCollection.prototype.add = function (elm) { this.elms.push(elm); } OrderedCollection.prototype.at = function (index) { return this.elms[index]; } OrderedCollection.prototype.size = function () { return this.elms.length; } OrderedCollection.prototype.removeFirst = function () { return this.elms.pop(); } OrderedCollection.prototype.remove = function (elm) { var index = 0, skipped = 0; for (var i = 0; i < this.elms.length; i++) { var value = this.elms[i]; if (value != elm) { this.elms[index] = value; index++; } else { skipped++; } } for (var i = 0; i < skipped; i++) this.elms.pop(); } /* --- * * S t r e n g t h * --- */ /** * Strengths are used to measure the relative importance of constraints. * New strengths may be inserted in the strength hierarchy without * disrupting current constraints. Strengths cannot be created outside * this class, so pointer comparison can be used for value comparison. */ function Strength(strengthValue, name) { this.strengthValue = strengthValue; this.name = name; } Strength.stronger = function (s1, s2) { return s1.strengthValue < s2.strengthValue; } Strength.weaker = function (s1, s2) { return s1.strengthValue > s2.strengthValue; } Strength.weakestOf = function (s1, s2) { return this.weaker(s1, s2) ? s1 : s2; } Strength.strongest = function (s1, s2) { return this.stronger(s1, s2) ? s1 : s2; } Strength.prototype.nextWeaker = function () { switch (this.strengthValue) { case 0: return Strength.WEAKEST; case 1: return Strength.WEAK_DEFAULT; case 2: return Strength.NORMAL; case 3: return Strength.STRONG_DEFAULT; case 4: return Strength.PREFERRED; case 5: return Strength.REQUIRED; } } // Strength constants. Strength.REQUIRED = new Strength(0, "required"); Strength.STONG_PREFERRED = new Strength(1, "strongPreferred"); Strength.PREFERRED = new Strength(2, "preferred"); Strength.STRONG_DEFAULT = new Strength(3, "strongDefault"); Strength.NORMAL = new Strength(4, "normal"); Strength.WEAK_DEFAULT = new Strength(5, "weakDefault"); Strength.WEAKEST = new Strength(6, "weakest"); /* --- * * C o n s t r a i n t * --- */ /** * An abstract class representing a system-maintainable relationship * (or "constraint") between a set of variables. A constraint supplies * a strength instance variable; concrete subclasses provide a means * of storing the constrained variables and other information required * to represent a constraint. */ function Constraint(strength) { this.strength = strength; } /** * Activate this constraint and attempt to satisfy it. */ Constraint.prototype.addConstraint = function () { this.addToGraph(); planner.incrementalAdd(this); } /** * Attempt to find a way to enforce this constraint. If successful, * record the solution, perhaps modifying the current dataflow * graph. Answer the constraint that this constraint overrides, if * there is one, or nil, if there isn't. * Assume: I am not already satisfied. */ Constraint.prototype.satisfy = function (mark) { this.chooseMethod(mark); if (!this.isSatisfied()) { if (this.strength == Strength.REQUIRED) alert("Could not satisfy a required constraint!"); return null; } this.markInputs(mark); var out = this.output(); var overridden = out.determinedBy; if (overridden != null) overridden.markUnsatisfied(); out.determinedBy = this; if (!planner.addPropagate(this, mark)) alert("Cycle encountered"); out.mark = mark; return overridden; } Constraint.prototype.destroyConstraint = function () { if (this.isSatisfied()) planner.incrementalRemove(this); else this.removeFromGraph(); } /** * Normal constraints are not input constraints. An input constraint * is one that depends on external state, such as the mouse, the * keybord, a clock, or some arbitraty piece of imperative code. */ Constraint.prototype.isInput = function () { return false; } /* --- * * U n a r y C o n s t r a i n t * --- */ /** * Abstract superclass for constraints having a single possible output * variable. */ function UnaryConstraint(v, strength) { UnaryConstraint.superConstructor.call(this, strength); this.myOutput = v; this.satisfied = false; this.addConstraint(); } UnaryConstraint.inheritsFrom(Constraint); /** * Adds this constraint to the constraint graph */ UnaryConstraint.prototype.addToGraph = function () { this.myOutput.addConstraint(this); this.satisfied = false; } /** * Decides if this constraint can be satisfied and records that * decision. */ UnaryConstraint.prototype.chooseMethod = function (mark) { this.satisfied = (this.myOutput.mark != mark) && Strength.stronger(this.strength, this.myOutput.walkStrength); } /** * Returns true if this constraint is satisfied in the current solution. */ UnaryConstraint.prototype.isSatisfied = function () { return this.satisfied; } UnaryConstraint.prototype.markInputs = function (mark) { // has no inputs } /** * Returns the current output variable. */ UnaryConstraint.prototype.output = function () { return this.myOutput; } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this constraint. Assume * this constraint is satisfied. */ UnaryConstraint.prototype.recalculate = function () { this.myOutput.walkStrength = this.strength; this.myOutput.stay = !this.isInput(); if (this.myOutput.stay) this.execute(); // Stay optimization } /** * Records that this constraint is unsatisfied */ UnaryConstraint.prototype.markUnsatisfied = function () { this.satisfied = false; } UnaryConstraint.prototype.inputsKnown = function () { return true; } UnaryConstraint.prototype.removeFromGraph = function () { if (this.myOutput != null) this.myOutput.removeConstraint(this); this.satisfied = false; } /* --- * * S t a y C o n s t r a i n t * --- */ /** * Variables that should, with some level of preference, stay the same. * Planners may exploit the fact that instances, if satisfied, will not * change their output during plan execution. This is called "stay * optimization". */ function StayConstraint(v, str) { StayConstraint.superConstructor.call(this, v, str); } StayConstraint.inheritsFrom(UnaryConstraint); StayConstraint.prototype.execute = function () { // Stay constraints do nothing } /* --- * * E d i t C o n s t r a i n t * --- */ /** * A unary input constraint used to mark a variable that the client * wishes to change. */ function EditConstraint(v, str) { EditConstraint.superConstructor.call(this, v, str); } EditConstraint.inheritsFrom(UnaryConstraint); /** * Edits indicate that a variable is to be changed by imperative code. */ EditConstraint.prototype.isInput = function () { return true; } EditConstraint.prototype.execute = function () { // Edit constraints do nothing } /* --- * * B i n a r y C o n s t r a i n t * --- */ var Direction = new Object(); Direction.NONE = 0; Direction.FORWARD = 1; Direction.BACKWARD = -1; /** * Abstract superclass for constraints having two possible output * variables. */ function BinaryConstraint(var1, var2, strength) { BinaryConstraint.superConstructor.call(this, strength); this.v1 = var1; this.v2 = var2; this.direction = Direction.NONE; this.addConstraint(); } BinaryConstraint.inheritsFrom(Constraint); /** * Decides if this constraint can be satisfied and which way it * should flow based on the relative strength of the variables related, * and record that decision. */ BinaryConstraint.prototype.chooseMethod = function (mark) { if (this.v1.mark == mark) { this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength)) ? Direction.FORWARD : Direction.NONE; } if (this.v2.mark == mark) { this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength)) ? Direction.BACKWARD : Direction.NONE; } if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) { this.direction = Strength.stronger(this.strength, this.v1.walkStrength) ? Direction.BACKWARD : Direction.NONE; } else { this.direction = Strength.stronger(this.strength, this.v2.walkStrength) ? Direction.FORWARD : Direction.BACKWARD } } /** * Add this constraint to the constraint graph */ BinaryConstraint.prototype.addToGraph = function () { this.v1.addConstraint(this); this.v2.addConstraint(this); this.direction = Direction.NONE; } /** * Answer true if this constraint is satisfied in the current solution. */ BinaryConstraint.prototype.isSatisfied = function () { return this.direction != Direction.NONE; } /** * Mark the input variable with the given mark. */ BinaryConstraint.prototype.markInputs = function (mark) { this.input().mark = mark; } /** * Returns the current input variable */ BinaryConstraint.prototype.input = function () { return (this.direction == Direction.FORWARD) ? this.v1 : this.v2; } /** * Returns the current output variable */ BinaryConstraint.prototype.output = function () { return (this.direction == Direction.FORWARD) ? this.v2 : this.v1; } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this * constraint. Assume this constraint is satisfied. */ BinaryConstraint.prototype.recalculate = function () { var ihn = this.input(), out = this.output(); out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); out.stay = ihn.stay; if (out.stay) this.execute(); } /** * Record the fact that this constraint is unsatisfied. */ BinaryConstraint.prototype.markUnsatisfied = function () { this.direction = Direction.NONE; } BinaryConstraint.prototype.inputsKnown = function (mark) { var i = this.input(); return i.mark == mark || i.stay || i.determinedBy == null; } BinaryConstraint.prototype.removeFromGraph = function () { if (this.v1 != null) this.v1.removeConstraint(this); if (this.v2 != null) this.v2.removeConstraint(this); this.direction = Direction.NONE; } /* --- * * S c a l e C o n s t r a i n t * --- */ /** * Relates two variables by the linear scaling relationship: "v2 = * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain * this relationship but the scale factor and offset are considered * read-only. */ function ScaleConstraint(src, scale, offset, dest, strength) { this.direction = Direction.NONE; this.scale = scale; this.offset = offset; ScaleConstraint.superConstructor.call(this, src, dest, strength); } ScaleConstraint.inheritsFrom(BinaryConstraint); /** * Adds this constraint to the constraint graph. */ ScaleConstraint.prototype.addToGraph = function () { ScaleConstraint.superConstructor.prototype.addToGraph.call(this); this.scale.addConstraint(this); this.offset.addConstraint(this); } ScaleConstraint.prototype.removeFromGraph = function () { ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this); if (this.scale != null) this.scale.removeConstraint(this); if (this.offset != null) this.offset.removeConstraint(this); } ScaleConstraint.prototype.markInputs = function (mark) { ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark); this.scale.mark = this.offset.mark = mark; } /** * Enforce this constraint. Assume that it is satisfied. */ ScaleConstraint.prototype.execute = function () { if (this.direction == Direction.FORWARD) { this.v2.value = this.v1.value * this.scale.value + this.offset.value; } else { this.v1.value = (this.v2.value - this.offset.value) / this.scale.value; } } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this constraint. Assume * this constraint is satisfied. */ ScaleConstraint.prototype.recalculate = function () { var ihn = this.input(), out = this.output(); out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); out.stay = ihn.stay && this.scale.stay && this.offset.stay; if (out.stay) this.execute(); } /* --- * * E q u a l i t y C o n s t r a i n t * --- */ /** * Constrains two variables to have the same value. */ function EqualityConstraint(var1, var2, strength) { EqualityConstraint.superConstructor.call(this, var1, var2, strength); } EqualityConstraint.inheritsFrom(BinaryConstraint); /** * Enforce this constraint. Assume that it is satisfied. */ EqualityConstraint.prototype.execute = function () { this.output().value = this.input().value; } /* --- * * V a r i a b l e * --- */ /** * A constrained variable. In addition to its value, it maintain the * structure of the constraint graph, the current dataflow graph, and * various parameters of interest to the DeltaBlue incremental * constraint solver. **/ function Variable(name, initialValue) { this.value = initialValue || 0; this.constraints = new OrderedCollection(); this.determinedBy = null; this.mark = 0; this.walkStrength = Strength.WEAKEST; this.stay = true; this.name = name; } /** * Add the given constraint to the set of all constraints that refer * this variable. */ Variable.prototype.addConstraint = function (c) { this.constraints.add(c); } /** * Removes all traces of c from this variable. */ Variable.prototype.removeConstraint = function (c) { this.constraints.remove(c); if (this.determinedBy == c) this.determinedBy = null; } /* --- * * P l a n n e r * --- */ /** * The DeltaBlue planner */ function Planner() { this.currentMark = 0; } /** * Attempt to satisfy the given constraint and, if successful, * incrementally update the dataflow graph. Details: If satifying * the constraint is successful, it may override a weaker constraint * on its output. The algorithm attempts to resatisfy that * constraint using some other method. This process is repeated * until either a) it reaches a variable that was not previously * determined by any constraint or b) it reaches a constraint that * is too weak to be satisfied using any of its methods. The * variables of constraints that have been processed are marked with * a unique mark value so that we know where we've been. This allows * the algorithm to avoid getting into an infinite loop even if the * constraint graph has an inadvertent cycle. */ Planner.prototype.incrementalAdd = function (c) { var mark = this.newMark(); var overridden = c.satisfy(mark); while (overridden != null) overridden = overridden.satisfy(mark); } /** * Entry point for retracting a constraint. Remove the given * constraint and incrementally update the dataflow graph. * Details: Retracting the given constraint may allow some currently * unsatisfiable downstream constraint to be satisfied. We therefore collect * a list of unsatisfied downstream constraints and attempt to * satisfy each one in turn. This list is traversed by constraint * strength, strongest first, as a heuristic for avoiding * unnecessarily adding and then overriding weak constraints. * Assume: c is satisfied. */ Planner.prototype.incrementalRemove = function (c) { var out = c.output(); c.markUnsatisfied(); c.removeFromGraph(); var unsatisfied = this.removePropagateFrom(out); var strength = Strength.REQUIRED; do { for (var i = 0; i < unsatisfied.size(); i++) { var u = unsatisfied.at(i); if (u.strength == strength) this.incrementalAdd(u); } strength = strength.nextWeaker(); } while (strength != Strength.WEAKEST); } /** * Select a previously unused mark value. */ Planner.prototype.newMark = function () { return ++this.currentMark; } /** * Extract a plan for resatisfaction starting from the given source * constraints, usually a set of input constraints. This method * assumes that stay optimization is desired; the plan will contain * only constraints whose output variables are not stay. Constraints * that do no computation, such as stay and edit constraints, are * not included in the plan. * Details: The outputs of a constraint are marked when it is added * to the plan under construction. A constraint may be appended to * the plan when all its input variables are known. A variable is * known if either a) the variable is marked (indicating that has * been computed by a constraint appearing earlier in the plan), b) * the variable is 'stay' (i.e. it is a constant at plan execution * time), or c) the variable is not determined by any * constraint. The last provision is for past states of history * variables, which are not stay but which are also not computed by * any constraint. * Assume: sources are all satisfied. */ Planner.prototype.makePlan = function (sources) { var mark = this.newMark(); var plan = new Plan(); var todo = sources; while (todo.size() > 0) { var c = todo.removeFirst(); if (c.output().mark != mark && c.inputsKnown(mark)) { plan.addConstraint(c); c.output().mark = mark; this.addConstraintsConsumingTo(c.output(), todo); } } return plan; } /** * Extract a plan for resatisfying starting from the output of the * given constraints, usually a set of input constraints. */ Planner.prototype.extractPlanFromConstraints = function (constraints) { var sources = new OrderedCollection(); for (var i = 0; i < constraints.size(); i++) { var c = constraints.at(i); if (c.isInput() && c.isSatisfied()) // not in plan already and eligible for inclusion sources.add(c); } return this.makePlan(sources); } /** * Recompute the walkabout strengths and stay flags of all variables * downstream of the given constraint and recompute the actual * values of all variables whose stay flag is true. If a cycle is * detected, remove the given constraint and answer * false. Otherwise, answer true. * Details: Cycles are detected when a marked variable is * encountered downstream of the given constraint. The sender is * assumed to have marked the inputs of the given constraint with * the given mark. Thus, encountering a marked node downstream of * the output constraint means that there is a path from the * constraint's output to one of its inputs. */ Planner.prototype.addPropagate = function (c, mark) { var todo = new OrderedCollection(); todo.add(c); while (todo.size() > 0) { var d = todo.removeFirst(); if (d.output().mark == mark) { this.incrementalRemove(c); return false; } d.recalculate(); this.addConstraintsConsumingTo(d.output(), todo); } return true; } /** * Update the walkabout strengths and stay flags of all variables * downstream of the given constraint. Answer a collection of * unsatisfied constraints sorted in order of decreasing strength. */ Planner.prototype.removePropagateFrom = function (out) { out.determinedBy = null; out.walkStrength = Strength.WEAKEST; out.stay = true; var unsatisfied = new OrderedCollection(); var todo = new OrderedCollection(); todo.add(out); while (todo.size() > 0) { var v = todo.removeFirst(); for (var i = 0; i < v.constraints.size(); i++) { var c = v.constraints.at(i); if (!c.isSatisfied()) unsatisfied.add(c); } var determining = v.determinedBy; for (var i = 0; i < v.constraints.size(); i++) { var next = v.constraints.at(i); if (next != determining && next.isSatisfied()) { next.recalculate(); todo.add(next.output()); } } } return unsatisfied; } Planner.prototype.addConstraintsConsumingTo = function (v, coll) { var determining = v.determinedBy; var cc = v.constraints; for (var i = 0; i < cc.size(); i++) { var c = cc.at(i); if (c != determining && c.isSatisfied()) coll.add(c); } } /* --- * * P l a n * --- */ /** * A Plan is an ordered list of constraints to be executed in sequence * to resatisfy all currently satisfiable constraints in the face of * one or more changing inputs. */ function Plan() { this.v = new OrderedCollection(); } Plan.prototype.addConstraint = function (c) { this.v.add(c); } Plan.prototype.size = function () { return this.v.size(); } Plan.prototype.constraintAt = function (index) { return this.v.at(index); } Plan.prototype.execute = function () { for (var i = 0; i < this.size(); i++) { var c = this.constraintAt(i); c.execute(); } } /* --- * * M a i n * --- */ /** * This is the standard DeltaBlue benchmark. A long chain of equality * constraints is constructed with a stay constraint on one end. An * edit constraint is then added to the opposite end and the time is * measured for adding and removing this constraint, and extracting * and executing a constraint satisfaction plan. There are two cases. * In case 1, the added constraint is stronger than the stay * constraint and values must propagate down the entire length of the * chain. In case 2, the added constraint is weaker than the stay * constraint so it cannot be accomodated. The cost in this case is, * of course, very low. Typical situations lie somewhere between these * two extremes. */ function chainTest(n) { planner = new Planner(); var prev = null, first = null, last = null; // Build chain of n equality constraints for (var i = 0; i <= n; i++) { var name = "v" + i; var v = new Variable(name); if (prev != null) new EqualityConstraint(prev, v, Strength.REQUIRED); if (i == 0) first = v; if (i == n) last = v; prev = v; } new StayConstraint(last, Strength.STRONG_DEFAULT); var edit = new EditConstraint(first, Strength.PREFERRED); var edits = new OrderedCollection(); edits.add(edit); var plan = planner.extractPlanFromConstraints(edits); for (var i = 0; i < 100; i++) { first.value = i; plan.execute(); if (last.value != i) alert("Chain test failed."); } } /** * This test constructs a two sets of variables related to each * other by a simple linear transformation (scale and offset). The * time is measured to change a variable on either side of the * mapping and to change the scale and offset factors. */ function projectionTest(n) { planner = new Planner(); var scale = new Variable("scale", 10); var offset = new Variable("offset", 1000); var src = null, dst = null; var dests = new OrderedCollection(); for (var i = 0; i < n; i++) { src = new Variable("src" + i, i); dst = new Variable("dst" + i, i); dests.add(dst); new StayConstraint(src, Strength.NORMAL); new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED); } change(src, 17); if (dst.value != 1170) alert("Projection 1 failed"); change(dst, 1050); if (src.value != 5) alert("Projection 2 failed"); change(scale, 5); for (var i = 0; i < n - 1; i++) { if (dests.at(i).value != i * 5 + 1000) alert("Projection 3 failed"); } change(offset, 2000); for (var i = 0; i < n - 1; i++) { if (dests.at(i).value != i * 5 + 2000) alert("Projection 4 failed"); } } function change(v, newValue) { var edit = new EditConstraint(v, Strength.PREFERRED); var edits = new OrderedCollection(); edits.add(edit); var plan = planner.extractPlanFromConstraints(edits); for (var i = 0; i < 10; i++) { v.value = newValue; plan.execute(); } edit.destroyConstraint(); } // Global variable holding the current planner. var planner = null; function deltaBlue() { chainTest(100); projectionTest(100); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/0000755000175000017500000000000014433667662024324 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/base.js0000644000175000017500000002104114433667662025572 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Simple framework for running the benchmark suites and // computing a score based on the timing measurements. // A benchmark has a name (string) and a function that will be run to // do the performance measurement. The optional setup and tearDown // arguments are functions that will be invoked before and after // running the benchmark, but the running time of these functions will // not be accounted for in the benchmark score. function Benchmark(name, run, setup, tearDown) { this.name = name; this.run = run; this.Setup = setup ? setup : function() { }; this.TearDown = tearDown ? tearDown : function() { }; } // Benchmark results hold the benchmark and the measured time used to // run the benchmark. The benchmark score is computed later once a // full benchmark suite has run to completion. function BenchmarkResult(benchmark, time) { this.benchmark = benchmark; this.time = time; } // Automatically convert results to numbers. Used by the geometric // mean computation. BenchmarkResult.prototype.valueOf = function() { return this.time; } // Suites of benchmarks consist of a name and the set of benchmarks in // addition to the reference timing that the final score will be based // on. This way, all scores are relative to a reference run and higher // scores implies better performance. function BenchmarkSuite(name, reference, benchmarks) { this.name = name; this.reference = reference; this.benchmarks = benchmarks; BenchmarkSuite.suites.push(this); } // Keep track of all declared benchmark suites. BenchmarkSuite.suites = []; // Scores are not comparable across versions. Bump the version if // you're making changes that will affect that scores, e.g. if you add // a new benchmark or change an existing one. BenchmarkSuite.version = '5'; // To make the benchmark results predictable, we replace Math.random // with a 100% deterministic alternative. Math.random = (function() { var seed = 49734321; return function() { // Robert Jenkins' 32 bit integer hash function. seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff; seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff; seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff; seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff; seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff; seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff; return (seed & 0xfffffff) / 0x10000000; }; })(); // Runs all registered benchmark suites and optionally yields between // each individual benchmark to avoid running for too long in the // context of browsers. Once done, the final score is reported to the // runner. BenchmarkSuite.RunSuites = function(runner) { var continuation = null; var suites = BenchmarkSuite.suites; var length = suites.length; BenchmarkSuite.scores = []; var index = 0; function RunStep() { while (continuation || index < length) { if (continuation) { continuation = continuation(); } else { var suite = suites[index++]; if (runner.NotifyStart) runner.NotifyStart(suite.name); continuation = suite.RunStep(runner); } if (continuation && typeof window != 'undefined' && window.setTimeout) { window.setTimeout(RunStep, 25); return; } } if (runner.NotifyScore) { var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores); var formatted = BenchmarkSuite.FormatScore(100 * score); runner.NotifyScore(formatted); } } RunStep(); } // Counts the total number of registered benchmarks. Useful for // showing progress as a percentage. BenchmarkSuite.CountBenchmarks = function() { var result = 0; var suites = BenchmarkSuite.suites; for (var i = 0; i < suites.length; i++) { result += suites[i].benchmarks.length; } return result; } // Computes the geometric mean of a set of numbers. BenchmarkSuite.GeometricMean = function(numbers) { var log = 0; for (var i = 0; i < numbers.length; i++) { log += Math.log(numbers[i]); } return Math.pow(Math.E, log / numbers.length); } // Converts a score value to a string with at least three significant // digits. BenchmarkSuite.FormatScore = function(value) { if (value > 100) { return value.toFixed(0); } else { return value.toPrecision(3); } } // Notifies the runner that we're done running a single benchmark in // the benchmark suite. This can be useful to report progress. BenchmarkSuite.prototype.NotifyStep = function(result) { this.results.push(result); if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name); } // Notifies the runner that we're done with running a suite and that // we have a result which can be reported to the user if needed. BenchmarkSuite.prototype.NotifyResult = function() { var mean = BenchmarkSuite.GeometricMean(this.results); var score = this.reference / mean; BenchmarkSuite.scores.push(score); if (this.runner.NotifyResult) { var formatted = BenchmarkSuite.FormatScore(100 * score); this.runner.NotifyResult(this.name, formatted); } } // Notifies the runner that running a benchmark resulted in an error. BenchmarkSuite.prototype.NotifyError = function(error) { if (this.runner.NotifyError) { this.runner.NotifyError(this.name, error); } if (this.runner.NotifyStep) { this.runner.NotifyStep(this.name); } } // Runs a single benchmark for at least a second and computes the // average time it takes to run a single iteration. BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark) { var elapsed = 0; var start = new Date(); for (var n = 0; elapsed < 1000; n++) { benchmark.run(); elapsed = new Date() - start; } var usec = (elapsed * 1000) / n; this.NotifyStep(new BenchmarkResult(benchmark, usec)); } // This function starts running a suite, but stops between each // individual benchmark in the suite and returns a continuation // function which can be invoked to run the next benchmark. Once the // last benchmark has been executed, null is returned. BenchmarkSuite.prototype.RunStep = function(runner) { this.results = []; this.runner = runner; var length = this.benchmarks.length; var index = 0; var suite = this; // Run the setup, the actual benchmark, and the tear down in three // separate steps to allow the framework to yield between any of the // steps. function RunNextSetup() { if (index < length) { try { suite.benchmarks[index].Setup(); } catch (e) { suite.NotifyError(e); return null; } return RunNextBenchmark; } suite.NotifyResult(); return null; } function RunNextBenchmark() { try { suite.RunSingleBenchmark(suite.benchmarks[index]); } catch (e) { suite.NotifyError(e); return null; } return RunNextTearDown; } function RunNextTearDown() { try { suite.benchmarks[index++].TearDown(); } catch (e) { suite.NotifyError(e); return null; } return RunNextSetup; } // Start out running the setup. return RunNextSetup(); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/raytrace.js0000644000175000017500000007036614433667662026510 0ustar apoapo// The ray tracer code in this file is written by Adam Burmister. It // is available in its original form from: // // http://labs.flog.nz.co/raytracer/ // // It has been modified slightly by Google to work as a standalone // benchmark, but the all the computational code remains // untouched. This file also contains a copy of parts of the Prototype // JavaScript framework which is used by the ray tracer. var RayTrace = new BenchmarkSuite('RayTrace', 932666, [ new Benchmark('RayTrace', renderScene) ]); // Variable used to hold a number that can be used to verify that // the scene was ray traced correctly. var checkNumber; // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // The following is a copy of parts of the Prototype JavaScript library: // Prototype JavaScript framework, version 1.5.0 // (c) 2005-2007 Sam Stephenson // // Prototype is freely distributable under the terms of an MIT-style license. // For details, see the Prototype web site: http://prototype.conio.net/ var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } }; Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; }; // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // The rest of this file is the actual ray tracer written by Adam // Burmister. It's a concatenation of the following files: // // flog/color.js // flog/light.js // flog/vector.js // flog/ray.js // flog/scene.js // flog/material/basematerial.js // flog/material/solid.js // flog/material/chessboard.js // flog/shape/baseshape.js // flog/shape/sphere.js // flog/shape/plane.js // flog/intersectioninfo.js // flog/camera.js // flog/background.js // flog/engine.js /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Color = Class.create(); Flog.RayTracer.Color.prototype = { red : 0.0, green : 0.0, blue : 0.0, initialize : function(r, g, b) { if(!r) r = 0.0; if(!g) g = 0.0; if(!b) b = 0.0; this.red = r; this.green = g; this.blue = b; }, add : function(c1, c2){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red + c2.red; result.green = c1.green + c2.green; result.blue = c1.blue + c2.blue; return result; }, addScalar: function(c1, s){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red + s; result.green = c1.green + s; result.blue = c1.blue + s; result.limit(); return result; }, subtract: function(c1, c2){ var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red - c2.red; result.green = c1.green - c2.green; result.blue = c1.blue - c2.blue; return result; }, multiply : function(c1, c2) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red * c2.red; result.green = c1.green * c2.green; result.blue = c1.blue * c2.blue; return result; }, multiplyScalar : function(c1, f) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red * f; result.green = c1.green * f; result.blue = c1.blue * f; return result; }, divideFactor : function(c1, f) { var result = new Flog.RayTracer.Color(0,0,0); result.red = c1.red / f; result.green = c1.green / f; result.blue = c1.blue / f; return result; }, limit: function(){ this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0; this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0; this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0; }, distance : function(color) { var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue); return d; }, blend: function(c1, c2, w){ var result = new Flog.RayTracer.Color(0,0,0); result = Flog.RayTracer.Color.prototype.add( Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w), Flog.RayTracer.Color.prototype.multiplyScalar(c2, w) ); return result; }, brightness : function() { var r = Math.floor(this.red*255); var g = Math.floor(this.green*255); var b = Math.floor(this.blue*255); return (r * 77 + g * 150 + b * 29) >> 8; }, toString : function () { var r = Math.floor(this.red*255); var g = Math.floor(this.green*255); var b = Math.floor(this.blue*255); return "rgb("+ r +","+ g +","+ b +")"; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Light = Class.create(); Flog.RayTracer.Light.prototype = { position: null, color: null, intensity: 10.0, initialize : function(pos, color, intensity) { this.position = pos; this.color = color; this.intensity = (intensity ? intensity : 10.0); }, getIntensity: function(distance){ if(distance >= intensity) return 0; return Math.pow((intensity - distance) / strength, 0.2); }, toString : function () { return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Vector = Class.create(); Flog.RayTracer.Vector.prototype = { x : 0.0, y : 0.0, z : 0.0, initialize : function(x, y, z) { this.x = (x ? x : 0); this.y = (y ? y : 0); this.z = (z ? z : 0); }, copy: function(vector){ this.x = vector.x; this.y = vector.y; this.z = vector.z; }, normalize : function() { var m = this.magnitude(); return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m); }, magnitude : function() { return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z)); }, cross : function(w) { return new Flog.RayTracer.Vector( -this.z * w.y + this.y * w.z, this.z * w.x - this.x * w.z, -this.y * w.x + this.x * w.y); }, dot : function(w) { return this.x * w.x + this.y * w.y + this.z * w.z; }, add : function(v, w) { return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z); }, subtract : function(v, w) { if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']'; return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z); }, multiplyVector : function(v, w) { return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z); }, multiplyScalar : function(v, w) { return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w); }, toString : function () { return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Ray = Class.create(); Flog.RayTracer.Ray.prototype = { position : null, direction : null, initialize : function(pos, dir) { this.position = pos; this.direction = dir; }, toString : function () { return 'Ray [' + this.position + ',' + this.direction + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Scene = Class.create(); Flog.RayTracer.Scene.prototype = { camera : null, shapes : [], lights : [], background : null, initialize : function() { this.camera = new Flog.RayTracer.Camera( new Flog.RayTracer.Vector(0,0,-5), new Flog.RayTracer.Vector(0,0,1), new Flog.RayTracer.Vector(0,1,0) ); this.shapes = new Array(); this.lights = new Array(); this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2); } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {}; Flog.RayTracer.Material.BaseMaterial = Class.create(); Flog.RayTracer.Material.BaseMaterial.prototype = { gloss: 2.0, // [0...infinity] 0 = matt transparency: 0.0, // 0=opaque reflection: 0.0, // [0...infinity] 0 = no reflection refraction: 0.50, hasTexture: false, initialize : function() { }, getColor: function(u, v){ }, wrapUp: function(t){ t = t % 2.0; if(t < -1) t += 2.0; if(t >= 1) t -= 2.0; return t; }, toString : function () { return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Material.Solid = Class.create(); Flog.RayTracer.Material.Solid.prototype = Object.extend( new Flog.RayTracer.Material.BaseMaterial(), { initialize : function(color, reflection, refraction, transparency, gloss) { this.color = color; this.reflection = reflection; this.transparency = transparency; this.gloss = gloss; this.hasTexture = false; }, getColor: function(u, v){ return this.color; }, toString : function () { return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } ); /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Material.Chessboard = Class.create(); Flog.RayTracer.Material.Chessboard.prototype = Object.extend( new Flog.RayTracer.Material.BaseMaterial(), { colorEven: null, colorOdd: null, density: 0.5, initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) { this.colorEven = colorEven; this.colorOdd = colorOdd; this.reflection = reflection; this.transparency = transparency; this.gloss = gloss; this.density = density; this.hasTexture = true; }, getColor: function(u, v){ var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density); if(t < 0.0) return this.colorEven; else return this.colorOdd; }, toString : function () { return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } ); /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; Flog.RayTracer.Shape.BaseShape = Class.create(); Flog.RayTracer.Shape.BaseShape.prototype = { position: null, material: null, initialize : function() { this.position = new Vector(0,0,0); this.material = new Flog.RayTracer.Material.SolidMaterial( new Flog.RayTracer.Color(1,0,1), 0, 0, 0 ); }, toString : function () { return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; Flog.RayTracer.Shape.Sphere = Class.create(); Flog.RayTracer.Shape.Sphere.prototype = { initialize : function(pos, radius, material) { this.radius = radius; this.position = pos; this.material = material; }, intersect: function(ray){ var info = new Flog.RayTracer.IntersectionInfo(); info.shape = this; var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position); var B = dst.dot(ray.direction); var C = dst.dot(dst) - (this.radius * this.radius); var D = (B * B) - C; if(D > 0){ // intersection! info.isHit = true; info.distance = (-B) - Math.sqrt(D); info.position = Flog.RayTracer.Vector.prototype.add( ray.position, Flog.RayTracer.Vector.prototype.multiplyScalar( ray.direction, info.distance ) ); info.normal = Flog.RayTracer.Vector.prototype.subtract( info.position, this.position ).normalize(); info.color = this.material.getColor(0,0); } else { info.isHit = false; } return info; }, toString : function () { return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {}; Flog.RayTracer.Shape.Plane = Class.create(); Flog.RayTracer.Shape.Plane.prototype = { d: 0.0, initialize : function(pos, d, material) { this.position = pos; this.d = d; this.material = material; }, intersect: function(ray){ var info = new Flog.RayTracer.IntersectionInfo(); var Vd = this.position.dot(ray.direction); if(Vd == 0) return info; // no intersection var t = -(this.position.dot(ray.position) + this.d) / Vd; if(t <= 0) return info; info.shape = this; info.isHit = true; info.position = Flog.RayTracer.Vector.prototype.add( ray.position, Flog.RayTracer.Vector.prototype.multiplyScalar( ray.direction, t ) ); info.normal = this.position; info.distance = t; if(this.material.hasTexture){ var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x); var vV = vU.cross(this.position); var u = info.position.dot(vU); var v = info.position.dot(vV); info.color = this.material.getColor(u,v); } else { info.color = this.material.getColor(0,0); } return info; }, toString : function () { return 'Plane [' + this.position + ', d=' + this.d + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.IntersectionInfo = Class.create(); Flog.RayTracer.IntersectionInfo.prototype = { isHit: false, hitCount: 0, shape: null, position: null, normal: null, color: null, distance: null, initialize : function() { this.color = new Flog.RayTracer.Color(0,0,0); }, toString : function () { return 'Intersection [' + this.position + ']'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Camera = Class.create(); Flog.RayTracer.Camera.prototype = { position: null, lookAt: null, equator: null, up: null, screen: null, initialize : function(pos, lookAt, up) { this.position = pos; this.lookAt = lookAt; this.up = up; this.equator = lookAt.normalize().cross(this.up); this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt); }, getRay: function(vx, vy){ var pos = Flog.RayTracer.Vector.prototype.subtract( this.screen, Flog.RayTracer.Vector.prototype.subtract( Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx), Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy) ) ); pos.y = pos.y * -1; var dir = Flog.RayTracer.Vector.prototype.subtract( pos, this.position ); var ray = new Flog.RayTracer.Ray(pos, dir.normalize()); return ray; }, toString : function () { return 'Ray []'; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Background = Class.create(); Flog.RayTracer.Background.prototype = { color : null, ambience : 0.0, initialize : function(color, ambience) { this.color = color; this.ambience = ambience; } } /* Fake a Flog.* namespace */ if(typeof(Flog) == 'undefined') var Flog = {}; if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {}; Flog.RayTracer.Engine = Class.create(); Flog.RayTracer.Engine.prototype = { canvas: null, /* 2d context we can render to */ initialize: function(options){ this.options = Object.extend({ canvasHeight: 100, canvasWidth: 100, pixelWidth: 2, pixelHeight: 2, renderDiffuse: false, renderShadows: false, renderHighlights: false, renderReflections: false, rayDepth: 2 }, options || {}); this.options.canvasHeight /= this.options.pixelHeight; this.options.canvasWidth /= this.options.pixelWidth; /* TODO: dynamically include other scripts */ }, setPixel: function(x, y, color){ var pxW, pxH; pxW = this.options.pixelWidth; pxH = this.options.pixelHeight; if (this.canvas) { this.canvas.fillStyle = color.toString(); this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH); } else { if (x === y) { checkNumber += color.brightness(); } // print(x * pxW, y * pxH, pxW, pxH); } }, renderScene: function(scene, canvas){ checkNumber = 0; /* Get canvas */ if (canvas) { this.canvas = canvas.getContext("2d"); } else { this.canvas = null; } var canvasHeight = this.options.canvasHeight; var canvasWidth = this.options.canvasWidth; for(var y=0; y < canvasHeight; y++){ for(var x=0; x < canvasWidth; x++){ var yp = y * 1.0 / canvasHeight * 2 - 1; var xp = x * 1.0 / canvasWidth * 2 - 1; var ray = scene.camera.getRay(xp, yp); var color = this.getPixelColor(ray, scene); this.setPixel(x, y, color); } } if (checkNumber !== 2321) { throw new Error("Scene rendered incorrectly"); } }, getPixelColor: function(ray, scene){ var info = this.testIntersection(ray, scene, null); if(info.isHit){ var color = this.rayTrace(info, ray, scene, 0); return color; } return scene.background.color; }, testIntersection: function(ray, scene, exclude){ var hits = 0; var best = new Flog.RayTracer.IntersectionInfo(); best.distance = 2000; for(var i=0; i= 0 && info.distance < best.distance){ best = info; hits++; } } } best.hitCount = hits; return best; }, getReflectionRay: function(P,N,V){ var c1 = -N.dot(V); var R1 = Flog.RayTracer.Vector.prototype.add( Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1), V ); return new Flog.RayTracer.Ray(P, R1); }, rayTrace: function(info, ray, scene, depth){ // Calc ambient var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience); var oldColor = color; var shininess = Math.pow(10, info.shape.material.gloss + 1); for(var i=0; i 0.0){ color = Flog.RayTracer.Color.prototype.add( color, Flog.RayTracer.Color.prototype.multiply( info.color, Flog.RayTracer.Color.prototype.multiplyScalar( light.color, L ) ) ); } } // The greater the depth the more accurate the colours, but // this is exponentially (!) expensive if(depth <= this.options.rayDepth){ // calculate reflection ray if(this.options.renderReflections && info.shape.material.reflection > 0) { var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction); var refl = this.testIntersection(reflectionRay, scene, info.shape); if (refl.isHit && refl.distance > 0){ refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1); } else { refl.color = scene.background.color; } color = Flog.RayTracer.Color.prototype.blend( color, refl.color, info.shape.material.reflection ); } // Refraction /* TODO */ } /* Render shadows and highlights */ var shadowInfo = new Flog.RayTracer.IntersectionInfo(); if(this.options.renderShadows){ var shadowRay = new Flog.RayTracer.Ray(info.position, v); shadowInfo = this.testIntersection(shadowRay, scene, info.shape); if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){ var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5); var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5)); color = Flog.RayTracer.Color.prototype.addScalar(vA,dB); } } // Phong specular highlights if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){ var Lv = Flog.RayTracer.Vector.prototype.subtract( info.shape.position, light.position ).normalize(); var E = Flog.RayTracer.Vector.prototype.subtract( scene.camera.position, info.shape.position ).normalize(); var H = Flog.RayTracer.Vector.prototype.subtract( E, Lv ).normalize(); var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess); color = Flog.RayTracer.Color.prototype.add( Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight), color ); } } color.limit(); return color; } }; function renderScene(){ var scene = new Flog.RayTracer.Scene(); scene.camera = new Flog.RayTracer.Camera( new Flog.RayTracer.Vector(0, 0, -15), new Flog.RayTracer.Vector(-0.2, 0, 5), new Flog.RayTracer.Vector(0, 1, 0) ); scene.background = new Flog.RayTracer.Background( new Flog.RayTracer.Color(0.5, 0.5, 0.5), 0.4 ); var sphere = new Flog.RayTracer.Shape.Sphere( new Flog.RayTracer.Vector(-1.5, 1.5, 2), 1.5, new Flog.RayTracer.Material.Solid( new Flog.RayTracer.Color(0,0.5,0.5), 0.3, 0.0, 0.0, 2.0 ) ); var sphere1 = new Flog.RayTracer.Shape.Sphere( new Flog.RayTracer.Vector(1, 0.25, 1), 0.5, new Flog.RayTracer.Material.Solid( new Flog.RayTracer.Color(0.9,0.9,0.9), 0.1, 0.0, 0.0, 1.5 ) ); var plane = new Flog.RayTracer.Shape.Plane( new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(), 1.2, new Flog.RayTracer.Material.Chessboard( new Flog.RayTracer.Color(1,1,1), new Flog.RayTracer.Color(0,0,0), 0.2, 0.0, 1.0, 0.7 ) ); scene.shapes.push(plane); scene.shapes.push(sphere); scene.shapes.push(sphere1); var light = new Flog.RayTracer.Light( new Flog.RayTracer.Vector(5, 10, -1), new Flog.RayTracer.Color(0.8, 0.8, 0.8) ); var light1 = new Flog.RayTracer.Light( new Flog.RayTracer.Vector(-3, 5, -15), new Flog.RayTracer.Color(0.8, 0.8, 0.8), 100 ); scene.lights.push(light); scene.lights.push(light1); var imageWidth = 100; // $F('imageWidth'); var imageHeight = 100; // $F('imageHeight'); var pixelSize = "5,5".split(','); // $F('pixelSize').split(','); var renderDiffuse = true; // $F('renderDiffuse'); var renderShadows = true; // $F('renderShadows'); var renderHighlights = true; // $F('renderHighlights'); var renderReflections = true; // $F('renderReflections'); var rayDepth = 2;//$F('rayDepth'); var raytracer = new Flog.RayTracer.Engine( { canvasWidth: imageWidth, canvasHeight: imageHeight, pixelWidth: pixelSize[0], pixelHeight: pixelSize[1], "renderDiffuse": renderDiffuse, "renderHighlights": renderHighlights, "renderShadows": renderShadows, "renderReflections": renderReflections, "rayDepth": rayDepth } ); raytracer.renderScene(scene, null, 0); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/regexp.js0000644000175000017500000031612514433667662026164 0ustar apoapo// Copyright 2009 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Automatically generated on 2009-01-30. // This benchmark is generated by loading 50 of the most popular pages // on the web and logging all regexp operations performed. Each // operation is given a weight that is calculated from an estimate of // the popularity of the pages where it occurs and the number of times // it is executed while loading each page. Finally the literal // letters in the data are encoded using ROT13 in a way that does not // affect how the regexps match their input. var RegRxp = new BenchmarkSuite('RegExp', 995230, [ new Benchmark("RegExp", runRegExpBenchmark) ]); function runRegExpBenchmark() { var re0 = /^ba/; var re1 = /(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/; var re2 = /^\s*|\s*$/g; var re3 = /\bQBZPbageby_cynprubyqre\b/; var re4 = /,/; var re5 = /\bQBZPbageby_cynprubyqre\b/g; var re6 = /^[\s\xa0]+|[\s\xa0]+$/g; var re7 = /(\d*)(\D*)/g; var re8 = /=/; var re9 = /(^|\s)lhv\-h(\s|$)/; var str0 = 'Zbmvyyn/5.0 (Jvaqbjf; H; Jvaqbjf AG 5.1; ra-HF) NccyrJroXvg/528.9 (XUGZY, yvxr Trpxb) Puebzr/2.0.157.0 Fnsnev/528.9'; var re10 = /\#/g; var re11 = /\./g; var re12 = /'/g; var re13 = /\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i; var str1 = 'Fubpxjnir Synfu 9.0 e115'; var re14 = /\s+/g; var re15 = /^\s*(\S*(\s+\S+)*)\s*$/; var re16 = /(-[a-z])/i; function runBlock0() { for (var i = 0; i < 6511; i++) { re0.exec('pyvpx'); } for (var i = 0; i < 1844; i++) { re1.exec('uggc://jjj.snprobbx.pbz/ybtva.cuc'); } for (var i = 0; i < 739; i++) { 'QBZPbageby_cynprubyqre'.replace(re2, ''); } for (var i = 0; i < 598; i++) { re1.exec('uggc://jjj.snprobbx.pbz/'); } for (var i = 0; i < 454; i++) { re1.exec('uggc://jjj.snprobbx.pbz/fepu.cuc'); } for (var i = 0; i < 352; i++) { /qqqq|qqq|qq|q|ZZZZ|ZZZ|ZZ|Z|llll|ll|l|uu|u|UU|U|zz|z|ff|f|gg|g|sss|ss|s|mmm|mm|m/g.exec('qqqq, ZZZ q, llll'); } for (var i = 0; i < 312; i++) { re3.exec('vachggrkg QBZPbageby_cynprubyqre'); } for (var i = 0; i < 282; i++) { re4.exec('/ZlFcnprUbzrcntr/Vaqrk-FvgrUbzr,10000000'); } for (var i = 0; i < 177; i++) { 'vachggrkg'.replace(re5, ''); } for (var i = 0; i < 170; i++) { '528.9'.replace(re6, ''); re7.exec('528'); } for (var i = 0; i < 156; i++) { re8.exec('VCPhygher=ra-HF'); re8.exec('CersreerqPhygher=ra-HF'); } for (var i = 0; i < 144; i++) { re0.exec('xrlcerff'); } for (var i = 0; i < 139; i++) { '521'.replace(re6, ''); re7.exec('521'); re9.exec(''); /JroXvg\/(\S+)/.exec(str0); } for (var i = 0; i < 137; i++) { 'qvi .so_zrah'.replace(re10, ''); 'qvi .so_zrah'.replace(/\[/g, ''); 'qvi.so_zrah'.replace(re11, ''); } for (var i = 0; i < 117; i++) { 'uvqqra_ryrz'.replace(re2, ''); } for (var i = 0; i < 95; i++) { /(?:^|;)\s*sevraqfgre_ynat=([^;]*)/.exec('sevraqfgre_naba=nvq%3Qn6ss9p85n868ro9s059pn854735956o3%26ers%3Q%26df%3Q%26vpgl%3QHF'); } for (var i = 0; i < 93; i++) { 'uggc://ubzr.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); re13.exec('uggc://ubzr.zlfcnpr.pbz/vaqrk.psz'); } for (var i = 0; i < 92; i++) { str1.replace(/([a-zA-Z]|\s)+/, ''); } for (var i = 0; i < 85; i++) { 'svefg'.replace(re14, ''); 'svefg'.replace(re15, ''); 'uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); 'ynfg'.replace(re14, ''); 'ynfg'.replace(re15, ''); re16.exec('qvfcynl'); re13.exec('uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz'); } } var re17 = /(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g; var str2 = '{"anzr":"","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":gehr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"\xa4","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":gehr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, qq ZZZZ llll UU:zz:ff","YbatQngrCnggrea":"qqqq, qq ZZZZ llll","YbatGvzrCnggrea":"UU:zz:ff","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"ZZ/qq/llll","FubegGvzrCnggrea":"UU:zz","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"llll ZZZZ","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":gehr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; var str3 = '{"anzr":"ra-HF","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":snyfr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"$","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":snyfr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, ZZZZ qq, llll u:zz:ff gg","YbatQngrCnggrea":"qqqq, ZZZZ qq, llll","YbatGvzrCnggrea":"u:zz:ff gg","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"Z/q/llll","FubegGvzrCnggrea":"u:zz gg","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"ZZZZ, llll","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":snyfr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}'; var str4 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str5 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var re18 = /^\s+|\s+$/g; var str6 = 'uggc://jjj.snprobbx.pbz/vaqrk.cuc'; var re19 = /(?:^|\s+)ba(?:\s+|$)/; var re20 = /[+, ]/; var re21 = /ybnqrq|pbzcyrgr/; var str7 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(d1)c=d1.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+d1.Cnaryf[c].Jvqgu,o=g+d1.Cnaryf[c].Urvtug;vs((pheKe)||(pheLo)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(d1&&d1.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(d1)d1.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(d1)d1.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;d1.IjTc=d2(n,c){ine nq=d1;vs(vfAnA(c)){sbe(ine v=0;v0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;d1.IjYvzvg0=d2(n,f){ine nq=d1,vh=f.fcyvg("/");sbe(ine v=0;v0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;d1.IjRVST=d2(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",d1.rvsg);};;d1.IjNavzSHC=d2(n,c){ine nq=d1;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ 0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;d1.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;d1.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c]/g; var str15 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=44132r503660'; var str16 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; AFP_zp_dfctwzs-aowb_80=44132r503660; __hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1; __hgzo=144631658.0.10.1231363638; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str17 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363621014&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363621014&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=348699119.1231363624&tn_fvq=1231363624&tn_uvq=895511034&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str18 = 'uggc://jjj.yrobapbva.se/yv'; var str19 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str20 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; function runBlock3() { for (var i = 0; i < 27; i++) { 'e115'.replace(/[A-Za-z]/g, ''); } for (var i = 0; i < 23; i++) { 'qvfcynl'.replace(re27, ''); 'cbfvgvba'.replace(re27, ''); } for (var i = 0; i < 22; i++) { 'unaqyr'.replace(re14, ''); 'unaqyr'.replace(re15, ''); 'yvar'.replace(re14, ''); 'yvar'.replace(re15, ''); 'cnerag puebzr6 fvatyr1 gno'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno'.replace(re15, ''); 'fyvqre'.replace(re14, ''); 'fyvqre'.replace(re15, ''); re28.exec(''); } for (var i = 0; i < 21; i++) { 'uggc://jjj.zlfcnpr.pbz/'.replace(re12, ''); re13.exec('uggc://jjj.zlfcnpr.pbz/'); } for (var i = 0; i < 20; i++) { 'cntrivrj'.replace(re29, ''); 'cntrivrj'.replace(re30, ''); re19.exec('ynfg'); re19.exec('ba svefg'); re8.exec('VC=74.125.75.3'); } for (var i = 0; i < 19; i++) { re31.exec('ra'); } for (var i = 0; i < 18; i++) { str10.split(re32); str11.split(re32); str12.replace(re33, ''); re8.exec('144631658.0.10.1231363570'); re8.exec('144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); re8.exec(str13); re8.exec(str14); re8.exec('__hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1'); re8.exec('__hgzo=144631658.0.10.1231363570'); re8.exec('__hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str10); re34.exec(str11); } for (var i = 0; i < 17; i++) { str0.match(/zfvr/gi); str0.match(/bcren/gi); str15.split(re32); str16.split(re32); 'ohggba'.replace(re14, ''); 'ohggba'.replace(re15, ''); 'puvyq p1 svefg sylbhg pybfrq'.replace(re14, ''); 'puvyq p1 svefg sylbhg pybfrq'.replace(re15, ''); 'pvgvrf'.replace(re14, ''); 'pvgvrf'.replace(re15, ''); 'pybfrq'.replace(re14, ''); 'pybfrq'.replace(re15, ''); 'qry'.replace(re14, ''); 'qry'.replace(re15, ''); 'uqy_zba'.replace(re14, ''); 'uqy_zba'.replace(re15, ''); str17.replace(re33, ''); str18.replace(/%3P/g, ''); str18.replace(/%3R/g, ''); str18.replace(/%3q/g, ''); str18.replace(re35, ''); 'yvaxyvfg16'.replace(re14, ''); 'yvaxyvfg16'.replace(re15, ''); 'zvahf'.replace(re14, ''); 'zvahf'.replace(re15, ''); 'bcra'.replace(re14, ''); 'bcra'.replace(re15, ''); 'cnerag puebzr5 fvatyr1 ps NU'.replace(re14, ''); 'cnerag puebzr5 fvatyr1 ps NU'.replace(re15, ''); 'cynlre'.replace(re14, ''); 'cynlre'.replace(re15, ''); 'cyhf'.replace(re14, ''); 'cyhf'.replace(re15, ''); 'cb_uqy'.replace(re14, ''); 'cb_uqy'.replace(re15, ''); 'hyJVzt'.replace(re14, ''); 'hyJVzt'.replace(re15, ''); re8.exec('144631658.0.10.1231363638'); re8.exec('144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.965867047679498800.1231363638.1231363638.1231363638.1'); re8.exec('4413268q3660'); re8.exec('4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); re8.exec('SbeprqRkcvengvba=633669321699093060'); re8.exec('VC=74.125.75.20'); re8.exec(str19); re8.exec(str20); re8.exec('AFP_zp_tfwsbrg-aowb_80=4413268q3660'); re8.exec('FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n'); re8.exec('__hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1'); re8.exec('__hgzo=144631658.0.10.1231363638'); re8.exec('__hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str15); re34.exec(str16); } } var re36 = /uers|fep|fryrpgrq/; var re37 = /\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g; var re38 = /^(\w+|\*)$/; var str21 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str22 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; __hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1; __hgzo=144631658.0.10.1231367822; __hgzp=144631658; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str23 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367803797&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367803797&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Szrffntvat.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1192552091.1231367807&tn_fvq=1231367807&tn_uvq=1155446857&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str24 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str25 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str26 = 'hy.ynat-fryrpgbe'; var re39 = /\\/g; var re40 = / /g; var re41 = /\/\xc4\/t/; var re42 = /\/\xd6\/t/; var re43 = /\/\xdc\/t/; var re44 = /\/\xdf\/t/; var re45 = /\/\xe4\/t/; var re46 = /\/\xf6\/t/; var re47 = /\/\xfc\/t/; var re48 = /\W/g; var re49 = /uers|fep|fglyr/; function runBlock4() { for (var i = 0; i < 16; i++) { ''.replace(/\*/g, ''); /\bnpgvir\b/.exec('npgvir'); /sversbk/i.exec(str0); re36.exec('glcr'); /zfvr/i.exec(str0); /bcren/i.exec(str0); } for (var i = 0; i < 15; i++) { str21.split(re32); str22.split(re32); 'uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); str23.replace(re33, ''); 'yv'.replace(re37, ''); 'yv'.replace(re18, ''); re8.exec('144631658.0.10.1231367822'); re8.exec('144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); re8.exec(str24); re8.exec(str25); re8.exec('__hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1'); re8.exec('__hgzo=144631658.0.10.1231367822'); re8.exec('__hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str21); re34.exec(str22); /\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g.exec(str26); re13.exec('uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'); re38.exec('yv'); } for (var i = 0; i < 14; i++) { ''.replace(re18, ''); '9.0 e115'.replace(/(\s+e|\s+o[0-9]+)/, ''); 'Funer guvf tnqtrg'.replace(//g, ''); 'Funer guvf tnqtrg'.replace(re39, ''); 'uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'.replace(re12, ''); 'grnfre'.replace(re40, ''); 'grnfre'.replace(re41, ''); 'grnfre'.replace(re42, ''); 'grnfre'.replace(re43, ''); 'grnfre'.replace(re44, ''); 'grnfre'.replace(re45, ''); 'grnfre'.replace(re46, ''); 'grnfre'.replace(re47, ''); 'grnfre'.replace(re48, ''); re16.exec('znetva-gbc'); re16.exec('cbfvgvba'); re19.exec('gno1'); re9.exec('qz'); re9.exec('qg'); re9.exec('zbqobk'); re9.exec('zbqobkva'); re9.exec('zbqgvgyr'); re13.exec('uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'); re26.exec('/vt/znvytnqtrg'); re49.exec('glcr'); } } var re50 = /(?:^|\s+)fryrpgrq(?:\s+|$)/; var re51 = /\&/g; var re52 = /\+/g; var re53 = /\?/g; var re54 = /\t/g; var re55 = /(\$\{nqiHey\})|(\$nqiHey\b)/g; var re56 = /(\$\{cngu\})|(\$cngu\b)/g; function runBlock5() { for (var i = 0; i < 13; i++) { 'purpx'.replace(re14, ''); 'purpx'.replace(re15, ''); 'pvgl'.replace(re14, ''); 'pvgl'.replace(re15, ''); 'qrpe fyvqrgrkg'.replace(re14, ''); 'qrpe fyvqrgrkg'.replace(re15, ''); 'svefg fryrpgrq'.replace(re14, ''); 'svefg fryrpgrq'.replace(re15, ''); 'uqy_rag'.replace(re14, ''); 'uqy_rag'.replace(re15, ''); 'vape fyvqrgrkg'.replace(re14, ''); 'vape fyvqrgrkg'.replace(re15, ''); 'vachggrkg QBZPbageby_cynprubyqre'.replace(re5, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re15, ''); 'cb_guz'.replace(re14, ''); 'cb_guz'.replace(re15, ''); 'fhozvg'.replace(re14, ''); 'fhozvg'.replace(re15, ''); re50.exec(''); /NccyrJroXvg\/([^\s]*)/.exec(str0); /XUGZY/.exec(str0); } for (var i = 0; i < 12; i++) { '${cebg}://${ubfg}${cngu}/${dz}'.replace(/(\$\{cebg\})|(\$cebg\b)/g, ''); '1'.replace(re40, ''); '1'.replace(re10, ''); '1'.replace(re51, ''); '1'.replace(re52, ''); '1'.replace(re53, ''); '1'.replace(re39, ''); '1'.replace(re54, ''); '9.0 e115'.replace(/^(.*)\..*$/, ''); '9.0 e115'.replace(/^.*e(.*)$/, ''); ''.replace(re55, ''); ''.replace(re55, ''); str1.replace(/^.*\s+(\S+\s+\S+$)/, ''); 'tzk%2Subzrcntr%2Sfgneg%2Sqr%2S'.replace(re30, ''); 'tzk'.replace(re30, ''); 'uggc://${ubfg}${cngu}/${dz}'.replace(/(\$\{ubfg\})|(\$ubfg\b)/g, ''); 'uggc://nqpyvrag.hvzfrei.arg${cngu}/${dz}'.replace(re56, ''); 'uggc://nqpyvrag.hvzfrei.arg/wf.at/${dz}'.replace(/(\$\{dz\})|(\$dz\b)/g, ''); 'frpgvba'.replace(re29, ''); 'frpgvba'.replace(re30, ''); 'fvgr'.replace(re29, ''); 'fvgr'.replace(re30, ''); 'fcrpvny'.replace(re29, ''); 'fcrpvny'.replace(re30, ''); re36.exec('anzr'); /e/.exec('9.0 e115'); } } var re57 = /##yv4##/gi; var re58 = /##yv16##/gi; var re59 = /##yv19##/gi; var str27 = '##yv4##Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str28 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str29 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str30 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##'; var str31 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. ##N##Yrnea zber##/N##'; var str32 = 'Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.Ybgf bs fgbentr (5 TO) - zber pbby fghss ba gur jnl. Yrnea zber##/N##'; var str33 = 'Bar Jvaqbjf Yvir VQ trgf lbh vagb Ubgznvy, Zrffratre, Kobk YVIR \u2014 naq bgure cynprf lbh frr #~#argjbexybtb#~#'; var re60 = /(?:^|\s+)bss(?:\s+|$)/; var re61 = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/; var re62 = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; var str34 = '${1}://${2}${3}${4}${5}'; var str35 = ' O=6gnyg0g4znrrn&o=3&f=gc; Q=_lyu=K3bQZGSxnT4lZzD3OS9GNmV3ZGLkAQxRpTyxNmRlZmRmAmNkAQLRqTImqNZjOUEgpTjQnJ5xMKtgoN--; SCF=qy'; function runBlock6() { for (var i = 0; i < 11; i++) { str27.replace(/##yv0##/gi, ''); str27.replace(re57, ''); str28.replace(re58, ''); str29.replace(re59, ''); str30.replace(/##\/o##/gi, ''); str30.replace(/##\/v##/gi, ''); str30.replace(/##\/h##/gi, ''); str30.replace(/##o##/gi, ''); str30.replace(/##oe##/gi, ''); str30.replace(/##v##/gi, ''); str30.replace(/##h##/gi, ''); str31.replace(/##n##/gi, ''); str32.replace(/##\/n##/gi, ''); str33.replace(/#~#argjbexybtb#~#/g, ''); / Zbovyr\//.exec(str0); /##yv1##/gi.exec(str27); /##yv10##/gi.exec(str28); /##yv11##/gi.exec(str28); /##yv12##/gi.exec(str28); /##yv13##/gi.exec(str28); /##yv14##/gi.exec(str28); /##yv15##/gi.exec(str28); re58.exec(str28); /##yv17##/gi.exec(str29); /##yv18##/gi.exec(str29); re59.exec(str29); /##yv2##/gi.exec(str27); /##yv20##/gi.exec(str30); /##yv21##/gi.exec(str30); /##yv22##/gi.exec(str30); /##yv23##/gi.exec(str30); /##yv3##/gi.exec(str27); re57.exec(str27); /##yv5##/gi.exec(str28); /##yv6##/gi.exec(str28); /##yv7##/gi.exec(str28); /##yv8##/gi.exec(str28); /##yv9##/gi.exec(str28); re8.exec('473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); re8.exec('SbeprqRkcvengvba=633669325184628362'); re8.exec('FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29'); /AbxvnA[^\/]*/.exec(str0); } for (var i = 0; i < 10; i++) { ' bss'.replace(/(?:^|\s+)bss(?:\s+|$)/g, ''); str34.replace(/(\$\{0\})|(\$0\b)/g, ''); str34.replace(/(\$\{1\})|(\$1\b)/g, ''); str34.replace(/(\$\{pbzcyrgr\})|(\$pbzcyrgr\b)/g, ''); str34.replace(/(\$\{sentzrag\})|(\$sentzrag\b)/g, ''); str34.replace(/(\$\{ubfgcbeg\})|(\$ubfgcbeg\b)/g, ''); str34.replace(re56, ''); str34.replace(/(\$\{cebgbpby\})|(\$cebgbpby\b)/g, ''); str34.replace(/(\$\{dhrel\})|(\$dhrel\b)/g, ''); 'nqfvmr'.replace(re29, ''); 'nqfvmr'.replace(re30, ''); 'uggc://${2}${3}${4}${5}'.replace(/(\$\{2\})|(\$2\b)/g, ''); 'uggc://wf.hv-cbegny.qr${3}${4}${5}'.replace(/(\$\{3\})|(\$3\b)/g, ''); 'arjf'.replace(re40, ''); 'arjf'.replace(re41, ''); 'arjf'.replace(re42, ''); 'arjf'.replace(re43, ''); 'arjf'.replace(re44, ''); 'arjf'.replace(re45, ''); 'arjf'.replace(re46, ''); 'arjf'.replace(re47, ''); 'arjf'.replace(re48, ''); / PC=i=(\d+)&oe=(.)/.exec(str35); re60.exec(' '); re60.exec(' bss'); re60.exec(''); re19.exec(' '); re19.exec('svefg ba'); re19.exec('ynfg vtaber'); re19.exec('ba'); re9.exec('scnq so '); re9.exec('zrqvgobk'); re9.exec('hsgy'); re9.exec('lhv-h'); /Fnsnev|Xbadhrebe|XUGZY/gi.exec(str0); re61.exec('uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); re62.exec('#Ybtva_rznvy'); } } var re63 = /\{0\}/g; var str36 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_tfwsbrg-aowb_80=4413268q3660'; var str37 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; AFP_zp_tfwsbrg-aowb_80=4413268q3660; __hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1; __hgzo=144631658.0.10.1231364074; __hgzp=144631658; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str38 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231364057761&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364057761&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Ssevraqf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1667363813.1231364061&tn_fvq=1231364061&tn_uvq=1917563877&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str39 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str40 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; function runBlock7() { for (var i = 0; i < 9; i++) { '0'.replace(re40, ''); '0'.replace(re10, ''); '0'.replace(re51, ''); '0'.replace(re52, ''); '0'.replace(re53, ''); '0'.replace(re39, ''); '0'.replace(re54, ''); 'Lrf'.replace(re40, ''); 'Lrf'.replace(re10, ''); 'Lrf'.replace(re51, ''); 'Lrf'.replace(re52, ''); 'Lrf'.replace(re53, ''); 'Lrf'.replace(re39, ''); 'Lrf'.replace(re54, ''); } for (var i = 0; i < 8; i++) { 'Pybfr {0}'.replace(re63, ''); 'Bcra {0}'.replace(re63, ''); str36.split(re32); str37.split(re32); 'puvyq p1 svefg gnournqref'.replace(re14, ''); 'puvyq p1 svefg gnournqref'.replace(re15, ''); 'uqy_fcb'.replace(re14, ''); 'uqy_fcb'.replace(re15, ''); 'uvag'.replace(re14, ''); 'uvag'.replace(re15, ''); str38.replace(re33, ''); 'yvfg'.replace(re14, ''); 'yvfg'.replace(re15, ''); 'at_bhgre'.replace(re30, ''); 'cnerag puebzr5 qbhoyr2 NU'.replace(re14, ''); 'cnerag puebzr5 qbhoyr2 NU'.replace(re15, ''); 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re14, ''); 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re15, ''); 'cnerag puebzr6 fvatyr1'.replace(re14, ''); 'cnerag puebzr6 fvatyr1'.replace(re15, ''); 'cb_qrs'.replace(re14, ''); 'cb_qrs'.replace(re15, ''); 'gnopbagrag'.replace(re14, ''); 'gnopbagrag'.replace(re15, ''); 'iv_svefg_gvzr'.replace(re30, ''); /(^|.)(ronl|qri-ehf3.wbg)(|fgberf|zbgbef|yvirnhpgvbaf|jvxv|rkcerff|punggre).(pbz(|.nh|.pa|.ux|.zl|.ft|.oe|.zk)|pb(.hx|.xe|.am)|pn|qr|se|vg|ay|or|ng|pu|vr|va|rf|cy|cu|fr)$/i.exec('cntrf.ronl.pbz'); re8.exec('144631658.0.10.1231364074'); re8.exec('144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); re8.exec('4413241q3660'); re8.exec('SbeprqRkcvengvba=633669357391353591'); re8.exec(str39); re8.exec(str40); re8.exec('AFP_zp_kkk-gdzogv_80=4413241q3660'); re8.exec('FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); re8.exec('__hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1'); re8.exec('__hgzo=144631658.0.10.1231364074'); re8.exec('__hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7'); re34.exec(str36); re34.exec(str37); } } var re64 = /\b[a-z]/g; var re65 = /^uggc:\/\//; var re66 = /(?:^|\s+)qvfnoyrq(?:\s+|$)/; var str41 = 'uggc://cebsvyr.zlfcnpr.pbz/Zbqhyrf/Nccyvpngvbaf/Cntrf/Pnainf.nfck'; function runBlock8() { for (var i = 0; i < 7; i++) { str1.match(/\d+/g); 'nsgre'.replace(re64, ''); 'orsber'.replace(re64, ''); 'obggbz'.replace(re64, ''); 'ohvygva_jrngure.kzy'.replace(re65, ''); 'ohggba'.replace(re37, ''); 'ohggba'.replace(re18, ''); 'qngrgvzr.kzy'.replace(re65, ''); 'uggc://eff.paa.pbz/eff/paa_gbcfgbevrf.eff'.replace(re65, ''); 'vachg'.replace(re37, ''); 'vachg'.replace(re18, ''); 'vafvqr'.replace(re64, ''); 'cbvagre'.replace(re27, ''); 'cbfvgvba'.replace(/[A-Z]/g, ''); 'gbc'.replace(re27, ''); 'gbc'.replace(re64, ''); 'hy'.replace(re37, ''); 'hy'.replace(re18, ''); str26.replace(re37, ''); str26.replace(re18, ''); 'lbhghor_vtbbtyr/i2/lbhghor.kzy'.replace(re65, ''); 'm-vaqrk'.replace(re27, ''); /#([\w-]+)/.exec(str26); re16.exec('urvtug'); re16.exec('znetvaGbc'); re16.exec('jvqgu'); re19.exec('gno0 svefg ba'); re19.exec('gno0 ba'); re19.exec('gno4 ynfg'); re19.exec('gno4'); re19.exec('gno5'); re19.exec('gno6'); re19.exec('gno7'); re19.exec('gno8'); /NqborNVE\/([^\s]*)/.exec(str0); /NccyrJroXvg\/([^ ]*)/.exec(str0); /XUGZY/gi.exec(str0); /^(?:obql|ugzy)$/i.exec('YV'); re38.exec('ohggba'); re38.exec('vachg'); re38.exec('hy'); re38.exec(str26); /^(\w+|\*)/.exec(str26); /znp|jva|yvahk/i.exec('Jva32'); /eton?\([\d\s,]+\)/.exec('fgngvp'); } for (var i = 0; i < 6; i++) { ''.replace(/\r/g, ''); '/'.replace(re40, ''); '/'.replace(re10, ''); '/'.replace(re51, ''); '/'.replace(re52, ''); '/'.replace(re53, ''); '/'.replace(re39, ''); '/'.replace(re54, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/{0}?[NDO]&{1}&{2}&[NDR]'.replace(re63, ''); str41.replace(re12, ''); 'uggc://jjj.snprobbx.pbz/fepu.cuc'.replace(re23, ''); 'freivpr'.replace(re40, ''); 'freivpr'.replace(re41, ''); 'freivpr'.replace(re42, ''); 'freivpr'.replace(re43, ''); 'freivpr'.replace(re44, ''); 'freivpr'.replace(re45, ''); 'freivpr'.replace(re46, ''); 'freivpr'.replace(re47, ''); 'freivpr'.replace(re48, ''); /((ZFVR\s+([6-9]|\d\d)\.))/.exec(str0); re66.exec(''); re50.exec('fryrpgrq'); re8.exec('8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); re8.exec('SbeprqRkcvengvba=633669340386893867'); re8.exec('VC=74.125.75.17'); re8.exec('FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn'); /Xbadhrebe|Fnsnev|XUGZY/.exec(str0); re13.exec(str41); re49.exec('unfsbphf'); } } var re67 = /zrah_byq/g; var str42 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str43 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; __hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1; __hgzo=144631658.0.10.1231364380; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str44 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_vzntrf_wf&qg=1231364373088&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364373088&punaary=svz_zlfcnpr_hfre-ivrj-pbzzragf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Spbzzrag.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1158737789.1231364375&tn_fvq=1231364375&tn_uvq=415520832&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str45 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str46 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var re68 = /^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/; var re69 = /\{1\}/g; var re70 = /\s+/; var re71 = /(\$\{4\})|(\$4\b)/g; var re72 = /(\$\{5\})|(\$5\b)/g; var re73 = /\{2\}/g; var re74 = /[^+>] [^+>]/; var re75 = /\bucpyv\s*=\s*([^;]*)/i; var re76 = /\bucuvqr\s*=\s*([^;]*)/i; var re77 = /\bucfie\s*=\s*([^;]*)/i; var re78 = /\bhfucjrn\s*=\s*([^;]*)/i; var re79 = /\bmvc\s*=\s*([^;]*)/i; var re80 = /^((?:[\w\u0128-\uffff*_-]|\\.)+)(#)((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re81 = /^([>+~])\s*(\w*)/i; var re82 = /^>\s*((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re83 = /^[\s[]?shapgvba/; var re84 = /v\/g.tvs#(.*)/i; var str47 = '#Zbq-Vasb-Vasb-WninFpevcgUvag'; var str48 = ',n.svryqOgaPnapry'; var str49 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_kkk-gdzogv_80=4413241q3660'; var str50 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; AFP_zp_kkk-gdzogv_80=4413241q3660; AFP_zp_kkk-aowb_80=4413235p3660; __hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1; __hgzo=144631658.0.10.1231367708; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str51 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367691141&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367691141&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sjjj.zlfcnpr.pbz%2S&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=320757904.1231367694&tn_fvq=1231367694&tn_uvq=1758792003&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str52 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N38%3N42%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=1024k768&p=24&x=L&oj=994&ou=634&uc=A&{2}&[NDR]'; var str53 = 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq qbhoyr2 ps'; var str54 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str55 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str56 = 'ne;ng;nh;or;oe;pn;pu;py;pa;qr;qx;rf;sv;se;to;ux;vq;vr;va;vg;wc;xe;zk;zl;ay;ab;am;cu;cy;cg;eh;fr;ft;gu;ge;gj;mn;'; var str57 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886&GHVQ=1'; var str58 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886'; var str59 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'; var str60 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF'; var str61 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/29/4RQP4969777N048NPS4RRR3PO2S7S.wct'; var str62 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/OQ/63NP9O94NS5OQP1249Q9S1ROP7NS3.wct'; var str63 = 'zbmvyyn/5.0 (jvaqbjf; h; jvaqbjf ag 5.1; ra-hf) nccyrjroxvg/528.9 (xugzy, yvxr trpxb) puebzr/2.0.157.0 fnsnev/528.9'; function runBlock9() { for (var i = 0; i < 5; i++) { str42.split(re32); str43.split(re32); 'svz_zlfcnpr_hfre-ivrj-pbzzragf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); str44.replace(re33, ''); 'zrah_arj zrah_arj_gbttyr zrah_gbttyr'.replace(re67, ''); 'zrah_byq zrah_byq_gbttyr zrah_gbttyr'.replace(re67, ''); re8.exec('102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); re8.exec('144631658.0.10.1231364380'); re8.exec('144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); re8.exec('441326q33660'); re8.exec('SbeprqRkcvengvba=633669341278771470'); re8.exec(str45); re8.exec(str46); re8.exec('AFP_zp_dfctwzssrwh-aowb_80=441326q33660'); re8.exec('FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98'); re8.exec('__hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1'); re8.exec('__hgzo=144631658.0.10.1231364380'); re8.exec('__hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); } for (var i = 0; i < 4; i++) { ' yvfg1'.replace(re14, ''); ' yvfg1'.replace(re15, ''); ' yvfg2'.replace(re14, ''); ' yvfg2'.replace(re15, ''); ' frneputebhc1'.replace(re14, ''); ' frneputebhc1'.replace(re15, ''); str47.replace(re68, ''); str47.replace(re18, ''); ''.replace(/&/g, ''); ''.replace(re35, ''); '(..-{0})(\|(\d+)|)'.replace(re63, ''); str48.replace(re18, ''); '//vzt.jro.qr/vij/FC/${cngu}/${anzr}/${inyhr}?gf=${abj}'.replace(re56, ''); '//vzt.jro.qr/vij/FC/tzk_uc/${anzr}/${inyhr}?gf=${abj}'.replace(/(\$\{anzr\})|(\$anzr\b)/g, ''); 'Jvaqbjf Yvir Ubgznvy{1}'.replace(re69, ''); '{0}{1}'.replace(re63, ''); '{1}'.replace(re69, ''); '{1}'.replace(re63, ''); 'Vzntrf'.replace(re15, ''); 'ZFA'.replace(re15, ''); 'Zncf'.replace(re15, ''); 'Zbq-Vasb-Vasb-WninFpevcgUvag'.replace(re39, ''); 'Arjf'.replace(re15, ''); str49.split(re32); str50.split(re32); 'Ivqrb'.replace(re15, ''); 'Jro'.replace(re15, ''); 'n'.replace(re39, ''); 'nwnkFgneg'.split(re70); 'nwnkFgbc'.split(re70); 'ovaq'.replace(re14, ''); 'ovaq'.replace(re15, ''); 'oevatf lbh zber. Zber fcnpr (5TO), zber frphevgl, fgvyy serr.'.replace(re63, ''); 'puvyq p1 svefg qrpx'.replace(re14, ''); 'puvyq p1 svefg qrpx'.replace(re15, ''); 'puvyq p1 svefg qbhoyr2'.replace(re14, ''); 'puvyq p1 svefg qbhoyr2'.replace(re15, ''); 'puvyq p2 ynfg'.replace(re14, ''); 'puvyq p2 ynfg'.replace(re15, ''); 'puvyq p2'.replace(re14, ''); 'puvyq p2'.replace(re15, ''); 'puvyq p3'.replace(re14, ''); 'puvyq p3'.replace(re15, ''); 'puvyq p4 ynfg'.replace(re14, ''); 'puvyq p4 ynfg'.replace(re15, ''); 'pbclevtug'.replace(re14, ''); 'pbclevtug'.replace(re15, ''); 'qZFAZR_1'.replace(re14, ''); 'qZFAZR_1'.replace(re15, ''); 'qbhoyr2 ps'.replace(re14, ''); 'qbhoyr2 ps'.replace(re15, ''); 'qbhoyr2'.replace(re14, ''); 'qbhoyr2'.replace(re15, ''); 'uqy_arj'.replace(re14, ''); 'uqy_arj'.replace(re15, ''); 'uc_fubccvatobk'.replace(re30, ''); 'ugzy%2Rvq'.replace(re29, ''); 'ugzy%2Rvq'.replace(re30, ''); str51.replace(re33, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${5}'.replace(re72, ''); str52.replace(re73, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); 'vztZFSG'.replace(re14, ''); 'vztZFSG'.replace(re15, ''); 'zfasbbg1 ps'.replace(re14, ''); 'zfasbbg1 ps'.replace(re15, ''); str53.replace(re14, ''); str53.replace(re15, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re14, ''); 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re15, ''); 'cevznel'.replace(re14, ''); 'cevznel'.replace(re15, ''); 'erpgnatyr'.replace(re30, ''); 'frpbaqnel'.replace(re14, ''); 'frpbaqnel'.replace(re15, ''); 'haybnq'.split(re70); '{0}{1}1'.replace(re63, ''); '|{1}1'.replace(re69, ''); /(..-HF)(\|(\d+)|)/i.exec('xb-xe,ra-va,gu-gu'); re4.exec('/ZlFcnprNccf/NccPnainf,45000012'); re8.exec('144631658.0.10.1231367708'); re8.exec('144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); re8.exec('4413235p3660'); re8.exec('441327q73660'); re8.exec('9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); re8.exec('SbeprqRkcvengvba=633669350559478880'); re8.exec(str54); re8.exec(str55); re8.exec('AFP_zp_dfctwzs-aowb_80=441327q73660'); re8.exec('AFP_zp_kkk-aowb_80=4413235p3660'); re8.exec('FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473'); re8.exec('__hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1'); re8.exec('__hgzo=144631658.0.10.1231367708'); re8.exec('__hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str49); re34.exec(str50); /ZFVR\s+5[.]01/.exec(str0); /HF(?=;)/i.exec(str56); re74.exec(str47); re28.exec('svefg npgvir svefgNpgvir'); re28.exec('ynfg'); /\bp:(..)/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF'); re75.exec(str57); re75.exec(str58); re76.exec(str57); re76.exec(str58); re77.exec(str57); re77.exec(str58); /\bhfucce\s*=\s*([^;]*)/i.exec(str59); re78.exec(str57); re78.exec(str58); /\bjci\s*=\s*([^;]*)/i.exec(str59); re79.exec(str58); re79.exec(str60); re79.exec(str59); /\|p:([a-z]{2})/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1'); re80.exec(str47); re61.exec('cebgbglcr.wf'); re68.exec(str47); re81.exec(str47); re82.exec(str47); /^Fubpxjnir Synfu (\d)/.exec(str1); /^Fubpxjnir Synfu (\d+)/.exec(str1); re83.exec('[bowrpg tybony]'); re62.exec(str47); re84.exec(str61); re84.exec(str62); /jroxvg/.exec(str63); } } var re85 = /eaq_zbqobkva/; var str64 = '1231365729213'; var str65 = '74.125.75.3-1057165600.29978900'; var str66 = '74.125.75.3-1057165600.29978900.1231365730214'; var str67 = 'Frnepu%20Zvpebfbsg.pbz'; var str68 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str69 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; __hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1; __hgzo=144631658.0.10.1231365779; __hgzp=144631658; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str70 = 'I=3%26THVQ=757q3ss871q44o7o805n8113n5p72q52'; var str71 = 'I=3&THVQ=757q3ss871q44o7o805n8113n5p72q52'; var str72 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365765292&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365765292&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sohyyrgvaf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1579793869.1231365768&tn_fvq=1231365768&tn_uvq=2056210897&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str73 = 'frnepu.zvpebfbsg.pbz'; var str74 = 'frnepu.zvpebfbsg.pbz/'; var str75 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str76 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; function runBlock10() { for (var i = 0; i < 3; i++) { '%3Szxg=ra-HF'.replace(re39, ''); '-8'.replace(re40, ''); '-8'.replace(re10, ''); '-8'.replace(re51, ''); '-8'.replace(re52, ''); '-8'.replace(re53, ''); '-8'.replace(re39, ''); '-8'.replace(re54, ''); '1.5'.replace(re40, ''); '1.5'.replace(re10, ''); '1.5'.replace(re51, ''); '1.5'.replace(re52, ''); '1.5'.replace(re53, ''); '1.5'.replace(re39, ''); '1.5'.replace(re54, ''); '1024k768'.replace(re40, ''); '1024k768'.replace(re10, ''); '1024k768'.replace(re51, ''); '1024k768'.replace(re52, ''); '1024k768'.replace(re53, ''); '1024k768'.replace(re39, ''); '1024k768'.replace(re54, ''); str64.replace(re40, ''); str64.replace(re10, ''); str64.replace(re51, ''); str64.replace(re52, ''); str64.replace(re53, ''); str64.replace(re39, ''); str64.replace(re54, ''); '14'.replace(re40, ''); '14'.replace(re10, ''); '14'.replace(re51, ''); '14'.replace(re52, ''); '14'.replace(re53, ''); '14'.replace(re39, ''); '14'.replace(re54, ''); '24'.replace(re40, ''); '24'.replace(re10, ''); '24'.replace(re51, ''); '24'.replace(re52, ''); '24'.replace(re53, ''); '24'.replace(re39, ''); '24'.replace(re54, ''); str65.replace(re40, ''); str65.replace(re10, ''); str65.replace(re51, ''); str65.replace(re52, ''); str65.replace(re53, ''); str65.replace(re39, ''); str65.replace(re54, ''); str66.replace(re40, ''); str66.replace(re10, ''); str66.replace(re51, ''); str66.replace(re52, ''); str66.replace(re53, ''); str66.replace(re39, ''); str66.replace(re54, ''); '9.0'.replace(re40, ''); '9.0'.replace(re10, ''); '9.0'.replace(re51, ''); '9.0'.replace(re52, ''); '9.0'.replace(re53, ''); '9.0'.replace(re39, ''); '9.0'.replace(re54, ''); '994k634'.replace(re40, ''); '994k634'.replace(re10, ''); '994k634'.replace(re51, ''); '994k634'.replace(re52, ''); '994k634'.replace(re53, ''); '994k634'.replace(re39, ''); '994k634'.replace(re54, ''); '?zxg=ra-HF'.replace(re40, ''); '?zxg=ra-HF'.replace(re10, ''); '?zxg=ra-HF'.replace(re51, ''); '?zxg=ra-HF'.replace(re52, ''); '?zxg=ra-HF'.replace(re53, ''); '?zxg=ra-HF'.replace(re54, ''); 'PAA.pbz'.replace(re25, ''); 'PAA.pbz'.replace(re12, ''); 'PAA.pbz'.replace(re39, ''); 'Qngr & Gvzr'.replace(re25, ''); 'Qngr & Gvzr'.replace(re12, ''); 'Qngr & Gvzr'.replace(re39, ''); 'Frnepu Zvpebfbsg.pbz'.replace(re40, ''); 'Frnepu Zvpebfbsg.pbz'.replace(re54, ''); str67.replace(re10, ''); str67.replace(re51, ''); str67.replace(re52, ''); str67.replace(re53, ''); str67.replace(re39, ''); str68.split(re32); str69.split(re32); str70.replace(re52, ''); str70.replace(re53, ''); str70.replace(re39, ''); str71.replace(re40, ''); str71.replace(re10, ''); str71.replace(re51, ''); str71.replace(re54, ''); 'Jrngure'.replace(re25, ''); 'Jrngure'.replace(re12, ''); 'Jrngure'.replace(re39, ''); 'LbhGhor'.replace(re25, ''); 'LbhGhor'.replace(re12, ''); 'LbhGhor'.replace(re39, ''); str72.replace(re33, ''); 'erzbgr_vsenzr_1'.replace(/^erzbgr_vsenzr_/, ''); str73.replace(re40, ''); str73.replace(re10, ''); str73.replace(re51, ''); str73.replace(re52, ''); str73.replace(re53, ''); str73.replace(re39, ''); str73.replace(re54, ''); str74.replace(re40, ''); str74.replace(re10, ''); str74.replace(re51, ''); str74.replace(re52, ''); str74.replace(re53, ''); str74.replace(re39, ''); str74.replace(re54, ''); 'lhv-h'.replace(/\-/g, ''); re9.exec('p'); re9.exec('qz p'); re9.exec('zbqynory'); re9.exec('lhv-h svefg'); re8.exec('144631658.0.10.1231365779'); re8.exec('144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); re8.exec(str75); re8.exec(str76); re8.exec('__hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1'); re8.exec('__hgzo=144631658.0.10.1231365779'); re8.exec('__hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str68); re34.exec(str69); /^$/.exec(''); re31.exec('qr'); /^znk\d+$/.exec(''); /^zva\d+$/.exec(''); /^erfgber$/.exec(''); re85.exec('zbqobkva zbqobk_abcnqqvat '); re85.exec('zbqgvgyr'); re85.exec('eaq_zbqobkva '); re85.exec('eaq_zbqgvgyr '); /frpgvba\d+_pbagragf/.exec('obggbz_ani'); } } var re86 = /;\s*/; var re87 = /(\$\{inyhr\})|(\$inyhr\b)/g; var re88 = /(\$\{abj\})|(\$abj\b)/g; var re89 = /\s+$/; var re90 = /^\s+/; var re91 = /(\\\"|\x00-|\x1f|\x7f-|\x9f|\u00ad|\u0600-|\u0604|\u070f|\u17b4|\u17b5|\u200c-|\u200f|\u2028-|\u202f|\u2060-|\u206f|\ufeff|\ufff0-|\uffff)/g; var re92 = /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/; var re93 = /^([:.#]*)((?:[\w\u0128-\uffff*_-]|\\.)+)/; var re94 = /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/; var str77 = '#fubhgobk .pybfr'; var str78 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzssrwh-aowb_80=441326q33660'; var str79 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; AFP_zp_dfctwzssrwh-aowb_80=441326q33660; __hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1; __hgzo=144631658.0.10.1231365869; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str80 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=441327q73660'; var str81 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; AFP_zp_dfctwzs-aowb_80=441327q73660; __hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1; __hgzo=144631658.0.10.1231367054; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str82 = '[glcr=fhozvg]'; var str83 = 'n.svryqOga,n.svryqOgaPnapry'; var str84 = 'n.svryqOgaPnapry'; var str85 = 'oyvpxchaxg'; var str86 = 'qvi.bow-nppbeqvba qg'; var str87 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_nccf_wf&qg=1231367052227&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367052227&punaary=svz_zlfcnpr_nccf-pnainf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2SZbqhyrf%2SNccyvpngvbaf%2SCntrf%2SPnainf.nfck&nq_glcr=grkg&rvq=6083027&rn=0&sez=1&tn_ivq=716357910.1231367056&tn_fvq=1231367056&tn_uvq=1387206491&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str88 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365851658&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365851658&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyrrqvg.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1979828129.1231365855&tn_fvq=1231365855&tn_uvq=2085229649&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22'; var str89 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N12%3N47%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=0k0&p=43835816&x=A&oj=994&ou=634&uc=A&{2}&[NDR]'; var str90 = 'zrgn[anzr=nwnkHey]'; var str91 = 'anpuevpugra'; var str92 = 'b oS={\'oT\':1.1};x $8n(B){z(B!=o9)};x $S(B){O(!$8n(B))z A;O(B.4L)z\'T\';b S=7t B;O(S==\'2P\'&&B.p4){23(B.7f){12 1:z\'T\';12 3:z/\S/.2g(B.8M)?\'ox\':\'oh\'}}O(S==\'2P\'||S==\'x\'){23(B.nE){12 2V:z\'1O\';12 7I:z\'5a\';12 18:z\'4B\'}O(7t B.I==\'4F\'){O(B.3u)z\'pG\';O(B.8e)z\'1p\'}}z S};x $2p(){b 4E={};Z(b v=0;v<1p.I;v++){Z(b X 1o 1p[v]){b nc=1p[v][X];b 6E=4E[X];O(6E&&$S(nc)==\'2P\'&&$S(6E)==\'2P\')4E[X]=$2p(6E,nc);17 4E[X]=nc}}z 4E};b $E=7p.E=x(){b 1d=1p;O(!1d[1])1d=[p,1d[0]];Z(b X 1o 1d[1])1d[0][X]=1d[1][X];z 1d[0]};b $4D=7p.pJ=x(){Z(b v=0,y=1p.I;v-1:p.3F(2R)>-1},nX:x(){z p.3y(/([.*+?^${}()|[\]\/\\])/t,\'\\$1\')}});2V.E({5V:x(1O){O(p.I<3)z A;O(p.I==4&&p[3]==0&&!1O)z\'p5\';b 3P=[];Z(b v=0;v<3;v++){b 52=(p[v]-0).4h(16);3P.1x((52.I==1)?\'0\'+52:52)}z 1O?3P:\'#\'+3P.2u(\'\')},5U:x(1O){O(p.I!=3)z A;b 1i=[];Z(b v=0;v<3;v++){1i.1x(5K((p[v].I==1)?p[v]+p[v]:p[v],16))}z 1O?1i:\'1i(\'+1i.2u(\',\')+\')\'}});7F.E({3n:x(P){b J=p;P=$2p({\'L\':J,\'V\':A,\'1p\':1S,\'2x\':A,\'4s\':A,\'6W\':A},P);O($2O(P.1p)&&$S(P.1p)!=\'1O\')P.1p=[P.1p];z x(V){b 1d;O(P.V){V=V||H.V;1d=[(P.V===1r)?V:Y P.V(V)];O(P.1p)1d.E(P.1p)}17 1d=P.1p||1p;b 3C=x(){z J.3H($5S(P'; var str93 = 'hagreunyghat'; var str94 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str95 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q'; var str96 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str97 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R='; var str98 = 'shapgvba (){Cuk.Nccyvpngvba.Frghc.Pber();Cuk.Nccyvpngvba.Frghc.Nwnk();Cuk.Nccyvpngvba.Frghc.Synfu();Cuk.Nccyvpngvba.Frghc.Zbqhyrf()}'; function runBlock11() { for (var i = 0; i < 2; i++) { ' .pybfr'.replace(re18, ''); ' n.svryqOgaPnapry'.replace(re18, ''); ' qg'.replace(re18, ''); str77.replace(re68, ''); str77.replace(re18, ''); ''.replace(re39, ''); ''.replace(/^/, ''); ''.split(re86); '*'.replace(re39, ''); '*'.replace(re68, ''); '*'.replace(re18, ''); '.pybfr'.replace(re68, ''); '.pybfr'.replace(re18, ''); '//vzt.jro.qr/vij/FC/tzk_uc/fperra/${inyhr}?gf=${abj}'.replace(re87, ''); '//vzt.jro.qr/vij/FC/tzk_uc/fperra/1024?gf=${abj}'.replace(re88, ''); '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/${inyhr}?gf=${abj}'.replace(re87, ''); '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/992/608?gf=${abj}'.replace(re88, ''); '300k120'.replace(re30, ''); '300k250'.replace(re30, ''); '310k120'.replace(re30, ''); '310k170'.replace(re30, ''); '310k250'.replace(re30, ''); '9.0 e115'.replace(/^.*\.(.*)\s.*$/, ''); 'Nppbeqvba'.replace(re2, ''); 'Nxghryy\x0a'.replace(re89, ''); 'Nxghryy\x0a'.replace(re90, ''); 'Nccyvpngvba'.replace(re2, ''); 'Oyvpxchaxg\x0a'.replace(re89, ''); 'Oyvpxchaxg\x0a'.replace(re90, ''); 'Svanamra\x0a'.replace(re89, ''); 'Svanamra\x0a'.replace(re90, ''); 'Tnzrf\x0a'.replace(re89, ''); 'Tnzrf\x0a'.replace(re90, ''); 'Ubebfxbc\x0a'.replace(re89, ''); 'Ubebfxbc\x0a'.replace(re90, ''); 'Xvab\x0a'.replace(re89, ''); 'Xvab\x0a'.replace(re90, ''); 'Zbqhyrf'.replace(re2, ''); 'Zhfvx\x0a'.replace(re89, ''); 'Zhfvx\x0a'.replace(re90, ''); 'Anpuevpugra\x0a'.replace(re89, ''); 'Anpuevpugra\x0a'.replace(re90, ''); 'Cuk'.replace(re2, ''); 'ErdhrfgSvavfu'.split(re70); 'ErdhrfgSvavfu.NWNK.Cuk'.split(re70); 'Ebhgr\x0a'.replace(re89, ''); 'Ebhgr\x0a'.replace(re90, ''); str78.split(re32); str79.split(re32); str80.split(re32); str81.split(re32); 'Fcbeg\x0a'.replace(re89, ''); 'Fcbeg\x0a'.replace(re90, ''); 'GI-Fcbg\x0a'.replace(re89, ''); 'GI-Fcbg\x0a'.replace(re90, ''); 'Gbhe\x0a'.replace(re89, ''); 'Gbhe\x0a'.replace(re90, ''); 'Hagreunyghat\x0a'.replace(re89, ''); 'Hagreunyghat\x0a'.replace(re90, ''); 'Ivqrb\x0a'.replace(re89, ''); 'Ivqrb\x0a'.replace(re90, ''); 'Jrggre\x0a'.replace(re89, ''); 'Jrggre\x0a'.replace(re90, ''); str82.replace(re68, ''); str82.replace(re18, ''); str83.replace(re68, ''); str83.replace(re18, ''); str84.replace(re68, ''); str84.replace(re18, ''); 'nqiFreivprObk'.replace(re30, ''); 'nqiFubccvatObk'.replace(re30, ''); 'nwnk'.replace(re39, ''); 'nxghryy'.replace(re40, ''); 'nxghryy'.replace(re41, ''); 'nxghryy'.replace(re42, ''); 'nxghryy'.replace(re43, ''); 'nxghryy'.replace(re44, ''); 'nxghryy'.replace(re45, ''); 'nxghryy'.replace(re46, ''); 'nxghryy'.replace(re47, ''); 'nxghryy'.replace(re48, ''); str85.replace(re40, ''); str85.replace(re41, ''); str85.replace(re42, ''); str85.replace(re43, ''); str85.replace(re44, ''); str85.replace(re45, ''); str85.replace(re46, ''); str85.replace(re47, ''); str85.replace(re48, ''); 'pngrtbel'.replace(re29, ''); 'pngrtbel'.replace(re30, ''); 'pybfr'.replace(re39, ''); 'qvi'.replace(re39, ''); str86.replace(re68, ''); str86.replace(re18, ''); 'qg'.replace(re39, ''); 'qg'.replace(re68, ''); 'qg'.replace(re18, ''); 'rzorq'.replace(re39, ''); 'rzorq'.replace(re68, ''); 'rzorq'.replace(re18, ''); 'svryqOga'.replace(re39, ''); 'svryqOgaPnapry'.replace(re39, ''); 'svz_zlfcnpr_nccf-pnainf,svz_zlfcnpr_havgrq-fgngrf'.split(re20); 'svanamra'.replace(re40, ''); 'svanamra'.replace(re41, ''); 'svanamra'.replace(re42, ''); 'svanamra'.replace(re43, ''); 'svanamra'.replace(re44, ''); 'svanamra'.replace(re45, ''); 'svanamra'.replace(re46, ''); 'svanamra'.replace(re47, ''); 'svanamra'.replace(re48, ''); 'sbphf'.split(re70); 'sbphf.gno sbphfva.gno'.split(re70); 'sbphfva'.split(re70); 'sbez'.replace(re39, ''); 'sbez.nwnk'.replace(re68, ''); 'sbez.nwnk'.replace(re18, ''); 'tnzrf'.replace(re40, ''); 'tnzrf'.replace(re41, ''); 'tnzrf'.replace(re42, ''); 'tnzrf'.replace(re43, ''); 'tnzrf'.replace(re44, ''); 'tnzrf'.replace(re45, ''); 'tnzrf'.replace(re46, ''); 'tnzrf'.replace(re47, ''); 'tnzrf'.replace(re48, ''); 'ubzrcntr'.replace(re30, ''); 'ubebfxbc'.replace(re40, ''); 'ubebfxbc'.replace(re41, ''); 'ubebfxbc'.replace(re42, ''); 'ubebfxbc'.replace(re43, ''); 'ubebfxbc'.replace(re44, ''); 'ubebfxbc'.replace(re45, ''); 'ubebfxbc'.replace(re46, ''); 'ubebfxbc'.replace(re47, ''); 'ubebfxbc'.replace(re48, ''); 'uc_cebzbobk_ugzy%2Puc_cebzbobk_vzt'.replace(re30, ''); 'uc_erpgnatyr'.replace(re30, ''); str87.replace(re33, ''); str88.replace(re33, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${5}'.replace(re72, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${5}'.replace(re72, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${4}${5}'.replace(re71, ''); 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${5}'.replace(re72, ''); str89.replace(re73, ''); 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&{1}&{2}&[NDR]'.replace(re69, ''); str6.replace(re23, ''); 'xvab'.replace(re40, ''); 'xvab'.replace(re41, ''); 'xvab'.replace(re42, ''); 'xvab'.replace(re43, ''); 'xvab'.replace(re44, ''); 'xvab'.replace(re45, ''); 'xvab'.replace(re46, ''); 'xvab'.replace(re47, ''); 'xvab'.replace(re48, ''); 'ybnq'.split(re70); 'zrqvnzbqgno lhv-anifrg lhv-anifrg-gbc'.replace(re18, ''); 'zrgn'.replace(re39, ''); str90.replace(re68, ''); str90.replace(re18, ''); 'zbhfrzbir'.split(re70); 'zbhfrzbir.gno'.split(re70); str63.replace(/^.*jroxvg\/(\d+(\.\d+)?).*$/, ''); 'zhfvx'.replace(re40, ''); 'zhfvx'.replace(re41, ''); 'zhfvx'.replace(re42, ''); 'zhfvx'.replace(re43, ''); 'zhfvx'.replace(re44, ''); 'zhfvx'.replace(re45, ''); 'zhfvx'.replace(re46, ''); 'zhfvx'.replace(re47, ''); 'zhfvx'.replace(re48, ''); 'zlfcnpr_nccf_pnainf'.replace(re52, ''); str91.replace(re40, ''); str91.replace(re41, ''); str91.replace(re42, ''); str91.replace(re43, ''); str91.replace(re44, ''); str91.replace(re45, ''); str91.replace(re46, ''); str91.replace(re47, ''); str91.replace(re48, ''); 'anzr'.replace(re39, ''); str92.replace(/\b\w+\b/g, ''); 'bow-nppbeqvba'.replace(re39, ''); 'bowrpg'.replace(re39, ''); 'bowrpg'.replace(re68, ''); 'bowrpg'.replace(re18, ''); 'cnenzf%2Rfglyrf'.replace(re29, ''); 'cnenzf%2Rfglyrf'.replace(re30, ''); 'cbchc'.replace(re30, ''); 'ebhgr'.replace(re40, ''); 'ebhgr'.replace(re41, ''); 'ebhgr'.replace(re42, ''); 'ebhgr'.replace(re43, ''); 'ebhgr'.replace(re44, ''); 'ebhgr'.replace(re45, ''); 'ebhgr'.replace(re46, ''); 'ebhgr'.replace(re47, ''); 'ebhgr'.replace(re48, ''); 'freivprobk_uc'.replace(re30, ''); 'fubccvatobk_uc'.replace(re30, ''); 'fubhgobk'.replace(re39, ''); 'fcbeg'.replace(re40, ''); 'fcbeg'.replace(re41, ''); 'fcbeg'.replace(re42, ''); 'fcbeg'.replace(re43, ''); 'fcbeg'.replace(re44, ''); 'fcbeg'.replace(re45, ''); 'fcbeg'.replace(re46, ''); 'fcbeg'.replace(re47, ''); 'fcbeg'.replace(re48, ''); 'gbhe'.replace(re40, ''); 'gbhe'.replace(re41, ''); 'gbhe'.replace(re42, ''); 'gbhe'.replace(re43, ''); 'gbhe'.replace(re44, ''); 'gbhe'.replace(re45, ''); 'gbhe'.replace(re46, ''); 'gbhe'.replace(re47, ''); 'gbhe'.replace(re48, ''); 'gi-fcbg'.replace(re40, ''); 'gi-fcbg'.replace(re41, ''); 'gi-fcbg'.replace(re42, ''); 'gi-fcbg'.replace(re43, ''); 'gi-fcbg'.replace(re44, ''); 'gi-fcbg'.replace(re45, ''); 'gi-fcbg'.replace(re46, ''); 'gi-fcbg'.replace(re47, ''); 'gi-fcbg'.replace(re48, ''); 'glcr'.replace(re39, ''); 'haqrsvarq'.replace(/\//g, ''); str93.replace(re40, ''); str93.replace(re41, ''); str93.replace(re42, ''); str93.replace(re43, ''); str93.replace(re44, ''); str93.replace(re45, ''); str93.replace(re46, ''); str93.replace(re47, ''); str93.replace(re48, ''); 'ivqrb'.replace(re40, ''); 'ivqrb'.replace(re41, ''); 'ivqrb'.replace(re42, ''); 'ivqrb'.replace(re43, ''); 'ivqrb'.replace(re44, ''); 'ivqrb'.replace(re45, ''); 'ivqrb'.replace(re46, ''); 'ivqrb'.replace(re47, ''); 'ivqrb'.replace(re48, ''); 'ivfvgf=1'.split(re86); 'jrggre'.replace(re40, ''); 'jrggre'.replace(re41, ''); 'jrggre'.replace(re42, ''); 'jrggre'.replace(re43, ''); 'jrggre'.replace(re44, ''); 'jrggre'.replace(re45, ''); 'jrggre'.replace(re46, ''); 'jrggre'.replace(re47, ''); 'jrggre'.replace(re48, ''); /#[a-z0-9]+$/i.exec('uggc://jjj.fpuhryreim.arg/Qrsnhyg'); re66.exec('fryrpgrq'); /(?:^|\s+)lhv-ani(?:\s+|$)/.exec('sff lhv-ani'); /(?:^|\s+)lhv-anifrg(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); /(?:^|\s+)lhv-anifrg-gbc(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg'); re91.exec('GnoThvq'); re91.exec('thvq'); /(pbzcngvoyr|jroxvg)/.exec(str63); /.+(?:ei|vg|en|vr)[\/: ]([\d.]+)/.exec(str63); re8.exec('144631658.0.10.1231365869'); re8.exec('144631658.0.10.1231367054'); re8.exec('144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); re8.exec('144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); re8.exec(str94); re8.exec(str95); re8.exec(str96); re8.exec(str97); re8.exec('__hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1'); re8.exec('__hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1'); re8.exec('__hgzo=144631658.0.10.1231365869'); re8.exec('__hgzo=144631658.0.10.1231367054'); re8.exec('__hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re8.exec('__hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)'); re34.exec(str78); re34.exec(str79); re34.exec(str81); re74.exec(str77); re74.exec('*'); re74.exec(str82); re74.exec(str83); re74.exec(str86); re74.exec('rzorq'); re74.exec('sbez.nwnk'); re74.exec(str90); re74.exec('bowrpg'); /\/onfr.wf(\?.+)?$/.exec('/uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf'); re28.exec('uvag ynfgUvag ynfg'); re75.exec(''); re76.exec(''); re77.exec(''); re78.exec(''); re80.exec(str77); re80.exec('*'); re80.exec('.pybfr'); re80.exec(str82); re80.exec(str83); re80.exec(str84); re80.exec(str86); re80.exec('qg'); re80.exec('rzorq'); re80.exec('sbez.nwnk'); re80.exec(str90); re80.exec('bowrpg'); re61.exec('qlaYvo.wf'); re61.exec('rssrpgYvo.wf'); re61.exec('uggc://jjj.tzk.arg/qr/?fgnghf=uvajrvf'); re92.exec(' .pybfr'); re92.exec(' n.svryqOgaPnapry'); re92.exec(' qg'); re92.exec(str48); re92.exec('.nwnk'); re92.exec('.svryqOga,n.svryqOgaPnapry'); re92.exec('.svryqOgaPnapry'); re92.exec('.bow-nppbeqvba qg'); re68.exec(str77); re68.exec('*'); re68.exec('.pybfr'); re68.exec(str82); re68.exec(str83); re68.exec(str84); re68.exec(str86); re68.exec('qg'); re68.exec('rzorq'); re68.exec('sbez.nwnk'); re68.exec(str90); re68.exec('bowrpg'); re93.exec(' .pybfr'); re93.exec(' n.svryqOgaPnapry'); re93.exec(' qg'); re93.exec(str48); re93.exec('.nwnk'); re93.exec('.svryqOga,n.svryqOgaPnapry'); re93.exec('.svryqOgaPnapry'); re93.exec('.bow-nppbeqvba qg'); re81.exec(str77); re81.exec('*'); re81.exec(str48); re81.exec('.pybfr'); re81.exec(str82); re81.exec(str83); re81.exec(str84); re81.exec(str86); re81.exec('qg'); re81.exec('rzorq'); re81.exec('sbez.nwnk'); re81.exec(str90); re81.exec('bowrpg'); re94.exec(' .pybfr'); re94.exec(' n.svryqOgaPnapry'); re94.exec(' qg'); re94.exec(str48); re94.exec('.nwnk'); re94.exec('.svryqOga,n.svryqOgaPnapry'); re94.exec('.svryqOgaPnapry'); re94.exec('.bow-nppbeqvba qg'); re94.exec('[anzr=nwnkHey]'); re94.exec(str82); re31.exec('rf'); re31.exec('wn'); re82.exec(str77); re82.exec('*'); re82.exec(str48); re82.exec('.pybfr'); re82.exec(str82); re82.exec(str83); re82.exec(str84); re82.exec(str86); re82.exec('qg'); re82.exec('rzorq'); re82.exec('sbez.nwnk'); re82.exec(str90); re82.exec('bowrpg'); re83.exec(str98); re83.exec('shapgvba sbphf() { [angvir pbqr] }'); re62.exec('#Ybtva'); re62.exec('#Ybtva_cnffjbeq'); re62.exec(str77); re62.exec('#fubhgobkWf'); re62.exec('#fubhgobkWfReebe'); re62.exec('#fubhgobkWfFhpprff'); re62.exec('*'); re62.exec(str82); re62.exec(str83); re62.exec(str86); re62.exec('rzorq'); re62.exec('sbez.nwnk'); re62.exec(str90); re62.exec('bowrpg'); re49.exec('pbagrag'); re24.exec(str6); /xbadhrebe/.exec(str63); /znp/.exec('jva32'); /zbmvyyn/.exec(str63); /zfvr/.exec(str63); /ag\s5\.1/.exec(str63); /bcren/.exec(str63); /fnsnev/.exec(str63); /jva/.exec('jva32'); /jvaqbjf/.exec(str63); } } for (var i = 0; i < 5; i++) { runBlock0(); runBlock1(); runBlock2(); runBlock3(); runBlock4(); runBlock5(); runBlock6(); runBlock7(); runBlock8(); runBlock9(); runBlock10(); runBlock11(); } } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/run.js0000644000175000017500000000460314433667662025471 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. load('base.js'); load('richards.js'); load('deltablue.js'); load('crypto.js'); load('raytrace.js'); load('earley-boyer.js'); load('regexp.js'); load('splay.js'); var success = true; function PrintResult(name, result) { print(name + ': ' + result); } function PrintError(name, error) { PrintResult(name, error); success = false; } function PrintScore(score) { if (success) { print('----'); print('Score (version ' + BenchmarkSuite.version + '): ' + score); } } // Run more than once to warm up JVM var runs = parseInt(arguments[0]) || 10; for (var i = 1; i <= runs; i++) { print('Run ' + i + ' out of ' + runs + ':'); BenchmarkSuite.RunSuites({ NotifyResult: PrintResult, NotifyError: PrintError, NotifyScore: PrintScore }); print(''); gc(); } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/richards.js0000644000175000017500000003664014433667662026472 0ustar apoapo// Copyright 2006-2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This is a JavaScript implementation of the Richards // benchmark from: // // http://www.cl.cam.ac.uk/~mr10/Bench.html // // The benchmark was originally implemented in BCPL by // Martin Richards. var Richards = new BenchmarkSuite('Richards', 34886, [ new Benchmark("Richards", runRichards) ]); /** * The Richards benchmark simulates the task dispatcher of an * operating system. **/ function runRichards() { var scheduler = new Scheduler(); scheduler.addIdleTask(ID_IDLE, 0, null, COUNT); var queue = new Packet(null, ID_WORKER, KIND_WORK); queue = new Packet(queue, ID_WORKER, KIND_WORK); scheduler.addWorkerTask(ID_WORKER, 1000, queue); queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE); scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue); queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE); scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue); scheduler.addDeviceTask(ID_DEVICE_A, 4000, null); scheduler.addDeviceTask(ID_DEVICE_B, 5000, null); scheduler.schedule(); if (scheduler.queueCount != EXPECTED_QUEUE_COUNT || scheduler.holdCount != EXPECTED_HOLD_COUNT) { var msg = "Error during execution: queueCount = " + scheduler.queueCount + ", holdCount = " + scheduler.holdCount + "."; throw new Error(msg); } } var COUNT = 1000; /** * These two constants specify how many times a packet is queued and * how many times a task is put on hold in a correct run of richards. * They don't have any meaning a such but are characteristic of a * correct run so if the actual queue or hold count is different from * the expected there must be a bug in the implementation. **/ var EXPECTED_QUEUE_COUNT = 2322; var EXPECTED_HOLD_COUNT = 928; /** * A scheduler can be used to schedule a set of tasks based on their relative * priorities. Scheduling is done by maintaining a list of task control blocks * which holds tasks and the data queue they are processing. * @constructor */ function Scheduler() { this.queueCount = 0; this.holdCount = 0; this.blocks = new Array(NUMBER_OF_IDS); this.list = null; this.currentTcb = null; this.currentId = null; } var ID_IDLE = 0; var ID_WORKER = 1; var ID_HANDLER_A = 2; var ID_HANDLER_B = 3; var ID_DEVICE_A = 4; var ID_DEVICE_B = 5; var NUMBER_OF_IDS = 6; var KIND_DEVICE = 0; var KIND_WORK = 1; /** * Add an idle task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {int} count the number of times to schedule the task */ Scheduler.prototype.addIdleTask = function (id, priority, queue, count) { this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count)); }; /** * Add a work task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addWorkerTask = function (id, priority, queue) { this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0)); }; /** * Add a handler task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addHandlerTask = function (id, priority, queue) { this.addTask(id, priority, queue, new HandlerTask(this)); }; /** * Add a handler task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task */ Scheduler.prototype.addDeviceTask = function (id, priority, queue) { this.addTask(id, priority, queue, new DeviceTask(this)) }; /** * Add the specified task and mark it as running. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {Task} task the task to add */ Scheduler.prototype.addRunningTask = function (id, priority, queue, task) { this.addTask(id, priority, queue, task); this.currentTcb.setRunning(); }; /** * Add the specified task to this scheduler. * @param {int} id the identity of the task * @param {int} priority the task's priority * @param {Packet} queue the queue of work to be processed by the task * @param {Task} task the task to add */ Scheduler.prototype.addTask = function (id, priority, queue, task) { this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task); this.list = this.currentTcb; this.blocks[id] = this.currentTcb; }; /** * Execute the tasks managed by this scheduler. */ Scheduler.prototype.schedule = function () { this.currentTcb = this.list; while (this.currentTcb != null) { if (this.currentTcb.isHeldOrSuspended()) { this.currentTcb = this.currentTcb.link; } else { this.currentId = this.currentTcb.id; this.currentTcb = this.currentTcb.run(); } } }; /** * Release a task that is currently blocked and return the next block to run. * @param {int} id the id of the task to suspend */ Scheduler.prototype.release = function (id) { var tcb = this.blocks[id]; if (tcb == null) return tcb; tcb.markAsNotHeld(); if (tcb.priority > this.currentTcb.priority) { return tcb; } else { return this.currentTcb; } }; /** * Block the currently executing task and return the next task control block * to run. The blocked task will not be made runnable until it is explicitly * released, even if new work is added to it. */ Scheduler.prototype.holdCurrent = function () { this.holdCount++; this.currentTcb.markAsHeld(); return this.currentTcb.link; }; /** * Suspend the currently executing task and return the next task control block * to run. If new work is added to the suspended task it will be made runnable. */ Scheduler.prototype.suspendCurrent = function () { this.currentTcb.markAsSuspended(); return this.currentTcb; }; /** * Add the specified packet to the end of the worklist used by the task * associated with the packet and make the task runnable if it is currently * suspended. * @param {Packet} packet the packet to add */ Scheduler.prototype.queue = function (packet) { var t = this.blocks[packet.id]; if (t == null) return t; this.queueCount++; packet.link = null; packet.id = this.currentId; return t.checkPriorityAdd(this.currentTcb, packet); }; /** * A task control block manages a task and the queue of work packages associated * with it. * @param {TaskControlBlock} link the preceding block in the linked block list * @param {int} id the id of this block * @param {int} priority the priority of this block * @param {Packet} queue the queue of packages to be processed by the task * @param {Task} task the task * @constructor */ function TaskControlBlock(link, id, priority, queue, task) { this.link = link; this.id = id; this.priority = priority; this.queue = queue; this.task = task; if (queue == null) { this.state = STATE_SUSPENDED; } else { this.state = STATE_SUSPENDED_RUNNABLE; } } /** * The task is running and is currently scheduled. */ var STATE_RUNNING = 0; /** * The task has packets left to process. */ var STATE_RUNNABLE = 1; /** * The task is not currently running. The task is not blocked as such and may * be started by the scheduler. */ var STATE_SUSPENDED = 2; /** * The task is blocked and cannot be run until it is explicitly released. */ var STATE_HELD = 4; var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE; var STATE_NOT_HELD = ~STATE_HELD; TaskControlBlock.prototype.setRunning = function () { this.state = STATE_RUNNING; }; TaskControlBlock.prototype.markAsNotHeld = function () { this.state = this.state & STATE_NOT_HELD; }; TaskControlBlock.prototype.markAsHeld = function () { this.state = this.state | STATE_HELD; }; TaskControlBlock.prototype.isHeldOrSuspended = function () { return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED); }; TaskControlBlock.prototype.markAsSuspended = function () { this.state = this.state | STATE_SUSPENDED; }; TaskControlBlock.prototype.markAsRunnable = function () { this.state = this.state | STATE_RUNNABLE; }; /** * Runs this task, if it is ready to be run, and returns the next task to run. */ TaskControlBlock.prototype.run = function () { var packet; if (this.state == STATE_SUSPENDED_RUNNABLE) { packet = this.queue; this.queue = packet.link; if (this.queue == null) { this.state = STATE_RUNNING; } else { this.state = STATE_RUNNABLE; } } else { packet = null; } return this.task.run(packet); }; /** * Adds a packet to the worklist of this block's task, marks this as runnable if * necessary, and returns the next runnable object to run (the one * with the highest priority). */ TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) { if (this.queue == null) { this.queue = packet; this.markAsRunnable(); if (this.priority > task.priority) return this; } else { this.queue = packet.addTo(this.queue); } return task; }; TaskControlBlock.prototype.toString = function () { return "tcb { " + this.task + "@" + this.state + " }"; }; /** * An idle task doesn't do any work itself but cycles control between the two * device tasks. * @param {Scheduler} scheduler the scheduler that manages this task * @param {int} v1 a seed value that controls how the device tasks are scheduled * @param {int} count the number of times this task should be scheduled * @constructor */ function IdleTask(scheduler, v1, count) { this.scheduler = scheduler; this.v1 = v1; this.count = count; } IdleTask.prototype.run = function (packet) { this.count--; if (this.count == 0) return this.scheduler.holdCurrent(); if ((this.v1 & 1) == 0) { this.v1 = this.v1 >> 1; return this.scheduler.release(ID_DEVICE_A); } else { this.v1 = (this.v1 >> 1) ^ 0xD008; return this.scheduler.release(ID_DEVICE_B); } }; IdleTask.prototype.toString = function () { return "IdleTask" }; /** * A task that suspends itself after each time it has been run to simulate * waiting for data from an external device. * @param {Scheduler} scheduler the scheduler that manages this task * @constructor */ function DeviceTask(scheduler) { this.scheduler = scheduler; this.v1 = null; } DeviceTask.prototype.run = function (packet) { if (packet == null) { if (this.v1 == null) return this.scheduler.suspendCurrent(); var v = this.v1; this.v1 = null; return this.scheduler.queue(v); } else { this.v1 = packet; return this.scheduler.holdCurrent(); } }; DeviceTask.prototype.toString = function () { return "DeviceTask"; }; /** * A task that manipulates work packets. * @param {Scheduler} scheduler the scheduler that manages this task * @param {int} v1 a seed used to specify how work packets are manipulated * @param {int} v2 another seed used to specify how work packets are manipulated * @constructor */ function WorkerTask(scheduler, v1, v2) { this.scheduler = scheduler; this.v1 = v1; this.v2 = v2; } WorkerTask.prototype.run = function (packet) { if (packet == null) { return this.scheduler.suspendCurrent(); } else { if (this.v1 == ID_HANDLER_A) { this.v1 = ID_HANDLER_B; } else { this.v1 = ID_HANDLER_A; } packet.id = this.v1; packet.a1 = 0; for (var i = 0; i < DATA_SIZE; i++) { this.v2++; if (this.v2 > 26) this.v2 = 1; packet.a2[i] = this.v2; } return this.scheduler.queue(packet); } }; WorkerTask.prototype.toString = function () { return "WorkerTask"; }; /** * A task that manipulates work packets and then suspends itself. * @param {Scheduler} scheduler the scheduler that manages this task * @constructor */ function HandlerTask(scheduler) { this.scheduler = scheduler; this.v1 = null; this.v2 = null; } HandlerTask.prototype.run = function (packet) { if (packet != null) { if (packet.kind == KIND_WORK) { this.v1 = packet.addTo(this.v1); } else { this.v2 = packet.addTo(this.v2); } } if (this.v1 != null) { var count = this.v1.a1; var v; if (count < DATA_SIZE) { if (this.v2 != null) { v = this.v2; this.v2 = this.v2.link; v.a1 = this.v1.a2[count]; this.v1.a1 = count + 1; return this.scheduler.queue(v); } } else { v = this.v1; this.v1 = this.v1.link; return this.scheduler.queue(v); } } return this.scheduler.suspendCurrent(); }; HandlerTask.prototype.toString = function () { return "HandlerTask"; }; /* --- * * P a c k e t * --- */ var DATA_SIZE = 4; /** * A simple package of data that is manipulated by the tasks. The exact layout * of the payload data carried by a packet is not importaint, and neither is the * nature of the work performed on packets by the tasks. * * Besides carrying data, packets form linked lists and are hence used both as * data and worklists. * @param {Packet} link the tail of the linked list of packets * @param {int} id an ID for this packet * @param {int} kind the type of this packet * @constructor */ function Packet(link, id, kind) { this.link = link; this.id = id; this.kind = kind; this.a1 = 0; this.a2 = new Array(DATA_SIZE); } /** * Add this packet to the end of a worklist, and return the worklist. * @param {Packet} queue the worklist to add this packet to */ Packet.prototype.addTo = function (queue) { this.link = null; if (queue == null) return this; var peek, next = queue; while ((peek = next.link) != null) next = peek; next.link = this; return queue; }; Packet.prototype.toString = function () { return "Packet"; }; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/crypto.js0000644000175000017500000013561614433667662026216 0ustar apoapo/* * Copyright (c) 2003-2005 Tom Wu * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * In addition, the following condition applies: * * All redistributions must retain an intact copy of this copyright notice * and disclaimer. */ // The code has been adapted for use as a benchmark by Google. var Crypto = new BenchmarkSuite('Crypto', 203037, [ new Benchmark("Encrypt", encrypt), new Benchmark("Decrypt", decrypt) ]); // Basic JavaScript BN library - subset useful for RSA encryption. // Bits per digit var dbits; var BI_DB; var BI_DM; var BI_DV; var BI_FP; var BI_FV; var BI_F1; var BI_F2; // JavaScript engine analysis var canary = 0xdeadbeefcafe; var j_lm = ((canary&0xffffff)==0xefcafe); // (public) Constructor function BigInteger(a,b,c) { this.array = new Array(); if(a != null) if("number" == typeof a) this.fromNumber(a,b,c); else if(b == null && "string" != typeof a) this.fromString(a,256); else this.fromString(a,b); } // return new, unset BigInteger function nbi() { return new BigInteger(null); } // am: Compute w_j += (x*this_i), propagate carries, // c is initial carry, returns final carry. // c < 3*dvalue, x < 2*dvalue, this_i < dvalue // We need to select the fastest one that works in this environment. // am1: use a single mult and divide to get the high bits, // max digit bits should be 26 because // max internal value = 2*dvalue^2-2*dvalue (< 2^53) function am1(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; while(--n >= 0) { var v = x*this_array[i++]+w_array[j]+c; c = Math.floor(v/0x4000000); w_array[j++] = v&0x3ffffff; } return c; } // am2 avoids a big mult-and-extract completely. // Max digit bits should be <= 30 because we do bitwise ops // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) function am2(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x7fff, xh = x>>15; while(--n >= 0) { var l = this_array[i]&0x7fff; var h = this_array[i++]>>15; var m = xh*l+h*xl; l = xl*l+((m&0x7fff)<<15)+w_array[j]+(c&0x3fffffff); c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); w_array[j++] = l&0x3fffffff; } return c; } // Alternately, set max digit bits to 28 since some // browsers slow down when dealing with 32-bit numbers. function am3(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x3fff, xh = x>>14; while(--n >= 0) { var l = this_array[i]&0x3fff; var h = this_array[i++]>>14; var m = xh*l+h*xl; l = xl*l+((m&0x3fff)<<14)+w_array[j]+c; c = (l>>28)+(m>>14)+xh*h; w_array[j++] = l&0xfffffff; } return c; } // This is tailored to VMs with 2-bit tagging. It makes sure // that all the computations stay within the 29 bits available. function am4(i,x,w,j,c,n) { var this_array = this.array; var w_array = w.array; var xl = x&0x1fff, xh = x>>13; while(--n >= 0) { var l = this_array[i]&0x1fff; var h = this_array[i++]>>13; var m = xh*l+h*xl; l = xl*l+((m&0x1fff)<<13)+w_array[j]+c; c = (l>>26)+(m>>13)+xh*h; w_array[j++] = l&0x3ffffff; } return c; } // am3/28 is best for SM, Rhino, but am4/26 is best for v8. // Kestrel (Opera 9.5) gets its best result with am4/26. // IE7 does 9% better with am3/28 than with am4/26. // Firefox (SM) gets 10% faster with am3/28 than with am4/26. setupEngine = function(fn, bits) { BigInteger.prototype.am = fn; dbits = bits; BI_DB = dbits; BI_DM = ((1<= 0; --i) r_array[i] = this_array[i]; r.t = this.t; r.s = this.s; } // (protected) set from integer value x, -DV <= x < DV function bnpFromInt(x) { var this_array = this.array; this.t = 1; this.s = (x<0)?-1:0; if(x > 0) this_array[0] = x; else if(x < -1) this_array[0] = x+DV; else this.t = 0; } // return bigint initialized to value function nbv(i) { var r = nbi(); r.fromInt(i); return r; } // (protected) set from string and radix function bnpFromString(s,b) { var this_array = this.array; var k; if(b == 16) k = 4; else if(b == 8) k = 3; else if(b == 256) k = 8; // byte array else if(b == 2) k = 1; else if(b == 32) k = 5; else if(b == 4) k = 2; else { this.fromRadix(s,b); return; } this.t = 0; this.s = 0; var i = s.length, mi = false, sh = 0; while(--i >= 0) { var x = (k==8)?s[i]&0xff:intAt(s,i); if(x < 0) { if(s.charAt(i) == "-") mi = true; continue; } mi = false; if(sh == 0) this_array[this.t++] = x; else if(sh+k > BI_DB) { this_array[this.t-1] |= (x&((1<<(BI_DB-sh))-1))<>(BI_DB-sh)); } else this_array[this.t-1] |= x<= BI_DB) sh -= BI_DB; } if(k == 8 && (s[0]&0x80) != 0) { this.s = -1; if(sh > 0) this_array[this.t-1] |= ((1<<(BI_DB-sh))-1)< 0 && this_array[this.t-1] == c) --this.t; } // (public) return string representation in given radix function bnToString(b) { var this_array = this.array; if(this.s < 0) return "-"+this.negate().toString(b); var k; if(b == 16) k = 4; else if(b == 8) k = 3; else if(b == 2) k = 1; else if(b == 32) k = 5; else if(b == 4) k = 2; else return this.toRadix(b); var km = (1< 0) { if(p < BI_DB && (d = this_array[i]>>p) > 0) { m = true; r = int2char(d); } while(i >= 0) { if(p < k) { d = (this_array[i]&((1<>(p+=BI_DB-k); } else { d = (this_array[i]>>(p-=k))&km; if(p <= 0) { p += BI_DB; --i; } } if(d > 0) m = true; if(m) r += int2char(d); } } return m?r:"0"; } // (public) -this function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } // (public) |this| function bnAbs() { return (this.s<0)?this.negate():this; } // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a) { var this_array = this.array; var a_array = a.array; var r = this.s-a.s; if(r != 0) return r; var i = this.t; r = i-a.t; if(r != 0) return r; while(--i >= 0) if((r=this_array[i]-a_array[i]) != 0) return r; return 0; } // returns bit length of the integer x function nbits(x) { var r = 1, t; if((t=x>>>16) != 0) { x = t; r += 16; } if((t=x>>8) != 0) { x = t; r += 8; } if((t=x>>4) != 0) { x = t; r += 4; } if((t=x>>2) != 0) { x = t; r += 2; } if((t=x>>1) != 0) { x = t; r += 1; } return r; } // (public) return the number of bits in "this" function bnBitLength() { var this_array = this.array; if(this.t <= 0) return 0; return BI_DB*(this.t-1)+nbits(this_array[this.t-1]^(this.s&BI_DM)); } // (protected) r = this << n*DB function bnpDLShiftTo(n,r) { var this_array = this.array; var r_array = r.array; var i; for(i = this.t-1; i >= 0; --i) r_array[i+n] = this_array[i]; for(i = n-1; i >= 0; --i) r_array[i] = 0; r.t = this.t+n; r.s = this.s; } // (protected) r = this >> n*DB function bnpDRShiftTo(n,r) { var this_array = this.array; var r_array = r.array; for(var i = n; i < this.t; ++i) r_array[i-n] = this_array[i]; r.t = Math.max(this.t-n,0); r.s = this.s; } // (protected) r = this << n function bnpLShiftTo(n,r) { var this_array = this.array; var r_array = r.array; var bs = n%BI_DB; var cbs = BI_DB-bs; var bm = (1<= 0; --i) { r_array[i+ds+1] = (this_array[i]>>cbs)|c; c = (this_array[i]&bm)<= 0; --i) r_array[i] = 0; r_array[ds] = c; r.t = this.t+ds+1; r.s = this.s; r.clamp(); } // (protected) r = this >> n function bnpRShiftTo(n,r) { var this_array = this.array; var r_array = r.array; r.s = this.s; var ds = Math.floor(n/BI_DB); if(ds >= this.t) { r.t = 0; return; } var bs = n%BI_DB; var cbs = BI_DB-bs; var bm = (1<>bs; for(var i = ds+1; i < this.t; ++i) { r_array[i-ds-1] |= (this_array[i]&bm)<>bs; } if(bs > 0) r_array[this.t-ds-1] |= (this.s&bm)<>= BI_DB; } if(a.t < this.t) { c -= a.s; while(i < this.t) { c += this_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += this.s; } else { c += this.s; while(i < a.t) { c -= a_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c -= a.s; } r.s = (c<0)?-1:0; if(c < -1) r_array[i++] = BI_DV+c; else if(c > 0) r_array[i++] = c; r.t = i; r.clamp(); } // (protected) r = this * a, r != this,a (HAC 14.12) // "this" should be the larger one if appropriate. function bnpMultiplyTo(a,r) { var this_array = this.array; var r_array = r.array; var x = this.abs(), y = a.abs(); var y_array = y.array; var i = x.t; r.t = i+y.t; while(--i >= 0) r_array[i] = 0; for(i = 0; i < y.t; ++i) r_array[i+x.t] = x.am(0,y_array[i],r,i,0,x.t); r.s = 0; r.clamp(); if(this.s != a.s) BigInteger.ZERO.subTo(r,r); } // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { var x = this.abs(); var x_array = x.array; var r_array = r.array; var i = r.t = 2*x.t; while(--i >= 0) r_array[i] = 0; for(i = 0; i < x.t-1; ++i) { var c = x.am(i,x_array[i],r,2*i,0,1); if((r_array[i+x.t]+=x.am(i+1,2*x_array[i],r,2*i+1,c,x.t-i-1)) >= BI_DV) { r_array[i+x.t] -= BI_DV; r_array[i+x.t+1] = 1; } } if(r.t > 0) r_array[r.t-1] += x.am(i,x_array[i],r,2*i,0,1); r.s = 0; r.clamp(); } // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { var pm = m.abs(); if(pm.t <= 0) return; var pt = this.abs(); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); return; } if(r == null) r = nbi(); var y = nbi(), ts = this.s, ms = m.s; var pm_array = pm.array; var nsh = BI_DB-nbits(pm_array[pm.t-1]); // normalize modulus if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; var y_array = y.array; var y0 = y_array[ys-1]; if(y0 == 0) return; var yt = y0*(1<1)?y_array[ys-2]>>BI_F2:0); var d1 = BI_FV/yt, d2 = (1<= 0) { r_array[r.t++] = 1; r.subTo(t,r); } BigInteger.ONE.dlShiftTo(ys,t); t.subTo(y,y); // "negative" y so we can replace sub with am later while(y.t < ys) y_array[y.t++] = 0; while(--j >= 0) { // Estimate quotient digit var qd = (r_array[--i]==y0)?BI_DM:Math.floor(r_array[i]*d1+(r_array[i-1]+e)*d2); if((r_array[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out y.dlShiftTo(j,t); r.subTo(t,r); while(r_array[i] < --qd) r.subTo(t,r); } } if(q != null) { r.drShiftTo(ys,q); if(ts != ms) BigInteger.ZERO.subTo(q,q); } r.t = ys; r.clamp(); if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder if(ts < 0) BigInteger.ZERO.subTo(r,r); } // (public) this mod a function bnMod(a) { var r = nbi(); this.abs().divRemTo(a,null,r); if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); return r; } // Modular reduction using "classic" algorithm function Classic(m) { this.m = m; } function cConvert(x) { if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); else return x; } function cRevert(x) { return x; } function cReduce(x) { x.divRemTo(this.m,null,x); } function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } Classic.prototype.convert = cConvert; Classic.prototype.revert = cRevert; Classic.prototype.reduce = cReduce; Classic.prototype.mulTo = cMulTo; Classic.prototype.sqrTo = cSqrTo; // (protected) return "-1/this % 2^DB"; useful for Mont. reduction // justification: // xy == 1 (mod m) // xy = 1+km // xy(2-xy) = (1+km)(1-km) // x[y(2-xy)] = 1-k^2m^2 // x[y(2-xy)] == 1 (mod m^2) // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. // JS multiply "overflows" differently from C/C++, so care is needed here. function bnpInvDigit() { var this_array = this.array; if(this.t < 1) return 0; var x = this_array[0]; if((x&1) == 0) return 0; var y = x&3; // y == 1/x mod 2^2 y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 // last step - calculate inverse mod DV directly; // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints y = (y*(2-x*y%BI_DV))%BI_DV; // y == 1/x mod 2^dbits // we really want the negative inverse, and -DV < y < DV return (y>0)?BI_DV-y:-y; } // Montgomery reduction function Montgomery(m) { this.m = m; this.mp = m.invDigit(); this.mpl = this.mp&0x7fff; this.mph = this.mp>>15; this.um = (1<<(BI_DB-15))-1; this.mt2 = 2*m.t; } // xR mod m function montConvert(x) { var r = nbi(); x.abs().dlShiftTo(this.m.t,r); r.divRemTo(this.m,null,r); if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); return r; } // x/R mod m function montRevert(x) { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } // x = x/R mod m (HAC 14.32) function montReduce(x) { var x_array = x.array; while(x.t <= this.mt2) // pad x so am has enough room later x_array[x.t++] = 0; for(var i = 0; i < this.m.t; ++i) { // faster way of calculating u0 = x[i]*mp mod DV var j = x_array[i]&0x7fff; var u0 = (j*this.mpl+(((j*this.mph+(x_array[i]>>15)*this.mpl)&this.um)<<15))&BI_DM; // use am to combine the multiply-shift-add into one call j = i+this.m.t; x_array[j] += this.m.am(0,u0,x,i,0,this.m.t); // propagate carry while(x_array[j] >= BI_DV) { x_array[j] -= BI_DV; x_array[++j]++; } } x.clamp(); x.drShiftTo(this.m.t,x); if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); } // r = "x^2/R mod m"; x != r function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } // r = "xy/R mod m"; x,y != r function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } Montgomery.prototype.convert = montConvert; Montgomery.prototype.revert = montRevert; Montgomery.prototype.reduce = montReduce; Montgomery.prototype.mulTo = montMulTo; Montgomery.prototype.sqrTo = montSqrTo; // (protected) true iff this is even function bnpIsEven() { var this_array = this.array; return ((this.t>0)?(this_array[0]&1):this.s) == 0; } // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) function bnpExp(e,z) { if(e > 0xffffffff || e < 1) return BigInteger.ONE; var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; g.copyTo(r); while(--i >= 0) { z.sqrTo(r,r2); if((e&(1< 0) z.mulTo(r2,g,r); else { var t = r; r = r2; r2 = t; } } return z.revert(r); } // (public) this^e % m, 0 <= e < 2^32 function bnModPowInt(e,m) { var z; if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); return this.exp(e,z); } // protected BigInteger.prototype.copyTo = bnpCopyTo; BigInteger.prototype.fromInt = bnpFromInt; BigInteger.prototype.fromString = bnpFromString; BigInteger.prototype.clamp = bnpClamp; BigInteger.prototype.dlShiftTo = bnpDLShiftTo; BigInteger.prototype.drShiftTo = bnpDRShiftTo; BigInteger.prototype.lShiftTo = bnpLShiftTo; BigInteger.prototype.rShiftTo = bnpRShiftTo; BigInteger.prototype.subTo = bnpSubTo; BigInteger.prototype.multiplyTo = bnpMultiplyTo; BigInteger.prototype.squareTo = bnpSquareTo; BigInteger.prototype.divRemTo = bnpDivRemTo; BigInteger.prototype.invDigit = bnpInvDigit; BigInteger.prototype.isEven = bnpIsEven; BigInteger.prototype.exp = bnpExp; // public BigInteger.prototype.toString = bnToString; BigInteger.prototype.negate = bnNegate; BigInteger.prototype.abs = bnAbs; BigInteger.prototype.compareTo = bnCompareTo; BigInteger.prototype.bitLength = bnBitLength; BigInteger.prototype.mod = bnMod; BigInteger.prototype.modPowInt = bnModPowInt; // "constants" BigInteger.ZERO = nbv(0); BigInteger.ONE = nbv(1); // Copyright (c) 2005 Tom Wu // All Rights Reserved. // See "LICENSE" for details. // Extended JavaScript BN functions, required for RSA private ops. // (public) function bnClone() { var r = nbi(); this.copyTo(r); return r; } // (public) return value as integer function bnIntValue() { var this_array = this.array; if(this.s < 0) { if(this.t == 1) return this_array[0]-BI_DV; else if(this.t == 0) return -1; } else if(this.t == 1) return this_array[0]; else if(this.t == 0) return 0; // assumes 16 < DB < 32 return ((this_array[1]&((1<<(32-BI_DB))-1))<>24; } // (public) return value as short (assumes DB>=16) function bnShortValue() { var this_array = this.array; return (this.t==0)?this.s:(this_array[0]<<16)>>16; } // (protected) return x s.t. r^x < DV function bnpChunkSize(r) { return Math.floor(Math.LN2*BI_DB/Math.log(r)); } // (public) 0 if this == 0, 1 if this > 0 function bnSigNum() { var this_array = this.array; if(this.s < 0) return -1; else if(this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0; else return 1; } // (protected) convert to radix string function bnpToRadix(b) { if(b == null) b = 10; if(this.signum() == 0 || b < 2 || b > 36) return "0"; var cs = this.chunkSize(b); var a = Math.pow(b,cs); var d = nbv(a), y = nbi(), z = nbi(), r = ""; this.divRemTo(d,y,z); while(y.signum() > 0) { r = (a+z.intValue()).toString(b).substr(1) + r; y.divRemTo(d,y,z); } return z.intValue().toString(b) + r; } // (protected) convert from radix string function bnpFromRadix(s,b) { this.fromInt(0); if(b == null) b = 10; var cs = this.chunkSize(b); var d = Math.pow(b,cs), mi = false, j = 0, w = 0; for(var i = 0; i < s.length; ++i) { var x = intAt(s,i); if(x < 0) { if(s.charAt(i) == "-" && this.signum() == 0) mi = true; continue; } w = b*w+x; if(++j >= cs) { this.dMultiply(d); this.dAddOffset(w,0); j = 0; w = 0; } } if(j > 0) { this.dMultiply(Math.pow(b,j)); this.dAddOffset(w,0); } if(mi) BigInteger.ZERO.subTo(this,this); } // (protected) alternate constructor function bnpFromNumber(a,b,c) { if("number" == typeof b) { // new BigInteger(int,int,RNG) if(a < 2) this.fromInt(1); else { this.fromNumber(a,c); if(!this.testBit(a-1)) // force MSB set this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); if(this.isEven()) this.dAddOffset(1,0); // force odd while(!this.isProbablePrime(b)) { this.dAddOffset(2,0); if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); } } } else { // new BigInteger(int,RNG) var x = new Array(), t = a&7; x.length = (a>>3)+1; b.nextBytes(x); if(t > 0) x[0] &= ((1< 0) { if(p < BI_DB && (d = this_array[i]>>p) != (this.s&BI_DM)>>p) r[k++] = d|(this.s<<(BI_DB-p)); while(i >= 0) { if(p < 8) { d = (this_array[i]&((1<>(p+=BI_DB-8); } else { d = (this_array[i]>>(p-=8))&0xff; if(p <= 0) { p += BI_DB; --i; } } if((d&0x80) != 0) d |= -256; if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; if(k > 0 || d != this.s) r[k++] = d; } } return r; } function bnEquals(a) { return(this.compareTo(a)==0); } function bnMin(a) { return(this.compareTo(a)<0)?this:a; } function bnMax(a) { return(this.compareTo(a)>0)?this:a; } // (protected) r = this op a (bitwise) function bnpBitwiseTo(a,op,r) { var this_array = this.array; var a_array = a.array; var r_array = r.array; var i, f, m = Math.min(a.t,this.t); for(i = 0; i < m; ++i) r_array[i] = op(this_array[i],a_array[i]); if(a.t < this.t) { f = a.s&BI_DM; for(i = m; i < this.t; ++i) r_array[i] = op(this_array[i],f); r.t = this.t; } else { f = this.s&BI_DM; for(i = m; i < a.t; ++i) r_array[i] = op(f,a_array[i]); r.t = a.t; } r.s = op(this.s,a.s); r.clamp(); } // (public) this & a function op_and(x,y) { return x&y; } function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } // (public) this | a function op_or(x,y) { return x|y; } function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } // (public) this ^ a function op_xor(x,y) { return x^y; } function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } // (public) this & ~a function op_andnot(x,y) { return x&~y; } function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } // (public) ~this function bnNot() { var this_array = this.array; var r = nbi(); var r_array = r.array; for(var i = 0; i < this.t; ++i) r_array[i] = BI_DM&~this_array[i]; r.t = this.t; r.s = ~this.s; return r; } // (public) this << n function bnShiftLeft(n) { var r = nbi(); if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); return r; } // (public) this >> n function bnShiftRight(n) { var r = nbi(); if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); return r; } // return index of lowest 1-bit in x, x < 2^31 function lbit(x) { if(x == 0) return -1; var r = 0; if((x&0xffff) == 0) { x >>= 16; r += 16; } if((x&0xff) == 0) { x >>= 8; r += 8; } if((x&0xf) == 0) { x >>= 4; r += 4; } if((x&3) == 0) { x >>= 2; r += 2; } if((x&1) == 0) ++r; return r; } // (public) returns index of lowest 1-bit (or -1 if none) function bnGetLowestSetBit() { var this_array = this.array; for(var i = 0; i < this.t; ++i) if(this_array[i] != 0) return i*BI_DB+lbit(this_array[i]); if(this.s < 0) return this.t*BI_DB; return -1; } // return number of 1 bits in x function cbit(x) { var r = 0; while(x != 0) { x &= x-1; ++r; } return r; } // (public) return number of set bits function bnBitCount() { var r = 0, x = this.s&BI_DM; for(var i = 0; i < this.t; ++i) r += cbit(this_array[i]^x); return r; } // (public) true iff nth bit is set function bnTestBit(n) { var this_array = this.array; var j = Math.floor(n/BI_DB); if(j >= this.t) return(this.s!=0); return((this_array[j]&(1<<(n%BI_DB)))!=0); } // (protected) this op (1<>= BI_DB; } if(a.t < this.t) { c += a.s; while(i < this.t) { c += this_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += this.s; } else { c += this.s; while(i < a.t) { c += a_array[i]; r_array[i++] = c&BI_DM; c >>= BI_DB; } c += a.s; } r.s = (c<0)?-1:0; if(c > 0) r_array[i++] = c; else if(c < -1) r_array[i++] = BI_DV+c; r.t = i; r.clamp(); } // (public) this + a function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } // (public) this - a function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } // (public) this * a function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } // (public) this / a function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } // (public) this % a function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } // (public) [this/a,this%a] function bnDivideAndRemainder(a) { var q = nbi(), r = nbi(); this.divRemTo(a,q,r); return new Array(q,r); } // (protected) this *= n, this >= 0, 1 < n < DV function bnpDMultiply(n) { var this_array = this.array; this_array[this.t] = this.am(0,n-1,this,0,0,this.t); ++this.t; this.clamp(); } // (protected) this += n << w words, this >= 0 function bnpDAddOffset(n,w) { var this_array = this.array; while(this.t <= w) this_array[this.t++] = 0; this_array[w] += n; while(this_array[w] >= BI_DV) { this_array[w] -= BI_DV; if(++w >= this.t) this_array[this.t++] = 0; ++this_array[w]; } } // A "null" reducer function NullExp() {} function nNop(x) { return x; } function nMulTo(x,y,r) { x.multiplyTo(y,r); } function nSqrTo(x,r) { x.squareTo(r); } NullExp.prototype.convert = nNop; NullExp.prototype.revert = nNop; NullExp.prototype.mulTo = nMulTo; NullExp.prototype.sqrTo = nSqrTo; // (public) this^e function bnPow(e) { return this.exp(e,new NullExp()); } // (protected) r = lower n words of "this * a", a.t <= n // "this" should be the larger one if appropriate. function bnpMultiplyLowerTo(a,n,r) { var r_array = r.array; var a_array = a.array; var i = Math.min(this.t+a.t,n); r.s = 0; // assumes a,this >= 0 r.t = i; while(i > 0) r_array[--i] = 0; var j; for(j = r.t-this.t; i < j; ++i) r_array[i+this.t] = this.am(0,a_array[i],r,i,0,this.t); for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a_array[i],r,i,0,n-i); r.clamp(); } // (protected) r = "this * a" without lower n words, n > 0 // "this" should be the larger one if appropriate. function bnpMultiplyUpperTo(a,n,r) { var r_array = r.array; var a_array = a.array; --n; var i = r.t = this.t+a.t-n; r.s = 0; // assumes a,this >= 0 while(--i >= 0) r_array[i] = 0; for(i = Math.max(n-this.t,0); i < a.t; ++i) r_array[this.t+i-n] = this.am(n-i,a_array[i],r,0,0,this.t+i-n); r.clamp(); r.drShiftTo(1,r); } // Barrett modular reduction function Barrett(m) { // setup Barrett this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2*m.t,this.r2); this.mu = this.r2.divide(m); this.m = m; } function barrettConvert(x) { if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); else if(x.compareTo(this.m) < 0) return x; else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } } function barrettRevert(x) { return x; } // x = x mod m (HAC 14.42) function barrettReduce(x) { x.drShiftTo(this.m.t-1,this.r2); if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); x.subTo(this.r2,x); while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); } // r = x^2 mod m; x != r function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } // r = x*y mod m; x,y != r function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } Barrett.prototype.convert = barrettConvert; Barrett.prototype.revert = barrettRevert; Barrett.prototype.reduce = barrettReduce; Barrett.prototype.mulTo = barrettMulTo; Barrett.prototype.sqrTo = barrettSqrTo; // (public) this^e % m (HAC 14.85) function bnModPow(e,m) { var e_array = e.array; var i = e.bitLength(), k, r = nbv(1), z; if(i <= 0) return r; else if(i < 18) k = 1; else if(i < 48) k = 3; else if(i < 144) k = 4; else if(i < 768) k = 5; else k = 6; if(i < 8) z = new Classic(m); else if(m.isEven()) z = new Barrett(m); else z = new Montgomery(m); // precomputation var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { var g2 = nbi(); z.sqrTo(g[1],g2); while(n <= km) { g[n] = nbi(); z.mulTo(g2,g[n-2],g[n]); n += 2; } } var j = e.t-1, w, is1 = true, r2 = nbi(), t; i = nbits(e_array[j])-1; while(j >= 0) { if(i >= k1) w = (e_array[j]>>(i-k1))&km; else { w = (e_array[j]&((1<<(i+1))-1))<<(k1-i); if(j > 0) w |= e_array[j-1]>>(BI_DB+i-k1); } n = k; while((w&1) == 0) { w >>= 1; --n; } if((i -= n) < 0) { i += BI_DB; --j; } if(is1) { // ret == 1, don't bother squaring or multiplying it g[w].copyTo(r); is1 = false; } else { while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } z.mulTo(r2,g[w],r); } while(j >= 0 && (e_array[j]&(1< 0) { x.rShiftTo(g,x); y.rShiftTo(g,y); } while(x.signum() > 0) { if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); if(x.compareTo(y) >= 0) { x.subTo(y,x); x.rShiftTo(1,x); } else { y.subTo(x,y); y.rShiftTo(1,y); } } if(g > 0) y.lShiftTo(g,y); return y; } // (protected) this % n, n < 2^26 function bnpModInt(n) { var this_array = this.array; if(n <= 0) return 0; var d = BI_DV%n, r = (this.s<0)?n-1:0; if(this.t > 0) if(d == 0) r = this_array[0]%n; else for(var i = this.t-1; i >= 0; --i) r = (d*r+this_array[i])%n; return r; } // (public) 1/this % m (HAC 14.61) function bnModInverse(m) { var ac = m.isEven(); if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; var u = m.clone(), v = this.clone(); var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); while(u.signum() != 0) { while(u.isEven()) { u.rShiftTo(1,u); if(ac) { if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } a.rShiftTo(1,a); } else if(!b.isEven()) b.subTo(m,b); b.rShiftTo(1,b); } while(v.isEven()) { v.rShiftTo(1,v); if(ac) { if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } c.rShiftTo(1,c); } else if(!d.isEven()) d.subTo(m,d); d.rShiftTo(1,d); } if(u.compareTo(v) >= 0) { u.subTo(v,u); if(ac) a.subTo(c,a); b.subTo(d,b); } else { v.subTo(u,v); if(ac) c.subTo(a,c); d.subTo(b,d); } } if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; if(d.compareTo(m) >= 0) return d.subtract(m); if(d.signum() < 0) d.addTo(m,d); else return d; if(d.signum() < 0) return d.add(m); else return d; } var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; var lplim = (1<<26)/lowprimes[lowprimes.length-1]; // (public) test primality with certainty >= 1-.5^t function bnIsProbablePrime(t) { var i, x = this.abs(); var x_array = x.array; if(x.t == 1 && x_array[0] <= lowprimes[lowprimes.length-1]) { for(i = 0; i < lowprimes.length; ++i) if(x_array[0] == lowprimes[i]) return true; return false; } if(x.isEven()) return false; i = 1; while(i < lowprimes.length) { var m = lowprimes[i], j = i+1; while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; m = x.modInt(m); while(i < j) if(m%lowprimes[i++] == 0) return false; } return x.millerRabin(t); } // (protected) true if probably prime (HAC 4.24, Miller-Rabin) function bnpMillerRabin(t) { var n1 = this.subtract(BigInteger.ONE); var k = n1.getLowestSetBit(); if(k <= 0) return false; var r = n1.shiftRight(k); t = (t+1)>>1; if(t > lowprimes.length) t = lowprimes.length; var a = nbi(); for(var i = 0; i < t; ++i) { a.fromInt(lowprimes[i]); var y = a.modPow(r,this); if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { var j = 1; while(j++ < k && y.compareTo(n1) != 0) { y = y.modPowInt(2,this); if(y.compareTo(BigInteger.ONE) == 0) return false; } if(y.compareTo(n1) != 0) return false; } } return true; } // protected BigInteger.prototype.chunkSize = bnpChunkSize; BigInteger.prototype.toRadix = bnpToRadix; BigInteger.prototype.fromRadix = bnpFromRadix; BigInteger.prototype.fromNumber = bnpFromNumber; BigInteger.prototype.bitwiseTo = bnpBitwiseTo; BigInteger.prototype.changeBit = bnpChangeBit; BigInteger.prototype.addTo = bnpAddTo; BigInteger.prototype.dMultiply = bnpDMultiply; BigInteger.prototype.dAddOffset = bnpDAddOffset; BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; BigInteger.prototype.modInt = bnpModInt; BigInteger.prototype.millerRabin = bnpMillerRabin; // public BigInteger.prototype.clone = bnClone; BigInteger.prototype.intValue = bnIntValue; BigInteger.prototype.byteValue = bnByteValue; BigInteger.prototype.shortValue = bnShortValue; BigInteger.prototype.signum = bnSigNum; BigInteger.prototype.toByteArray = bnToByteArray; BigInteger.prototype.equals = bnEquals; BigInteger.prototype.min = bnMin; BigInteger.prototype.max = bnMax; BigInteger.prototype.and = bnAnd; BigInteger.prototype.or = bnOr; BigInteger.prototype.xor = bnXor; BigInteger.prototype.andNot = bnAndNot; BigInteger.prototype.not = bnNot; BigInteger.prototype.shiftLeft = bnShiftLeft; BigInteger.prototype.shiftRight = bnShiftRight; BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; BigInteger.prototype.bitCount = bnBitCount; BigInteger.prototype.testBit = bnTestBit; BigInteger.prototype.setBit = bnSetBit; BigInteger.prototype.clearBit = bnClearBit; BigInteger.prototype.flipBit = bnFlipBit; BigInteger.prototype.add = bnAdd; BigInteger.prototype.subtract = bnSubtract; BigInteger.prototype.multiply = bnMultiply; BigInteger.prototype.divide = bnDivide; BigInteger.prototype.remainder = bnRemainder; BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; BigInteger.prototype.modPow = bnModPow; BigInteger.prototype.modInverse = bnModInverse; BigInteger.prototype.pow = bnPow; BigInteger.prototype.gcd = bnGCD; BigInteger.prototype.isProbablePrime = bnIsProbablePrime; // BigInteger interfaces not implemented in jsbn: // BigInteger(int signum, byte[] magnitude) // double doubleValue() // float floatValue() // int hashCode() // long longValue() // static BigInteger valueOf(long val) // prng4.js - uses Arcfour as a PRNG function Arcfour() { this.i = 0; this.j = 0; this.S = new Array(); } // Initialize arcfour context from key, an array of ints, each from [0..255] function ARC4init(key) { var i, j, t; for(i = 0; i < 256; ++i) this.S[i] = i; j = 0; for(i = 0; i < 256; ++i) { j = (j + this.S[i] + key[i % key.length]) & 255; t = this.S[i]; this.S[i] = this.S[j]; this.S[j] = t; } this.i = 0; this.j = 0; } function ARC4next() { var t; this.i = (this.i + 1) & 255; this.j = (this.j + this.S[this.i]) & 255; t = this.S[this.i]; this.S[this.i] = this.S[this.j]; this.S[this.j] = t; return this.S[(t + this.S[this.i]) & 255]; } Arcfour.prototype.init = ARC4init; Arcfour.prototype.next = ARC4next; // Plug in your RNG constructor here function prng_newstate() { return new Arcfour(); } // Pool size must be a multiple of 4 and greater than 32. // An array of bytes the size of the pool will be passed to init() var rng_psize = 256; // Random number generator - requires a PRNG backend, e.g. prng4.js // For best results, put code like // // in your main HTML document. var rng_state; var rng_pool; var rng_pptr; // Mix in a 32-bit integer into the pool function rng_seed_int(x) { rng_pool[rng_pptr++] ^= x & 255; rng_pool[rng_pptr++] ^= (x >> 8) & 255; rng_pool[rng_pptr++] ^= (x >> 16) & 255; rng_pool[rng_pptr++] ^= (x >> 24) & 255; if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; } // Mix in the current time (w/milliseconds) into the pool function rng_seed_time() { // Use pre-computed date to avoid making the benchmark // results dependent on the current date. rng_seed_int(1122926989487); } // Initialize the pool with junk if needed. if(rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; while(rng_pptr < rng_psize) { // extract some randomness from Math.random() t = Math.floor(65536 * Math.random()); rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t & 255; } rng_pptr = 0; rng_seed_time(); //rng_seed_int(window.screenX); //rng_seed_int(window.screenY); } function rng_get_byte() { if(rng_state == null) { rng_seed_time(); rng_state = prng_newstate(); rng_state.init(rng_pool); for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) rng_pool[rng_pptr] = 0; rng_pptr = 0; //rng_pool = null; } // TODO: allow reseeding after first request return rng_state.next(); } function rng_get_bytes(ba) { var i; for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); } function SecureRandom() {} SecureRandom.prototype.nextBytes = rng_get_bytes; // Depends on jsbn.js and rng.js // convert a (hex) string to a bignum object function parseBigInt(str,r) { return new BigInteger(str,r); } function linebrk(s,n) { var ret = ""; var i = 0; while(i + n < s.length) { ret += s.substring(i,i+n) + "\n"; i += n; } return ret + s.substring(i,s.length); } function byte2Hex(b) { if(b < 0x10) return "0" + b.toString(16); else return b.toString(16); } // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint function pkcs1pad2(s,n) { if(n < s.length + 11) { alert("Message too long for RSA"); return null; } var ba = new Array(); var i = s.length - 1; while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--); ba[--n] = 0; var rng = new SecureRandom(); var x = new Array(); while(n > 2) { // random non-zero pad x[0] = 0; while(x[0] == 0) rng.nextBytes(x); ba[--n] = x[0]; } ba[--n] = 2; ba[--n] = 0; return new BigInteger(ba); } // "empty" RSA key constructor function RSAKey() { this.n = null; this.e = 0; this.d = null; this.p = null; this.q = null; this.dmp1 = null; this.dmq1 = null; this.coeff = null; } // Set the public key fields N and e from hex strings function RSASetPublic(N,E) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); } else alert("Invalid RSA public key"); } // Perform raw public operation on "x": return x^e (mod n) function RSADoPublic(x) { return x.modPowInt(this.e, this.n); } // Return the PKCS#1 RSA encryption of "text" as an even-length hex string function RSAEncrypt(text) { var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); if(m == null) return null; var c = this.doPublic(m); if(c == null) return null; var h = c.toString(16); if((h.length & 1) == 0) return h; else return "0" + h; } // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string //function RSAEncryptB64(text) { // var h = this.encrypt(text); // if(h) return hex2b64(h); else return null; //} // protected RSAKey.prototype.doPublic = RSADoPublic; // public RSAKey.prototype.setPublic = RSASetPublic; RSAKey.prototype.encrypt = RSAEncrypt; //RSAKey.prototype.encrypt_b64 = RSAEncryptB64; // Depends on rsa.js and jsbn2.js // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext function pkcs1unpad2(d,n) { var b = d.toByteArray(); var i = 0; while(i < b.length && b[i] == 0) ++i; if(b.length-i != n-1 || b[i] != 2) return null; ++i; while(b[i] != 0) if(++i >= b.length) return null; var ret = ""; while(++i < b.length) ret += String.fromCharCode(b[i]); return ret; } // Set the private key fields N, e, and d from hex strings function RSASetPrivate(N,E,D) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); this.d = parseBigInt(D,16); } else alert("Invalid RSA private key"); } // Set the private key fields N, e, d and CRT params from hex strings function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) { if(N != null && E != null && N.length > 0 && E.length > 0) { this.n = parseBigInt(N,16); this.e = parseInt(E,16); this.d = parseBigInt(D,16); this.p = parseBigInt(P,16); this.q = parseBigInt(Q,16); this.dmp1 = parseBigInt(DP,16); this.dmq1 = parseBigInt(DQ,16); this.coeff = parseBigInt(C,16); } else alert("Invalid RSA private key"); } // Generate a new random private key B bits long, using public expt E function RSAGenerate(B,E) { var rng = new SecureRandom(); var qs = B>>1; this.e = parseInt(E,16); var ee = new BigInteger(E,16); for(;;) { for(;;) { this.p = new BigInteger(B-qs,1,rng); if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break; } for(;;) { this.q = new BigInteger(qs,1,rng); if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break; } if(this.p.compareTo(this.q) <= 0) { var t = this.p; this.p = this.q; this.q = t; } var p1 = this.p.subtract(BigInteger.ONE); var q1 = this.q.subtract(BigInteger.ONE); var phi = p1.multiply(q1); if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) { this.n = this.p.multiply(this.q); this.d = ee.modInverse(phi); this.dmp1 = this.d.mod(p1); this.dmq1 = this.d.mod(q1); this.coeff = this.q.modInverse(this.p); break; } } } // Perform raw private operation on "x": return x^d (mod n) function RSADoPrivate(x) { if(this.p == null || this.q == null) return x.modPow(this.d, this.n); // TODO: re-calculate any missing CRT params var xp = x.mod(this.p).modPow(this.dmp1, this.p); var xq = x.mod(this.q).modPow(this.dmq1, this.q); while(xp.compareTo(xq) < 0) xp = xp.add(this.p); return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq); } // Return the PKCS#1 RSA decryption of "ctext". // "ctext" is an even-length hex string and the output is a plain string. function RSADecrypt(ctext) { var c = parseBigInt(ctext, 16); var m = this.doPrivate(c); if(m == null) return null; return pkcs1unpad2(m, (this.n.bitLength()+7)>>3); } // Return the PKCS#1 RSA decryption of "ctext". // "ctext" is a Base64-encoded string and the output is a plain string. //function RSAB64Decrypt(ctext) { // var h = b64tohex(ctext); // if(h) return this.decrypt(h); else return null; //} // protected RSAKey.prototype.doPrivate = RSADoPrivate; // public RSAKey.prototype.setPrivate = RSASetPrivate; RSAKey.prototype.setPrivateEx = RSASetPrivateEx; RSAKey.prototype.generate = RSAGenerate; RSAKey.prototype.decrypt = RSADecrypt; //RSAKey.prototype.b64_decrypt = RSAB64Decrypt; nValue="a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3"; eValue="10001"; dValue="8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161"; pValue="d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d"; qValue="cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f"; dmp1Value="1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25"; dmq1Value="3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd"; coeffValue="3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250"; setupEngine(am3, 28); var TEXT = "The quick brown fox jumped over the extremely lazy frog! " + "Now is the time for all good men to come to the party."; var encrypted; function encrypt() { var RSA = new RSAKey(); RSA.setPublic(nValue, eValue); RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); encrypted = RSA.encrypt(TEXT); } function decrypt() { var RSA = new RSAKey(); RSA.setPublic(nValue, eValue); RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue); var decrypted = RSA.decrypt(encrypted); if (decrypted != TEXT) { throw new Error("Crypto operation failed"); } } closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/README.txt0000644000175000017500000000500714433667662026024 0ustar apoapoV8 Benchmark Suite ================== This is the V8 benchmark suite: A collection of pure JavaScript benchmarks that we have used to tune V8. The licenses for the individual benchmarks are included in the JavaScript files. In addition to the benchmarks, the suite consists of the benchmark framework (base.js), which must be loaded before any of the individual benchmark files, and two benchmark runners: An HTML version (run.html) and a standalone JavaScript version (run.js). Changes From Version 1 To Version 2 =================================== For version 2 the crypto benchmark was fixed. Previously, the decryption stage was given plaintext as input, which resulted in an error. Now, the decryption stage is given the output of the encryption stage as input. The result is checked against the original plaintext. For this to give the correct results the crypto objects are reset for each iteration of the benchmark. In addition, the size of the plain text has been increased a little and the use of Math.random() and new Date() to build an RNG pool has been removed. Other benchmarks were fixed to do elementary verification of the results of their calculations. This is to avoid accidentally obtaining scores that are the result of an incorrect JavaScript engine optimization. Changes From Version 2 To Version 3 =================================== Version 3 adds a new benchmark, RegExp. The RegExp benchmark is generated by loading 50 of the most popular pages on the web and logging all regexp operations performed. Each operation is given a weight that is calculated from an estimate of the popularity of the pages where it occurs and the number of times it is executed while loading each page. Finally the literal letters in the data are encoded using ROT13 in a way that does not affect how the regexps match their input. Changes from Version 3 to Version 4 =================================== The Splay benchmark is a newcomer in version 4. It manipulates a splay tree by adding and removing data nodes, thus exercising the memory management subsystem of the JavaScript engine. Furthermore, all the unused parts of the Prototype library were removed from the RayTrace benchmark. This does not affect the running of the benchmark. Changes from Version 4 to Version 5 =================================== Removed duplicate line in random seed code, and changed the name of the Object.prototype.inherits function in the DeltaBlue benchmark to inheritsFrom to avoid name clashes when running in Chromium with extensions enabled. closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/splay.js0000644000175000017500000002426714433667662026025 0ustar apoapo// Copyright 2009 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This benchmark is based on a JavaScript log processing module used // by the V8 profiler to generate execution time profiles for runs of // JavaScript applications, and it effectively measures how fast the // JavaScript engine is at allocating nodes and reclaiming the memory // used for old nodes. Because of the way splay trees work, the engine // also has to deal with a lot of changes to the large tree object // graph. var Splay = new BenchmarkSuite('Splay', 126125, [ new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown) ]); // Configuration. var kSplayTreeSize = 8000; var kSplayTreeModifications = 80; var kSplayTreePayloadDepth = 5; var splayTree = null; function GeneratePayloadTree(depth, key) { if (depth == 0) { return { array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], string : 'String for key ' + key + ' in leaf node' }; } else { return { left: GeneratePayloadTree(depth - 1, key), right: GeneratePayloadTree(depth - 1, key) }; } } function GenerateKey() { // The benchmark framework guarantees that Math.random is // deterministic; see base.js. return Math.random(); } function InsertNewNode() { // Insert new node with a unique key. var key; do { key = GenerateKey(); } while (splayTree.find(key) != null); splayTree.insert(key, GeneratePayloadTree(kSplayTreePayloadDepth, key)); return key; } function SplaySetup() { splayTree = new SplayTree(); for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode(); } function SplayTearDown() { // Allow the garbage collector to reclaim the memory // used by the splay tree no matter how we exit the // tear down function. var keys = splayTree.exportKeys(); splayTree = null; // Verify that the splay tree has the right size. var length = keys.length; if (length != kSplayTreeSize) { throw new Error("Splay tree has wrong size"); } // Verify that the splay tree has sorted, unique keys. for (var i = 0; i < length - 1; i++) { if (keys[i] >= keys[i + 1]) { throw new Error("Splay tree not sorted"); } } } function SplayRun() { // Replace a few nodes in the splay tree. for (var i = 0; i < kSplayTreeModifications; i++) { var key = InsertNewNode(); var greatest = splayTree.findGreatestLessThan(key); if (greatest == null) splayTree.remove(key); else splayTree.remove(greatest.key); } } /** * Constructs a Splay tree. A splay tree is a self-balancing binary * search tree with the additional property that recently accessed * elements are quick to access again. It performs basic operations * such as insertion, look-up and removal in O(log(n)) amortized time. * * @constructor */ function SplayTree() { }; /** * Pointer to the root node of the tree. * * @type {SplayTree.Node} * @private */ SplayTree.prototype.root_ = null; /** * @return {boolean} Whether the tree is empty. */ SplayTree.prototype.isEmpty = function() { return !this.root_; }; /** * Inserts a node into the tree with the specified key and value if * the tree does not already contain a node with the specified key. If * the value is inserted, it becomes the root of the tree. * * @param {number} key Key to insert into the tree. * @param {*} value Value to insert into the tree. */ SplayTree.prototype.insert = function(key, value) { if (this.isEmpty()) { this.root_ = new SplayTree.Node(key, value); return; } // Splay on the key to move the last node on the search path for // the key to the root of the tree. this.splay_(key); if (this.root_.key == key) { return; } var node = new SplayTree.Node(key, value); if (key > this.root_.key) { node.left = this.root_; node.right = this.root_.right; this.root_.right = null; } else { node.right = this.root_; node.left = this.root_.left; this.root_.left = null; } this.root_ = node; }; /** * Removes a node with the specified key from the tree if the tree * contains a node with this key. The removed node is returned. If the * key is not found, an exception is thrown. * * @param {number} key Key to find and remove from the tree. * @return {SplayTree.Node} The removed node. */ SplayTree.prototype.remove = function(key) { if (this.isEmpty()) { throw Error('Key not found: ' + key); } this.splay_(key); if (this.root_.key != key) { throw Error('Key not found: ' + key); } var removed = this.root_; if (!this.root_.left) { this.root_ = this.root_.right; } else { var right = this.root_.right; this.root_ = this.root_.left; // Splay to make sure that the new root has an empty right child. this.splay_(key); // Insert the original right child as the right child of the new // root. this.root_.right = right; } return removed; }; /** * Returns the node having the specified key or null if the tree doesn't contain * a node with the specified key. * * @param {number} key Key to find in the tree. * @return {SplayTree.Node} Node having the specified key. */ SplayTree.prototype.find = function(key) { if (this.isEmpty()) { return null; } this.splay_(key); return this.root_.key == key ? this.root_ : null; }; /** * @return {SplayTree.Node} Node having the maximum key value that * is less or equal to the specified key value. */ SplayTree.prototype.findGreatestLessThan = function(key) { if (this.isEmpty()) { return null; } // Splay on the key to move the node with the given key or the last // node on the search path to the top of the tree. this.splay_(key); // Now the result is either the root node or the greatest node in // the left subtree. if (this.root_.key <= key) { return this.root_; } else if (this.root_.left) { return this.findMax(this.root_.left); } else { return null; } }; /** * @return {Array<*>} An array containing all the keys of tree's nodes. */ SplayTree.prototype.exportKeys = function() { var result = []; if (!this.isEmpty()) { this.root_.traverse_(function(node) { result.push(node.key); }); } return result; }; /** * Perform the splay operation for the given key. Moves the node with * the given key to the top of the tree. If no node has the given * key, the last node on the search path is moved to the top of the * tree. This is the simplified top-down splaying algorithm from: * "Self-adjusting Binary Search Trees" by Sleator and Tarjan * * @param {number} key Key to splay the tree on. * @private */ SplayTree.prototype.splay_ = function(key) { if (this.isEmpty()) { return; } // Create a dummy node. The use of the dummy node is a bit // counter-intuitive: The right child of the dummy node will hold // the L tree of the algorithm. The left child of the dummy node // will hold the R tree of the algorithm. Using a dummy node, left // and right will always be nodes and we avoid special cases. var dummy, left, right; dummy = left = right = new SplayTree.Node(null, null); var current = this.root_; while (true) { if (key < current.key) { if (!current.left) { break; } if (key < current.left.key) { // Rotate right. var tmp = current.left; current.left = tmp.right; tmp.right = current; current = tmp; if (!current.left) { break; } } // Link right. right.left = current; right = current; current = current.left; } else if (key > current.key) { if (!current.right) { break; } if (key > current.right.key) { // Rotate left. var tmp = current.right; current.right = tmp.left; tmp.left = current; current = tmp; if (!current.right) { break; } } // Link left. left.right = current; left = current; current = current.right; } else { break; } } // Assemble. left.right = current.left; right.left = current.right; current.left = dummy.right; current.right = dummy.left; this.root_ = current; }; /** * Constructs a Splay tree node. * * @param {number} key Key. * @param {*} value Value. */ SplayTree.Node = function(key, value) { this.key = key; this.value = value; }; /** * @type {SplayTree.Node} */ SplayTree.Node.prototype.left = null; /** * @type {SplayTree.Node} */ SplayTree.Node.prototype.right = null; /** * Performs an ordered traversal of the subtree starting at * this SplayTree.Node. * * @param {function(SplayTree.Node)} f Visitor function. * @private */ SplayTree.Node.prototype.traverse_ = function(f) { var current = this; while (current) { var left = current.left; if (left) left.traverse_(f); f(current); current = current.right; } }; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/earley-boyer.js0000644000175000017500000057534114433667662027300 0ustar apoapo// This file is automatically generated by scheme2js, except for the // benchmark harness code at the beginning and end of the file. var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 765819, [ new Benchmark("Earley", function () { BgL_earleyzd2benchmarkzd2(); }), new Benchmark("Boyer", function () { BgL_nboyerzd2benchmarkzd2(); }) ]); /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /************* GENERATED FILE - DO NOT EDIT *************/ /* * To use write/prints/... the default-output port has to be set first. * Simply setting SC_DEFAULT_OUT and SC_ERROR_OUT to the desired values * should do the trick. * In the following example the std-out and error-port are redirected to * a DIV. function initRuntime() { function escapeHTML(s) { var tmp = s; tmp = tmp.replace(/&/g, "&"); tmp = tmp.replace(//g, ">"); tmp = tmp.replace(/ /g, " "); tmp = tmp.replace(/\n/g, "
      "); tmp = tmp.replace(/\t/g, "    "); return tmp; } document.write("
      "); SC_DEFAULT_OUT = new sc_GenericOutputPort( function(s) { var stdout = document.getElementById('stdout'); stdout.innerHTML = stdout.innerHTML + escapeHTML(s); }); SC_ERROR_OUT = SC_DEFAULT_OUT; } */ function sc_print_debug() { sc_print.apply(null, arguments); } /*** META ((export *js*)) */ var sc_JS_GLOBALS = this; var __sc_LINE=-1; var __sc_FILE=""; /*** META ((export #t)) */ function sc_alert() { var len = arguments.length; var s = ""; var i; for( i = 0; i < len; i++ ) { s += sc_toDisplayString(arguments[ i ]); } return alert( s ); } /*** META ((export #t)) */ function sc_typeof( x ) { return typeof x; } /*** META ((export #t)) */ function sc_error() { var a = [sc_jsstring2symbol("*error*")]; for (var i = 0; i < arguments.length; i++) { a[i+1] = arguments[i]; } throw a; } /*** META ((export #t) (peephole (prefix "throw "))) */ function sc_raise(obj) { throw obj; } /*** META ((export with-handler-lambda)) */ function sc_withHandlerLambda(handler, body) { try { return body(); } catch(e) { if (!e._internalException) return handler(e); else throw e; } } var sc_properties = new Object(); /*** META ((export #t)) */ function sc_putpropBang(sym, key, val) { var ht = sc_properties[sym]; if (!ht) { ht = new Object(); sc_properties[sym] = ht; } ht[key] = val; } /*** META ((export #t)) */ function sc_getprop(sym, key) { var ht = sc_properties[sym]; if (ht) { if (key in ht) return ht[key]; else return false; } else return false; } /*** META ((export #t)) */ function sc_rempropBang(sym, key) { var ht = sc_properties[sym]; if (ht) delete ht[key]; } /*** META ((export #t)) */ function sc_any2String(o) { return jsstring2string(sc_toDisplayString(o)); } /*** META ((export #t) (peephole (infix 2 2 "===")) (type bool)) */ function sc_isEqv(o1, o2) { return (o1 === o2); } /*** META ((export #t) (peephole (infix 2 2 "===")) (type bool)) */ function sc_isEq(o1, o2) { return (o1 === o2); } /*** META ((export #t) (type bool)) */ function sc_isNumber(n) { return (typeof n === "number"); } /*** META ((export #t) (type bool)) */ function sc_isComplex(n) { return sc_isNumber(n); } /*** META ((export #t) (type bool)) */ function sc_isReal(n) { return sc_isNumber(n); } /*** META ((export #t) (type bool)) */ function sc_isRational(n) { return sc_isReal(n); } /*** META ((export #t) (type bool)) */ function sc_isInteger(n) { return (parseInt(n) === n); } /*** META ((export #t) (type bool) (peephole (postfix ", false"))) */ // we don't have exact numbers... function sc_isExact(n) { return false; } /*** META ((export #t) (peephole (postfix ", true")) (type bool)) */ function sc_isInexact(n) { return true; } /*** META ((export = =fx =fl) (type bool) (peephole (infix 2 2 "==="))) */ function sc_equal(x) { for (var i = 1; i < arguments.length; i++) if (x !== arguments[i]) return false; return true; } /*** META ((export < = arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export > >fx >fl) (type bool) (peephole (infix 2 2 ">"))) */ function sc_greater(x, y) { for (var i = 1; i < arguments.length; i++) { if (x <= arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export <= <=fx <=fl) (type bool) (peephole (infix 2 2 "<="))) */ function sc_lessEqual(x, y) { for (var i = 1; i < arguments.length; i++) { if (x > arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export >= >=fl >=fx) (type bool) (peephole (infix 2 2 ">="))) */ function sc_greaterEqual(x, y) { for (var i = 1; i < arguments.length; i++) { if (x < arguments[i]) return false; x = arguments[i]; } return true; } /*** META ((export #t) (type bool) (peephole (postfix "=== 0"))) */ function sc_isZero(x) { return (x === 0); } /*** META ((export #t) (type bool) (peephole (postfix "> 0"))) */ function sc_isPositive(x) { return (x > 0); } /*** META ((export #t) (type bool) (peephole (postfix "< 0"))) */ function sc_isNegative(x) { return (x < 0); } /*** META ((export #t) (type bool) (peephole (postfix "%2===1"))) */ function sc_isOdd(x) { return (x % 2 === 1); } /*** META ((export #t) (type bool) (peephole (postfix "%2===0"))) */ function sc_isEven(x) { return (x % 2 === 0); } /*** META ((export #t)) */ var sc_max = Math.max; /*** META ((export #t)) */ var sc_min = Math.min; /*** META ((export + +fx +fl) (peephole (infix 0 #f "+" "0"))) */ function sc_plus() { var sum = 0; for (var i = 0; i < arguments.length; i++) sum += arguments[i]; return sum; } /*** META ((export * *fx *fl) (peephole (infix 0 #f "*" "1"))) */ function sc_multi() { var product = 1; for (var i = 0; i < arguments.length; i++) product *= arguments[i]; return product; } /*** META ((export - -fx -fl) (peephole (minus))) */ function sc_minus(x) { if (arguments.length === 1) return -x; else { var res = x; for (var i = 1; i < arguments.length; i++) res -= arguments[i]; return res; } } /*** META ((export / /fl) (peephole (div))) */ function sc_div(x) { if (arguments.length === 1) return 1/x; else { var res = x; for (var i = 1; i < arguments.length; i++) res /= arguments[i]; return res; } } /*** META ((export #t)) */ var sc_abs = Math.abs; /*** META ((export quotient /fx) (peephole (hole 2 "parseInt(" x "/" y ")"))) */ function sc_quotient(x, y) { return parseInt(x / y); } /*** META ((export #t) (peephole (infix 2 2 "%"))) */ function sc_remainder(x, y) { return x % y; } /*** META ((export #t) (peephole (modulo))) */ function sc_modulo(x, y) { var remainder = x % y; // if they don't have the same sign if ((remainder * y) < 0) return remainder + y; else return remainder; } function sc_euclid_gcd(a, b) { var temp; if (a === 0) return b; if (b === 0) return a; if (a < 0) {a = -a;}; if (b < 0) {b = -b;}; if (b > a) {temp = a; a = b; b = temp;}; while (true) { a %= b; if(a === 0) {return b;}; b %= a; if(b === 0) {return a;}; }; return b; } /*** META ((export #t)) */ function sc_gcd() { var gcd = 0; for (var i = 0; i < arguments.length; i++) gcd = sc_euclid_gcd(gcd, arguments[i]); return gcd; } /*** META ((export #t)) */ function sc_lcm() { var lcm = 1; for (var i = 0; i < arguments.length; i++) { var f = Math.round(arguments[i] / sc_euclid_gcd(arguments[i], lcm)); lcm *= Math.abs(f); } return lcm; } // LIMITATION: numerator and denominator don't make sense in floating point world. //var SC_MAX_DECIMALS = 1000000 // // function sc_numerator(x) { // var rounded = Math.round(x * SC_MAX_DECIMALS); // return Math.round(rounded / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); // } // function sc_denominator(x) { // var rounded = Math.round(x * SC_MAX_DECIMALS); // return Math.round(SC_MAX_DECIMALS / sc_euclid_gcd(rounded, SC_MAX_DECIMALS)); // } /*** META ((export #t)) */ var sc_floor = Math.floor; /*** META ((export #t)) */ var sc_ceiling = Math.ceil; /*** META ((export #t)) */ var sc_truncate = parseInt; /*** META ((export #t)) */ var sc_round = Math.round; // LIMITATION: sc_rationalize doesn't make sense in a floating point world. /*** META ((export #t)) */ var sc_exp = Math.exp; /*** META ((export #t)) */ var sc_log = Math.log; /*** META ((export #t)) */ var sc_sin = Math.sin; /*** META ((export #t)) */ var sc_cos = Math.cos; /*** META ((export #t)) */ var sc_tan = Math.tan; /*** META ((export #t)) */ var sc_asin = Math.asin; /*** META ((export #t)) */ var sc_acos = Math.acos; /*** META ((export #t)) */ var sc_atan = Math.atan; /*** META ((export #t)) */ var sc_sqrt = Math.sqrt; /*** META ((export #t)) */ var sc_expt = Math.pow; // LIMITATION: we don't have complex numbers. // LIMITATION: the following functions are hence not implemented. // LIMITATION: make-rectangular, make-polar, real-part, imag-part, magnitude, angle // LIMITATION: 2 argument atan /*** META ((export #t) (peephole (id))) */ function sc_exact2inexact(x) { return x; } /*** META ((export #t) (peephole (id))) */ function sc_inexact2exact(x) { return x; } function sc_number2jsstring(x, radix) { if (radix) return x.toString(radix); else return x.toString(); } function sc_jsstring2number(s, radix) { if (s === "") return false; if (radix) { var t = parseInt(s, radix); if (!t && t !== 0) return false; // verify that each char is in range. (parseInt ignores leading // white and trailing chars) var allowedChars = "01234567890abcdefghijklmnopqrstuvwxyz".substring(0, radix+1); if ((new RegExp("^["+allowedChars+"]*$", "i")).test(s)) return t; else return false; } else { var t = +s; // does not ignore trailing chars. if (!t && t !== 0) return false; // simply verify that first char is not whitespace. var c = s.charAt(0); // if +c is 0, but the char is not "0", then we have a whitespace. if (+c === 0 && c !== "0") return false; return t; } } /*** META ((export #t) (type bool) (peephole (not))) */ function sc_not(b) { return b === false; } /*** META ((export #t) (type bool)) */ function sc_isBoolean(b) { return (b === true) || (b === false); } function sc_Pair(car, cdr) { this.car = car; this.cdr = cdr; } sc_Pair.prototype.toString = function() { return sc_toDisplayString(this); }; sc_Pair.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { var current = this; var res = "("; while(true) { res += writeOrDisplay(current.car); if (sc_isPair(current.cdr)) { res += " "; current = current.cdr; } else if (current.cdr !== null) { res += " . " + writeOrDisplay(current.cdr); break; } else // current.cdr == null break; } res += ")"; return res; }; sc_Pair.prototype.sc_toDisplayString = function() { return this.sc_toWriteOrDisplayString(sc_toDisplayString); }; sc_Pair.prototype.sc_toWriteString = function() { return this.sc_toWriteOrDisplayString(sc_toWriteString); }; // sc_Pair.prototype.sc_toWriteCircleString in IO.js /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_Pair"))) */ function sc_isPair(p) { return (p instanceof sc_Pair); } function sc_isPairEqual(p1, p2, comp) { return (comp(p1.car, p2.car) && comp(p1.cdr, p2.cdr)); } /*** META ((export #t) (peephole (hole 2 "new sc_Pair(" car ", " cdr ")"))) */ function sc_cons(car, cdr) { return new sc_Pair(car, cdr); } /*** META ((export cons*)) */ function sc_consStar() { var res = arguments[arguments.length - 1]; for (var i = arguments.length-2; i >= 0; i--) res = new sc_Pair(arguments[i], res); return res; } /*** META ((export #t) (peephole (postfix ".car"))) */ function sc_car(p) { return p.car; } /*** META ((export #t) (peephole (postfix ".cdr"))) */ function sc_cdr(p) { return p.cdr; } /*** META ((export #t) (peephole (hole 2 p ".car = " val))) */ function sc_setCarBang(p, val) { p.car = val; } /*** META ((export #t) (peephole (hole 2 p ".cdr = " val))) */ function sc_setCdrBang(p, val) { p.cdr = val; } /*** META ((export #t) (peephole (postfix ".car.car"))) */ function sc_caar(p) { return p.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.car"))) */ function sc_cadr(p) { return p.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.cdr"))) */ function sc_cdar(p) { return p.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr"))) */ function sc_cddr(p) { return p.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.car"))) */ function sc_caaar(p) { return p.car.car.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.car"))) */ function sc_cadar(p) { return p.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.car"))) */ function sc_caadr(p) { return p.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car"))) */ function sc_caddr(p) { return p.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.car.cdr"))) */ function sc_cdaar(p) { return p.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr"))) */ function sc_cdadr(p) { return p.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr"))) */ function sc_cddar(p) { return p.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr"))) */ function sc_cdddr(p) { return p.cdr.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.car.car"))) */ function sc_caaaar(p) { return p.car.car.car.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.car.car"))) */ function sc_caadar(p) { return p.car.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.car.car"))) */ function sc_caaadr(p) { return p.cdr.car.car.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car.car"))) */ function sc_caaddr(p) { return p.cdr.cdr.car.car; } /*** META ((export #t) (peephole (postfix ".car.car.car.cdr"))) */ function sc_cdaaar(p) { return p.car.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.car.cdr"))) */ function sc_cdadar(p) { return p.car.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.car.cdr"))) */ function sc_cdaadr(p) { return p.cdr.car.car.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.car.cdr"))) */ function sc_cdaddr(p) { return p.cdr.cdr.car.cdr; } /*** META ((export #t) (peephole (postfix ".car.car.cdr.car"))) */ function sc_cadaar(p) { return p.car.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr.car"))) */ function sc_caddar(p) { return p.car.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr.car"))) */ function sc_cadadr(p) { return p.cdr.car.cdr.car; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr.car"))) */ function sc_cadddr(p) { return p.cdr.cdr.cdr.car; } /*** META ((export #t) (peephole (postfix ".car.car.cdr.cdr"))) */ function sc_cddaar(p) { return p.car.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".car.cdr.cdr.cdr"))) */ function sc_cdddar(p) { return p.car.cdr.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.car.cdr.cdr"))) */ function sc_cddadr(p) { return p.cdr.car.cdr.cdr; } /*** META ((export #t) (peephole (postfix ".cdr.cdr.cdr.cdr"))) */ function sc_cddddr(p) { return p.cdr.cdr.cdr.cdr; } /*** META ((export #t)) */ function sc_lastPair(l) { if (!sc_isPair(l)) sc_error("sc_lastPair: pair expected"); var res = l; var cdr = l.cdr; while (sc_isPair(cdr)) { res = cdr; cdr = res.cdr; } return res; } /*** META ((export #t) (type bool) (peephole (postfix " === null"))) */ function sc_isNull(o) { return (o === null); } /*** META ((export #t) (type bool)) */ function sc_isList(o) { var rabbit; var turtle; var rabbit = o; var turtle = o; while (true) { if (rabbit === null || (rabbit instanceof sc_Pair && rabbit.cdr === null)) return true; // end of list else if ((rabbit instanceof sc_Pair) && (rabbit.cdr instanceof sc_Pair)) { rabbit = rabbit.cdr.cdr; turtle = turtle.cdr; if (rabbit === turtle) return false; // cycle } else return false; // not pair } } /*** META ((export #t)) */ function sc_list() { var res = null; var a = arguments; for (var i = a.length-1; i >= 0; i--) res = new sc_Pair(a[i], res); return res; } /*** META ((export #t)) */ function sc_iota(num, init) { var res = null; if (!init) init = 0; for (var i = num - 1; i >= 0; i--) res = new sc_Pair(i + init, res); return res; } /*** META ((export #t)) */ function sc_makeList(nbEls, fill) { var res = null; for (var i = 0; i < nbEls; i++) res = new sc_Pair(fill, res); return res; } /*** META ((export #t)) */ function sc_length(l) { var res = 0; while (l !== null) { res++; l = l.cdr; } return res; } /*** META ((export #t)) */ function sc_remq(o, l) { var dummy = { cdr : null }; var tail = dummy; while (l !== null) { if (l.car !== o) { tail.cdr = sc_cons(l.car, null); tail = tail.cdr; } l = l.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_remqBang(o, l) { var dummy = { cdr : null }; var tail = dummy; var needsAssig = true; while (l !== null) { if (l.car === o) { needsAssig = true; } else { if (needsAssig) { tail.cdr = l; needsAssig = false; } tail = l; } l = l.cdr; } tail.cdr = null; return dummy.cdr; } /*** META ((export #t)) */ function sc_delete(o, l) { var dummy = { cdr : null }; var tail = dummy; while (l !== null) { if (!sc_isEqual(l.car, o)) { tail.cdr = sc_cons(l.car, null); tail = tail.cdr; } l = l.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_deleteBang(o, l) { var dummy = { cdr : null }; var tail = dummy; var needsAssig = true; while (l !== null) { if (sc_isEqual(l.car, o)) { needsAssig = true; } else { if (needsAssig) { tail.cdr = l; needsAssig = false; } tail = l; } l = l.cdr; } tail.cdr = null; return dummy.cdr; } function sc_reverseAppendBang(l1, l2) { var res = l2; while (l1 !== null) { var tmp = res; res = l1; l1 = l1.cdr; res.cdr = tmp; } return res; } function sc_dualAppend(l1, l2) { if (l1 === null) return l2; if (l2 === null) return l1; var rev = sc_reverse(l1); return sc_reverseAppendBang(rev, l2); } /*** META ((export #t)) */ function sc_append() { if (arguments.length === 0) return null; var res = arguments[arguments.length - 1]; for (var i = arguments.length - 2; i >= 0; i--) res = sc_dualAppend(arguments[i], res); return res; } function sc_dualAppendBang(l1, l2) { if (l1 === null) return l2; if (l2 === null) return l1; var tmp = l1; while (tmp.cdr !== null) tmp=tmp.cdr; tmp.cdr = l2; return l1; } /*** META ((export #t)) */ function sc_appendBang() { var res = null; for (var i = 0; i < arguments.length; i++) res = sc_dualAppendBang(res, arguments[i]); return res; } /*** META ((export #t)) */ function sc_reverse(l1) { var res = null; while (l1 !== null) { res = sc_cons(l1.car, res); l1 = l1.cdr; } return res; } /*** META ((export #t)) */ function sc_reverseBang(l) { return sc_reverseAppendBang(l, null); } /*** META ((export #t)) */ function sc_listTail(l, k) { var res = l; for (var i = 0; i < k; i++) { res = res.cdr; } return res; } /*** META ((export #t)) */ function sc_listRef(l, k) { return sc_listTail(l, k).car; } /* // unoptimized generic versions function sc_memX(o, l, comp) { while (l != null) { if (comp(l.car, o)) return l; l = l.cdr; } return false; } function sc_memq(o, l) { return sc_memX(o, l, sc_isEq); } function sc_memv(o, l) { return sc_memX(o, l, sc_isEqv); } function sc_member(o, l) { return sc_memX(o, l, sc_isEqual); } */ /* optimized versions */ /*** META ((export #t)) */ function sc_memq(o, l) { while (l !== null) { if (l.car === o) return l; l = l.cdr; } return false; } /*** META ((export #t)) */ function sc_memv(o, l) { while (l !== null) { if (l.car === o) return l; l = l.cdr; } return false; } /*** META ((export #t)) */ function sc_member(o, l) { while (l !== null) { if (sc_isEqual(l.car,o)) return l; l = l.cdr; } return false; } /* // generic unoptimized versions function sc_assX(o, al, comp) { while (al != null) { if (comp(al.car.car, o)) return al.car; al = al.cdr; } return false; } function sc_assq(o, al) { return sc_assX(o, al, sc_isEq); } function sc_assv(o, al) { return sc_assX(o, al, sc_isEqv); } function sc_assoc(o, al) { return sc_assX(o, al, sc_isEqual); } */ // optimized versions /*** META ((export #t)) */ function sc_assq(o, al) { while (al !== null) { if (al.car.car === o) return al.car; al = al.cdr; } return false; } /*** META ((export #t)) */ function sc_assv(o, al) { while (al !== null) { if (al.car.car === o) return al.car; al = al.cdr; } return false; } /*** META ((export #t)) */ function sc_assoc(o, al) { while (al !== null) { if (sc_isEqual(al.car.car, o)) return al.car; al = al.cdr; } return false; } /* can be used for mutable strings and characters */ function sc_isCharStringEqual(cs1, cs2) { return cs1.val === cs2.val; } function sc_isCharStringLess(cs1, cs2) { return cs1.val < cs2.val; } function sc_isCharStringGreater(cs1, cs2) { return cs1.val > cs2.val; } function sc_isCharStringLessEqual(cs1, cs2) { return cs1.val <= cs2.val; } function sc_isCharStringGreaterEqual(cs1, cs2) { return cs1.val >= cs2.val; } function sc_isCharStringCIEqual(cs1, cs2) { return cs1.val.toLowerCase() === cs2.val.toLowerCase(); } function sc_isCharStringCILess(cs1, cs2) { return cs1.val.toLowerCase() < cs2.val.toLowerCase(); } function sc_isCharStringCIGreater(cs1, cs2) { return cs1.val.toLowerCase() > cs2.val.toLowerCase(); } function sc_isCharStringCILessEqual(cs1, cs2) { return cs1.val.toLowerCase() <= cs2.val.toLowerCase(); } function sc_isCharStringCIGreaterEqual(cs1, cs2) { return cs1.val.toLowerCase() >= cs2.val.toLowerCase(); } function sc_Char(c) { var cached = sc_Char.lazy[c]; if (cached) return cached; this.val = c; sc_Char.lazy[c] = this; // add return, so FF does not complain. return undefined; } sc_Char.lazy = new Object(); // thanks to Eric sc_Char.char2readable = { "\000": "#\\null", "\007": "#\\bell", "\010": "#\\backspace", "\011": "#\\tab", "\012": "#\\newline", "\014": "#\\page", "\015": "#\\return", "\033": "#\\escape", "\040": "#\\space", "\177": "#\\delete", /* poeticless names */ "\001": "#\\soh", "\002": "#\\stx", "\003": "#\\etx", "\004": "#\\eot", "\005": "#\\enq", "\006": "#\\ack", "\013": "#\\vt", "\016": "#\\so", "\017": "#\\si", "\020": "#\\dle", "\021": "#\\dc1", "\022": "#\\dc2", "\023": "#\\dc3", "\024": "#\\dc4", "\025": "#\\nak", "\026": "#\\syn", "\027": "#\\etb", "\030": "#\\can", "\031": "#\\em", "\032": "#\\sub", "\033": "#\\esc", "\034": "#\\fs", "\035": "#\\gs", "\036": "#\\rs", "\037": "#\\us"}; sc_Char.readable2char = { "null": "\000", "bell": "\007", "backspace": "\010", "tab": "\011", "newline": "\012", "page": "\014", "return": "\015", "escape": "\033", "space": "\040", "delete": "\000", "soh": "\001", "stx": "\002", "etx": "\003", "eot": "\004", "enq": "\005", "ack": "\006", "bel": "\007", "bs": "\010", "ht": "\011", "nl": "\012", "vt": "\013", "np": "\014", "cr": "\015", "so": "\016", "si": "\017", "dle": "\020", "dc1": "\021", "dc2": "\022", "dc3": "\023", "dc4": "\024", "nak": "\025", "syn": "\026", "etb": "\027", "can": "\030", "em": "\031", "sub": "\032", "esc": "\033", "fs": "\034", "gs": "\035", "rs": "\036", "us": "\037", "sp": "\040", "del": "\177"}; sc_Char.prototype.toString = function() { return this.val; }; // sc_toDisplayString == toString sc_Char.prototype.sc_toWriteString = function() { var entry = sc_Char.char2readable[this.val]; if (entry) return entry; else return "#\\" + this.val; }; /*** META ((export #t) (type bool) (peephole (postfix "instanceof sc_Char"))) */ function sc_isChar(c) { return (c instanceof sc_Char); } /*** META ((export char=?) (type bool) (peephole (hole 2 c1 ".val === " c2 ".val"))) */ var sc_isCharEqual = sc_isCharStringEqual; /*** META ((export char?) (type bool) (peephole (hole 2 c1 ".val > " c2 ".val"))) */ var sc_isCharGreater = sc_isCharStringGreater; /*** META ((export char<=?) (type bool) (peephole (hole 2 c1 ".val <= " c2 ".val"))) */ var sc_isCharLessEqual = sc_isCharStringLessEqual; /*** META ((export char>=?) (type bool) (peephole (hole 2 c1 ".val >= " c2 ".val"))) */ var sc_isCharGreaterEqual = sc_isCharStringGreaterEqual; /*** META ((export char-ci=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() === " c2 ".val.toLowerCase()"))) */ var sc_isCharCIEqual = sc_isCharStringCIEqual; /*** META ((export char-ci?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() > " c2 ".val.toLowerCase()"))) */ var sc_isCharCIGreater = sc_isCharStringCIGreater; /*** META ((export char-ci<=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() <= " c2 ".val.toLowerCase()"))) */ var sc_isCharCILessEqual = sc_isCharStringCILessEqual; /*** META ((export char-ci>=?) (type bool) (peephole (hole 2 c1 ".val.toLowerCase() >= " c2 ".val.toLowerCase()"))) */ var sc_isCharCIGreaterEqual = sc_isCharStringCIGreaterEqual; var SC_NUMBER_CLASS = "0123456789"; var SC_WHITESPACE_CLASS = ' \r\n\t\f'; var SC_LOWER_CLASS = 'abcdefghijklmnopqrstuvwxyz'; var SC_UPPER_CLASS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; function sc_isCharOfClass(c, cl) { return (cl.indexOf(c) != -1); } /*** META ((export #t) (type bool)) */ function sc_isCharAlphabetic(c) { return sc_isCharOfClass(c.val, SC_LOWER_CLASS) || sc_isCharOfClass(c.val, SC_UPPER_CLASS); } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_NUMBER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharNumeric(c) { return sc_isCharOfClass(c.val, SC_NUMBER_CLASS); } /*** META ((export #t) (type bool)) */ function sc_isCharWhitespace(c) { var tmp = c.val; return tmp === " " || tmp === "\r" || tmp === "\n" || tmp === "\t" || tmp === "\f"; } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_UPPER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharUpperCase(c) { return sc_isCharOfClass(c.val, SC_UPPER_CLASS); } /*** META ((export #t) (type bool) (peephole (hole 1 "SC_LOWER_CLASS.indexOf(" c ".val) != -1"))) */ function sc_isCharLowerCase(c) { return sc_isCharOfClass(c.val, SC_LOWER_CLASS); } /*** META ((export #t) (peephole (postfix ".val.charCodeAt(0)"))) */ function sc_char2integer(c) { return c.val.charCodeAt(0); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(String.fromCharCode(" n "))"))) */ function sc_integer2char(n) { return new sc_Char(String.fromCharCode(n)); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(" c ".val.toUpperCase())"))) */ function sc_charUpcase(c) { return new sc_Char(c.val.toUpperCase()); } /*** META ((export #t) (peephole (hole 1 "new sc_Char(" c ".val.toLowerCase())"))) */ function sc_charDowncase(c) { return new sc_Char(c.val.toLowerCase()); } function sc_makeJSStringOfLength(k, c) { var fill; if (c === undefined) fill = " "; else fill = c; var res = ""; var len = 1; // every round doubles the size of fill. while (k >= len) { if (k & len) res = res.concat(fill); fill = fill.concat(fill); len *= 2; } return res; } function sc_makejsString(k, c) { var fill; if (c) fill = c.val; else fill = " "; return sc_makeJSStringOfLength(k, fill); } function sc_jsstring2list(s) { var res = null; for (var i = s.length - 1; i >= 0; i--) res = sc_cons(new sc_Char(s.charAt(i)), res); return res; } function sc_list2jsstring(l) { var a = new Array(); while(l !== null) { a.push(l.car.val); l = l.cdr; } return "".concat.apply("", a); } var sc_Vector = Array; sc_Vector.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) { if (this.length === 0) return "#()"; var res = "#(" + writeOrDisplay(this[0]); for (var i = 1; i < this.length; i++) res += " " + writeOrDisplay(this[i]); res += ")"; return res; }; sc_Vector.prototype.sc_toDisplayString = function() { return this.sc_toWriteOrDisplayString(sc_toDisplayString); }; sc_Vector.prototype.sc_toWriteString = function() { return this.sc_toWriteOrDisplayString(sc_toWriteString); }; /*** META ((export vector? array?) (type bool) (peephole (postfix " instanceof sc_Vector"))) */ function sc_isVector(v) { return (v instanceof sc_Vector); } // only applies to vectors function sc_isVectorEqual(v1, v2, comp) { if (v1.length !== v2.length) return false; for (var i = 0; i < v1.length; i++) if (!comp(v1[i], v2[i])) return false; return true; } /*** META ((export make-vector make-array)) */ function sc_makeVector(size, fill) { var a = new sc_Vector(size); if (fill !== undefined) sc_vectorFillBang(a, fill); return a; } /*** META ((export vector array) (peephole (vector))) */ function sc_vector() { var a = new sc_Vector(); for (var i = 0; i < arguments.length; i++) a.push(arguments[i]); return a; } /*** META ((export vector-length array-length) (peephole (postfix ".length"))) */ function sc_vectorLength(v) { return v.length; } /*** META ((export vector-ref array-ref) (peephole (hole 2 v "[" pos "]"))) */ function sc_vectorRef(v, pos) { return v[pos]; } /*** META ((export vector-set! array-set!) (peephole (hole 3 v "[" pos "] = " val))) */ function sc_vectorSetBang(v, pos, val) { v[pos] = val; } /*** META ((export vector->list array->list)) */ function sc_vector2list(a) { var res = null; for (var i = a.length-1; i >= 0; i--) res = sc_cons(a[i], res); return res; } /*** META ((export list->vector list->array)) */ function sc_list2vector(l) { var a = new sc_Vector(); while(l !== null) { a.push(l.car); l = l.cdr; } return a; } /*** META ((export vector-fill! array-fill!)) */ function sc_vectorFillBang(a, fill) { for (var i = 0; i < a.length; i++) a[i] = fill; } /*** META ((export #t)) */ function sc_copyVector(a, len) { if (len <= a.length) return a.slice(0, len); else { var tmp = a.concat(); tmp.length = len; return tmp; } } /*** META ((export #t) (peephole (hole 3 a ".slice(" start "," end ")"))) */ function sc_vectorCopy(a, start, end) { return a.slice(start, end); } /*** META ((export #t)) */ function sc_vectorCopyBang(target, tstart, source, sstart, send) { if (!sstart) sstart = 0; if (!send) send = source.length; // if target == source we don't want to overwrite not yet copied elements. if (tstart <= sstart) { for (var i = tstart, j = sstart; j < send; i++, j++) { target[i] = source[j]; } } else { var diff = send - sstart; for (var i = tstart + diff - 1, j = send - 1; j >= sstart; i--, j--) { target[i] = source[j]; } } return target; } /*** META ((export #t) (type bool) (peephole (hole 1 "typeof " o " === 'function'"))) */ function sc_isProcedure(o) { return (typeof o === "function"); } /*** META ((export #t)) */ function sc_apply(proc) { var args = new Array(); // first part of arguments are not in list-form. for (var i = 1; i < arguments.length - 1; i++) args.push(arguments[i]); var l = arguments[arguments.length - 1]; while (l !== null) { args.push(l.car); l = l.cdr; } return proc.apply(null, args); } /*** META ((export #t)) */ function sc_map(proc, l1) { if (l1 === undefined) return null; // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); var revres = null; while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } revres = sc_cons(proc.apply(null, applyArgs), revres); } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_mapBang(proc, l1) { if (l1 === undefined) return null; // else var l1_orig = l1; var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); while (l1 !== null) { var tmp = l1; for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } tmp.car = proc.apply(null, applyArgs); } return l1_orig; } /*** META ((export #t)) */ function sc_forEach(proc, l1) { if (l1 === undefined) return undefined; // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } proc.apply(null, applyArgs); } // add return so FF does not complain. return undefined; } /*** META ((export #t)) */ function sc_filter(proc, l1) { var dummy = { cdr : null }; var tail = dummy; while (l1 !== null) { if (proc(l1.car) !== false) { tail.cdr = sc_cons(l1.car, null); tail = tail.cdr; } l1 = l1.cdr; } return dummy.cdr; } /*** META ((export #t)) */ function sc_filterBang(proc, l1) { var head = sc_cons("dummy", l1); var it = head; var next = l1; while (next !== null) { if (proc(next.car) !== false) { it.cdr = next it = next; } next = next.cdr; } it.cdr = null; return head.cdr; } function sc_filterMap1(proc, l1) { var revres = null; while (l1 !== null) { var tmp = proc(l1.car) if (tmp !== false) revres = sc_cons(tmp, revres); l1 = l1.cdr; } return sc_reverseAppendBang(revres, null); } function sc_filterMap2(proc, l1, l2) { var revres = null; while (l1 !== null) { var tmp = proc(l1.car, l2.car); if(tmp !== false) revres = sc_cons(tmp, revres); l1 = l1.cdr; l2 = l2.cdr } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_filterMap(proc, l1, l2, l3) { if (l2 === undefined) return sc_filterMap1(proc, l1); else if (l3 === undefined) return sc_filterMap2(proc, l1, l2); // else var nbApplyArgs = arguments.length - 1; var applyArgs = new Array(nbApplyArgs); var revres = null; while (l1 !== null) { for (var i = 0; i < nbApplyArgs; i++) { applyArgs[i] = arguments[i + 1].car; arguments[i + 1] = arguments[i + 1].cdr; } var tmp = proc.apply(null, applyArgs); if(tmp !== false) revres = sc_cons(tmp, revres); } return sc_reverseAppendBang(revres, null); } /*** META ((export #t)) */ function sc_any(proc, l) { var revres = null; while (l !== null) { var tmp = proc(l.car); if(tmp !== false) return tmp; l = l.cdr; } return false; } /*** META ((export any?) (peephole (hole 2 "sc_any(" proc "," l ") !== false"))) */ function sc_anyPred(proc, l) { return sc_any(proc, l)!== false; } /*** META ((export #t)) */ function sc_every(proc, l) { var revres = null; var tmp = true; while (l !== null) { tmp = proc(l.car); if (tmp === false) return false; l = l.cdr; } return tmp; } /*** META ((export every?) (peephole (hole 2 "sc_every(" proc "," l ") !== false"))) */ function sc_everyPred(proc, l) { var tmp = sc_every(proc, l); if (tmp !== false) return true; return false; } /*** META ((export #t) (peephole (postfix "()"))) */ function sc_force(o) { return o(); } /*** META ((export #t)) */ function sc_makePromise(proc) { var isResultReady = false; var result = undefined; return function() { if (!isResultReady) { var tmp = proc(); if (!isResultReady) { isResultReady = true; result = tmp; } } return result; }; } function sc_Values(values) { this.values = values; } /*** META ((export #t) (peephole (values))) */ function sc_values() { if (arguments.length === 1) return arguments[0]; else return new sc_Values(arguments); } /*** META ((export #t)) */ function sc_callWithValues(producer, consumer) { var produced = producer(); if (produced instanceof sc_Values) return consumer.apply(null, produced.values); else return consumer(produced); } /*** META ((export #t)) */ function sc_dynamicWind(before, thunk, after) { before(); try { var res = thunk(); return res; } finally { after(); } } // TODO: eval/scheme-report-environment/null-environment/interaction-environment // LIMITATION: 'load' doesn't exist without files. // LIMITATION: transcript-on/transcript-off doesn't exist without files. function sc_Struct(name) { this.name = name; } sc_Struct.prototype.sc_toDisplayString = function() { return "#"; }; sc_Struct.prototype.sc_toWriteString = sc_Struct.prototype.sc_toDisplayString; /*** META ((export #t) (peephole (hole 1 "new sc_Struct(" name ")"))) */ function sc_makeStruct(name) { return new sc_Struct(name); } /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_Struct"))) */ function sc_isStruct(o) { return (o instanceof sc_Struct); } /*** META ((export #t) (type bool) (peephole (hole 2 "(" 1 " instanceof sc_Struct) && ( " 1 ".name === " 0 ")"))) */ function sc_isStructNamed(name, s) { return ((s instanceof sc_Struct) && (s.name === name)); } /*** META ((export struct-field) (peephole (hole 3 0 "[" 2 "]"))) */ function sc_getStructField(s, name, field) { return s[field]; } /*** META ((export struct-field-set!) (peephole (hole 4 0 "[" 2 "] = " 3))) */ function sc_setStructFieldBang(s, name, field, val) { s[field] = val; } /*** META ((export #t) (peephole (prefix "~"))) */ function sc_bitNot(x) { return ~x; } /*** META ((export #t) (peephole (infix 2 2 "&"))) */ function sc_bitAnd(x, y) { return x & y; } /*** META ((export #t) (peephole (infix 2 2 "|"))) */ function sc_bitOr(x, y) { return x | y; } /*** META ((export #t) (peephole (infix 2 2 "^"))) */ function sc_bitXor(x, y) { return x ^ y; } /*** META ((export #t) (peephole (infix 2 2 "<<"))) */ function sc_bitLsh(x, y) { return x << y; } /*** META ((export #t) (peephole (infix 2 2 ">>"))) */ function sc_bitRsh(x, y) { return x >> y; } /*** META ((export #t) (peephole (infix 2 2 ">>>"))) */ function sc_bitUrsh(x, y) { return x >>> y; } /*** META ((export js-field js-property) (peephole (hole 2 o "[" field "]"))) */ function sc_jsField(o, field) { return o[field]; } /*** META ((export js-field-set! js-property-set!) (peephole (hole 3 o "[" field "] = " val))) */ function sc_setJsFieldBang(o, field, val) { return o[field] = val; } /*** META ((export js-field-delete! js-property-delete!) (peephole (hole 2 "delete" o "[" field "]"))) */ function sc_deleteJsFieldBang(o, field) { delete o[field]; } /*** META ((export #t) (peephole (jsCall))) */ function sc_jsCall(o, fun) { var args = new Array(); for (var i = 2; i < arguments.length; i++) args[i-2] = arguments[i]; return fun.apply(o, args); } /*** META ((export #t) (peephole (jsMethodCall))) */ function sc_jsMethodCall(o, field) { var args = new Array(); for (var i = 2; i < arguments.length; i++) args[i-2] = arguments[i]; return o[field].apply(o, args); } /*** META ((export new js-new) (peephole (jsNew))) */ function sc_jsNew(c) { var evalStr = "new c("; evalStr +=arguments.length > 1? "arguments[1]": ""; for (var i = 2; i < arguments.length; i++) evalStr += ", arguments[" + i + "]"; evalStr +=")"; return eval(evalStr); } // ======================== RegExp ==================== /*** META ((export #t)) */ function sc_pregexp(re) { return new RegExp(sc_string2jsstring(re)); } /*** META ((export #t)) */ function sc_pregexpMatch(re, s) { var reg = (re instanceof RegExp) ? re : sc_pregexp(re); var tmp = reg.exec(sc_string2jsstring(s)); if (tmp == null) return false; var res = null; for (var i = tmp.length-1; i >= 0; i--) { if (tmp[i] !== null) { res = sc_cons(sc_jsstring2string(tmp[i]), res); } else { res = sc_cons(false, res); } } return res; } /*** META ((export #t)) */ function sc_pregexpReplace(re, s1, s2) { var reg; var jss1 = sc_string2jsstring(s1); var jss2 = sc_string2jsstring(s2); if (re instanceof RegExp) { if (re.global) reg = re; else reg = new RegExp(re.source); } else { reg = new RegExp(sc_string2jsstring(re)); } return jss1.replace(reg, jss2); } /*** META ((export pregexp-replace*)) */ function sc_pregexpReplaceAll(re, s1, s2) { var reg; var jss1 = sc_string2jsstring(s1); var jss2 = sc_string2jsstring(s2); if (re instanceof RegExp) { if (re.global) reg = re; else reg = new RegExp(re.source, "g"); } else { reg = new RegExp(sc_string2jsstring(re), "g"); } return jss1.replace(reg, jss2); } /*** META ((export #t)) */ function sc_pregexpSplit(re, s) { var reg = ((re instanceof RegExp) ? re : new RegExp(sc_string2jsstring(re))); var jss = sc_string2jsstring(s); var tmp = jss.split(reg); if (tmp == null) return false; return sc_vector2list(tmp); } /* =========================================================================== */ /* Other library stuff */ /* =========================================================================== */ /*** META ((export #t) (peephole (hole 1 "Math.floor(Math.random()*" 'n ")"))) */ function sc_random(n) { return Math.floor(Math.random()*n); } /*** META ((export current-date) (peephole (hole 0 "new Date()"))) */ function sc_currentDate() { return new Date(); } function sc_Hashtable() { } sc_Hashtable.prototype.toString = function() { return "#{%hashtable}"; }; // sc_toWriteString == sc_toDisplayString == toString function sc_HashtableElement(key, val) { this.key = key; this.val = val; } /*** META ((export #t) (peephole (hole 0 "new sc_Hashtable()"))) */ function sc_makeHashtable() { return new sc_Hashtable(); } /*** META ((export #t)) */ function sc_hashtablePutBang(ht, key, val) { var hash = sc_hash(key); ht[hash] = new sc_HashtableElement(key, val); } /*** META ((export #t)) */ function sc_hashtableGet(ht, key) { var hash = sc_hash(key); if (hash in ht) return ht[hash].val; else return false; } /*** META ((export #t)) */ function sc_hashtableForEach(ht, f) { for (var v in ht) { if (ht[v] instanceof sc_HashtableElement) f(ht[v].key, ht[v].val); } } /*** META ((export hashtable-contains?) (peephole (hole 2 "sc_hash(" 1 ") in " 0))) */ function sc_hashtableContains(ht, key) { var hash = sc_hash(key); if (hash in ht) return true; else return false; } var SC_HASH_COUNTER = 0; function sc_hash(o) { if (o === null) return "null"; else if (o === undefined) return "undefined"; else if (o === true) return "true"; else if (o === false) return "false"; else if (typeof o === "number") return "num-" + o; else if (typeof o === "string") return "jsstr-" + o; else if (o.sc_getHash) return o.sc_getHash(); else return sc_counterHash.call(o); } function sc_counterHash() { if (!this.sc_hash) { this.sc_hash = "hash-" + SC_HASH_COUNTER; SC_HASH_COUNTER++; } return this.sc_hash; } function sc_Trampoline(args, maxTailCalls) { this['__trampoline return__'] = true; this.args = args; this.MAX_TAIL_CALLs = maxTailCalls; } // TODO: call/cc stuff sc_Trampoline.prototype.restart = function() { var o = this; while (true) { // set both globals. SC_TAIL_OBJECT.calls = o.MAX_TAIL_CALLs-1; var fun = o.args.callee; var res = fun.apply(SC_TAIL_OBJECT, o.args); if (res instanceof sc_Trampoline) o = res; else return res; } } /*** META ((export bind-exit-lambda)) */ function sc_bindExitLambda(proc) { var escape_obj = new sc_BindExitException(); var escape = function(res) { escape_obj.res = res; throw escape_obj; }; try { return proc(escape); } catch(e) { if (e === escape_obj) { return e.res; } throw e; } } function sc_BindExitException() { this._internalException = true; } var SC_SCM2JS_GLOBALS = new Object(); // default tail-call depth. // normally the program should set it again. but just in case... var SC_TAIL_OBJECT = new Object(); SC_SCM2JS_GLOBALS.TAIL_OBJECT = SC_TAIL_OBJECT; // ======================== I/O ======================= /*------------------------------------------------------------------*/ function sc_EOF() { } var SC_EOF_OBJECT = new sc_EOF(); function sc_Port() { } /* --------------- Input ports -------------------------------------*/ function sc_InputPort() { } sc_InputPort.prototype = new sc_Port(); sc_InputPort.prototype.peekChar = function() { if (!("peeked" in this)) this.peeked = this.getNextChar(); return this.peeked; } sc_InputPort.prototype.readChar = function() { var tmp = this.peekChar(); delete this.peeked; return tmp; } sc_InputPort.prototype.isCharReady = function() { return true; } sc_InputPort.prototype.close = function() { // do nothing } /* .............. String port ..........................*/ function sc_ErrorInputPort() { }; sc_ErrorInputPort.prototype = new sc_InputPort(); sc_ErrorInputPort.prototype.getNextChar = function() { throw "can't read from error-port."; }; sc_ErrorInputPort.prototype.isCharReady = function() { return false; }; /* .............. String port ..........................*/ function sc_StringInputPort(jsStr) { // we are going to do some charAts on the str. // instead of recreating all the time a String-object, we // create one in the beginning. (not sure, if this is really an optim) this.str = new String(jsStr); this.pos = 0; } sc_StringInputPort.prototype = new sc_InputPort(); sc_StringInputPort.prototype.getNextChar = function() { if (this.pos >= this.str.length) return SC_EOF_OBJECT; return this.str.charAt(this.pos++); }; /* ------------- Read and other lib-funs -------------------------------*/ function sc_Token(type, val, pos) { this.type = type; this.val = val; this.pos = pos; } sc_Token.EOF = 0/*EOF*/; sc_Token.OPEN_PAR = 1/*OPEN_PAR*/; sc_Token.CLOSE_PAR = 2/*CLOSE_PAR*/; sc_Token.OPEN_BRACE = 3/*OPEN_BRACE*/; sc_Token.CLOSE_BRACE = 4/*CLOSE_BRACE*/; sc_Token.OPEN_BRACKET = 5/*OPEN_BRACKET*/; sc_Token.CLOSE_BRACKET = 6/*CLOSE_BRACKET*/; sc_Token.WHITESPACE = 7/*WHITESPACE*/; sc_Token.QUOTE = 8/*QUOTE*/; sc_Token.ID = 9/*ID*/; sc_Token.DOT = 10/*DOT*/; sc_Token.STRING = 11/*STRING*/; sc_Token.NUMBER = 12/*NUMBER*/; sc_Token.ERROR = 13/*ERROR*/; sc_Token.VECTOR_BEGIN = 14/*VECTOR_BEGIN*/; sc_Token.TRUE = 15/*TRUE*/; sc_Token.FALSE = 16/*FALSE*/; sc_Token.UNSPECIFIED = 17/*UNSPECIFIED*/; sc_Token.REFERENCE = 18/*REFERENCE*/; sc_Token.STORE = 19/*STORE*/; sc_Token.CHAR = 20/*CHAR*/; var SC_ID_CLASS = SC_LOWER_CLASS + SC_UPPER_CLASS + "!$%*+-./:<=>?@^_~"; function sc_Tokenizer(port) { this.port = port; } sc_Tokenizer.prototype.peekToken = function() { if (this.peeked) return this.peeked; var newToken = this.nextToken(); this.peeked = newToken; return newToken; }; sc_Tokenizer.prototype.readToken = function() { var tmp = this.peekToken(); delete this.peeked; return tmp; }; sc_Tokenizer.prototype.nextToken = function() { var port = this.port; function isNumberChar(c) { return (c >= "0" && c <= "9"); }; function isIdOrNumberChar(c) { return SC_ID_CLASS.indexOf(c) != -1 || // ID-char (c >= "0" && c <= "9"); } function isWhitespace(c) { return c === " " || c === "\r" || c === "\n" || c === "\t" || c === "\f"; }; function isWhitespaceOrEOF(c) { return isWhitespace(c) || c === SC_EOF_OBJECT; }; function readString() { res = ""; while (true) { var c = port.readChar(); switch (c) { case '"': return new sc_Token(11/*STRING*/, res); case "\\": var tmp = port.readChar(); switch (tmp) { case '0': res += "\0"; break; case 'a': res += "\a"; break; case 'b': res += "\b"; break; case 'f': res += "\f"; break; case 'n': res += "\n"; break; case 'r': res += "\r"; break; case 't': res += "\t"; break; case 'v': res += "\v"; break; case '"': res += '"'; break; case '\\': res += '\\'; break; case 'x': /* hexa-number */ var nb = 0; while (true) { var hexC = port.peekChar(); if (hexC >= '0' && hexC <= '9') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - '0'.charCodeAt(0); } else if (hexC >= 'a' && hexC <= 'f') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - 'a'.charCodeAt(0); } else if (hexC >= 'A' && hexC <= 'F') { port.readChar(); nb = nb * 16 + hexC.charCodeAt(0) - 'A'.charCodeAt(0); } else { // next char isn't part of hex. res += String.fromCharCode(nb); break; } } break; default: if (tmp === SC_EOF_OBJECT) { return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); } res += tmp; } break; default: if (c === SC_EOF_OBJECT) { return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res); } res += c; } } }; function readIdOrNumber(firstChar) { var res = firstChar; while (isIdOrNumberChar(port.peekChar())) res += port.readChar(); if (isNaN(res)) return new sc_Token(9/*ID*/, res); else return new sc_Token(12/*NUMBER*/, res - 0); }; function skipWhitespaceAndComments() { var done = false; while (!done) { done = true; while (isWhitespace(port.peekChar())) port.readChar(); if (port.peekChar() === ';') { port.readChar(); done = false; while (true) { curChar = port.readChar(); if (curChar === SC_EOF_OBJECT || curChar === '\n') break; } } } }; function readDot() { if (isWhitespace(port.peekChar())) return new sc_Token(10/*DOT*/); else return readIdOrNumber("."); }; function readSharp() { var c = port.readChar(); if (isWhitespace(c)) return new sc_Token(13/*ERROR*/, "bad #-pattern0."); // reference if (isNumberChar(c)) { var nb = c - 0; while (isNumberChar(port.peekChar())) nb = nb*10 + (port.readChar() - 0); switch (port.readChar()) { case '#': return new sc_Token(18/*REFERENCE*/, nb); case '=': return new sc_Token(19/*STORE*/, nb); default: return new sc_Token(13/*ERROR*/, "bad #-pattern1." + nb); } } if (c === "(") return new sc_Token(14/*VECTOR_BEGIN*/); if (c === "\\") { // character var tmp = "" while (!isWhitespaceOrEOF(port.peekChar())) tmp += port.readChar(); switch (tmp.length) { case 0: // it's escaping a whitespace char: if (sc_isEOFObject(port.peekChar)) return new sc_Token(13/*ERROR*/, "bad #-pattern2."); else return new sc_Token(20/*CHAR*/, port.readChar()); case 1: return new sc_Token(20/*CHAR*/, tmp); default: var entry = sc_Char.readable2char[tmp.toLowerCase()]; if (entry) return new sc_Token(20/*CHAR*/, entry); else return new sc_Token(13/*ERROR*/, "unknown character description: #\\" + tmp); } } // some constants (#t, #f, #unspecified) var res; var needing; switch (c) { case 't': res = new sc_Token(15/*TRUE*/, true); needing = ""; break; case 'f': res = new sc_Token(16/*FALSE*/, false); needing = ""; break; case 'u': res = new sc_Token(17/*UNSPECIFIED*/, undefined); needing = "nspecified"; break; default: return new sc_Token(13/*ERROR*/, "bad #-pattern3: " + c); } while(true) { c = port.peekChar(); if ((isWhitespaceOrEOF(c) || c === ')') && needing == "") return res; else if (isWhitespace(c) || needing == "") return new sc_Token(13/*ERROR*/, "bad #-pattern4 " + c + " " + needing); else if (needing.charAt(0) == c) { port.readChar(); // consume needing = needing.slice(1); } else return new sc_Token(13/*ERROR*/, "bad #-pattern5"); } }; skipWhitespaceAndComments(); var curChar = port.readChar(); if (curChar === SC_EOF_OBJECT) return new sc_Token(0/*EOF*/, curChar); switch (curChar) { case " ": case "\n": case "\t": return readWhitespace(); case "(": return new sc_Token(1/*OPEN_PAR*/); case ")": return new sc_Token(2/*CLOSE_PAR*/); case "{": return new sc_Token(3/*OPEN_BRACE*/); case "}": return new sc_Token(4/*CLOSE_BRACE*/); case "[": return new sc_Token(5/*OPEN_BRACKET*/); case "]": return new sc_Token(6/*CLOSE_BRACKET*/); case "'": return new sc_Token(8/*QUOTE*/); case "#": return readSharp(); case ".": return readDot(); case '"': return readString(); default: if (isIdOrNumberChar(curChar)) return readIdOrNumber(curChar); throw "unexpected character: " + curChar; } }; function sc_Reader(tokenizer) { this.tokenizer = tokenizer; this.backref = new Array(); } sc_Reader.prototype.read = function() { function readList(listBeginType) { function matchesPeer(open, close) { return open === 1/*OPEN_PAR*/ && close === 2/*CLOSE_PAR*/ || open === 3/*OPEN_BRACE*/ && close === 4/*CLOSE_BRACE*/ || open === 5/*OPEN_BRACKET*/ && close === 6/*CLOSE_BRACKET*/; }; var res = null; while (true) { var token = tokenizer.peekToken(); switch (token.type) { case 2/*CLOSE_PAR*/: case 4/*CLOSE_BRACE*/: case 6/*CLOSE_BRACKET*/: if (matchesPeer(listBeginType, token.type)) { tokenizer.readToken(); // consume token return sc_reverseBang(res); } else throw "closing par doesn't match: " + listBeginType + " " + listEndType; case 0/*EOF*/: throw "unexpected end of file"; case 10/*DOT*/: tokenizer.readToken(); // consume token var cdr = this.read(); var par = tokenizer.readToken(); if (!matchesPeer(listBeginType, par.type)) throw "closing par doesn't match: " + listBeginType + " " + par.type; else return sc_reverseAppendBang(res, cdr); default: res = sc_cons(this.read(), res); } } }; function readQuote() { return sc_cons("quote", sc_cons(this.read(), null)); }; function readVector() { // opening-parenthesis is already consumed var a = new Array(); while (true) { var token = tokenizer.peekToken(); switch (token.type) { case 2/*CLOSE_PAR*/: tokenizer.readToken(); return a; default: a.push(this.read()); } } }; function storeRefence(nb) { var tmp = this.read(); this.backref[nb] = tmp; return tmp; }; function readReference(nb) { if (nb in this.backref) return this.backref[nb]; else throw "bad reference: " + nb; }; var tokenizer = this.tokenizer; var token = tokenizer.readToken(); // handle error if (token.type === 13/*ERROR*/) throw token.val; switch (token.type) { case 1/*OPEN_PAR*/: case 3/*OPEN_BRACE*/: case 5/*OPEN_BRACKET*/: return readList.call(this, token.type); case 8/*QUOTE*/: return readQuote.call(this); case 11/*STRING*/: return sc_jsstring2string(token.val); case 20/*CHAR*/: return new sc_Char(token.val); case 14/*VECTOR_BEGIN*/: return readVector.call(this); case 18/*REFERENCE*/: return readReference.call(this, token.val); case 19/*STORE*/: return storeRefence.call(this, token.val); case 9/*ID*/: return sc_jsstring2symbol(token.val); case 0/*EOF*/: case 12/*NUMBER*/: case 15/*TRUE*/: case 16/*FALSE*/: case 17/*UNSPECIFIED*/: return token.val; default: throw "unexpected token " + token.type + " " + token.val; } }; /*** META ((export #t)) */ function sc_read(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var reader = new sc_Reader(new sc_Tokenizer(port)); return reader.read(); } /*** META ((export #t)) */ function sc_readChar(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var t = port.readChar(); return t === SC_EOF_OBJECT? t: new sc_Char(t); } /*** META ((export #t)) */ function sc_peekChar(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... var t = port.peekChar(); return t === SC_EOF_OBJECT? t: new sc_Char(t); } /*** META ((export #t) (type bool)) */ function sc_isCharReady(port) { if (port === undefined) // we assume the port hasn't been given. port = SC_DEFAULT_IN; // THREAD: shared var... return port.isCharReady(); } /*** META ((export #t) (peephole (postfix ".close()"))) */ function sc_closeInputPort(p) { return p.close(); } /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_InputPort"))) */ function sc_isInputPort(o) { return (o instanceof sc_InputPort); } /*** META ((export eof-object?) (type bool) (peephole (postfix " === SC_EOF_OBJECT"))) */ function sc_isEOFObject(o) { return o === SC_EOF_OBJECT; } /*** META ((export #t) (peephole (hole 0 "SC_DEFAULT_IN"))) */ function sc_currentInputPort() { return SC_DEFAULT_IN; } /* ------------ file operations are not supported -----------*/ /*** META ((export #t)) */ function sc_callWithInputFile(s, proc) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_callWithOutputFile(s, proc) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_withInputFromFile(s, thunk) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_withOutputToFile(s, thunk) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_openInputFile(s) { throw "can't open " + s; } /*** META ((export #t)) */ function sc_openOutputFile(s) { throw "can't open " + s; } /* ----------------------------------------------------------------------------*/ /*** META ((export #t)) */ function sc_basename(p) { var i = p.lastIndexOf('/'); if(i >= 0) return p.substring(i + 1, p.length); else return ''; } /*** META ((export #t)) */ function sc_dirname(p) { var i = p.lastIndexOf('/'); if(i >= 0) return p.substring(0, i); else return ''; } /* ----------------------------------------------------------------------------*/ /*** META ((export #t)) */ function sc_withInputFromPort(p, thunk) { try { var tmp = SC_DEFAULT_IN; // THREAD: shared var. SC_DEFAULT_IN = p; return thunk(); } finally { SC_DEFAULT_IN = tmp; } } /*** META ((export #t)) */ function sc_withInputFromString(s, thunk) { return sc_withInputFromPort(new sc_StringInputPort(sc_string2jsstring(s)), thunk); } /*** META ((export #t)) */ function sc_withOutputToPort(p, thunk) { try { var tmp = SC_DEFAULT_OUT; // THREAD: shared var. SC_DEFAULT_OUT = p; return thunk(); } finally { SC_DEFAULT_OUT = tmp; } } /*** META ((export #t)) */ function sc_withOutputToString(thunk) { var p = new sc_StringOutputPort(); sc_withOutputToPort(p, thunk); return p.close(); } /*** META ((export #t)) */ function sc_withOutputToProcedure(proc, thunk) { var t = function(s) { proc(sc_jsstring2string(s)); }; return sc_withOutputToPort(new sc_GenericOutputPort(t), thunk); } /*** META ((export #t) (peephole (hole 0 "new sc_StringOutputPort()"))) */ function sc_openOutputString() { return new sc_StringOutputPort(); } /*** META ((export #t)) */ function sc_openInputString(str) { return new sc_StringInputPort(sc_string2jsstring(str)); } /* ----------------------------------------------------------------------------*/ function sc_OutputPort() { } sc_OutputPort.prototype = new sc_Port(); sc_OutputPort.prototype.appendJSString = function(obj) { /* do nothing */ } sc_OutputPort.prototype.close = function() { /* do nothing */ } function sc_StringOutputPort() { this.res = ""; } sc_StringOutputPort.prototype = new sc_OutputPort(); sc_StringOutputPort.prototype.appendJSString = function(s) { this.res += s; } sc_StringOutputPort.prototype.close = function() { return sc_jsstring2string(this.res); } /*** META ((export #t)) */ function sc_getOutputString(sp) { return sc_jsstring2string(sp.res); } function sc_ErrorOutputPort() { } sc_ErrorOutputPort.prototype = new sc_OutputPort(); sc_ErrorOutputPort.prototype.appendJSString = function(s) { throw "don't write on ErrorPort!"; } sc_ErrorOutputPort.prototype.close = function() { /* do nothing */ } function sc_GenericOutputPort(appendJSString, close) { this.appendJSString = appendJSString; if (close) this.close = close; } sc_GenericOutputPort.prototype = new sc_OutputPort(); /*** META ((export #t) (type bool) (peephole (postfix " instanceof sc_OutputPort"))) */ function sc_isOutputPort(o) { return (o instanceof sc_OutputPort); } /*** META ((export #t) (peephole (postfix ".close()"))) */ function sc_closeOutputPort(p) { return p.close(); } /* ------------------ write ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_write(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toWriteString(o)); } function sc_toWriteString(o) { if (o === null) return "()"; else if (o === true) return "#t"; else if (o === false) return "#f"; else if (o === undefined) return "#unspecified"; else if (typeof o === 'function') return "#"; else if (o.sc_toWriteString) return o.sc_toWriteString(); else return o.toString(); } function sc_escapeWriteString(s) { var res = ""; var j = 0; for (i = 0; i < s.length; i++) { switch (s.charAt(i)) { case "\0": res += s.substring(j, i) + "\\0"; j = i + 1; break; case "\b": res += s.substring(j, i) + "\\b"; j = i + 1; break; case "\f": res += s.substring(j, i) + "\\f"; j = i + 1; break; case "\n": res += s.substring(j, i) + "\\n"; j = i + 1; break; case "\r": res += s.substring(j, i) + "\\r"; j = i + 1; break; case "\t": res += s.substring(j, i) + "\\t"; j = i + 1; break; case "\v": res += s.substring(j, i) + "\\v"; j = i + 1; break; case '"': res += s.substring(j, i) + '\\"'; j = i + 1; break; case "\\": res += s.substring(j, i) + "\\\\"; j = i + 1; break; default: var c = s.charAt(i); if ("\a" !== "a" && c == "\a") { res += s.substring(j, i) + "\\a"; j = i + 1; continue; } if ("\v" !== "v" && c == "\v") { res += s.substring(j, i) + "\\v"; j = i + 1; continue; } //if (s.charAt(i) < ' ' || s.charCodeAt(i) > 127) { // CARE: Manuel is this OK with HOP? if (s.charAt(i) < ' ') { /* non printable character and special chars */ res += s.substring(j, i) + "\\x" + s.charCodeAt(i).toString(16); j = i + 1; } // else just let i increase... } } res += s.substring(j, i); return res; } /* ------------------ display ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_display(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toDisplayString(o)); } function sc_toDisplayString(o) { if (o === null) return "()"; else if (o === true) return "#t"; else if (o === false) return "#f"; else if (o === undefined) return "#unspecified"; else if (typeof o === 'function') return "#"; else if (o.sc_toDisplayString) return o.sc_toDisplayString(); else return o.toString(); } /* ------------------ newline ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_newline(p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString("\n"); } /* ------------------ write-char ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_writeChar(c, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(c.val); } /* ------------------ write-circle ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_writeCircle(o, p) { if (p === undefined) // we assume not given p = SC_DEFAULT_OUT; p.appendJSString(sc_toWriteCircleString(o)); } function sc_toWriteCircleString(o) { var symb = sc_gensym("writeCircle"); var nbPointer = new Object(); nbPointer.nb = 0; sc_prepWriteCircle(o, symb, nbPointer); return sc_genToWriteCircleString(o, symb); } function sc_prepWriteCircle(o, symb, nbPointer) { // TODO sc_Struct if (o instanceof sc_Pair || o instanceof sc_Vector) { if (o[symb] !== undefined) { // not the first visit. o[symb]++; // unless there is already a number, assign one. if (!o[symb + "nb"]) o[symb + "nb"] = nbPointer.nb++; return; } o[symb] = 0; if (o instanceof sc_Pair) { sc_prepWriteCircle(o.car, symb, nbPointer); sc_prepWriteCircle(o.cdr, symb, nbPointer); } else { for (var i = 0; i < o.length; i++) sc_prepWriteCircle(o[i], symb, nbPointer); } } } function sc_genToWriteCircleString(o, symb) { if (!(o instanceof sc_Pair || o instanceof sc_Vector)) return sc_toWriteString(o); return o.sc_toWriteCircleString(symb); } sc_Pair.prototype.sc_toWriteCircleString = function(symb, inList) { if (this[symb + "use"]) { // use-flag is set. Just use it. var nb = this[symb + "nb"]; if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } if (inList) return '. #' + nb + '#'; else return '#' + nb + '#'; } if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } var res = ""; if (this[symb] !== undefined) { // implies > 0 this[symb + "use"] = true; if (inList) res += '. #' + this[symb + "nb"] + '='; else res += '#' + this[symb + "nb"] + '='; inList = false; } if (!inList) res += "("; // print car res += sc_genToWriteCircleString(this.car, symb); if (sc_isPair(this.cdr)) { res += " " + this.cdr.sc_toWriteCircleString(symb, true); } else if (this.cdr !== null) { res += " . " + sc_genToWriteCircleString(this.cdr, symb); } if (!inList) res += ")"; return res; }; sc_Vector.prototype.sc_toWriteCircleString = function(symb) { if (this[symb + "use"]) { // use-flag is set. Just use it. var nb = this[symb + "nb"]; if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } return '#' + nb + '#'; } if (this[symb]-- === 0) { // if we are the last use. remove all fields. delete this[symb]; delete this[symb + "nb"]; delete this[symb + "use"]; } var res = ""; if (this[symb] !== undefined) { // implies > 0 this[symb + "use"] = true; res += '#' + this[symb + "nb"] + '='; } res += "#("; for (var i = 0; i < this.length; i++) { res += sc_genToWriteCircleString(this[i], symb); if (i < this.length - 1) res += " "; } res += ")"; return res; }; /* ------------------ print ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_print(s) { if (arguments.length === 1) { sc_display(s); sc_newline(); } else { for (var i = 0; i < arguments.length; i++) sc_display(arguments[i]); sc_newline(); } } /* ------------------ format ---------------------------------------------------*/ /*** META ((export #t)) */ function sc_format(s, args) { var len = s.length; var p = new sc_StringOutputPort(); var i = 0, j = 1; while( i < len ) { var i2 = s.indexOf("~", i); if (i2 == -1) { p.appendJSString( s.substring( i, len ) ); return p.close(); } else { if (i2 > i) { if (i2 == (len - 1)) { p.appendJSString(s.substring(i, len)); return p.close(); } else { p.appendJSString(s.substring(i, i2)); i = i2; } } switch(s.charCodeAt(i2 + 1)) { case 65: case 97: // a sc_display(arguments[j], p); i += 2; j++; break; case 83: case 115: // s sc_write(arguments[j], p); i += 2; j++; break; case 86: case 118: // v sc_display(arguments[j], p); p.appendJSString("\n"); i += 2; j++; break; case 67: case 99: // c p.appendJSString(String.fromCharCode(arguments[j])); i += 2; j++; break; case 88: case 120: // x p.appendJSString(arguments[j].toString(6)); i += 2; j++; break; case 79: case 111: // o p.appendJSString(arguments[j].toString(8)); i += 2; j++; break; case 66: case 98: // b p.appendJSString(arguments[j].toString(2)); i += 2; j++; break; case 37: case 110: // %, n p.appendJSString("\n"); i += 2; break; case 114: // r p.appendJSString("\r"); i += 2; break; case 126: // ~ p.appendJSString("~"); i += 2; break; default: sc_error( "format: illegal ~" + String.fromCharCode(s.charCodeAt(i2 + 1)) + " sequence" ); return ""; } } } return p.close(); } /* ------------------ global ports ---------------------------------------------------*/ var SC_DEFAULT_IN = new sc_ErrorInputPort(); var SC_DEFAULT_OUT = new sc_ErrorOutputPort(); var SC_ERROR_OUT = new sc_ErrorOutputPort(); var sc_SYMBOL_PREFIX = "\u1E9C"; var sc_KEYWORD_PREFIX = "\u1E9D"; /*** META ((export #t) (peephole (id))) */ function sc_jsstring2string(s) { return s; } /*** META ((export #t) (peephole (prefix "'\\u1E9C' +"))) */ function sc_jsstring2symbol(s) { return sc_SYMBOL_PREFIX + s; } /*** META ((export #t) (peephole (id))) */ function sc_string2jsstring(s) { return s; } /*** META ((export #t) (peephole (symbol2jsstring_immutable))) */ function sc_symbol2jsstring(s) { return s.slice(1); } /*** META ((export #t) (peephole (postfix ".slice(1)"))) */ function sc_keyword2jsstring(k) { return k.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9D' +"))) */ function sc_jsstring2keyword(s) { return sc_KEYWORD_PREFIX + s; } /*** META ((export #t) (type bool)) */ function sc_isKeyword(s) { return (typeof s === "string") && (s.charAt(0) === sc_KEYWORD_PREFIX); } /*** META ((export #t)) */ var sc_gensym = function() { var counter = 1000; return function(sym) { counter++; if (!sym) sym = sc_SYMBOL_PREFIX; return sym + "s" + counter + "~" + "^sC-GeNsYm "; }; }(); /*** META ((export #t) (type bool)) */ function sc_isEqual(o1, o2) { return ((o1 === o2) || (sc_isPair(o1) && sc_isPair(o2) && sc_isPairEqual(o1, o2, sc_isEqual)) || (sc_isVector(o1) && sc_isVector(o2) && sc_isVectorEqual(o1, o2, sc_isEqual))); } /*** META ((export number->symbol integer->symbol)) */ function sc_number2symbol(x, radix) { return sc_SYMBOL_PREFIX + sc_number2jsstring(x, radix); } /*** META ((export number->string integer->string)) */ var sc_number2string = sc_number2jsstring; /*** META ((export #t)) */ function sc_symbol2number(s, radix) { return sc_jsstring2number(s.slice(1), radix); } /*** META ((export #t)) */ var sc_string2number = sc_jsstring2number; /*** META ((export #t) (peephole (prefix "+" s))) ;; peephole will only apply if no radix is given. */ function sc_string2integer(s, radix) { if (!radix) return +s; return parseInt(s, radix); } /*** META ((export #t) (peephole (prefix "+"))) */ function sc_string2real(s) { return +s; } /*** META ((export #t) (type bool)) */ function sc_isSymbol(s) { return (typeof s === "string") && (s.charAt(0) === sc_SYMBOL_PREFIX); } /*** META ((export #t) (peephole (symbol2string_immutable))) */ function sc_symbol2string(s) { return s.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9C' +"))) */ function sc_string2symbol(s) { return sc_SYMBOL_PREFIX + s; } /*** META ((export symbol-append) (peephole (symbolAppend_immutable))) */ function sc_symbolAppend() { var res = sc_SYMBOL_PREFIX; for (var i = 0; i < arguments.length; i++) res += arguments[i].slice(1); return res; } /*** META ((export #t) (peephole (postfix ".val"))) */ function sc_char2string(c) { return c.val; } /*** META ((export #t) (peephole (hole 1 "'\\u1E9C' + " c ".val"))) */ function sc_char2symbol(c) { return sc_SYMBOL_PREFIX + c.val; } /*** META ((export #t) (type bool)) */ function sc_isString(s) { return (typeof s === "string") && (s.charAt(0) !== sc_SYMBOL_PREFIX); } /*** META ((export #t)) */ var sc_makeString = sc_makejsString; /*** META ((export #t)) */ function sc_string() { for (var i = 0; i < arguments.length; i++) arguments[i] = arguments[i].val; return "".concat.apply("", arguments); } /*** META ((export #t) (peephole (postfix ".length"))) */ function sc_stringLength(s) { return s.length; } /*** META ((export #t)) */ function sc_stringRef(s, k) { return new sc_Char(s.charAt(k)); } /* there's no stringSet in the immutable version function sc_stringSet(s, k, c) */ /*** META ((export string=?) (type bool) (peephole (hole 2 str1 " === " str2))) */ function sc_isStringEqual(s1, s2) { return s1 === s2; } /*** META ((export string?) (type bool) (peephole (hole 2 str1 " > " str2))) */ function sc_isStringGreater(s1, s2) { return s1 > s2; } /*** META ((export string<=?) (type bool) (peephole (hole 2 str1 " <= " str2))) */ function sc_isStringLessEqual(s1, s2) { return s1 <= s2; } /*** META ((export string>=?) (type bool) (peephole (hole 2 str1 " >= " str2))) */ function sc_isStringGreaterEqual(s1, s2) { return s1 >= s2; } /*** META ((export string-ci=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() === " str2 ".toLowerCase()"))) */ function sc_isStringCIEqual(s1, s2) { return s1.toLowerCase() === s2.toLowerCase(); } /*** META ((export string-ci?) (type bool) (peephole (hole 2 str1 ".toLowerCase() > " str2 ".toLowerCase()"))) */ function sc_isStringCIGreater(s1, s2) { return s1.toLowerCase() > s2.toLowerCase(); } /*** META ((export string-ci<=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() <= " str2 ".toLowerCase()"))) */ function sc_isStringCILessEqual(s1, s2) { return s1.toLowerCase() <= s2.toLowerCase(); } /*** META ((export string-ci>=?) (type bool) (peephole (hole 2 str1 ".toLowerCase() >= " str2 ".toLowerCase()"))) */ function sc_isStringCIGreaterEqual(s1, s2) { return s1.toLowerCase() >= s2.toLowerCase(); } /*** META ((export #t) (peephole (hole 3 s ".substring(" start ", " end ")"))) */ function sc_substring(s, start, end) { return s.substring(start, end); } /*** META ((export #t)) */ function sc_isSubstring_at(s1, s2, i) { return s2 == s1.substring(i, i+ s2.length); } /*** META ((export #t) (peephole (infix 0 #f "+" "''"))) */ function sc_stringAppend() { return "".concat.apply("", arguments); } /*** META ((export #t)) */ var sc_string2list = sc_jsstring2list; /*** META ((export #t)) */ var sc_list2string = sc_list2jsstring; /*** META ((export #t) (peephole (id))) */ function sc_stringCopy(s) { return s; } /* there's no string-fill in the immutable version function sc_stringFill(s, c) */ /*** META ((export #t) (peephole (postfix ".slice(1)"))) */ function sc_keyword2string(o) { return o.slice(1); } /*** META ((export #t) (peephole (prefix "'\\u1E9D' +"))) */ function sc_string2keyword(o) { return sc_KEYWORD_PREFIX + o; } String.prototype.sc_toDisplayString = function() { if (this.charAt(0) === sc_SYMBOL_PREFIX) // TODO: care for symbols with spaces (escape-chars symbols). return this.slice(1); else if (this.charAt(0) === sc_KEYWORD_PREFIX) return ":" + this.slice(1); else return this.toString(); }; String.prototype.sc_toWriteString = function() { if (this.charAt(0) === sc_SYMBOL_PREFIX) // TODO: care for symbols with spaces (escape-chars symbols). return this.slice(1); else if (this.charAt(0) === sc_KEYWORD_PREFIX) return ":" + this.slice(1); else return '"' + sc_escapeWriteString(this) + '"'; }; /* Exported Variables */ var BgL_testzd2boyerzd2; var BgL_nboyerzd2benchmarkzd2; var BgL_setupzd2boyerzd2; /* End Exports */ var translate_term_nboyer; var translate_args_nboyer; var untranslate_term_nboyer; var BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer; var BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer; var translate_alist_nboyer; var apply_subst_nboyer; var apply_subst_lst_nboyer; var tautologyp_nboyer; var if_constructor_nboyer; var rewrite_count_nboyer; var rewrite_nboyer; var rewrite_args_nboyer; var unify_subst_nboyer; var one_way_unify1_nboyer; var false_term_nboyer; var true_term_nboyer; var trans_of_implies1_nboyer; var is_term_equal_nboyer; var is_term_member_nboyer; var const_nboyer; var sc_const_3_nboyer; var sc_const_4_nboyer; { (sc_const_4_nboyer = (new sc_Pair("\u1E9Cimplies",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cu",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cw",null)))))),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cw",null)))))),null))))))); (sc_const_3_nboyer = sc_list((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccompile",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Ccodegen",(new sc_Pair((new sc_Pair("\u1E9Coptimize",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreaterp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clesseqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cboolean",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ciff",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceven1",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Codd",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccountps-",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccountps-loop",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfact-",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfact-loop",(new sc_Pair("\u1E9Ci",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdivides",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-true",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-false",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctautology-checker",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctautologyp",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfalsify",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfalsify1",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime1",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair("\u1E9Cp",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))))),(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cc",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cplus-fringe",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair("\u1E9Cenvrn",null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmc-flatten",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cintersect",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Ck",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ck",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Csort-lp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus1",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Ci",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cbase",null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cj",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cj",(new sc_Pair((1),null)))))),null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Ci",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cw",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cz",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnlistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csamefringe",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cz",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cw",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair(sc_list("\u1E9Cand", (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Ca",null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cb",null)))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cl",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cl",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdsort",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx1",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx2",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx3",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx4",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx5",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx6",(new sc_Pair("\u1E9Cx7",null)))))),null)))))),null)))))),null)))))),null)))))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((6),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx7",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cy",(new sc_Pair((2),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csigma",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Ci",null)))),null)))))),(new sc_Pair((2),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cz",null)))),null)))))),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Ca",null)))),null)))),(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Cb",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair("\u1E9Cz",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cassignedp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair((new sc_Pair("\u1E9Cset",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cval",(new sc_Pair("\u1E9Cmem",null)))))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair("\u1E9Cval",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cmem",null)))))),null)))))))),null)))))))); (const_nboyer = (new sc_Pair((new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))))),null))))))))))); BgL_nboyerzd2benchmarkzd2 = function() { var args = null; for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { args = sc_cons(arguments[sc_tmp], args); } var n; return ((n = ((args === null)?(0):(args.car))), (BgL_setupzd2boyerzd2()), (BgL_runzd2benchmarkzd2(("nboyer"+(sc_number2string(n))), (1), function() { return (BgL_testzd2boyerzd2(n)); }, function(rewrites) { if ((sc_isNumber(rewrites))) switch (n) { case (0): return (rewrites===(95024)); break; case (1): return (rewrites===(591777)); break; case (2): return (rewrites===(1813975)); break; case (3): return (rewrites===(5375678)); break; case (4): return (rewrites===(16445406)); break; case (5): return (rewrites===(51507739)); break; default: return true; break; } else return false; }))); }; BgL_setupzd2boyerzd2 = function() { return true; }; BgL_testzd2boyerzd2 = function() { return true; }; translate_term_nboyer = function(term) { var lst; return (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((translate_term_nboyer((lst.car))), (translate_args_nboyer((lst.cdr)))))))))); }; translate_args_nboyer = function(lst) { var sc_lst_5; var term; return ((lst === null)?null:(new sc_Pair(((term = (lst.car)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))), ((sc_lst_5 = (lst.cdr)), ((sc_lst_5 === null)?null:(new sc_Pair((translate_term_nboyer((sc_lst_5.car))), (translate_args_nboyer((sc_lst_5.cdr)))))))))); }; untranslate_term_nboyer = function(term) { var optrOpnd; var tail1131; var L1127; var falseHead1130; var symbol_record; if (!(term instanceof sc_Pair)) return term; else { (falseHead1130 = (new sc_Pair(null, null))); (L1127 = (term.cdr)); (tail1131 = falseHead1130); while (!(L1127 === null)) { { (tail1131.cdr = (new sc_Pair((untranslate_term_nboyer((L1127.car))), null))); (tail1131 = (tail1131.cdr)); (L1127 = (L1127.cdr)); } } (optrOpnd = (falseHead1130.cdr)); return (new sc_Pair(((symbol_record = (term.car)), (symbol_record[(0)])), optrOpnd)); } }; BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer = function(sym) { var r; var x; return ((x = (sc_assq(sym, BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), ((x!== false)?(x.cdr):((r = [sym, null]), (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = (new sc_Pair((new sc_Pair(sym, r)), BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), r))); }; (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); translate_alist_nboyer = function(alist) { var sc_alist_6; var term; return ((alist === null)?null:(new sc_Pair((new sc_Pair((alist.car.car), ((term = (alist.car.cdr)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))))), ((sc_alist_6 = (alist.cdr)), ((sc_alist_6 === null)?null:(new sc_Pair((new sc_Pair((sc_alist_6.car.car), (translate_term_nboyer((sc_alist_6.car.cdr))))), (translate_alist_nboyer((sc_alist_6.cdr)))))))))); }; apply_subst_nboyer = function(alist, term) { var lst; var temp_temp; return (!(term instanceof sc_Pair)?((temp_temp = (sc_assq(term, alist))), ((temp_temp!== false)?(temp_temp.cdr):term)):(new sc_Pair((term.car), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), (apply_subst_lst_nboyer(alist, (lst.cdr)))))))))); }; apply_subst_lst_nboyer = function(alist, lst) { var sc_lst_7; return ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), ((sc_lst_7 = (lst.cdr)), ((sc_lst_7 === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (sc_lst_7.car))), (apply_subst_lst_nboyer(alist, (sc_lst_7.cdr)))))))))); }; tautologyp_nboyer = function(sc_x_11, true_lst, false_lst) { var tmp1125; var x; var tmp1126; var sc_x_8; var sc_tmp1125_9; var sc_tmp1126_10; var sc_x_11; var true_lst; var false_lst; while (true) { if ((((sc_tmp1126_10 = (is_term_equal_nboyer(sc_x_11, true_term_nboyer))), ((sc_tmp1126_10!== false)?sc_tmp1126_10:(is_term_member_nboyer(sc_x_11, true_lst))))!== false)) return true; else if ((((sc_tmp1125_9 = (is_term_equal_nboyer(sc_x_11, false_term_nboyer))), ((sc_tmp1125_9!== false)?sc_tmp1125_9:(is_term_member_nboyer(sc_x_11, false_lst))))!== false)) return false; else if (!(sc_x_11 instanceof sc_Pair)) return false; else if (((sc_x_11.car)===if_constructor_nboyer)) if ((((sc_x_8 = (sc_x_11.cdr.car)), (tmp1126 = (is_term_equal_nboyer(sc_x_8, true_term_nboyer))), ((tmp1126!== false)?tmp1126:(is_term_member_nboyer(sc_x_8, true_lst))))!== false)) (sc_x_11 = (sc_x_11.cdr.cdr.car)); else if ((((x = (sc_x_11.cdr.car)), (tmp1125 = (is_term_equal_nboyer(x, false_term_nboyer))), ((tmp1125!== false)?tmp1125:(is_term_member_nboyer(x, false_lst))))!== false)) (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); else if (((tautologyp_nboyer((sc_x_11.cdr.cdr.car), (new sc_Pair((sc_x_11.cdr.car), true_lst)), false_lst))!== false)) { (false_lst = (new sc_Pair((sc_x_11.cdr.car), false_lst))); (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car)); } else return false; else return false; } }; (if_constructor_nboyer = "\u1E9C*"); (rewrite_count_nboyer = (0)); rewrite_nboyer = function(term) { var term2; var sc_term_12; var lst; var symbol_record; var sc_lst_13; { (++rewrite_count_nboyer); if (!(term instanceof sc_Pair)) return term; else { (sc_term_12 = (new sc_Pair((term.car), ((sc_lst_13 = (term.cdr)), ((sc_lst_13 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_13.car))), (rewrite_args_nboyer((sc_lst_13.cdr)))))))))); (lst = ((symbol_record = (term.car)), (symbol_record[(1)]))); while (true) { if ((lst === null)) return sc_term_12; else if ((((term2 = ((lst.car).cdr.car)), (unify_subst_nboyer = null), (one_way_unify1_nboyer(sc_term_12, term2)))!== false)) return (rewrite_nboyer((apply_subst_nboyer(unify_subst_nboyer, ((lst.car).cdr.cdr.car))))); else (lst = (lst.cdr)); } } } }; rewrite_args_nboyer = function(lst) { var sc_lst_14; return ((lst === null)?null:(new sc_Pair((rewrite_nboyer((lst.car))), ((sc_lst_14 = (lst.cdr)), ((sc_lst_14 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_14.car))), (rewrite_args_nboyer((sc_lst_14.cdr)))))))))); }; (unify_subst_nboyer = "\u1E9C*"); one_way_unify1_nboyer = function(term1, term2) { var lst1; var lst2; var temp_temp; if (!(term2 instanceof sc_Pair)) { (temp_temp = (sc_assq(term2, unify_subst_nboyer))); if ((temp_temp!== false)) return (is_term_equal_nboyer(term1, (temp_temp.cdr))); else if ((sc_isNumber(term2))) return (sc_isEqual(term1, term2)); else { (unify_subst_nboyer = (new sc_Pair((new sc_Pair(term2, term1)), unify_subst_nboyer))); return true; } } else if (!(term1 instanceof sc_Pair)) return false; else if (((term1.car)===(term2.car))) { (lst1 = (term1.cdr)); (lst2 = (term2.cdr)); while (true) { if ((lst1 === null)) return (lst2 === null); else if ((lst2 === null)) return false; else if (((one_way_unify1_nboyer((lst1.car), (lst2.car)))!== false)) { (lst1 = (lst1.cdr)); (lst2 = (lst2.cdr)); } else return false; } } else return false; }; (false_term_nboyer = "\u1E9C*"); (true_term_nboyer = "\u1E9C*"); trans_of_implies1_nboyer = function(n) { var sc_n_15; return ((sc_isEqual(n, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (n-(1)), n)), ((sc_n_15 = (n-(1))), ((sc_isEqual(sc_n_15, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (sc_n_15-(1)), sc_n_15)), (trans_of_implies1_nboyer((sc_n_15-(1))))))))))); }; is_term_equal_nboyer = function(x, y) { var lst1; var lst2; var r2; var r1; if ((x instanceof sc_Pair)) if ((y instanceof sc_Pair)) if ((((r1 = (x.car)), (r2 = (y.car)), (r1===r2))!== false)) { (lst1 = (x.cdr)); (lst2 = (y.cdr)); while (true) { if ((lst1 === null)) return (lst2 === null); else if ((lst2 === null)) return false; else if (((is_term_equal_nboyer((lst1.car), (lst2.car)))!== false)) { (lst1 = (lst1.cdr)); (lst2 = (lst2.cdr)); } else return false; } } else return false; else return false; else return (sc_isEqual(x, y)); }; is_term_member_nboyer = function(x, lst) { var x; var lst; while (true) { if ((lst === null)) return false; else if (((is_term_equal_nboyer(x, (lst.car)))!== false)) return true; else (lst = (lst.cdr)); } }; BgL_setupzd2boyerzd2 = function() { var symbol_record; var value; var BgL_sc_symbolzd2record_16zd2; var sym; var sc_sym_17; var term; var lst; var sc_term_18; var sc_term_19; { (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null); (if_constructor_nboyer = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer("\u1E9Cif"))); (false_term_nboyer = ((sc_term_19 = (new sc_Pair("\u1E9Cf",null))), (!(sc_term_19 instanceof sc_Pair)?sc_term_19:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_19.car))), (translate_args_nboyer((sc_term_19.cdr)))))))); (true_term_nboyer = ((sc_term_18 = (new sc_Pair("\u1E9Ct",null))), (!(sc_term_18 instanceof sc_Pair)?sc_term_18:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_18.car))), (translate_args_nboyer((sc_term_18.cdr)))))))); (lst = sc_const_3_nboyer); while (!(lst === null)) { { (term = (lst.car)); if (((term instanceof sc_Pair)&&(((term.car)==="\u1E9Cequal")&&((term.cdr.car) instanceof sc_Pair)))) { (sc_sym_17 = ((term.cdr.car).car)); (value = (new sc_Pair((!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr)))))), ((sym = ((term.cdr.car).car)), (BgL_sc_symbolzd2record_16zd2 = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sym))), (BgL_sc_symbolzd2record_16zd2[(1)]))))); (symbol_record = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sc_sym_17))); (symbol_record[(1)] = value); } else (sc_error("ADD-LEMMA did not like term: ", term)); (lst = (lst.cdr)); } } return true; } }; BgL_testzd2boyerzd2 = function(n) { var optrOpnd; var term; var sc_n_20; var answer; var sc_term_21; var sc_term_22; { (rewrite_count_nboyer = (0)); (term = sc_const_4_nboyer); (sc_n_20 = n); while (!(sc_n_20=== 0)) { { (term = (sc_list("\u1E9Cor", term, (new sc_Pair("\u1E9Cf",null))))); (--sc_n_20); } } (sc_term_22 = term); if (!(sc_term_22 instanceof sc_Pair)) (optrOpnd = sc_term_22); else (optrOpnd = (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_22.car))), (translate_args_nboyer((sc_term_22.cdr)))))); (sc_term_21 = (apply_subst_nboyer(((const_nboyer === null)?null:(new sc_Pair((new sc_Pair((const_nboyer.car.car), (translate_term_nboyer((const_nboyer.car.cdr))))), (translate_alist_nboyer((const_nboyer.cdr)))))), optrOpnd))); (answer = (tautologyp_nboyer((rewrite_nboyer(sc_term_21)), null, null))); (sc_write(rewrite_count_nboyer)); (sc_display(" rewrites")); (sc_newline()); if ((answer!== false)) return rewrite_count_nboyer; else return false; } }; } /* Exported Variables */ var BgL_parsezd2ze3nbzd2treesze3; var BgL_earleyzd2benchmarkzd2; var BgL_parsezd2ze3parsedzf3zc2; var test; var BgL_parsezd2ze3treesz31; var BgL_makezd2parserzd2; /* End Exports */ var const_earley; { (const_earley = (new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair((new sc_Pair("\u1E9Ca",null)),(new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair("\u1E9Cs",null)))),null)))))),null))); BgL_makezd2parserzd2 = function(grammar, lexer) { var i; var parser_descr; var def_loop; var nb_nts; var names; var steps; var predictors; var enders; var starters; var nts; var sc_names_1; var sc_steps_2; var sc_predictors_3; var sc_enders_4; var sc_starters_5; var nb_confs; var BgL_sc_defzd2loop_6zd2; var BgL_sc_nbzd2nts_7zd2; var sc_nts_8; var BgL_sc_defzd2loop_9zd2; var ind; { ind = function(nt, sc_nts_10) { var i; { (i = ((sc_nts_10.length)-(1))); while (true) { if ((i>=(0))) if ((sc_isEqual((sc_nts_10[i]), nt))) return i; else (--i); else return false; } } }; (sc_nts_8 = ((BgL_sc_defzd2loop_9zd2 = function(defs, sc_nts_11) { var rule_loop; var head; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, sc_nts_12) { var nt; var l; var sc_nts_13; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (l = rule); (sc_nts_13 = sc_nts_12); while ((l instanceof sc_Pair)) { { (nt = (l.car)); (l = (l.cdr)); (sc_nts_13 = (((sc_member(nt, sc_nts_13))!== false)?sc_nts_13:(new sc_Pair(nt, sc_nts_13)))); } } return (rule_loop((rules.cdr), sc_nts_13)); } else return (BgL_sc_defzd2loop_9zd2((defs.cdr), sc_nts_12)); }), (rule_loop((def.cdr), (((sc_member(head, sc_nts_11))!== false)?sc_nts_11:(new sc_Pair(head, sc_nts_11)))))):(sc_list2vector((sc_reverse(sc_nts_11))))); }), (BgL_sc_defzd2loop_9zd2(grammar, null)))); (BgL_sc_nbzd2nts_7zd2 = (sc_nts_8.length)); (nb_confs = (((BgL_sc_defzd2loop_6zd2 = function(defs, BgL_sc_nbzd2confs_14zd2) { var rule_loop; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (rule_loop = function(rules, BgL_sc_nbzd2confs_15zd2) { var l; var BgL_sc_nbzd2confs_16zd2; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (l = rule); (BgL_sc_nbzd2confs_16zd2 = BgL_sc_nbzd2confs_15zd2); while ((l instanceof sc_Pair)) { { (l = (l.cdr)); (++BgL_sc_nbzd2confs_16zd2); } } return (rule_loop((rules.cdr), (BgL_sc_nbzd2confs_16zd2+(1)))); } else return (BgL_sc_defzd2loop_6zd2((defs.cdr), BgL_sc_nbzd2confs_15zd2)); }), (rule_loop((def.cdr), BgL_sc_nbzd2confs_14zd2))):BgL_sc_nbzd2confs_14zd2); }), (BgL_sc_defzd2loop_6zd2(grammar, (0))))+BgL_sc_nbzd2nts_7zd2)); (sc_starters_5 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_enders_4 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_predictors_3 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null))); (sc_steps_2 = (sc_makeVector(nb_confs, false))); (sc_names_1 = (sc_makeVector(nb_confs, false))); (nts = sc_nts_8); (starters = sc_starters_5); (enders = sc_enders_4); (predictors = sc_predictors_3); (steps = sc_steps_2); (names = sc_names_1); (nb_nts = (sc_nts_8.length)); (i = (nb_nts-(1))); while ((i>=(0))) { { (sc_steps_2[i] = (i-nb_nts)); (sc_names_1[i] = (sc_list((sc_nts_8[i]), (0)))); (sc_enders_4[i] = (sc_list(i))); (--i); } } def_loop = function(defs, conf) { var rule_loop; var head; var def; return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, conf, rule_num) { var i; var sc_i_17; var nt; var l; var sc_conf_18; var sc_i_19; var rule; if ((rules instanceof sc_Pair)) { (rule = (rules.car)); (names[conf] = (sc_list(head, rule_num))); (sc_i_19 = (ind(head, nts))); (starters[sc_i_19] = (new sc_Pair(conf, (starters[sc_i_19])))); (l = rule); (sc_conf_18 = conf); while ((l instanceof sc_Pair)) { { (nt = (l.car)); (steps[sc_conf_18] = (ind(nt, nts))); (sc_i_17 = (ind(nt, nts))); (predictors[sc_i_17] = (new sc_Pair(sc_conf_18, (predictors[sc_i_17])))); (l = (l.cdr)); (++sc_conf_18); } } (steps[sc_conf_18] = ((ind(head, nts))-nb_nts)); (i = (ind(head, nts))); (enders[i] = (new sc_Pair(sc_conf_18, (enders[i])))); return (rule_loop((rules.cdr), (sc_conf_18+(1)), (rule_num+(1)))); } else return (def_loop((defs.cdr), conf)); }), (rule_loop((def.cdr), conf, (1)))):undefined); }; (def_loop(grammar, (sc_nts_8.length))); (parser_descr = [lexer, sc_nts_8, sc_starters_5, sc_enders_4, sc_predictors_3, sc_steps_2, sc_names_1]); return function(input) { var optrOpnd; var sc_optrOpnd_20; var sc_optrOpnd_21; var sc_optrOpnd_22; var loop1; var BgL_sc_stateza2_23za2; var toks; var BgL_sc_nbzd2nts_24zd2; var sc_steps_25; var sc_enders_26; var state_num; var BgL_sc_statesza2_27za2; var states; var i; var conf; var l; var tok_nts; var sc_i_28; var sc_i_29; var l1; var l2; var tok; var tail1129; var L1125; var goal_enders; var BgL_sc_statesza2_30za2; var BgL_sc_nbzd2nts_31zd2; var BgL_sc_nbzd2confs_32zd2; var nb_toks; var goal_starters; var sc_states_33; var BgL_sc_nbzd2confs_34zd2; var BgL_sc_nbzd2toks_35zd2; var sc_toks_36; var falseHead1128; var sc_names_37; var sc_steps_38; var sc_predictors_39; var sc_enders_40; var sc_starters_41; var sc_nts_42; var lexer; var sc_ind_43; var make_states; var BgL_sc_confzd2setzd2getza2_44za2; var conf_set_merge_new_bang; var conf_set_adjoin; var BgL_sc_confzd2setzd2adjoinza2_45za2; var BgL_sc_confzd2setzd2adjoinza2za2_46z00; var conf_set_union; var forw; var is_parsed; var deriv_trees; var BgL_sc_derivzd2treesza2_47z70; var nb_deriv_trees; var BgL_sc_nbzd2derivzd2treesza2_48za2; { sc_ind_43 = function(nt, sc_nts_49) { var i; { (i = ((sc_nts_49.length)-(1))); while (true) { if ((i>=(0))) if ((sc_isEqual((sc_nts_49[i]), nt))) return i; else (--i); else return false; } } }; make_states = function(BgL_sc_nbzd2toks_50zd2, BgL_sc_nbzd2confs_51zd2) { var v; var i; var sc_states_52; { (sc_states_52 = (sc_makeVector((BgL_sc_nbzd2toks_50zd2+(1)), false))); (i = BgL_sc_nbzd2toks_50zd2); while ((i>=(0))) { { (v = (sc_makeVector((BgL_sc_nbzd2confs_51zd2+(1)), false))); (v[(0)] = (-1)); (sc_states_52[i] = v); (--i); } } return sc_states_52; } }; BgL_sc_confzd2setzd2getza2_44za2 = function(state, BgL_sc_statezd2num_53zd2, sc_conf_54) { var conf_set; var BgL_sc_confzd2set_55zd2; return ((BgL_sc_confzd2set_55zd2 = (state[(sc_conf_54+(1))])), ((BgL_sc_confzd2set_55zd2!== false)?BgL_sc_confzd2set_55zd2:((conf_set = (sc_makeVector((BgL_sc_statezd2num_53zd2+(6)), false))), (conf_set[(1)] = (-3)), (conf_set[(2)] = (-1)), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)), (state[(sc_conf_54+(1))] = conf_set), conf_set))); }; conf_set_merge_new_bang = function(conf_set) { return ((conf_set[((conf_set[(1)])+(5))] = (conf_set[(4)])), (conf_set[(1)] = (conf_set[(3)])), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1))); }; conf_set_adjoin = function(state, conf_set, sc_conf_56, i) { var tail; return ((tail = (conf_set[(3)])), (conf_set[(i+(5))] = (-1)), (conf_set[(tail+(5))] = i), (conf_set[(3)] = i), ((tail<(0))?((conf_set[(0)] = (state[(0)])), (state[(0)] = sc_conf_56)):undefined)); }; BgL_sc_confzd2setzd2adjoinza2_45za2 = function(sc_states_57, BgL_sc_statezd2num_58zd2, l, i) { var conf_set; var sc_conf_59; var l1; var state; { (state = (sc_states_57[BgL_sc_statezd2num_58zd2])); (l1 = l); while ((l1 instanceof sc_Pair)) { { (sc_conf_59 = (l1.car)); (conf_set = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_58zd2, sc_conf_59))); if (((conf_set[(i+(5))])=== false)) { (conf_set_adjoin(state, conf_set, sc_conf_59, i)); (l1 = (l1.cdr)); } else (l1 = (l1.cdr)); } } return undefined; } }; BgL_sc_confzd2setzd2adjoinza2za2_46z00 = function(sc_states_60, BgL_sc_statesza2_61za2, BgL_sc_statezd2num_62zd2, sc_conf_63, i) { var BgL_sc_confzd2setza2_64z70; var BgL_sc_stateza2_65za2; var conf_set; var state; return ((state = (sc_states_60[BgL_sc_statezd2num_62zd2])), ((((conf_set = (state[(sc_conf_63+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)?((BgL_sc_stateza2_65za2 = (BgL_sc_statesza2_61za2[BgL_sc_statezd2num_62zd2])), (BgL_sc_confzd2setza2_64z70 = (BgL_sc_confzd2setzd2getza2_44za2(BgL_sc_stateza2_65za2, BgL_sc_statezd2num_62zd2, sc_conf_63))), (((BgL_sc_confzd2setza2_64z70[(i+(5))])=== false)?(conf_set_adjoin(BgL_sc_stateza2_65za2, BgL_sc_confzd2setza2_64z70, sc_conf_63, i)):undefined), true):false)); }; conf_set_union = function(state, conf_set, sc_conf_66, other_set) { var i; { (i = (other_set[(2)])); while ((i>=(0))) { if (((conf_set[(i+(5))])=== false)) { (conf_set_adjoin(state, conf_set, sc_conf_66, i)); (i = (other_set[(i+(5))])); } else (i = (other_set[(i+(5))])); } return undefined; } }; forw = function(sc_states_67, BgL_sc_statezd2num_68zd2, sc_starters_69, sc_enders_70, sc_predictors_71, sc_steps_72, sc_nts_73) { var next_set; var next; var conf_set; var ender; var l; var starter_set; var starter; var sc_l_74; var sc_loop1_75; var head; var BgL_sc_confzd2set_76zd2; var BgL_sc_statezd2num_77zd2; var state; var sc_states_78; var preds; var BgL_sc_confzd2set_79zd2; var step; var sc_conf_80; var BgL_sc_nbzd2nts_81zd2; var sc_state_82; { (sc_state_82 = (sc_states_67[BgL_sc_statezd2num_68zd2])); (BgL_sc_nbzd2nts_81zd2 = (sc_nts_73.length)); while (true) { { (sc_conf_80 = (sc_state_82[(0)])); if ((sc_conf_80>=(0))) { (step = (sc_steps_72[sc_conf_80])); (BgL_sc_confzd2set_79zd2 = (sc_state_82[(sc_conf_80+(1))])); (head = (BgL_sc_confzd2set_79zd2[(4)])); (sc_state_82[(0)] = (BgL_sc_confzd2set_79zd2[(0)])); (conf_set_merge_new_bang(BgL_sc_confzd2set_79zd2)); if ((step>=(0))) { (sc_l_74 = (sc_starters_69[step])); while ((sc_l_74 instanceof sc_Pair)) { { (starter = (sc_l_74.car)); (starter_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, starter))); if (((starter_set[(BgL_sc_statezd2num_68zd2+(5))])=== false)) { (conf_set_adjoin(sc_state_82, starter_set, starter, BgL_sc_statezd2num_68zd2)); (sc_l_74 = (sc_l_74.cdr)); } else (sc_l_74 = (sc_l_74.cdr)); } } (l = (sc_enders_70[step])); while ((l instanceof sc_Pair)) { { (ender = (l.car)); if ((((conf_set = (sc_state_82[(ender+(1))])), ((conf_set!== false)?(conf_set[(BgL_sc_statezd2num_68zd2+(5))]):false))!== false)) { (next = (sc_conf_80+(1))); (next_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, next))); (conf_set_union(sc_state_82, next_set, next, BgL_sc_confzd2set_79zd2)); (l = (l.cdr)); } else (l = (l.cdr)); } } } else { (preds = (sc_predictors_71[(step+BgL_sc_nbzd2nts_81zd2)])); (sc_states_78 = sc_states_67); (state = sc_state_82); (BgL_sc_statezd2num_77zd2 = BgL_sc_statezd2num_68zd2); (BgL_sc_confzd2set_76zd2 = BgL_sc_confzd2set_79zd2); sc_loop1_75 = function(l) { var sc_state_83; var BgL_sc_nextzd2set_84zd2; var sc_next_85; var pred_set; var i; var pred; if ((l instanceof sc_Pair)) { (pred = (l.car)); (i = head); while ((i>=(0))) { { (pred_set = ((sc_state_83 = (sc_states_78[i])), (sc_state_83[(pred+(1))]))); if ((pred_set!== false)) { (sc_next_85 = (pred+(1))); (BgL_sc_nextzd2set_84zd2 = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_77zd2, sc_next_85))); (conf_set_union(state, BgL_sc_nextzd2set_84zd2, sc_next_85, pred_set)); } (i = (BgL_sc_confzd2set_76zd2[(i+(5))])); } } return (sc_loop1_75((l.cdr))); } else return undefined; }; (sc_loop1_75(preds)); } } else return undefined; } } } }; is_parsed = function(nt, i, j, sc_nts_86, sc_enders_87, sc_states_88) { var conf_set; var state; var sc_conf_89; var l; var BgL_sc_ntza2_90za2; { (BgL_sc_ntza2_90za2 = (sc_ind_43(nt, sc_nts_86))); if ((BgL_sc_ntza2_90za2!== false)) { (sc_nts_86.length); (l = (sc_enders_87[BgL_sc_ntza2_90za2])); while (true) { if ((l instanceof sc_Pair)) { (sc_conf_89 = (l.car)); if ((((state = (sc_states_88[j])), (conf_set = (state[(sc_conf_89+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) return true; else (l = (l.cdr)); } else return false; } } else return false; } }; deriv_trees = function(sc_conf_91, i, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2) { var sc_loop1_98; var prev; var name; return ((name = (sc_names_94[sc_conf_91])), ((name!== false)?((sc_conf_91=(0))) if (((k>=i)&&(((sc_state_99 = (sc_states_96[k])), (conf_set = (sc_state_99[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) { (prev_trees = (deriv_trees(prev, i, k, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); (ender_trees = (deriv_trees(ender, k, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2))); loop3 = function(l3, l2) { var l4; var sc_l2_100; var ender_tree; if ((l3 instanceof sc_Pair)) { (ender_tree = (sc_list((l3.car)))); (l4 = prev_trees); (sc_l2_100 = l2); while ((l4 instanceof sc_Pair)) { { (sc_l2_100 = (new sc_Pair((sc_append((l4.car), ender_tree)), sc_l2_100))); (l4 = (l4.cdr)); } } return (loop3((l3.cdr), sc_l2_100)); } else return (loop2((ender_set[(k+(5))]), l2)); }; return (loop3(ender_trees, l2)); } else (k = (ender_set[(k+(5))])); else return (sc_loop1_98((l1.cdr), l2)); } }; return (loop2((ender_set[(2)]), l2)); } else (l1 = (l1.cdr)); } else return l2; } }), (sc_loop1_98((sc_enders_92[(sc_steps_93[prev])]), null))))); }; BgL_sc_derivzd2treesza2_47z70 = function(nt, i, j, sc_nts_101, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106) { var conf_set; var state; var sc_conf_107; var l; var trees; var BgL_sc_nbzd2nts_108zd2; var BgL_sc_ntza2_109za2; { (BgL_sc_ntza2_109za2 = (sc_ind_43(nt, sc_nts_101))); if ((BgL_sc_ntza2_109za2!== false)) { (BgL_sc_nbzd2nts_108zd2 = (sc_nts_101.length)); (l = (sc_enders_102[BgL_sc_ntza2_109za2])); (trees = null); while ((l instanceof sc_Pair)) { { (sc_conf_107 = (l.car)); if ((((state = (sc_states_106[j])), (conf_set = (state[(sc_conf_107+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) { (l = (l.cdr)); (trees = (sc_append((deriv_trees(sc_conf_107, i, j, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106, BgL_sc_nbzd2nts_108zd2)), trees))); } else (l = (l.cdr)); } } return trees; } else return false; } }; nb_deriv_trees = function(sc_conf_110, i, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2) { var sc_loop1_116; var tmp1124; var prev; return ((prev = (sc_conf_110-(1))), ((((tmp1124 = (sc_conf_110=(0))) { if (((k>=i)&&(((state = (sc_states_114[k])), (conf_set = (state[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))) { (nb_prev_trees = (nb_deriv_trees(prev, i, k, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); (nb_ender_trees = (nb_deriv_trees(ender, k, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2))); (k = (ender_set[(k+(5))])); (n +=(nb_prev_trees*nb_ender_trees)); } else (k = (ender_set[(k+(5))])); } return (sc_loop1_116((l.cdr), n)); } else (l = (l.cdr)); } else return sc_n_118; } }), (sc_loop1_116((sc_enders_111[(sc_steps_112[prev])]), (0)))))); }; BgL_sc_nbzd2derivzd2treesza2_48za2 = function(nt, i, j, sc_nts_119, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123) { var conf_set; var state; var sc_conf_124; var l; var nb_trees; var BgL_sc_nbzd2nts_125zd2; var BgL_sc_ntza2_126za2; { (BgL_sc_ntza2_126za2 = (sc_ind_43(nt, sc_nts_119))); if ((BgL_sc_ntza2_126za2!== false)) { (BgL_sc_nbzd2nts_125zd2 = (sc_nts_119.length)); (l = (sc_enders_120[BgL_sc_ntza2_126za2])); (nb_trees = (0)); while ((l instanceof sc_Pair)) { { (sc_conf_124 = (l.car)); if ((((state = (sc_states_123[j])), (conf_set = (state[(sc_conf_124+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)) { (l = (l.cdr)); (nb_trees = ((nb_deriv_trees(sc_conf_124, i, j, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123, BgL_sc_nbzd2nts_125zd2))+nb_trees)); } else (l = (l.cdr)); } } return nb_trees; } else return false; } }; (lexer = (parser_descr[(0)])); (sc_nts_42 = (parser_descr[(1)])); (sc_starters_41 = (parser_descr[(2)])); (sc_enders_40 = (parser_descr[(3)])); (sc_predictors_39 = (parser_descr[(4)])); (sc_steps_38 = (parser_descr[(5)])); (sc_names_37 = (parser_descr[(6)])); (falseHead1128 = (new sc_Pair(null, null))); (L1125 = (lexer(input))); (tail1129 = falseHead1128); while (!(L1125 === null)) { { (tok = (L1125.car)); (l1 = (tok.cdr)); (l2 = null); while ((l1 instanceof sc_Pair)) { { (sc_i_29 = (sc_ind_43((l1.car), sc_nts_42))); if ((sc_i_29!== false)) { (l1 = (l1.cdr)); (l2 = (new sc_Pair(sc_i_29, l2))); } else (l1 = (l1.cdr)); } } (sc_optrOpnd_22 = (new sc_Pair((tok.car), (sc_reverse(l2))))); (sc_optrOpnd_21 = (new sc_Pair(sc_optrOpnd_22, null))); (tail1129.cdr = sc_optrOpnd_21); (tail1129 = (tail1129.cdr)); (L1125 = (L1125.cdr)); } } (sc_optrOpnd_20 = (falseHead1128.cdr)); (sc_toks_36 = (sc_list2vector(sc_optrOpnd_20))); (BgL_sc_nbzd2toks_35zd2 = (sc_toks_36.length)); (BgL_sc_nbzd2confs_34zd2 = (sc_steps_38.length)); (sc_states_33 = (make_states(BgL_sc_nbzd2toks_35zd2, BgL_sc_nbzd2confs_34zd2))); (goal_starters = (sc_starters_41[(0)])); (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (0), goal_starters, (0))); (forw(sc_states_33, (0), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42)); (sc_i_28 = (0)); while ((sc_i_28=(0))) { { (states = sc_states_33); (BgL_sc_statesza2_27za2 = BgL_sc_statesza2_30za2); (state_num = i); (sc_enders_26 = sc_enders_40); (sc_steps_25 = sc_steps_38); (BgL_sc_nbzd2nts_24zd2 = BgL_sc_nbzd2nts_31zd2); (toks = sc_toks_36); (BgL_sc_stateza2_23za2 = (BgL_sc_statesza2_30za2[i])); loop1 = function() { var sc_loop1_127; var prev; var BgL_sc_statesza2_128za2; var sc_states_129; var j; var i; var sc_i_130; var head; var conf_set; var sc_conf_131; { (sc_conf_131 = (BgL_sc_stateza2_23za2[(0)])); if ((sc_conf_131>=(0))) { (conf_set = (BgL_sc_stateza2_23za2[(sc_conf_131+(1))])); (head = (conf_set[(4)])); (BgL_sc_stateza2_23za2[(0)] = (conf_set[(0)])); (conf_set_merge_new_bang(conf_set)); (sc_i_130 = head); while ((sc_i_130>=(0))) { { (i = sc_i_130); (j = state_num); (sc_states_129 = states); (BgL_sc_statesza2_128za2 = BgL_sc_statesza2_27za2); (prev = (sc_conf_131-(1))); if (((sc_conf_131>=BgL_sc_nbzd2nts_24zd2)&&((sc_steps_25[prev])>=(0)))) { sc_loop1_127 = function(l) { var k; var ender_set; var state; var ender; var l; while (true) { if ((l instanceof sc_Pair)) { (ender = (l.car)); (ender_set = ((state = (sc_states_129[j])), (state[(ender+(1))]))); if ((ender_set!== false)) { (k = (ender_set[(2)])); while ((k>=(0))) { { if ((k>=i)) if (((BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, k, prev, i))!== false)) (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, j, ender, k)); (k = (ender_set[(k+(5))])); } } return (sc_loop1_127((l.cdr))); } else (l = (l.cdr)); } else return undefined; } }; (sc_loop1_127((sc_enders_26[(sc_steps_25[prev])]))); } (sc_i_130 = (conf_set[(sc_i_130+(5))])); } } return (loop1()); } else return undefined; } }; (loop1()); (--i); } } (optrOpnd = BgL_sc_statesza2_30za2); return [sc_nts_42, sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_names_37, sc_toks_36, optrOpnd, is_parsed, BgL_sc_derivzd2treesza2_47z70, BgL_sc_nbzd2derivzd2treesza2_48za2]; } }; } }; BgL_parsezd2ze3parsedzf3zc2 = function(parse, nt, i, j) { var is_parsed; var states; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (states = (parse[(7)])), (is_parsed = (parse[(8)])), (is_parsed(nt, i, j, nts, enders, states))); }; BgL_parsezd2ze3treesz31 = function(parse, nt, i, j) { var BgL_sc_derivzd2treesza2_132z70; var states; var toks; var names; var steps; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (names = (parse[(5)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_derivzd2treesza2_132z70 = (parse[(9)])), (BgL_sc_derivzd2treesza2_132z70(nt, i, j, nts, enders, steps, names, toks, states))); }; BgL_parsezd2ze3nbzd2treesze3 = function(parse, nt, i, j) { var BgL_sc_nbzd2derivzd2treesza2_133za2; var states; var toks; var steps; var enders; var nts; return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_nbzd2derivzd2treesza2_133za2 = (parse[(10)])), (BgL_sc_nbzd2derivzd2treesza2_133za2(nt, i, j, nts, enders, steps, toks, states))); }; test = function(k) { var x; var p; return ((p = (BgL_makezd2parserzd2(const_earley, function(l) { var sc_x_134; var tail1134; var L1130; var falseHead1133; { (falseHead1133 = (new sc_Pair(null, null))); (tail1134 = falseHead1133); (L1130 = l); while (!(L1130 === null)) { { (tail1134.cdr = (new sc_Pair(((sc_x_134 = (L1130.car)), (sc_list(sc_x_134, sc_x_134))), null))); (tail1134 = (tail1134.cdr)); (L1130 = (L1130.cdr)); } } return (falseHead1133.cdr); } }))), (x = (p((sc_vector2list((sc_makeVector(k, "\u1E9Ca"))))))), (sc_length((BgL_parsezd2ze3treesz31(x, "\u1E9Cs", (0), k))))); }; BgL_earleyzd2benchmarkzd2 = function() { var args = null; for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) { args = sc_cons(arguments[sc_tmp], args); } var k; return ((k = ((args === null)?(7):(args.car))), (BgL_runzd2benchmarkzd2("earley", (1), function() { return (test(k)); }, function(result) { return ((sc_display(result)), (sc_newline()), result == 132); }))); }; } /************* END OF GENERATED CODE *************/ // Invoke this function to run a benchmark. // The first argument is a string identifying the benchmark. // The second argument is the number of times to run the benchmark. // The third argument is a function that runs the benchmark. // The fourth argument is a unary function that warns if the result // returned by the benchmark is incorrect. // // Example: // RunBenchmark("new Array()", // 1, // function () { new Array(1000000); } // function (v) { // return (v instanceof Array) && (v.length == 1000000); // }); SC_DEFAULT_OUT = new sc_GenericOutputPort(function(s) {}); SC_ERROR_OUT = SC_DEFAULT_OUT; function RunBenchmark(name, count, run, warn) { for (var n = 0; n < count; ++n) { result = run(); if (!warn(result)) { throw new Error("Earley or Boyer did incorrect number of rewrites"); } } } var BgL_runzd2benchmarkzd2 = RunBenchmark; closure-compiler-20130227+dfsg1/rhino/testsrc/benchmarks/v8-benchmarks-v5/deltablue.js0000644000175000017500000006206514433667662026634 0ustar apoapo// Copyright 2008 the V8 project authors. All rights reserved. // Copyright 1996 John Maloney and Mario Wolczko. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // This implementation of the DeltaBlue benchmark is derived // from the Smalltalk implementation by John Maloney and Mario // Wolczko. Some parts have been translated directly, whereas // others have been modified more aggresively to make it feel // more like a JavaScript program. var DeltaBlue = new BenchmarkSuite('DeltaBlue', 71104, [ new Benchmark('DeltaBlue', deltaBlue) ]); /** * A JavaScript implementation of the DeltaBlue constrain-solving * algorithm, as described in: * * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver" * Bjorn N. Freeman-Benson and John Maloney * January 1990 Communications of the ACM, * also available as University of Washington TR 89-08-06. * * Beware: this benchmark is written in a grotesque style where * the constraint model is built by side-effects from constructors. * I've kept it this way to avoid deviating too much from the original * implementation. */ /* --- O b j e c t M o d e l --- */ Object.prototype.inheritsFrom = function (shuper) { function Inheriter() { } Inheriter.prototype = shuper.prototype; this.prototype = new Inheriter(); this.superConstructor = shuper; } function OrderedCollection() { this.elms = new Array(); } OrderedCollection.prototype.add = function (elm) { this.elms.push(elm); } OrderedCollection.prototype.at = function (index) { return this.elms[index]; } OrderedCollection.prototype.size = function () { return this.elms.length; } OrderedCollection.prototype.removeFirst = function () { return this.elms.pop(); } OrderedCollection.prototype.remove = function (elm) { var index = 0, skipped = 0; for (var i = 0; i < this.elms.length; i++) { var value = this.elms[i]; if (value != elm) { this.elms[index] = value; index++; } else { skipped++; } } for (var i = 0; i < skipped; i++) this.elms.pop(); } /* --- * * S t r e n g t h * --- */ /** * Strengths are used to measure the relative importance of constraints. * New strengths may be inserted in the strength hierarchy without * disrupting current constraints. Strengths cannot be created outside * this class, so pointer comparison can be used for value comparison. */ function Strength(strengthValue, name) { this.strengthValue = strengthValue; this.name = name; } Strength.stronger = function (s1, s2) { return s1.strengthValue < s2.strengthValue; } Strength.weaker = function (s1, s2) { return s1.strengthValue > s2.strengthValue; } Strength.weakestOf = function (s1, s2) { return this.weaker(s1, s2) ? s1 : s2; } Strength.strongest = function (s1, s2) { return this.stronger(s1, s2) ? s1 : s2; } Strength.prototype.nextWeaker = function () { switch (this.strengthValue) { case 0: return Strength.WEAKEST; case 1: return Strength.WEAK_DEFAULT; case 2: return Strength.NORMAL; case 3: return Strength.STRONG_DEFAULT; case 4: return Strength.PREFERRED; case 5: return Strength.REQUIRED; } } // Strength constants. Strength.REQUIRED = new Strength(0, "required"); Strength.STONG_PREFERRED = new Strength(1, "strongPreferred"); Strength.PREFERRED = new Strength(2, "preferred"); Strength.STRONG_DEFAULT = new Strength(3, "strongDefault"); Strength.NORMAL = new Strength(4, "normal"); Strength.WEAK_DEFAULT = new Strength(5, "weakDefault"); Strength.WEAKEST = new Strength(6, "weakest"); /* --- * * C o n s t r a i n t * --- */ /** * An abstract class representing a system-maintainable relationship * (or "constraint") between a set of variables. A constraint supplies * a strength instance variable; concrete subclasses provide a means * of storing the constrained variables and other information required * to represent a constraint. */ function Constraint(strength) { this.strength = strength; } /** * Activate this constraint and attempt to satisfy it. */ Constraint.prototype.addConstraint = function () { this.addToGraph(); planner.incrementalAdd(this); } /** * Attempt to find a way to enforce this constraint. If successful, * record the solution, perhaps modifying the current dataflow * graph. Answer the constraint that this constraint overrides, if * there is one, or nil, if there isn't. * Assume: I am not already satisfied. */ Constraint.prototype.satisfy = function (mark) { this.chooseMethod(mark); if (!this.isSatisfied()) { if (this.strength == Strength.REQUIRED) alert("Could not satisfy a required constraint!"); return null; } this.markInputs(mark); var out = this.output(); var overridden = out.determinedBy; if (overridden != null) overridden.markUnsatisfied(); out.determinedBy = this; if (!planner.addPropagate(this, mark)) alert("Cycle encountered"); out.mark = mark; return overridden; } Constraint.prototype.destroyConstraint = function () { if (this.isSatisfied()) planner.incrementalRemove(this); else this.removeFromGraph(); } /** * Normal constraints are not input constraints. An input constraint * is one that depends on external state, such as the mouse, the * keybord, a clock, or some arbitraty piece of imperative code. */ Constraint.prototype.isInput = function () { return false; } /* --- * * U n a r y C o n s t r a i n t * --- */ /** * Abstract superclass for constraints having a single possible output * variable. */ function UnaryConstraint(v, strength) { UnaryConstraint.superConstructor.call(this, strength); this.myOutput = v; this.satisfied = false; this.addConstraint(); } UnaryConstraint.inheritsFrom(Constraint); /** * Adds this constraint to the constraint graph */ UnaryConstraint.prototype.addToGraph = function () { this.myOutput.addConstraint(this); this.satisfied = false; } /** * Decides if this constraint can be satisfied and records that * decision. */ UnaryConstraint.prototype.chooseMethod = function (mark) { this.satisfied = (this.myOutput.mark != mark) && Strength.stronger(this.strength, this.myOutput.walkStrength); } /** * Returns true if this constraint is satisfied in the current solution. */ UnaryConstraint.prototype.isSatisfied = function () { return this.satisfied; } UnaryConstraint.prototype.markInputs = function (mark) { // has no inputs } /** * Returns the current output variable. */ UnaryConstraint.prototype.output = function () { return this.myOutput; } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this constraint. Assume * this constraint is satisfied. */ UnaryConstraint.prototype.recalculate = function () { this.myOutput.walkStrength = this.strength; this.myOutput.stay = !this.isInput(); if (this.myOutput.stay) this.execute(); // Stay optimization } /** * Records that this constraint is unsatisfied */ UnaryConstraint.prototype.markUnsatisfied = function () { this.satisfied = false; } UnaryConstraint.prototype.inputsKnown = function () { return true; } UnaryConstraint.prototype.removeFromGraph = function () { if (this.myOutput != null) this.myOutput.removeConstraint(this); this.satisfied = false; } /* --- * * S t a y C o n s t r a i n t * --- */ /** * Variables that should, with some level of preference, stay the same. * Planners may exploit the fact that instances, if satisfied, will not * change their output during plan execution. This is called "stay * optimization". */ function StayConstraint(v, str) { StayConstraint.superConstructor.call(this, v, str); } StayConstraint.inheritsFrom(UnaryConstraint); StayConstraint.prototype.execute = function () { // Stay constraints do nothing } /* --- * * E d i t C o n s t r a i n t * --- */ /** * A unary input constraint used to mark a variable that the client * wishes to change. */ function EditConstraint(v, str) { EditConstraint.superConstructor.call(this, v, str); } EditConstraint.inheritsFrom(UnaryConstraint); /** * Edits indicate that a variable is to be changed by imperative code. */ EditConstraint.prototype.isInput = function () { return true; } EditConstraint.prototype.execute = function () { // Edit constraints do nothing } /* --- * * B i n a r y C o n s t r a i n t * --- */ var Direction = new Object(); Direction.NONE = 0; Direction.FORWARD = 1; Direction.BACKWARD = -1; /** * Abstract superclass for constraints having two possible output * variables. */ function BinaryConstraint(var1, var2, strength) { BinaryConstraint.superConstructor.call(this, strength); this.v1 = var1; this.v2 = var2; this.direction = Direction.NONE; this.addConstraint(); } BinaryConstraint.inheritsFrom(Constraint); /** * Decides if this constratint can be satisfied and which way it * should flow based on the relative strength of the variables related, * and record that decision. */ BinaryConstraint.prototype.chooseMethod = function (mark) { if (this.v1.mark == mark) { this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength)) ? Direction.FORWARD : Direction.NONE; } if (this.v2.mark == mark) { this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength)) ? Direction.BACKWARD : Direction.NONE; } if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) { this.direction = Strength.stronger(this.strength, this.v1.walkStrength) ? Direction.BACKWARD : Direction.NONE; } else { this.direction = Strength.stronger(this.strength, this.v2.walkStrength) ? Direction.FORWARD : Direction.BACKWARD } } /** * Add this constraint to the constraint graph */ BinaryConstraint.prototype.addToGraph = function () { this.v1.addConstraint(this); this.v2.addConstraint(this); this.direction = Direction.NONE; } /** * Answer true if this constraint is satisfied in the current solution. */ BinaryConstraint.prototype.isSatisfied = function () { return this.direction != Direction.NONE; } /** * Mark the input variable with the given mark. */ BinaryConstraint.prototype.markInputs = function (mark) { this.input().mark = mark; } /** * Returns the current input variable */ BinaryConstraint.prototype.input = function () { return (this.direction == Direction.FORWARD) ? this.v1 : this.v2; } /** * Returns the current output variable */ BinaryConstraint.prototype.output = function () { return (this.direction == Direction.FORWARD) ? this.v2 : this.v1; } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this * constraint. Assume this constraint is satisfied. */ BinaryConstraint.prototype.recalculate = function () { var ihn = this.input(), out = this.output(); out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); out.stay = ihn.stay; if (out.stay) this.execute(); } /** * Record the fact that this constraint is unsatisfied. */ BinaryConstraint.prototype.markUnsatisfied = function () { this.direction = Direction.NONE; } BinaryConstraint.prototype.inputsKnown = function (mark) { var i = this.input(); return i.mark == mark || i.stay || i.determinedBy == null; } BinaryConstraint.prototype.removeFromGraph = function () { if (this.v1 != null) this.v1.removeConstraint(this); if (this.v2 != null) this.v2.removeConstraint(this); this.direction = Direction.NONE; } /* --- * * S c a l e C o n s t r a i n t * --- */ /** * Relates two variables by the linear scaling relationship: "v2 = * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain * this relationship but the scale factor and offset are considered * read-only. */ function ScaleConstraint(src, scale, offset, dest, strength) { this.direction = Direction.NONE; this.scale = scale; this.offset = offset; ScaleConstraint.superConstructor.call(this, src, dest, strength); } ScaleConstraint.inheritsFrom(BinaryConstraint); /** * Adds this constraint to the constraint graph. */ ScaleConstraint.prototype.addToGraph = function () { ScaleConstraint.superConstructor.prototype.addToGraph.call(this); this.scale.addConstraint(this); this.offset.addConstraint(this); } ScaleConstraint.prototype.removeFromGraph = function () { ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this); if (this.scale != null) this.scale.removeConstraint(this); if (this.offset != null) this.offset.removeConstraint(this); } ScaleConstraint.prototype.markInputs = function (mark) { ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark); this.scale.mark = this.offset.mark = mark; } /** * Enforce this constraint. Assume that it is satisfied. */ ScaleConstraint.prototype.execute = function () { if (this.direction == Direction.FORWARD) { this.v2.value = this.v1.value * this.scale.value + this.offset.value; } else { this.v1.value = (this.v2.value - this.offset.value) / this.scale.value; } } /** * Calculate the walkabout strength, the stay flag, and, if it is * 'stay', the value for the current output of this constraint. Assume * this constraint is satisfied. */ ScaleConstraint.prototype.recalculate = function () { var ihn = this.input(), out = this.output(); out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength); out.stay = ihn.stay && this.scale.stay && this.offset.stay; if (out.stay) this.execute(); } /* --- * * E q u a l i t y C o n s t r a i n t * --- */ /** * Constrains two variables to have the same value. */ function EqualityConstraint(var1, var2, strength) { EqualityConstraint.superConstructor.call(this, var1, var2, strength); } EqualityConstraint.inheritsFrom(BinaryConstraint); /** * Enforce this constraint. Assume that it is satisfied. */ EqualityConstraint.prototype.execute = function () { this.output().value = this.input().value; } /* --- * * V a r i a b l e * --- */ /** * A constrained variable. In addition to its value, it maintain the * structure of the constraint graph, the current dataflow graph, and * various parameters of interest to the DeltaBlue incremental * constraint solver. **/ function Variable(name, initialValue) { this.value = initialValue || 0; this.constraints = new OrderedCollection(); this.determinedBy = null; this.mark = 0; this.walkStrength = Strength.WEAKEST; this.stay = true; this.name = name; } /** * Add the given constraint to the set of all constraints that refer * this variable. */ Variable.prototype.addConstraint = function (c) { this.constraints.add(c); } /** * Removes all traces of c from this variable. */ Variable.prototype.removeConstraint = function (c) { this.constraints.remove(c); if (this.determinedBy == c) this.determinedBy = null; } /* --- * * P l a n n e r * --- */ /** * The DeltaBlue planner */ function Planner() { this.currentMark = 0; } /** * Attempt to satisfy the given constraint and, if successful, * incrementally update the dataflow graph. Details: If satifying * the constraint is successful, it may override a weaker constraint * on its output. The algorithm attempts to resatisfy that * constraint using some other method. This process is repeated * until either a) it reaches a variable that was not previously * determined by any constraint or b) it reaches a constraint that * is too weak to be satisfied using any of its methods. The * variables of constraints that have been processed are marked with * a unique mark value so that we know where we've been. This allows * the algorithm to avoid getting into an infinite loop even if the * constraint graph has an inadvertent cycle. */ Planner.prototype.incrementalAdd = function (c) { var mark = this.newMark(); var overridden = c.satisfy(mark); while (overridden != null) overridden = overridden.satisfy(mark); } /** * Entry point for retracting a constraint. Remove the given * constraint and incrementally update the dataflow graph. * Details: Retracting the given constraint may allow some currently * unsatisfiable downstream constraint to be satisfied. We therefore collect * a list of unsatisfied downstream constraints and attempt to * satisfy each one in turn. This list is traversed by constraint * strength, strongest first, as a heuristic for avoiding * unnecessarily adding and then overriding weak constraints. * Assume: c is satisfied. */ Planner.prototype.incrementalRemove = function (c) { var out = c.output(); c.markUnsatisfied(); c.removeFromGraph(); var unsatisfied = this.removePropagateFrom(out); var strength = Strength.REQUIRED; do { for (var i = 0; i < unsatisfied.size(); i++) { var u = unsatisfied.at(i); if (u.strength == strength) this.incrementalAdd(u); } strength = strength.nextWeaker(); } while (strength != Strength.WEAKEST); } /** * Select a previously unused mark value. */ Planner.prototype.newMark = function () { return ++this.currentMark; } /** * Extract a plan for resatisfaction starting from the given source * constraints, usually a set of input constraints. This method * assumes that stay optimization is desired; the plan will contain * only constraints whose output variables are not stay. Constraints * that do no computation, such as stay and edit constraints, are * not included in the plan. * Details: The outputs of a constraint are marked when it is added * to the plan under construction. A constraint may be appended to * the plan when all its input variables are known. A variable is * known if either a) the variable is marked (indicating that has * been computed by a constraint appearing earlier in the plan), b) * the variable is 'stay' (i.e. it is a constant at plan execution * time), or c) the variable is not determined by any * constraint. The last provision is for past states of history * variables, which are not stay but which are also not computed by * any constraint. * Assume: sources are all satisfied. */ Planner.prototype.makePlan = function (sources) { var mark = this.newMark(); var plan = new Plan(); var todo = sources; while (todo.size() > 0) { var c = todo.removeFirst(); if (c.output().mark != mark && c.inputsKnown(mark)) { plan.addConstraint(c); c.output().mark = mark; this.addConstraintsConsumingTo(c.output(), todo); } } return plan; } /** * Extract a plan for resatisfying starting from the output of the * given constraints, usually a set of input constraints. */ Planner.prototype.extractPlanFromConstraints = function (constraints) { var sources = new OrderedCollection(); for (var i = 0; i < constraints.size(); i++) { var c = constraints.at(i); if (c.isInput() && c.isSatisfied()) // not in plan already and eligible for inclusion sources.add(c); } return this.makePlan(sources); } /** * Recompute the walkabout strengths and stay flags of all variables * downstream of the given constraint and recompute the actual * values of all variables whose stay flag is true. If a cycle is * detected, remove the given constraint and answer * false. Otherwise, answer true. * Details: Cycles are detected when a marked variable is * encountered downstream of the given constraint. The sender is * assumed to have marked the inputs of the given constraint with * the given mark. Thus, encountering a marked node downstream of * the output constraint means that there is a path from the * constraint's output to one of its inputs. */ Planner.prototype.addPropagate = function (c, mark) { var todo = new OrderedCollection(); todo.add(c); while (todo.size() > 0) { var d = todo.removeFirst(); if (d.output().mark == mark) { this.incrementalRemove(c); return false; } d.recalculate(); this.addConstraintsConsumingTo(d.output(), todo); } return true; } /** * Update the walkabout strengths and stay flags of all variables * downstream of the given constraint. Answer a collection of * unsatisfied constraints sorted in order of decreasing strength. */ Planner.prototype.removePropagateFrom = function (out) { out.determinedBy = null; out.walkStrength = Strength.WEAKEST; out.stay = true; var unsatisfied = new OrderedCollection(); var todo = new OrderedCollection(); todo.add(out); while (todo.size() > 0) { var v = todo.removeFirst(); for (var i = 0; i < v.constraints.size(); i++) { var c = v.constraints.at(i); if (!c.isSatisfied()) unsatisfied.add(c); } var determining = v.determinedBy; for (var i = 0; i < v.constraints.size(); i++) { var next = v.constraints.at(i); if (next != determining && next.isSatisfied()) { next.recalculate(); todo.add(next.output()); } } } return unsatisfied; } Planner.prototype.addConstraintsConsumingTo = function (v, coll) { var determining = v.determinedBy; var cc = v.constraints; for (var i = 0; i < cc.size(); i++) { var c = cc.at(i); if (c != determining && c.isSatisfied()) coll.add(c); } } /* --- * * P l a n * --- */ /** * A Plan is an ordered list of constraints to be executed in sequence * to resatisfy all currently satisfiable constraints in the face of * one or more changing inputs. */ function Plan() { this.v = new OrderedCollection(); } Plan.prototype.addConstraint = function (c) { this.v.add(c); } Plan.prototype.size = function () { return this.v.size(); } Plan.prototype.constraintAt = function (index) { return this.v.at(index); } Plan.prototype.execute = function () { for (var i = 0; i < this.size(); i++) { var c = this.constraintAt(i); c.execute(); } } /* --- * * M a i n * --- */ /** * This is the standard DeltaBlue benchmark. A long chain of equality * constraints is constructed with a stay constraint on one end. An * edit constraint is then added to the opposite end and the time is * measured for adding and removing this constraint, and extracting * and executing a constraint satisfaction plan. There are two cases. * In case 1, the added constraint is stronger than the stay * constraint and values must propagate down the entire length of the * chain. In case 2, the added constraint is weaker than the stay * constraint so it cannot be accomodated. The cost in this case is, * of course, very low. Typical situations lie somewhere between these * two extremes. */ function chainTest(n) { planner = new Planner(); var prev = null, first = null, last = null; // Build chain of n equality constraints for (var i = 0; i <= n; i++) { var name = "v" + i; var v = new Variable(name); if (prev != null) new EqualityConstraint(prev, v, Strength.REQUIRED); if (i == 0) first = v; if (i == n) last = v; prev = v; } new StayConstraint(last, Strength.STRONG_DEFAULT); var edit = new EditConstraint(first, Strength.PREFERRED); var edits = new OrderedCollection(); edits.add(edit); var plan = planner.extractPlanFromConstraints(edits); for (var i = 0; i < 100; i++) { first.value = i; plan.execute(); if (last.value != i) alert("Chain test failed."); } } /** * This test constructs a two sets of variables related to each * other by a simple linear transformation (scale and offset). The * time is measured to change a variable on either side of the * mapping and to change the scale and offset factors. */ function projectionTest(n) { planner = new Planner(); var scale = new Variable("scale", 10); var offset = new Variable("offset", 1000); var src = null, dst = null; var dests = new OrderedCollection(); for (var i = 0; i < n; i++) { src = new Variable("src" + i, i); dst = new Variable("dst" + i, i); dests.add(dst); new StayConstraint(src, Strength.NORMAL); new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED); } change(src, 17); if (dst.value != 1170) alert("Projection 1 failed"); change(dst, 1050); if (src.value != 5) alert("Projection 2 failed"); change(scale, 5); for (var i = 0; i < n - 1; i++) { if (dests.at(i).value != i * 5 + 1000) alert("Projection 3 failed"); } change(offset, 2000); for (var i = 0; i < n - 1; i++) { if (dests.at(i).value != i * 5 + 2000) alert("Projection 4 failed"); } } function change(v, newValue) { var edit = new EditConstraint(v, Strength.PREFERRED); var edits = new OrderedCollection(); edits.add(edit); var plan = planner.extractPlanFromConstraints(edits); for (var i = 0; i < 10; i++) { v.value = newValue; plan.execute(); } edit.destroyConstraint(); } // Global variable holding the current planner. var planner = null; function deltaBlue() { chainTest(100); projectionTest(100); } closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/0000755000175000017500000000000014433667662021037 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/doctests/regexp.class-overflow.doctest0000755000175000017500000000035714433667662026675 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> /[\u0000-\uFFFF]/i.test(0) true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/whitespace.doctest0000644000175000017500000000107614433667662024566 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js') js> var bom = String.fromCharCode(0xFEFF); js> eval(bom); js> var _ = eval("(function(){return"+bom+"1;})"); js> expectError(function() { > eval("i"+bom+"f (false);"); > }, SyntaxError); js> parseInt(bom+"1") 1 js> parseFloat(bom+"1.5"); 1.5 js> (bom+"abc"+bom).trim().length 3 js> /\s/.test(bom) true js> (bom+"1"+bom) * 1 1 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/429121.doctest0000644000175000017500000000037114433667662023171 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> encodeURIComponent('$ a ;') %24%20a%20%3B closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/368019.doctest0000644000175000017500000000064414433667662023204 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Regression test for bug https://bugzilla.mozilla.org/show_bug.cgi?id=368019 // - regular expression (regex) parses /[/]/ incorrectly js> var re; js> re = /[^/]*/; /[^/]*/ js> re = /[/]/; /[/]/ js> re = /\[/; /\[/ closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/parseint.doctest0000644000175000017500000000073014433667662024253 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> parseInt function parseInt() { [native code for parseInt, arity=2] } js> parseInt("0"); 0 js> parseInt("1") 1 js> parseInt("-1") -1 js> parseInt(" 2") 2 js> parseInt("3then some other chars") 3 js> parseInt('0xF') 15 js> parseInt('0xf') 15 js> parseInt('010') 8 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/array.dense.doctest0000644000175000017500000000161014433667662024637 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // test various array methods with dense arrays js> var x = [] js> x[10] = 1 1 js> x.length 11 js> 0 in x false js> x.shift() js> x.length 10 js> 0 in x false js> x ,,,,,,,,,1 js> x.reverse() 1,,,,,,,,, js> x.length 10 js> 9 in x false js> x.reverse() ,,,,,,,,,1 js> x.length 10 js> 5 in x false js> x.unshift(2) 11 js> x 2,,,,,,,,,,1 js> 9 in x false js> var r = x.splice(3, 4, 3, 4, 5) js> r ,,, js> r.length 4 js> 2 in r false js> x.length 10 js> x 2,,,3,4,5,,,,1 js> x[5] 5 js> 8 in x false js> var s = x.slice(6, 8); js> s , js> s.length 2 js> 1 in s false js> var y = [] js> y[9] = 1 1 js> var z = y.concat(x) js> z ,,,,,,,,,1,2,,,3,4,5,,,,1 js> z.length 20 js> 2 in z false js> 12 in z false closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/util.js0000644000175000017500000000070214433667662022351 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ function expectTypeError(code) { expectError(code, TypeError); } function expectError(code, error) { try { code(); throw (code.toSource() + ' should have thrown a '+error); } catch (e if e instanceof error) { // all good } } closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/700651.doctest0000644000175000017500000000044114433667662023167 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> escape(/[\u0000]/.exec("\u0000")) %00 js> escape(/[^\u0000]/.exec("\u0000")) null closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/date.tojson.doctest0000644000175000017500000000173514433667662024664 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Date.prototype.toJSON; function toJSON() { [native code for Date.toJSON, arity=1] } js> Date.prototype.toJSON.call({ > valueOf: function() { return Infinity; } > }, '') null js> expectError(function() { > Date.prototype.toJSON.call({}, '') > }, TypeError) js> expectError(function() { > Date.prototype.toJSON.call(5, '') > }, TypeError) js> expectError(function() { > Date.prototype.toJSON.call({toISOString:5}, '') > }, TypeError) js> expectError(function() { > Date.prototype.toJSON.call({toISOString:function(){ return [] }}, '') > }, TypeError) js> Date.prototype.toJSON.call({toISOString: function() { return 'w00t' }}, '') w00t js> var now = new Date() js> now.toJSON('') === now.toISOString() true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/arrays.doctest0000644000175000017500000000204214433667662023725 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> [1,2,undefined,4].join(';') 1;2;;4 js> [1,2,null,4].join(';') 1;2;;4 js> var arr = []; arr[0] = 1; arr[1] = 2; arr[3] = 4; arr.join(';') 1;2;;4 js> var arr = [1,2,3,4]; delete arr[2]; arr.join(';') 1;2;;4 js> var arr = ["a","b","c","d"]; js> var _ = arr.length = 2; js> arr[2] === undefined true js> arr[1] === "b" true js> [1,2,3].reverse().toSource() [3, 2, 1] js> [2,1,3].sort().toSource() [1, 2, 3] js> var arr = [1,2,3]; arr.push(4); arr.toSource() [1, 2, 3, 4] js> var arr = [1,2,3]; arr.pop(); 3 js> arr.toSource() [1, 2] js> var arr = [1,2,3]; arr.shift(); 1 js> arr.toSource() [2, 3] js> var arr = [2,3]; arr.unshift(1); arr.toSource() [1, 2, 3] js> var arr = [1,2,3]; arr.splice(1, 1, "a", "b").toSource() [2] js> arr.toSource() [1, "a", "b", 3] js> [1,2,3].concat([4,5,6]).toSource() [1, 2, 3, 4, 5, 6] js> [1,2,3].indexOf(2) 1 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.seal.doctest0000644000175000017500000000142414433667662024620 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.seal; function seal() { [native code for Object.seal, arity=1] } js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.seal(value) }) > }) js> expectTypeError(function() { Object.seal() }) js> var x = {} js> var y = Object.seal(x) js> x === y true js> var obj = Object.defineProperty({}, 'a', {configurable:true}) js> var _ = Object.seal(obj) js> Object.getOwnPropertyDescriptor(obj, 'a').configurable false js> Object.isExtensible(obj) false js> Object.isSealed(obj) true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/757410.doctest0000755000175000017500000000071414433667662023202 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // https://bugzilla.mozilla.org/show_bug.cgi?id=757410 js> version(180) 0 js> function gen() { > [yield 1]; > ({x: yield 2}); > } js> var g = gen() js> g.next() == 1 && g.next() == 2 true js> try {g.next()} catch(e) {e instanceof StopIteration} true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/serialize.doctest0000644000175000017500000000075214433667662024421 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var obj = { a:3, b:"hi", 9:"nine", 12:1200 }; js> serialize(obj, "foo.bin"); js> obj2 = deserialize("foo.bin"); [object Object] js> uneval(obj) ({a:3, b:"hi", 9:"nine", 12:1200}) js> uneval(obj2) ({a:3, b:"hi", 9:"nine", 12:1200}) js> (new java.io.File("foo.bin"))["delete"](); true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.create.doctest0000644000175000017500000000223714433667662025142 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.create; function create() { [native code for Object.create, arity=2] } js> expectTypeError(function() { Object.create() }); js> [undefined, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.create(value) }) > }) js> expectTypeError(function() { Object.create({}, null) }) js> var obj = Object.create({}); js> var obj = Object.create({}, {}); js> var obj = Object.create({}, undefined); js> var orig = {} js> var next = Object.create(orig); js> Object.getPrototypeOf(next) === orig; true js> var obj = Object.create({}, {a: {value:1}, b: {value:2}}); js> [obj.a, obj.b].toSource(); [1, 2] js> var orig = {a:1}; js> var obj = Object.create(orig, {a:{value:2}, b:{value:3}}); js> [obj.a, obj.b].toSource() [2, 3] js> expectTypeError(function() { Object.create({}, {b: {value:1}, c:1}) }); js> var obj = Object.create(null, {a: {value:1}}) js> Object.getPrototypeOf(obj) === null true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/480758.doctest0000644000175000017500000000112614433667662023205 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> function primBeget(proto) { > if (proto === null) { fail("Cannot beget from null."); } > if (proto === (void 0)) { fail("Cannot beget from undefined."); } > function F() {} > F.prototype = proto; > var result = new F(); > return result; > } js> var x = [1].concat(primBeget(Array.prototype)); js> x 1, js> x[1] js> typeof x[1] object js> x[1] instanceof Array trueclosure-compiler-20130227+dfsg1/rhino/testsrc/doctests/regexp.literals.doctest0000644000175000017500000000053314433667662025537 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> for (var i = 0; i < 4; i++) print(/a/g.exec("a")); a a a a js> var r = /a/g; for (var i = 0; i < 4; i++) print(r.exec("a")); a null a null closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/xmlOptions.doctest0000644000175000017500000000102114433667662024574 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var x = 1; js> x 1 js> var xmlLib = org.mozilla.javascript.xml.XMLLib.extractFromScope(this); js> xmlLib.isPrettyPrinting(); true js> xmlLib.setPrettyPrinting(false); js> xmlLib.isPrettyPrinting(); false js> x 1closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/iterable.doctest0000644000175000017500000000104514433667662024215 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Test of Creating a JavaScript Iterator from a Java Iterable or Iterator // See http://developer.mozilla.org/en/docs/New_in_Rhino_1.7R1 js> m = new java.util.LinkedHashMap() {} js> m.put("a",1); m.put("b",2); m {a=1.0, b=2.0} js> for (i in Iterator(m.values())) print(i) 1.0 2.0 js> for (i in Iterator(m.values().iterator())) print(i) 1.0 2.0 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/773573.doctest0000755000175000017500000000054214433667662023211 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> version(180) 0 js> try { > (function({a}) { return a }).foo() > } catch (e) { > e.message.indexOf("function ({a}) {...}") != -1; > } true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/413838.doctest0000644000175000017500000000145714433667662023207 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var map = new java.util.HashMap(); js> map.put("a","hi"); null js> map.put("b","hi"); null js> map.get("a") == map.get("b") true js> map.put("c",1) null js> map.put("c",1) 1.0 js> map.put("d",1) null js> map.get("c") == map.get("d") true js> map.get("a") == map.get("d") false js> map.put("e","1") null js> map.get("d") == map.get("e") true js> map.put("f", true) null js> map.put("g", true) null js> map.get("f") == map.get("g") true js> var obj = {} js> map.put("h", obj) null js> map.put("i", obj) null js> map.get("h") == map.get("i") true js> map.put("j", {}) null js> map.get("i") == map.get("j") falseclosure-compiler-20130227+dfsg1/rhino/testsrc/doctests/442922.doctest0000644000175000017500000000061214433667662023173 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var x = ; js> serialize(x, "x.ser"); js> deserialize("x.ser"); js> (new java.io.File("x.ser"))["delete"](); trueclosure-compiler-20130227+dfsg1/rhino/testsrc/doctests/473761.doctest0000644000175000017500000000065014433667662023202 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var xml = ; js> var order = xml.customer.order; js> order.orderid = "1"; 1 js> order.test = "expected_string"; expected_string js> xml.customer.order.test; expected_string closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/784358.doctest0000755000175000017500000000524014433667662023214 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // https://bugzilla.mozilla.org/show_bug.cgi?id=784358 js> var v1=1 js> v1 1 js> eval("var v2=1") js> v2 1 js> const c1=1 js> c1 1 js> eval("const c2=1") js> c2 1 js> try {eval("const v1=2")}catch(e){e instanceof TypeError} true js> try {eval("const v1=2")}catch(e){e instanceof TypeError} true js> try{eval("const c1=1")}catch(e){e instanceof TypeError} true js> try{eval("const c2=1")}catch(e){e instanceof TypeError} true js> try{eval("var c1=1")}catch(e){e instanceof TypeError} true js> try{eval("var c2=1")}catch(e){e instanceof TypeError} true js> try {(1,eval)("const v1=2")}catch(e){e instanceof TypeError} true js> try {(1,eval)("const v1=2")}catch(e){e instanceof TypeError} true js> try{(1,eval)("const c1=1")}catch(e){e instanceof TypeError} true js> try{(1,eval)("const c2=1")}catch(e){e instanceof TypeError} true js> try{(1,eval)("var c1=1")}catch(e){e instanceof TypeError} true js> try{(1,eval)("var c2=1")}catch(e){e instanceof TypeError} true js> (function(){eval("const v1=2"); return v1})() 2 js> (function(){eval("const v2=2"); return v2})() 2 js> (function(){eval("const c1=2"); return c1})() 2 js> (function(){eval("const c2=2"); return c2})() 2 js> (function(){eval("var c1=2"); return c1})() 2 js> (function(){eval("var c2=2"); return c2})() 2 js> try{ (function(){(1,eval)("const v1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){(1,eval)("const v2=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){(1,eval)("const c1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){(1,eval)("const c2=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){(1,eval)("var c1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){(1,eval)("var c2=1")})() }catch(e){e instanceof TypeError} true js> (v1 = 3, v1) 3 js> (v2 = 3, v2) 3 js> (c1 = 3, c1) 1 js> (c2 = 3, c2) 1 js> delete v1 false js> delete v2 true js> delete c1 false js> delete c2 false js> try{ (function(){const c1=1; eval("const c1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){eval("const c1=1"); const c1=1})() }catch(e){e instanceof TypeError} true js> try{ (function(){eval("const c1=1"); eval("const c1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){const c1=1; eval("var c1=1")})() }catch(e){e instanceof TypeError} true js> try{ (function(){eval("var c1=1"); const c1=1})() }catch(e){e instanceof TypeError} true js> try{ (function(){var c1=1; eval("const c1=1")})() }catch(e){e instanceof TypeError} true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/string.trim.doctest0000644000175000017500000000200414433667662024702 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> String.prototype.trim; function trim() { [native code for String.trim, arity=0] } js> String.prototype.trim.call({toString: function() { return "a" }}); a js> " hello ".trim() === "hello"; true js> var chr = String.fromCharCode; js> var str = "" + > // ecma whitespace > chr(0x0009) + chr(0x000B) + chr(0x000C) + chr(0x0020) + chr(0x00A0) + chr(0xFEFF) + > // unicode whitespace > chr(0x1680) + chr(0x180E) + > chr(0x2000) + chr(0x2001) + chr(0x2002) + chr(0x2003) + chr(0x2004) + chr(0x2005) + chr(0x2006) + chr(0x2007) + chr(0x2008) + chr(0x2009) + chr(0x200A) + > chr(0x202F) + chr(0x205F) + chr(0x3000) + > // ecma line terminators > chr(0x000A) + chr(0x000D) + chr(0x2028) + chr(0x2029) + > > "abc"; js> str.trim() === "abc"; true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.defineproperty.doctest0000644000175000017500000002002214433667662026726 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.defineProperty function defineProperty() { [native code for Object.defineProperty, arity=3] } js> expectTypeError(function() { Object.defineProperty() }); js> expectTypeError(function() { Object.defineProperty({}) }); js> expectTypeError(function() { Object.defineProperty({}, 'p') }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.defineProperty(value, 'p', {}) }) > }) js> Object.defineProperty({}, 'p', {value:1}).p 1 js> var obj = {} js> Object.defineProperty(obj, 'a', { > value: 1, > enumerable: false, > writable: false, > configurable: false > }) [object Object] js> for (var p in obj) print(p); // check it has no enumerable properties js> obj.a = 2; obj.a; // check that the property is not writable 1 js> delete obj.a; obj.a // check that the property is not deletable 1 js> var define = Object.defineProperty; js> var describe = Object.getOwnPropertyDescriptor; js> // when define new property with empty descriptor then default values are used for the descriptor js> var obj = define({}, 'a', {}); js> describe(obj, 'a').toSource() ({value:undefined, writable:false, enumerable:false, configurable:false}) js> // when define new property with data descriptor then those values are used for the descriptor js> var obj = define({}, 'a', { value: 2, writable: true }); js> var {value:v, writable:w} = describe(obj, 'a'); [v, w].toSource(); [2, true] js> obj.a 2 js> // when define new property with accessor descriptor then those values are used for the descriptor js> var obj = define({}, 'a', { get: function() { return 3; }, set: function(value) {} }); js> var {get:g, set:s} = describe(obj, 'a'); [g, s].toSource(); [(function () {return 3;}), (function (value) {})] js> obj.a 3 js> // when define existing property with empty descriptor then descriptor is left unchanged js> var descriptor = {value:1, writable:true, enumerable:true, configurable:true}; js> var obj = define({}, 'a', descriptor); js> var obj = define(obj, 'a', {}); js> describe(obj, 'a').toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> // when define existing property with same descriptor then descriptor is left unchanged js> var descriptor = {value:1, writable:true, enumerable:true, configurable:true}; js> var obj = define({}, 'a', descriptor); js> var obj = define(obj, 'a', descriptor); js> describe(obj, 'a').toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> // may not change configurable from false to true js> expectTypeError(function() { > var obj = define({}, 'a', {configurable : false}); > define(obj, 'a', {configurable : true}); > }); js> // may not change enumerable when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {enumerable : true, configurable:false}); > define(obj, 'a', {enumerable : false}); > }); js> // may not change writable from false to true when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {writable : false, configurable: false}); > define(obj, 'a', {writable : true}); > }); js> // may not change value when writable is false js> expectTypeError(function() { > var obj = define({}, 'a', {value : 1, writable:false}); > define(obj, 'a', {value : 2}); > }); js> // may not change getter when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {get: function() { return 1 }, configurable:false}); > define(obj, 'a', {get: function() { return 1 }}); > }); js> // may not change setter when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {set: function(val) {}, configurable:false}); > define(obj, 'a', {set: function(val) {}}); > }); js> // may not change from data property to accessor property when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {value : 1, configurable:false}); > define(obj, 'a', {get : function() { return 1 }}); > }); js> // may not change from accessor property to data property when configurable is false js> expectTypeError(function() { > var obj = define({}, 'a', {get : function() { return 1 }, configurable:false}); > define(obj, 'a', {value : 1}); > }); js> // can change writable from true to false when configurable is false js> var obj = define({}, 'a', {writable:true, configurable:false}); js> var obj = define(obj, 'a', {writable:false}); js> // can set enumerable to the same value when configurable is false js> var obj = define({}, 'a', {enumerable:true, configurable:false}); js> var obj = define(obj, 'a', {enumerable:true}); js> // can change from data property to accessor property when configurable is true js> var obj = define({}, 'a', {value : 1, configurable: true}); js> var obj = define(obj, 'a', {get : function() { return 4 }}); js> obj.a 4 js> describe(obj, 'a').toSource() ({enumerable:false, configurable:true, get:(function () {return 4;})}) js> // can change from accessor property to data property when configurable is true js> var obj = define({}, 'a', {get : function() { return 2 }, configurable:true}); js> var obj = define(obj, 'a', {value : 5}); js> obj.a 5 js> describe(obj, 'a').toSource() ({value:5, writable:false, enumerable:false, configurable:true}) js> // can change enumerable and writable to true when configurable is true js> var obj = define({}, 'a', {writable : false, enumerable : false, configurable:true}); js> var obj = define(obj, 'a', {writable : true, enumerable : true, configurable:true}); js> // can change the value if writable is true js> var obj = define({}, 'a', {value:6, writable:true}) js> obj.a 6 js> var obj = define(obj, 'a', {value:7}) js> obj.a 7 js> // defining a new property should fail loudly when object is not extensible js> var obj = Object.preventExtensions({}); js> expectTypeError(function() { define(obj, 'a', {value:1}) }) js> // defining new property should succeed when object is extensible js> var obj = {} js> Object.isExtensible(obj); true js> obj.a = 8; obj.a 8 js> // changing existing property should succeed when object is not extensible js> var obj = define({}, 'a', {value:1, writable:true}); js> var obj = Object.preventExtensions(obj); js> obj.a = 9; obj.a 9 js> // defined getters and setters must be functions js> expectTypeError(function() { define({}, 'a', {get:1}); }) js> expectTypeError(function() { define({}, 'a', {set:1}); }) js> // make sure defineProperty works properly with numbers as ids js> Object.defineProperty({}, 0, {value:1, enumerable:true})['0'] 1 js> // make sure defineProperty works properly with arrays js> Object.defineProperty([], 0, {value:1, enumerable:true})[0] 1 js> // make sure defineProperty works properly with arrays js> Object.defineProperty([], 'a', {value:1, enumerable:true})['a'] 1 js> // make sure defineProperty updates length properly for arrays js> Object.defineProperty([], 0, {value:1}).length 1 js> // make sure that getters and setters are actually used to get and set property js> Object.defineProperty({}, 'a', {get:function() { return "get called"; }}).a get called js> Object.defineProperty({}, 'a', {set:function(val) { print("set called with "+val); }}).a = 1; undefined; set called with 1 js> // make sure defineProperty works for builtin properties js> Object.defineProperty(JSON, 'stringify', {value:1}).stringify 1 js> Object.defineProperty(JSON, 'parse', {get:function() {print('do get'); return undefined}}).parse do get js> // an accessor property without a setter behaves as if the setter were undefined js> // and thus the setter can be set to undefined even when configurable is false js> var obj = Object.defineProperty({}, 'a', {get:function(){return 1}}) js> var _ = Object.defineProperty(obj, 'a', {set:undefined}) js> closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/regexp.source.doctest0000644000175000017500000000263514433667662025225 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> x = RegExp('') /(?:)/ js> x.source js> RegExp('a').source a js> RegExp('\\\\').source \\ js> RegExp('\\\\\\\\').source \\\\ js> x = RegExp('/') /\// js> x.source \/ js> x.test('/') true js> x = RegExp('//') /\/\// js> x.source \/\/ js> x.test('//') true js> x = RegExp('\/') /\// js> x.source \/ js> x.test('/') true js> x = RegExp('\\/') /\// js> x.source \/ js> x.test('/') true js> x = RegExp('\\\/') /\// js> x.source \/ js> x.test('/') true js> x = RegExp('\\\\/') /\\// js> x.source \\/ js> x.test('/') false js> x.test('\\/') true js> x = RegExp('/abc\/foo\\/bar\\\/xyz/') /\/abc\/foo\/bar\/xyz\// js> x.source \/abc\/foo\/bar\/xyz\/ js> x.test('/abc/foo/bar/xyz/') true js> RegExp('[^/]*') /[^\/]*/ js> RegExp('[^\/]*') /[^\/]*/ js> RegExp('[^\\/]*') /[^\/]*/ js> /./.compile('') /(?:)/ js> /./.compile('a') /a/ js> /./.compile('\\\\') /\\/ js> /./.compile('\\\\\\\\') /\\\\/ js> /./.compile('/') /\// js> /./.compile('//') /\/\// js> /./.compile('\/') /\// js> /./.compile('\\/') /\// js> /./.compile('\\\/') /\// js> /./.compile('\\\\/') /\\// js> /./.compile('/abc\/foo\\/bar\\\/xyz/') /\/abc\/foo\/bar\/xyz\// js> /./.compile('[^/]*') /[^\/]*/ js> /./.compile('[^\/]*') /[^\/]*/ js> /./.compile('[^\\/]*') /[^\/]*/ closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/array.length.doctest0000644000175000017500000000057614433667662025034 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> function Array() { throw "don't call this" } js> [].length 0 js> [1].length 1 js> [,1,2].length 3 js> [1,,2].length 3 js> [1,2,].length 2 js> [,].length 1 js> [1,,].length 2 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/441417.doctest0000644000175000017500000000146414433667662023177 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var friendInfo = xmlns="http://ode.apache.org/simpel/1.0/definition/XmlData"> xmlns=""/>; js> var msgIn = xmlns:xd="http://ode.apache.org/simpel/1.0/definition/XmlData"> > > John > Doe > (999)999-9999 > > ; js> friendInfo.name = msgIn.person.firstName.text() + " " + > msgIn.person.lastName.text(); John Doe closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/433878.doctest0000644000175000017500000000106114433667662023204 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> version(170) 0 js> function f(a,b,c) { > let sum = a + b + c; > return sum / 3; > } js> f.toString() function f(a, b, c) { let sum = a + b + c; return sum / 3; } js> try { > eval("function f() { for (;;) let a=3; return 3; }"); > } catch (e) { > e; > } SyntaxError: SyntaxError: let declaration not directly within block closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/date.toisostring.doctest0000644000175000017500000000174414433667662025734 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Date.prototype.toISOString; function toISOString() { [native code for Date.toISOString, arity=0] } js> expectError(function() { > new Date(Infinity).toISOString() > }, RangeError); js> new Date(0).toISOString() 1970-01-01T00:00:00.000Z js> var isoFormat = /(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d).(\d\d\d)Z/; js> var now = new Date(); js> var matches = isoFormat.exec(now.toISOString()); js> matches[0] === now.toISOString() true js> matches[1] == now.getUTCFullYear() true js> matches[2] == now.getUTCMonth()+1 true js> matches[3] == now.getUTCDate() true js> matches[4] == now.getUTCHours() true js> matches[5] == now.getUTCMinutes() true js> matches[6] == now.getUTCSeconds() true js> matches[7] == now.getUTCMilliseconds() true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/number.tostring.doctest0000644000175000017500000000135314433667662025570 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> 10000000000000000000000 1e+22 js> 100000000000000000000001 1.0000000000000001e+23 js> 1000000000000000000000001 1e+24 js> -10000000000000000000000 -1e+22 js> -100000000000000000000001 -1.0000000000000001e+23 js> -1000000000000000000000001 -1e+24 js> 0.1 0.1 js> -0.1 -0.1 js> 0.00001 0.00001 js> -0.00001 -0.00001 js> 0.000001 0.000001 js> -0.000001 -0.000001 js> 0.0000001 1e-7 js> -0.0000001 -1e-7 js> -0.0000000000000001 -1e-16 js> 0.0000000000000001 1e-16 js> 0.00000000000000000000000001 1e-26 js> -0.00000000000000000000000001 -1e-26 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.freeze.doctest0000644000175000017500000000177314433667662025163 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.freeze; function freeze() { [native code for Object.freeze, arity=1] } js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.freeze(value) }) > }) js> expectTypeError(function() { Object.freeze() }) js> var x = {} js> var y = Object.freeze(x) js> x === y true js> var obj = Object.defineProperty({}, 'a', {configurable:true, writable:true}) js> var _ = Object.freeze(obj) js> var a = Object.getOwnPropertyDescriptor(obj, 'a'); js> a.configurable false js> a.writable false js> Object.isExtensible(obj) false js> Object.isFrozen(obj) true js> var _ = Object.freeze([]) js> var _ = Object.freeze({}) js> var _ = Object.freeze(function(){}) js> var _ = Object.freeze(/a/) js> var _ = Object.freeze(RegExp) closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/tail-call-in-try.doctest0000644000175000017500000000104114433667662025504 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Fix bug reported in newsgroup: // Tail call optimization was interfering with catching exceptions. // The following program should print 7, but was resulting in an uncaught // exception. js> function g() { > throw 3; > } js> function f() { > try { > return g(); > } catch (e) { > return 7; > } > } js> f() 7closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/467396.doctest0000644000175000017500000000075514433667662023217 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var a = java.lang.reflect.Array.newInstance(java.lang.Integer, [17,4]); js> a [[Ljava.lang.Integer;@1543c88 js> a.length 17 js> a[0].length 4 js> var a = java.lang.reflect.Array.newInstance(java.lang.Integer, 17, 4); js> a [[Ljava.lang.Integer;@f11404 js> a.length 17 js> a[0].length 4 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/393794.doctest0000644000175000017500000000042314433667662023207 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var x = { foo: "bar" } js> var y = { __proto__: x }; js> y.foo; bar closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/524931.doctest0000644000175000017500000000155614433667662023204 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var x = bar; js> x.foo[0] = "baz"; baz js> x.foo[1] = "barf"; barf js> x baz barf js> var xmlTester=; js> xmlTester['data']['test'][0] = "test0"; test0 js> xmlTester['data']['test'][1] = "test1"; test1 js> xmlTester test0 test1 js> var xmlTester=; js> xmlTester['data']['test'][0] = subtest1; subtest1 js> xmlTester['data']['test'][1] = "test1"; test1 js> xmlTester subtest1 test1 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.getownpropertynames.doctest0000644000175000017500000000334014433667662030027 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.getOwnPropertyNames; function getOwnPropertyNames() { [native code for Object.getOwnPropertyNames, arity=1] } js> expectTypeError(function() { Object.getOwnPropertyNames() }) js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.getOwnPropertyNames(value) }) > }) js> Object.getOwnPropertyNames({}).toSource(); [] js> Object.getOwnPropertyNames({a:2}).toSource(); ["a"] js> Object.getOwnPropertyNames({a:1, b:2}).toSource(); ["a", "b"] js> Object.getOwnPropertyNames({'a.b':1, 'c d':2}).toSource(); ["a.b", "c d"] js> Object.getOwnPropertyNames([]).toSource(); ["length"] js> Object.getOwnPropertyNames(['a', 'b', 'c']).toSource(); ["0", "1", "2", "length"] js> function UserDefined() { this.a = 1; this.b = 2 }; js> var obj = new UserDefined() js> Object.getOwnPropertyNames(obj).toSource() ["a", "b"] js> UserDefined.prototype.c = 3; 3 js> Object.getOwnPropertyNames(obj).toSource() ["a", "b"] js> // test properties of result are enumerable js> for (var p in Object.getOwnPropertyNames({a:2, b:3})) print(p) 0 1 js> // test that properties of result are writable js> var k = Object.getOwnPropertyNames({a:2, b:3}); js> k[1] = 'c'; k.toSource(); ["a", "c"] js> // test that properties of result are configurable js> var k = Object.getOwnPropertyNames({a:2, b:3}) js> delete k[1]; true js> k a, js> // TODO test that the attributes of the properties can be changed js> var k = Object.getOwnPropertyNames({a:2, 5:6}) js> typeof k[1] string closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/error.tostring.doctest0000644000175000017500000000201014433667662025420 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js') js> var str = Error.prototype.toString js> str.call(new TypeError("msg")) TypeError: msg js> str.call(new TypeError()) // message is initialised to '' TypeError js> str.call(new Error("msg")) Error: msg js> str.call(new Error()) // message is initialised to '' Error js> str.call({name:"my error", message:"my message"}) my error: my message js> str.call({}) Error js> str.call({name:"no message defined"}) no message defined js> str.call({name:"message is undefined", message:undefined}) message is undefined js> str.call({name:"null message", message:null}) null message: null js> str.call({message:"no name defined"}) Error: no name defined js> str.call({name:undefined, message:"name is undefined"}) Error: name is undefined js> str.call({name:null, message:"null name"}) null: null name closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/array.isarray.doctest0000644000175000017500000000123114433667662025212 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> Array.isArray; function isArray() { [native code for Array.isArray, arity=1] } js> Array.isArray() false js> Array.isArray(undefined); false js> Array.isArray(null); false js> Array.isArray(true); false js> Array.isArray(1); false js> Array.isArray('hello'); false js> Array.isArray({}); false js> Array.isArray(function(){}) false js> (function() { print(Array.isArray(arguments)) })() false js> Array.isArray([]) true js> Array.isArray(new Array()) true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.getownpropertydescriptor.doctest0000644000175000017500000000452614433667662031111 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.getOwnPropertyDescriptor; function getOwnPropertyDescriptor() { [native code for Object.getOwnPropertyDescriptor, arity=2] } js> expectTypeError(function() { Object.getOwnPropertyDescriptor() }) js> var desc = Object.getOwnPropertyDescriptor({undefined:3}); js> desc.value 3 js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.getOwnPropertyDescriptor(value, 'p') }) > }) js> Object.getOwnPropertyDescriptor({}, 'p') === undefined; true js> var desc = Object.getOwnPropertyDescriptor({p:1}, 'p'); js> desc.value 1 js> desc.writable true js> desc.enumerable true js> desc.configurable true js> var desc = Object.getOwnPropertyDescriptor({ get p() {}, set p() {} }, 'p'); js> desc.value === undefined; true js> desc.writable js> desc.get.toSource() (function () {}) js> desc.set.toSource() (function () {}) js> desc.enumerable true js> desc.configurable true js> desc.__proto__ === Object.prototype true js> desc.__parent__; [object global] js> var func = function(){} js> func.a = 1; Object.getOwnPropertyDescriptor(func, 'a').toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> Object.getOwnPropertyDescriptor({undefined: 1}, undefined).toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> Object.getOwnPropertyDescriptor({0:1}, 0).toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> Object.getOwnPropertyDescriptor([1], 0).toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> Object.getOwnPropertyDescriptor([1], '0').toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> Object.getOwnPropertyDescriptor([1], 1) === undefined true js> Object.getOwnPropertyDescriptor([1], -1) === undefined true js> var arr = []; js> arr.a = 1; 1 js> Object.getOwnPropertyDescriptor(arr, 'a').toSource() ({value:1, writable:true, enumerable:true, configurable:true}) js> var arr = Object.defineProperty([], 'a', {value:1, writable:false,}) js> var desc = Object.getOwnPropertyDescriptor(arr, 'a'); ([desc.value, desc.writable]).toSource() [1, false] closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/expressionclosure.doctest0000644000175000017500000000130414433667662026220 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> version(180) 0 js> x = function(x) x; function (x) x js> x.toSource() (function (x) x) js> x(123) === 123 true js> x = function([a, b]) a + b; function ([a, b]) a + b js> x([1, 2]) 3 js> x.toSource() (function ([a, b]) a + b) js> function outer() { > var k = function(a) a + 1; > return function(b) k(b) * 2; > } js> outer function outer() { var k = function (a) a + 1; return function (b) k(b) * 2; } js> outer() function (b) k(b) * 2 js> outer()(4) 10 js> outer()(5) 12 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/json.doctest0000644000175000017500000000047314433667662023403 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> ({"1": true})["1"] true js> JSON.parse("{\"1\": true}")["1"] true js> JSON.parse("{\"a\": true}")["a"] true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.keys.doctest0000644000175000017500000000272314433667662024652 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.keys; function keys() { [native code for Object.keys, arity=1] } js> expectTypeError(function() { Object.keys() }) js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.keys(value) }) > }) js> Object.keys({}).toSource(); [] js> Object.keys({a:2}).toSource(); ["a"] js> Object.keys({a:1, b:2}).toSource(); ["a", "b"] js> Object.keys({'a.b':1, 'c d':2}).toSource(); ["a.b", "c d"] js> Object.keys([]).toSource(); [] js> Object.keys(['a', 'b', 'c']).toSource(); ["0", "1", "2"] js> function UserDefined() { this.a = 1; this.b = 2 }; js> var obj = new UserDefined() js> Object.keys(obj).toSource() ["a", "b"] js> UserDefined.prototype.c = 3; 3 js> Object.keys(obj).toSource() ["a", "b"] js> // test properties of result are enumerable js> for (var p in Object.keys({a:2, b:3})) print(p) 0 1 js> // test that properties of result are writable js> var k = Object.keys({a:2, b:3}); js> k[1] = 'c'; k.toSource(); ["a", "c"] js> // test that properties of result are configurable js> var k = Object.keys({a:2, b:3}) js> delete k[1]; true js> k a, js> // TODO test that the attributes of the properties can be changed js> var k = Object.keys({ a:1, 3:2 }); js> typeof k[1]; string closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/controlchars.doctest0000644000175000017500000000214714433667662025133 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var controlChars = [ > 0x200C, 0x200D, 0x200E, 0x200F, > 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, > 0x00AD, > 0x2061, 0x2062, 0x2063, 0x2064, > 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F > ]; js> function validInRegExpLiteral(num) { > eval('/.'+String.fromCharCode(num)+'./'); > } js> function validInStringLiteral(num) { > eval('"'+String.fromCharCode(num)+'"'); > } js> function validInComments(num) { > eval('/*'+String.fromCharCode(num)+'*/'); > } js> function check(test) { > return function(codePoint) { > try { > test(codePoint); > return true; > } catch (e) { > throw "problem with U+"+codePoint.toString(16).toUpperCase()+": "+e; > } > }; > } js> controlChars.every(check(validInRegExpLiteral)) true js> controlChars.every(check(validInStringLiteral)) true js> controlChars.every(check(validInComments)) true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/javaadapter.doctest0000644000175000017500000000371214433667662024713 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var x = new JavaAdapter(java.util.HashMap, {}) js> x {} js> x instanceof java.util.HashMap true js> x instanceof java.util.Map true js> x.put("foo", "bar") null js> x {foo=bar} js> x.get("foo") bar js> x = new JavaAdapter(java.util.HashMap, {}, {a: "b"}) {a=b} js> x.get("a") b js> x.remove("a") b js> x {} js> x.get("a") null js> x = new JavaAdapter(java.util.HashMap, {get: function(key) {return 2 + this.super$get(key)}}) {} js> x.put("foo", "bar") null js> x.get("foo") 2bar js> x.get("bar") 2.0 js> x = new JavaAdapter({}); x.class.superclass class java.lang.Object js> x.class.interfaces.length 0 js> var Appendable = java.lang.Appendable js> var Runnable = java.lang.Runnable js> function append() {return this} js> function run() {return "done"} js> var x = new JavaAdapter(Runnable, Appendable, {run: run, append: append}) js> x instanceof Appendable true js> x instanceof Runnable true js> x.append("foo") === x true js> x.run() js> // test access to protected methods js> x = JavaAdapter(java.util.Hashtable, {test: function() {this.rehash();}}); {} js> x.test() js> // test access to protected fields js> x = JavaAdapter(java.util.Vector, {test: function() {return this.elementCount;}}); [] js> x.test() 0 js> x.add(1) true js> x.test() 1 js> // test non-empty constructor with protected field js> x = JavaAdapter(java.util.Vector, {test: function() {return this.elementData.length;}}, 20); [] js> x.test() 20 js> // test extending abstract base class js> x = new java.util.AbstractList({get: function(i) { return i + 1; }}) [] js> x.get(0) 1.0 js> x.get(1) 2.0 js> x.get(2) 3.0 js> // test implementing an interface js> x = new java.util.List({get: function(i) { return i + 1; }, toString: function() { return "[]"; }}) [] js> x.get(0) 1.0 js> x.get(1) 2.0 js> x.get(2) 3.0 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/369860.doctest0000644000175000017500000000101714433667662023204 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var re = new RegExp("(?:)(.*)<\/script>") js> var t = 'foo bar' js> var r = re.exec(t) js> if (r[1] != "boo();") { > throw "Bad result: " + r[1] > } else { > print("ok") > } ok js> var str = "< var matches = str.match(/(?:<<)xy/); js> print(matches.join(", ")); < load('testsrc/doctests/util.js'); js> Object.isSealed function isSealed() { [native code for Object.isSealed, arity=1] } js> expectTypeError(function() { Object.isSealed() }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.isSealed(value) }) > }) js> Object.isSealed({}) false js> var obj = Object.preventExtensions({}); js> Object.isSealed(obj); true js> var obj = Object.defineProperty({}, 'a', {configurable:false}); js> var _ = Object.preventExtensions(obj); js> Object.isSealed(obj); true js> var obj = Object.defineProperty({}, 'a', {configurable:true}); js> var _ = Object.preventExtensions(obj); js> Object.isSealed(obj); false closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/array.sparse.doctest0000644000175000017500000000254414433667662025045 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // test various array methods with sparse arrays js> var x = [] js> x.length = 101 101 js> x[100] = 1 1 js> x.length 101 js> 0 in x false js> x.shift() js> x.length 100 js> 0 in x false js> x ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 js> x.reverse() 1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, js> x.length 100 js> 10 in x false js> x.reverse() ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 js> x.length 100 js> 10 in x false js> x.unshift(2) 101 js> x 2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 js> 10 in x false js> var r = x.splice(50, 5, 3, 4, 5) js> r ,,,, js> r.length 5 js> 3 in r false js> x.length 99 js> x 2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,4,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1 js> x[50] 3 js> 10 in x false js> var s = x.slice(60, 70); js> s ,,,,,,,,, js> s.length 10 js> 5 in s false js> var y = [] js> y.length = 100 100 js> var z = y.concat(x) js> z.length 199 js> 30 in z false js> 130 in z false closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/date.parse.doctest0000644000175000017500000000127214433667662024456 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> Date.parse('1970-01-01T00:00:00.000Z') 0 js> Date.parse('1970-01-01T00:00:00.000A') NaN js> Date.parse('1971-02-02T10:10:10.001Z') 34337410001 js> var d = new Date() js> var _ = d.setMilliseconds(0); js> Date.parse(d.toISOString()) === d.valueOf(); true js> Date.parse(d.toISOString()) === Date.parse(d.toString()); true js> Date.parse(d.toISOString()) === Date.parse(d.toUTCString()); true js> Date.parse('1970-01-01T00:00:00.000Z a b c') NaN js> Date.parse('1970-01-01T00:00:00.000A') NaN closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.defineproperties.doctest0000644000175000017500000000273014433667662027244 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.defineProperties function defineProperties() { [native code for Object.defineProperties, arity=2] } js> expectTypeError(function() { Object.defineProperties() }); js> expectTypeError(function() { Object.defineProperties({}) }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.defineProperties(value, {}) }) > }) js> Object.defineProperties({}, {p: {value:1}}).p 1 js> var obj = Object.defineProperties({}, {a: {value:1}, b: {value:2}}); js> [obj.a, obj.b].toSource(); [1, 2] js> Object.defineProperties({}, {'wierd name': {value:1}})['wierd name'] 1 js> Object.defineProperties({}, {}).toSource() ({}) js> var obj = {a:1}; js> var obj = Object.defineProperties(obj, {a:{value:2}, b:{value:3}}); js> [obj.a, obj.b].toSource() [2, 3] js> expectTypeError(function() { Object.defineProperties({}, {a: null}) }) js> expectTypeError(function() { Object.defineProperties({}, {a: 1}) }) js> expectTypeError(function() { Object.defineProperties({}, {a: {get: 1}}) }) js> var obj = {a:1} js> expectTypeError(function() { > obj = Object.defineProperties(obj, {b: {value:1}, c:1}); > }); js> obj.b js> js> Object.defineProperties({}, {'0.0': {value:1}, 0: {value:2}})['0'] 2 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/423557.doctest0000644000175000017500000000062614433667662023203 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Test for using keywords as identifiers // https://bugzilla.mozilla.org/show_bug.cgi?id=423557 js> var x = {if: 1} js> x.if 1 js> x.function = 3 3 js> x.if + x.function 4 js> delete x.if true js> x.if closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/623246.doctest0000644000175000017500000000046514433667662023201 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var table = {}; js> table["0"] = 1; 1 js> table["+0"] js> table["-0"] js> table["0"] 1 js> table[0] 1 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/eval.doctest0000644000175000017500000000153414433667662023360 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> // only direct calls to eval should use scope of call js> // indirect calls should be allowed, but use global scope instead js> var value = 'outer'; js> (function() { var value = "inner"; return eval("value"); })(); inner js> (function(_eval) { var value = "inner"; return _eval("value"); })(eval); outer js> (function(obj) { var value = "inner"; return obj.eval("value"); })({eval:eval}); outer js> (function(obj) { var value = "inner"; return obj.f("value"); })({f:eval}); outer js> (function(arr) { var value = "inner"; return arr[0]("value"); })([eval]); outer js> (function() { var value = "inner"; return eval("eval")("value"); })(); outer closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/Counter.doctest0000644000175000017500000000053014433667662024043 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> defineClass("Counter") js> c = new Counter(7) [object Counter] js> c.count 7 js> c.count 8 js> c.count 9 js> c.resetCount() js> c.count 0closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/439530.doctest0000644000175000017500000000045714433667662023203 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var obj = { run: function () { print("\nrunning"); } } js> var r = new java.lang.Runnable(obj); closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/function.bind.doctest0000644000175000017500000000267614433667662025201 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Function.prototype.bind function bind() { [native code for Function.bind, arity=1] } js> expectTypeError(function() { > Function.prototype.bind.call({}) > }); js> typeof (function(){}).bind({}) function js> function Val() { > this.val = 0; > this.change = function(newVal) { this.val = newVal }; > this.valueOf = function() { return 1; } > } js> var a = new Val(), b = new Val(); js> a.change.bind(b)(1) js> [a.val, b.val].toSource() [0, 1] js> new (Val.bind({})) instanceof Val true js> new Val() instanceof Val.bind({}) true js> function add(a, b) { return a + b } js> var add1 = add.bind({}, 1) js> add1(2) 3 js> var add5 = add.bind({}, 5) js> add5(1) 6 js> add1(3) 4 js> function LazyAdd(a, b) { this.result = function() { return a + b } } js> var LazyAdd1 = LazyAdd.bind({}, 1) js> new LazyAdd1(4).result() 5 js> add1.length 1 js> Object.isExtensible(add1) true js> Object.getPrototypeOf(add1) === Function.prototype true js> expectTypeError(function() { > var x = add1.caller > }) js> expectTypeError(function() { > add1.caller = 1 > }) js> expectTypeError(function() { > var x = add1.arguments > }) js> expectTypeError(function() { > add1.arguments = 1 > }) closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.isfrozen.doctest0000644000175000017500000000236114433667662025534 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.isFrozen function isFrozen() { [native code for Object.isFrozen, arity=1] } js> expectTypeError(function() { Object.isFrozen() }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.isFrozen(value) }) > }) js> Object.isFrozen({}) false js> var obj = Object.preventExtensions({}); js> Object.isFrozen(obj); true js> var obj = Object.defineProperty({}, 'a', {configurable:true, writable:false}) js> var _ = Object.preventExtensions(obj); js> Object.isFrozen(obj); false js> var obj = Object.defineProperty({}, 'a', {configurable:false, writable:true}) js> var _ = Object.preventExtensions(obj); js> Object.isFrozen(obj); false js> var obj = Object.defineProperty({}, 'a', {configurable:false, writable:false}) js> var _ = Object.preventExtensions(obj); js> Object.isFrozen(obj); true js> var obj = Object.defineProperty({}, 'a', {configurable:false, set: function(){} }) js> var _ = Object.preventExtensions(obj); js> Object.isFrozen(obj); true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/Matrix.doctest0000644000175000017500000000137514433667662023700 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. Test of Matrix example. js> defineClass("Matrix") js> var m = new Matrix(2); // A constructor call, see "Matrix(int dimension)" js> m // Object.toString will call "Matrix.getClassName()" [object Matrix] js> m[0][0] = 3; 3 js> uneval(m[0]); // an array was created automatically! [3] js> uneval(m[1]); // array is created even if we don't set a value [] js> m.dim; // we can access the "dim" property 2 js> m.dim = 3; 3 js> m.dim; // but not modify the "dim" property 2 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.extensible.doctest0000644000175000017500000000210714433667662026035 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.isExtensible; function isExtensible() { [native code for Object.isExtensible, arity=1] } js> expectTypeError(function() { Object.isExtensible() }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.isExtensible(value) }) > }) js> Object.preventExtensions; function preventExtensions() { [native code for Object.preventExtensions, arity=1] } js> expectTypeError(function() { Object.preventExtensions() }); js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.preventExtensions(value) }) > }) js> var x = {}; js> Object.isExtensible(x); true js> var y = Object.preventExtensions(x); js> y === x; true js> Object.isExtensible(x); false js> x.a = 1; x.a js> js> x.__defineGetter__('b', function() { return 1 }); x.b js> closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/iteratorKeys.doctest0000644000175000017500000000063314433667662025115 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> version(170) 0 js> var foo = { > __iterator__ : function(onlyKeys) { > print(onlyKeys); > yield [0, "a"]; > } > }; js> for each (let f in foo) {} false js> for (let f in foo) {} trueclosure-compiler-20130227+dfsg1/rhino/testsrc/doctests/434041.doctest0000644000175000017500000000074014433667662023166 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Regression test for bug https://bugzilla.mozilla.org/show_bug.cgi?id=434041 js> function add(_object, _key, _value) { > _object[_key] = _value; > } js> var o = {}; js> add(o, 'a', 'b'); js> o.toSource(); ({a:"b"}) js> add(o, 3, 'c'); js> o.toSource(); ({a:"b", 3:"c"})closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/477233.doctest0000644000175000017500000000117714433667662023205 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var xml = > > One > > > Two > > ; js> var atom = new Namespace("http://www.w3.org/2005/Atom"); js> print(xml.atom::entry.(atom::title == "One")); One closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/arguments.doctest0000644000175000017500000000643014433667662024436 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var args = (function() { return arguments })() js> Object.getPrototypeOf(args) === Object.prototype true js> args.constructor === Object.prototype.constructor true js> Object.getOwnPropertyDescriptor(args, "constructor") === void 0 true js> args.toString() [object Arguments] js> var toStr = Object.prototype.toString; js> var _ = Object.prototype.toString = function() { > return this === args ? > "executes Object.prototype.toString.call(arguments)" : > "'this' should be 'arguments'" > } js> args.toString() executes Object.prototype.toString.call(arguments) js> Object.prototype.toString = toStr; undefined js> var toLocStr = Object.prototype.toLocaleString; js> var _ = Object.prototype.toLocaleString = function() { > return this === args ? > "executes Object.prototype.toLocaleString.call(arguments)" : > "'this' should be 'arguments'" > } js> args.toLocaleString() executes Object.prototype.toLocaleString.call(arguments) js> Object.prototype.toLocaleString = toLocStr; undefined; js> (function() { return arguments[2] })('a','b','c') c js> (function(x,y,z) { return arguments[2] })('a','b','c') c js> (function(x) { > arguments[0] = "modified"; > return x; > })("original") modified js> (function(x) { > x = "modified"; > return arguments[0]; > })("original") modified js> (function(x) { > delete arguments[0]; > arguments[0] = "modified"; > return x; > })("original") original js> (function(x) { > delete x; > var x = "modified"; > return arguments[0] > })("original") modified js> (function(x) { > Object.defineProperty(arguments, 0, {get:function(){return "modified"}}); > return arguments[0]; > })("original") modified js> (function(x) { > Object.defineProperty(arguments, 0, {get:function(){return "modified"}}); > return x; > })("original") original js> (function(x) { > Object.defineProperty(arguments, 0, {value:"modified"}); > return arguments[0]; > })("original") modified js> (function(x) { > Object.defineProperty(arguments, 0, {value:"modified"}); > return x; > })("original") modified js> (function() { for (var i in arguments) print(i); })('a','b','c') 0 1 2 js> (function() { > arguments.a = 1; > Object.defineProperty(arguments, 'b', {value:2, enumerable:true}); > Object.defineProperty(arguments, 'c', {value:3, enumerable:false}); > Object.defineProperty(arguments, 0, {enumerable:false}); > for (var p in arguments) print(p); > })('hi') a b js> (function() { return Object.getOwnPropertyDescriptor(arguments, 0) === undefined })() true js> (function() { return Object.getOwnPropertyDescriptor(arguments, 0).toSource(); })("a") ({value:"a", writable:true, enumerable:true, configurable:true}) js> (function(x) { > Object.defineProperty(arguments, 0, {enumerable:false}); > return Object.getOwnPropertyDescriptor(arguments, 0).enumerable; > })("original") false js> (function() { > Object.defineProperty(arguments, 0, {value:2, writable:false}); > arguments[0] = 3; > return arguments[0]; > })(1); 2 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/with.doctest0000644000175000017500000000216514433667662023405 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> // when the variable is not in the with-object, it'll be set on the outer scope js> (function() { with ({}) { var foo = 1; }; return foo; })() 1 js> // when the function is not in the with-object, it'll be set on the outer scope js> (function() { with ({}) { function foo() { return 2; } }; return foo(); })() 2 js> // when the variable is in the with-object, it will be updated on the with-object js> (function() { var obj = {foo:0}; with (obj) { var foo = 3; }; return obj.foo; })() 3 js> // functions declared inside with blocks are always bound to the closest enclosing function js> (function() { function foo(){ return 0; }; with ({}) { function foo() { return 4; } }; return foo(); })() 4 js> // functions declared inside with blocks do not affect the properties of the with-obj js> (function() { var obj = {foo:5}; with (obj) { function foo() { return 0; } }; return obj.foo; })() 5 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/object.getprototypeof.doctest0000644000175000017500000000157514433667662026775 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> load('testsrc/doctests/util.js'); js> Object.getPrototypeOf; function getPrototypeOf() { [native code for Object.getPrototypeOf, arity=1] } js> expectTypeError(function() { Object.getPrototypeOf() }) js> [undefined, null, true, 1, 'hello'].forEach(function(value) { > expectTypeError(function() { Object.getPrototypeOf(value) }) > }) js> [(function(){}), [], {}].every(function(obj) { > return Object.getPrototypeOf(obj) === obj.__proto__; > }); true js> function UserDefined() {} js> [Date, UserDefined].every(function(type) { > var instance; > eval('instance = new '+type.name); > return Object.getPrototypeOf(instance) === type.prototype; > }); true closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/canonicalize.doctest0000644000175000017500000000103714433667662025066 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> var obj = new java.lang.Object(); js> obj java.lang.Object@97a560 js> obj + "%%" java.lang.Object@97a560%% js> var obj = new java.lang.Object(); js> obj java.lang.Object@1d15445 js> "foo" + obj + "bar" foojava.lang.Object@1d15445bar js> var obj2 = new java.lang.Object(); js> obj + obj2 java.lang.Object@1d15445java.lang.Object@1f3aa07 closure-compiler-20130227+dfsg1/rhino/testsrc/doctests/test2.doctest0000644000175000017500000000063014433667662023466 0ustar apoapo// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. js> a = [] js> a[0] = 10 10 js> a[1] = 11 11 js> a[3] = 13 13 js> a[4] = 14 14 js> a[5] = 15 15 js> a.reverse() 15,14,13,,11,10 js> a.reverse() 10,11,,13,14,15 js> a.concat([16,17]) 10,11,,13,14,15,16,17 closure-compiler-20130227+dfsg1/rhino/testsrc/opt0.tests0000644000175000017500000022156114433667662021164 0ustar apoapoe4x/Expressions/11.1.1.js e4x/Expressions/11.1.2.js e4x/Expressions/11.1.3.js e4x/Expressions/11.1.4-05.js e4x/Expressions/11.1.4-06.js e4x/Expressions/11.1.4-07.js e4x/Expressions/11.1.5.js e4x/Expressions/11.2.1.js e4x/Expressions/11.2.2.js e4x/Expressions/11.2.3.js e4x/Expressions/11.2.4.js e4x/Expressions/11.3.1.js e4x/Expressions/11.3.2.js e4x/Expressions/11.4.1.js e4x/Expressions/11.5.1.js e4x/Expressions/11.6.2.js e4x/Expressions/11.6.3.js e4x/Expressions/regress-301545.js e4x/Expressions/regress-302531.js e4x/Expressions/regress-340024.js e4x/Expressions/regress-496113.js e4x/GC/regress-292455.js e4x/GC/regress-313952-01.js e4x/GC/regress-324117.js e4x/Namespace/13.2.1.js e4x/Namespace/13.2.2.js e4x/Namespace/13.2.5.js e4x/Namespace/regress-283972.js e4x/Namespace/regress-292863.js e4x/Namespace/regress-350442.js e4x/Namespace/regress-444608-02.js e4x/Namespace/regress-444608.js e4x/QName/13.3.1.js e4x/QName/13.3.2.js e4x/QName/13.3.5.js e4x/QName/regress-373595-01.js e4x/QName/regress-373595-02.js e4x/QName/regress-373595-03.js e4x/QName/regress-444608.js e4x/Regress/regress-263934.js e4x/Regress/regress-263935.js e4x/Regress/regress-263936.js e4x/Regress/regress-264369.js e4x/Regress/regress-271545.js e4x/Regress/regress-277650.js e4x/Regress/regress-277664.js e4x/Regress/regress-277683.js e4x/Regress/regress-277779.js e4x/Regress/regress-277935.js e4x/Regress/regress-283349.js e4x/Regress/regress-290056.js e4x/Regress/regress-290499.js e4x/Regress/regress-301553.js e4x/Regress/regress-301573.js e4x/Regress/regress-301596.js e4x/Regress/regress-301692.js e4x/Regress/regress-313799.js e4x/Regress/regress-318922.js e4x/Regress/regress-325425.js e4x/Regress/regress-327691-01.js e4x/Regress/regress-327691-02.js e4x/Regress/regress-327697.js e4x/Regress/regress-328249.js e4x/Regress/regress-329257.js e4x/Regress/regress-331664.js e4x/Regress/regress-350206-1.js e4x/Regress/regress-350206.js e4x/Regress/regress-352103.js e4x/Regress/regress-354145-01.js e4x/Regress/regress-354145-02.js e4x/Regress/regress-354145-03.js e4x/Regress/regress-354145-04.js e4x/Regress/regress-354145-05.js e4x/Regress/regress-354145-07.js e4x/Regress/regress-355474-02.js e4x/Regress/regress-356238-01.js e4x/Regress/regress-369032.js e4x/Regress/regress-369536.js e4x/Regress/regress-372564.js e4x/Regress/regress-374106.js e4x/Regress/regress-374112.js e4x/Regress/regress-374116.js e4x/Regress/regress-374160.js e4x/Regress/regress-375406.js e4x/Regress/regress-378492.js e4x/Regress/regress-407323.js e4x/Regress/regress-426520.js e4x/Regress/regress-453915.js e4x/Regress/regress-460180.js e4x/Regress/regress-465063.js e4x/Regress/regress-470619.js e4x/Regress/regress-473709.js e4x/Regress/regress-474319.js e4x/Regress/regress-477053.js e4x/Statements/12.1.js e4x/Statements/12.2.js e4x/Statements/12.3-01.js e4x/TypeConversion/10.1.1.js e4x/TypeConversion/10.1.2.js e4x/TypeConversion/10.3.1.js e4x/TypeConversion/10.3.js e4x/TypeConversion/10.4.1.js e4x/TypeConversion/10.4.js e4x/Types/9.1.1.1.js e4x/Types/9.1.1.2.js e4x/Types/9.1.1.3.js e4x/Types/9.1.1.6.js e4x/Types/9.2.1.1.js e4x/Types/9.2.1.2.js e4x/Types/9.2.1.8.js e4x/XML/13.4.1.js e4x/XML/13.4.2.js e4x/XML/13.4.3.10.js e4x/XML/13.4.4.11.js e4x/XML/13.4.4.12-1.js e4x/XML/13.4.4.12.js e4x/XML/13.4.4.13.js e4x/XML/13.4.4.14.js e4x/XML/13.4.4.15.js e4x/XML/13.4.4.16.js e4x/XML/13.4.4.18.js e4x/XML/13.4.4.19.js e4x/XML/13.4.4.2.js e4x/XML/13.4.4.20.js e4x/XML/13.4.4.21.js e4x/XML/13.4.4.22.js e4x/XML/13.4.4.23.js e4x/XML/13.4.4.24.js e4x/XML/13.4.4.25.js e4x/XML/13.4.4.27.js e4x/XML/13.4.4.29.js e4x/XML/13.4.4.3.js e4x/XML/13.4.4.30.js e4x/XML/13.4.4.31.js e4x/XML/13.4.4.32-01.js e4x/XML/13.4.4.32.js e4x/XML/13.4.4.33.js e4x/XML/13.4.4.34.js e4x/XML/13.4.4.35.js e4x/XML/13.4.4.36.js e4x/XML/13.4.4.37.js e4x/XML/13.4.4.38.js e4x/XML/13.4.4.39.js e4x/XML/13.4.4.4.js e4x/XML/13.4.4.40.js e4x/XML/13.4.4.5.js e4x/XML/13.4.4.6.js e4x/XML/13.4.4.7.js e4x/XML/13.4.4.8.js e4x/XML/13.4.4.9.js e4x/XML/regress-291930.js e4x/XML/regress-324688.js e4x/XML/regress-336921.js e4x/XMLList/13.5.1.js e4x/XMLList/13.5.2.js e4x/XMLList/13.5.4.10.js e4x/XMLList/13.5.4.11.js e4x/XMLList/13.5.4.12.js e4x/XMLList/13.5.4.13.js e4x/XMLList/13.5.4.14.js e4x/XMLList/13.5.4.15.js e4x/XMLList/13.5.4.16.js e4x/XMLList/13.5.4.17.js e4x/XMLList/13.5.4.18.js e4x/XMLList/13.5.4.19.js e4x/XMLList/13.5.4.2.js e4x/XMLList/13.5.4.20.js e4x/XMLList/13.5.4.21.js e4x/XMLList/13.5.4.22.js e4x/XMLList/13.5.4.3.js e4x/XMLList/13.5.4.4.js e4x/XMLList/13.5.4.5.js e4x/XMLList/13.5.4.6.js e4x/XMLList/13.5.4.7.js e4x/XMLList/13.5.4.8.js e4x/XMLList/13.5.4.9.js e4x/decompilation/regress-349814.js e4x/decompilation/regress-349815.js e4x/decompilation/regress-349822.js e4x/decompilation/regress-349956.js e4x/decompilation/regress-355101.js e4x/decompilation/regress-355474-01.js e4x/decompilation/regress-373678.js e4x/decompilation/regress-429249.js e4x/extensions/regress-305335.js e4x/extensions/regress-321547.js e4x/extensions/regress-327534.js e4x/extensions/regress-327897.js e4x/extensions/regress-354145-06.js e4x/extensions/regress-354151-01.js e4x/extensions/regress-354151-02.js e4x/extensions/regress-374025.js e4x/extensions/regress-450871-01.js e4x/extensions/regress-450871-02.js e4x/extensions/regress-462734-01.js ecma/Array/15.4-1.js ecma/Array/15.4-2.js ecma/Array/15.4.1.1.js ecma/Array/15.4.1.2.js ecma/Array/15.4.1.3.js ecma/Array/15.4.1.js ecma/Array/15.4.2.1-1.js ecma/Array/15.4.2.1-2.js ecma/Array/15.4.2.1-3.js ecma/Array/15.4.2.2-1.js ecma/Array/15.4.2.2-2.js ecma/Array/15.4.2.3.js ecma/Array/15.4.3.1-2.js ecma/Array/15.4.3.2.js ecma/Array/15.4.4.1.js ecma/Array/15.4.4.2.js ecma/Array/15.4.4.3-1.js ecma/Array/15.4.4.4-1.js ecma/Array/15.4.4.4-2.js ecma/Array/15.4.4.5-1.js ecma/Array/15.4.4.5-2.js ecma/Array/15.4.4.5-3.js ecma/Array/15.4.4.js ecma/Array/15.4.5.1-2.js ecma/Array/15.4.5.2-1.js ecma/Array/15.4.5.2-2.js ecma/Boolean/15.6.1.js ecma/Boolean/15.6.2.js ecma/Boolean/15.6.3.1-1.js ecma/Boolean/15.6.3.1-2.js ecma/Boolean/15.6.3.1-3.js ecma/Boolean/15.6.3.1-4.js ecma/Boolean/15.6.3.1.js ecma/Boolean/15.6.4-1.js ecma/Boolean/15.6.4.1.js ecma/Boolean/15.6.4.2-1.js ecma/Boolean/15.6.4.2-2.js ecma/Boolean/15.6.4.2-3.js ecma/Boolean/15.6.4.2-4-n.js ecma/Boolean/15.6.4.3-1.js ecma/Boolean/15.6.4.3-2.js ecma/Boolean/15.6.4.3-3.js ecma/Boolean/15.6.4.3-4-n.js ecma/Boolean/15.6.4.3.js ecma/Boolean/15.6.4.js ecma/Date/15.9.1.1-1.js ecma/Date/15.9.1.1-2.js ecma/Date/15.9.1.13-1.js ecma/Date/15.9.2.1.js ecma/Date/15.9.2.2-1.js ecma/Date/15.9.2.2-2.js ecma/Date/15.9.2.2-3.js ecma/Date/15.9.2.2-4.js ecma/Date/15.9.2.2-5.js ecma/Date/15.9.2.2-6.js ecma/Date/15.9.3.1-1.js ecma/Date/15.9.3.1-2.js ecma/Date/15.9.3.1-3.js ecma/Date/15.9.3.1-4.js ecma/Date/15.9.3.1-5.js ecma/Date/15.9.3.2-1.js ecma/Date/15.9.3.2-2.js ecma/Date/15.9.3.2-3.js ecma/Date/15.9.3.2-4.js ecma/Date/15.9.3.2-5.js ecma/Date/15.9.3.8-1.js ecma/Date/15.9.3.8-2.js ecma/Date/15.9.3.8-3.js ecma/Date/15.9.3.8-4.js ecma/Date/15.9.3.8-5.js ecma/Date/15.9.4.2-1.js ecma/Date/15.9.4.2.js ecma/Date/15.9.4.3.js ecma/Date/15.9.5.1.js ecma/Date/15.9.5.10-1.js ecma/Date/15.9.5.10-10.js ecma/Date/15.9.5.10-11.js ecma/Date/15.9.5.10-12.js ecma/Date/15.9.5.10-13.js ecma/Date/15.9.5.10-3.js ecma/Date/15.9.5.10-4.js ecma/Date/15.9.5.10-5.js ecma/Date/15.9.5.10-6.js ecma/Date/15.9.5.10-7.js ecma/Date/15.9.5.10-8.js ecma/Date/15.9.5.10-9.js ecma/Date/15.9.5.11-1.js ecma/Date/15.9.5.11-3.js ecma/Date/15.9.5.11-4.js ecma/Date/15.9.5.11-5.js ecma/Date/15.9.5.11-6.js ecma/Date/15.9.5.11-7.js ecma/Date/15.9.5.12-1.js ecma/Date/15.9.5.12-3.js ecma/Date/15.9.5.12-4.js ecma/Date/15.9.5.12-5.js ecma/Date/15.9.5.12-6.js ecma/Date/15.9.5.12-7.js ecma/Date/15.9.5.12-8.js ecma/Date/15.9.5.13-1.js ecma/Date/15.9.5.13-2.js ecma/Date/15.9.5.13-3.js ecma/Date/15.9.5.13-4.js ecma/Date/15.9.5.13-5.js ecma/Date/15.9.5.13-6.js ecma/Date/15.9.5.13-7.js ecma/Date/15.9.5.13-8.js ecma/Date/15.9.5.14.js ecma/Date/15.9.5.15.js ecma/Date/15.9.5.16.js ecma/Date/15.9.5.17.js ecma/Date/15.9.5.18.js ecma/Date/15.9.5.19.js ecma/Date/15.9.5.2-1.js ecma/Date/15.9.5.2-2-n.js ecma/Date/15.9.5.2.js ecma/Date/15.9.5.20.js ecma/Date/15.9.5.21-1.js ecma/Date/15.9.5.21-2.js ecma/Date/15.9.5.21-3.js ecma/Date/15.9.5.21-4.js ecma/Date/15.9.5.21-5.js ecma/Date/15.9.5.21-6.js ecma/Date/15.9.5.21-7.js ecma/Date/15.9.5.21-8.js ecma/Date/15.9.5.22-1.js ecma/Date/15.9.5.22-2.js ecma/Date/15.9.5.22-3.js ecma/Date/15.9.5.22-4.js ecma/Date/15.9.5.22-5.js ecma/Date/15.9.5.22-6.js ecma/Date/15.9.5.22-7.js ecma/Date/15.9.5.22-8.js ecma/Date/15.9.5.23-1.js ecma/Date/15.9.5.23-10.js ecma/Date/15.9.5.23-11.js ecma/Date/15.9.5.23-12.js ecma/Date/15.9.5.23-13.js ecma/Date/15.9.5.23-14.js ecma/Date/15.9.5.23-15.js ecma/Date/15.9.5.23-16.js ecma/Date/15.9.5.23-17.js ecma/Date/15.9.5.23-18.js ecma/Date/15.9.5.23-2.js ecma/Date/15.9.5.23-3-n.js ecma/Date/15.9.5.23-4.js ecma/Date/15.9.5.23-5.js ecma/Date/15.9.5.23-6.js ecma/Date/15.9.5.23-7.js ecma/Date/15.9.5.23-8.js ecma/Date/15.9.5.23-9.js ecma/Date/15.9.5.24-1.js ecma/Date/15.9.5.24-2.js ecma/Date/15.9.5.24-3.js ecma/Date/15.9.5.24-4.js ecma/Date/15.9.5.24-5.js ecma/Date/15.9.5.24-6.js ecma/Date/15.9.5.24-7.js ecma/Date/15.9.5.24-8.js ecma/Date/15.9.5.25-1.js ecma/Date/15.9.5.26-1.js ecma/Date/15.9.5.27-1.js ecma/Date/15.9.5.28-1.js ecma/Date/15.9.5.29-1.js ecma/Date/15.9.5.3-1-n.js ecma/Date/15.9.5.3-2.js ecma/Date/15.9.5.30-1.js ecma/Date/15.9.5.31-1.js ecma/Date/15.9.5.32-1.js ecma/Date/15.9.5.33-1.js ecma/Date/15.9.5.34-1.js ecma/Date/15.9.5.35-1.js ecma/Date/15.9.5.36-1.js ecma/Date/15.9.5.36-2.js ecma/Date/15.9.5.36-3.js ecma/Date/15.9.5.36-4.js ecma/Date/15.9.5.36-5.js ecma/Date/15.9.5.36-6.js ecma/Date/15.9.5.36-7.js ecma/Date/15.9.5.37-1.js ecma/Date/15.9.5.37-2.js ecma/Date/15.9.5.37-3.js ecma/Date/15.9.5.37-4.js ecma/Date/15.9.5.37-5.js ecma/Date/15.9.5.4-1.js ecma/Date/15.9.5.4-2-n.js ecma/Date/15.9.5.5.js ecma/Date/15.9.5.6.js ecma/Date/15.9.5.7.js ecma/Date/15.9.5.8.js ecma/Date/15.9.5.9.js ecma/Date/15.9.5.js ecma/ExecutionContexts/10.1.3-1.js ecma/ExecutionContexts/10.1.3-2.js ecma/ExecutionContexts/10.1.3.js ecma/ExecutionContexts/10.1.4-1.js ecma/ExecutionContexts/10.1.4-10.js ecma/ExecutionContexts/10.1.4-2.js ecma/ExecutionContexts/10.1.4-3.js ecma/ExecutionContexts/10.1.4-4.js ecma/ExecutionContexts/10.1.4-5.js ecma/ExecutionContexts/10.1.4-6.js ecma/ExecutionContexts/10.1.4-7.js ecma/ExecutionContexts/10.1.4-8.js ecma/ExecutionContexts/10.1.5-1.js ecma/ExecutionContexts/10.1.5-2.js ecma/ExecutionContexts/10.1.5-3.js ecma/ExecutionContexts/10.1.5-4.js ecma/ExecutionContexts/10.1.8-2.js ecma/ExecutionContexts/10.1.8-3.js ecma/ExecutionContexts/10.2.1.js ecma/ExecutionContexts/10.2.2-1.js ecma/ExecutionContexts/10.2.2-2.js ecma/ExecutionContexts/10.2.3-1.js ecma/ExecutionContexts/10.2.3-2.js ecma/Expressions/11.1.1.js ecma/Expressions/11.10-1.js ecma/Expressions/11.10-2.js ecma/Expressions/11.10-3.js ecma/Expressions/11.12-1.js ecma/Expressions/11.12-2-n.js ecma/Expressions/11.12-3.js ecma/Expressions/11.12-4.js ecma/Expressions/11.13.1.js ecma/Expressions/11.13.2-1.js ecma/Expressions/11.13.2-2.js ecma/Expressions/11.13.2-3.js ecma/Expressions/11.13.2-4.js ecma/Expressions/11.13.2-5.js ecma/Expressions/11.13.js ecma/Expressions/11.14-1.js ecma/Expressions/11.2.1-1.js ecma/Expressions/11.2.1-2.js ecma/Expressions/11.2.1-3-n.js ecma/Expressions/11.2.1-4-n.js ecma/Expressions/11.2.1-5.js ecma/Expressions/11.2.2-1-n.js ecma/Expressions/11.2.2-1.js ecma/Expressions/11.2.2-10-n.js ecma/Expressions/11.2.2-11.js ecma/Expressions/11.2.2-2-n.js ecma/Expressions/11.2.2-3-n.js ecma/Expressions/11.2.2-4-n.js ecma/Expressions/11.2.2-5-n.js ecma/Expressions/11.2.2-6-n.js ecma/Expressions/11.2.2-7-n.js ecma/Expressions/11.2.2-8-n.js ecma/Expressions/11.2.2-9-n.js ecma/Expressions/11.2.3-1.js ecma/Expressions/11.2.3-2-n.js ecma/Expressions/11.2.3-3-n.js ecma/Expressions/11.2.3-4-n.js ecma/Expressions/11.2.3-5.js ecma/Expressions/11.3.1.js ecma/Expressions/11.3.2.js ecma/Expressions/11.4.1.js ecma/Expressions/11.4.2.js ecma/Expressions/11.4.3.js ecma/Expressions/11.4.4.js ecma/Expressions/11.4.5.js ecma/Expressions/11.4.6.js ecma/Expressions/11.4.7-01.js ecma/Expressions/11.4.7-02.js ecma/Expressions/11.4.8.js ecma/Expressions/11.4.9.js ecma/Expressions/11.5.1.js ecma/Expressions/11.5.2.js ecma/Expressions/11.5.3.js ecma/Expressions/11.6.1-1.js ecma/Expressions/11.6.1-2.js ecma/Expressions/11.6.1-3.js ecma/Expressions/11.6.2-1.js ecma/Expressions/11.6.3.js ecma/Expressions/11.7.1.js ecma/Expressions/11.7.2.js ecma/Expressions/11.7.3.js ecma/Expressions/11.8.1.js ecma/Expressions/11.8.2.js ecma/Expressions/11.8.3.js ecma/Expressions/11.8.4.js ecma/Expressions/11.9.1.js ecma/Expressions/11.9.2.js ecma/Expressions/11.9.3.js ecma/FunctionObjects/15.3.1.1-1.js ecma/FunctionObjects/15.3.1.1-2.js ecma/FunctionObjects/15.3.1.1-3.js ecma/FunctionObjects/15.3.2.1-1.js ecma/FunctionObjects/15.3.2.1-2.js ecma/FunctionObjects/15.3.2.1-3.js ecma/FunctionObjects/15.3.3.1-2.js ecma/FunctionObjects/15.3.3.1-3.js ecma/FunctionObjects/15.3.3.1-4.js ecma/FunctionObjects/15.3.3.2.js ecma/FunctionObjects/15.3.4-1.js ecma/FunctionObjects/15.3.4.1.js ecma/FunctionObjects/15.3.4.js ecma/FunctionObjects/15.3.5-1.js ecma/FunctionObjects/15.3.5-2.js ecma/FunctionObjects/15.3.5.1.js ecma/FunctionObjects/15.3.5.3.js ecma/GlobalObject/15.1-1-n.js ecma/GlobalObject/15.1-2-n.js ecma/GlobalObject/15.1.1.1.js ecma/GlobalObject/15.1.1.2.js ecma/GlobalObject/15.1.2.1-2.js ecma/GlobalObject/15.1.2.2-2.js ecma/GlobalObject/15.1.2.3-2.js ecma/GlobalObject/15.1.2.5-2.js ecma/GlobalObject/15.1.2.5-3.js ecma/LexicalConventions/7.1-1.js ecma/LexicalConventions/7.1-2.js ecma/LexicalConventions/7.1-3.js ecma/LexicalConventions/7.2-1.js ecma/LexicalConventions/7.2-2-n.js ecma/LexicalConventions/7.2-3-n.js ecma/LexicalConventions/7.2-4-n.js ecma/LexicalConventions/7.2-5-n.js ecma/LexicalConventions/7.2-6.js ecma/LexicalConventions/7.3-1.js ecma/LexicalConventions/7.3-10.js ecma/LexicalConventions/7.3-11.js ecma/LexicalConventions/7.3-12.js ecma/LexicalConventions/7.3-13-n.js ecma/LexicalConventions/7.3-2.js ecma/LexicalConventions/7.3-3.js ecma/LexicalConventions/7.3-4.js ecma/LexicalConventions/7.3-5.js ecma/LexicalConventions/7.3-6.js ecma/LexicalConventions/7.3-7.js ecma/LexicalConventions/7.3-8.js ecma/LexicalConventions/7.3-9.js ecma/LexicalConventions/7.4.1-1-n.js ecma/LexicalConventions/7.4.1-2-n.js ecma/LexicalConventions/7.4.1-3-n.js ecma/LexicalConventions/7.4.2-1-n.js ecma/LexicalConventions/7.4.2-10-n.js ecma/LexicalConventions/7.4.2-11-n.js ecma/LexicalConventions/7.4.2-12-n.js ecma/LexicalConventions/7.4.2-13-n.js ecma/LexicalConventions/7.4.2-14-n.js ecma/LexicalConventions/7.4.2-15-n.js ecma/LexicalConventions/7.4.2-16-n.js ecma/LexicalConventions/7.4.2-2-n.js ecma/LexicalConventions/7.4.2-3-n.js ecma/LexicalConventions/7.4.2-4-n.js ecma/LexicalConventions/7.4.2-5-n.js ecma/LexicalConventions/7.4.2-6-n.js ecma/LexicalConventions/7.4.2-7-n.js ecma/LexicalConventions/7.4.2-8-n.js ecma/LexicalConventions/7.4.2-9-n.js ecma/LexicalConventions/7.4.3-1-n.js ecma/LexicalConventions/7.4.3-10-n.js ecma/LexicalConventions/7.4.3-11-n.js ecma/LexicalConventions/7.4.3-12-n.js ecma/LexicalConventions/7.4.3-13-n.js ecma/LexicalConventions/7.4.3-14-n.js ecma/LexicalConventions/7.4.3-15-n.js ecma/LexicalConventions/7.4.3-16-n.js ecma/LexicalConventions/7.4.3-2-n.js ecma/LexicalConventions/7.4.3-3-n.js ecma/LexicalConventions/7.4.3-4-n.js ecma/LexicalConventions/7.4.3-5-n.js ecma/LexicalConventions/7.4.3-6-n.js ecma/LexicalConventions/7.4.3-7-n.js ecma/LexicalConventions/7.4.3-8-n.js ecma/LexicalConventions/7.4.3-9-n.js ecma/LexicalConventions/7.5-1.js ecma/LexicalConventions/7.5-10-n.js ecma/LexicalConventions/7.5-2-n.js ecma/LexicalConventions/7.5-3-n.js ecma/LexicalConventions/7.5-4-n.js ecma/LexicalConventions/7.5-5-n.js ecma/LexicalConventions/7.5-6.js ecma/LexicalConventions/7.5-7.js ecma/LexicalConventions/7.5-8-n.js ecma/LexicalConventions/7.5-9-n.js ecma/LexicalConventions/7.6.js ecma/LexicalConventions/7.7.1.js ecma/LexicalConventions/7.7.2.js ecma/LexicalConventions/7.7.3-1.js ecma/LexicalConventions/7.7.3-2.js ecma/LexicalConventions/7.7.3.js ecma/LexicalConventions/7.7.4.js ecma/LexicalConventions/7.8.2-n.js ecma/Math/15.8-2-n.js ecma/Math/15.8-3-n.js ecma/Math/15.8.1.1-1.js ecma/Math/15.8.1.1-2.js ecma/Math/15.8.1.2-1.js ecma/Math/15.8.1.2-2.js ecma/Math/15.8.1.3-1.js ecma/Math/15.8.1.3-2.js ecma/Math/15.8.1.4-1.js ecma/Math/15.8.1.4-2.js ecma/Math/15.8.1.5-1.js ecma/Math/15.8.1.5-2.js ecma/Math/15.8.1.6-1.js ecma/Math/15.8.1.6-2.js ecma/Math/15.8.1.7-1.js ecma/Math/15.8.1.7-2.js ecma/Math/15.8.1.8-1.js ecma/Math/15.8.1.8-2.js ecma/Math/15.8.1.8-3.js ecma/Math/15.8.1.js ecma/Math/15.8.2.1.js ecma/Math/15.8.2.10.js ecma/Math/15.8.2.11.js ecma/Math/15.8.2.12.js ecma/Math/15.8.2.13.js ecma/Math/15.8.2.14.js ecma/Math/15.8.2.15.js ecma/Math/15.8.2.16.js ecma/Math/15.8.2.17.js ecma/Math/15.8.2.18.js ecma/Math/15.8.2.2.js ecma/Math/15.8.2.3.js ecma/Math/15.8.2.4.js ecma/Math/15.8.2.5.js ecma/Math/15.8.2.6.js ecma/Math/15.8.2.7.js ecma/Math/15.8.2.8.js ecma/Math/15.8.2.9.js ecma/Number/15.7.1.js ecma/Number/15.7.2.js ecma/Number/15.7.3.1-1.js ecma/Number/15.7.3.1-2.js ecma/Number/15.7.3.1-3.js ecma/Number/15.7.3.2-1.js ecma/Number/15.7.3.2-2.js ecma/Number/15.7.3.2-3.js ecma/Number/15.7.3.2-4.js ecma/Number/15.7.3.3-1.js ecma/Number/15.7.3.3-2.js ecma/Number/15.7.3.3-3.js ecma/Number/15.7.3.3-4.js ecma/Number/15.7.3.4-1.js ecma/Number/15.7.3.4-2.js ecma/Number/15.7.3.4-3.js ecma/Number/15.7.3.4-4.js ecma/Number/15.7.3.5-1.js ecma/Number/15.7.3.5-2.js ecma/Number/15.7.3.5-3.js ecma/Number/15.7.3.5-4.js ecma/Number/15.7.3.6-1.js ecma/Number/15.7.3.6-2.js ecma/Number/15.7.3.6-3.js ecma/Number/15.7.3.6-4.js ecma/Number/15.7.3.js ecma/Number/15.7.4-1.js ecma/Number/15.7.4.1.js ecma/Number/15.7.4.2-1.js ecma/Number/15.7.4.2-2-n.js ecma/Number/15.7.4.2-3-n.js ecma/Number/15.7.4.2-4.js ecma/Number/15.7.4.3-1.js ecma/Number/15.7.4.3-2.js ecma/Number/15.7.4.3-3-n.js ecma/ObjectObjects/15.2.1.1.js ecma/ObjectObjects/15.2.1.2.js ecma/ObjectObjects/15.2.2.1.js ecma/ObjectObjects/15.2.2.2.js ecma/ObjectObjects/15.2.3-1.js ecma/ObjectObjects/15.2.3.1-1.js ecma/ObjectObjects/15.2.3.1-2.js ecma/ObjectObjects/15.2.3.1-3.js ecma/ObjectObjects/15.2.3.1-4.js ecma/ObjectObjects/15.2.3.js ecma/ObjectObjects/15.2.4.1.js ecma/ObjectObjects/15.2.4.2.js ecma/ObjectObjects/15.2.4.3.js ecma/SourceText/6-1.js ecma/SourceText/6-2.js ecma/Statements/12.10-1.js ecma/Statements/12.10.js ecma/Statements/12.2-1.js ecma/Statements/12.5-1.js ecma/Statements/12.5-2.js ecma/Statements/12.6.1-1.js ecma/Statements/12.6.2-1.js ecma/Statements/12.6.2-2.js ecma/Statements/12.6.2-3.js ecma/Statements/12.6.2-4.js ecma/Statements/12.6.2-5.js ecma/Statements/12.6.2-6.js ecma/Statements/12.6.2-7.js ecma/Statements/12.6.2-8.js ecma/Statements/12.6.2-9-n.js ecma/Statements/12.6.3-1.js ecma/Statements/12.6.3-10.js ecma/Statements/12.6.3-11.js ecma/Statements/12.6.3-12.js ecma/Statements/12.6.3-19.js ecma/Statements/12.6.3-2.js ecma/Statements/12.6.3-3.js ecma/Statements/12.6.3-4.js ecma/Statements/12.6.3-5-n.js ecma/Statements/12.6.3-6-n.js ecma/Statements/12.6.3-7-n.js ecma/Statements/12.6.3-8-n.js ecma/Statements/12.6.3-9-n.js ecma/Statements/12.7-1-n.js ecma/Statements/12.8-1-n.js ecma/Statements/12.9-1-n.js ecma/String/15.5.1.js ecma/String/15.5.2.js ecma/String/15.5.3.1-1.js ecma/String/15.5.3.1-2.js ecma/String/15.5.3.1-3.js ecma/String/15.5.3.1-4.js ecma/String/15.5.3.2-1.js ecma/String/15.5.3.2-2.js ecma/String/15.5.3.2-3.js ecma/String/15.5.3.js ecma/String/15.5.4.1.js ecma/String/15.5.4.10-1.js ecma/String/15.5.4.11-1.js ecma/String/15.5.4.11-3.js ecma/String/15.5.4.11-4.js ecma/String/15.5.4.11-6.js ecma/String/15.5.4.12-2.js ecma/String/15.5.4.12-3.js ecma/String/15.5.4.2-1.js ecma/String/15.5.4.2-2-n.js ecma/String/15.5.4.2-3.js ecma/String/15.5.4.2.js ecma/String/15.5.4.3-1.js ecma/String/15.5.4.3-2.js ecma/String/15.5.4.3-3-n.js ecma/String/15.5.4.4-1.js ecma/String/15.5.4.4-2.js ecma/String/15.5.4.4-3.js ecma/String/15.5.4.4-4.js ecma/String/15.5.4.5-1.js ecma/String/15.5.4.5-2.js ecma/String/15.5.4.5-3.js ecma/String/15.5.4.5-4.js ecma/String/15.5.4.5-5.js ecma/String/15.5.4.6-1.js ecma/String/15.5.4.6-2.js ecma/String/15.5.4.7-1.js ecma/String/15.5.4.7-2.js ecma/String/15.5.4.8-1.js ecma/String/15.5.4.8-3.js ecma/String/15.5.4.9-1.js ecma/String/15.5.4.js ecma/String/15.5.5.1.js ecma/TypeConversion/9.2.js ecma/TypeConversion/9.3-1.js ecma/TypeConversion/9.3.1-1.js ecma/TypeConversion/9.3.1-2.js ecma/TypeConversion/9.3.1-3.js ecma/TypeConversion/9.3.js ecma/TypeConversion/9.4-1.js ecma/TypeConversion/9.4-2.js ecma/TypeConversion/9.5-2.js ecma/TypeConversion/9.6.js ecma/TypeConversion/9.7.js ecma/TypeConversion/9.8.1.js ecma/TypeConversion/9.9-1.js ecma/Types/8.1.js ecma/Types/8.4.js ecma/Types/8.6.2.1-1.js ecma/extensions/10.1.4-9.js ecma/extensions/10.1.6.js ecma/extensions/10.1.8-1.js ecma/extensions/11.6.1-1.js ecma/extensions/11.6.1-2.js ecma/extensions/11.6.1-3.js ecma/extensions/11.6.2-1.js ecma/extensions/15-1.js ecma/extensions/15-2.js ecma/extensions/15.2.1.1.js ecma/extensions/15.2.3-1.js ecma/extensions/15.2.4.js ecma/extensions/15.3.1.1-1.js ecma/extensions/15.3.1.1-2.js ecma/extensions/15.3.2.1-1.js ecma/extensions/15.3.2.1-2.js ecma/extensions/15.3.3.1-1.js ecma/extensions/15.4.3.js ecma/extensions/15.5.3.js ecma/extensions/15.5.4.2.js ecma/extensions/15.5.4.4-4.js ecma/extensions/15.5.4.5-6.js ecma/extensions/15.5.4.7-3.js ecma/extensions/15.6.3.1-5.js ecma/extensions/15.6.3.js ecma/extensions/15.6.4-2.js ecma/extensions/15.7.3.js ecma/extensions/15.7.4.js ecma/extensions/15.8-1.js ecma/extensions/15.9.5.js ecma/extensions/8.6.2.1-1.js ecma/extensions/9.9-1.js ecma/jsref.js ecma_2/Exceptions/boolean-001.js ecma_2/Exceptions/boolean-002.js ecma_2/Exceptions/date-001.js ecma_2/Exceptions/date-002.js ecma_2/Exceptions/date-003.js ecma_2/Exceptions/date-004.js ecma_2/Exceptions/exception-001.js ecma_2/Exceptions/exception-002.js ecma_2/Exceptions/exception-003.js ecma_2/Exceptions/exception-004.js ecma_2/Exceptions/exception-005.js ecma_2/Exceptions/exception-006.js ecma_2/Exceptions/exception-007.js ecma_2/Exceptions/exception-008.js ecma_2/Exceptions/exception-009.js ecma_2/Exceptions/exception-010-n.js ecma_2/Exceptions/exception-011-n.js ecma_2/Exceptions/expression-001.js ecma_2/Exceptions/expression-002.js ecma_2/Exceptions/expression-003.js ecma_2/Exceptions/expression-004.js ecma_2/Exceptions/expression-005.js ecma_2/Exceptions/expression-006.js ecma_2/Exceptions/expression-007.js ecma_2/Exceptions/expression-008.js ecma_2/Exceptions/expression-009.js ecma_2/Exceptions/expression-010.js ecma_2/Exceptions/expression-011.js ecma_2/Exceptions/expression-012.js ecma_2/Exceptions/expression-013.js ecma_2/Exceptions/expression-014.js ecma_2/Exceptions/expression-015.js ecma_2/Exceptions/expression-016.js ecma_2/Exceptions/expression-017.js ecma_2/Exceptions/expression-019.js ecma_2/Exceptions/function-001.js ecma_2/Exceptions/global-001.js ecma_2/Exceptions/global-002.js ecma_2/Exceptions/lexical-001.js ecma_2/Exceptions/lexical-002.js ecma_2/Exceptions/lexical-003.js ecma_2/Exceptions/lexical-004.js ecma_2/Exceptions/lexical-005.js ecma_2/Exceptions/lexical-006.js ecma_2/Exceptions/lexical-007.js ecma_2/Exceptions/lexical-008.js ecma_2/Exceptions/lexical-009.js ecma_2/Exceptions/lexical-010.js ecma_2/Exceptions/lexical-011.js ecma_2/Exceptions/lexical-012.js ecma_2/Exceptions/lexical-013.js ecma_2/Exceptions/lexical-014.js ecma_2/Exceptions/lexical-015.js ecma_2/Exceptions/lexical-016.js ecma_2/Exceptions/lexical-017.js ecma_2/Exceptions/lexical-018.js ecma_2/Exceptions/lexical-019.js ecma_2/Exceptions/lexical-020.js ecma_2/Exceptions/lexical-021.js ecma_2/Exceptions/lexical-022.js ecma_2/Exceptions/lexical-023.js ecma_2/Exceptions/lexical-024.js ecma_2/Exceptions/lexical-025.js ecma_2/Exceptions/lexical-026.js ecma_2/Exceptions/lexical-027.js ecma_2/Exceptions/lexical-028.js ecma_2/Exceptions/lexical-029.js ecma_2/Exceptions/lexical-030.js ecma_2/Exceptions/lexical-031.js ecma_2/Exceptions/lexical-032.js ecma_2/Exceptions/lexical-033.js ecma_2/Exceptions/lexical-034.js ecma_2/Exceptions/lexical-035.js ecma_2/Exceptions/lexical-036.js ecma_2/Exceptions/lexical-037.js ecma_2/Exceptions/lexical-038.js ecma_2/Exceptions/lexical-039.js ecma_2/Exceptions/lexical-040.js ecma_2/Exceptions/lexical-041.js ecma_2/Exceptions/lexical-042.js ecma_2/Exceptions/lexical-047.js ecma_2/Exceptions/lexical-048.js ecma_2/Exceptions/lexical-049.js ecma_2/Exceptions/lexical-050.js ecma_2/Exceptions/lexical-051.js ecma_2/Exceptions/lexical-052.js ecma_2/Exceptions/lexical-053.js ecma_2/Exceptions/lexical-054.js ecma_2/Exceptions/number-001.js ecma_2/Exceptions/number-002.js ecma_2/Exceptions/number-003.js ecma_2/Exceptions/statement-001.js ecma_2/Exceptions/statement-002.js ecma_2/Exceptions/statement-003.js ecma_2/Exceptions/statement-004.js ecma_2/Exceptions/statement-005.js ecma_2/Exceptions/statement-006.js ecma_2/Exceptions/statement-007.js ecma_2/Exceptions/statement-008.js ecma_2/Exceptions/statement-009.js ecma_2/Exceptions/string-001.js ecma_2/Exceptions/string-002.js ecma_2/Expressions/StrictEquality-001.js ecma_2/FunctionObjects/apply-001-n.js ecma_2/FunctionObjects/call-1.js ecma_2/LexicalConventions/keywords-001.js ecma_2/LexicalConventions/regexp-literals-001.js ecma_2/LexicalConventions/regexp-literals-002.js ecma_2/RegExp/constructor-001.js ecma_2/RegExp/exec-002.js ecma_2/RegExp/function-001.js ecma_2/RegExp/hex-001.js ecma_2/RegExp/multiline-001.js ecma_2/RegExp/octal-001.js ecma_2/RegExp/octal-002.js ecma_2/RegExp/octal-003.js ecma_2/RegExp/properties-001.js ecma_2/RegExp/properties-002.js ecma_2/RegExp/regexp-enumerate-001.js ecma_2/RegExp/regress-001.js ecma_2/RegExp/unicode-001.js ecma_2/Statements/dowhile-001.js ecma_2/Statements/dowhile-002.js ecma_2/Statements/dowhile-003.js ecma_2/Statements/dowhile-004.js ecma_2/Statements/dowhile-005.js ecma_2/Statements/dowhile-006.js ecma_2/Statements/dowhile-007.js ecma_2/Statements/forin-001.js ecma_2/Statements/forin-002.js ecma_2/Statements/if-001.js ecma_2/Statements/label-001.js ecma_2/Statements/label-002.js ecma_2/Statements/switch-001.js ecma_2/Statements/switch-002.js ecma_2/Statements/switch-003.js ecma_2/Statements/switch-004.js ecma_2/Statements/try-001.js ecma_2/Statements/try-003.js ecma_2/Statements/try-004.js ecma_2/Statements/try-005.js ecma_2/Statements/try-006.js ecma_2/Statements/try-007.js ecma_2/Statements/try-008.js ecma_2/Statements/try-009.js ecma_2/Statements/try-010.js ecma_2/Statements/try-012.js ecma_2/Statements/while-001.js ecma_2/Statements/while-002.js ecma_2/Statements/while-003.js ecma_2/Statements/while-004.js ecma_2/String/match-001.js ecma_2/String/match-002.js ecma_2/String/match-003.js ecma_2/String/match-004.js ecma_2/String/split-001.js ecma_2/String/split-002.js ecma_2/String/split-003.js ecma_2/extensions/constructor-001.js ecma_2/extensions/function-001.js ecma_2/extensions/instanceof-001.js ecma_2/extensions/instanceof-002.js ecma_2/extensions/instanceof-003-n.js ecma_2/extensions/instanceof-004-n.js ecma_2/extensions/instanceof-005-n.js ecma_2/extensions/instanceof-006.js ecma_2/instanceof/instanceof-001.js ecma_2/instanceof/instanceof-002.js ecma_2/instanceof/regress-7635.js ecma_2/jsref.js ecma_3/Array/15.4.4.11-01.js ecma_3/Array/15.4.4.3-1.js ecma_3/Array/15.4.4.4-001.js ecma_3/Array/15.5.4.8-01.js ecma_3/Array/regress-101488.js ecma_3/Array/regress-130451.js ecma_3/Array/regress-322135-01.js ecma_3/Array/regress-390598.js ecma_3/Array/regress-421325.js ecma_3/Array/regress-430717.js ecma_3/Array/regress-488989.js ecma_3/Date/15.9.1.2-01.js ecma_3/Date/15.9.4.3.js ecma_3/Date/15.9.5.3.js ecma_3/Date/15.9.5.4.js ecma_3/Date/15.9.5.5.js ecma_3/Date/15.9.5.6.js ecma_3/Date/15.9.5.7.js ecma_3/Date/regress-452786.js ecma_3/Exceptions/15.11.1.1.js ecma_3/Exceptions/15.11.4.4-1.js ecma_3/Exceptions/15.11.7.6-001.js ecma_3/Exceptions/15.11.7.6-002.js ecma_3/Exceptions/15.11.7.6-003.js ecma_3/Exceptions/binding-001.js ecma_3/Exceptions/regress-58946.js ecma_3/Exceptions/regress-95101.js ecma_3/ExecutionContexts/10.1.3-1.js ecma_3/ExecutionContexts/10.1.3-2.js ecma_3/ExecutionContexts/10.1.3.js ecma_3/ExecutionContexts/10.1.4-1.js ecma_3/ExecutionContexts/10.6.1-01.js ecma_3/ExecutionContexts/regress-23346.js ecma_3/ExecutionContexts/regress-448595-01.js ecma_3/Expressions/11.10-01.js ecma_3/Expressions/11.10-02.js ecma_3/Expressions/11.10-03.js ecma_3/Expressions/11.6.1-1.js ecma_3/Expressions/11.7.1-01.js ecma_3/Expressions/11.7.2-01.js ecma_3/Expressions/11.7.3-01.js ecma_3/Expressions/11.9.6-1.js ecma_3/FunExpr/fe-001-n.js ecma_3/FunExpr/fe-001.js ecma_3/FunExpr/fe-002.js ecma_3/Function/15.3.4.3-1.js ecma_3/Function/15.3.4.4-1.js ecma_3/Function/arguments-001.js ecma_3/Function/arguments-002.js ecma_3/Function/call-001.js ecma_3/Function/regress-131964.js ecma_3/Function/regress-137181.js ecma_3/Function/regress-193555.js ecma_3/Function/regress-313570.js ecma_3/Function/regress-49286.js ecma_3/Function/regress-58274.js ecma_3/Function/regress-85880.js ecma_3/Function/regress-94506.js ecma_3/Function/regress-97921.js ecma_3/Function/scope-001.js ecma_3/Function/scope-002.js ecma_3/Number/15.7.4.2-01.js ecma_3/Number/15.7.4.3-02.js ecma_3/Number/15.7.4.5-1.js ecma_3/Number/15.7.4.5-2.js ecma_3/Number/15.7.4.6-1.js ecma_3/Number/15.7.4.7-1.js ecma_3/Number/15.7.4.7-2.js ecma_3/Number/regress-442242-01.js ecma_3/NumberFormatting/tostring-001.js ecma_3/Object/8.6.2.6-001.js ecma_3/Object/class-001.js ecma_3/Object/class-002.js ecma_3/Object/class-003.js ecma_3/Object/class-004.js ecma_3/Object/class-005.js ecma_3/Object/regress-361274.js ecma_3/Object/regress-385393-07.js ecma_3/Object/regress-459405.js ecma_3/Object/regress-72773.js ecma_3/Object/regress-79129-001.js ecma_3/Operators/11.13.1-001.js ecma_3/Operators/11.13.1-002.js ecma_3/Operators/11.4.1-001.js ecma_3/Operators/11.4.1-002.js ecma_3/RegExp/15.10.2-1.js ecma_3/RegExp/15.10.2.12.js ecma_3/RegExp/15.10.3.1-1.js ecma_3/RegExp/15.10.3.1-2.js ecma_3/RegExp/15.10.4.1-1.js ecma_3/RegExp/15.10.4.1-2.js ecma_3/RegExp/15.10.4.1-3.js ecma_3/RegExp/15.10.4.1-4.js ecma_3/RegExp/15.10.4.1-5-n.js ecma_3/RegExp/15.10.6.2-1.js ecma_3/RegExp/15.10.6.2-2.js ecma_3/RegExp/octal-001.js ecma_3/RegExp/octal-002.js ecma_3/RegExp/perlstress-001.js ecma_3/RegExp/perlstress-002.js ecma_3/RegExp/regress-100199.js ecma_3/RegExp/regress-105972.js ecma_3/RegExp/regress-119909.js ecma_3/RegExp/regress-122076.js ecma_3/RegExp/regress-123437.js ecma_3/RegExp/regress-165353.js ecma_3/RegExp/regress-169497.js ecma_3/RegExp/regress-169534.js ecma_3/RegExp/regress-187133.js ecma_3/RegExp/regress-191479.js ecma_3/RegExp/regress-202564.js ecma_3/RegExp/regress-209067.js ecma_3/RegExp/regress-209919.js ecma_3/RegExp/regress-216591.js ecma_3/RegExp/regress-220367-001.js ecma_3/RegExp/regress-223273.js ecma_3/RegExp/regress-223535.js ecma_3/RegExp/regress-224676.js ecma_3/RegExp/regress-225289.js ecma_3/RegExp/regress-225343.js ecma_3/RegExp/regress-24712.js ecma_3/RegExp/regress-285219.js ecma_3/RegExp/regress-28686.js ecma_3/RegExp/regress-289669.js ecma_3/RegExp/regress-309840.js ecma_3/RegExp/regress-312351.js ecma_3/RegExp/regress-31316.js ecma_3/RegExp/regress-334158.js ecma_3/RegExp/regress-346090.js ecma_3/RegExp/regress-367888.js: ecma_3/RegExp/regress-375642.js: ecma_3/RegExp/regress-375651.js ecma_3/RegExp/regress-375715-02.js ecma_3/RegExp/regress-375715-03.js ecma_3/RegExp/regress-465862.js ecma_3/RegExp/regress-57572.js ecma_3/RegExp/regress-57631.js ecma_3/RegExp/regress-67773.js ecma_3/RegExp/regress-72964.js ecma_3/RegExp/regress-76683.js ecma_3/RegExp/regress-78156.js ecma_3/RegExp/regress-85721.js ecma_3/RegExp/regress-87231.js ecma_3/RegExp/regress-98306.js ecma_3/Regress/regress-385393-04.js ecma_3/Regress/regress-419152.js ecma_3/Regress/regress-420087.js ecma_3/Regress/regress-420610.js ecma_3/Regress/regress-441477-01.js ecma_3/Regress/regress-469937.js ecma_3/Statements/12.10-01.js ecma_3/Statements/12.6.3.js ecma_3/Statements/regress-131348.js ecma_3/Statements/regress-157509.js ecma_3/Statements/regress-194364.js ecma_3/Statements/regress-226517.js ecma_3/Statements/regress-444979.js ecma_3/Statements/regress-74474-001.js ecma_3/Statements/regress-83532-001.js ecma_3/Statements/regress-83532-002.js ecma_3/Statements/switch-001.js ecma_3/String/regress-104375.js ecma_3/String/regress-189898.js ecma_3/String/regress-304376.js ecma_3/String/regress-313567.js ecma_3/String/regress-392378.js ecma_3/String/regress-83293.js ecma_3/Unicode/regress-352044-02-n.js ecma_3/Unicode/uc-001-n.js ecma_3/Unicode/uc-002-n.js ecma_3/Unicode/uc-002.js ecma_3/Unicode/uc-003.js ecma_3/Unicode/uc-004.js ecma_3/Unicode/uc-005.js ecma_3/extensions/10.1.3-2.js ecma_3/extensions/regress-103087.js ecma_3/extensions/regress-188206-01.js ecma_3/extensions/regress-188206-02.js ecma_3/extensions/regress-220367-002.js ecma_3/extensions/regress-228087.js ecma_3/extensions/regress-320854.js ecma_3/extensions/regress-327170.js ecma_3/extensions/regress-385393-03.js ecma_3/extensions/regress-429248.js js-test-driver-begin.js js-test-driver-end.js js1_1/jsref.js js1_2/Array/array_split_1.js js1_2/Array/general1.js js1_2/Array/general2.js js1_2/Array/slice.js js1_2/Array/splice1.js js1_2/Array/splice2.js js1_2/Array/tostring_1.js js1_2/Array/tostring_2.js js1_2/Objects/toString-001.js js1_2/String/charCodeAt.js js1_2/String/concat.js js1_2/String/match.js js1_2/String/slice.js js1_2/function/Number.js js1_2/function/String.js js1_2/function/definition-1.js js1_2/function/length.js js1_2/function/nesting-1.js js1_2/function/nesting.js js1_2/function/regexparg-1.js js1_2/jsref.js js1_2/operator/strictEquality.js js1_2/regexp/RegExp_dollar_number.js js1_2/regexp/RegExp_input.js js1_2/regexp/RegExp_input_as_array.js js1_2/regexp/RegExp_lastIndex.js js1_2/regexp/RegExp_lastMatch.js js1_2/regexp/RegExp_lastMatch_as_array.js js1_2/regexp/RegExp_lastParen.js js1_2/regexp/RegExp_lastParen_as_array.js js1_2/regexp/RegExp_leftContext.js js1_2/regexp/RegExp_leftContext_as_array.js js1_2/regexp/RegExp_multiline.js js1_2/regexp/RegExp_multiline_as_array.js js1_2/regexp/RegExp_object.js js1_2/regexp/RegExp_rightContext.js js1_2/regexp/RegExp_rightContext_as_array.js js1_2/regexp/alphanumeric.js js1_2/regexp/asterisk.js js1_2/regexp/backslash.js js1_2/regexp/backspace.js js1_2/regexp/beginLine.js js1_2/regexp/character_class.js js1_2/regexp/compile.js js1_2/regexp/control_characters.js js1_2/regexp/digit.js js1_2/regexp/dot.js js1_2/regexp/endLine.js js1_2/regexp/everything.js js1_2/regexp/exec.js js1_2/regexp/flags.js js1_2/regexp/global.js js1_2/regexp/hexadecimal.js js1_2/regexp/ignoreCase.js js1_2/regexp/interval.js js1_2/regexp/octal.js js1_2/regexp/parentheses.js js1_2/regexp/plus.js js1_2/regexp/question_mark.js js1_2/regexp/regress-6359.js js1_2/regexp/regress-9141.js js1_2/regexp/simple_form.js js1_2/regexp/source.js js1_2/regexp/special_characters.js js1_2/regexp/string_replace.js js1_2/regexp/string_search.js js1_2/regexp/string_split.js js1_2/regexp/test.js js1_2/regexp/toString.js js1_2/regexp/vertical_bar.js js1_2/regexp/whitespace.js js1_2/regexp/word_boundary.js js1_2/regress/regress-144834.js js1_2/regress/regress-7703.js js1_2/statements/break.js js1_2/statements/continue.js js1_2/statements/do_while.js js1_2/statements/switch.js js1_2/statements/switch2.js js1_2/version120/boolean-001.js js1_3/Boolean/boolean-001.js js1_3/Script/delete-001.js js1_3/Script/function-002.js js1_3/Script/in-001.js js1_3/Script/new-001.js js1_3/Script/switch-001.js js1_3/extensions/proto_10.js js1_3/extensions/proto_2.js js1_3/extensions/proto_5.js js1_3/extensions/script-001.js js1_3/inherit/proto_1.js js1_3/inherit/proto_10.js js1_3/inherit/proto_11.js js1_3/inherit/proto_12.js js1_3/inherit/proto_3.js js1_3/inherit/proto_4.js js1_3/inherit/proto_6.js js1_3/inherit/proto_7.js js1_3/inherit/proto_8.js js1_3/inherit/proto_9.js js1_3/jsref.js js1_3/regress/delete-001.js js1_3/regress/function-002.js js1_3/regress/in-001.js js1_3/regress/new-001.js js1_3/regress/switch-001.js js1_4/Eval/eval-001.js js1_4/Eval/eval-002.js js1_4/Eval/eval-003.js js1_4/Functions/function-001.js js1_4/Regress/date-001-n.js js1_4/Regress/function-001.js js1_4/Regress/function-002.js js1_4/Regress/function-003.js js1_4/Regress/function-004-n.js js1_4/Regress/regress-7224.js js1_4/Regress/toString-001-n.js js1_4/jsref.js js1_5/Array/11.1.4.js js1_5/Array/array-001.js js1_5/Array/regress-101964.js js1_5/Array/regress-107138.js js1_5/Array/regress-108440.js js1_5/Array/regress-154338.js js1_5/Array/regress-178722.js js1_5/Array/regress-255555.js js1_5/Array/regress-299644.js js1_5/Array/regress-300858.js js1_5/Array/regress-310351.js js1_5/Array/regress-311515.js js1_5/Array/regress-313153.js js1_5/Array/regress-315509-01.js js1_5/Array/regress-345961.js js1_5/Array/regress-348810.js js1_5/Array/regress-350256-01.js js1_5/Array/regress-350256-02.js js1_5/Array/regress-360681-01.js js1_5/Array/regress-360681-02.js js1_5/Array/regress-364104.js js1_5/Array/regress-422286.js js1_5/Array/regress-424954.js js1_5/Array/regress-451483.js js1_5/Array/regress-451906.js js1_5/Array/regress-456845.js js1_5/Array/regress-465980-01.js js1_5/Array/regress-474529.js js1_5/Array/regress-94257.js js1_5/Date/regress-188211.js js1_5/Date/regress-301738-01.js js1_5/Date/regress-309925-01.js js1_5/Date/regress-346027.js js1_5/Error/regress-354246.js js1_5/Error/regress-412324.js js1_5/Error/regress-465377.js js1_5/Exceptions/catchguard-002-n.js js1_5/Exceptions/catchguard-003-n.js js1_5/Exceptions/regress-123002.js js1_5/Exceptions/regress-232182.js js1_5/Exceptions/regress-273931.js js1_5/Exceptions/regress-347674.js js1_5/Exceptions/regress-350837.js js1_5/Expressions/regress-192288.js js1_5/Expressions/regress-96526-argsub.js js1_5/Expressions/regress-96526-noargsub.js js1_5/Function/10.1.6.js js1_5/Function/15.3.4.4.js js1_5/Function/regress-123371.js js1_5/Function/regress-178389.js js1_5/Function/regress-292215.js js1_5/Function/regress-344052.js js1_5/Function/regress-364023.js js1_5/GC/regress-104584.js js1_5/GC/regress-203278-3.js js1_5/GC/regress-278725.js js1_5/GC/regress-306788.js js1_5/GC/regress-311497.js js1_5/GC/regress-313276.js js1_5/GC/regress-313479.js js1_5/GC/regress-316885-02.js js1_5/GC/regress-316885-03.js js1_5/GC/regress-319980-01.js js1_5/GC/regress-331719.js js1_5/GC/regress-341877-01.js js1_5/GC/regress-341877-02.js js1_5/GC/regress-352606.js js1_5/GC/regress-383269-01.js js1_5/GC/regress-383269-02.js js1_5/GC/regress-390078.js js1_5/GC/regress-418128.js js1_5/GC/regress-440558.js js1_5/GetSet/regress-375976.js js1_5/LexicalConventions/lexical-001.js js1_5/LexicalConventions/regress-177314.js js1_5/LexicalConventions/regress-469940.js js1_5/Object/regress-137000.js js1_5/Object/regress-192105.js js1_5/Object/regress-338709.js js1_5/Object/regress-382503.js js1_5/Object/regress-382532.js js1_5/Object/regress-465476.js js1_5/Object/regress-90596-003.js js1_5/Regress/regress-102725.js js1_5/Regress/regress-10278.js js1_5/Regress/regress-104077.js js1_5/Regress/regress-110286.js js1_5/Regress/regress-114491.js js1_5/Regress/regress-114493.js js1_5/Regress/regress-115436.js js1_5/Regress/regress-116228.js js1_5/Regress/regress-118849.js js1_5/Regress/regress-127243.js js1_5/Regress/regress-127557.js js1_5/Regress/regress-131510-001.js js1_5/Regress/regress-139316.js js1_5/Regress/regress-140852.js js1_5/Regress/regress-140974.js js1_5/Regress/regress-146596.js js1_5/Regress/regress-152646.js js1_5/Regress/regress-156354.js js1_5/Regress/regress-159334.js js1_5/Regress/regress-162392.js js1_5/Regress/regress-165201.js js1_5/Regress/regress-167658.js js1_5/Regress/regress-168347.js js1_5/Regress/regress-170193.js js1_5/Regress/regress-174709.js js1_5/Regress/regress-176125.js js1_5/Regress/regress-179524.js js1_5/Regress/regress-185165.js js1_5/Regress/regress-191633.js js1_5/Regress/regress-191668.js js1_5/Regress/regress-192414.js js1_5/Regress/regress-193418.js js1_5/Regress/regress-203402.js js1_5/Regress/regress-203841.js js1_5/Regress/regress-204210.js js1_5/Regress/regress-210682.js js1_5/Regress/regress-211590.js js1_5/Regress/regress-214761.js js1_5/Regress/regress-216320.js js1_5/Regress/regress-224956.js js1_5/Regress/regress-229006.js js1_5/Regress/regress-230216-1.js js1_5/Regress/regress-230216-2.js js1_5/Regress/regress-230216-3.js js1_5/Regress/regress-233483-2.js js1_5/Regress/regress-233483.js js1_5/Regress/regress-238881.js js1_5/Regress/regress-238945.js js1_5/Regress/regress-243174.js js1_5/Regress/regress-243389-n.js js1_5/Regress/regress-243869.js js1_5/Regress/regress-244619.js js1_5/Regress/regress-245113.js js1_5/Regress/regress-245308.js js1_5/Regress/regress-246911.js js1_5/Regress/regress-246964.js js1_5/Regress/regress-247179.js js1_5/Regress/regress-248444.js js1_5/Regress/regress-253150.js js1_5/Regress/regress-254296.js js1_5/Regress/regress-254974.js js1_5/Regress/regress-256501.js js1_5/Regress/regress-256617.js js1_5/Regress/regress-256798.js js1_5/Regress/regress-259935.js js1_5/Regress/regress-260541.js js1_5/Regress/regress-261886.js js1_5/Regress/regress-261887.js js1_5/Regress/regress-274035.js js1_5/Regress/regress-274888.js js1_5/Regress/regress-275378.js js1_5/Regress/regress-276103.js js1_5/Regress/regress-278873.js js1_5/Regress/regress-280769-3.js js1_5/Regress/regress-280769-4.js js1_5/Regress/regress-281487.js js1_5/Regress/regress-281930.js js1_5/Regress/regress-283477.js js1_5/Regress/regress-286216.js js1_5/Regress/regress-288688.js js1_5/Regress/regress-289094.js js1_5/Regress/regress-290656.js js1_5/Regress/regress-294191.js js1_5/Regress/regress-294195-01.js js1_5/Regress/regress-294195-02.js js1_5/Regress/regress-294302.js js1_5/Regress/regress-295052.js js1_5/Regress/regress-295666.js js1_5/Regress/regress-299209.js js1_5/Regress/regress-299641.js js1_5/Regress/regress-306633.js js1_5/Regress/regress-306727.js js1_5/Regress/regress-306794.js js1_5/Regress/regress-308566.js js1_5/Regress/regress-310295.js js1_5/Regress/regress-310607.js js1_5/Regress/regress-310993.js js1_5/Regress/regress-311071.js js1_5/Regress/regress-311629.js js1_5/Regress/regress-312260.js js1_5/Regress/regress-31255.js js1_5/Regress/regress-314401.js js1_5/Regress/regress-315990.js js1_5/Regress/regress-317476.js js1_5/Regress/regress-317714-01.js js1_5/Regress/regress-317714-02.js js1_5/Regress/regress-319384.js js1_5/Regress/regress-319391.js js1_5/Regress/regress-320032.js js1_5/Regress/regress-321757.js js1_5/Regress/regress-321874.js js1_5/Regress/regress-321971.js js1_5/Regress/regress-322430.js js1_5/Regress/regress-325925.js js1_5/Regress/regress-326453.js js1_5/Regress/regress-326467.js js1_5/Regress/regress-328012.js js1_5/Regress/regress-328897.js js1_5/Regress/regress-329383.js js1_5/Regress/regress-330951.js js1_5/Regress/regress-334807-01.js js1_5/Regress/regress-334807-02.js js1_5/Regress/regress-334807-03.js js1_5/Regress/regress-334807-04.js js1_5/Regress/regress-334807-05.js js1_5/Regress/regress-334807-06.js js1_5/Regress/regress-338307.js js1_5/Regress/regress-340369.js js1_5/Regress/regress-341360.js js1_5/Regress/regress-343713.js js1_5/Regress/regress-343966.js js1_5/Regress/regress-344711-n.js js1_5/Regress/regress-344804.js js1_5/Regress/regress-344959.js js1_5/Regress/regress-346237.js js1_5/Regress/regress-346801.js js1_5/Regress/regress-349482-01.js js1_5/Regress/regress-349482-02.js js1_5/Regress/regress-349592.js js1_5/Regress/regress-350253.js js1_5/Regress/regress-350312.js js1_5/Regress/regress-350415.js js1_5/Regress/regress-350529.js js1_5/Regress/regress-351116.js js1_5/Regress/regress-351515.js js1_5/Regress/regress-352009.js js1_5/Regress/regress-352208.js js1_5/Regress/regress-355829-01.js js1_5/Regress/regress-355829-02.js js1_5/Regress/regress-355829-03.js js1_5/Regress/regress-356250.js js1_5/Regress/regress-360969-01.js js1_5/Regress/regress-360969-02.js js1_5/Regress/regress-360969-03.js js1_5/Regress/regress-360969-04.js js1_5/Regress/regress-366122.js js1_5/Regress/regress-366468.js js1_5/Regress/regress-366601.js js1_5/Regress/regress-367561-01.js js1_5/Regress/regress-379245.js js1_5/Regress/regress-383674.js js1_5/Regress/regress-387951-01.js js1_5/Regress/regress-387951-02.js js1_5/Regress/regress-387951-03.js js1_5/Regress/regress-39309.js js1_5/Regress/regress-396684.js js1_5/Regress/regress-398609.js js1_5/Regress/regress-406769.js js1_5/Regress/regress-407024.js js1_5/Regress/regress-407323.js js1_5/Regress/regress-407957.js js1_5/Regress/regress-416737-01.js js1_5/Regress/regress-416737-02.js js1_5/Regress/regress-417893.js js1_5/Regress/regress-418504.js js1_5/Regress/regress-418540.js js1_5/Regress/regress-419018.js js1_5/Regress/regress-419803.js js1_5/Regress/regress-424311.js js1_5/Regress/regress-425360.js js1_5/Regress/regress-428366.js js1_5/Regress/regress-438415-01.js js1_5/Regress/regress-438415-02.js js1_5/Regress/regress-449627.js js1_5/Regress/regress-449666.js js1_5/Regress/regress-450369.js js1_5/Regress/regress-450833.js js1_5/Regress/regress-451884.js js1_5/Regress/regress-451946.js js1_5/Regress/regress-452008.js js1_5/Regress/regress-452170.js js1_5/Regress/regress-452333.js js1_5/Regress/regress-452336.js js1_5/Regress/regress-452346.js js1_5/Regress/regress-452495.js js1_5/Regress/regress-452573-01.js js1_5/Regress/regress-452573-02.js js1_5/Regress/regress-452713.js js1_5/Regress/regress-452724-01.js js1_5/Regress/regress-452724-02.js js1_5/Regress/regress-452742-01.js js1_5/Regress/regress-452742-02.js js1_5/Regress/regress-452853.js js1_5/Regress/regress-452884-01.js js1_5/Regress/regress-452884-02.js js1_5/Regress/regress-453024.js js1_5/Regress/regress-453173.js js1_5/Regress/regress-453397.js js1_5/Regress/regress-453701.js js1_5/Regress/regress-453747.js js1_5/Regress/regress-454682.js js1_5/Regress/regress-454981.js js1_5/Regress/regress-455605.js js1_5/Regress/regress-455748.js js1_5/Regress/regress-455758-01.js js1_5/Regress/regress-455758-02.js js1_5/Regress/regress-455775.js js1_5/Regress/regress-456470.js js1_5/Regress/regress-456477-01.js js1_5/Regress/regress-456477-02.js js1_5/Regress/regress-456494.js js1_5/Regress/regress-456540-01.js js1_5/Regress/regress-456540-02.js js1_5/Regress/regress-457065-03.js js1_5/Regress/regress-457456.js js1_5/Regress/regress-457778.js js1_5/Regress/regress-458851.js js1_5/Regress/regress-459085.js js1_5/Regress/regress-459628.js js1_5/Regress/regress-459990.js js1_5/Regress/regress-460024.js js1_5/Regress/regress-460117.js js1_5/Regress/regress-460886-01.js js1_5/Regress/regress-460886-02.js js1_5/Regress/regress-461307.js js1_5/Regress/regress-461723.js js1_5/Regress/regress-462292.js js1_5/Regress/regress-462879.js js1_5/Regress/regress-462989.js js1_5/Regress/regress-463259.js js1_5/Regress/regress-463782.js js1_5/Regress/regress-464334.js js1_5/Regress/regress-464862.js js1_5/Regress/regress-465013.js js1_5/Regress/regress-465132.js js1_5/Regress/regress-465133.js js1_5/Regress/regress-465135.js js1_5/Regress/regress-465136.js js1_5/Regress/regress-465137.js js1_5/Regress/regress-465262.js js1_5/Regress/regress-465272.js js1_5/Regress/regress-465347.js js1_5/Regress/regress-465366.js js1_5/Regress/regress-466262.js js1_5/Regress/regress-466747.js js1_5/Regress/regress-469044.js js1_5/Regress/regress-470061.js js1_5/Regress/regress-470187-01.js js1_5/Regress/regress-470187-02.js js1_5/Regress/regress-470758-01.js js1_5/Regress/regress-470758-02.js js1_5/Regress/regress-472533.js js1_5/Regress/regress-475645-01.js js1_5/Regress/regress-475645-02.js js1_5/Regress/regress-476049.js js1_5/Regress/regress-476192.js js1_5/Regress/regress-477733.js js1_5/Regress/regress-477758.js js1_5/Regress/regress-478314.js js1_5/Regress/regress-479353.js js1_5/Regress/regress-480147.js js1_5/Regress/regress-480244.js js1_5/Regress/regress-481436.js js1_5/Regress/regress-482421.js js1_5/Regress/regress-482783.js js1_5/Regress/regress-483103.js js1_5/Regress/regress-501124.js js1_5/Regress/regress-504078.js js1_5/Regress/regress-57043.js js1_5/Regress/regress-58116.js js1_5/Regress/regress-68498-001.js js1_5/Regress/regress-68498-002.js js1_5/Regress/regress-68498-004.js js1_5/Regress/regress-69607.js js1_5/Regress/regress-71107.js js1_5/Regress/regress-76054.js js1_5/Regress/regress-82306.js js1_5/Regress/regress-89474.js js1_5/Regress/regress-90445.js js1_5/Regress/regress-96526-001.js js1_5/Scope/regress-154693.js js1_5/Scope/regress-184107.js js1_5/Scope/regress-185485.js js1_5/Scope/regress-191276.js js1_5/Scope/regress-192226.js js1_5/Scope/regress-202678-001.js js1_5/Scope/regress-202678-002.js js1_5/Scope/regress-208496-001.js js1_5/Scope/regress-208496-002.js js1_5/Scope/regress-220362.js js1_5/Scope/regress-446026-02.js js1_5/Scope/regress-77578-001.js js1_5/Scope/scope-002.js js1_5/Scope/scope-003.js js1_5/Scope/scope-004.js js1_5/String/regress-107771.js js1_5/String/regress-112626.js js1_5/String/regress-179068.js js1_5/decompilation/regress-344120.js js1_5/decompilation/regress-349489.js js1_5/decompilation/regress-349663.js js1_5/decompilation/regress-350670.js js1_5/decompilation/regress-351336.js js1_5/decompilation/regress-351625.js js1_5/decompilation/regress-351626.js js1_5/decompilation/regress-352022.js js1_5/decompilation/regress-352360.js js1_5/decompilation/regress-352873-01.js js1_5/decompilation/regress-352873-02.js js1_5/decompilation/regress-353120.js js1_5/decompilation/regress-354878.js js1_5/decompilation/regress-354910.js js1_5/decompilation/regress-371692.js js1_5/decompilation/regress-373678.js js1_5/decompilation/regress-375639.js js1_5/decompilation/regress-383721.js js1_5/decompilation/regress-406555.js js1_5/decompilation/regress-437288-02.js js1_5/decompilation/regress-443071-01.js js1_5/decompilation/regress-456964-01.js js1_5/decompilation/regress-457093-01.js js1_5/decompilation/regress-460116-01.js js1_5/decompilation/regress-460116-02.js js1_5/decompilation/regress-460116-03.js js1_5/decompilation/regress-461108.js js1_5/decompilation/regress-461110.js js1_5/decompilation/regress-461111.js js1_5/extensions/catchguard-001-n.js js1_5/extensions/catchguard-001.js js1_5/extensions/catchguard-002.js js1_5/extensions/catchguard-003.js js1_5/extensions/getset-004.js js1_5/extensions/getset-005.js js1_5/extensions/getset-006.js js1_5/extensions/no-such-method.js js1_5/extensions/regress-104077.js js1_5/extensions/regress-178722.js js1_5/extensions/regress-220584.js js1_5/extensions/regress-225831.js js1_5/extensions/regress-226078.js js1_5/extensions/regress-231518.js js1_5/extensions/regress-237461.js js1_5/extensions/regress-245148.js js1_5/extensions/regress-245795.js js1_5/extensions/regress-255245.js js1_5/extensions/regress-291213.js js1_5/extensions/regress-300079.js js1_5/extensions/regress-304897.js js1_5/extensions/regress-311161.js js1_5/extensions/regress-311583.js js1_5/extensions/regress-311792-01.js js1_5/extensions/regress-311792-02.js js1_5/extensions/regress-312278.js js1_5/extensions/regress-313500.js js1_5/extensions/regress-313630.js js1_5/extensions/regress-313763.js js1_5/extensions/regress-313803.js js1_5/extensions/regress-313938.js js1_5/extensions/regress-314874.js js1_5/extensions/regress-315509-02.js js1_5/extensions/regress-319683.js js1_5/extensions/regress-322957.js js1_5/extensions/regress-325269.js js1_5/extensions/regress-327608.js js1_5/extensions/regress-328443.js js1_5/extensions/regress-328556.js js1_5/extensions/regress-338804-01.js js1_5/extensions/regress-338804-03.js js1_5/extensions/regress-339685.js js1_5/extensions/regress-340199.js js1_5/extensions/regress-341956-01.js js1_5/extensions/regress-341956-02.js js1_5/extensions/regress-341956-03.js js1_5/extensions/regress-346494-01.js js1_5/extensions/regress-350312-01.js js1_5/extensions/regress-350312-02.js js1_5/extensions/regress-350312-03.js js1_5/extensions/regress-351102-01.js js1_5/extensions/regress-351102-02.js js1_5/extensions/regress-351102-06.js js1_5/extensions/regress-351973.js js1_5/extensions/regress-352261.js js1_5/extensions/regress-352281.js js1_5/extensions/regress-352291.js js1_5/extensions/regress-354297.js js1_5/extensions/regress-354541-01.js js1_5/extensions/regress-354541-02.js js1_5/extensions/regress-354541-03.js js1_5/extensions/regress-354541-04.js js1_5/extensions/regress-355982.js js1_5/extensions/regress-356402.js js1_5/extensions/regress-363258.js js1_5/extensions/regress-363988.js js1_5/extensions/regress-365527.js js1_5/extensions/regress-365692.js js1_5/extensions/regress-366288.js js1_5/extensions/regress-366292.js js1_5/extensions/regress-366396.js js1_5/extensions/regress-367118-01.js js1_5/extensions/regress-367118-02.js js1_5/extensions/regress-367120-01.js js1_5/extensions/regress-367120-02.js js1_5/extensions/regress-367121.js js1_5/extensions/regress-367501-01.js js1_5/extensions/regress-367501-02.js js1_5/extensions/regress-367501-03.js js1_5/extensions/regress-367501-04.js js1_5/extensions/regress-367589.js js1_5/extensions/regress-369404.js js1_5/extensions/regress-369696-01.js js1_5/extensions/regress-369696-03.js js1_5/extensions/regress-372309.js js1_5/extensions/regress-374589.js js1_5/extensions/regress-375183.js js1_5/extensions/regress-375344.js js1_5/extensions/regress-380889.js js1_5/extensions/regress-385134.js js1_5/extensions/regress-385393-02.js js1_5/extensions/regress-390597.js js1_5/extensions/regress-390598.js js1_5/extensions/regress-394967.js js1_5/extensions/regress-396326.js js1_5/extensions/regress-406572.js js1_5/extensions/regress-407019.js js1_5/extensions/regress-407501.js js1_5/extensions/regress-407720.js js1_5/extensions/regress-412926.js js1_5/extensions/regress-414755.js js1_5/extensions/regress-416354.js js1_5/extensions/regress-416460.js js1_5/extensions/regress-416834.js js1_5/extensions/regress-422137.js js1_5/extensions/regress-422592.js js1_5/extensions/regress-426711.js js1_5/extensions/regress-427196-01.js js1_5/extensions/regress-427196-02.js js1_5/extensions/regress-427196-03.js js1_5/extensions/regress-429264.js js1_5/extensions/regress-431428.js js1_5/extensions/regress-434837-01.js js1_5/extensions/regress-435497-01.js js1_5/extensions/regress-435497-02.js js1_5/extensions/regress-435497-03.js js1_5/extensions/regress-436741.js js1_5/extensions/regress-437288-01.js js1_5/extensions/regress-44009.js js1_5/extensions/regress-443569.js js1_5/extensions/regress-446386.js js1_5/extensions/regress-452168.js js1_5/extensions/regress-452329.js js1_5/extensions/regress-452338.js js1_5/extensions/regress-452372.js js1_5/extensions/regress-452565.js js1_5/extensions/regress-453249.js js1_5/extensions/regress-454040.js js1_5/extensions/regress-454704.js js1_5/extensions/regress-455380.js js1_5/extensions/regress-455408.js js1_5/extensions/regress-459606.js js1_5/extensions/regress-465276.js js1_5/extensions/regress-469625.js js1_5/extensions/regress-469761.js js1_5/extensions/regress-472599.js js1_5/extensions/regress-476447.js js1_5/extensions/regress-479487.js js1_5/extensions/regress-479551.js js1_5/extensions/regress-480579.js js1_5/extensions/regress-481516.js js1_5/extensions/regress-488995.js js1_5/extensions/regress-90596-001.js js1_5/extensions/regress-90596-002.js js1_5/extensions/regress-96284-001.js js1_5/extensions/regress-96284-002.js js1_5/extensions/scope-001.js js1_6/Array/filter.js js1_6/Array/regress-290592.js js1_6/Array/regress-304828.js js1_6/Array/regress-305002.js js1_6/Array/regress-310425-01.js js1_6/Array/regress-310425-02.js js1_6/Array/regress-320887.js js1_6/Array/regress-352742-01.js js1_6/Array/regress-352742-02.js js1_6/Array/regress-386030.js js1_6/Array/regress-415451.js js1_6/Array/regress-415540.js js1_6/Regress/regress-301574.js js1_6/Regress/regress-311157-01.js js1_6/Regress/regress-311157-02.js js1_6/Regress/regress-314887.js js1_6/Regress/regress-320172.js js1_6/Regress/regress-351795.js js1_6/Regress/regress-352271.js js1_6/Regress/regress-353078.js js1_6/Regress/regress-372565.js js1_6/Regress/regress-378492.js js1_6/Regress/regress-475469.js js1_6/Regress/regress-476655.js js1_6/decompilation/regress-352084.js js1_6/extensions/regress-385393-08.js js1_6/extensions/regress-455464-01.js js1_6/extensions/regress-455464-02.js js1_6/extensions/regress-455464-03.js js1_6/extensions/regress-455464-04.js js1_6/extensions/regress-457521.js js1_6/extensions/regress-472508.js js1_6/extensions/regress-475144.js js1_6/extensions/regress-479567.js js1_7/GC/regress-341675.js js1_7/block/order-of-operation.js js1_7/block/regress-341939.js js1_7/block/regress-343765.js js1_7/block/regress-344139.js js1_7/block/regress-344262.js js1_7/block/regress-344370.js js1_7/block/regress-345542.js js1_7/block/regress-348685.js js1_7/block/regress-349283.js js1_7/block/regress-349298.js js1_7/block/regress-349507.js js1_7/block/regress-349653.js js1_7/block/regress-349962.js js1_7/block/regress-350279.js js1_7/block/regress-350730.js js1_7/block/regress-350793-01.js js1_7/block/regress-351497.js js1_7/block/regress-351606.js js1_7/block/regress-352092.js js1_7/block/regress-352185.js js1_7/block/regress-352212.js js1_7/block/regress-352267.js js1_7/block/regress-352616.js js1_7/block/regress-352624.js js1_7/block/regress-352907.js js1_7/block/regress-357754.js js1_7/block/regress-358508.js js1_7/block/regress-376410.js js1_7/block/regress-396900.js js1_7/block/regress-411279.js js1_7/decompilation/regress-349633.js js1_7/decompilation/regress-350810.js js1_7/decompilation/regress-352015.js js1_7/decompilation/regress-352025.js js1_7/decompilation/regress-352198.js js1_7/decompilation/regress-352269.js js1_7/decompilation/regress-352272.js js1_7/decompilation/regress-352283.js js1_7/decompilation/regress-352732.js js1_7/decompilation/regress-355635.js js1_7/decompilation/regress-355786.js js1_7/decompilation/regress-375794.js js1_7/decompilation/regress-380506.js js1_7/decompilation/regress-410649.js js1_7/expressions/destructuring-scope.js js1_7/expressions/regress-346203.js js1_7/expressions/regress-346645-01.js js1_7/expressions/regress-346645-02.js js1_7/expressions/regress-346645-03.js js1_7/expressions/regress-349624.js js1_7/expressions/regress-349818.js js1_7/expressions/regress-418051.js js1_7/expressions/regress-421806.js js1_7/expressions/regress-451340.js js1_7/extensions/basic-Iterator.js js1_7/extensions/basic-for-each.js js1_7/extensions/basic-for-in.js js1_7/extensions/destructuring-order.js js1_7/extensions/iterator-ctor.js js1_7/extensions/regress-346021.js js1_7/extensions/regress-346642-02.js js1_7/extensions/regress-346773.js js1_7/extensions/regress-349619.js js1_7/extensions/regress-350312.js js1_7/extensions/regress-351070-02.js js1_7/extensions/regress-352797-01.js js1_7/extensions/regress-352885-01.js js1_7/extensions/regress-352885-02.js js1_7/extensions/regress-353214-02.js js1_7/extensions/regress-354499-01.js js1_7/extensions/regress-354499-02.js js1_7/extensions/regress-354945-01.js js1_7/extensions/regress-355052-01.js js1_7/extensions/regress-355052-02.js js1_7/extensions/regress-355052-03.js js1_7/extensions/regress-355410.js js1_7/extensions/regress-355512.js js1_7/extensions/regress-355578.js js1_7/extensions/regress-355583.js js1_7/extensions/regress-363040-01.js js1_7/extensions/regress-363040-02.js js1_7/extensions/regress-366668-01.js js1_7/extensions/regress-366668-02.js js1_7/extensions/regress-372364.js js1_7/extensions/regress-387955-01.js js1_7/extensions/regress-387955-02.js js1_7/extensions/regress-392308.js js1_7/extensions/regress-396326.js js1_7/extensions/regress-429266.js js1_7/extensions/regress-455982-01.js js1_7/extensions/regress-455982-02.js js1_7/extensions/regress-469234.js js1_7/extensions/regress-469405-01.js js1_7/extensions/regress-469405-02.js js1_7/extensions/regress-470176.js js1_7/extensions/regress-470300-01.js js1_7/extensions/regress-470300-02.js js1_7/extensions/regress-474771-01.js js1_7/extensions/regress-474771-02.js js1_7/extensions/regress-477048.js js1_7/geniter/326466-01.js js1_7/geniter/builtin-Iterator-function.js js1_7/geniter/evens.js js1_7/geniter/fibonacci-matrix-generator.js js1_7/geniter/iterator-toString.js js1_7/geniter/message-value-passing.js js1_7/geniter/multiple-close.js js1_7/geniter/nested-yield.js js1_7/geniter/pi-generator.js js1_7/geniter/regress-345736.js js1_7/geniter/regress-345855.js js1_7/geniter/regress-345879-01.js js1_7/geniter/regress-345879-02.js js1_7/geniter/regress-347593.js js1_7/geniter/regress-349012-02.js js1_7/geniter/regress-349012-03.js js1_7/geniter/regress-349012-04.js js1_7/geniter/regress-349012-05.js js1_7/geniter/regress-349023-01.js js1_7/geniter/regress-349023-02.js js1_7/geniter/regress-349023-03.js js1_7/geniter/regress-349362.js js1_7/geniter/regress-349851.js js1_7/geniter/regress-350621.js js1_7/geniter/regress-350809.js js1_7/geniter/regress-351120.js js1_7/geniter/regress-351514.js js1_7/geniter/regress-352197.js js1_7/geniter/regress-352605.js js1_7/geniter/regress-352876.js js1_7/geniter/regress-355834.js js1_7/geniter/regress-359062.js js1_7/geniter/regress-366941.js js1_7/geniter/regress-382335.js js1_7/geniter/regress-387871.js js1_7/geniter/regress-390918.js js1_7/geniter/regress-392310.js js1_7/geniter/regress-466206.js js1_7/geniter/send-no-rhs.js js1_7/geniter/sequential-yields.js js1_7/geniter/simple-fib.js js1_7/geniter/throw-after-close.js js1_7/geniter/throw-forever.js js1_7/geniter/unreachable-yield.js js1_7/geniter/yield-undefined.js js1_7/iterable/regress-340526-01.js js1_7/iterable/regress-341496.js js1_7/iterable/regress-341499.js js1_7/iterable/regress-341510.js js1_7/iterable/regress-341815.js js1_7/iterable/regress-341821.js js1_7/iterable/regress-354750-01.js js1_7/iterable/regress-355025.js js1_7/iterable/regress-355075-01.js js1_7/iterable/regress-355075-02.js js1_7/iterable/regress-355090.js js1_7/iterable/regress-412467.js js1_7/iterable/regress-415922.js js1_7/lexical/regress-346642-04.js js1_7/regress/regress-352640-01.js js1_7/regress/regress-352640-02.js js1_7/regress/regress-352640-03.js js1_7/regress/regress-352640-04.js js1_7/regress/regress-352797-02.js js1_7/regress/regress-352870-03.js js1_7/regress/regress-353079.js js1_7/regress/regress-355023.js js1_7/regress/regress-355832-01.js js1_7/regress/regress-355832-02.js js1_7/regress/regress-361566.js js1_7/regress/regress-363040-02.js js1_7/regress/regress-369666-01.js js1_7/regress/regress-369666-02.js js1_7/regress/regress-372331.js js1_7/regress/regress-373827-01.js js1_7/regress/regress-373827-02.js js1_7/regress/regress-373828.js js1_7/regress/regress-379442.js js1_7/regress/regress-385393-05.js js1_7/regress/regress-387951.js js1_7/regress/regress-407727-01.js js1_7/regress/regress-407727-02.js js1_7/regress/regress-407957.js js1_7/regress/regress-414553.js js1_7/regress/regress-416601.js js1_7/regress/regress-416705.js js1_7/regress/regress-418641.js js1_7/regress/regress-419803.js js1_7/regress/regress-428706.js js1_7/regress/regress-428708.js js1_7/regress/regress-452703.js js1_7/regress/regress-452960.js js1_7/regress/regress-453049.js js1_7/regress/regress-453051.js js1_7/regress/regress-453411.js js1_7/regress/regress-461235.js js1_7/regress/regress-461945.js js1_7/regress/regress-462071.js js1_7/regress/regress-462282.js js1_7/regress/regress-462388.js js1_7/regress/regress-462407.js js1_7/regress/regress-464403.js js1_7/regress/regress-465236.js js1_7/regress/regress-465424.js js1_7/regress/regress-465484.js js1_7/regress/regress-465686.js js1_7/regress/regress-469239-01.js js1_7/regress/regress-469239-02.js js1_7/regress/regress-470223.js js1_7/regress/regress-470388-01.js js1_7/regress/regress-470388-02.js js1_7/regress/regress-470388-03.js js1_7/regress/regress-474771.js js1_8/decompilation/regress-260106.js js1_8/decompilation/regress-381504.js js1_8/decompilation/regress-461233.js js1_8/decompilation/regress-469625-01.js js1_8/extensions/dekker.js js1_8/extensions/for-in.js js1_8/extensions/lamport.js js1_8/extensions/peterson.js js1_8/extensions/regress-378789.js js1_8/extensions/regress-385393-01.js js1_8/extensions/regress-385393-10.js js1_8/extensions/regress-385393-11.js js1_8/extensions/regress-415721.js js1_8/extensions/regress-417817.js js1_8/extensions/regress-419091.js js1_8/extensions/regress-422269.js js1_8/extensions/regress-445818.js js1_8/extensions/regress-446169-01.js js1_8/extensions/regress-446169-02.js js1_8/extensions/regress-452476.js js1_8/extensions/regress-452913.js js1_8/extensions/regress-454744.js js1_8/extensions/regress-455973.js js1_8/extensions/regress-465337.js js1_8/extensions/regress-471197.js js1_8/extensions/regress-475971.js js1_8/extensions/regress-476653.js js1_8/extensions/regress-476871-02.js js1_8/extensions/regress-479252.js js1_8/extensions/regress-479381.js js1_8/extensions/simple-tree.js js1_8/genexps/regress-347739.js js1_8/regress/regress-404734.js js1_8/regress/regress-427798.js js1_8/regress/regress-433279-01.js js1_8/regress/regress-433279-02.js js1_8/regress/regress-433279-03.js js1_8/regress/regress-452491.js js1_8/regress/regress-457065-01.js js1_8/regress/regress-457065-02.js js1_8/regress/regress-458076.js js1_8/regress/regress-459389.js js1_8/regress/regress-461930.js js1_8/regress/regress-461932.js js1_8/regress/regress-463334-01.js js1_8/regress/regress-463334-02.js js1_8/regress/regress-463783.js js1_8/regress/regress-464092-01.js js1_8/regress/regress-464092-02.js js1_8/regress/regress-464096.js js1_8/regress/regress-464418.js js1_8/regress/regress-464978.js js1_8/regress/regress-465220.js js1_8/regress/regress-465234.js js1_8/regress/regress-465239.js js1_8/regress/regress-465241.js js1_8/regress/regress-465249.js js1_8/regress/regress-465261.js js1_8/regress/regress-465308.js js1_8/regress/regress-465454.js js1_8/regress/regress-465460-02.js js1_8/regress/regress-465460-03.js js1_8/regress/regress-465460-04.js js1_8/regress/regress-465460-05.js js1_8/regress/regress-465460-06.js js1_8/regress/regress-465460-07.js js1_8/regress/regress-465460-08.js js1_8/regress/regress-465460-09.js js1_8/regress/regress-465460-10.js js1_8/regress/regress-465460-11.js js1_8/regress/regress-465460-12.js js1_8/regress/regress-465483.js js1_8/regress/regress-465567-01.js js1_8/regress/regress-465567-02.js js1_8/regress/regress-465688.js js1_8/regress/regress-466128.js js1_8/regress/regress-466787.js js1_8/regress/regress-467495-01.js js1_8/regress/regress-467495-02.js js1_8/regress/regress-467495-04.js js1_8/regress/regress-467495-06.js js1_8/regress/regress-468711.js js1_8/regress/regress-469547.js js1_8/regress/regress-469625-02.js js1_8/regress/regress-471660.js js1_8/regress/regress-472450-01.js js1_8/regress/regress-472450-02.js js1_8/regress/regress-472528-01.js js1_8/regress/regress-472528-02.js js1_8/regress/regress-472703.js js1_8/regress/regress-474769.js js1_8/regress/regress-474771.js js1_8/regress/regress-474935.js js1_8/regress/regress-476655.js js1_8/regress/regress-477581.js js1_8/regress/regress-478205.js js1_8/regress/regress-479353.js js1_8/regress/regress-479740.js js1_8/regress/regress-481800.js js1_8/regress/regress-483749.js js1_8_1/JSON/regress-458959.js js1_8_1/JSON/regress-459293.js js1_8_1/decompilation/regress-371802.js js1_8_1/extensions/regress-466905-04.js js1_8_1/extensions/regress-466905-05.js js1_8_1/extensions/regress-477158.js js1_8_1/extensions/regress-477187.js js1_8_1/regress/regress-452498-006.js js1_8_1/regress/regress-452498-027.js js1_8_1/regress/regress-452498-030.js js1_8_1/regress/regress-452498-038.js js1_8_1/regress/regress-452498-040.js js1_8_1/regress/regress-452498-050.js js1_8_1/regress/regress-452498-051.js js1_8_1/regress/regress-452498-052-a.js js1_8_1/regress/regress-452498-058.js js1_8_1/regress/regress-452498-062.js js1_8_1/regress/regress-452498-063.js js1_8_1/regress/regress-452498-071.js js1_8_1/regress/regress-452498-072.js js1_8_1/regress/regress-452498-073.js js1_8_1/regress/regress-452498-074.js js1_8_1/regress/regress-452498-075.js js1_8_1/regress/regress-452498-076.js js1_8_1/regress/regress-452498-077.js js1_8_1/regress/regress-452498-079.js js1_8_1/regress/regress-452498-101.js js1_8_1/regress/regress-452498-102.js js1_8_1/regress/regress-452498-103.js js1_8_1/regress/regress-452498-104.js js1_8_1/regress/regress-452498-108.js js1_8_1/regress/regress-452498-111.js js1_8_1/regress/regress-452498-114-a.js js1_8_1/regress/regress-452498-114.js js1_8_1/regress/regress-452498-116.js js1_8_1/regress/regress-452498-118.js js1_8_1/regress/regress-452498-121.js js1_8_1/regress/regress-452498-123.js js1_8_1/regress/regress-452498-129.js js1_8_1/regress/regress-452498-130.js js1_8_1/regress/regress-452498-131.js js1_8_1/regress/regress-452498-135-a.js js1_8_1/regress/regress-452498-155.js js1_8_1/regress/regress-452498-168-1.js js1_8_1/regress/regress-452498-176.js js1_8_1/regress/regress-452498-181.js js1_8_1/regress/regress-452498-184.js js1_8_1/regress/regress-452498-192.js js1_8_1/regress/regress-466905-01.js js1_8_1/regress/regress-466905-02.js js1_8_1/regress/regress-479430-01.js js1_8_1/regress/regress-479430-02.js js1_8_1/regress/regress-479430-03.js js1_8_1/regress/regress-479430-04.js js1_8_1/regress/regress-479430-05.js js1_8_1/regress/regress-495907.js js1_8_1/trace/math-trace-tests.js js1_8_1/trace/regress-458838.js js1_8_1/trace/regress-462459-01.js js1_8_1/trace/regress-462459-02.js js1_8_1/trace/regress-462459-03.js js1_8_1/trace/regress-462459-04.js js1_8_1/trace/regress-462459-05.js js1_8_1/trace/regress-462459-06.js js1_8_1/trace/regress-462459-07.js js1_8_1/trace/regress-462459-08.js js1_8_1/trace/regress-462459-09.js js1_8_1/trace/regress-462459-10.js js1_8_1/trace/regress-462459-11.js js1_8_1/trace/regress-462459-12.js js1_8_1/trace/regress-471635.js js1_8_1/trace/regress-489682.js lc2/Arrays/array-001.js lc2/Arrays/array-002.js lc2/Arrays/array-003.js lc2/Arrays/array-004.js lc2/Arrays/array-005.js lc2/Arrays/array-006-n.js lc2/Arrays/array-007-n.js lc2/Classes/class-001.js lc2/Classes/class-002.js lc2/JSToJava/character-001.js lc2/JSToJava/double-001.js lc2/JSToJava/double-002.js lc2/JSToJava/float-001.js lc2/JSToJava/float-002.js lc2/JSToJava/integer-001.js lc2/JSToJava/integer-002.js lc2/JSToJava/long-001.js lc2/JSToJava/long-002.js lc2/JSToJava/long-003-n.js lc2/JSToJava/short-001.js lc2/JSToJava/short-002.js lc2/JSToJava/short-003-n.js lc2/JavaToJS/String-001.js lc2/JavaToJS/boolean-001.js lc2/JavaToJS/boolean-003.js lc2/JavaToJS/boolean-004.js lc2/JavaToJS/boolean-005.js lc2/JavaToJS/char-001.js lc2/JavaToJS/char-002.js lc2/JavaToJS/enum-001.js lc2/JavaToJS/enum-002.js lc2/JavaToJS/null-001.js lc2/JavaToJS/number-001.js lc2/JavaToJS/number-002.js lc2/Methods/method-001.js lc2/Methods/method-002.js lc2/Methods/method-003.js lc2/Methods/method-004-n.js lc2/Methods/method-005.js lc2/Methods/method-006.js lc2/Methods/println-001.js lc2/Objects/object-001.js lc2/Objects/object-002.js lc2/Objects/object-003.js lc2/Objects/object-004.js lc2/Objects/object-005.js lc2/Objects/object-006.js lc2/Packages/package-001.js lc2/Packages/package-002.js lc2/Packages/package-003.js lc2/Packages/package-004.js lc2/Packages/package-005.js lc2/Packages/package-006.js lc2/Packages/package-007-n.js lc2/Packages/package-008-n.js lc2/misc/constructor.js lc2/misc/wrapUnwrap.js lc3/ArrayMethods/byte-001.js lc3/ArrayMethods/byte-002.js lc3/ConvertBoolean/boolean-005-n.js lc3/ConvertBoolean/boolean-006-n.js lc3/ConvertBoolean/boolean-007-n.js lc3/ConvertBoolean/boolean-008-n.js lc3/ConvertBoolean/boolean-009-n.js lc3/ConvertBoolean/boolean-010-n.js lc3/ConvertBoolean/boolean-011-n.js lc3/ConvertBoolean/boolean-012-n.js lc3/ConvertBoolean/boolean-013-n.js lc3/ConvertNull/null-002.js lc3/ConvertNull/null-003-n.js lc3/ConvertNull/null-004-n.js lc3/ConvertNull/null-006-n.js lc3/ConvertString/string-004-n.js lc3/ConvertString/string-005-n.js lc3/ConvertString/string-007-n.js lc3/ConvertUndefined/undefined-001-n.js lc3/JSBoolean/boolean-002-n.js lc3/JSBoolean/boolean-003-n.js lc3/JSBoolean/boolean-004-n.js lc3/JSBoolean/boolean-005-n.js lc3/JSBoolean/boolean-006-n.js lc3/JSBoolean/boolean-007-n.js lc3/JSBoolean/boolean-008-n.js lc3/JSNull/ToBoolean-001-n.js lc3/JSNull/ToFloat-001-n.js lc3/JSNull/ToLong-001-n.js lc3/JSNull/ToNumber-001-n.js lc3/JSNumber/ToByte-002-n.js lc3/JSNumber/ToByte-003-n.js lc3/JSNumber/ToByte-005-n.js lc3/JSNumber/ToChar-002-n.js lc3/JSNumber/ToChar-003-n.js lc3/JSNumber/ToChar-005-n.js lc3/JSNumber/ToChar-006-n.js lc3/JSNumber/ToInt-002-n.js lc3/JSNumber/ToInt-003-n.js lc3/JSNumber/ToInt-005-n.js lc3/JSNumber/ToLong-002-n.js lc3/JSNumber/ToLong-003-n.js lc3/JSNumber/ToLong-004-n.js lc3/JSNumber/ToLong-005-n.js lc3/JSNumber/ToLong-006-n.js lc3/JSNumber/ToLong-007-n.js lc3/JSNumber/ToLong-008-n.js lc3/JSNumber/ToLong-009-n.js lc3/JSNumber/ToLong-010-n.js lc3/JSNumber/ToLong-011-n.js lc3/JSNumber/ToShort-002-n.js lc3/JSNumber/ToShort-003-n.js lc3/JSNumber/ToShort-005-n.js lc3/JSObject/ToDouble-002-n.js lc3/JSObject/ToDouble-003-n.js lc3/JSObject/ToFloat-002-n.js lc3/JSObject/ToFloat-003-n.js lc3/JSUndefined/undefined-002-n.js lc3/JSUndefined/undefined-003-n.js lc3/JSUndefined/undefined-004-n.js lc3/JSUndefined/undefined-005-n.js lc3/JSUndefined/undefined-006-n.js lc3/JSUndefined/undefined-007-n.js lc3/JSUndefined/undefined-008-n.js lc3/JSUndefined/undefined-009-n.js lc3/JSUndefined/undefined-010-n.js lc3/JavaArray/ToArray-002-n.js lc3/JavaArray/ToBoolean-001-n.js lc3/JavaObject/JavaObjectToBoolean-001-n.js lc3/JavaObject/JavaObjectToBoolean-002-n.js lc3/JavaObject/JavaObjectToByte-002-n.js lc3/JavaObject/JavaObjectToByte-003-n.js lc3/JavaObject/JavaObjectToByte-004-n.js lc3/JavaObject/JavaObjectToByte-007-n.js lc3/JavaObject/JavaObjectToByte-008-n.js lc3/JavaObject/JavaObjectToChar-003-n.js lc3/JavaObject/JavaObjectToChar-005-n.js lc3/JavaObject/JavaObjectToChar-006-n.js lc3/JavaObject/JavaObjectToInt-002-n.js lc3/JavaObject/JavaObjectToInt-003-n.js lc3/JavaObject/JavaObjectToInt-004-n.js lc3/JavaObject/JavaObjectToLong-002-n.js lc3/JavaObject/JavaObjectToLong-003-n.js lc3/JavaObject/JavaObjectToLong-004-n.js lc3/JavaObject/JavaObjectToLong-006-n.js lc3/JavaObject/JavaObjectToShort-002-n.js lc3/JavaObject/JavaObjectToShort-003-n.js lc3/JavaObject/JavaObjectToShort-004-n.js lc3/StringMethods/string-001.js lc3/instanceof/instanceof-001.js bigo-test.js closure-compiler-20130227+dfsg1/rhino/testsrc/opt-1.tests0000644000175000017500000022275114433667662021244 0ustar apoapoe4x/Expressions/11.1.1.js e4x/Expressions/11.1.2.js e4x/Expressions/11.1.3.js e4x/Expressions/11.1.4-05.js e4x/Expressions/11.1.4-06.js e4x/Expressions/11.1.4-07.js e4x/Expressions/11.1.5.js e4x/Expressions/11.2.1.js e4x/Expressions/11.2.2.js e4x/Expressions/11.2.3.js e4x/Expressions/11.2.4.js e4x/Expressions/11.3.1.js e4x/Expressions/11.3.2.js e4x/Expressions/11.4.1.js e4x/Expressions/11.5.1.js e4x/Expressions/11.6.2.js e4x/Expressions/11.6.3.js e4x/Expressions/regress-301545.js e4x/Expressions/regress-302531.js e4x/Expressions/regress-340024.js e4x/Expressions/regress-496113.js e4x/GC/regress-292455.js e4x/GC/regress-313952-01.js e4x/GC/regress-324117.js e4x/Namespace/13.2.1.js e4x/Namespace/13.2.2.js e4x/Namespace/13.2.5.js e4x/Namespace/regress-283972.js e4x/Namespace/regress-292863.js e4x/Namespace/regress-350442.js e4x/Namespace/regress-444608-02.js e4x/Namespace/regress-444608.js e4x/QName/13.3.1.js e4x/QName/13.3.2.js e4x/QName/13.3.5.js e4x/QName/regress-373595-01.js e4x/QName/regress-373595-02.js e4x/QName/regress-373595-03.js e4x/QName/regress-444608.js e4x/Regress/regress-263934.js e4x/Regress/regress-263935.js e4x/Regress/regress-263936.js e4x/Regress/regress-264369.js e4x/Regress/regress-271545.js e4x/Regress/regress-277650.js e4x/Regress/regress-277664.js e4x/Regress/regress-277683.js e4x/Regress/regress-277779.js e4x/Regress/regress-277935.js e4x/Regress/regress-283349.js e4x/Regress/regress-290056.js e4x/Regress/regress-290499.js e4x/Regress/regress-301553.js e4x/Regress/regress-301573.js e4x/Regress/regress-301596.js e4x/Regress/regress-301692.js e4x/Regress/regress-313799.js e4x/Regress/regress-318922.js e4x/Regress/regress-325425.js e4x/Regress/regress-327691-01.js e4x/Regress/regress-327691-02.js e4x/Regress/regress-327697.js e4x/Regress/regress-328249.js e4x/Regress/regress-329257.js e4x/Regress/regress-331664.js e4x/Regress/regress-350206-1.js e4x/Regress/regress-350206.js e4x/Regress/regress-352103.js e4x/Regress/regress-354145-01.js e4x/Regress/regress-354145-02.js e4x/Regress/regress-354145-03.js e4x/Regress/regress-354145-04.js e4x/Regress/regress-354145-05.js e4x/Regress/regress-354145-07.js e4x/Regress/regress-355474-02.js e4x/Regress/regress-356238-01.js e4x/Regress/regress-369032.js e4x/Regress/regress-369536.js e4x/Regress/regress-372564.js e4x/Regress/regress-374106.js e4x/Regress/regress-374112.js e4x/Regress/regress-374116.js e4x/Regress/regress-374160.js e4x/Regress/regress-375406.js e4x/Regress/regress-378492.js e4x/Regress/regress-407323.js e4x/Regress/regress-426520.js e4x/Regress/regress-453915.js e4x/Regress/regress-460180.js e4x/Regress/regress-465063.js e4x/Regress/regress-470619.js e4x/Regress/regress-473709.js e4x/Regress/regress-474319.js e4x/Regress/regress-477053.js e4x/Statements/12.1.js e4x/Statements/12.2.js e4x/Statements/12.3-01.js e4x/TypeConversion/10.1.1.js e4x/TypeConversion/10.1.2.js e4x/TypeConversion/10.3.1.js e4x/TypeConversion/10.3.js e4x/TypeConversion/10.4.1.js e4x/TypeConversion/10.4.js e4x/Types/9.1.1.1.js e4x/Types/9.1.1.2.js e4x/Types/9.1.1.3.js e4x/Types/9.1.1.6.js e4x/Types/9.2.1.1.js e4x/Types/9.2.1.2.js e4x/Types/9.2.1.8.js e4x/XML/13.4.1.js e4x/XML/13.4.2.js e4x/XML/13.4.3.10.js e4x/XML/13.4.4.11.js e4x/XML/13.4.4.12-1.js e4x/XML/13.4.4.12.js e4x/XML/13.4.4.13.js e4x/XML/13.4.4.14.js e4x/XML/13.4.4.15.js e4x/XML/13.4.4.16.js e4x/XML/13.4.4.18.js e4x/XML/13.4.4.19.js e4x/XML/13.4.4.2.js e4x/XML/13.4.4.20.js e4x/XML/13.4.4.21.js e4x/XML/13.4.4.22.js e4x/XML/13.4.4.23.js e4x/XML/13.4.4.24.js e4x/XML/13.4.4.25.js e4x/XML/13.4.4.27.js e4x/XML/13.4.4.29.js e4x/XML/13.4.4.3.js e4x/XML/13.4.4.30.js e4x/XML/13.4.4.31.js e4x/XML/13.4.4.32-01.js e4x/XML/13.4.4.32.js e4x/XML/13.4.4.33.js e4x/XML/13.4.4.34.js e4x/XML/13.4.4.35.js e4x/XML/13.4.4.36.js e4x/XML/13.4.4.37.js e4x/XML/13.4.4.38.js e4x/XML/13.4.4.39.js e4x/XML/13.4.4.4.js e4x/XML/13.4.4.40.js e4x/XML/13.4.4.5.js e4x/XML/13.4.4.6.js e4x/XML/13.4.4.7.js e4x/XML/13.4.4.8.js e4x/XML/13.4.4.9.js e4x/XML/regress-291930.js e4x/XML/regress-324688.js e4x/XML/regress-336921.js e4x/XMLList/13.5.1.js e4x/XMLList/13.5.2.js e4x/XMLList/13.5.4.10.js e4x/XMLList/13.5.4.11.js e4x/XMLList/13.5.4.12.js e4x/XMLList/13.5.4.13.js e4x/XMLList/13.5.4.14.js e4x/XMLList/13.5.4.15.js e4x/XMLList/13.5.4.16.js e4x/XMLList/13.5.4.17.js e4x/XMLList/13.5.4.18.js e4x/XMLList/13.5.4.19.js e4x/XMLList/13.5.4.2.js e4x/XMLList/13.5.4.20.js e4x/XMLList/13.5.4.21.js e4x/XMLList/13.5.4.22.js e4x/XMLList/13.5.4.3.js e4x/XMLList/13.5.4.4.js e4x/XMLList/13.5.4.5.js e4x/XMLList/13.5.4.6.js e4x/XMLList/13.5.4.7.js e4x/XMLList/13.5.4.8.js e4x/XMLList/13.5.4.9.js e4x/decompilation/regress-349814.js e4x/decompilation/regress-349815.js e4x/decompilation/regress-349822.js e4x/decompilation/regress-349956.js e4x/decompilation/regress-355101.js e4x/decompilation/regress-355474-01.js e4x/decompilation/regress-373678.js e4x/decompilation/regress-429249.js e4x/extensions/regress-305335.js e4x/extensions/regress-321547.js e4x/extensions/regress-327534.js e4x/extensions/regress-327897.js e4x/extensions/regress-354145-06.js e4x/extensions/regress-354151-01.js e4x/extensions/regress-354151-02.js e4x/extensions/regress-374025.js e4x/extensions/regress-450871-01.js e4x/extensions/regress-450871-02.js e4x/extensions/regress-462734-01.js ecma/Array/15.4-1.js ecma/Array/15.4-2.js ecma/Array/15.4.1.1.js ecma/Array/15.4.1.2.js ecma/Array/15.4.1.3.js ecma/Array/15.4.1.js ecma/Array/15.4.2.1-1.js ecma/Array/15.4.2.1-2.js ecma/Array/15.4.2.1-3.js ecma/Array/15.4.2.2-1.js ecma/Array/15.4.2.2-2.js ecma/Array/15.4.2.3.js ecma/Array/15.4.3.1-2.js ecma/Array/15.4.3.2.js ecma/Array/15.4.4.1.js ecma/Array/15.4.4.2.js ecma/Array/15.4.4.3-1.js ecma/Array/15.4.4.4-1.js ecma/Array/15.4.4.4-2.js ecma/Array/15.4.4.5-1.js ecma/Array/15.4.4.5-2.js ecma/Array/15.4.4.5-3.js ecma/Array/15.4.4.js ecma/Array/15.4.5.1-2.js ecma/Array/15.4.5.2-1.js ecma/Array/15.4.5.2-2.js ecma/Boolean/15.6.1.js ecma/Boolean/15.6.2.js ecma/Boolean/15.6.3.1-1.js ecma/Boolean/15.6.3.1-2.js ecma/Boolean/15.6.3.1-3.js ecma/Boolean/15.6.3.1-4.js ecma/Boolean/15.6.3.1.js ecma/Boolean/15.6.4-1.js ecma/Boolean/15.6.4.1.js ecma/Boolean/15.6.4.2-1.js ecma/Boolean/15.6.4.2-2.js ecma/Boolean/15.6.4.2-3.js ecma/Boolean/15.6.4.2-4-n.js ecma/Boolean/15.6.4.3-1.js ecma/Boolean/15.6.4.3-2.js ecma/Boolean/15.6.4.3-3.js ecma/Boolean/15.6.4.3-4-n.js ecma/Boolean/15.6.4.3.js ecma/Boolean/15.6.4.js ecma/Date/15.9.1.1-1.js ecma/Date/15.9.1.1-2.js ecma/Date/15.9.1.13-1.js ecma/Date/15.9.2.1.js ecma/Date/15.9.2.2-1.js ecma/Date/15.9.2.2-2.js ecma/Date/15.9.2.2-3.js ecma/Date/15.9.2.2-4.js ecma/Date/15.9.2.2-5.js ecma/Date/15.9.2.2-6.js ecma/Date/15.9.3.1-1.js ecma/Date/15.9.3.1-2.js ecma/Date/15.9.3.1-3.js ecma/Date/15.9.3.1-4.js ecma/Date/15.9.3.1-5.js ecma/Date/15.9.3.2-1.js ecma/Date/15.9.3.2-2.js ecma/Date/15.9.3.2-3.js ecma/Date/15.9.3.2-4.js ecma/Date/15.9.3.2-5.js ecma/Date/15.9.3.8-1.js ecma/Date/15.9.3.8-2.js ecma/Date/15.9.3.8-3.js ecma/Date/15.9.3.8-4.js ecma/Date/15.9.3.8-5.js ecma/Date/15.9.4.2-1.js ecma/Date/15.9.4.2.js ecma/Date/15.9.4.3.js ecma/Date/15.9.5.1.js ecma/Date/15.9.5.10-1.js ecma/Date/15.9.5.10-10.js ecma/Date/15.9.5.10-11.js ecma/Date/15.9.5.10-12.js ecma/Date/15.9.5.10-13.js ecma/Date/15.9.5.10-3.js ecma/Date/15.9.5.10-4.js ecma/Date/15.9.5.10-5.js ecma/Date/15.9.5.10-6.js ecma/Date/15.9.5.10-7.js ecma/Date/15.9.5.10-8.js ecma/Date/15.9.5.10-9.js ecma/Date/15.9.5.11-1.js ecma/Date/15.9.5.11-3.js ecma/Date/15.9.5.11-4.js ecma/Date/15.9.5.11-5.js ecma/Date/15.9.5.11-6.js ecma/Date/15.9.5.11-7.js ecma/Date/15.9.5.12-1.js ecma/Date/15.9.5.12-3.js ecma/Date/15.9.5.12-4.js ecma/Date/15.9.5.12-5.js ecma/Date/15.9.5.12-6.js ecma/Date/15.9.5.12-7.js ecma/Date/15.9.5.12-8.js ecma/Date/15.9.5.13-1.js ecma/Date/15.9.5.13-2.js ecma/Date/15.9.5.13-3.js ecma/Date/15.9.5.13-4.js ecma/Date/15.9.5.13-5.js ecma/Date/15.9.5.13-6.js ecma/Date/15.9.5.13-7.js ecma/Date/15.9.5.13-8.js ecma/Date/15.9.5.14.js ecma/Date/15.9.5.15.js ecma/Date/15.9.5.16.js ecma/Date/15.9.5.17.js ecma/Date/15.9.5.18.js ecma/Date/15.9.5.19.js ecma/Date/15.9.5.2-1.js ecma/Date/15.9.5.2-2-n.js ecma/Date/15.9.5.2.js ecma/Date/15.9.5.20.js ecma/Date/15.9.5.21-1.js ecma/Date/15.9.5.21-2.js ecma/Date/15.9.5.21-3.js ecma/Date/15.9.5.21-4.js ecma/Date/15.9.5.21-5.js ecma/Date/15.9.5.21-6.js ecma/Date/15.9.5.21-7.js ecma/Date/15.9.5.21-8.js ecma/Date/15.9.5.22-1.js ecma/Date/15.9.5.22-2.js ecma/Date/15.9.5.22-3.js ecma/Date/15.9.5.22-4.js ecma/Date/15.9.5.22-5.js ecma/Date/15.9.5.22-6.js ecma/Date/15.9.5.22-7.js ecma/Date/15.9.5.22-8.js ecma/Date/15.9.5.23-1.js ecma/Date/15.9.5.23-10.js ecma/Date/15.9.5.23-11.js ecma/Date/15.9.5.23-12.js ecma/Date/15.9.5.23-13.js ecma/Date/15.9.5.23-14.js ecma/Date/15.9.5.23-15.js ecma/Date/15.9.5.23-16.js ecma/Date/15.9.5.23-17.js ecma/Date/15.9.5.23-18.js ecma/Date/15.9.5.23-2.js ecma/Date/15.9.5.23-3-n.js ecma/Date/15.9.5.23-4.js ecma/Date/15.9.5.23-5.js ecma/Date/15.9.5.23-6.js ecma/Date/15.9.5.23-7.js ecma/Date/15.9.5.23-8.js ecma/Date/15.9.5.23-9.js ecma/Date/15.9.5.24-1.js ecma/Date/15.9.5.24-2.js ecma/Date/15.9.5.24-3.js ecma/Date/15.9.5.24-4.js ecma/Date/15.9.5.24-5.js ecma/Date/15.9.5.24-6.js ecma/Date/15.9.5.24-7.js ecma/Date/15.9.5.24-8.js ecma/Date/15.9.5.25-1.js ecma/Date/15.9.5.26-1.js ecma/Date/15.9.5.27-1.js ecma/Date/15.9.5.28-1.js ecma/Date/15.9.5.29-1.js ecma/Date/15.9.5.3-1-n.js ecma/Date/15.9.5.3-2.js ecma/Date/15.9.5.30-1.js ecma/Date/15.9.5.31-1.js ecma/Date/15.9.5.32-1.js ecma/Date/15.9.5.33-1.js ecma/Date/15.9.5.34-1.js ecma/Date/15.9.5.35-1.js ecma/Date/15.9.5.36-1.js ecma/Date/15.9.5.36-2.js ecma/Date/15.9.5.36-3.js ecma/Date/15.9.5.36-4.js ecma/Date/15.9.5.36-5.js ecma/Date/15.9.5.36-6.js ecma/Date/15.9.5.36-7.js ecma/Date/15.9.5.37-1.js ecma/Date/15.9.5.37-2.js ecma/Date/15.9.5.37-3.js ecma/Date/15.9.5.37-4.js ecma/Date/15.9.5.37-5.js ecma/Date/15.9.5.4-1.js ecma/Date/15.9.5.4-2-n.js ecma/Date/15.9.5.5.js ecma/Date/15.9.5.6.js ecma/Date/15.9.5.7.js ecma/Date/15.9.5.8.js ecma/Date/15.9.5.9.js ecma/Date/15.9.5.js ecma/ExecutionContexts/10.1.3-1.js ecma/ExecutionContexts/10.1.3-2.js ecma/ExecutionContexts/10.1.3.js ecma/ExecutionContexts/10.1.4-1.js ecma/ExecutionContexts/10.1.4-10.js ecma/ExecutionContexts/10.1.4-2.js ecma/ExecutionContexts/10.1.4-3.js ecma/ExecutionContexts/10.1.4-4.js ecma/ExecutionContexts/10.1.4-5.js ecma/ExecutionContexts/10.1.4-6.js ecma/ExecutionContexts/10.1.4-7.js ecma/ExecutionContexts/10.1.4-8.js ecma/ExecutionContexts/10.1.5-1.js ecma/ExecutionContexts/10.1.5-2.js ecma/ExecutionContexts/10.1.5-3.js ecma/ExecutionContexts/10.1.5-4.js ecma/ExecutionContexts/10.1.8-2.js ecma/ExecutionContexts/10.1.8-3.js ecma/ExecutionContexts/10.2.1.js ecma/ExecutionContexts/10.2.2-1.js ecma/ExecutionContexts/10.2.2-2.js ecma/ExecutionContexts/10.2.3-1.js ecma/ExecutionContexts/10.2.3-2.js ecma/Expressions/11.1.1.js ecma/Expressions/11.10-1.js ecma/Expressions/11.10-2.js ecma/Expressions/11.10-3.js ecma/Expressions/11.12-1.js ecma/Expressions/11.12-2-n.js ecma/Expressions/11.12-3.js ecma/Expressions/11.12-4.js ecma/Expressions/11.13.1.js ecma/Expressions/11.13.2-1.js ecma/Expressions/11.13.2-2.js ecma/Expressions/11.13.2-3.js ecma/Expressions/11.13.2-4.js ecma/Expressions/11.13.2-5.js ecma/Expressions/11.13.js ecma/Expressions/11.14-1.js ecma/Expressions/11.2.1-1.js ecma/Expressions/11.2.1-2.js ecma/Expressions/11.2.1-3-n.js ecma/Expressions/11.2.1-4-n.js ecma/Expressions/11.2.1-5.js ecma/Expressions/11.2.2-1-n.js ecma/Expressions/11.2.2-1.js ecma/Expressions/11.2.2-10-n.js ecma/Expressions/11.2.2-11.js ecma/Expressions/11.2.2-2-n.js ecma/Expressions/11.2.2-3-n.js ecma/Expressions/11.2.2-4-n.js ecma/Expressions/11.2.2-5-n.js ecma/Expressions/11.2.2-6-n.js ecma/Expressions/11.2.2-7-n.js ecma/Expressions/11.2.2-8-n.js ecma/Expressions/11.2.2-9-n.js ecma/Expressions/11.2.3-1.js ecma/Expressions/11.2.3-2-n.js ecma/Expressions/11.2.3-3-n.js ecma/Expressions/11.2.3-4-n.js ecma/Expressions/11.2.3-5.js ecma/Expressions/11.3.1.js ecma/Expressions/11.3.2.js ecma/Expressions/11.4.1.js ecma/Expressions/11.4.2.js ecma/Expressions/11.4.3.js ecma/Expressions/11.4.4.js ecma/Expressions/11.4.5.js ecma/Expressions/11.4.6.js ecma/Expressions/11.4.7-01.js ecma/Expressions/11.4.7-02.js ecma/Expressions/11.4.8.js ecma/Expressions/11.4.9.js ecma/Expressions/11.5.1.js ecma/Expressions/11.5.2.js ecma/Expressions/11.5.3.js ecma/Expressions/11.6.1-1.js ecma/Expressions/11.6.1-2.js ecma/Expressions/11.6.1-3.js ecma/Expressions/11.6.2-1.js ecma/Expressions/11.6.3.js ecma/Expressions/11.7.1.js ecma/Expressions/11.7.2.js ecma/Expressions/11.7.3.js ecma/Expressions/11.8.1.js ecma/Expressions/11.8.2.js ecma/Expressions/11.8.3.js ecma/Expressions/11.8.4.js ecma/Expressions/11.9.1.js ecma/Expressions/11.9.2.js ecma/Expressions/11.9.3.js ecma/FunctionObjects/15.3.1.1-1.js ecma/FunctionObjects/15.3.1.1-2.js ecma/FunctionObjects/15.3.1.1-3.js ecma/FunctionObjects/15.3.2.1-1.js ecma/FunctionObjects/15.3.2.1-2.js ecma/FunctionObjects/15.3.2.1-3.js ecma/FunctionObjects/15.3.3.1-2.js ecma/FunctionObjects/15.3.3.1-3.js ecma/FunctionObjects/15.3.3.1-4.js ecma/FunctionObjects/15.3.3.2.js ecma/FunctionObjects/15.3.4-1.js ecma/FunctionObjects/15.3.4.1.js ecma/FunctionObjects/15.3.4.js ecma/FunctionObjects/15.3.5-1.js ecma/FunctionObjects/15.3.5-2.js ecma/FunctionObjects/15.3.5.1.js ecma/FunctionObjects/15.3.5.3.js ecma/GlobalObject/15.1-1-n.js ecma/GlobalObject/15.1-2-n.js ecma/GlobalObject/15.1.1.1.js ecma/GlobalObject/15.1.1.2.js ecma/GlobalObject/15.1.2.1-2.js ecma/GlobalObject/15.1.2.2-2.js ecma/GlobalObject/15.1.2.3-2.js ecma/GlobalObject/15.1.2.5-2.js ecma/GlobalObject/15.1.2.5-3.js ecma/LexicalConventions/7.1-1.js ecma/LexicalConventions/7.1-2.js ecma/LexicalConventions/7.1-3.js ecma/LexicalConventions/7.2-1.js ecma/LexicalConventions/7.2-2-n.js ecma/LexicalConventions/7.2-3-n.js ecma/LexicalConventions/7.2-4-n.js ecma/LexicalConventions/7.2-5-n.js ecma/LexicalConventions/7.2-6.js ecma/LexicalConventions/7.3-1.js ecma/LexicalConventions/7.3-10.js ecma/LexicalConventions/7.3-11.js ecma/LexicalConventions/7.3-12.js ecma/LexicalConventions/7.3-13-n.js ecma/LexicalConventions/7.3-2.js ecma/LexicalConventions/7.3-3.js ecma/LexicalConventions/7.3-4.js ecma/LexicalConventions/7.3-5.js ecma/LexicalConventions/7.3-6.js ecma/LexicalConventions/7.3-7.js ecma/LexicalConventions/7.3-8.js ecma/LexicalConventions/7.3-9.js ecma/LexicalConventions/7.4.1-1-n.js ecma/LexicalConventions/7.4.1-2-n.js ecma/LexicalConventions/7.4.1-3-n.js ecma/LexicalConventions/7.4.2-1-n.js ecma/LexicalConventions/7.4.2-10-n.js ecma/LexicalConventions/7.4.2-11-n.js ecma/LexicalConventions/7.4.2-12-n.js ecma/LexicalConventions/7.4.2-13-n.js ecma/LexicalConventions/7.4.2-14-n.js ecma/LexicalConventions/7.4.2-15-n.js ecma/LexicalConventions/7.4.2-16-n.js ecma/LexicalConventions/7.4.2-2-n.js ecma/LexicalConventions/7.4.2-3-n.js ecma/LexicalConventions/7.4.2-4-n.js ecma/LexicalConventions/7.4.2-5-n.js ecma/LexicalConventions/7.4.2-6-n.js ecma/LexicalConventions/7.4.2-7-n.js ecma/LexicalConventions/7.4.2-8-n.js ecma/LexicalConventions/7.4.2-9-n.js ecma/LexicalConventions/7.4.3-1-n.js ecma/LexicalConventions/7.4.3-10-n.js ecma/LexicalConventions/7.4.3-11-n.js ecma/LexicalConventions/7.4.3-12-n.js ecma/LexicalConventions/7.4.3-13-n.js ecma/LexicalConventions/7.4.3-14-n.js ecma/LexicalConventions/7.4.3-15-n.js ecma/LexicalConventions/7.4.3-16-n.js ecma/LexicalConventions/7.4.3-2-n.js ecma/LexicalConventions/7.4.3-3-n.js ecma/LexicalConventions/7.4.3-4-n.js ecma/LexicalConventions/7.4.3-5-n.js ecma/LexicalConventions/7.4.3-6-n.js ecma/LexicalConventions/7.4.3-7-n.js ecma/LexicalConventions/7.4.3-8-n.js ecma/LexicalConventions/7.4.3-9-n.js ecma/LexicalConventions/7.5-1.js ecma/LexicalConventions/7.5-10-n.js ecma/LexicalConventions/7.5-2-n.js ecma/LexicalConventions/7.5-3-n.js ecma/LexicalConventions/7.5-4-n.js ecma/LexicalConventions/7.5-5-n.js ecma/LexicalConventions/7.5-6.js ecma/LexicalConventions/7.5-7.js ecma/LexicalConventions/7.5-8-n.js ecma/LexicalConventions/7.5-9-n.js ecma/LexicalConventions/7.6.js ecma/LexicalConventions/7.7.1.js ecma/LexicalConventions/7.7.2.js ecma/LexicalConventions/7.7.3-1.js ecma/LexicalConventions/7.7.3-2.js ecma/LexicalConventions/7.7.3.js ecma/LexicalConventions/7.7.4.js ecma/LexicalConventions/7.8.2-n.js ecma/Math/15.8-2-n.js ecma/Math/15.8-3-n.js ecma/Math/15.8.1.1-1.js ecma/Math/15.8.1.1-2.js ecma/Math/15.8.1.2-1.js ecma/Math/15.8.1.2-2.js ecma/Math/15.8.1.3-1.js ecma/Math/15.8.1.3-2.js ecma/Math/15.8.1.4-1.js ecma/Math/15.8.1.4-2.js ecma/Math/15.8.1.5-1.js ecma/Math/15.8.1.5-2.js ecma/Math/15.8.1.6-1.js ecma/Math/15.8.1.6-2.js ecma/Math/15.8.1.7-1.js ecma/Math/15.8.1.7-2.js ecma/Math/15.8.1.8-1.js ecma/Math/15.8.1.8-2.js ecma/Math/15.8.1.8-3.js ecma/Math/15.8.1.js ecma/Math/15.8.2.1.js ecma/Math/15.8.2.10.js ecma/Math/15.8.2.11.js ecma/Math/15.8.2.12.js ecma/Math/15.8.2.13.js ecma/Math/15.8.2.14.js ecma/Math/15.8.2.15.js ecma/Math/15.8.2.16.js ecma/Math/15.8.2.17.js ecma/Math/15.8.2.18.js ecma/Math/15.8.2.2.js ecma/Math/15.8.2.3.js ecma/Math/15.8.2.4.js ecma/Math/15.8.2.5.js ecma/Math/15.8.2.6.js ecma/Math/15.8.2.7.js ecma/Math/15.8.2.8.js ecma/Math/15.8.2.9.js ecma/Number/15.7.1.js ecma/Number/15.7.2.js ecma/Number/15.7.3.1-1.js ecma/Number/15.7.3.1-2.js ecma/Number/15.7.3.1-3.js ecma/Number/15.7.3.2-1.js ecma/Number/15.7.3.2-2.js ecma/Number/15.7.3.2-3.js ecma/Number/15.7.3.2-4.js ecma/Number/15.7.3.3-1.js ecma/Number/15.7.3.3-2.js ecma/Number/15.7.3.3-3.js ecma/Number/15.7.3.3-4.js ecma/Number/15.7.3.4-1.js ecma/Number/15.7.3.4-2.js ecma/Number/15.7.3.4-3.js ecma/Number/15.7.3.4-4.js ecma/Number/15.7.3.5-1.js ecma/Number/15.7.3.5-2.js ecma/Number/15.7.3.5-3.js ecma/Number/15.7.3.5-4.js ecma/Number/15.7.3.6-1.js ecma/Number/15.7.3.6-2.js ecma/Number/15.7.3.6-3.js ecma/Number/15.7.3.6-4.js ecma/Number/15.7.3.js ecma/Number/15.7.4-1.js ecma/Number/15.7.4.1.js ecma/Number/15.7.4.2-1.js ecma/Number/15.7.4.2-2-n.js ecma/Number/15.7.4.2-3-n.js ecma/Number/15.7.4.2-4.js ecma/Number/15.7.4.3-1.js ecma/Number/15.7.4.3-2.js ecma/Number/15.7.4.3-3-n.js ecma/ObjectObjects/15.2.1.1.js ecma/ObjectObjects/15.2.1.2.js ecma/ObjectObjects/15.2.2.1.js ecma/ObjectObjects/15.2.2.2.js ecma/ObjectObjects/15.2.3-1.js ecma/ObjectObjects/15.2.3.1-1.js ecma/ObjectObjects/15.2.3.1-2.js ecma/ObjectObjects/15.2.3.1-3.js ecma/ObjectObjects/15.2.3.1-4.js ecma/ObjectObjects/15.2.3.js ecma/ObjectObjects/15.2.4.1.js ecma/ObjectObjects/15.2.4.2.js ecma/ObjectObjects/15.2.4.3.js ecma/SourceText/6-1.js ecma/SourceText/6-2.js ecma/Statements/12.10-1.js ecma/Statements/12.10.js ecma/Statements/12.2-1.js ecma/Statements/12.5-1.js ecma/Statements/12.5-2.js ecma/Statements/12.6.1-1.js ecma/Statements/12.6.2-1.js ecma/Statements/12.6.2-2.js ecma/Statements/12.6.2-3.js ecma/Statements/12.6.2-4.js ecma/Statements/12.6.2-5.js ecma/Statements/12.6.2-6.js ecma/Statements/12.6.2-7.js ecma/Statements/12.6.2-8.js ecma/Statements/12.6.2-9-n.js ecma/Statements/12.6.3-1.js ecma/Statements/12.6.3-10.js ecma/Statements/12.6.3-11.js ecma/Statements/12.6.3-12.js ecma/Statements/12.6.3-19.js ecma/Statements/12.6.3-2.js ecma/Statements/12.6.3-3.js ecma/Statements/12.6.3-4.js ecma/Statements/12.6.3-5-n.js ecma/Statements/12.6.3-6-n.js ecma/Statements/12.6.3-7-n.js ecma/Statements/12.6.3-8-n.js ecma/Statements/12.6.3-9-n.js ecma/Statements/12.7-1-n.js ecma/Statements/12.8-1-n.js ecma/Statements/12.9-1-n.js ecma/String/15.5.1.js ecma/String/15.5.2.js ecma/String/15.5.3.1-1.js ecma/String/15.5.3.1-2.js ecma/String/15.5.3.1-3.js ecma/String/15.5.3.1-4.js ecma/String/15.5.3.2-1.js ecma/String/15.5.3.2-2.js ecma/String/15.5.3.2-3.js ecma/String/15.5.3.js ecma/String/15.5.4.1.js ecma/String/15.5.4.10-1.js ecma/String/15.5.4.11-1.js ecma/String/15.5.4.11-3.js ecma/String/15.5.4.11-4.js ecma/String/15.5.4.11-6.js ecma/String/15.5.4.12-2.js ecma/String/15.5.4.12-3.js ecma/String/15.5.4.2-1.js ecma/String/15.5.4.2-2-n.js ecma/String/15.5.4.2-3.js ecma/String/15.5.4.2.js ecma/String/15.5.4.3-1.js ecma/String/15.5.4.3-2.js ecma/String/15.5.4.3-3-n.js ecma/String/15.5.4.4-1.js ecma/String/15.5.4.4-2.js ecma/String/15.5.4.4-3.js ecma/String/15.5.4.4-4.js ecma/String/15.5.4.5-1.js ecma/String/15.5.4.5-2.js ecma/String/15.5.4.5-3.js ecma/String/15.5.4.5-4.js ecma/String/15.5.4.5-5.js ecma/String/15.5.4.6-1.js ecma/String/15.5.4.6-2.js ecma/String/15.5.4.7-1.js ecma/String/15.5.4.7-2.js ecma/String/15.5.4.8-1.js ecma/String/15.5.4.8-3.js ecma/String/15.5.4.9-1.js ecma/String/15.5.4.js ecma/String/15.5.5.1.js ecma/TypeConversion/9.2.js ecma/TypeConversion/9.3-1.js ecma/TypeConversion/9.3.1-1.js ecma/TypeConversion/9.3.1-2.js ecma/TypeConversion/9.3.1-3.js ecma/TypeConversion/9.3.js ecma/TypeConversion/9.4-1.js ecma/TypeConversion/9.4-2.js ecma/TypeConversion/9.5-2.js ecma/TypeConversion/9.6.js ecma/TypeConversion/9.7.js ecma/TypeConversion/9.8.1.js ecma/TypeConversion/9.9-1.js ecma/Types/8.1.js ecma/Types/8.4.js ecma/Types/8.6.2.1-1.js ecma/extensions/10.1.4-9.js ecma/extensions/10.1.6.js ecma/extensions/10.1.8-1.js ecma/extensions/11.6.1-1.js ecma/extensions/11.6.1-2.js ecma/extensions/11.6.1-3.js ecma/extensions/11.6.2-1.js ecma/extensions/15-1.js ecma/extensions/15-2.js ecma/extensions/15.2.1.1.js ecma/extensions/15.2.3-1.js ecma/extensions/15.2.4.js ecma/extensions/15.3.1.1-1.js ecma/extensions/15.3.1.1-2.js ecma/extensions/15.3.2.1-1.js ecma/extensions/15.3.2.1-2.js ecma/extensions/15.3.3.1-1.js ecma/extensions/15.4.3.js ecma/extensions/15.5.3.js ecma/extensions/15.5.4.2.js ecma/extensions/15.5.4.4-4.js ecma/extensions/15.5.4.5-6.js ecma/extensions/15.5.4.7-3.js ecma/extensions/15.6.3.1-5.js ecma/extensions/15.6.3.js ecma/extensions/15.6.4-2.js ecma/extensions/15.7.3.js ecma/extensions/15.7.4.js ecma/extensions/15.8-1.js ecma/extensions/15.9.5.js ecma/extensions/8.6.2.1-1.js ecma/extensions/9.9-1.js ecma/jsref.js ecma_2/Exceptions/boolean-001.js ecma_2/Exceptions/boolean-002.js ecma_2/Exceptions/date-001.js ecma_2/Exceptions/date-002.js ecma_2/Exceptions/date-003.js ecma_2/Exceptions/date-004.js ecma_2/Exceptions/exception-001.js ecma_2/Exceptions/exception-002.js ecma_2/Exceptions/exception-003.js ecma_2/Exceptions/exception-004.js ecma_2/Exceptions/exception-005.js ecma_2/Exceptions/exception-006.js ecma_2/Exceptions/exception-007.js ecma_2/Exceptions/exception-008.js ecma_2/Exceptions/exception-009.js ecma_2/Exceptions/exception-010-n.js ecma_2/Exceptions/exception-011-n.js ecma_2/Exceptions/expression-001.js ecma_2/Exceptions/expression-002.js ecma_2/Exceptions/expression-003.js ecma_2/Exceptions/expression-004.js ecma_2/Exceptions/expression-005.js ecma_2/Exceptions/expression-006.js ecma_2/Exceptions/expression-007.js ecma_2/Exceptions/expression-008.js ecma_2/Exceptions/expression-009.js ecma_2/Exceptions/expression-010.js ecma_2/Exceptions/expression-011.js ecma_2/Exceptions/expression-012.js ecma_2/Exceptions/expression-013.js ecma_2/Exceptions/expression-014.js ecma_2/Exceptions/expression-015.js ecma_2/Exceptions/expression-016.js ecma_2/Exceptions/expression-017.js ecma_2/Exceptions/expression-019.js ecma_2/Exceptions/function-001.js ecma_2/Exceptions/global-001.js ecma_2/Exceptions/global-002.js ecma_2/Exceptions/lexical-001.js ecma_2/Exceptions/lexical-002.js ecma_2/Exceptions/lexical-003.js ecma_2/Exceptions/lexical-004.js ecma_2/Exceptions/lexical-005.js ecma_2/Exceptions/lexical-006.js ecma_2/Exceptions/lexical-007.js ecma_2/Exceptions/lexical-008.js ecma_2/Exceptions/lexical-009.js ecma_2/Exceptions/lexical-010.js ecma_2/Exceptions/lexical-011.js ecma_2/Exceptions/lexical-012.js ecma_2/Exceptions/lexical-013.js ecma_2/Exceptions/lexical-014.js ecma_2/Exceptions/lexical-015.js ecma_2/Exceptions/lexical-016.js ecma_2/Exceptions/lexical-017.js ecma_2/Exceptions/lexical-018.js ecma_2/Exceptions/lexical-019.js ecma_2/Exceptions/lexical-020.js ecma_2/Exceptions/lexical-021.js ecma_2/Exceptions/lexical-022.js ecma_2/Exceptions/lexical-023.js ecma_2/Exceptions/lexical-024.js ecma_2/Exceptions/lexical-025.js ecma_2/Exceptions/lexical-026.js ecma_2/Exceptions/lexical-027.js ecma_2/Exceptions/lexical-028.js ecma_2/Exceptions/lexical-029.js ecma_2/Exceptions/lexical-030.js ecma_2/Exceptions/lexical-031.js ecma_2/Exceptions/lexical-032.js ecma_2/Exceptions/lexical-033.js ecma_2/Exceptions/lexical-034.js ecma_2/Exceptions/lexical-035.js ecma_2/Exceptions/lexical-036.js ecma_2/Exceptions/lexical-037.js ecma_2/Exceptions/lexical-038.js ecma_2/Exceptions/lexical-039.js ecma_2/Exceptions/lexical-040.js ecma_2/Exceptions/lexical-041.js ecma_2/Exceptions/lexical-042.js ecma_2/Exceptions/lexical-047.js ecma_2/Exceptions/lexical-048.js ecma_2/Exceptions/lexical-049.js ecma_2/Exceptions/lexical-050.js ecma_2/Exceptions/lexical-051.js ecma_2/Exceptions/lexical-052.js ecma_2/Exceptions/lexical-053.js ecma_2/Exceptions/lexical-054.js ecma_2/Exceptions/number-001.js ecma_2/Exceptions/number-002.js ecma_2/Exceptions/number-003.js ecma_2/Exceptions/statement-001.js ecma_2/Exceptions/statement-002.js ecma_2/Exceptions/statement-003.js ecma_2/Exceptions/statement-004.js ecma_2/Exceptions/statement-005.js ecma_2/Exceptions/statement-006.js ecma_2/Exceptions/statement-007.js ecma_2/Exceptions/statement-008.js ecma_2/Exceptions/statement-009.js ecma_2/Exceptions/string-001.js ecma_2/Exceptions/string-002.js ecma_2/Expressions/StrictEquality-001.js ecma_2/FunctionObjects/apply-001-n.js ecma_2/FunctionObjects/call-1.js ecma_2/LexicalConventions/keywords-001.js ecma_2/LexicalConventions/regexp-literals-001.js ecma_2/LexicalConventions/regexp-literals-002.js ecma_2/RegExp/constructor-001.js ecma_2/RegExp/exec-002.js ecma_2/RegExp/function-001.js ecma_2/RegExp/hex-001.js ecma_2/RegExp/multiline-001.js ecma_2/RegExp/octal-001.js ecma_2/RegExp/octal-002.js ecma_2/RegExp/octal-003.js ecma_2/RegExp/properties-001.js ecma_2/RegExp/properties-002.js ecma_2/RegExp/regexp-enumerate-001.js ecma_2/RegExp/regress-001.js ecma_2/RegExp/unicode-001.js ecma_2/Statements/dowhile-001.js ecma_2/Statements/dowhile-002.js ecma_2/Statements/dowhile-003.js ecma_2/Statements/dowhile-004.js ecma_2/Statements/dowhile-005.js ecma_2/Statements/dowhile-006.js ecma_2/Statements/dowhile-007.js ecma_2/Statements/forin-001.js ecma_2/Statements/forin-002.js ecma_2/Statements/if-001.js ecma_2/Statements/label-001.js ecma_2/Statements/label-002.js ecma_2/Statements/switch-001.js ecma_2/Statements/switch-002.js ecma_2/Statements/switch-003.js ecma_2/Statements/switch-004.js ecma_2/Statements/try-001.js ecma_2/Statements/try-003.js ecma_2/Statements/try-004.js ecma_2/Statements/try-005.js ecma_2/Statements/try-006.js ecma_2/Statements/try-007.js ecma_2/Statements/try-008.js ecma_2/Statements/try-009.js ecma_2/Statements/try-010.js ecma_2/Statements/try-012.js ecma_2/Statements/while-001.js ecma_2/Statements/while-002.js ecma_2/Statements/while-003.js ecma_2/Statements/while-004.js ecma_2/String/match-001.js ecma_2/String/match-002.js ecma_2/String/match-003.js ecma_2/String/match-004.js ecma_2/String/split-001.js ecma_2/String/split-002.js ecma_2/String/split-003.js ecma_2/extensions/constructor-001.js ecma_2/extensions/function-001.js ecma_2/extensions/instanceof-001.js ecma_2/extensions/instanceof-002.js ecma_2/extensions/instanceof-003-n.js ecma_2/extensions/instanceof-004-n.js ecma_2/extensions/instanceof-005-n.js ecma_2/extensions/instanceof-006.js ecma_2/instanceof/instanceof-001.js ecma_2/instanceof/instanceof-002.js ecma_2/instanceof/regress-7635.js ecma_2/jsref.js ecma_3/Array/15.4.4.11-01.js ecma_3/Array/15.4.4.3-1.js ecma_3/Array/15.4.4.4-001.js ecma_3/Array/15.5.4.8-01.js ecma_3/Array/regress-101488.js ecma_3/Array/regress-130451.js ecma_3/Array/regress-322135-01.js ecma_3/Array/regress-390598.js ecma_3/Array/regress-421325.js ecma_3/Array/regress-430717.js ecma_3/Array/regress-488989.js ecma_3/Date/15.9.1.2-01.js ecma_3/Date/15.9.4.3.js ecma_3/Date/15.9.5.3.js ecma_3/Date/15.9.5.4.js ecma_3/Date/15.9.5.5.js ecma_3/Date/15.9.5.6.js ecma_3/Date/15.9.5.7.js ecma_3/Date/regress-452786.js ecma_3/Exceptions/15.11.1.1.js ecma_3/Exceptions/15.11.4.4-1.js ecma_3/Exceptions/15.11.7.6-001.js ecma_3/Exceptions/15.11.7.6-002.js ecma_3/Exceptions/15.11.7.6-003.js ecma_3/Exceptions/binding-001.js ecma_3/Exceptions/regress-58946.js ecma_3/Exceptions/regress-95101.js ecma_3/ExecutionContexts/10.1.3-1.js ecma_3/ExecutionContexts/10.1.3-2.js ecma_3/ExecutionContexts/10.1.3.js ecma_3/ExecutionContexts/10.1.4-1.js ecma_3/ExecutionContexts/10.6.1-01.js ecma_3/ExecutionContexts/regress-23346.js ecma_3/ExecutionContexts/regress-448595-01.js ecma_3/Expressions/11.10-01.js ecma_3/Expressions/11.10-02.js ecma_3/Expressions/11.10-03.js ecma_3/Expressions/11.6.1-1.js ecma_3/Expressions/11.7.1-01.js ecma_3/Expressions/11.7.2-01.js ecma_3/Expressions/11.7.3-01.js ecma_3/Expressions/11.9.6-1.js ecma_3/FunExpr/fe-001-n.js ecma_3/FunExpr/fe-001.js ecma_3/FunExpr/fe-002.js ecma_3/Function/15.3.4.3-1.js ecma_3/Function/15.3.4.4-1.js ecma_3/Function/arguments-001.js ecma_3/Function/arguments-002.js ecma_3/Function/call-001.js ecma_3/Function/regress-131964.js ecma_3/Function/regress-137181.js ecma_3/Function/regress-193555.js ecma_3/Function/regress-313570.js ecma_3/Function/regress-49286.js ecma_3/Function/regress-58274.js ecma_3/Function/regress-85880.js ecma_3/Function/regress-94506.js ecma_3/Function/regress-97921.js ecma_3/Function/scope-001.js ecma_3/Function/scope-002.js ecma_3/Number/15.7.4.2-01.js ecma_3/Number/15.7.4.3-02.js ecma_3/Number/15.7.4.5-1.js ecma_3/Number/15.7.4.5-2.js ecma_3/Number/15.7.4.6-1.js ecma_3/Number/15.7.4.7-1.js ecma_3/Number/15.7.4.7-2.js ecma_3/Number/regress-442242-01.js ecma_3/NumberFormatting/tostring-001.js ecma_3/Object/8.6.2.6-001.js ecma_3/Object/class-001.js ecma_3/Object/class-002.js ecma_3/Object/class-003.js ecma_3/Object/class-004.js ecma_3/Object/class-005.js ecma_3/Object/regress-361274.js ecma_3/Object/regress-385393-07.js ecma_3/Object/regress-459405.js ecma_3/Object/regress-72773.js ecma_3/Object/regress-79129-001.js ecma_3/Operators/11.13.1-001.js ecma_3/Operators/11.13.1-002.js ecma_3/Operators/11.4.1-001.js ecma_3/Operators/11.4.1-002.js ecma_3/RegExp/15.10.2-1.js ecma_3/RegExp/15.10.2.12.js ecma_3/RegExp/15.10.3.1-1.js ecma_3/RegExp/15.10.3.1-2.js ecma_3/RegExp/15.10.4.1-1.js ecma_3/RegExp/15.10.4.1-2.js ecma_3/RegExp/15.10.4.1-3.js ecma_3/RegExp/15.10.4.1-4.js ecma_3/RegExp/15.10.4.1-5-n.js ecma_3/RegExp/15.10.6.2-1.js ecma_3/RegExp/15.10.6.2-2.js ecma_3/RegExp/octal-001.js ecma_3/RegExp/octal-002.js ecma_3/RegExp/perlstress-001.js ecma_3/RegExp/perlstress-002.js ecma_3/RegExp/regress-100199.js ecma_3/RegExp/regress-105972.js ecma_3/RegExp/regress-119909.js ecma_3/RegExp/regress-122076.js ecma_3/RegExp/regress-123437.js ecma_3/RegExp/regress-165353.js ecma_3/RegExp/regress-169497.js ecma_3/RegExp/regress-169534.js ecma_3/RegExp/regress-187133.js ecma_3/RegExp/regress-191479.js ecma_3/RegExp/regress-202564.js ecma_3/RegExp/regress-209067.js ecma_3/RegExp/regress-209919.js ecma_3/RegExp/regress-216591.js ecma_3/RegExp/regress-220367-001.js ecma_3/RegExp/regress-223273.js ecma_3/RegExp/regress-223535.js ecma_3/RegExp/regress-224676.js ecma_3/RegExp/regress-225289.js ecma_3/RegExp/regress-225343.js ecma_3/RegExp/regress-24712.js ecma_3/RegExp/regress-285219.js ecma_3/RegExp/regress-28686.js ecma_3/RegExp/regress-309840.js ecma_3/RegExp/regress-312351.js ecma_3/RegExp/regress-31316.js ecma_3/RegExp/regress-334158.js ecma_3/RegExp/regress-346090.js ecma_3/RegExp/regress-367888.js: ecma_3/RegExp/regress-375642.js: ecma_3/RegExp/regress-375651.js ecma_3/RegExp/regress-375715-02.js ecma_3/RegExp/regress-375715-03.js ecma_3/RegExp/regress-465862.js ecma_3/RegExp/regress-57572.js ecma_3/RegExp/regress-57631.js ecma_3/RegExp/regress-67773.js ecma_3/RegExp/regress-72964.js ecma_3/RegExp/regress-76683.js ecma_3/RegExp/regress-78156.js ecma_3/RegExp/regress-85721.js ecma_3/RegExp/regress-87231.js ecma_3/RegExp/regress-98306.js ecma_3/Regress/regress-385393-04.js ecma_3/Regress/regress-419152.js ecma_3/Regress/regress-420087.js ecma_3/Regress/regress-420610.js ecma_3/Regress/regress-441477-01.js ecma_3/Regress/regress-469937.js ecma_3/Statements/12.10-01.js ecma_3/Statements/12.6.3.js ecma_3/Statements/regress-131348.js ecma_3/Statements/regress-157509.js ecma_3/Statements/regress-194364.js ecma_3/Statements/regress-226517.js ecma_3/Statements/regress-302439.js ecma_3/Statements/regress-324650.js ecma_3/Statements/regress-444979.js ecma_3/Statements/regress-74474-001.js ecma_3/Statements/regress-74474-002.js ecma_3/Statements/regress-74474-003.js ecma_3/Statements/regress-83532-001.js ecma_3/Statements/regress-83532-002.js ecma_3/Statements/switch-001.js ecma_3/String/regress-104375.js ecma_3/String/regress-189898.js ecma_3/String/regress-304376.js ecma_3/String/regress-313567.js ecma_3/String/regress-392378.js ecma_3/String/regress-83293.js ecma_3/Unicode/regress-352044-02-n.js ecma_3/Unicode/uc-001-n.js ecma_3/Unicode/uc-002-n.js ecma_3/Unicode/uc-002.js ecma_3/Unicode/uc-003.js ecma_3/Unicode/uc-004.js ecma_3/Unicode/uc-005.js ecma_3/extensions/10.1.3-2.js ecma_3/extensions/regress-103087.js ecma_3/extensions/regress-188206-01.js ecma_3/extensions/regress-188206-02.js ecma_3/extensions/regress-220367-002.js ecma_3/extensions/regress-228087.js ecma_3/extensions/regress-320854.js ecma_3/extensions/regress-327170.js ecma_3/extensions/regress-385393-03.js ecma_3/extensions/regress-429248.js js-test-driver-begin.js js-test-driver-end.js js1_1/jsref.js js1_2/Array/array_split_1.js js1_2/Array/general1.js js1_2/Array/general2.js js1_2/Array/slice.js js1_2/Array/splice1.js js1_2/Array/splice2.js js1_2/Array/tostring_1.js js1_2/Array/tostring_2.js js1_2/Objects/toString-001.js js1_2/String/charCodeAt.js js1_2/String/concat.js js1_2/String/match.js js1_2/String/slice.js js1_2/function/Number.js js1_2/function/String.js js1_2/function/definition-1.js js1_2/function/length.js js1_2/function/nesting-1.js js1_2/function/nesting.js js1_2/function/regexparg-1.js js1_2/jsref.js js1_2/operator/strictEquality.js js1_2/regexp/RegExp_dollar_number.js js1_2/regexp/RegExp_input.js js1_2/regexp/RegExp_input_as_array.js js1_2/regexp/RegExp_lastIndex.js js1_2/regexp/RegExp_lastMatch.js js1_2/regexp/RegExp_lastMatch_as_array.js js1_2/regexp/RegExp_lastParen.js js1_2/regexp/RegExp_lastParen_as_array.js js1_2/regexp/RegExp_leftContext.js js1_2/regexp/RegExp_leftContext_as_array.js js1_2/regexp/RegExp_multiline.js js1_2/regexp/RegExp_multiline_as_array.js js1_2/regexp/RegExp_object.js js1_2/regexp/RegExp_rightContext.js js1_2/regexp/RegExp_rightContext_as_array.js js1_2/regexp/alphanumeric.js js1_2/regexp/asterisk.js js1_2/regexp/backslash.js js1_2/regexp/backspace.js js1_2/regexp/beginLine.js js1_2/regexp/character_class.js js1_2/regexp/compile.js js1_2/regexp/control_characters.js js1_2/regexp/digit.js js1_2/regexp/dot.js js1_2/regexp/endLine.js js1_2/regexp/everything.js js1_2/regexp/exec.js js1_2/regexp/flags.js js1_2/regexp/global.js js1_2/regexp/hexadecimal.js js1_2/regexp/ignoreCase.js js1_2/regexp/interval.js js1_2/regexp/octal.js js1_2/regexp/parentheses.js js1_2/regexp/plus.js js1_2/regexp/question_mark.js js1_2/regexp/regress-6359.js js1_2/regexp/regress-9141.js js1_2/regexp/simple_form.js js1_2/regexp/source.js js1_2/regexp/special_characters.js js1_2/regexp/string_replace.js js1_2/regexp/string_search.js js1_2/regexp/string_split.js js1_2/regexp/test.js js1_2/regexp/toString.js js1_2/regexp/vertical_bar.js js1_2/regexp/whitespace.js js1_2/regexp/word_boundary.js js1_2/regress/regress-144834.js js1_2/regress/regress-7703.js js1_2/statements/break.js js1_2/statements/continue.js js1_2/statements/do_while.js js1_2/statements/switch.js js1_2/statements/switch2.js js1_2/version120/boolean-001.js js1_3/Boolean/boolean-001.js js1_3/Script/delete-001.js js1_3/Script/function-002.js js1_3/Script/in-001.js js1_3/Script/new-001.js js1_3/Script/switch-001.js js1_3/extensions/proto_10.js js1_3/extensions/proto_2.js js1_3/extensions/proto_5.js js1_3/extensions/script-001.js js1_3/inherit/proto_1.js js1_3/inherit/proto_10.js js1_3/inherit/proto_11.js js1_3/inherit/proto_12.js js1_3/inherit/proto_3.js js1_3/inherit/proto_4.js js1_3/inherit/proto_6.js js1_3/inherit/proto_7.js js1_3/inherit/proto_8.js js1_3/inherit/proto_9.js js1_3/jsref.js js1_3/regress/delete-001.js js1_3/regress/function-002.js js1_3/regress/in-001.js js1_3/regress/new-001.js js1_3/regress/switch-001.js js1_4/Eval/eval-001.js js1_4/Eval/eval-002.js js1_4/Eval/eval-003.js js1_4/Functions/function-001.js js1_4/Regress/date-001-n.js js1_4/Regress/function-001.js js1_4/Regress/function-002.js js1_4/Regress/function-003.js js1_4/Regress/function-004-n.js js1_4/Regress/regress-7224.js js1_4/Regress/toString-001-n.js js1_4/jsref.js js1_5/Array/11.1.4.js js1_5/Array/array-001.js js1_5/Array/regress-101964.js js1_5/Array/regress-107138.js js1_5/Array/regress-108440.js js1_5/Array/regress-154338.js js1_5/Array/regress-178722.js js1_5/Array/regress-255555.js js1_5/Array/regress-299644.js js1_5/Array/regress-300858.js js1_5/Array/regress-310351.js js1_5/Array/regress-311515.js js1_5/Array/regress-313153.js js1_5/Array/regress-315509-01.js js1_5/Array/regress-345961.js js1_5/Array/regress-348810.js js1_5/Array/regress-350256-01.js js1_5/Array/regress-350256-02.js js1_5/Array/regress-360681-01.js js1_5/Array/regress-360681-02.js js1_5/Array/regress-364104.js js1_5/Array/regress-422286.js js1_5/Array/regress-424954.js js1_5/Array/regress-451483.js js1_5/Array/regress-451906.js js1_5/Array/regress-456845.js js1_5/Array/regress-465980-01.js js1_5/Array/regress-474529.js js1_5/Array/regress-94257.js js1_5/Date/regress-188211.js js1_5/Date/regress-301738-01.js js1_5/Date/regress-309925-01.js js1_5/Date/regress-346027.js js1_5/Error/regress-354246.js js1_5/Error/regress-412324.js js1_5/Error/regress-465377.js js1_5/Exceptions/catchguard-002-n.js js1_5/Exceptions/catchguard-003-n.js js1_5/Exceptions/regress-123002.js js1_5/Exceptions/regress-232182.js js1_5/Exceptions/regress-257751.js js1_5/Exceptions/regress-273931.js js1_5/Exceptions/regress-347674.js js1_5/Exceptions/regress-350837.js js1_5/Expressions/regress-192288.js js1_5/Expressions/regress-96526-argsub.js js1_5/Expressions/regress-96526-noargsub.js js1_5/Function/10.1.6.js js1_5/Function/15.3.4.4.js js1_5/Function/regress-123371.js js1_5/Function/regress-178389.js js1_5/Function/regress-292215.js js1_5/Function/regress-344052.js js1_5/Function/regress-364023.js js1_5/GC/regress-104584.js js1_5/GC/regress-203278-3.js js1_5/GC/regress-278725.js js1_5/GC/regress-306788.js js1_5/GC/regress-311497.js js1_5/GC/regress-313276.js js1_5/GC/regress-313479.js js1_5/GC/regress-316885-02.js js1_5/GC/regress-316885-03.js js1_5/GC/regress-319980-01.js js1_5/GC/regress-331719.js js1_5/GC/regress-341877-01.js js1_5/GC/regress-341877-02.js js1_5/GC/regress-352606.js js1_5/GC/regress-383269-01.js js1_5/GC/regress-390078.js js1_5/GC/regress-418128.js js1_5/GC/regress-440558.js js1_5/GetSet/regress-375976.js js1_5/LexicalConventions/lexical-001.js js1_5/LexicalConventions/regress-177314.js js1_5/LexicalConventions/regress-469940.js js1_5/Object/regress-137000.js js1_5/Object/regress-192105.js js1_5/Object/regress-338709.js js1_5/Object/regress-382503.js js1_5/Object/regress-382532.js js1_5/Object/regress-465476.js js1_5/Object/regress-90596-003.js js1_5/Regress/regress-102725.js js1_5/Regress/regress-10278.js js1_5/Regress/regress-104077.js js1_5/Regress/regress-110286.js js1_5/Regress/regress-111557.js js1_5/Regress/regress-114491.js js1_5/Regress/regress-114493.js js1_5/Regress/regress-115436.js js1_5/Regress/regress-116228.js js1_5/Regress/regress-118849.js js1_5/Regress/regress-127243.js js1_5/Regress/regress-127557.js js1_5/Regress/regress-131510-001.js js1_5/Regress/regress-139316.js js1_5/Regress/regress-140852.js js1_5/Regress/regress-140974.js js1_5/Regress/regress-146596.js js1_5/Regress/regress-152646.js js1_5/Regress/regress-155081-2.js js1_5/Regress/regress-155081.js js1_5/Regress/regress-156354.js js1_5/Regress/regress-159334.js js1_5/Regress/regress-162392.js js1_5/Regress/regress-165201.js js1_5/Regress/regress-167328.js js1_5/Regress/regress-167658.js js1_5/Regress/regress-168347.js js1_5/Regress/regress-169559.js js1_5/Regress/regress-170193.js js1_5/Regress/regress-174709.js js1_5/Regress/regress-176125.js js1_5/Regress/regress-179524.js js1_5/Regress/regress-185165.js js1_5/Regress/regress-191633.js js1_5/Regress/regress-191668.js js1_5/Regress/regress-192414.js js1_5/Regress/regress-193418.js js1_5/Regress/regress-203402.js js1_5/Regress/regress-203841.js js1_5/Regress/regress-204210.js js1_5/Regress/regress-210682.js js1_5/Regress/regress-211590.js js1_5/Regress/regress-214761.js js1_5/Regress/regress-216320.js js1_5/Regress/regress-224956.js js1_5/Regress/regress-229006.js js1_5/Regress/regress-230216-1.js js1_5/Regress/regress-230216-2.js js1_5/Regress/regress-230216-3.js js1_5/Regress/regress-233483-2.js js1_5/Regress/regress-233483.js js1_5/Regress/regress-238881.js js1_5/Regress/regress-238945.js js1_5/Regress/regress-243174.js js1_5/Regress/regress-243389-n.js js1_5/Regress/regress-243869.js js1_5/Regress/regress-244470.js js1_5/Regress/regress-244619.js js1_5/Regress/regress-245113.js js1_5/Regress/regress-245308.js js1_5/Regress/regress-246911.js js1_5/Regress/regress-246964.js js1_5/Regress/regress-247179.js js1_5/Regress/regress-248444.js js1_5/Regress/regress-253150.js js1_5/Regress/regress-254296.js js1_5/Regress/regress-254974.js js1_5/Regress/regress-256501.js js1_5/Regress/regress-256617.js js1_5/Regress/regress-256798.js js1_5/Regress/regress-259935.js js1_5/Regress/regress-260541.js js1_5/Regress/regress-261886.js js1_5/Regress/regress-261887.js js1_5/Regress/regress-274035.js js1_5/Regress/regress-274888.js js1_5/Regress/regress-275378.js js1_5/Regress/regress-276103.js js1_5/Regress/regress-278873.js js1_5/Regress/regress-280769-3.js js1_5/Regress/regress-280769-4.js js1_5/Regress/regress-281487.js js1_5/Regress/regress-281930.js js1_5/Regress/regress-283477.js js1_5/Regress/regress-286216.js js1_5/Regress/regress-288688.js js1_5/Regress/regress-289094.js js1_5/Regress/regress-290656.js js1_5/Regress/regress-294191.js js1_5/Regress/regress-294195-01.js js1_5/Regress/regress-294195-02.js js1_5/Regress/regress-294302.js js1_5/Regress/regress-295052.js js1_5/Regress/regress-295666.js js1_5/Regress/regress-299209.js js1_5/Regress/regress-299641.js js1_5/Regress/regress-306633.js js1_5/Regress/regress-306727.js js1_5/Regress/regress-306794.js js1_5/Regress/regress-308085.js js1_5/Regress/regress-308566.js js1_5/Regress/regress-310295.js js1_5/Regress/regress-310607.js js1_5/Regress/regress-310993.js js1_5/Regress/regress-311071.js js1_5/Regress/regress-311629.js js1_5/Regress/regress-312260.js js1_5/Regress/regress-31255.js js1_5/Regress/regress-314401.js js1_5/Regress/regress-315990.js js1_5/Regress/regress-317476.js js1_5/Regress/regress-317714-01.js js1_5/Regress/regress-317714-02.js js1_5/Regress/regress-319384.js js1_5/Regress/regress-319391.js js1_5/Regress/regress-320032.js js1_5/Regress/regress-321757.js js1_5/Regress/regress-321874.js js1_5/Regress/regress-321971.js js1_5/Regress/regress-322430.js js1_5/Regress/regress-325925.js js1_5/Regress/regress-326453.js js1_5/Regress/regress-326467.js js1_5/Regress/regress-328012.js js1_5/Regress/regress-328897.js js1_5/Regress/regress-329383.js js1_5/Regress/regress-330951.js js1_5/Regress/regress-334807-01.js js1_5/Regress/regress-334807-02.js js1_5/Regress/regress-334807-03.js js1_5/Regress/regress-334807-04.js js1_5/Regress/regress-334807-05.js js1_5/Regress/regress-334807-06.js js1_5/Regress/regress-338307.js js1_5/Regress/regress-340369.js js1_5/Regress/regress-341360.js js1_5/Regress/regress-343713.js js1_5/Regress/regress-343966.js js1_5/Regress/regress-344711-n.js js1_5/Regress/regress-344804.js js1_5/Regress/regress-344959.js js1_5/Regress/regress-346237.js js1_5/Regress/regress-346801.js js1_5/Regress/regress-349482-01.js js1_5/Regress/regress-349482-02.js js1_5/Regress/regress-349592.js js1_5/Regress/regress-350253.js js1_5/Regress/regress-350312.js js1_5/Regress/regress-350415.js js1_5/Regress/regress-350529.js js1_5/Regress/regress-351116.js js1_5/Regress/regress-351515.js js1_5/Regress/regress-352009.js js1_5/Regress/regress-352208.js js1_5/Regress/regress-355829-01.js js1_5/Regress/regress-355829-02.js js1_5/Regress/regress-355829-03.js js1_5/Regress/regress-356250.js js1_5/Regress/regress-360969-01.js js1_5/Regress/regress-360969-02.js js1_5/Regress/regress-360969-03.js js1_5/Regress/regress-360969-04.js js1_5/Regress/regress-366122.js js1_5/Regress/regress-366468.js js1_5/Regress/regress-366601.js js1_5/Regress/regress-367561-01.js js1_5/Regress/regress-379245.js js1_5/Regress/regress-383674.js js1_5/Regress/regress-387951-01.js js1_5/Regress/regress-387951-02.js js1_5/Regress/regress-387951-03.js js1_5/Regress/regress-39309.js js1_5/Regress/regress-396684.js js1_5/Regress/regress-398085-01.js js1_5/Regress/regress-398085-02.js js1_5/Regress/regress-398609.js js1_5/Regress/regress-406769.js js1_5/Regress/regress-407024.js js1_5/Regress/regress-407323.js js1_5/Regress/regress-407957.js js1_5/Regress/regress-416737-01.js js1_5/Regress/regress-416737-02.js js1_5/Regress/regress-417893.js js1_5/Regress/regress-418504.js js1_5/Regress/regress-418540.js js1_5/Regress/regress-419018.js js1_5/Regress/regress-419803.js js1_5/Regress/regress-424311.js js1_5/Regress/regress-425360.js js1_5/Regress/regress-428366.js js1_5/Regress/regress-438415-01.js js1_5/Regress/regress-438415-02.js js1_5/Regress/regress-449627.js js1_5/Regress/regress-449666.js js1_5/Regress/regress-450369.js js1_5/Regress/regress-450833.js js1_5/Regress/regress-451884.js js1_5/Regress/regress-451946.js js1_5/Regress/regress-452008.js js1_5/Regress/regress-452170.js js1_5/Regress/regress-452333.js js1_5/Regress/regress-452336.js js1_5/Regress/regress-452346.js js1_5/Regress/regress-452495.js js1_5/Regress/regress-452573-01.js js1_5/Regress/regress-452573-02.js js1_5/Regress/regress-452713.js js1_5/Regress/regress-452724-01.js js1_5/Regress/regress-452724-02.js js1_5/Regress/regress-452742-01.js js1_5/Regress/regress-452742-02.js js1_5/Regress/regress-452853.js js1_5/Regress/regress-452884-01.js js1_5/Regress/regress-452884-02.js js1_5/Regress/regress-453024.js js1_5/Regress/regress-453173.js js1_5/Regress/regress-453397.js js1_5/Regress/regress-453701.js js1_5/Regress/regress-453747.js js1_5/Regress/regress-454682.js js1_5/Regress/regress-454981.js js1_5/Regress/regress-455605.js js1_5/Regress/regress-455748.js js1_5/Regress/regress-455758-01.js js1_5/Regress/regress-455758-02.js js1_5/Regress/regress-455775.js js1_5/Regress/regress-456470.js js1_5/Regress/regress-456477-01.js js1_5/Regress/regress-456477-02.js js1_5/Regress/regress-456494.js js1_5/Regress/regress-456540-01.js js1_5/Regress/regress-456540-02.js js1_5/Regress/regress-457065-03.js js1_5/Regress/regress-457456.js js1_5/Regress/regress-457778.js js1_5/Regress/regress-458851.js js1_5/Regress/regress-459085.js js1_5/Regress/regress-459628.js js1_5/Regress/regress-459990.js js1_5/Regress/regress-460024.js js1_5/Regress/regress-460117.js js1_5/Regress/regress-460886-01.js js1_5/Regress/regress-460886-02.js js1_5/Regress/regress-461307.js js1_5/Regress/regress-461723.js js1_5/Regress/regress-462292.js js1_5/Regress/regress-462879.js js1_5/Regress/regress-462989.js js1_5/Regress/regress-463259.js js1_5/Regress/regress-463782.js js1_5/Regress/regress-464334.js js1_5/Regress/regress-464862.js js1_5/Regress/regress-465013.js js1_5/Regress/regress-465132.js js1_5/Regress/regress-465133.js js1_5/Regress/regress-465135.js js1_5/Regress/regress-465136.js js1_5/Regress/regress-465137.js js1_5/Regress/regress-465262.js js1_5/Regress/regress-465272.js js1_5/Regress/regress-465347.js js1_5/Regress/regress-465366.js js1_5/Regress/regress-466262.js js1_5/Regress/regress-466747.js js1_5/Regress/regress-469044.js js1_5/Regress/regress-470061.js js1_5/Regress/regress-470187-01.js js1_5/Regress/regress-470187-02.js js1_5/Regress/regress-470758-01.js js1_5/Regress/regress-470758-02.js js1_5/Regress/regress-472533.js js1_5/Regress/regress-475645-01.js js1_5/Regress/regress-475645-02.js js1_5/Regress/regress-476049.js js1_5/Regress/regress-476192.js js1_5/Regress/regress-477733.js js1_5/Regress/regress-477758.js js1_5/Regress/regress-478314.js js1_5/Regress/regress-479353.js js1_5/Regress/regress-480147.js js1_5/Regress/regress-480244.js js1_5/Regress/regress-481436.js js1_5/Regress/regress-482421.js js1_5/Regress/regress-482783.js js1_5/Regress/regress-483103.js js1_5/Regress/regress-501124.js js1_5/Regress/regress-504078.js js1_5/Regress/regress-57043.js js1_5/Regress/regress-58116.js js1_5/Regress/regress-68498-001.js js1_5/Regress/regress-68498-002.js js1_5/Regress/regress-68498-004.js js1_5/Regress/regress-69607.js js1_5/Regress/regress-71107.js js1_5/Regress/regress-76054.js js1_5/Regress/regress-80981.js js1_5/Regress/regress-82306.js js1_5/Regress/regress-89443.js js1_5/Regress/regress-89474.js js1_5/Regress/regress-90445.js js1_5/Regress/regress-96526-001.js js1_5/Regress/regress-96526-003.js js1_5/Regress/regress-98901.js js1_5/Scope/regress-154693.js js1_5/Scope/regress-184107.js js1_5/Scope/regress-185485.js js1_5/Scope/regress-191276.js js1_5/Scope/regress-192226.js js1_5/Scope/regress-202678-001.js js1_5/Scope/regress-202678-002.js js1_5/Scope/regress-208496-001.js js1_5/Scope/regress-208496-002.js js1_5/Scope/regress-220362.js js1_5/Scope/regress-446026-02.js js1_5/Scope/regress-77578-001.js js1_5/Scope/scope-002.js js1_5/Scope/scope-003.js js1_5/Scope/scope-004.js js1_5/String/regress-107771.js js1_5/String/regress-112626.js js1_5/String/regress-179068.js js1_5/decompilation/regress-344120.js js1_5/decompilation/regress-349489.js js1_5/decompilation/regress-349663.js js1_5/decompilation/regress-350670.js js1_5/decompilation/regress-351336.js js1_5/decompilation/regress-351625.js js1_5/decompilation/regress-351626.js js1_5/decompilation/regress-352022.js js1_5/decompilation/regress-352360.js js1_5/decompilation/regress-352873-01.js js1_5/decompilation/regress-352873-02.js js1_5/decompilation/regress-353120.js js1_5/decompilation/regress-354878.js js1_5/decompilation/regress-354910.js js1_5/decompilation/regress-371692.js js1_5/decompilation/regress-373678.js js1_5/decompilation/regress-375639.js js1_5/decompilation/regress-383721.js js1_5/decompilation/regress-406555.js js1_5/decompilation/regress-437288-02.js js1_5/decompilation/regress-443071-01.js js1_5/decompilation/regress-456964-01.js js1_5/decompilation/regress-457093-01.js js1_5/decompilation/regress-460116-01.js js1_5/decompilation/regress-460116-02.js js1_5/decompilation/regress-460116-03.js js1_5/decompilation/regress-461108.js js1_5/decompilation/regress-461110.js js1_5/decompilation/regress-461111.js js1_5/extensions/catchguard-001-n.js js1_5/extensions/catchguard-001.js js1_5/extensions/catchguard-002.js js1_5/extensions/catchguard-003.js js1_5/extensions/getset-004.js js1_5/extensions/getset-005.js js1_5/extensions/getset-006.js js1_5/extensions/no-such-method.js js1_5/extensions/regress-104077.js js1_5/extensions/regress-178722.js js1_5/extensions/regress-220584.js js1_5/extensions/regress-225831.js js1_5/extensions/regress-226078.js js1_5/extensions/regress-226507.js js1_5/extensions/regress-231518.js js1_5/extensions/regress-237461.js js1_5/extensions/regress-245148.js js1_5/extensions/regress-245795.js js1_5/extensions/regress-255245.js js1_5/extensions/regress-291213.js js1_5/extensions/regress-300079.js js1_5/extensions/regress-304897.js js1_5/extensions/regress-311161.js js1_5/extensions/regress-311583.js js1_5/extensions/regress-311792-01.js js1_5/extensions/regress-311792-02.js js1_5/extensions/regress-312278.js js1_5/extensions/regress-313500.js js1_5/extensions/regress-313630.js js1_5/extensions/regress-313763.js js1_5/extensions/regress-313803.js js1_5/extensions/regress-313938.js js1_5/extensions/regress-314874.js js1_5/extensions/regress-315509-02.js js1_5/extensions/regress-319683.js js1_5/extensions/regress-322957.js js1_5/extensions/regress-325269.js js1_5/extensions/regress-327608.js js1_5/extensions/regress-328443.js js1_5/extensions/regress-328556.js js1_5/extensions/regress-338804-01.js js1_5/extensions/regress-338804-03.js js1_5/extensions/regress-339685.js js1_5/extensions/regress-340199.js js1_5/extensions/regress-341956-01.js js1_5/extensions/regress-341956-02.js js1_5/extensions/regress-341956-03.js js1_5/extensions/regress-346494-01.js js1_5/extensions/regress-350312-01.js js1_5/extensions/regress-350312-02.js js1_5/extensions/regress-350312-03.js js1_5/extensions/regress-351102-01.js js1_5/extensions/regress-351102-02.js js1_5/extensions/regress-351102-06.js js1_5/extensions/regress-351973.js js1_5/extensions/regress-352261.js js1_5/extensions/regress-352281.js js1_5/extensions/regress-352291.js js1_5/extensions/regress-354297.js js1_5/extensions/regress-354541-01.js js1_5/extensions/regress-354541-03.js js1_5/extensions/regress-355982.js js1_5/extensions/regress-356402.js js1_5/extensions/regress-363258.js js1_5/extensions/regress-363988.js js1_5/extensions/regress-365527.js js1_5/extensions/regress-365692.js js1_5/extensions/regress-366288.js js1_5/extensions/regress-366292.js js1_5/extensions/regress-366396.js js1_5/extensions/regress-367118-01.js js1_5/extensions/regress-367118-02.js js1_5/extensions/regress-367120-01.js js1_5/extensions/regress-367120-02.js js1_5/extensions/regress-367121.js js1_5/extensions/regress-367501-01.js js1_5/extensions/regress-367501-02.js js1_5/extensions/regress-367501-03.js js1_5/extensions/regress-367501-04.js js1_5/extensions/regress-367589.js js1_5/extensions/regress-369404.js js1_5/extensions/regress-369696-01.js js1_5/extensions/regress-369696-03.js js1_5/extensions/regress-372309.js js1_5/extensions/regress-374589.js js1_5/extensions/regress-375183.js js1_5/extensions/regress-375344.js js1_5/extensions/regress-380889.js js1_5/extensions/regress-385134.js js1_5/extensions/regress-385393-02.js js1_5/extensions/regress-390597.js js1_5/extensions/regress-390598.js js1_5/extensions/regress-394967.js js1_5/extensions/regress-396326.js js1_5/extensions/regress-406572.js js1_5/extensions/regress-407019.js js1_5/extensions/regress-407501.js js1_5/extensions/regress-407720.js js1_5/extensions/regress-412926.js js1_5/extensions/regress-414755.js js1_5/extensions/regress-416354.js js1_5/extensions/regress-416460.js js1_5/extensions/regress-416834.js js1_5/extensions/regress-422137.js js1_5/extensions/regress-422592.js js1_5/extensions/regress-426711.js js1_5/extensions/regress-427196-01.js js1_5/extensions/regress-427196-02.js js1_5/extensions/regress-427196-03.js js1_5/extensions/regress-429264.js js1_5/extensions/regress-431428.js js1_5/extensions/regress-434837-01.js js1_5/extensions/regress-435497-01.js js1_5/extensions/regress-435497-02.js js1_5/extensions/regress-435497-03.js js1_5/extensions/regress-436741.js js1_5/extensions/regress-437288-01.js js1_5/extensions/regress-44009.js js1_5/extensions/regress-443569.js js1_5/extensions/regress-446386.js js1_5/extensions/regress-452168.js js1_5/extensions/regress-452329.js js1_5/extensions/regress-452338.js js1_5/extensions/regress-452372.js js1_5/extensions/regress-452565.js js1_5/extensions/regress-453249.js js1_5/extensions/regress-454040.js js1_5/extensions/regress-454704.js js1_5/extensions/regress-455380.js js1_5/extensions/regress-455408.js js1_5/extensions/regress-459606.js js1_5/extensions/regress-465276.js js1_5/extensions/regress-469625.js js1_5/extensions/regress-469761.js js1_5/extensions/regress-472599.js js1_5/extensions/regress-476447.js js1_5/extensions/regress-479487.js js1_5/extensions/regress-479551.js js1_5/extensions/regress-480579.js js1_5/extensions/regress-481516.js js1_5/extensions/regress-488995.js js1_5/extensions/regress-50447.js js1_5/extensions/regress-90596-001.js js1_5/extensions/regress-90596-002.js js1_5/extensions/regress-96284-001.js js1_5/extensions/regress-96284-002.js js1_5/extensions/scope-001.js js1_6/Array/filter.js js1_6/Array/regress-290592.js js1_6/Array/regress-304828.js js1_6/Array/regress-305002.js js1_6/Array/regress-310425-01.js js1_6/Array/regress-310425-02.js js1_6/Array/regress-320887.js js1_6/Array/regress-352742-01.js js1_6/Array/regress-352742-02.js js1_6/Array/regress-386030.js js1_6/Array/regress-415451.js js1_6/Array/regress-415540.js js1_6/Regress/regress-301574.js js1_6/Regress/regress-311157-01.js js1_6/Regress/regress-311157-02.js js1_6/Regress/regress-314887.js js1_6/Regress/regress-320172.js js1_6/Regress/regress-351795.js js1_6/Regress/regress-352271.js js1_6/Regress/regress-353078.js js1_6/Regress/regress-372565.js js1_6/Regress/regress-378492.js js1_6/Regress/regress-475469.js js1_6/Regress/regress-476655.js js1_6/decompilation/regress-352084.js js1_6/extensions/regress-385393-08.js js1_6/extensions/regress-455464-01.js js1_6/extensions/regress-455464-02.js js1_6/extensions/regress-455464-03.js js1_6/extensions/regress-455464-04.js js1_6/extensions/regress-457521.js js1_6/extensions/regress-472508.js js1_6/extensions/regress-475144.js js1_6/extensions/regress-479567.js js1_7/GC/regress-341675.js js1_7/block/order-of-operation.js js1_7/block/regress-341939.js js1_7/block/regress-343765.js js1_7/block/regress-344139.js js1_7/block/regress-344262.js js1_7/block/regress-344370.js js1_7/block/regress-345542.js js1_7/block/regress-348685.js js1_7/block/regress-349283.js js1_7/block/regress-349298.js js1_7/block/regress-349507.js js1_7/block/regress-349653.js js1_7/block/regress-349962.js js1_7/block/regress-350279.js js1_7/block/regress-350730.js js1_7/block/regress-350793-01.js js1_7/block/regress-351497.js js1_7/block/regress-351606.js js1_7/block/regress-352092.js js1_7/block/regress-352185.js js1_7/block/regress-352212.js js1_7/block/regress-352267.js js1_7/block/regress-352616.js js1_7/block/regress-352624.js js1_7/block/regress-352907.js js1_7/block/regress-357754.js js1_7/block/regress-358508.js js1_7/block/regress-376410.js js1_7/block/regress-396900.js js1_7/block/regress-411279.js js1_7/decompilation/regress-349633.js js1_7/decompilation/regress-350810.js js1_7/decompilation/regress-352015.js js1_7/decompilation/regress-352025.js js1_7/decompilation/regress-352198.js js1_7/decompilation/regress-352269.js js1_7/decompilation/regress-352272.js js1_7/decompilation/regress-352283.js js1_7/decompilation/regress-352732.js js1_7/decompilation/regress-355635.js js1_7/decompilation/regress-355786.js js1_7/decompilation/regress-375794.js js1_7/decompilation/regress-380506.js js1_7/decompilation/regress-410649.js js1_7/expressions/destructuring-scope.js js1_7/expressions/regress-346203.js js1_7/expressions/regress-346645-01.js js1_7/expressions/regress-346645-02.js js1_7/expressions/regress-346645-03.js js1_7/expressions/regress-349624.js js1_7/expressions/regress-349818.js js1_7/expressions/regress-418051.js js1_7/expressions/regress-421806.js js1_7/expressions/regress-451340.js js1_7/extensions/basic-Iterator.js js1_7/extensions/basic-for-each.js js1_7/extensions/basic-for-in.js js1_7/extensions/destructuring-order.js js1_7/extensions/iterator-ctor.js js1_7/extensions/regress-346021.js js1_7/extensions/regress-346642-02.js js1_7/extensions/regress-346773.js js1_7/extensions/regress-349619.js js1_7/extensions/regress-350312.js js1_7/extensions/regress-351070-02.js js1_7/extensions/regress-352797-01.js js1_7/extensions/regress-352885-01.js js1_7/extensions/regress-352885-02.js js1_7/extensions/regress-353214-02.js js1_7/extensions/regress-354499-01.js js1_7/extensions/regress-354499-02.js js1_7/extensions/regress-354945-01.js js1_7/extensions/regress-355052-01.js js1_7/extensions/regress-355052-02.js js1_7/extensions/regress-355052-03.js js1_7/extensions/regress-355410.js js1_7/extensions/regress-355512.js js1_7/extensions/regress-355578.js js1_7/extensions/regress-355583.js js1_7/extensions/regress-363040-01.js js1_7/extensions/regress-363040-02.js js1_7/extensions/regress-366668-01.js js1_7/extensions/regress-366668-02.js js1_7/extensions/regress-372364.js js1_7/extensions/regress-387955-01.js js1_7/extensions/regress-387955-02.js js1_7/extensions/regress-392308.js js1_7/extensions/regress-396326.js js1_7/extensions/regress-429266.js js1_7/extensions/regress-455982-01.js js1_7/extensions/regress-455982-02.js js1_7/extensions/regress-469234.js js1_7/extensions/regress-469405-01.js js1_7/extensions/regress-469405-02.js js1_7/extensions/regress-470176.js js1_7/extensions/regress-470300-01.js js1_7/extensions/regress-470300-02.js js1_7/extensions/regress-474771-01.js js1_7/extensions/regress-474771-02.js js1_7/extensions/regress-477048.js js1_7/geniter/326466-01.js js1_7/geniter/builtin-Iterator-function.js js1_7/geniter/evens.js js1_7/geniter/fibonacci-matrix-generator.js js1_7/geniter/iterator-toString.js js1_7/geniter/message-value-passing.js js1_7/geniter/multiple-close.js js1_7/geniter/nested-yield.js js1_7/geniter/pi-generator.js js1_7/geniter/regress-345736.js js1_7/geniter/regress-345855.js js1_7/geniter/regress-345879-01.js js1_7/geniter/regress-345879-02.js js1_7/geniter/regress-347593.js js1_7/geniter/regress-349012-02.js js1_7/geniter/regress-349012-03.js js1_7/geniter/regress-349012-04.js js1_7/geniter/regress-349012-05.js js1_7/geniter/regress-349023-01.js js1_7/geniter/regress-349023-02.js js1_7/geniter/regress-349023-03.js js1_7/geniter/regress-349362.js js1_7/geniter/regress-349851.js js1_7/geniter/regress-350621.js js1_7/geniter/regress-350809.js js1_7/geniter/regress-351120.js js1_7/geniter/regress-351514.js js1_7/geniter/regress-352197.js js1_7/geniter/regress-352605.js js1_7/geniter/regress-352876.js js1_7/geniter/regress-355834.js js1_7/geniter/regress-359062.js js1_7/geniter/regress-366941.js js1_7/geniter/regress-382335.js js1_7/geniter/regress-387871.js js1_7/geniter/regress-390918.js js1_7/geniter/regress-392310.js js1_7/geniter/regress-466206.js js1_7/geniter/send-no-rhs.js js1_7/geniter/sequential-yields.js js1_7/geniter/simple-fib.js js1_7/geniter/throw-after-close.js js1_7/geniter/throw-forever.js js1_7/geniter/unreachable-yield.js js1_7/geniter/yield-undefined.js js1_7/iterable/regress-340526-01.js js1_7/iterable/regress-341496.js js1_7/iterable/regress-341499.js js1_7/iterable/regress-341510.js js1_7/iterable/regress-341815.js js1_7/iterable/regress-341821.js js1_7/iterable/regress-354750-01.js js1_7/iterable/regress-355025.js js1_7/iterable/regress-355075-01.js js1_7/iterable/regress-355075-02.js js1_7/iterable/regress-355090.js js1_7/iterable/regress-412467.js js1_7/iterable/regress-415922.js js1_7/lexical/regress-346642-04.js js1_7/regress/regress-352640-01.js js1_7/regress/regress-352640-02.js js1_7/regress/regress-352640-03.js js1_7/regress/regress-352640-04.js js1_7/regress/regress-352797-02.js js1_7/regress/regress-352870-03.js js1_7/regress/regress-353079.js js1_7/regress/regress-355023.js js1_7/regress/regress-355832-01.js js1_7/regress/regress-355832-02.js js1_7/regress/regress-361566.js js1_7/regress/regress-363040-02.js js1_7/regress/regress-369666-01.js js1_7/regress/regress-369666-02.js js1_7/regress/regress-372331.js js1_7/regress/regress-373827-01.js js1_7/regress/regress-373827-02.js js1_7/regress/regress-373828.js js1_7/regress/regress-379442.js js1_7/regress/regress-385393-05.js js1_7/regress/regress-387951.js js1_7/regress/regress-407727-01.js js1_7/regress/regress-407727-02.js js1_7/regress/regress-407957.js js1_7/regress/regress-414553.js js1_7/regress/regress-416601.js js1_7/regress/regress-416705.js js1_7/regress/regress-418641.js js1_7/regress/regress-419803.js js1_7/regress/regress-428706.js js1_7/regress/regress-428708.js js1_7/regress/regress-452703.js js1_7/regress/regress-452960.js js1_7/regress/regress-453049.js js1_7/regress/regress-453051.js js1_7/regress/regress-453411.js js1_7/regress/regress-461235.js js1_7/regress/regress-461945.js js1_7/regress/regress-462071.js js1_7/regress/regress-462282.js js1_7/regress/regress-462388.js js1_7/regress/regress-462407.js js1_7/regress/regress-464403.js js1_7/regress/regress-465236.js js1_7/regress/regress-465424.js js1_7/regress/regress-465484.js js1_7/regress/regress-465686.js js1_7/regress/regress-469239-01.js js1_7/regress/regress-469239-02.js js1_7/regress/regress-470223.js js1_7/regress/regress-470388-01.js js1_7/regress/regress-470388-02.js js1_7/regress/regress-470388-03.js js1_7/regress/regress-474771.js js1_8/decompilation/regress-260106.js js1_8/decompilation/regress-381504.js js1_8/decompilation/regress-461233.js js1_8/decompilation/regress-469625-01.js js1_8/extensions/dekker.js js1_8/extensions/for-in.js js1_8/extensions/lamport.js js1_8/extensions/peterson.js js1_8/extensions/regress-378789.js js1_8/extensions/regress-385393-01.js js1_8/extensions/regress-385393-10.js js1_8/extensions/regress-385393-11.js js1_8/extensions/regress-415721.js js1_8/extensions/regress-417131.js js1_8/extensions/regress-417817.js js1_8/extensions/regress-419091.js js1_8/extensions/regress-422269.js js1_8/extensions/regress-445818.js js1_8/extensions/regress-446169-01.js js1_8/extensions/regress-446169-02.js js1_8/extensions/regress-452476.js js1_8/extensions/regress-452913.js js1_8/extensions/regress-454744.js js1_8/extensions/regress-455973.js js1_8/extensions/regress-465337.js js1_8/extensions/regress-471197.js js1_8/extensions/regress-475971.js js1_8/extensions/regress-476427.js js1_8/extensions/regress-476653.js js1_8/extensions/regress-476871-02.js js1_8/extensions/regress-479252.js js1_8/extensions/regress-479381.js js1_8/extensions/simple-tree.js js1_8/genexps/regress-347739.js js1_8/regress/regress-404734.js js1_8/regress/regress-427798.js js1_8/regress/regress-433279-01.js js1_8/regress/regress-433279-02.js js1_8/regress/regress-433279-03.js js1_8/regress/regress-452491.js js1_8/regress/regress-457065-01.js js1_8/regress/regress-457065-02.js js1_8/regress/regress-458076.js js1_8/regress/regress-459389.js js1_8/regress/regress-461930.js js1_8/regress/regress-461932.js js1_8/regress/regress-463334-01.js js1_8/regress/regress-463334-02.js js1_8/regress/regress-463783.js js1_8/regress/regress-464092-01.js js1_8/regress/regress-464092-02.js js1_8/regress/regress-464096.js js1_8/regress/regress-464418.js js1_8/regress/regress-464978.js js1_8/regress/regress-465220.js js1_8/regress/regress-465234.js js1_8/regress/regress-465239.js js1_8/regress/regress-465241.js js1_8/regress/regress-465249.js js1_8/regress/regress-465261.js js1_8/regress/regress-465308.js js1_8/regress/regress-465454.js js1_8/regress/regress-465460-01.js js1_8/regress/regress-465460-02.js js1_8/regress/regress-465460-03.js js1_8/regress/regress-465460-04.js js1_8/regress/regress-465460-05.js js1_8/regress/regress-465460-06.js js1_8/regress/regress-465460-07.js js1_8/regress/regress-465460-08.js js1_8/regress/regress-465460-09.js js1_8/regress/regress-465460-10.js js1_8/regress/regress-465460-11.js js1_8/regress/regress-465460-12.js js1_8/regress/regress-465483.js js1_8/regress/regress-465567-01.js js1_8/regress/regress-465567-02.js js1_8/regress/regress-465688.js js1_8/regress/regress-466128.js js1_8/regress/regress-466787.js js1_8/regress/regress-467495-01.js js1_8/regress/regress-467495-02.js js1_8/regress/regress-467495-04.js js1_8/regress/regress-467495-06.js js1_8/regress/regress-468711.js js1_8/regress/regress-469547.js js1_8/regress/regress-469625-02.js js1_8/regress/regress-471660.js js1_8/regress/regress-472450-01.js js1_8/regress/regress-472450-02.js js1_8/regress/regress-472528-01.js js1_8/regress/regress-472528-02.js js1_8/regress/regress-472703.js js1_8/regress/regress-474769.js js1_8/regress/regress-474771.js js1_8/regress/regress-474935.js js1_8/regress/regress-476655.js js1_8/regress/regress-477581.js js1_8/regress/regress-478205.js js1_8/regress/regress-479353.js js1_8/regress/regress-479740.js js1_8/regress/regress-481800.js js1_8/regress/regress-483749.js js1_8_1/JSON/regress-458959.js js1_8_1/JSON/regress-459293.js js1_8_1/decompilation/regress-371802.js js1_8_1/extensions/regress-466905-04.js js1_8_1/extensions/regress-466905-05.js js1_8_1/extensions/regress-477158.js js1_8_1/extensions/regress-477187.js js1_8_1/regress/regress-452498-006.js js1_8_1/regress/regress-452498-027.js js1_8_1/regress/regress-452498-030.js js1_8_1/regress/regress-452498-038.js js1_8_1/regress/regress-452498-040.js js1_8_1/regress/regress-452498-050.js js1_8_1/regress/regress-452498-051.js js1_8_1/regress/regress-452498-052-a.js js1_8_1/regress/regress-452498-058.js js1_8_1/regress/regress-452498-062.js js1_8_1/regress/regress-452498-063.js js1_8_1/regress/regress-452498-071.js js1_8_1/regress/regress-452498-072.js js1_8_1/regress/regress-452498-073.js js1_8_1/regress/regress-452498-074.js js1_8_1/regress/regress-452498-075.js js1_8_1/regress/regress-452498-076.js js1_8_1/regress/regress-452498-077.js js1_8_1/regress/regress-452498-079.js js1_8_1/regress/regress-452498-101.js js1_8_1/regress/regress-452498-102.js js1_8_1/regress/regress-452498-103.js js1_8_1/regress/regress-452498-104.js js1_8_1/regress/regress-452498-108.js js1_8_1/regress/regress-452498-111.js js1_8_1/regress/regress-452498-114-a.js js1_8_1/regress/regress-452498-114.js js1_8_1/regress/regress-452498-116.js js1_8_1/regress/regress-452498-118.js js1_8_1/regress/regress-452498-121.js js1_8_1/regress/regress-452498-123.js js1_8_1/regress/regress-452498-129.js js1_8_1/regress/regress-452498-130.js js1_8_1/regress/regress-452498-131.js js1_8_1/regress/regress-452498-135-a.js js1_8_1/regress/regress-452498-155.js js1_8_1/regress/regress-452498-168-1.js js1_8_1/regress/regress-452498-176.js js1_8_1/regress/regress-452498-181.js js1_8_1/regress/regress-452498-184.js js1_8_1/regress/regress-452498-192.js js1_8_1/regress/regress-466905-01.js js1_8_1/regress/regress-466905-02.js js1_8_1/regress/regress-479430-01.js js1_8_1/regress/regress-479430-02.js js1_8_1/regress/regress-479430-03.js js1_8_1/regress/regress-479430-04.js js1_8_1/regress/regress-479430-05.js js1_8_1/regress/regress-495907.js js1_8_1/trace/math-trace-tests.js js1_8_1/trace/regress-458838.js js1_8_1/trace/regress-462459-01.js js1_8_1/trace/regress-462459-02.js js1_8_1/trace/regress-462459-03.js js1_8_1/trace/regress-462459-04.js js1_8_1/trace/regress-462459-05.js js1_8_1/trace/regress-462459-06.js js1_8_1/trace/regress-462459-07.js js1_8_1/trace/regress-462459-08.js js1_8_1/trace/regress-462459-09.js js1_8_1/trace/regress-462459-10.js js1_8_1/trace/regress-462459-11.js js1_8_1/trace/regress-462459-12.js js1_8_1/trace/regress-471635.js js1_8_1/trace/regress-489682.js lc2/Arrays/array-001.js lc2/Arrays/array-002.js lc2/Arrays/array-003.js lc2/Arrays/array-004.js lc2/Arrays/array-005.js lc2/Arrays/array-006-n.js lc2/Arrays/array-007-n.js lc2/Classes/class-001.js lc2/Classes/class-002.js lc2/JSToJava/character-001.js lc2/JSToJava/double-001.js lc2/JSToJava/double-002.js lc2/JSToJava/float-001.js lc2/JSToJava/float-002.js lc2/JSToJava/integer-001.js lc2/JSToJava/integer-002.js lc2/JSToJava/long-001.js lc2/JSToJava/long-002.js lc2/JSToJava/long-003-n.js lc2/JSToJava/short-001.js lc2/JSToJava/short-002.js lc2/JSToJava/short-003-n.js lc2/JavaToJS/String-001.js lc2/JavaToJS/boolean-001.js lc2/JavaToJS/boolean-003.js lc2/JavaToJS/boolean-004.js lc2/JavaToJS/boolean-005.js lc2/JavaToJS/char-001.js lc2/JavaToJS/char-002.js lc2/JavaToJS/enum-001.js lc2/JavaToJS/enum-002.js lc2/JavaToJS/null-001.js lc2/JavaToJS/number-001.js lc2/JavaToJS/number-002.js lc2/Methods/method-001.js lc2/Methods/method-002.js lc2/Methods/method-003.js lc2/Methods/method-004-n.js lc2/Methods/method-005.js lc2/Methods/method-006.js lc2/Methods/println-001.js lc2/Objects/object-001.js lc2/Objects/object-002.js lc2/Objects/object-003.js lc2/Objects/object-004.js lc2/Objects/object-005.js lc2/Objects/object-006.js lc2/Packages/package-001.js lc2/Packages/package-002.js lc2/Packages/package-003.js lc2/Packages/package-004.js lc2/Packages/package-005.js lc2/Packages/package-006.js lc2/Packages/package-007-n.js lc2/Packages/package-008-n.js lc2/misc/constructor.js lc2/misc/wrapUnwrap.js lc3/ArrayMethods/byte-001.js lc3/ArrayMethods/byte-002.js lc3/ConvertBoolean/boolean-005-n.js lc3/ConvertBoolean/boolean-006-n.js lc3/ConvertBoolean/boolean-007-n.js lc3/ConvertBoolean/boolean-008-n.js lc3/ConvertBoolean/boolean-009-n.js lc3/ConvertBoolean/boolean-010-n.js lc3/ConvertBoolean/boolean-011-n.js lc3/ConvertBoolean/boolean-012-n.js lc3/ConvertBoolean/boolean-013-n.js lc3/ConvertNull/null-002.js lc3/ConvertNull/null-003-n.js lc3/ConvertNull/null-004-n.js lc3/ConvertNull/null-006-n.js lc3/ConvertString/string-004-n.js lc3/ConvertString/string-005-n.js lc3/ConvertString/string-007-n.js lc3/ConvertUndefined/undefined-001-n.js lc3/JSBoolean/boolean-002-n.js lc3/JSBoolean/boolean-003-n.js lc3/JSBoolean/boolean-004-n.js lc3/JSBoolean/boolean-005-n.js lc3/JSBoolean/boolean-006-n.js lc3/JSBoolean/boolean-007-n.js lc3/JSBoolean/boolean-008-n.js lc3/JSNull/ToBoolean-001-n.js lc3/JSNull/ToFloat-001-n.js lc3/JSNull/ToLong-001-n.js lc3/JSNull/ToNumber-001-n.js lc3/JSNumber/ToByte-002-n.js lc3/JSNumber/ToByte-003-n.js lc3/JSNumber/ToByte-005-n.js lc3/JSNumber/ToChar-002-n.js lc3/JSNumber/ToChar-003-n.js lc3/JSNumber/ToChar-005-n.js lc3/JSNumber/ToChar-006-n.js lc3/JSNumber/ToInt-002-n.js lc3/JSNumber/ToInt-003-n.js lc3/JSNumber/ToInt-005-n.js lc3/JSNumber/ToLong-002-n.js lc3/JSNumber/ToLong-003-n.js lc3/JSNumber/ToLong-004-n.js lc3/JSNumber/ToLong-005-n.js lc3/JSNumber/ToLong-006-n.js lc3/JSNumber/ToLong-007-n.js lc3/JSNumber/ToLong-008-n.js lc3/JSNumber/ToLong-009-n.js lc3/JSNumber/ToLong-010-n.js lc3/JSNumber/ToLong-011-n.js lc3/JSNumber/ToShort-002-n.js lc3/JSNumber/ToShort-003-n.js lc3/JSNumber/ToShort-005-n.js lc3/JSObject/ToDouble-002-n.js lc3/JSObject/ToDouble-003-n.js lc3/JSObject/ToFloat-002-n.js lc3/JSObject/ToFloat-003-n.js lc3/JSUndefined/undefined-002-n.js lc3/JSUndefined/undefined-003-n.js lc3/JSUndefined/undefined-004-n.js lc3/JSUndefined/undefined-005-n.js lc3/JSUndefined/undefined-006-n.js lc3/JSUndefined/undefined-007-n.js lc3/JSUndefined/undefined-008-n.js lc3/JSUndefined/undefined-009-n.js lc3/JSUndefined/undefined-010-n.js lc3/JavaArray/ToArray-002-n.js lc3/JavaArray/ToBoolean-001-n.js lc3/JavaObject/JavaObjectToBoolean-001-n.js lc3/JavaObject/JavaObjectToBoolean-002-n.js lc3/JavaObject/JavaObjectToByte-002-n.js lc3/JavaObject/JavaObjectToByte-003-n.js lc3/JavaObject/JavaObjectToByte-004-n.js lc3/JavaObject/JavaObjectToByte-007-n.js lc3/JavaObject/JavaObjectToByte-008-n.js lc3/JavaObject/JavaObjectToChar-003-n.js lc3/JavaObject/JavaObjectToChar-005-n.js lc3/JavaObject/JavaObjectToChar-006-n.js lc3/JavaObject/JavaObjectToInt-002-n.js lc3/JavaObject/JavaObjectToInt-003-n.js lc3/JavaObject/JavaObjectToInt-004-n.js lc3/JavaObject/JavaObjectToLong-002-n.js lc3/JavaObject/JavaObjectToLong-003-n.js lc3/JavaObject/JavaObjectToLong-004-n.js lc3/JavaObject/JavaObjectToLong-006-n.js lc3/JavaObject/JavaObjectToShort-002-n.js lc3/JavaObject/JavaObjectToShort-003-n.js lc3/JavaObject/JavaObjectToShort-004-n.js lc3/StringMethods/string-001.js lc3/instanceof/instanceof-001.js closure-compiler-20130227+dfsg1/rhino/testsrc/test262.properties0000644000175000017500000002463614433667662022551 0ustar apoapobuilt-ins/String ! prototype/replace/15.5.4.11-1.js ! prototype/replace/S15.5.4.11_A12.js ! prototype/S15.5.4_A1.js ! prototype/S15.5.4_A2.js ! prototype/S15.5.4_A3.js ! prototype/split/S15.5.4.14_A2_T37.js ! prototype/substring/S15.5.4.15_A1_T5.js ! raw ! fromCodePoint ! prototype/Symbol.iterator/this-val-non-obj-coercible.js ! prototype/endsWith/return-abrupt-from-searchstring-regexp-test.js ! prototype/includes/return-abrupt-from-searchstring-regexp-test.js ! prototype/includes/return-true-if-searchstring-is-empty.js ! prototype/normalize/return-normalized-string-using-default-parameter.js ! prototype/replace/cstm-replace-get-err.js ! prototype/replace/cstm-replace-invocation.js ! prototype/startsWith/return-abrupt-from-searchstring-regexp-test.js ! prototype/toLocaleLowerCase/special_casing_conditional.js ! prototype/toLowerCase/special_casing_conditional.js built-ins/StringIteratorPrototype language/statements/for-of ! Array.prototype.entries.js ! Array.prototype.keys.js ! body-dstr-assign-error.js ! body-put-error.js ! for-of/break ! const-bound-names-fordecl-tdz-for-of.js ! const-fresh-binding-per-iteration-for-of.js ! for-of/continue ! for-of/generator ! generic-iterable.js ! iterator-as-proxy.js ! iterator-close-get-method-error.js ! iterator-close-non-object.js ! iterator-close-via-break.js ! iterator-close-via-return.js ! iterator-close-via-throw.js ! iterator-next-result-type.js ! let-bound-names-fordecl-tdz-for-of.js ! let-fresh-binding-per-iteration-for-of.js ! for-of/map ! nested.js ! for-of/return ! for-of/set ! for-of/throw ! for-of/yield built-ins/Array # incorrect length handling ! every/15.4.4.16-3-7 ! every/15.4.4.16-3-8 ! every/15.4.4.16-3-12 ! every/15.4.4.16-3-14 ! every/15.4.4.16-3-25 ! every/15.4.4.16-3-29 ! filter/15.4.4.20-3-7 ! filter/15.4.4.20-3-12 ! filter/15.4.4.20-3-25 ! forEach/15.4.4.18-3-7 ! forEach/15.4.4.18-3-12 ! forEach/15.4.4.18-3-25 ! indexOf/15.4.4.14-3-7 ! indexOf/15.4.4.14-3-8 ! indexOf/15.4.4.14-3-12 ! indexOf/15.4.4.14-3-14 ! indexOf/15.4.4.14-3-25 ! indexOf/15.4.4.14-3-28 ! indexOf/15.4.4.14-3-29 ! join/S15.4.4.5_A4_T3 ! lastIndexOf/15.4.4.15-3-7 ! lastIndexOf/15.4.4.15-3-12 ! lastIndexOf/15.4.4.15-3-25 ! lastIndexOf/15.4.4.15-3-28 ! map/15.4.4.19-3-7 ! map/15.4.4.19-3-8 ! map/15.4.4.19-3-12 ! map/15.4.4.19-3-14 ! map/15.4.4.19-3-25 ! map/15.4.4.19-3-28 ! map/15.4.4.19-3-29 ! pop/S15.4.4.6_A2_T2 ! pop/S15.4.4.6_A3_T1 ! pop/S15.4.4.6_A3_T2 ! pop/S15.4.4.6_A3_T3 ! push/S15.4.4.7_A2_T2 ! push/S15.4.4.7_A4_T1 ! push/S15.4.4.7_A4_T3 ! reduce/15.4.4.21-3-7 ! reduce/15.4.4.21-3-12 ! reduce/15.4.4.21-3-25 ! reduceRight/15.4.4.22-3-7 ! reduceRight/15.4.4.22-3-12 ! reduceRight/15.4.4.22-3-25 ! reverse/S15.4.4.8_A3_T3 ! shift/S15.4.4.9_A3_T3 ! slice/S15.4.4.10_A3_T1 ! slice/S15.4.4.10_A3_T2 ! slice/S15.4.4.10_A3_T3 ! some/15.4.4.17-3-7 ! some/15.4.4.17-3-8 ! some/15.4.4.17-3-12 ! some/15.4.4.17-3-14 ! some/15.4.4.17-3-25 ! some/15.4.4.17-3-28 ! some/15.4.4.17-3-29 ! sort/S15.4.4.11_A4_T3 ! splice/S15.4.4.12_A3_T1 ! splice/S15.4.4.12_A3_T3 ! splice/S15.4.4.12_A6.1_T2 ! unshift/S15.4.4.13_A3_T2 # bugs? ! splice/S15.4.4.12_A6.1_T3 # 'this === null' or 'this === undefined' ! every/15.4.4.16-1-1 ! every/15.4.4.16-1-2 ! filter/15.4.4.20-1-1 ! filter/15.4.4.20-1-2 ! forEach/15.4.4.18-1-1 ! forEach/15.4.4.18-1-2 ! indexOf/15.4.4.14-1-1 ! indexOf/15.4.4.14-1-2 ! indexOf/15.4.4.14-5-28 ! lastIndexOf/15.4.4.15-1-1 ! lastIndexOf/15.4.4.15-1-2 ! lastIndexOf/15.4.4.15-5-28 ! map/15.4.4.19-1-1 ! map/15.4.4.19-1-2 ! reduce/15.4.4.21-1-1 ! reduce/15.4.4.21-1-2 ! reduceRight/15.4.4.22-1-1 ! reduceRight/15.4.4.22-1-2 ! some/15.4.4.17-1-1 ! some/15.4.4.17-1-2 # strictness issues ! every/15.4.4.16-5-1-s ! filter/15.4.4.20-5-1-s ! find/Array.prototype.find_this-arg ! find/Array.prototype.find_this-arg-receiver-primitive ! find/Array.prototype.find_this-undefined ! findIndex/Array.prototype.findIndex_this-arg ! findIndex/Array.prototype.findIndex_this-arg-receiver-coercion ! findIndex/Array.prototype.findIndex_this-arg-receiver-primitive ! forEach/15.4.4.18-5-1-s ! map/15.4.4.19-5-1-s ! reduce/15.4.4.21-9-c-ii-4-s ! reduceRight/15.4.4.22-9-c-ii-4-s ! some/15.4.4.17-5-1-s ! sort/S15.4.4.11_A8 ! prototype/findIndex/predicate-call-this-strict.js ! prototype/find/predicate-call-this-strict.js ! prototype/splice/set_length_no_args.js ! prototype/toLocaleString/primitive_this_value.js ! prototype/toLocaleString/primitive_this_value_getter.js # not implemented ! prototype/Symbol.iterator.js ! symbol-species-name.js ! symbol-species.js ! Proxy ! from/ ! of/ ! fill/ ! concat/ ! entries/ ! keys/ ! copyWithin ! values built-ins/ArrayIteratorPrototype built-ins/Symbol ! prototype/Symbol.toStringTag ! species language/expressions/arrow-function # not implemented ## strict mode ! ArrowFunction_restricted-properties.js ## class syntax ! lexical-new.target-closure-returned.js ! lexical-new.target.js ! lexical-super-call-from-within-constructor.js ! lexical-super-property-from-within-constructor.js ! lexical-super-property.js ! lexical-supercall-from-immediately-invoked-arrow.js ! syntax/early-errors/arrowparameters-bindingidentifier-no-yield.js ## generator ! syntax/arrowparameters-bindingidentifier-yield.js ! syntax/arrowparameters-cover-formalparameters-yield.js ! syntax/early-errors/arrowparameters-cover-no-yield.js ## spread operator ! syntax/arrowparameters-cover-includes-rest-concisebody-functionbody.js ! syntax/arrowparameters-cover-rest-concisebody-functionbody.js ! syntax/arrowparameters-cover-rest-lineterminator-concisebody-functionbody.js ! syntax/early-errors/arrowparameters-bindingidentifier-rest.js ## default parameter ! syntax/arrowparameters-cover-initialize-1.js ! syntax/arrowparameters-cover-initialize-2.js ## destructuring assignment ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-array-1.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-array-2.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-array-3.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-1.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-2.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-3.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-4.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-5.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-binding-object-6.js ! syntax/early-errors/arrowparameters-cover-no-duplicates-rest.js ! syntax/early-errors/arrowparameters-cover-no-duplicates.js language/arguments-object ! arguments-object/mapped/Symbol.iterator.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-3.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-1.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-2.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-3.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-delete-4.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-1.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-2.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-3.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-4.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-nonwritable-5.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-1.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-2.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-3.js ! arguments-object/mapped/mapped-arguments-nonconfigurable-strict-delete-4.js ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-1.js ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-2.js ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-3.js ! arguments-object/mapped/mapped-arguments-nonwritable-nonconfigurable-4.js ! arguments-object/unmapped/Symbol.iterator.js language/types/reference ! S8.7.2_A1_T1.js ! S8.7.2_A1_T2.js ! 8.7.2-3-a-1gs.js built-ins/Object/defineProperties/15.2.3.7-6-a-26.js built-ins/Object/defineProperties/15.2.3.7-6-a-27.js built-ins/Object/defineProperties/15.2.3.7-6-a-32.js built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-230.js language/expressions/delete/11.4.1-4-a-1-s.js language/expressions/delete/11.4.1-4-a-2-s.js language/expressions/delete/11.4.1-4-a-4-s.js language/expressions/delete/11.4.1-4.a-2.js language/expressions/delete/11.4.1-4.a-3-s.js language/expressions/delete/11.4.1-4.a-3.js language/expressions/delete/11.4.1-4.a-4.js language/expressions/delete/11.4.1-4.a-8-s.js language/expressions/delete/11.4.1-4.a-9-s.js language/expressions/delete/11.4.1-4.a-9.js language/expressions/delete/11.4.4-4.a-3-s.js language/keywords # expected: but was: ! S7.6.1.1_A1.18.js language/future-reserved-words expressions/postfix-decrement expressions/postfix-increment expressions/prefix-decrement expressions/prefix-increment language/literals/numeric built-ins/Date ! 15.9.1.15-1.js ! construct_with_date.js ! prototype/setFullYear/15.9.5.40_1.js built-ins/Object ! defineProperties ! defineProperty ! freeze ! getOwnPropertyDescriptor ! getPrototypeOf ! prototype/hasOwnProperty ! prototype/propertyIsEnumerable ! prototype/toLocaleString ! prototype/toString ! prototype/valueOf ! isExtensible ! isFrozen ! isPrototypeOf ! isSealed ! preventExtensions ! setPrototypeOf ! seal # No Proxy object yet ! assign/source-own-prop-desc-missing.js ! assign/source-own-prop-error.js ! assign/source-own-prop-keys-error.js # Disagreement on strict mode ! assign/target-set-not-writable.js ! create/15.2.3.5-4-311.js closure-compiler-20130227+dfsg1/rhino/testsrc/org/0000755000175000017500000000000014433667662017776 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/0000755000175000017500000000000014433667662021445 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/0000755000175000017500000000000014433667662023613 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/drivers/0000755000175000017500000000000014433667662025271 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/drivers/results.html0000644000175000017500000000433214433667662027662 0ustar apoapo Rhino: Test Results

      Results of JavaScript Test Library for Rhino

      Summary

      Test List
      Skip List
      Results
      Platform, JRE
      Classpath
      Time Execution took , ending at

      Failure Details Retest List

      Failure Details

      Testcase path failed Bug Number XXX

      				
      				
      				
      closure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/drivers/ShellTest.java0000644000175000017500000003052514433667662030050 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.javascript.drivers; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; import org.mozilla.javascript.*; import org.mozilla.javascript.tools.shell.Global; import org.mozilla.javascript.tools.shell.Main; import org.mozilla.javascript.tools.shell.ShellContextFactory; /** * @version $Id: ShellTest.java,v 1.14 2011/03/29 15:17:49 hannes%helma.at Exp $ */ public class ShellTest { public static final FileFilter DIRECTORY_FILTER = new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory() && !pathname.getName().equals("CVS"); } }; public static final FileFilter TEST_FILTER = new FileFilter() { public boolean accept(File pathname) { return pathname.getName().endsWith(".js") && !pathname.getName().equals("shell.js") && !pathname.getName().equals("browser.js") && !pathname.getName().equals("template.js"); } }; public static String getStackTrace(Throwable t) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); t.printStackTrace(new PrintStream(bytes)); return new String(bytes.toByteArray()); } private static void runFileIfExists(Context cx, Scriptable global, File f) { if(f.isFile()) { Main.processFileNoThrow(cx, global, f.getPath()); } } private static class TestState { boolean finished; ErrorReporterWrapper errors; int exitCode = 0; } public static abstract class Status { private boolean negative; public final void setNegative() { this.negative = true; } public final boolean isNegative() { return this.negative; } public final void hadErrors(JsError[] errors) { if (!negative && errors.length > 0) { failed("JavaScript errors:\n" + JsError.toString(errors)); } else if (negative && errors.length == 0) { failed("Should have produced runtime error."); } } public final void hadErrors(File jsFile, JsError[] errors) { if (!negative && errors.length > 0) { failed("JavaScript errors in " + jsFile + ":\n" + JsError.toString(errors)); } else if (negative && errors.length == 0) { failed("Should have produced runtime error in " + jsFile + "."); } } public abstract void running(File jsFile); public abstract void failed(String s); public abstract void threw(Throwable t); public abstract void timedOut(); public abstract void exitCodesWere(int expected, int actual); public abstract void outputWas(String s); static Status compose(final Status[] array) { return new Status() { @Override public void running(File file) { for (int i=0; i errors = new ArrayList(); ErrorReporterWrapper(ErrorReporter original) { this.original = original; } private void addError(String string, String string0, int i, String string1, int i0) { errors.add( new Status.JsError(string, string0, i, string1, i0) ); } public void warning(String string, String string0, int i, String string1, int i0) { original.warning(string, string0, i, string1, i0); } public EvaluatorException runtimeError(String string, String string0, int i, String string1, int i0) { return original.runtimeError(string, string0, i, string1, i0); } public void error(String string, String string0, int i, String string1, int i0) { addError(string, string0, i, string1, i0); } } public static abstract class Parameters { public abstract int getTimeoutMilliseconds(); } @SuppressWarnings(value={"deprecation"}) private static void callStop(Thread t) { t.stop(); } public static void run(final ShellContextFactory shellContextFactory, final File jsFile, final Parameters parameters, final Status status) throws Exception { final Global global = new Global(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); final PrintStream p = new PrintStream(out); global.setOut(p); global.setErr(p); global.defineFunctionProperties( new String[] { "options" }, ShellTest.class, ScriptableObject.DONTENUM | ScriptableObject.PERMANENT | ScriptableObject.READONLY); // test suite expects keywords to be disallowed as identifiers shellContextFactory.setAllowReservedKeywords(false); final TestState testState = new TestState(); if (jsFile.getName().endsWith("-n.js")) { status.setNegative(); } final Throwable thrown[] = {null}; Thread t = new Thread(new Runnable() { public void run() { try { shellContextFactory.call(new ContextAction() { public Object run(Context cx) { status.running(jsFile); testState.errors = new ErrorReporterWrapper(cx.getErrorReporter()); cx.setErrorReporter( testState.errors ); global.init(cx); try { runFileIfExists(cx, global, new File(jsFile.getParentFile().getParentFile().getParentFile(), "shell.js")); runFileIfExists(cx, global, new File(jsFile.getParentFile().getParentFile(), "shell.js")); runFileIfExists(cx, global, new File(jsFile.getParentFile(), "shell.js")); runFileIfExists(cx, global, jsFile); status.hadErrors(jsFile, testState.errors.errors.toArray(new Status.JsError[0])); } catch (ThreadDeath e) { } catch (Throwable t) { status.threw(t); } return null; } }); } catch (Error t) { thrown[0] = t; } catch (RuntimeException t) { thrown[0] = t; } finally { synchronized(testState) { testState.finished = true; } } } }, jsFile.getPath()); t.setDaemon(true); t.start(); t.join(parameters.getTimeoutMilliseconds()); synchronized(testState) { if(!testState.finished) { callStop(t); status.timedOut(); } } int expectedExitCode = 0; p.flush(); status.outputWas(new String(out.toByteArray())); BufferedReader r = new BufferedReader(new InputStreamReader( new ByteArrayInputStream(out.toByteArray()))); String failures = ""; for(;;) { String s = r.readLine(); if(s == null) { break; } if(s.indexOf("FAILED!") != -1) { failures += s + '\n'; } int expex = s.indexOf("EXPECT EXIT CODE "); if(expex != -1) { expectedExitCode = s.charAt(expex + "EXPECT EXIT CODE ".length()) - '0'; } } if (thrown[0] != null) { status.threw(thrown[0]); } status.exitCodesWere(expectedExitCode, testState.exitCode); if(failures != "") { status.failed(failures); } } // Global function to mimic options() function in spidermonkey. // It looks like this toggles jit compiler mode in spidermonkey // when called with "jit" as argument. Our version is a no-op // and returns an empty string. public static String options() { return ""; } } closure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/drivers/LanguageVersion.java0000644000175000017500000000101014433667662031215 0ustar apoapopackage org.mozilla.javascript.drivers; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.TYPE; /** * Intended to be used as an optional annotation for subclasses * of {@link org.mozilla.javascript.drivers.ScriptTestsBase}. * Sets the language version of test's script execution context. */ @Target(TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LanguageVersion { int value(); } closure-compiler-20130227+dfsg1/rhino/testsrc/org/mozilla/javascript/drivers/TestUtils.java0000644000175000017500000000572614433667662030106 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.javascript.drivers; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Arrays; import org.mozilla.javascript.ContextFactory; public class TestUtils { private static ContextFactory.GlobalSetter globalSetter; public static void grabContextFactoryGlobalSetter() { if (globalSetter == null) { globalSetter = ContextFactory.getGlobalSetter(); } } public static void setGlobalContextFactory(ContextFactory factory) { grabContextFactoryGlobalSetter(); globalSetter.setContextFactoryGlobal(factory); } public static File[] recursiveListFiles(File dir, FileFilter filter) { if (!dir.isDirectory()) throw new IllegalArgumentException(dir + " is not a directory"); List fileList = new ArrayList(); recursiveListFilesHelper(dir, filter, fileList); return fileList.toArray(new File[fileList.size()]); } public static void recursiveListFilesHelper(File dir, FileFilter filter, List fileList) { for (File f: dir.listFiles()) { if (f.isDirectory()) { recursiveListFilesHelper(f, filter, fileList); } else { if (filter.accept(f)) fileList.add(f); } } } public static void addTestsFromFile(String filename, List list) throws IOException { addTestsFromStream(new FileInputStream(new File(filename)), list); } public static void addTestsFromStream(InputStream in, List list) throws IOException { Properties props = new Properties(); props.load(in); for (Object obj: props.keySet()) { list.add(obj.toString()); } } public static String[] loadTestsFromResource(String resource, String[] inherited) throws IOException { List list = inherited == null ? new ArrayList() : new ArrayList(Arrays.asList(inherited)); InputStream in = JsTestsBase.class.getResourceAsStream(resource); if (in != null) addTestsFromStream(in, list); return list.toArray(new String[0]); } public static boolean matches(String[] patterns, String path) { for (int i=0; i list = new ArrayList(); for (int i=0; i < tests.length; i++) { if (tests[i].startsWith("@")) TestUtils.addTestsFromFile(tests[i].substring(1), list); else list.add(tests[i]); } return list.toArray(new String[0]); } private boolean matches(String path) { if (list.length == 0) return true; return TestUtils.matches(list, path); } private boolean excluded(String path) { if (skip.length == 0) return false; return TestUtils.matches(skip, path); } private void addFiles(List'; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += 'Click here to skip to main content.'; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ''; s += '
      '; s += '
      CNN.com'; s += ' '; s += ''; s += ' '; s += ''; s += ''; s += ''; s += ''; s += '
      '; s += '
      '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      SEARCH
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
        The Web  CNN.com   
      enhanced by Google
      '; s += '
      '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
      '; s += '
      SERVICES
       
       
       
      SEARCH
      '; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      WebCNN.com
      enhanced by Google
      '; s += ''; s += ''; s += ''; s += '
      '; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      Updated: 05:53 p.m. EDT (2153 GMT) June 12, 2003
      '; s += '
      '; s += ''; s += ''; s += ' '; s += ' Oscar-winner Peck dies'; s += ''; s += '
      '; s += '

      Oscar-winner Peck dies

      '; s += '

      '; s += 'Actor Gregory Peck, who won an Oscar for his portrayal of upstanding lawyer Atticus Finch in 1962s "To Kill a Mockingbird," has died at age 87. Peck was best known for roles of dignified statesmen and people who followed a strong code of ethics. But he also could play against type. All told, Peck was nominated for five Academy Awards.'; s += '

      '; s += '

      '; s += ' FULL STORY'; s += '

      '; s += ''; s += ''; s += ''; s += '• Video: premium content A leading mans leading man
      '; s += ''; s += ''; s += ''; s += ' '; s += '• Interactive: Gregory Peck through the years
      '; s += ''; s += ' '; s += '• Gregory Peck filmographyexternal link
      '; s += ''; s += ' '; s += '• Pecks Finch chararcter AFIs top heroexternal link
      '; s += '
      '; s += ''; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      MORE TOP STORIES Hot Stories
      '; s += '
      '; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ' '; s += ''; s += '
      '; s += ''; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += ' '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += '
      '; s += ' CNNRADIO'; s += '
      Listen to latest updates'; s += '
      '; s += ''; s += '
      '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      VIDEOMORE VIDEO
      '; s += ' Soldier broke dozens of hearts over e-mail
      '; s += ' premium content PLAY VIDEO
      '; s += '
      '; s += ' '; s += '
      '; s += ' '; s += '
      '; s += ''; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += '
      '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      ON THE SCENEmore reports
      '; s += ''; s += ''; s += ' '; s += ' '; s += ''; s += ''; s += ' '; s += '
      Jeffrey Toobin: "It takes guts" for Peterson defense to subpoena judge over wiretap issue.'; s += 'Full Storyimage
      '; s += '
      '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      BUSINESS'; s += '  at CNN/Money  Business News
      '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      STOCK/FUND QUOTES:
      enter symbol
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      sponsored by:Click Here
      '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += '
      MARKETS: '; s += ''; s += '4:30pm ET, 6/12
      DJIA+13.309196.50+ 0.14%
      NAS+ 7.601653.62+ 0.46%
      S&P+ 1.03998.51+ 0.10%
      '; s += '
      '; s += ''; s += '
      '; s += ''; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      MORE REAL TVMore Entertainment
      '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ' '; s += ' Go ahead, follow me
      '; s += 'New reality series and the movie debut of "Idol" finalists'; s += '
      Go ahead, follow me
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += '
      '; s += ''; s += '
      '; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      GIFT IDEASBusiness News
      '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ''; s += ''; s += 'CNN/Money: Fathers Day
      '; s += 'Smaller is better --from digital cameras to iPod'; s += '
      Fathers Day
      '; s += '
      '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '

      U.S. News:
      '; s += ''; s += ' '; s += '• Miami police link 4 rapes to serial rapist
      '; s += ''; s += ' '; s += '• Woman mistaken for fugitive jailed
      '; s += ''; s += ' '; s += '• Pregnant woman impaled on mic stand
      '; s += '
      World News:
      '; s += ''; s += ' '; s += '• NATO reshapes for new era
      '; s += ''; s += ' '; s += '• U.N. reviews Bunia peace force
      '; s += ''; s += ''; s += ''; s += '• TIME.com: Saddams curtain trailexternal link
      '; s += '
      Sci-Tech News:
      '; s += ''; s += ' '; s += '• Another reason to throw out your VCR
      '; s += ''; s += ' '; s += '• Flat screen TV prices dropping
      '; s += '
      Entertainment News:
      '; s += ''; s += ' '; s += '• CNN hires Soledad OBrien for "AM"
      '; s += ''; s += ' '; s += '• Dating show star let go by law firm
      '; s += '
      Politics News:
      '; s += ''; s += ' '; s += '• Schwarzenegger on California politics
      '; s += ''; s += ' '; s += '• House approves extension on child tax credit
      '; s += '
      Law News:
      '; s += ''; s += ' '; s += '• Court bars cash advances to plaintiffs
      '; s += ''; s += ' '; s += '• Lawsuit against Jackson settled
      '; s += '
      Health News:
      '; s += ''; s += ' '; s += '• Monkeypox spreading person-to-person?
      '; s += ''; s += ' '; s += '• A full body X-ray in 13 seconds
      '; s += '
      Space News:
      '; s += ''; s += ' '; s += '• Hydrogen fuel may disturb ozone layer
      '; s += ''; s += ' '; s += '• New threat found for shuttle launches
      '; s += '
      Travel News:
      '; s += ''; s += ' '; s += '• Walking America from coast to coast
      '; s += ''; s += ' '; s += '• Airline execs not seeing sunny skies yet
      '; s += '
      Education News:
      '; s += ''; s += ' '; s += '• Arab students seek prom balance
      '; s += ''; s += ' '; s += '• Public schools turn to upscale fundraising
      '; s += '
      Sports News:
      Business News:
      '; s += '• Here come the "Duppies"
      '; s += '• Oracle beats estimates
      '; s += '
      '; s += '
      '; s += '
      '; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      WATCH CNN TV
      On CNN TV
      '; s += ''; s += ' '; s += ' '; s += ' '; s += '
      American Morning, 7 a.m. ETAmerican Morning (7 a.m. ET): Tomorrow, singer Carnie Wilson talks about her new book, "Im Still Hungry."'; s += '
      '; s += ''; s += ''; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      ANALYSIS
      U.S. News
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += 'Fight It, Martha'; s += ''; s += ''; s += 'NYTimes: Fight It, Martha
      '; s += 'William Safire: I hope Martha Stewart beats this bum rap'; s += ''; s += ''; s += ''; s += ''; s += '
      '; s += '
      '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      OFFBEAT
      more offbeat
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += 'Waiting list'; s += ' '; s += ' Waiting list
      '; s += 'Chinas "smart sperm" bank needs donors'; s += '
      '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
       WEATHER
      Get your hometown weather on the home page! Enter city name or U.S. Zip Code:
      Or select location from a list
      '; s += ''; s += ''; s += ''; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      Quick Vote'; s += ''; s += 'Click Here'; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += '
      Should an international peacekeeping force be sent to the Mideast?
      Yes'; s += '
      No'; s += '
      '; s += ''; s += '
      VIEW RESULTS
      '; s += ''; s += '
      '; s += ''; s += '
      '; s += '
      '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ''; s += '
      From our Partners'; s += '  External site icon
      '; s += 'Time:
        Subscribe to TIME  

      '; s += 'CNNsi.com:
      '; s += '• Marty Burns: Nets pull out all stops
      '; s += '• Michael Farber: Sens look good for "04
      '; s += '• Tim Layden: NFL or bust for Neuheisel
      '; s += '
      '; s += '
        Subscribe to Sports Illustrated  
      '; s += '

      '; s += 'New York Times:
        Get 50% OFF the NY Times  
      '; s += '
      '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += ''; s += ''; s += '
      '; s += ''; s += '
      '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ''; s += ' '; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      International Edition
      CNN TVCNN InternationalHeadline NewsTranscriptsPreferencesAbout CNN.com
      '; s += ''; s += '
      '; s += ''; s += ''; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      '; s += '© 2003 Cable News Network LP, LLLP.
      '; s += 'An AOL Time Warner Company. All Rights Reserved.
      '; s += 'Terms under which this service is provided to you.
      '; s += 'Read our privacy guidelines. Contact us.'; s += '
      '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += ' '; s += '
      external link
      All external sites will open in a new browser.
      '; s += ' CNN.com does not endorse external sites.
      '; s += ''; s += '
       Premium content icon Denotes premium content.
      '; s += ''; s += '
      '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ' '; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; s += ''; return s; } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/perlstress-001.js0000644000175000017500000022272514433667662025746 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 2002-07-07 * SUMMARY: Testing JS RegExp engine against Perl 5 RegExp engine. * Adjust cnLBOUND, cnUBOUND below to restrict which sections are tested. * * This test was created by running various patterns and strings through the * Perl 5 RegExp engine. We saved the results below to test the JS engine. * * NOTE: ECMA/JS and Perl do differ on certain points. We have either commented * out such sections altogether, or modified them to fit what we expect from JS. * * EXAMPLES: * * - In JS, regexp captures (/(a) etc./) must hold |undefined| if not used. * See http://bugzilla.mozilla.org/show_bug.cgi?id=123437. * By contrast, in Perl, unmatched captures hold the empty string. * We have modified such sections accordingly. Example: pattern = /^([^a-z])|(\^)$/; string = '.'; actualmatch = string.match(pattern); //expectedmatch = Array('.', '.', ''); <<<--- Perl expectedmatch = Array('.', '.', undefined); <<<--- JS addThis(); * - In JS, you can't refer to a capture before it's encountered & completed * * - Perl supports ] & ^] inside a [], ECMA does not * * - ECMA does support (?: (?= and (?! operators, but doesn't support (?< etc. * * - ECMA doesn't support (?imsx or (?-imsx * * - ECMA doesn't support (?(condition) * * - Perl has \Z has end-of-line, ECMA doesn't * * - In ECMA, ^ matches only the empty string before the first character * * - In ECMA, $ matches only the empty string at end of input (unless multiline) * * - ECMA spec says that each atom in a range must be a single character * * - ECMA doesn't support \A * * - ECMA doesn't have rules for [: * */ //----------------------------------------------------------------------------- var gTestfile = 'perlstress-001.js'; var i = 0; var BUGNUMBER = 85721; var summary = 'Testing regular expression edge cases'; var cnSingleSpace = ' '; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); var cnLBOUND = 1; var cnUBOUND = 1000; status = inSection(1); pattern = /abc/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(2); pattern = /abc/; string = 'xabcy'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(3); pattern = /abc/; string = 'ababc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(4); pattern = /ab*c/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(5); pattern = /ab*bc/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(6); pattern = /ab*bc/; string = 'abbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbc'); addThis(); status = inSection(7); pattern = /ab*bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(8); pattern = /.{1}/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(9); pattern = /.{3,4}/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbb'); addThis(); status = inSection(10); pattern = /ab{0,}bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(11); pattern = /ab+bc/; string = 'abbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbc'); addThis(); status = inSection(12); pattern = /ab+bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(13); pattern = /ab{1,}bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(14); pattern = /ab{1,3}bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(15); pattern = /ab{3,4}bc/; string = 'abbbbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbbbc'); addThis(); status = inSection(16); pattern = /ab?bc/; string = 'abbc'; actualmatch = string.match(pattern); expectedmatch = Array('abbc'); addThis(); status = inSection(17); pattern = /ab?bc/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(18); pattern = /ab{0,1}bc/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(19); pattern = /ab?c/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(20); pattern = /ab{0,1}c/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(21); pattern = /^abc$/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(22); pattern = /^abc/; string = 'abcc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(23); pattern = /abc$/; string = 'aabc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(24); pattern = /^/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(25); pattern = /$/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(26); pattern = /a.c/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(27); pattern = /a.c/; string = 'axc'; actualmatch = string.match(pattern); expectedmatch = Array('axc'); addThis(); status = inSection(28); pattern = /a.*c/; string = 'axyzc'; actualmatch = string.match(pattern); expectedmatch = Array('axyzc'); addThis(); status = inSection(29); pattern = /a[bc]d/; string = 'abd'; actualmatch = string.match(pattern); expectedmatch = Array('abd'); addThis(); status = inSection(30); pattern = /a[b-d]e/; string = 'ace'; actualmatch = string.match(pattern); expectedmatch = Array('ace'); addThis(); status = inSection(31); pattern = /a[b-d]/; string = 'aac'; actualmatch = string.match(pattern); expectedmatch = Array('ac'); addThis(); status = inSection(32); pattern = /a[-b]/; string = 'a-'; actualmatch = string.match(pattern); expectedmatch = Array('a-'); addThis(); status = inSection(33); pattern = /a[b-]/; string = 'a-'; actualmatch = string.match(pattern); expectedmatch = Array('a-'); addThis(); status = inSection(34); pattern = /a]/; string = 'a]'; actualmatch = string.match(pattern); expectedmatch = Array('a]'); addThis(); /* Perl supports ] & ^] inside a [], ECMA does not pattern = /a[]]b/; status = inSection(35); string = 'a]b'; actualmatch = string.match(pattern); expectedmatch = Array('a]b'); addThis(); */ status = inSection(36); pattern = /a[^bc]d/; string = 'aed'; actualmatch = string.match(pattern); expectedmatch = Array('aed'); addThis(); status = inSection(37); pattern = /a[^-b]c/; string = 'adc'; actualmatch = string.match(pattern); expectedmatch = Array('adc'); addThis(); /* Perl supports ] & ^] inside a [], ECMA does not status = inSection(38); pattern = /a[^]b]c/; string = 'adc'; actualmatch = string.match(pattern); expectedmatch = Array('adc'); addThis(); */ status = inSection(39); pattern = /\ba\b/; string = 'a-'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(40); pattern = /\ba\b/; string = '-a'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(41); pattern = /\ba\b/; string = '-a-'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(42); pattern = /\By\b/; string = 'xy'; actualmatch = string.match(pattern); expectedmatch = Array('y'); addThis(); status = inSection(43); pattern = /\by\B/; string = 'yz'; actualmatch = string.match(pattern); expectedmatch = Array('y'); addThis(); status = inSection(44); pattern = /\By\B/; string = 'xyz'; actualmatch = string.match(pattern); expectedmatch = Array('y'); addThis(); status = inSection(45); pattern = /\w/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(46); pattern = /\W/; string = '-'; actualmatch = string.match(pattern); expectedmatch = Array('-'); addThis(); status = inSection(47); pattern = /a\Sb/; string = 'a-b'; actualmatch = string.match(pattern); expectedmatch = Array('a-b'); addThis(); status = inSection(48); pattern = /\d/; string = '1'; actualmatch = string.match(pattern); expectedmatch = Array('1'); addThis(); status = inSection(49); pattern = /\D/; string = '-'; actualmatch = string.match(pattern); expectedmatch = Array('-'); addThis(); status = inSection(50); pattern = /[\w]/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(51); pattern = /[\W]/; string = '-'; actualmatch = string.match(pattern); expectedmatch = Array('-'); addThis(); status = inSection(52); pattern = /a[\S]b/; string = 'a-b'; actualmatch = string.match(pattern); expectedmatch = Array('a-b'); addThis(); status = inSection(53); pattern = /[\d]/; string = '1'; actualmatch = string.match(pattern); expectedmatch = Array('1'); addThis(); status = inSection(54); pattern = /[\D]/; string = '-'; actualmatch = string.match(pattern); expectedmatch = Array('-'); addThis(); status = inSection(55); pattern = /ab|cd/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(56); pattern = /ab|cd/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(57); pattern = /()ef/; string = 'def'; actualmatch = string.match(pattern); expectedmatch = Array('ef', ''); addThis(); status = inSection(58); pattern = /a\(b/; string = 'a(b'; actualmatch = string.match(pattern); expectedmatch = Array('a(b'); addThis(); status = inSection(59); pattern = /a\(*b/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(60); pattern = /a\(*b/; string = 'a((b'; actualmatch = string.match(pattern); expectedmatch = Array('a((b'); addThis(); status = inSection(61); pattern = /a\\b/; string = 'a\\b'; actualmatch = string.match(pattern); expectedmatch = Array('a\\b'); addThis(); status = inSection(62); pattern = /((a))/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a', 'a'); addThis(); status = inSection(63); pattern = /(a)b(c)/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc', 'a', 'c'); addThis(); status = inSection(64); pattern = /a+b+c/; string = 'aabbabc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(65); pattern = /a{1,}b{1,}c/; string = 'aabbabc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(66); pattern = /a.+?c/; string = 'abcabc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); status = inSection(67); pattern = /(a+|b)*/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab', 'b'); addThis(); status = inSection(68); pattern = /(a+|b){0,}/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab', 'b'); addThis(); status = inSection(69); pattern = /(a+|b)+/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab', 'b'); addThis(); status = inSection(70); pattern = /(a+|b){1,}/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab', 'b'); addThis(); status = inSection(71); pattern = /(a+|b)?/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a'); addThis(); status = inSection(72); pattern = /(a+|b){0,1}/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a'); addThis(); status = inSection(73); pattern = /[^ab]*/; string = 'cde'; actualmatch = string.match(pattern); expectedmatch = Array('cde'); addThis(); status = inSection(74); pattern = /([abc])*d/; string = 'abbbcd'; actualmatch = string.match(pattern); expectedmatch = Array('abbbcd', 'c'); addThis(); status = inSection(75); pattern = /([abc])*bcd/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'a'); addThis(); status = inSection(76); pattern = /a|b|c|d|e/; string = 'e'; actualmatch = string.match(pattern); expectedmatch = Array('e'); addThis(); status = inSection(77); pattern = /(a|b|c|d|e)f/; string = 'ef'; actualmatch = string.match(pattern); expectedmatch = Array('ef', 'e'); addThis(); status = inSection(78); pattern = /abcd*efg/; string = 'abcdefg'; actualmatch = string.match(pattern); expectedmatch = Array('abcdefg'); addThis(); status = inSection(79); pattern = /ab*/; string = 'xabyabbbz'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(80); pattern = /ab*/; string = 'xayabbbz'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(81); pattern = /(ab|cd)e/; string = 'abcde'; actualmatch = string.match(pattern); expectedmatch = Array('cde', 'cd'); addThis(); status = inSection(82); pattern = /[abhgefdc]ij/; string = 'hij'; actualmatch = string.match(pattern); expectedmatch = Array('hij'); addThis(); status = inSection(83); pattern = /(abc|)ef/; string = 'abcdef'; actualmatch = string.match(pattern); expectedmatch = Array('ef', ''); addThis(); status = inSection(84); pattern = /(a|b)c*d/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('bcd', 'b'); addThis(); status = inSection(85); pattern = /(ab|ab*)bc/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc', 'a'); addThis(); status = inSection(86); pattern = /a([bc]*)c*/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc', 'bc'); addThis(); status = inSection(87); pattern = /a([bc]*)(c*d)/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'bc', 'd'); addThis(); status = inSection(88); pattern = /a([bc]+)(c*d)/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'bc', 'd'); addThis(); status = inSection(89); pattern = /a([bc]*)(c+d)/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'b', 'cd'); addThis(); status = inSection(90); pattern = /a[bcd]*dcdcde/; string = 'adcdcde'; actualmatch = string.match(pattern); expectedmatch = Array('adcdcde'); addThis(); status = inSection(91); pattern = /(ab|a)b*c/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc', 'ab'); addThis(); status = inSection(92); pattern = /((a)(b)c)(d)/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'abc', 'a', 'b', 'd'); addThis(); status = inSection(93); pattern = /[a-zA-Z_][a-zA-Z0-9_]*/; string = 'alpha'; actualmatch = string.match(pattern); expectedmatch = Array('alpha'); addThis(); status = inSection(94); pattern = /^a(bc+|b[eh])g|.h$/; string = 'abh'; actualmatch = string.match(pattern); expectedmatch = Array('bh', undefined); addThis(); status = inSection(95); pattern = /(bc+d$|ef*g.|h?i(j|k))/; string = 'effgz'; actualmatch = string.match(pattern); expectedmatch = Array('effgz', 'effgz', undefined); addThis(); status = inSection(96); pattern = /(bc+d$|ef*g.|h?i(j|k))/; string = 'ij'; actualmatch = string.match(pattern); expectedmatch = Array('ij', 'ij', 'j'); addThis(); status = inSection(97); pattern = /(bc+d$|ef*g.|h?i(j|k))/; string = 'reffgz'; actualmatch = string.match(pattern); expectedmatch = Array('effgz', 'effgz', undefined); addThis(); status = inSection(98); pattern = /((((((((((a))))))))))/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'); addThis(); status = inSection(99); pattern = /((((((((((a))))))))))\10/; string = 'aa'; actualmatch = string.match(pattern); expectedmatch = Array('aa', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'); addThis(); status = inSection(100); pattern = /((((((((((a))))))))))/; string = 'a!'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'); addThis(); status = inSection(101); pattern = /(((((((((a)))))))))/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'); addThis(); status = inSection(102); pattern = /(.*)c(.*)/; string = 'abcde'; actualmatch = string.match(pattern); expectedmatch = Array('abcde', 'ab', 'de'); addThis(); status = inSection(103); pattern = /abcd/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd'); addThis(); status = inSection(104); pattern = /a(bc)d/; string = 'abcd'; actualmatch = string.match(pattern); expectedmatch = Array('abcd', 'bc'); addThis(); status = inSection(105); pattern = /a[-]?c/; string = 'ac'; actualmatch = string.match(pattern); expectedmatch = Array('ac'); addThis(); status = inSection(106); pattern = /(abc)\1/; string = 'abcabc'; actualmatch = string.match(pattern); expectedmatch = Array('abcabc', 'abc'); addThis(); status = inSection(107); pattern = /([a-c]*)\1/; string = 'abcabc'; actualmatch = string.match(pattern); expectedmatch = Array('abcabc', 'abc'); addThis(); status = inSection(108); pattern = /(a)|\1/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a'); addThis(); status = inSection(109); pattern = /(([a-c])b*?\2)*/; string = 'ababbbcbc'; actualmatch = string.match(pattern); expectedmatch = Array('ababb', 'bb', 'b'); addThis(); status = inSection(110); pattern = /(([a-c])b*?\2){3}/; string = 'ababbbcbc'; actualmatch = string.match(pattern); expectedmatch = Array('ababbbcbc', 'cbc', 'c'); addThis(); /* Can't refer to a capture before it's encountered & completed status = inSection(111); pattern = /((\3|b)\2(a)x)+/; string = 'aaaxabaxbaaxbbax'; actualmatch = string.match(pattern); expectedmatch = Array('bbax', 'bbax', 'b', 'a'); addThis(); status = inSection(112); pattern = /((\3|b)\2(a)){2,}/; string = 'bbaababbabaaaaabbaaaabba'; actualmatch = string.match(pattern); expectedmatch = Array('bbaaaabba', 'bba', 'b', 'a'); addThis(); */ status = inSection(113); pattern = /abc/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(114); pattern = /abc/i; string = 'XABCY'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(115); pattern = /abc/i; string = 'ABABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(116); pattern = /ab*c/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(117); pattern = /ab*bc/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(118); pattern = /ab*bc/i; string = 'ABBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBC'); addThis(); status = inSection(119); pattern = /ab*?bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(120); pattern = /ab{0,}?bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(121); pattern = /ab+?bc/i; string = 'ABBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBC'); addThis(); status = inSection(122); pattern = /ab+bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(123); pattern = /ab{1,}?bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(124); pattern = /ab{1,3}?bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(125); pattern = /ab{3,4}?bc/i; string = 'ABBBBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBBC'); addThis(); status = inSection(126); pattern = /ab??bc/i; string = 'ABBC'; actualmatch = string.match(pattern); expectedmatch = Array('ABBC'); addThis(); status = inSection(127); pattern = /ab??bc/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(128); pattern = /ab{0,1}?bc/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(129); pattern = /ab??c/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(130); pattern = /ab{0,1}?c/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(131); pattern = /^abc$/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(132); pattern = /^abc/i; string = 'ABCC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(133); pattern = /abc$/i; string = 'AABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(134); pattern = /^/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(135); pattern = /$/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(136); pattern = /a.c/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(137); pattern = /a.c/i; string = 'AXC'; actualmatch = string.match(pattern); expectedmatch = Array('AXC'); addThis(); status = inSection(138); pattern = /a.*?c/i; string = 'AXYZC'; actualmatch = string.match(pattern); expectedmatch = Array('AXYZC'); addThis(); status = inSection(139); pattern = /a[bc]d/i; string = 'ABD'; actualmatch = string.match(pattern); expectedmatch = Array('ABD'); addThis(); status = inSection(140); pattern = /a[b-d]e/i; string = 'ACE'; actualmatch = string.match(pattern); expectedmatch = Array('ACE'); addThis(); status = inSection(141); pattern = /a[b-d]/i; string = 'AAC'; actualmatch = string.match(pattern); expectedmatch = Array('AC'); addThis(); status = inSection(142); pattern = /a[-b]/i; string = 'A-'; actualmatch = string.match(pattern); expectedmatch = Array('A-'); addThis(); status = inSection(143); pattern = /a[b-]/i; string = 'A-'; actualmatch = string.match(pattern); expectedmatch = Array('A-'); addThis(); status = inSection(144); pattern = /a]/i; string = 'A]'; actualmatch = string.match(pattern); expectedmatch = Array('A]'); addThis(); /* Perl supports ] & ^] inside a [], ECMA does not status = inSection(145); pattern = /a[]]b/i; string = 'A]B'; actualmatch = string.match(pattern); expectedmatch = Array('A]B'); addThis(); */ status = inSection(146); pattern = /a[^bc]d/i; string = 'AED'; actualmatch = string.match(pattern); expectedmatch = Array('AED'); addThis(); status = inSection(147); pattern = /a[^-b]c/i; string = 'ADC'; actualmatch = string.match(pattern); expectedmatch = Array('ADC'); addThis(); /* Perl supports ] & ^] inside a [], ECMA does not status = inSection(148); pattern = /a[^]b]c/i; string = 'ADC'; actualmatch = string.match(pattern); expectedmatch = Array('ADC'); addThis(); */ status = inSection(149); pattern = /ab|cd/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('AB'); addThis(); status = inSection(150); pattern = /ab|cd/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('AB'); addThis(); status = inSection(151); pattern = /()ef/i; string = 'DEF'; actualmatch = string.match(pattern); expectedmatch = Array('EF', ''); addThis(); status = inSection(152); pattern = /a\(b/i; string = 'A(B'; actualmatch = string.match(pattern); expectedmatch = Array('A(B'); addThis(); status = inSection(153); pattern = /a\(*b/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB'); addThis(); status = inSection(154); pattern = /a\(*b/i; string = 'A((B'; actualmatch = string.match(pattern); expectedmatch = Array('A((B'); addThis(); status = inSection(155); pattern = /a\\b/i; string = 'A\\B'; actualmatch = string.match(pattern); expectedmatch = Array('A\\B'); addThis(); status = inSection(156); pattern = /((a))/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A', 'A'); addThis(); status = inSection(157); pattern = /(a)b(c)/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC', 'A', 'C'); addThis(); status = inSection(158); pattern = /a+b+c/i; string = 'AABBABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(159); pattern = /a{1,}b{1,}c/i; string = 'AABBABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(160); pattern = /a.+?c/i; string = 'ABCABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(161); pattern = /a.*?c/i; string = 'ABCABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(162); pattern = /a.{0,5}?c/i; string = 'ABCABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC'); addThis(); status = inSection(163); pattern = /(a+|b)*/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB', 'B'); addThis(); status = inSection(164); pattern = /(a+|b){0,}/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB', 'B'); addThis(); status = inSection(165); pattern = /(a+|b)+/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB', 'B'); addThis(); status = inSection(166); pattern = /(a+|b){1,}/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB', 'B'); addThis(); status = inSection(167); pattern = /(a+|b)?/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A'); addThis(); status = inSection(168); pattern = /(a+|b){0,1}/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A'); addThis(); status = inSection(169); pattern = /(a+|b){0,1}?/i; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('', undefined); addThis(); status = inSection(170); pattern = /[^ab]*/i; string = 'CDE'; actualmatch = string.match(pattern); expectedmatch = Array('CDE'); addThis(); status = inSection(171); pattern = /([abc])*d/i; string = 'ABBBCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABBBCD', 'C'); addThis(); status = inSection(172); pattern = /([abc])*bcd/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'A'); addThis(); status = inSection(173); pattern = /a|b|c|d|e/i; string = 'E'; actualmatch = string.match(pattern); expectedmatch = Array('E'); addThis(); status = inSection(174); pattern = /(a|b|c|d|e)f/i; string = 'EF'; actualmatch = string.match(pattern); expectedmatch = Array('EF', 'E'); addThis(); status = inSection(175); pattern = /abcd*efg/i; string = 'ABCDEFG'; actualmatch = string.match(pattern); expectedmatch = Array('ABCDEFG'); addThis(); status = inSection(176); pattern = /ab*/i; string = 'XABYABBBZ'; actualmatch = string.match(pattern); expectedmatch = Array('AB'); addThis(); status = inSection(177); pattern = /ab*/i; string = 'XAYABBBZ'; actualmatch = string.match(pattern); expectedmatch = Array('A'); addThis(); status = inSection(178); pattern = /(ab|cd)e/i; string = 'ABCDE'; actualmatch = string.match(pattern); expectedmatch = Array('CDE', 'CD'); addThis(); status = inSection(179); pattern = /[abhgefdc]ij/i; string = 'HIJ'; actualmatch = string.match(pattern); expectedmatch = Array('HIJ'); addThis(); status = inSection(180); pattern = /(abc|)ef/i; string = 'ABCDEF'; actualmatch = string.match(pattern); expectedmatch = Array('EF', ''); addThis(); status = inSection(181); pattern = /(a|b)c*d/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('BCD', 'B'); addThis(); status = inSection(182); pattern = /(ab|ab*)bc/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC', 'A'); addThis(); status = inSection(183); pattern = /a([bc]*)c*/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC', 'BC'); addThis(); status = inSection(184); pattern = /a([bc]*)(c*d)/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'BC', 'D'); addThis(); status = inSection(185); pattern = /a([bc]+)(c*d)/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'BC', 'D'); addThis(); status = inSection(186); pattern = /a([bc]*)(c+d)/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'B', 'CD'); addThis(); status = inSection(187); pattern = /a[bcd]*dcdcde/i; string = 'ADCDCDE'; actualmatch = string.match(pattern); expectedmatch = Array('ADCDCDE'); addThis(); status = inSection(188); pattern = /(ab|a)b*c/i; string = 'ABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABC', 'AB'); addThis(); status = inSection(189); pattern = /((a)(b)c)(d)/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'ABC', 'A', 'B', 'D'); addThis(); status = inSection(190); pattern = /[a-zA-Z_][a-zA-Z0-9_]*/i; string = 'ALPHA'; actualmatch = string.match(pattern); expectedmatch = Array('ALPHA'); addThis(); status = inSection(191); pattern = /^a(bc+|b[eh])g|.h$/i; string = 'ABH'; actualmatch = string.match(pattern); expectedmatch = Array('BH', undefined); addThis(); status = inSection(192); pattern = /(bc+d$|ef*g.|h?i(j|k))/i; string = 'EFFGZ'; actualmatch = string.match(pattern); expectedmatch = Array('EFFGZ', 'EFFGZ', undefined); addThis(); status = inSection(193); pattern = /(bc+d$|ef*g.|h?i(j|k))/i; string = 'IJ'; actualmatch = string.match(pattern); expectedmatch = Array('IJ', 'IJ', 'J'); addThis(); status = inSection(194); pattern = /(bc+d$|ef*g.|h?i(j|k))/i; string = 'REFFGZ'; actualmatch = string.match(pattern); expectedmatch = Array('EFFGZ', 'EFFGZ', undefined); addThis(); status = inSection(195); pattern = /((((((((((a))))))))))/i; string = 'A'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'); addThis(); status = inSection(196); pattern = /((((((((((a))))))))))\10/i; string = 'AA'; actualmatch = string.match(pattern); expectedmatch = Array('AA', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'); addThis(); status = inSection(197); pattern = /((((((((((a))))))))))/i; string = 'A!'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'); addThis(); status = inSection(198); pattern = /(((((((((a)))))))))/i; string = 'A'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'); addThis(); status = inSection(199); pattern = /(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))/i; string = 'A'; actualmatch = string.match(pattern); expectedmatch = Array('A', 'A'); addThis(); status = inSection(200); pattern = /(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))/i; string = 'C'; actualmatch = string.match(pattern); expectedmatch = Array('C', 'C'); addThis(); status = inSection(201); pattern = /(.*)c(.*)/i; string = 'ABCDE'; actualmatch = string.match(pattern); expectedmatch = Array('ABCDE', 'AB', 'DE'); addThis(); status = inSection(202); pattern = /abcd/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD'); addThis(); status = inSection(203); pattern = /a(bc)d/i; string = 'ABCD'; actualmatch = string.match(pattern); expectedmatch = Array('ABCD', 'BC'); addThis(); status = inSection(204); pattern = /a[-]?c/i; string = 'AC'; actualmatch = string.match(pattern); expectedmatch = Array('AC'); addThis(); status = inSection(205); pattern = /(abc)\1/i; string = 'ABCABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABCABC', 'ABC'); addThis(); status = inSection(206); pattern = /([a-c]*)\1/i; string = 'ABCABC'; actualmatch = string.match(pattern); expectedmatch = Array('ABCABC', 'ABC'); addThis(); status = inSection(207); pattern = /a(?!b)./; string = 'abad'; actualmatch = string.match(pattern); expectedmatch = Array('ad'); addThis(); status = inSection(208); pattern = /a(?=d)./; string = 'abad'; actualmatch = string.match(pattern); expectedmatch = Array('ad'); addThis(); status = inSection(209); pattern = /a(?=c|d)./; string = 'abad'; actualmatch = string.match(pattern); expectedmatch = Array('ad'); addThis(); status = inSection(210); pattern = /a(?:b|c|d)(.)/; string = 'ace'; actualmatch = string.match(pattern); expectedmatch = Array('ace', 'e'); addThis(); status = inSection(211); pattern = /a(?:b|c|d)*(.)/; string = 'ace'; actualmatch = string.match(pattern); expectedmatch = Array('ace', 'e'); addThis(); status = inSection(212); pattern = /a(?:b|c|d)+?(.)/; string = 'ace'; actualmatch = string.match(pattern); expectedmatch = Array('ace', 'e'); addThis(); status = inSection(213); pattern = /a(?:b|c|d)+?(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acd', 'd'); addThis(); status = inSection(214); pattern = /a(?:b|c|d)+(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdbe', 'e'); addThis(); status = inSection(215); pattern = /a(?:b|c|d){2}(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdb', 'b'); addThis(); status = inSection(216); pattern = /a(?:b|c|d){4,5}(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdb', 'b'); addThis(); status = inSection(217); pattern = /a(?:b|c|d){4,5}?(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcd', 'd'); addThis(); // MODIFIED - ECMA has different rules for paren contents status = inSection(218); pattern = /((foo)|(bar))*/; string = 'foobar'; actualmatch = string.match(pattern); //expectedmatch = Array('foobar', 'bar', 'foo', 'bar'); expectedmatch = Array('foobar', 'bar', undefined, 'bar'); addThis(); status = inSection(219); pattern = /a(?:b|c|d){6,7}(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdbe', 'e'); addThis(); status = inSection(220); pattern = /a(?:b|c|d){6,7}?(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdbe', 'e'); addThis(); status = inSection(221); pattern = /a(?:b|c|d){5,6}(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdbe', 'e'); addThis(); status = inSection(222); pattern = /a(?:b|c|d){5,6}?(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdb', 'b'); addThis(); status = inSection(223); pattern = /a(?:b|c|d){5,7}(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdbe', 'e'); addThis(); status = inSection(224); pattern = /a(?:b|c|d){5,7}?(.)/; string = 'acdbcdbe'; actualmatch = string.match(pattern); expectedmatch = Array('acdbcdb', 'b'); addThis(); status = inSection(225); pattern = /a(?:b|(c|e){1,2}?|d)+?(.)/; string = 'ace'; actualmatch = string.match(pattern); expectedmatch = Array('ace', 'c', 'e'); addThis(); status = inSection(226); pattern = /^(.+)?B/; string = 'AB'; actualmatch = string.match(pattern); expectedmatch = Array('AB', 'A'); addThis(); /* MODIFIED - ECMA has different rules for paren contents */ status = inSection(227); pattern = /^([^a-z])|(\^)$/; string = '.'; actualmatch = string.match(pattern); //expectedmatch = Array('.', '.', ''); expectedmatch = Array('.', '.', undefined); addThis(); status = inSection(228); pattern = /^[<>]&/; string = '<&OUT'; actualmatch = string.match(pattern); expectedmatch = Array('<&'); addThis(); /* Can't refer to a capture before it's encountered & completed status = inSection(229); pattern = /^(a\1?){4}$/; string = 'aaaaaaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaaaaaa', 'aaaa'); addThis(); status = inSection(230); pattern = /^(a(?(1)\1)){4}$/; string = 'aaaaaaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaaaaaa', 'aaaa'); addThis(); */ status = inSection(231); pattern = /((a{4})+)/; string = 'aaaaaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaaaa', 'aaaaaaaa', 'aaaa'); addThis(); status = inSection(232); pattern = /(((aa){2})+)/; string = 'aaaaaaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaaaa', 'aaaaaaaa', 'aaaa', 'aa'); addThis(); status = inSection(233); pattern = /(((a{2}){2})+)/; string = 'aaaaaaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaaaa', 'aaaaaaaa', 'aaaa', 'aa'); addThis(); status = inSection(234); pattern = /(?:(f)(o)(o)|(b)(a)(r))*/; string = 'foobar'; actualmatch = string.match(pattern); //expectedmatch = Array('foobar', 'f', 'o', 'o', 'b', 'a', 'r'); expectedmatch = Array('foobar', undefined, undefined, undefined, 'b', 'a', 'r'); addThis(); /* ECMA supports (?: (?= and (?! but doesn't support (?< etc. status = inSection(235); pattern = /(?<=a)b/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('b'); addThis(); status = inSection(236); pattern = /(? status = inSection(311); pattern = /(?>a+)b/; string = 'aaab'; actualmatch = string.match(pattern); expectedmatch = Array('aaab'); addThis(); */ status = inSection(312); pattern = /([[:]+)/; string = 'a:[b]:'; actualmatch = string.match(pattern); expectedmatch = Array(':[', ':['); addThis(); status = inSection(313); pattern = /([[=]+)/; string = 'a=[b]='; actualmatch = string.match(pattern); expectedmatch = Array('=[', '=['); addThis(); status = inSection(314); pattern = /([[.]+)/; string = 'a.[b].'; actualmatch = string.match(pattern); expectedmatch = Array('.[', '.['); addThis(); /* ECMA doesn't have rules for [: status = inSection(315); pattern = /[a[:]b[:c]/; string = 'abc'; actualmatch = string.match(pattern); expectedmatch = Array('abc'); addThis(); */ /* ECMA doesn't support (?> status = inSection(316); pattern = /((?>a+)b)/; string = 'aaab'; actualmatch = string.match(pattern); expectedmatch = Array('aaab', 'aaab'); addThis(); status = inSection(317); pattern = /(?>(a+))b/; string = 'aaab'; actualmatch = string.match(pattern); expectedmatch = Array('aaab', 'aaa'); addThis(); status = inSection(318); pattern = /((?>[^()]+)|\([^()]*\))+/; string = '((abc(ade)ufh()()x'; actualmatch = string.match(pattern); expectedmatch = Array('abc(ade)ufh()()x', 'x'); addThis(); */ /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(319); pattern = /\Z/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(320); pattern = /\z/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(321); pattern = /$/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(322); pattern = /\Z/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(323); pattern = /\z/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(324); pattern = /$/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(325); pattern = /\Z/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(326); pattern = /\z/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(327); pattern = /$/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(328); pattern = /\Z/m; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(329); pattern = /\z/m; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(330); pattern = /$/m; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(331); pattern = /\Z/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(332); pattern = /\z/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(333); pattern = /$/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(334); pattern = /\Z/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); status = inSection(335); pattern = /\z/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); */ status = inSection(336); pattern = /$/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array(''); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(337); pattern = /a\Z/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); */ /* $ only matches end of input unless multiline status = inSection(338); pattern = /a$/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); */ /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(339); pattern = /a\Z/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(340); pattern = /a\z/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); */ status = inSection(341); pattern = /a$/; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(342); pattern = /a$/m; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(343); pattern = /a\Z/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); */ status = inSection(344); pattern = /a$/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(345); pattern = /a\Z/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(346); pattern = /a\z/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); */ status = inSection(347); pattern = /a$/m; string = 'b\na'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(348); pattern = /aa\Z/; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); */ /* $ only matches end of input unless multiline status = inSection(349); pattern = /aa$/; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); */ /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(350); pattern = /aa\Z/; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); status = inSection(351); pattern = /aa\z/; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); */ status = inSection(352); pattern = /aa$/; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); status = inSection(353); pattern = /aa$/m; string = 'aa\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(354); pattern = /aa\Z/m; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); */ status = inSection(355); pattern = /aa$/m; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(356); pattern = /aa\Z/m; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); status = inSection(357); pattern = /aa\z/m; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); */ status = inSection(358); pattern = /aa$/m; string = 'b\naa'; actualmatch = string.match(pattern); expectedmatch = Array('aa'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(359); pattern = /ab\Z/; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); */ /* $ only matches end of input unless multiline status = inSection(360); pattern = /ab$/; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); */ /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(361); pattern = /ab\Z/; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(362); pattern = /ab\z/; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); */ status = inSection(363); pattern = /ab$/; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(364); pattern = /ab$/m; string = 'ab\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(365); pattern = /ab\Z/m; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); */ status = inSection(366); pattern = /ab$/m; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(367); pattern = /ab\Z/m; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); status = inSection(368); pattern = /ab\z/m; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); */ status = inSection(369); pattern = /ab$/m; string = 'b\nab'; actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(370); pattern = /abb\Z/; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); */ /* $ only matches end of input unless multiline status = inSection(371); pattern = /abb$/; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); */ /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(372); pattern = /abb\Z/; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); status = inSection(373); pattern = /abb\z/; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); */ status = inSection(374); pattern = /abb$/; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); status = inSection(375); pattern = /abb$/m; string = 'abb\nb\n'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(376); pattern = /abb\Z/m; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); */ status = inSection(377); pattern = /abb$/m; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); /* Perl has \Z has end-of-line, ECMA doesn't status = inSection(378); pattern = /abb\Z/m; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); status = inSection(379); pattern = /abb\z/m; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); */ status = inSection(380); pattern = /abb$/m; string = 'b\nabb'; actualmatch = string.match(pattern); expectedmatch = Array('abb'); addThis(); status = inSection(381); pattern = /(^|x)(c)/; string = 'ca'; actualmatch = string.match(pattern); expectedmatch = Array('c', '', 'c'); addThis(); status = inSection(382); pattern = /foo.bart/; string = 'foo.bart'; actualmatch = string.match(pattern); expectedmatch = Array('foo.bart'); addThis(); status = inSection(383); pattern = /^d[x][x][x]/m; string = 'abcd\ndxxx'; actualmatch = string.match(pattern); expectedmatch = Array('dxxx'); addThis(); status = inSection(384); pattern = /tt+$/; string = 'xxxtt'; actualmatch = string.match(pattern); expectedmatch = Array('tt'); addThis(); /* ECMA spec says that each atom in a range must be a single character status = inSection(385); pattern = /([a-\d]+)/; string = 'za-9z'; actualmatch = string.match(pattern); expectedmatch = Array('9', '9'); addThis(); status = inSection(386); pattern = /([\d-z]+)/; string = 'a0-za'; actualmatch = string.match(pattern); expectedmatch = Array('0-z', '0-z'); addThis(); */ /* ECMA doesn't support [: status = inSection(387); pattern = /([a-[:digit:]]+)/; string = 'za-9z'; actualmatch = string.match(pattern); expectedmatch = Array('a-9', 'a-9'); addThis(); status = inSection(388); pattern = /([[:digit:]-z]+)/; string = '=0-z='; actualmatch = string.match(pattern); expectedmatch = Array('0-z', '0-z'); addThis(); status = inSection(389); pattern = /([[:digit:]-[:alpha:]]+)/; string = '=0-z='; actualmatch = string.match(pattern); expectedmatch = Array('0-z', '0-z'); addThis(); */ status = inSection(390); pattern = /(\d+\.\d+)/; string = '3.1415926'; actualmatch = string.match(pattern); expectedmatch = Array('3.1415926', '3.1415926'); addThis(); status = inSection(391); pattern = /\.c(pp|xx|c)?$/i; string = 'IO.c'; actualmatch = string.match(pattern); expectedmatch = Array('.c', undefined); addThis(); status = inSection(392); pattern = /(\.c(pp|xx|c)?$)/i; string = 'IO.c'; actualmatch = string.match(pattern); expectedmatch = Array('.c', '.c', undefined); addThis(); status = inSection(393); pattern = /(^|a)b/; string = 'ab'; actualmatch = string.match(pattern); expectedmatch = Array('ab', 'a'); addThis(); status = inSection(394); pattern = /^([ab]*?)(b)?(c)$/; string = 'abac'; actualmatch = string.match(pattern); expectedmatch = Array('abac', 'aba', undefined, 'c'); addThis(); status = inSection(395); pattern = /^(?:.,){2}c/i; string = 'a,b,c'; actualmatch = string.match(pattern); expectedmatch = Array('a,b,c'); addThis(); status = inSection(396); pattern = /^(.,){2}c/i; string = 'a,b,c'; actualmatch = string.match(pattern); expectedmatch = Array('a,b,c', 'b,'); addThis(); status = inSection(397); pattern = /^(?:[^,]*,){2}c/; string = 'a,b,c'; actualmatch = string.match(pattern); expectedmatch = Array('a,b,c'); addThis(); status = inSection(398); pattern = /^([^,]*,){2}c/; string = 'a,b,c'; actualmatch = string.match(pattern); expectedmatch = Array('a,b,c', 'b,'); addThis(); status = inSection(399); pattern = /^([^,]*,){3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(400); pattern = /^([^,]*,){3,}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(401); pattern = /^([^,]*,){0,3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(402); pattern = /^([^,]{1,3},){3}d/i; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(403); pattern = /^([^,]{1,3},){3,}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(404); pattern = /^([^,]{1,3},){0,3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(405); pattern = /^([^,]{1,},){3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(406); pattern = /^([^,]{1,},){3,}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(407); pattern = /^([^,]{1,},){0,3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(408); pattern = /^([^,]{0,3},){3}d/i; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(409); pattern = /^([^,]{0,3},){3,}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); status = inSection(410); pattern = /^([^,]{0,3},){0,3}d/; string = 'aaa,b,c,d'; actualmatch = string.match(pattern); expectedmatch = Array('aaa,b,c,d', 'c,'); addThis(); /* ECMA doesn't support \A status = inSection(411); pattern = /(?!\A)x/m; string = 'a\nxb\n'; actualmatch = string.match(pattern); expectedmatch = Array('\n'); addThis(); */ status = inSection(412); pattern = /^(a(b)?)+$/; string = 'aba'; actualmatch = string.match(pattern); expectedmatch = Array('aba', 'a', undefined); addThis(); status = inSection(413); pattern = /^(aa(bb)?)+$/; string = 'aabbaa'; actualmatch = string.match(pattern); expectedmatch = Array('aabbaa', 'aa', undefined); addThis(); status = inSection(414); pattern = /^.{9}abc.*\n/m; string = '123\nabcabcabcabc\n'; actualmatch = string.match(pattern); expectedmatch = Array('abcabcabcabc\n'); addThis(); status = inSection(415); pattern = /^(a)?a$/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', undefined); addThis(); status = inSection(416); pattern = /^(a\1?)(a\1?)(a\2?)(a\3?)$/; string = 'aaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaa', 'a', 'aa', 'a', 'aa'); addThis(); /* Can't refer to a capture before it's encountered & completed status = inSection(417); pattern = /^(a\1?){4}$/; string = 'aaaaaa'; actualmatch = string.match(pattern); expectedmatch = Array('aaaaaa', 'aaa'); addThis(); */ status = inSection(418); pattern = /^(0+)?(?:x(1))?/; string = 'x1'; actualmatch = string.match(pattern); expectedmatch = Array('x1', undefined, '1'); addThis(); status = inSection(419); pattern = /^([0-9a-fA-F]+)(?:x([0-9a-fA-F]+)?)(?:x([0-9a-fA-F]+))?/; string = '012cxx0190'; actualmatch = string.match(pattern); expectedmatch = Array('012cxx0190', '012c', undefined, '0190'); addThis(); status = inSection(420); pattern = /^(b+?|a){1,2}c/; string = 'bbbac'; actualmatch = string.match(pattern); expectedmatch = Array('bbbac', 'a'); addThis(); status = inSection(421); pattern = /^(b+?|a){1,2}c/; string = 'bbbbac'; actualmatch = string.match(pattern); expectedmatch = Array('bbbbac', 'a'); addThis(); status = inSection(422); pattern = /((?:aaaa|bbbb)cccc)?/; string = 'aaaacccc'; actualmatch = string.match(pattern); expectedmatch = Array('aaaacccc', 'aaaacccc'); addThis(); status = inSection(423); pattern = /((?:aaaa|bbbb)cccc)?/; string = 'bbbbcccc'; actualmatch = string.match(pattern); expectedmatch = Array('bbbbcccc', 'bbbbcccc'); addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { if(omitCurrentSection()) return; statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function omitCurrentSection() { try { // current section number is in global status variable var n = status.match(/(\d+)/)[1]; return ((n < cnLBOUND) || (n > cnUBOUND)); } catch(e) { return false; } } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/15.10.4.1-5-n.js0000644000175000017500000000574514433667662024605 0ustar apoapo/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = '15.10.4.1-5-n.js'; /* * * Date: 26 November 2000 * * *SUMMARY: Passing a RegExp object to a RegExp() constructor. *This test arose from Bugzilla bug 61266. The ECMA3 section is: * * 15.10.4.1 new RegExp(pattern, flags) * * If pattern is an object R whose [[Class]] property is "RegExp" and * flags is undefined, then let P be the pattern used to construct R * and let F be the flags used to construct R. If pattern is an object R * whose [[Class]] property is "RegExp" and flags is not undefined, * then throw a TypeError exception. Otherwise, let P be the empty string * if pattern is undefined and ToString(pattern) otherwise, and let F be * the empty string if flags is undefined and ToString(flags) otherwise. * * *The current test will check the second scenario outlined above: * * "pattern" is itself a RegExp object R * "flags" is NOT undefined * * This should throw an exception ... we test for this. * */ //------------------------------------------------------------------------------------------------- var BUGNUMBER = '61266'; var summary = 'Negative test: Passing (RegExp object, flag) to RegExp() constructor'; var statprefix = 'Passing RegExp object on pattern '; var statsuffix = '; passing flag '; var cnFAILURE = 'Expected an exception to be thrown, but none was -'; var singlequote = "'"; var i = -1; var j = -1; var s = ''; var f = ''; var obj1 = {}; var obj2 = {}; var patterns = new Array(); var flags = new Array(); // various regular expressions to try - patterns[0] = ''; patterns[1] = 'abc'; patterns[2] = '(.*)(3-1)\s\w'; patterns[3] = '(.*)(...)\\s\\w'; patterns[4] = '[^A-Za-z0-9_]'; patterns[5] = '[^\f\n\r\t\v](123.5)([4 - 8]$)'; // various flags to try - flags[0] = 'i'; flags[1] = 'g'; flags[2] = 'm'; DESCRIPTION = "Negative test: Passing (RegExp object, flag) to RegExp() constructor" EXPECTED = "error"; //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); for (i in patterns) { s = patterns[i]; for (j in flags) { f = flags[j]; printStatus(getStatus(s, f)); obj1 = new RegExp(s, f); obj2 = new RegExp(obj1, f); // this should cause an exception // WE SHOULD NEVER REACH THIS POINT - reportCompare('PASS', 'FAIL', cnFAILURE); } } exitFunc ('test'); } function getStatus(regexp, flag) { return (statprefix + quote(regexp) + statsuffix + flag); } function quote(text) { return (singlequote + text + singlequote); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-188206.js0000644000175000017500000000771114433667662025456 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 21 January 2003 * SUMMARY: Invalid use of regexp quantifiers should generate SyntaxErrors * * See http://bugzilla.mozilla.org/show_bug.cgi?id=188206 * and http://bugzilla.mozilla.org/show_bug.cgi?id=85721#c48 etc. * and http://bugzilla.mozilla.org/show_bug.cgi?id=190685 * and http://bugzilla.mozilla.org/show_bug.cgi?id=197451 */ //----------------------------------------------------------------------------- var gTestfile = 'regress-188206.js'; var UBound = 0; var BUGNUMBER = 188206; var summary = 'Invalid use of regexp quantifiers should generate SyntaxErrors'; var TEST_PASSED = 'SyntaxError'; var TEST_FAILED = 'Generated an error, but NOT a SyntaxError!'; var TEST_FAILED_BADLY = 'Did not generate ANY error!!!'; var CHECK_PASSED = 'Should not generate an error'; var CHECK_FAILED = 'Generated an error!'; var status = ''; var statusitems = []; var actual = ''; var actualvalues = []; var expect= ''; var expectedvalues = []; /* * All the following are invalid uses of regexp quantifiers and * should generate SyntaxErrors. That's what we're testing for. * * To allow the test to compile and run, we have to hide the errors * inside eval strings, and check they are caught at run-time - * */ status = inSection(1); testThis(' /a**/ '); status = inSection(2); testThis(' /a***/ '); status = inSection(3); testThis(' /a++/ '); status = inSection(4); testThis(' /a+++/ '); /* * The ? quantifier, unlike * or +, may appear twice in succession. * Thus we need at least three in a row to provoke a SyntaxError - */ status = inSection(5); testThis(' /a???/ '); status = inSection(6); testThis(' /a????/ '); /* * Now do some weird things on the left side of the regexps - */ status = inSection(9); testThis(' /+a/ '); status = inSection(10); testThis(' /++a/ '); status = inSection(11); testThis(' /?a/ '); status = inSection(12); testThis(' /??a/ '); /* * Misusing the {DecmalDigits} quantifier - according to BOTH ECMA and Perl. * * Just as with the * and + quantifiers above, can't have two {DecmalDigits} * quantifiers in succession - it's a SyntaxError. */ status = inSection(28); testThis(' /x{1}{1}/ '); status = inSection(29); testThis(' /x{1,}{1}/ '); status = inSection(30); testThis(' /x{1,2}{1}/ '); status = inSection(31); testThis(' /x{1}{1,}/ '); status = inSection(32); testThis(' /x{1,}{1,}/ '); status = inSection(33); testThis(' /x{1,2}{1,}/ '); status = inSection(34); testThis(' /x{1}{1,2}/ '); status = inSection(35); testThis(' /x{1,}{1,2}/ '); status = inSection(36); testThis(' /x{1,2}{1,2}/ '); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- /* * Invalid syntax should generate a SyntaxError */ function testThis(sInvalidSyntax) { expect = TEST_PASSED; actual = TEST_FAILED_BADLY; try { eval(sInvalidSyntax); } catch(e) { if (e instanceof SyntaxError) actual = TEST_PASSED; else actual = TEST_FAILED; } statusitems[UBound] = status; expectedvalues[UBound] = expect; actualvalues[UBound] = actual; UBound++; } /* * Allowed syntax shouldn't generate any errors */ function checkThis(sAllowedSyntax) { expect = CHECK_PASSED; actual = CHECK_PASSED; try { eval(sAllowedSyntax); } catch(e) { actual = CHECK_FAILED; } statusitems[UBound] = status; expectedvalues[UBound] = expect; actualvalues[UBound] = actual; UBound++; } function test() { enterFunc('test'); printBugNumber(BUGNUMBER); printStatus(summary); for (var i=0; i((.*\n?)*?)<\/body>/i; actualmatch = string.match(pattern); expectedmatch = Array(sBody, '\n

      Kibology for all

      \n

      All for Kibology

      \n', '

      All for Kibology

      \n'); addThis(); //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-285219.js0000755000175000017500000000127514433667662025462 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-285219.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 285219; var summary = 'Do not crash on RangeError: reserved slot out of range'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); var o = {hi: 'there'}; eval("var r = /re(1)(2)(3)/g", o); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-375715-03.js0000755000175000017500000000161114433667662025675 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-375715-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 375715; var summary = 'Do not assert: (c2 <= cs->length) && (c1 <= c2)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); /[_-t]/i.exec(""); reportCompare(expect, actual, summary + '/[_-t]/i.exec("")'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-78156.js0000644000175000017500000000443214433667662025375 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Date: 06 February 2001 * * SUMMARY: Arose from Bugzilla bug 78156: * "m flag of regular expression does not work with $" * * See http://bugzilla.mozilla.org/show_bug.cgi?id=78156 * * The m flag means a regular expression should search strings * across multiple lines, i.e. across '\n', '\r'. */ //----------------------------------------------------------------------------- var gTestfile = 'regress-78156.js'; var i = 0; var BUGNUMBER = 78156; var summary = 'Testing regular expressions with ^, $, and the m flag -'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); /* * All patterns have an m flag; all strings are multiline. * Looking for digit characters at beginning/end of lines. */ string = 'aaa\n789\r\nccc\r\n345'; status = inSection(1); pattern = /^\d/gm; actualmatch = string.match(pattern); expectedmatch = ['7','3']; addThis(); status = inSection(2); pattern = /\d$/gm; actualmatch = string.match(pattern); expectedmatch = ['9','5']; addThis(); string = 'aaa\n789\r\nccc\r\nddd'; status = inSection(3); pattern = /^\d/gm; actualmatch = string.match(pattern); expectedmatch = ['7']; addThis(); status = inSection(4); pattern = /\d$/gm; actualmatch = string.match(pattern); expectedmatch = ['9']; addThis(); //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-122076.js0000644000175000017500000000444014433667662025443 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 12 Feb 2002 * SUMMARY: Don't crash on invalid regexp literals / \\/ / * * See http://bugzilla.mozilla.org/show_bug.cgi?id=122076 * The function checkURL() below sometimes caused a compile-time error: * * SyntaxError: unterminated parenthetical (: * * However, sometimes it would cause a crash instead. The presence of * other functions below is merely fodder to help provoke the crash. * The constant |STRESS| is number of times we'll try to crash on this. * */ //----------------------------------------------------------------------------- var gTestfile = 'regress-122076.js'; var BUGNUMBER = 122076; var summary = "Don't crash on invalid regexp literals / \\/ /"; var STRESS = 10; var sEval = ''; printBugNumber(BUGNUMBER); printStatus(summary); sEval += 'function checkDate()' sEval += '{' sEval += 'return (this.value.search(/^[012]?\d\/[0123]?\d\/[0]\d$/) != -1);' sEval += '}' sEval += 'function checkDNSName()' sEval += '{' sEval += ' return (this.value.search(/^([\w\-]+\.)+([\w\-]{2,3})$/) != -1);' sEval += '}' sEval += 'function checkEmail()' sEval += '{' sEval += ' return (this.value.search(/^([\w\-]+\.)*[\w\-]+@([\w\-]+\.)+([\w\-]{2,3})$/) != -1);' sEval += '}' sEval += 'function checkHostOrIP()' sEval += '{' sEval += ' if (this.value.search(/^([\w\-]+\.)+([\w\-]{2,3})$/) == -1)' sEval += ' return (this.value.search(/^[1-2]?\d{1,2}\.[1-2]?\d{1,2}\.[1-2]?\d{1,2}\.[1-2]?\d{1,2}$/) != -1);' sEval += ' else' sEval += ' return true;' sEval += '}' sEval += 'function checkIPAddress()' sEval += '{' sEval += ' return (this.value.search(/^[1-2]?\d{1,2}\.[1-2]?\d{1,2}\.[1-2]?\d{1,2}\.[1-2]?\d{1,2}$/) != -1);' sEval += '}' sEval += 'function checkURL()' sEval += '{' sEval += ' return (this.value.search(/^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,4}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\/\*\$+@&#;`~=%!]*)(\.\w{2,})?)*\/?)$/) != -1);' sEval += '}' for (var i=0; ia+)ab/; string = 'aaab'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(106); pattern = /a\Z/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(107); pattern = /a\z/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(108); pattern = /a$/; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(109); pattern = /a\z/; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(110); pattern = /a\z/m; string = 'a\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(111); pattern = /a\z/m; string = 'b\na\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(112); pattern = /aa\Z/; string = 'aa\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(113); pattern = /aa\z/; string = 'aa\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(114); pattern = /aa$/; string = 'aa\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(115); pattern = /aa\z/; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(116); pattern = /aa\z/m; string = 'aa\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(117); pattern = /aa\z/m; string = 'b\naa\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(118); pattern = /aa\Z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(119); pattern = /aa\z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(120); pattern = /aa$/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(121); pattern = /aa\Z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(122); pattern = /aa\z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(123); pattern = /aa$/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(124); pattern = /aa\Z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(125); pattern = /aa\z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(126); pattern = /aa$/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(127); pattern = /aa\Z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(128); pattern = /aa\z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(129); pattern = /aa$/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(130); pattern = /aa\Z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(131); pattern = /aa\z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(132); pattern = /aa$/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(133); pattern = /aa\Z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(134); pattern = /aa\z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(135); pattern = /aa$/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(136); pattern = /aa\Z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(137); pattern = /aa\z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(138); pattern = /aa$/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(139); pattern = /aa\Z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(140); pattern = /aa\z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(141); pattern = /aa$/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(142); pattern = /aa\Z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(143); pattern = /aa\z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(144); pattern = /aa$/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(145); pattern = /aa\Z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(146); pattern = /aa\z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(147); pattern = /aa$/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(148); pattern = /aa\Z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(149); pattern = /aa\z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(150); pattern = /aa$/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(151); pattern = /aa\Z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(152); pattern = /aa\z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(153); pattern = /aa$/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(154); pattern = /ab\Z/; string = 'ab\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(155); pattern = /ab\z/; string = 'ab\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(156); pattern = /ab$/; string = 'ab\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(157); pattern = /ab\z/; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(158); pattern = /ab\z/m; string = 'ab\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(159); pattern = /ab\z/m; string = 'b\nab\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(160); pattern = /ab\Z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(161); pattern = /ab\z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(162); pattern = /ab$/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(163); pattern = /ab\Z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(164); pattern = /ab\z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(165); pattern = /ab$/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(166); pattern = /ab\Z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(167); pattern = /ab\z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(168); pattern = /ab$/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(169); pattern = /ab\Z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(170); pattern = /ab\z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(171); pattern = /ab$/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(172); pattern = /ab\Z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(173); pattern = /ab\z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(174); pattern = /ab$/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(175); pattern = /ab\Z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(176); pattern = /ab\z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(177); pattern = /ab$/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(178); pattern = /ab\Z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(179); pattern = /ab\z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(180); pattern = /ab$/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(181); pattern = /ab\Z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(182); pattern = /ab\z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(183); pattern = /ab$/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(184); pattern = /ab\Z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(185); pattern = /ab\z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(186); pattern = /ab$/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(187); pattern = /ab\Z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(188); pattern = /ab\z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(189); pattern = /ab$/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(190); pattern = /ab\Z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(191); pattern = /ab\z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(192); pattern = /ab$/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(193); pattern = /ab\Z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(194); pattern = /ab\z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(195); pattern = /ab$/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(196); pattern = /abb\Z/; string = 'abb\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(197); pattern = /abb\z/; string = 'abb\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(198); pattern = /abb$/; string = 'abb\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(199); pattern = /abb\z/; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(200); pattern = /abb\z/m; string = 'abb\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(201); pattern = /abb\z/m; string = 'b\nabb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(202); pattern = /abb\Z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(203); pattern = /abb\z/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(204); pattern = /abb$/; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(205); pattern = /abb\Z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(206); pattern = /abb\z/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(207); pattern = /abb$/; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(208); pattern = /abb\Z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(209); pattern = /abb\z/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(210); pattern = /abb$/; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(211); pattern = /abb\Z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(212); pattern = /abb\z/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(213); pattern = /abb$/m; string = 'ac\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(214); pattern = /abb\Z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(215); pattern = /abb\z/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(216); pattern = /abb$/m; string = 'b\nac\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(217); pattern = /abb\Z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(218); pattern = /abb\z/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(219); pattern = /abb$/m; string = 'b\nac'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(220); pattern = /abb\Z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(221); pattern = /abb\z/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(222); pattern = /abb$/; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(223); pattern = /abb\Z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(224); pattern = /abb\z/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(225); pattern = /abb$/; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(226); pattern = /abb\Z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(227); pattern = /abb\z/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(228); pattern = /abb$/; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(229); pattern = /abb\Z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(230); pattern = /abb\z/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(231); pattern = /abb$/m; string = 'ca\nb\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(232); pattern = /abb\Z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(233); pattern = /abb\z/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(234); pattern = /abb$/m; string = 'b\nca\n'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(235); pattern = /abb\Z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(236); pattern = /abb\z/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(237); pattern = /abb$/m; string = 'b\nca'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(238); pattern = /a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz/; string = 'x'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(239); pattern = /\GX.*X/; string = 'aaaXbX'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(240); pattern = /\.c(pp|xx|c)?$/i; string = 'Changes'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(241); pattern = /^([a-z]:)/; string = 'C:/'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(242); pattern = /(\w)?(abc)\1b/; string = 'abcab'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); /* ECMA doesn't support (?( status = inSection(243); pattern = /^(a)?(?(1)a|b)+$/; string = 'a'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); */ //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { if(omitCurrentSection()) return; statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function omitCurrentSection() { try { // current section number is in global status variable var n = status.match(/(\d+)/)[1]; return ((n < cnLBOUND) || (n > cnUBOUND)); } catch(e) { return false; } } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/15.10.4.1-6.js0000644000175000017500000000313014433667662024335 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* 15.10.4.1 new RegExp(pattern, flags) If F contains any character other than "g", "i", or" m", or if it contains the same one more than once, then throw a SyntaxError exception. */ var gTestfile = '15.10.4.1-6.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476940; var summary = 'Section 15.10.4.1 - RegExp with invalid flags'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var invalidflags = ['ii', 'gg', 'mm', 'a']; for (var i = 0; i < invalidflags.length; i++) { var flag = invalidflags[i]; expect = 'SyntaxError: invalid regular expression flag ' + flag.charAt(0); actual = ''; try { new RegExp('bar', flag); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': new RegExp("bar", "' + flag + '")'); actual = ''; try { eval("/bar/" + flag); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': /bar/' + flag + ')'); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-24712.js0000644000175000017500000000117514433667662025363 0ustar apoapo/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-24712.js'; test(); function test() { enterFunc ("test"); printBugNumber (24712); var re = /([\S]+([ \t]+[\S]+)*)[ \t]*=[ \t]*[\S]+/; var result = re.exec("Course_Creator = Test") + ''; reportCompare('Course_Creator = Test,Course_Creator,', result, 'exec() returned null'); exitFunc ("test"); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-330684.js0000755000175000017500000000146414433667662025457 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-330684.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 330684; var summary = 'Do not hang on RegExp'; var actual = 'Do not hang on RegExp'; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); var re = /^(?:(?:%[0-9A-Fa-f]{2})*[!\$&'\*-;=\?-Z_a-z]*)+$/; var url = "http://tw.yimg.com/a/tw/wenchuan/cam_240x400_381615_030806_2.swf?clickTAG=javascript:VRECopenWindow(1)"; printStatus(re.test(url)); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-346090.js0000755000175000017500000000163114433667662025451 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-346090.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 346090; var summary = 'Do not crash with this regexp'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var r = /%((h[^l]+)|(l[^h]+)){0,2}?a/g; r.exec('%lld %d'); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-100199.js0000644000175000017500000001332014433667662025442 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Date: 17 September 2001 * * SUMMARY: Regression test for Bugzilla bug 100199 * See http://bugzilla.mozilla.org/show_bug.cgi?id=100199 * * The empty character class [] is a valid RegExp construct: the condition * that a given character belong to a set containing no characters. As such, * it can never be met and is always FALSE. Similarly, [^] is a condition * that matches any given character and is always TRUE. * * Neither one of these conditions should cause syntax errors in a RegExp. */ //----------------------------------------------------------------------------- var gTestfile = 'regress-100199.js'; var i = 0; var BUGNUMBER = 100199; var summary = '[], [^] are valid RegExp conditions. Should not cause errors -'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); pattern = /[]/; string = 'abc'; status = inSection(1); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ''; status = inSection(2); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '['; status = inSection(3); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '/'; status = inSection(4); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '['; status = inSection(5); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ']'; status = inSection(6); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '[]'; status = inSection(7); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '[ ]'; status = inSection(8); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ']['; status = inSection(9); actualmatch = string.match(pattern); expectedmatch = null; addThis(); pattern = /a[]/; string = 'abc'; status = inSection(10); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ''; status = inSection(11); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = 'a['; status = inSection(12); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = 'a[]'; status = inSection(13); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '['; status = inSection(14); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ']'; status = inSection(15); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '[]'; status = inSection(16); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = '[ ]'; status = inSection(17); actualmatch = string.match(pattern); expectedmatch = null; addThis(); string = ']['; status = inSection(18); actualmatch = string.match(pattern); expectedmatch = null; addThis(); pattern = /[^]/; string = 'abc'; status = inSection(19); actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); string = ''; status = inSection(20); actualmatch = string.match(pattern); expectedmatch = null; //there are no characters to test against the condition addThis(); string = '\/'; status = inSection(21); actualmatch = string.match(pattern); expectedmatch = Array('/'); addThis(); string = '\['; status = inSection(22); actualmatch = string.match(pattern); expectedmatch = Array('['); addThis(); string = '['; status = inSection(23); actualmatch = string.match(pattern); expectedmatch = Array('['); addThis(); string = ']'; status = inSection(24); actualmatch = string.match(pattern); expectedmatch = Array(']'); addThis(); string = '[]'; status = inSection(25); actualmatch = string.match(pattern); expectedmatch = Array('['); addThis(); string = '[ ]'; status = inSection(26); actualmatch = string.match(pattern); expectedmatch = Array('['); addThis(); string = ']['; status = inSection(27); actualmatch = string.match(pattern); expectedmatch = Array(']'); addThis(); pattern = /a[^]/; string = 'abc'; status = inSection(28); actualmatch = string.match(pattern); expectedmatch = Array('ab'); addThis(); string = ''; status = inSection(29); actualmatch = string.match(pattern); expectedmatch = null; //there are no characters to test against the condition addThis(); string = 'a['; status = inSection(30); actualmatch = string.match(pattern); expectedmatch = Array('a['); addThis(); string = 'a]'; status = inSection(31); actualmatch = string.match(pattern); expectedmatch = Array('a]'); addThis(); string = 'a[]'; status = inSection(32); actualmatch = string.match(pattern); expectedmatch = Array('a['); addThis(); string = 'a[ ]'; status = inSection(33); actualmatch = string.match(pattern); expectedmatch = Array('a['); addThis(); string = 'a]['; status = inSection(34); actualmatch = string.match(pattern); expectedmatch = Array('a]'); addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-375715-04.js0000755000175000017500000000206214433667662025677 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-375715-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 375715; var summary = 'Do not assert: (c2 <= cs->length) && (c1 <= c2)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { expect = 'SyntaxError: invalid range in character class'; (new RegExp("[\xDF-\xC7]]", "i")).exec(""); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + '(new RegExp("[\xDF-\xC7]]", "i")).exec("")'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-105972.js0000644000175000017500000000656514433667662025463 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Date: 22 October 2001 * * SUMMARY: Regression test for Bugzilla bug 105972: * "/^.*?$/ will not match anything" * * See http://bugzilla.mozilla.org/show_bug.cgi?id=105972 */ //----------------------------------------------------------------------------- var gTestfile = 'regress-105972.js'; var i = 0; var BUGNUMBER = 105972; var summary = 'Regression test for Bugzilla bug 105972'; var cnEmptyString = ''; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); /* * The bug: this match was coming up null in Rhino and SpiderMonkey. * It should match the whole string. The reason: * * The * operator is greedy, but *? is non-greedy: it will stop * at the simplest match it can find. But the pattern here asks us * to match till the end of the string. So the simplest match must * go all the way out to the end, and *? has no choice but to do it. */ status = inSection(1); pattern = /^.*?$/; string = 'Hello World'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Leave off the '$' condition - here we expect the empty string. * Unlike the above pattern, we don't have to match till the end of * the string, so the non-greedy operator *? doesn't try to... */ status = inSection(2); pattern = /^.*?/; string = 'Hello World'; actualmatch = string.match(pattern); expectedmatch = Array(cnEmptyString); addThis(); /* * Try '$' combined with an 'or' operator. * * The operator *? will consume the string from left to right, * attempting to satisfy the condition (:|$). When it hits ':', * the match will stop because the operator *? is non-greedy. * * The submatch $1 = (:|$) will contain the ':' */ status = inSection(3); pattern = /^.*?(:|$)/; string = 'Hello: World'; actualmatch = string.match(pattern); expectedmatch = Array('Hello:', ':'); addThis(); /* * Again, '$' combined with an 'or' operator. * * The operator * will consume the string from left to right, * attempting to satisfy the condition (:|$). When it hits ':', * the match will not stop since * is greedy. The match will * continue until it hits $, the end-of-string boundary. * * The submatch $1 = (:|$) will contain the empty string * conceived to exist at the end-of-string boundary. */ status = inSection(4); pattern = /^.*(:|$)/; string = 'Hello: World'; actualmatch = string.match(pattern); expectedmatch = Array(string, cnEmptyString); addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/15.10.6.2-1.js0000644000175000017500000000636014433667662024343 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Date: 23 October 2001 * * SUMMARY: Testing regexps with the global flag set. * NOT every substring fitting the given pattern will be matched. * The parent string is CONSUMED as successive matches are found. * * From the ECMA-262 Final spec: * * 15.10.6.2 RegExp.prototype.exec(string) * Performs a regular expression match of string against the regular * expression and returns an Array object containing the results of * the match, or null if the string did not match. * * The string ToString(string) is searched for an occurrence of the * regular expression pattern as follows: * * 1. Let S be the value of ToString(string). * 2. Let length be the length of S. * 3. Let lastIndex be the value of the lastIndex property. * 4. Let i be the value of ToInteger(lastIndex). * 5. If the global property is false, let i = 0. * 6. If i < 0 or i > length then set lastIndex to 0 and return null. * 7. Call [[Match]], giving it the arguments S and i. * If [[Match]] returned failure, go to step 8; * otherwise let r be its State result and go to step 10. * 8. Let i = i+1. * 9. Go to step 6. * 10. Let e be r's endIndex value. * 11. If the global property is true, set lastIndex to e. * * etc. * * * So when the global flag is set, |lastIndex| is incremented every time * there is a match; not from i to i+1, but from i to "endIndex" e: * * e = (index of last input character matched so far by the pattern) + 1 * * Thus in the example below, the first endIndex e occurs after the * first match 'a b'. The next match will begin AFTER this, and so * will NOT be 'b c', but rather 'c d'. Similarly, 'd e' won't be matched. */ //----------------------------------------------------------------------------- var gTestfile = '15.10.6.2-1.js'; var i = 0; var BUGNUMBER = '(none)'; var summary = 'Testing regexps with the global flag set'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); status = inSection(1); string = 'a b c d e'; pattern = /\w\s\w/g; actualmatch = string.match(pattern); expectedmatch = ['a b','c d']; // see above explanation - addThis(); status = inSection(2); string = '12345678'; pattern = /\d\d\d/g; actualmatch = string.match(pattern); expectedmatch = ['123','456']; addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-225289.js0000644000175000017500000000617414433667662025463 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 10 November 2003 * SUMMARY: Testing regexps with complementary alternatives * * See http://bugzilla.mozilla.org/show_bug.cgi?id=225289 * */ //----------------------------------------------------------------------------- var gTestfile = 'regress-225289.js'; var i = 0; var BUGNUMBER = 225289; var summary = 'Testing regexps with complementary alternatives'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); // this pattern should match any string! pattern = /a|[^a]/; status = inSection(1); string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a'); addThis(); status = inSection(2); string = ''; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(3); string = '()'; actualmatch = string.match(pattern); expectedmatch = Array('('); addThis(); pattern = /(a|[^a])/; status = inSection(4); string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a'); addThis(); status = inSection(5); string = ''; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(6); string = '()'; actualmatch = string.match(pattern); expectedmatch = Array('(', '('); addThis(); pattern = /(a)|([^a])/; status = inSection(7); string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', 'a', undefined); addThis(); status = inSection(8); string = ''; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(9); string = '()'; actualmatch = string.match(pattern); expectedmatch = Array('(', undefined, '('); addThis(); // note this pattern has one non-capturing parens pattern = /((?:a|[^a])*)/g; status = inSection(10); string = 'a'; actualmatch = string.match(pattern); expectedmatch = Array('a', ''); // see bug 225289 comment 6 addThis(); status = inSection(11); string = ''; actualmatch = string.match(pattern); expectedmatch = Array(''); // see bug 225289 comment 9 addThis(); status = inSection(12); string = '()'; actualmatch = string.match(pattern); expectedmatch = Array('()', ''); // see bug 225289 comment 6 addThis(); //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/octal-002.js0000644000175000017500000001206214433667662024632 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 31 July 2002 * SUMMARY: Testing regexps containing octal escape sequences * This is an elaboration of mozilla/js/tests/ecma_2/RegExp/octal-003.js * * See http://bugzilla.mozilla.org/show_bug.cgi?id=141078 * for a reference on octal escape sequences in regexps. * * NOTE: * We will use the identities '\011' === '\u0009' === '\x09' === '\t' * * The first is an octal escape sequence (\(0-3)OO; O an octal digit). * See ECMA-262 Edition 2, Section 7.7.4 "String Literals". These were * dropped in Edition 3 but we support them for backward compatibility. * * The second is a Unicode escape sequence (\uHHHH; H a hex digit). * Since octal 11 = hex 9, the two escapes define the same character. * * The third is a hex escape sequence (\xHH; H a hex digit). * Since hex 09 = hex 0009, this defines the same character. * * The fourth is the familiar escape sequence for a horizontal tab, * defined in the ECMA spec as having Unicode value \u0009. */ //----------------------------------------------------------------------------- var gTestfile = 'octal-002.js'; var i = 0; var BUGNUMBER = 141078; var summary = 'Testing regexps containing octal escape sequences'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); /* * Test a string containing the null character '\0' followed by the string '11' * * 'a' + String.fromCharCode(0) + '11'; * * Note we can't simply write 'a\011', because '\011' would be interpreted * as the octal escape sequence for the tab character (see above). * * We should get no match from the regexp /.\011/, because it should be * looking for the octal escape sequence \011, i.e. the tab character - * */ status = inSection(1); pattern = /.\011/; string = 'a' + String.fromCharCode(0) + '11'; actualmatch = string.match(pattern); expectedmatch = null; addThis(); /* * Try same thing with 'xx' in place of '11'. * * Should get a match now, because the octal escape sequence in the regexp * has been reduced from \011 to \0, and '\0' is present in the string - */ status = inSection(2); pattern = /.\0xx/; string = 'a' + String.fromCharCode(0) + 'xx'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Same thing; don't use |String.fromCharCode(0)| this time. * There is no ambiguity in '\0xx': it is the null character * followed by two x's, no other interpretation is possible. */ status = inSection(3); pattern = /.\0xx/; string = 'a\0xx'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * This one should produce a match. The two-character string * 'a' + '\011' is duplicated in the pattern and test string: */ status = inSection(4); pattern = /.\011/; string = 'a\011'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Same as above, only now, for the second character of the string, * use the Unicode escape '\u0009' instead of the octal escape '\011' */ status = inSection(5); pattern = /.\011/; string = 'a\u0009'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Same as above, only now for the second character of the string, * use the hex escape '\x09' instead of the octal escape '\011' */ status = inSection(6); pattern = /.\011/; string = 'a\x09'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Same as above, only now for the second character of the string, * use the escape '\t' instead of the octal escape '\011' */ status = inSection(7); pattern = /.\011/; string = 'a\t'; actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); /* * Return to the string from Section 1. * * Unlike Section 1, use the RegExp() function to create the * regexp pattern: null character followed by the string '11'. * * Since this is exactly what the string is, we should get a match - */ status = inSection(8); string = 'a' + String.fromCharCode(0) + '11'; pattern = RegExp(string); actualmatch = string.match(pattern); expectedmatch = Array(string); addThis(); //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/RegExp/regress-76683.js0000644000175000017500000000443414433667662025402 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Date: 01 May 2001 * * SUMMARY: Regression test for Bugzilla bug 76683 on Rhino: * "RegExp regression (NullPointerException)" * * See http://bugzilla.mozilla.org/show_bug.cgi?id=76683 */ //----------------------------------------------------------------------------- var gTestfile = 'regress-76683.js'; var i = 0; var BUGNUMBER = 76683; var summary = 'Regression test for Bugzilla bug 76683'; var status = ''; var statusmessages = new Array(); var pattern = ''; var patterns = new Array(); var string = ''; var strings = new Array(); var actualmatch = ''; var actualmatches = new Array(); var expectedmatch = ''; var expectedmatches = new Array(); /* * Rhino (2001-04-19) crashed on the 3rd regular expression below. * It didn't matter what the string was. No problem in SpiderMonkey - */ string = 'abc'; status = inSection(1); pattern = /()|(<([\$\w:\.\-]+)((([ ][^\/>]*)?\/>)|(([ ][^>]*)?>)))/; actualmatch = string.match(pattern); expectedmatch = null; addThis(); status = inSection(2); pattern = /()|(<(tagPattern)((([ ][^\/>]*)?\/>)|(([ ][^>]*)?>)))/; actualmatch = string.match(pattern); expectedmatch = null; addThis(); // This was the one causing a Rhino crash - status = inSection(3); pattern = /()|(<(tagPattern)((([ ][^\/>]*)?\/>)|(([ ][^>]*)?>)))|(<\/tagPattern[^>]*>)/; actualmatch = string.match(pattern); expectedmatch = null; addThis(); //------------------------------------------------------------------------------------------------- test(); //------------------------------------------------------------------------------------------------- function addThis() { statusmessages[i] = status; patterns[i] = pattern; strings[i] = string; actualmatches[i] = actualmatch; expectedmatches[i] = expectedmatch; i++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); testRegExp(statusmessages, patterns, strings, actualmatches, expectedmatches); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/Statements/0000755000175000017500000000000014433667662023627 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/Statements/shell.js0000644000175000017500000000035414433667662025276 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Statements'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/ecma_3/Statements/regress-131348.js0000644000175000017500000000554514433667662026411 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * * Date: 10 Apr 2002 * Revised: 14 July 2002 * * SUMMARY: JS should NOT error on |for(i in undefined)|, |for(i in null)| * * ECMA-262 3rd Edition Final spec says such statements SHOULD error. See: * * Section 12.6.4 The for-in Statement * Section 9.9 ToObject * * * But SpiderMonkey has decided NOT to follow this; it's a bug in the spec. * See http://bugzilla.mozilla.org/show_bug.cgi?id=131348 * * Update: Rhino has also decided not to follow the spec on this * See http://bugzilla.mozilla.org/show_bug.cgi?id=136893 */ //----------------------------------------------------------------------------- var gTestfile = 'regress-131348.js'; var UBound = 0; var BUGNUMBER = 131348; var summary = 'JS should not error on |for(i in undefined)|, |for(i in null)|'; var TEST_PASSED = 'No error'; var TEST_FAILED = 'An error was generated!!!'; var status = ''; var statusitems = []; var actual = ''; var actualvalues = []; var expect= ''; var expectedvalues = []; status = inSection(1); expect = TEST_PASSED; actual = TEST_PASSED; try { for (var i in undefined) { print(i); } } catch(e) { actual = TEST_FAILED; } addThis(); status = inSection(2); expect = TEST_PASSED; actual = TEST_PASSED; try { for (var i in null) { print(i); } } catch(e) { actual = TEST_FAILED; } addThis(); status = inSection(3); expect = TEST_PASSED; actual = TEST_PASSED; /* * Variable names that cannot be looked up generate ReferenceErrors; however, * property names like obj.ZZZ that cannot be looked up are set to |undefined| * * Therefore, this should indirectly test | for (var i in undefined) | */ try { for (var i in this.ZZZ) { print(i); } } catch(e) { actual = TEST_FAILED; } addThis(); status = inSection(4); expect = TEST_PASSED; actual = TEST_PASSED; /* * The result of an unsuccessful regexp match is the null value * Therefore, this should indirectly test | for (var i in null) | */ try { for (var i in 'bbb'.match(/aaa/)) { print(i); } } catch(e) { actual = TEST_FAILED; } addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function addThis() { statusitems[UBound] = status; actualvalues[UBound] = actual; expectedvalues[UBound] = expect; UBound++; } function test() { enterFunc('test'); printBugNumber(BUGNUMBER); printStatus(summary); for (var i=0; i * See http://bugzilla.mozilla.org/show_bug.cgi?id=101488 * * Without the "new" keyword, assigning arr.length = Number(n) worked. * But with it, Rhino was giving an error "Inappropriate array length" * and SpiderMonkey was exiting without giving any error or return value - * * Comments on the Rhino code by igor@icesoft.no: * * jsSet_length requires that the new length value should be an instance * of Number. But according to Ecma 15.4.5.1, item 12-13, an error should * be thrown only if ToUint32(length_value) != ToNumber(length_value) */ //----------------------------------------------------------------------------- var gTestfile = 'regress-101488.js'; var UBound = 0; var BUGNUMBER = 101488; var summary = 'Try assigning arr.length = new Number(n)'; var status = ''; var statusitems = []; var actual = ''; var actualvalues = []; var expect= ''; var expectedvalues = []; var arr = []; status = inSection(1); arr = Array(); tryThis('arr.length = new Number(1);'); actual = arr.length; expect = 1; addThis(); status = inSection(2); arr = Array(5); tryThis('arr.length = new Number(1);'); actual = arr.length; expect = 1; addThis(); status = inSection(3); arr = Array(); tryThis('arr.length = new Number(17);'); actual = arr.length; expect = 17; addThis(); status = inSection(4); arr = Array(5); tryThis('arr.length = new Number(17);'); actual = arr.length; expect = 17; addThis(); /* * Also try the above with the "new" keyword before Array(). * Array() and new Array() should be equivalent, by ECMA 15.4.1.1 */ status = inSection(5); arr = new Array(); tryThis('arr.length = new Number(1);'); actual = arr.length; expect = 1; addThis(); status = inSection(6); arr = new Array(5); tryThis('arr.length = new Number(1);'); actual = arr.length; expect = 1; addThis(); arr = new Array(); tryThis('arr.length = new Number(17);'); actual = arr.length; expect = 17; addThis(); status = inSection(7); arr = new Array(5); tryThis('arr.length = new Number(17);'); actual = arr.length; expect = 17; addThis(); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function tryThis(s) { try { eval(s); } catch(e) { // keep going } } function addThis() { statusitems[UBound] = status; actualvalues[UBound] = actual; expectedvalues[UBound] = expect; UBound++; } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); for (var i=0; i>= 4; b = digits[ch & 0xf]; ch >>= 4; if (ch) { c = digits[ch & 0xf]; ch >>= 4; d = digits[ch & 0xf]; result += "\\u" + d + c + b + a; } else { result += "\\x" + b + a; } } return result; } /* * assertEq(actual, expected) * Throw if the two arguments are not === * see https://bugzilla.mozilla.org/show_bug.cgi?id=480199 */ if (typeof assertEq == 'undefined') { var assertEq = function (actual, expected) { if (actual !== expected) { throw new TypeError('Assertion failed: got "' + actual + '", expected "' + expected); } }; } /* * Compare expected result to actual result, if they differ (in value and/or * type) report a failure. If description is provided, include it in the * failure report. */ function reportCompare (expected, actual, description) { var expected_t = typeof expected; var actual_t = typeof actual; var output = ""; if (typeof description == "undefined") { description = ''; } else if (VERBOSE) { printStatus ("Comparing '" + description + "'"); } if (expected_t != actual_t) { output += "Type mismatch, expected type " + expected_t + ", actual type " + actual_t + " "; } else if (VERBOSE) { printStatus ("Expected type '" + expected_t + "' matched actual " + "type '" + actual_t + "'"); } if (expected != actual) { output += "Expected value '" + toPrinted(expected) + "', Actual value '" + toPrinted(actual) + "' "; } else if (VERBOSE) { printStatus ("Expected value '" + toPrinted(expected) + "' matched actual value '" + toPrinted(actual) + "'"); } var testcase = new TestCase(gTestfile, description, expected, actual); testcase.reason = output; if (testcase.passed) { print(PASSED + description); } else { reportFailure (description + " : " + output); } return testcase.passed; } /* * Attempt to match a regular expression describing the result to * the actual result, if they differ (in value and/or * type) report a failure. If description is provided, include it in the * failure report. */ function reportMatch (expectedRegExp, actual, description) { var expected_t = "string"; var actual_t = typeof actual; var output = ""; if (typeof description == "undefined") { description = ''; } else if (VERBOSE) { printStatus ("Comparing '" + description + "'"); } if (expected_t != actual_t) { output += "Type mismatch, expected type " + expected_t + ", actual type " + actual_t + " "; } else if (VERBOSE) { printStatus ("Expected type '" + expected_t + "' matched actual " + "type '" + actual_t + "'"); } var matches = expectedRegExp.test(actual); if (!matches) { output += "Expected match to '" + toPrinted(expectedRegExp) + "', Actual value '" + toPrinted(actual) + "' "; } else if (VERBOSE) { printStatus ("Expected match to '" + toPrinted(expectedRegExp) + "' matched actual value '" + toPrinted(actual) + "'"); } var testcase = new TestCase(gTestfile, description, true, matches); testcase.reason = output; if (testcase.passed) { print(PASSED + description); } else { reportFailure (description + " : " + output); } return testcase.passed; } /* * Puts funcName at the top of the call stack. This stack is used to show * a function-reported-from field when reporting failures. */ function enterFunc (funcName) { if (!funcName.match(/\(\)$/)) funcName += "()"; callStack.push(funcName); } /* * Pops the top funcName off the call stack. funcName is optional, and can be * used to check push-pop balance. */ function exitFunc (funcName) { var lastFunc = callStack.pop(); if (funcName) { if (!funcName.match(/\(\)$/)) funcName += "()"; if (lastFunc != funcName) reportCompare(funcName, lastFunc, "Test driver failure wrong exit function "); } } /* * Peeks at the top of the call stack. */ function currentFunc() { return callStack[callStack.length - 1]; } /* Calculate the "order" of a set of data points {X: [], Y: []} by computing successive "derivatives" of the data until the data is exhausted or the derivative is linear. */ function BigO(data) { var order = 0; var origLength = data.X.length; while (data.X.length > 2) { var lr = new LinearRegression(data); if (lr.b > 1e-6) { // only increase the order if the slope // is "great" enough order++; } if (lr.r > 0.98 || lr.Syx < 1 || lr.b < 1e-6) { // terminate if close to a line lr.r // small error lr.Syx // small slope lr.b break; } data = dataDeriv(data); } if (2 == origLength - order) { order = Number.POSITIVE_INFINITY; } return order; function LinearRegression(data) { /* y = a + bx for data points (Xi, Yi); 0 <= i < n b = (n*SUM(XiYi) - SUM(Xi)*SUM(Yi))/(n*SUM(Xi*Xi) - SUM(Xi)*SUM(Xi)) a = (SUM(Yi) - b*SUM(Xi))/n */ var i; if (data.X.length != data.Y.length) { throw 'LinearRegression: data point length mismatch'; } if (data.X.length < 3) { throw 'LinearRegression: data point length < 2'; } var n = data.X.length; var X = data.X; var Y = data.Y; this.Xavg = 0; this.Yavg = 0; var SUM_X = 0; var SUM_XY = 0; var SUM_XX = 0; var SUM_Y = 0; var SUM_YY = 0; for (i = 0; i < n; i++) { SUM_X += X[i]; SUM_XY += X[i]*Y[i]; SUM_XX += X[i]*X[i]; SUM_Y += Y[i]; SUM_YY += Y[i]*Y[i]; } this.b = (n * SUM_XY - SUM_X * SUM_Y)/(n * SUM_XX - SUM_X * SUM_X); this.a = (SUM_Y - this.b * SUM_X)/n; this.Xavg = SUM_X/n; this.Yavg = SUM_Y/n; var SUM_Ydiff2 = 0; var SUM_Xdiff2 = 0; var SUM_XdiffYdiff = 0; for (i = 0; i < n; i++) { var Ydiff = Y[i] - this.Yavg; var Xdiff = X[i] - this.Xavg; SUM_Ydiff2 += Ydiff * Ydiff; SUM_Xdiff2 += Xdiff * Xdiff; SUM_XdiffYdiff += Xdiff * Ydiff; } var Syx2 = (SUM_Ydiff2 - Math.pow(SUM_XdiffYdiff/SUM_Xdiff2, 2))/(n - 2); var r2 = Math.pow((n*SUM_XY - SUM_X * SUM_Y), 2) / ((n*SUM_XX - SUM_X*SUM_X)*(n*SUM_YY-SUM_Y*SUM_Y)); this.Syx = Math.sqrt(Syx2); this.r = Math.sqrt(r2); } function dataDeriv(data) { if (data.X.length != data.Y.length) { throw 'length mismatch'; } var length = data.X.length; if (length < 2) { throw 'length ' + length + ' must be >= 2'; } var X = data.X; var Y = data.Y; var deriv = {X: [], Y: [] }; for (var i = 0; i < length - 1; i++) { deriv.X[i] = (X[i] + X[i+1])/2; deriv.Y[i] = (Y[i+1] - Y[i])/(X[i+1] - X[i]); } return deriv; } return 0; } function compareSource(expect, actual, summary) { // compare source var expectP = expect. replace(/([(){},.:\[\]])/mg, ' $1 '). replace(/(\w+)/mg, ' $1 '). replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>'). replace(/\s+/mg, ' '). replace(/new (\w+)\s*\(\s*\)/mg, 'new $1'); var actualP = actual. replace(/([(){},.:\[\]])/mg, ' $1 '). replace(/(\w+)/mg, ' $1 '). replace(/<(\/)? (\w+) (\/)?>/mg, '<$1$2$3>'). replace(/\s+/mg, ' '). replace(/new (\w+)\s*\(\s*\)/mg, 'new $1'); print('expect:\n' + expectP); print('actual:\n' + actualP); reportCompare(expectP, actualP, summary); // actual must be compilable if expect is? try { var expectCompile = 'No Error'; var actualCompile; eval(expect); try { eval(actual); actualCompile = 'No Error'; } catch(ex1) { actualCompile = ex1 + ''; } reportCompare(expectCompile, actualCompile, summary + ': compile actual'); } catch(ex) { } } function optionsInit() { // record initial values to support resetting // options to their initial values options.initvalues = {}; // record values in a stack to support pushing // and popping options options.stackvalues = []; var optionNames = options().split(','); for (var i = 0; i < optionNames.length; i++) { var optionName = optionNames[i]; if (optionName) { options.initvalues[optionName] = ''; } } } function optionsClear() { // turn off current settings var optionNames = options().split(','); for (var i = 0; i < optionNames.length; i++) { var optionName = optionNames[i]; if (optionName) { options(optionName); } } } function optionsPush() { var optionsframe = {}; options.stackvalues.push(optionsframe); var optionNames = options().split(','); for (var i = 0; i < optionNames.length; i++) { var optionName = optionNames[i]; if (optionName) { optionsframe[optionName] = ''; } } optionsClear(); } function optionsPop() { var optionsframe = options.stackvalues.pop(); optionsClear(); for (optionName in optionsframe) { options(optionName); } } function optionsReset() { try { optionsClear(); // turn on initial settings for (optionName in options.initvalues) { options(optionName); } } catch(ex) { print('optionsReset: caught ' + ex); } } if (typeof options == 'function') { optionsInit(); optionsClear(); } function getTestCaseResult(expected, actual) { if (typeof expected != typeof actual) return false; if (typeof expected != 'number') // Note that many tests depend on the use of '==' here, not '==='. return actual == expected; // Distinguish NaN from other values. Using x != x comparisons here // works even if tests redefine isNaN. if (actual != actual) return expected != expected; if (expected != expected) return false; // Tolerate a certain degree of error. if (actual != expected) return Math.abs(actual - expected) <= 1E-10; // Here would be a good place to distinguish 0 and -0, if we wanted // to. However, doing so would introduce a number of failures in // areas where they don't seem important. For example, the WeekDay // function in ECMA-262 returns -0 for Sundays before the epoch, but // the Date functions in SpiderMonkey specified in terms of WeekDay // often don't. This seems unimportant. return true; } if (typeof dump == 'undefined') { if (typeof window == 'undefined' && typeof print == 'function') { dump = print; } else { dump = (function () {}); } } function test() { for ( gTc=0; gTc < gTestcases.length; gTc++ ) { // temporary hack to work around some unknown issue in 1.7 try { gTestcases[gTc].passed = writeTestCaseResult( gTestcases[gTc].expect, gTestcases[gTc].actual, gTestcases[gTc].description +" = "+ gTestcases[gTc].actual ); gTestcases[gTc].reason += ( gTestcases[gTc].passed ) ? "" : "wrong value "; } catch(e) { print('test(): empty testcase for gTc = ' + gTc + ' ' + e); } } stopTest(); return ( gTestcases ); } /* * Begin printing functions. These functions use the shell's * print function. When running tests in the browser, these * functions, override these functions with functions that use * document.write. */ function writeTestCaseResult( expect, actual, string ) { var passed = getTestCaseResult( expect, actual ); writeFormattedResult( expect, actual, string, passed ); return passed; } function writeFormattedResult( expect, actual, string, passed ) { var s = ( passed ? PASSED : FAILED ) + string + ' expected: ' + expect; print(s); return passed; } function writeHeaderToLog( string ) { print( string ); } /* end of print functions */ /* * When running in the shell, run the garbage collector after the * test has completed. */ function stopTest() { var gc; if ( gc != undefined ) { gc(); } } /* * Convenience function for displaying failed test cases. Useful * when running tests manually. * */ function getFailedCases() { for ( var i = 0; i < gTestcases.length; i++ ) { if ( ! gTestcases[i].passed ) { print( gTestcases[i].description + " = " +gTestcases[i].actual + " expected: " + gTestcases[i].expect ); } } } function jsTestDriverEnd() { // gDelayTestDriverEnd is used to // delay collection of the test result and // signal to Spider so that tests can continue // to run after page load has fired. They are // responsible for setting gDelayTestDriverEnd = true // then when completed, setting gDelayTestDriverEnd = false // then calling jsTestDriverEnd() if (gDelayTestDriverEnd) { return; } try { optionsReset(); } catch(ex) { dump('jsTestDriverEnd ' + ex); } for (var i = 0; i < gTestcases.length; i++) { gTestcases[i].dump(); } } function jit(on) { if (on && !options().match(/jit/)) { options('jit'); } else if (!on && options().match(/jit/)) { options('jit'); } } /* * Some tests need to know if we are in Rhino as opposed to SpiderMonkey */ function inRhino() { return (typeof defineClass == "function"); } /* these functions are useful for running tests manually in Rhino */ function GetContext() { return Packages.com.netscape.javascript.Context.getCurrentContext(); } function OptLevel( i ) { i = Number(i); var cx = GetContext(); cx.setOptimizationLevel(i); } /* end of Rhino functions */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/0000755000175000017500000000000014433667662021270 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/shell.js0000644000175000017500000000046514433667662022742 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsuite = 'js1_3'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/jsref.js0000644000175000017500000001240514433667662022741 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var completed = false; var testcases; SECTION = ""; VERSION = ""; BUGNUMBER =""; var EXCLUDE = ""; TZ_DIFF = -8; var TT = ""; var TT_ = ""; var BR = ""; var NBSP = " "; var CR = "\n"; var FONT = ""; var FONT_ = ""; var FONT_RED = ""; var FONT_GREEN = ""; var B = ""; var B_ = "" var H2 = ""; var H2_ = ""; var HR = ""; var DEBUG = false; version(130); var PASSED = " PASSED!" var FAILED = " FAILED! expected: "; function test() { for ( tc=0; tc < testcases.length; tc++ ) { testcases[tc].passed = writeTestCaseResult( testcases[tc].expect, testcases[tc].actual, testcases[tc].description +" = "+ testcases[tc].actual ); testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value "; } stopTest(); return ( testcases ); } function TestCase( n, d, e, a ) { this.name = n; this.description = d; this.expect = e; this.actual = a; this.passed = true; this.reason = ""; this.bugnumber = BUGNUMBER; this.passed = getTestCaseResult( this.expect, this.actual ); if ( DEBUG ) { print( "added " + this.description ); } } function startTest() { // JavaScript 1.3 is supposed to be compliant ecma version 1.0 if ( VERSION == "ECMA_1" ) { version ( "130" ); } if ( VERSION == "JS_1.3" ) { version ( "130" ); } if ( VERSION == "JS_1.2" ) { version ( "120" ); } if ( VERSION == "JS_1.1" ) { version ( "110" ); } // for ecma version 2.0, we will leave the javascript version to // the default ( for now ). } function getTestCaseResult( expect, actual ) { // because ( NaN == NaN ) always returns false, need to do // a special compare to see if we got the right result. if ( actual != actual ) { if ( typeof actual == "object" ) { actual = "NaN object"; } else { actual = "NaN number"; } } if ( expect != expect ) { if ( typeof expect == "object" ) { expect = "NaN object"; } else { expect = "NaN number"; } } var passed = ( expect == actual ) ? true : false; // if both objects are numbers // need to replace w/ IEEE standard for rounding if ( !passed && typeof(actual) == "number" && typeof(expect) == "number" ) { if ( Math.abs(actual-expect) < 0.0000001 ) { passed = true; } } // verify type is the same if ( typeof(expect) != typeof(actual) ) { passed = false; } return passed; } function writeTestCaseResult( expect, actual, string ) { var passed = getTestCaseResult( expect, actual ); writeFormattedResult( expect, actual, string, passed ); return passed; } function writeFormattedResult( expect, actual, string, passed ) { var s = TT + string ; for ( k = 0; k < (60 - string.length >= 0 ? 60 - string.length : 5) ; k++ ) { } s += B ; s += ( passed ) ? FONT_GREEN + NBSP + PASSED : FONT_RED + NBSP + FAILED + expect + TT_ ; print( s + FONT_ + B_ + TT_ ); return passed; } function writeHeaderToLog( string ) { print( H2 + string + H2_ ); } function stopTest() { var sizeTag = "<#TEST CASES SIZE>"; var doneTag = "<#TEST CASES DONE>"; var beginTag = "<#TEST CASE "; var endTag = ">"; print(sizeTag); print(testcases.length); for (tc = 0; tc < testcases.length; tc++) { print(beginTag + 'PASSED' + endTag); print(testcases[tc].passed); print(beginTag + 'NAME' + endTag); print(testcases[tc].name); print(beginTag + 'EXPECTED' + endTag); print(testcases[tc].expect); print(beginTag + 'ACTUAL' + endTag); print(testcases[tc].actual); print(beginTag + 'DESCRIPTION' + endTag); print(testcases[tc].description); print(beginTag + 'REASON' + endTag); print(( testcases[tc].passed ) ? "" : "wrong value "); print(beginTag + 'BUGNUMBER' + endTag); print( BUGNUMBER ); } print(doneTag); print( HR ); gc(); } function getFailedCases() { for ( var i = 0; i < testcases.length; i++ ) { if ( ! testcases[i].passed ) { print( testcases[i].description +" = " +testcases[i].actual +" expected: "+ testcases[i].expect ); } } } function err( msg, page, line ) { testcases[tc].actual = "error"; testcases[tc].reason = msg; writeTestCaseResult( testcases[tc].expect, testcases[tc].actual, testcases[tc].description +" = "+ testcases[tc].actual + ": " + testcases[tc].reason ); stopTest(); return true; } function Enumerate ( o ) { var p; for ( p in o ) { print( p +": " + o[p] ); } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/0000755000175000017500000000000014433667662022742 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/shell.js0000644000175000017500000000035114433667662024406 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'regress'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/new-001.js0000644000175000017500000000317614433667662024376 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'new-001.js'; /** File Name: new-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=76103 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "new-001"; var VERSION = "JS1_3"; var TITLE = "new-001"; var BUGNUMBER="31567"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Test_One (x) { this.v = x+1; return x*2 } function Test_Two( x, y ) { this.v = x; return y; } new TestCase( SECTION, "Test_One(18)", 36, Test_One(18) ); new TestCase( SECTION, "new Test_One(18)", "[object Object]", new Test_One(18) +"" ); new TestCase( SECTION, "new Test_One(18).v", 19, new Test_One(18).v ); new TestCase( SECTION, "Test_Two(2,7)", 7, Test_Two(2,7) ); new TestCase( SECTION, "new Test_Two(2,7)", "[object Object]", new Test_Two(2,7) +"" ); new TestCase( SECTION, "new Test_Two(2,7).v", 2, new Test_Two(2,7).v ); new TestCase( SECTION, "new (Function)(\"x\", \"return x+3\")(5,6)", 8, new (Function)("x","return x+3")(5,6) ); new TestCase( SECTION, "new new Test_Two(String, 2).v(0123)", "83", new new Test_Two(String, 2).v(0123) +""); new TestCase( SECTION, "new new Test_Two(String, 2).v(0123).length", 2, new new Test_Two(String, 2).v(0123).length ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/in-001.js0000644000175000017500000000143714433667662024211 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'in-001.js'; /** File Name: in-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=196109 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "in-001"; var VERSION = "JS1_3"; var TITLE = "Regression test for 196109"; var BUGNUMBER="196109"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); o = {}; o.foo = 'sil'; new TestCase( SECTION, "\"foo\" in o", true, "foo" in o ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/delete-001.js0000644000175000017500000000223614433667662025043 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'delete-001.js'; /** File Name: delete-001.js Section: regress Description: Regression test for http://scopus.mcom.com/bugsplat/show_bug.cgi?id=108736 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "JS1_2"; var VERSION = "JS1_2"; var TITLE = "The variable statement"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); // delete all properties of the global object // per ecma, this does not affect variables in the global object declared // with var or functions for ( p in this ) { delete p; } var result =""; for ( p in this ) { result += String( p ); } // not too picky here... just want to make sure we didn't crash or something new TestCase( SECTION, "delete all properties of the global object", "PASSED", result == "" ? "FAILED" : "PASSED" ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/function-002.js0000644000175000017500000000211214433667662025420 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-002.js'; /** File Name: function-002.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=249579 function definitions in conditional statements should be allowed. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "function-002"; var VERSION = "JS1_3"; var TITLE = "Regression test for 249579"; var BUGNUMBER="249579"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "0?function(){}:0", 0, 0?function(){}:0 ); bar = true; foo = bar ? function () { return true; } : function() { return false; }; new TestCase( SECTION, "bar = true; foo = bar ? function () { return true; } : function() { return false; }; foo()", true, foo() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/function-001-n.js0000644000175000017500000000241214433667662025655 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-001-n.js'; /** * File Name: boolean-001.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=99232 * * eval("function f(){}function g(){}") at top level is an error for JS1.2 * and above (missing ; between named function expressions), but declares f * and g as functions below 1.2. * * Fails to produce error regardless of version: * js> version(100) * 120 * js> eval("function f(){}function g(){}") * js> version(120); * 100 * js> eval("function f(){}function g(){}") * js> * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "function-001.js"; var VERSION = "JS_1.3"; var TITLE = "functions not separated by semicolons are errors in version 120 and higher"; var BUGNUMBER="10278"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "eval(\"function f(){}function g(){}\")", "error", eval("function f(){}function g(){}") ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/browser.js0000644000175000017500000000000014433667662024751 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/CVS/0000755000175000017500000000000014433667662023375 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/CVS/Repository0000644000175000017500000000003714433667662025477 0ustar apoapomozilla/js/tests/js1_3/regress closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/CVS/Entries0000644000175000017500000000055114433667662024732 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:53 2005// /delete-001.js/1.5/Sat May 26 00:19:35 2007// /function-001-n.js/1.7/Sat May 26 00:19:35 2007// /function-002.js/1.5/Sat May 26 00:19:35 2007// /in-001.js/1.5/Sat May 26 00:19:35 2007// /new-001.js/1.5/Sat May 26 00:19:35 2007// /shell.js/1.2/Sat May 26 00:19:35 2007// /switch-001.js/1.5/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/CVS/Root0000644000175000017500000000006314433667662024242 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/regress/switch-001.js0000644000175000017500000000233014433667662025075 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'switch-001.js'; /** File Name: switch-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=315767 Verify that switches do not use strict equality in versions of JavaScript < 1.4. It's now been decided that we won't put in version switches, so all switches will be ECMA. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "switch-001"; var VERSION = "JS1_3"; var TITLE = "switch-001"; var BUGNUMBER="315767"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); result = "fail: did not enter switch"; switch (true) { case 1: result = "fail: version 130 should force strict equality"; break; case true: result = "pass"; break; default: result = "fail: evaluated default statement"; } new TestCase( SECTION, "switch / case should use strict equality in version of JS < 1.4", "pass", result ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/0000755000175000017500000000000014433667662022732 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_11.js0000644000175000017500000000423314433667662024736 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_11.js'; /** File Name: proto_11.js Section: Description: Global Information in Constructors This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_11"; var VERSION = "JS1_3"; var TITLE = "Global Information in Constructors"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var idCounter = 1; function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; this.id = idCounter++; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Employee( "Toonces, Pat", "Tech Pubs" ) var terry = new Employee( "O'Sherry Terry", "Marketing" ); var les = new Engineer( "Morris, Les", new Array("JavaScript"), "indy" ); new TestCase( SECTION, "pat.id", 5, pat.id ); new TestCase( SECTION, "terry.id", 6, terry.id ); new TestCase( SECTION, "les.id", 7, les.id ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/shell.js0000644000175000017500000000035114433667662024376 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'inherit'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_12.js0000644000175000017500000000515114433667662024737 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_12.js'; /** File Name: proto_12.js Section: Description: new PrototypeObject This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm No Multiple Inheritance Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_12"; var VERSION = "JS1_3"; var TITLE = "No Multiple Inheritance"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; this.id = idCounter++; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Hobbyist( hobby ) { this.hobby = hobby || "yodeling"; } function Engineer ( name, projs, machine, hobby ) { this.base1 = WorkerBee; this.base1( name, "engineering", projs ) this.base2 = Hobbyist; this.base2( hobby ); this.projects = projs || new Array(); this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var idCounter = 1; var les = new Engineer( "Morris, Les", new Array("JavaScript"), "indy" ); Hobbyist.prototype.equipment = [ "horn", "mountain", "goat" ]; new TestCase( SECTION, "les.name", "Morris, Les", les.name ); new TestCase( SECTION, "les.dept", "engineering", les.dept ); Array.prototype.getClass = Object.prototype.toString; new TestCase( SECTION, "les.projects.getClass()", "[object Array]", les.projects.getClass() ); new TestCase( SECTION, "les.projects[0]", "JavaScript", les.projects[0] ); new TestCase( SECTION, "les.machine", "indy", les.machine ); new TestCase( SECTION, "les.hobby", "yodeling", les.hobby ); new TestCase( SECTION, "les.equpment", void 0, les.equipment ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_9.js0000644000175000017500000000350514433667662024666 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_9.js'; /** File Name: proto_9.js Section: Description: Local versus Inherited Values This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. This tests Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_9"; var VERSION = "JS1_3"; var TITLE = "Local versus Inherited Values"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function WorkerBee ( name, dept, projs ) { this.projects = new Array(); } WorkerBee.prototype = new Employee(); var pat = new WorkerBee() Employee.prototype.specialty = "none"; Employee.prototype.name = "Unknown"; Array.prototype.getClass = Object.prototype.toString; // Pat, the WorkerBee new TestCase( SECTION, "pat.name", "", pat.name ); new TestCase( SECTION, "pat.dept", "general", pat.dept ); new TestCase( SECTION, "pat.projects.getClass", "[object Array]", pat.projects.getClass() ); new TestCase( SECTION, "pat.projects.length", 0, pat.projects.length ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_8.js0000644000175000017500000000444614433667662024672 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_8.js'; /** File Name: proto_8.js Section: Description: Adding Properties to the Prototype Object This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_8"; var VERSION = "JS1_3"; var TITLE = "Adding Properties to the Prototype Object"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Engineer( "Toonces, Pat", ["SpiderMonkey", "Rhino"], "indy" ); Employee.prototype.specialty = "none"; // Pat, the Engineer new TestCase( SECTION, "pat.name", "Toonces, Pat", pat.name ); new TestCase( SECTION, "pat.dept", "engineering", pat.dept ); new TestCase( SECTION, "pat.projects.length", 2, pat.projects.length ); new TestCase( SECTION, "pat.projects[0]", "SpiderMonkey", pat.projects[0] ); new TestCase( SECTION, "pat.projects[1]", "Rhino", pat.projects[1] ); new TestCase( SECTION, "pat.machine", "indy", pat.machine ); new TestCase( SECTION, "pat.specialty", "none", pat.specialty ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_6.js0000644000175000017500000000614514433667662024666 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_6.js'; /** File Name: proto_6.js Section: Description: Logical OR || in constructors This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. This tests the logical OR opererator || syntax in constructors. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_6"; var VERSION = "JS1_3"; var TITLE = "Logical OR || in constructors"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Engineer( "Toonces, Pat", ["SpiderMonkey", "Rhino"], "indy" ); var les = new WorkerBee( "Morris, Les", "Training", ["Hippo"] ) var terry = new Employee( "Boomberi, Terry", "Marketing" ); // Pat, the Engineer new TestCase( SECTION, "pat.name", "Toonces, Pat", pat.name ); new TestCase( SECTION, "pat.dept", "engineering", pat.dept ); new TestCase( SECTION, "pat.projects.length", 2, pat.projects.length ); new TestCase( SECTION, "pat.projects[0]", "SpiderMonkey", pat.projects[0] ); new TestCase( SECTION, "pat.projects[1]", "Rhino", pat.projects[1] ); new TestCase( SECTION, "pat.machine", "indy", pat.machine ); // Les, the WorkerBee new TestCase( SECTION, "les.name", "Morris, Les", les.name ); new TestCase( SECTION, "les.dept", "Training", les.dept ); new TestCase( SECTION, "les.projects.length", 1, les.projects.length ); new TestCase( SECTION, "les.projects[0]", "Hippo", les.projects[0] ); // Terry, the Employee new TestCase( SECTION, "terry.name", "Boomberi, Terry", terry.name ); new TestCase( SECTION, "terry.dept", "Marketing", terry.dept ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_7.js0000644000175000017500000000447014433667662024666 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_7.js'; /** File Name: proto_7.js Section: Description: Adding Properties to the Prototype Object This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. This tests Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_6"; var VERSION = "JS1_3"; var TITLE = "Adding properties to the Prototype Object"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } // Engineer.prototype = new WorkerBee(); var pat = new Engineer( "Toonces, Pat", ["SpiderMonkey", "Rhino"], "indy" ); Employee.prototype.specialty = "none"; // Pat, the Engineer new TestCase( SECTION, "pat.name", "Toonces, Pat", pat.name ); new TestCase( SECTION, "pat.dept", "engineering", pat.dept ); new TestCase( SECTION, "pat.projects.length", 2, pat.projects.length ); new TestCase( SECTION, "pat.projects[0]", "SpiderMonkey", pat.projects[0] ); new TestCase( SECTION, "pat.projects[1]", "Rhino", pat.projects[1] ); new TestCase( SECTION, "pat.machine", "indy", pat.machine ); new TestCase( SECTION, "pat.specialty", void 0, pat.specialty ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_10.js0000644000175000017500000000476114433667662024743 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_10.js'; /** File Name: proto_10.js Section: Description: Determining Instance Relationships This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_10"; var VERSION = "JS1_3"; var TITLE = "Determining Instance Relationships"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function InstanceOf( object, constructor ) { return object instanceof constructor; } function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Engineer(); new TestCase( SECTION, "InstanceOf( pat, Engineer )", true, InstanceOf( pat, Engineer ) ); new TestCase( SECTION, "InstanceOf( pat, WorkerBee )", true, InstanceOf( pat, WorkerBee ) ); new TestCase( SECTION, "InstanceOf( pat, Employee )", true, InstanceOf( pat, Employee ) ); new TestCase( SECTION, "InstanceOf( pat, Object )", true, InstanceOf( pat, Object ) ); new TestCase( SECTION, "InstanceOf( pat, SalesPerson )", false, InstanceOf ( pat, SalesPerson ) ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/browser.js0000644000175000017500000000000014433667662024741 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_1.js0000644000175000017500000000614114433667662024655 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_1.js'; /** File Name: proto_1.js Section: Description: new PrototypeObject This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_1"; var VERSION = "JS1_3"; var TITLE = "new PrototypeObject"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee () { this.name = ""; this.dept = "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee () { this.projects = new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer () { this.dept = "engineering"; this.machine = ""; } Engineer.prototype = new WorkerBee(); var jim = new Employee(); new TestCase( SECTION, "jim = new Employee(); jim.name", "", jim.name ); new TestCase( SECTION, "jim = new Employee(); jim.dept", "general", jim.dept ); var sally = new Manager(); new TestCase( SECTION, "sally = new Manager(); sally.name", "", sally.name ); new TestCase( SECTION, "sally = new Manager(); sally.dept", "general", sally.dept ); new TestCase( SECTION, "sally = new Manager(); sally.reports.length", 0, sally.reports.length ); new TestCase( SECTION, "sally = new Manager(); typeof sally.reports", "object", typeof sally.reports ); var fred = new SalesPerson(); new TestCase( SECTION, "fred = new SalesPerson(); fred.name", "", fred.name ); new TestCase( SECTION, "fred = new SalesPerson(); fred.dept", "sales", fred.dept ); new TestCase( SECTION, "fred = new SalesPerson(); fred.quota", 100, fred.quota ); new TestCase( SECTION, "fred = new SalesPerson(); fred.projects.length", 0, fred.projects.length ); var jane = new Engineer(); new TestCase( SECTION, "jane = new Engineer(); jane.name", "", jane.name ); new TestCase( SECTION, "jane = new Engineer(); jane.dept", "engineering", jane.dept ); new TestCase( SECTION, "jane = new Engineer(); jane.projects.length", 0, jane.projects.length ); new TestCase( SECTION, "jane = new Engineer(); jane.machine", "", jane.machine ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/CVS/0000755000175000017500000000000014433667662023365 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/CVS/Repository0000644000175000017500000000003714433667662025467 0ustar apoapomozilla/js/tests/js1_3/inherit closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/CVS/Entries0000644000175000017500000000100714433667662024717 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:52 2005// /proto_1.js/1.5/Sat May 26 00:19:35 2007// /proto_10.js/1.6/Sat May 26 00:19:35 2007// /proto_11.js/1.5/Sat May 26 00:19:35 2007// /proto_12.js/1.5/Sat May 26 00:19:35 2007// /proto_3.js/1.5/Sat May 26 00:19:35 2007// /proto_4.js/1.5/Sat May 26 00:19:35 2007// /proto_6.js/1.5/Sat May 26 00:19:35 2007// /proto_7.js/1.5/Sat May 26 00:19:35 2007// /proto_8.js/1.5/Sat May 26 00:19:35 2007// /proto_9.js/1.5/Sat May 26 00:19:35 2007// /shell.js/1.2/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/CVS/Root0000644000175000017500000000006314433667662024232 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_3.js0000644000175000017500000000350014433667662024653 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_3.js'; /** File Name: proto_3.js Section: Description: Adding properties to an instance This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_3"; var VERSION = "JS1_3"; var TITLE = "Adding properties to an Instance"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee () { this.name = ""; this.dept = "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee () { this.projects = new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer () { this.dept = "engineering"; this.machine = ""; } Engineer.prototype = new WorkerBee(); var jim = new Employee(); var pat = new Employee(); jim.bonus = 300; new TestCase( SECTION, "jim = new Employee(); jim.bonus = 300; jim.bonus", 300, jim.bonus ); new TestCase( SECTION, "pat = new Employee(); pat.bonus", void 0, pat.bonus ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/inherit/proto_4.js0000644000175000017500000000613614433667662024664 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_4.js'; /** File Name: proto_4.js Section: Description: new PrototypeObject This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. If you add a property to an object in the prototype chain, instances of objects that derive from that prototype should inherit that property, even if they were instatiated after the property was added to the prototype object. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_3"; var VERSION = "JS1_3"; var TITLE = "Adding properties to the prototype"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee () { this.name = ""; this.dept = "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee () { this.projects = new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer () { this.dept = "engineering"; this.machine = ""; } Engineer.prototype = new WorkerBee(); var jim = new Employee(); var terry = new Engineer(); var sean = new SalesPerson(); var wally = new Manager(); Employee.prototype.specialty = "none"; var pat = new Employee(); var leslie = new Engineer(); var bubbles = new SalesPerson(); var furry = new Manager(); Engineer.prototype.specialty = "code"; var chris = new Engineer(); new TestCase( SECTION, "jim = new Employee(); jim.specialty", "none", jim.specialty ); new TestCase( SECTION, "terry = new Engineer(); terry.specialty", "code", terry.specialty ); new TestCase( SECTION, "sean = new SalesPerson(); sean.specialty", "none", sean.specialty ); new TestCase( SECTION, "wally = new Manager(); wally.specialty", "none", wally.specialty ); new TestCase( SECTION, "furry = new Manager(); furry.specialty", "none", furry.specialty ); new TestCase( SECTION, "pat = new Employee(); pat.specialty", "none", pat.specialty ); new TestCase( SECTION, "leslie = new Engineer(); leslie.specialty", "code", leslie.specialty ); new TestCase( SECTION, "bubbles = new SalesPerson(); bubbles.specialty", "none", bubbles.specialty ); new TestCase( SECTION, "chris = new Employee(); chris.specialty", "code", chris.specialty ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/0000755000175000017500000000000014433667662022223 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/shell.js0000644000175000017500000000034614433667662023673 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'misc'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/browser.js0000644000175000017500000000000014433667662024232 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/CVS/0000755000175000017500000000000014433667662022656 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/CVS/Repository0000644000175000017500000000003414433667662024755 0ustar apoapomozilla/js/tests/js1_3/misc closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/CVS/Entries0000644000175000017500000000012614433667662024211 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:52 2005// /shell.js/1.2/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/misc/CVS/Root0000644000175000017500000000006314433667662023523 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/README0000755000175000017500000000001714433667662022151 0ustar apoapoJavaScript 1.3 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/0000755000175000017500000000000014433667662022534 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/shell.js0000644000175000017500000000035014433667662024177 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Script'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/new-001.js0000644000175000017500000000317614433667662024170 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'new-001.js'; /** File Name: new-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=76103 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "new-001"; var VERSION = "JS1_3"; var TITLE = "new-001"; var BUGNUMBER="31567"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Test_One (x) { this.v = x+1; return x*2 } function Test_Two( x, y ) { this.v = x; return y; } new TestCase( SECTION, "Test_One(18)", 36, Test_One(18) ); new TestCase( SECTION, "new Test_One(18)", "[object Object]", new Test_One(18) +"" ); new TestCase( SECTION, "new Test_One(18).v", 19, new Test_One(18).v ); new TestCase( SECTION, "Test_Two(2,7)", 7, Test_Two(2,7) ); new TestCase( SECTION, "new Test_Two(2,7)", "[object Object]", new Test_Two(2,7) +"" ); new TestCase( SECTION, "new Test_Two(2,7).v", 2, new Test_Two(2,7).v ); new TestCase( SECTION, "new (Function)(\"x\", \"return x+3\")(5,6)", 8, new (Function)("x","return x+3")(5,6) ); new TestCase( SECTION, "new new Test_Two(String, 2).v(0123)", "83", new new Test_Two(String, 2).v(0123) +""); new TestCase( SECTION, "new new Test_Two(String, 2).v(0123).length", 2, new new Test_Two(String, 2).v(0123).length ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/in-001.js0000644000175000017500000000143614433667662024002 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'in-001.js'; /** File Name: in-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=196109 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "in-001"; var VERSION = "JS1_3"; var TITLE = "Regression test for 196109"; var BUGNUMBER="196109"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); o = {}; o.foo = 'sil'; new TestCase( SECTION, "\"foo\" in o", true, "foo" in o ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/delete-001.js0000644000175000017500000000223614433667662024635 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'delete-001.js'; /** File Name: delete-001.js Section: regress Description: Regression test for http://scopus.mcom.com/bugsplat/show_bug.cgi?id=108736 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "JS1_2"; var VERSION = "JS1_2"; var TITLE = "The variable statement"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); // delete all properties of the global object // per ecma, this does not affect variables in the global object declared // with var or functions for ( p in this ) { delete p; } var result =""; for ( p in this ) { result += String( p ); } // not too picky here... just want to make sure we didn't crash or something new TestCase( SECTION, "delete all properties of the global object", "PASSED", result == "" ? "FAILED" : "PASSED" ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/function-002.js0000644000175000017500000000211214433667662025212 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-002.js'; /** File Name: function-002.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=249579 function definitions in conditional statements should be allowed. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "function-002"; var VERSION = "JS1_3"; var TITLE = "Regression test for 249579"; var BUGNUMBER="249579"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "0?function(){}:0", 0, 0?function(){}:0 ); bar = true; foo = bar ? function () { return true; } : function() { return false; }; new TestCase( SECTION, "bar = true; foo = bar ? function () { return true; } : function() { return false; }; foo()", true, foo() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/function-001-n.js0000644000175000017500000000241214433667662025447 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-001-n.js'; /** * File Name: boolean-001.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=99232 * * eval("function f(){}function g(){}") at top level is an error for JS1.2 * and above (missing ; between named function expressions), but declares f * and g as functions below 1.2. * * Fails to produce error regardless of version: * js> version(100) * 120 * js> eval("function f(){}function g(){}") * js> version(120); * 100 * js> eval("function f(){}function g(){}") * js> * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "function-001.js"; var VERSION = "JS_1.3"; var TITLE = "functions not separated by semicolons are errors in version 120 and higher"; var BUGNUMBER="10278"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "eval(\"function f(){}function g(){}\")", "error", eval("function f(){}function g(){}") ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/browser.js0000644000175000017500000000000014433667662024543 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/CVS/0000755000175000017500000000000014433667662023167 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/CVS/Repository0000644000175000017500000000003614433667662025270 0ustar apoapomozilla/js/tests/js1_3/Script closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/CVS/Entries0000644000175000017500000000055114433667662024524 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:52 2005// /delete-001.js/1.5/Sat May 26 00:19:34 2007// /function-001-n.js/1.7/Sat May 26 00:19:34 2007// /function-002.js/1.5/Sat May 26 00:19:34 2007// /in-001.js/1.5/Sat May 26 00:19:34 2007// /new-001.js/1.5/Sat May 26 00:19:34 2007// /shell.js/1.2/Sat May 26 00:19:34 2007// /switch-001.js/1.5/Sat May 26 00:19:34 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/CVS/Root0000644000175000017500000000006314433667662024034 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Script/switch-001.js0000644000175000017500000000221214433667662024666 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'switch-001.js'; /** File Name: switch-001.js Section: Description: http://scopus.mcom.com/bugsplat/show_bug.cgi?id=315767 Verify that switches do not use strict equality in versions of JavaScript < 1.4 Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "switch-001"; var VERSION = "JS1_3"; var TITLE = "switch-001"; var BUGNUMBER="315767"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); result = "fail: did not enter switch"; switch (true) { case 1: result = "fail: for backwards compatibility, version 130 use strict equality"; break; case true: result = "pass"; break; default: result = "fail: evaluated default statement"; } new TestCase( SECTION, "switch / case should use strict equality in version of JS < 1.4", "pass", result ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/0000755000175000017500000000000014433667662022647 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/shell.js0000644000175000017500000000035114433667662024313 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Boolean'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/browser.js0000644000175000017500000000000014433667662024656 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/boolean-001.js0000644000175000017500000000221314433667662025120 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'boolean-001.js'; /** * File Name: boolean-001.js * Description: * * In JavaScript 1.2, new Boolean(false) evaluates to false. * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "boolean-001.js"; var VERSION = "JS_1.3"; var TITLE = "new Boolean(false) should evaluate to false"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); BooleanTest( "new Boolean(true)", new Boolean(true), true ); BooleanTest( "new Boolean(false)", new Boolean(false), true ); BooleanTest( "true", true, true ); BooleanTest( "false", false, false ); test(); function BooleanTest( string, object, expect ) { if ( object ) { result = true; } else { result = false; } new TestCase( SECTION, string, expect, result ); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/CVS/0000755000175000017500000000000014433667662023302 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/CVS/Repository0000644000175000017500000000003714433667662025404 0ustar apoapomozilla/js/tests/js1_3/Boolean closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/CVS/Entries0000644000175000017500000000020514433667662024633 0ustar apoapo/boolean-001.js/1.6/Sat May 26 00:19:34 2007// /browser.js/1.1/Fri Mar 18 19:09:51 2005// /shell.js/1.2/Sat May 26 00:19:34 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/Boolean/CVS/Root0000644000175000017500000000006314433667662024147 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/0000755000175000017500000000000014433667662023467 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/shell.js0000644000175000017500000000035414433667662025136 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'extensions'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/proto_2.js0000755000175000017500000000456414433667662025425 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_2.js'; /** File Name: proto_2.js Section: Description: new PrototypeObject This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_2"; var VERSION = "JS1_3"; var TITLE = "new PrototypeObject"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee () { this.name = ""; this.dept = "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee () { this.projects = new Array(); } WorkerBee.prototype = new Employee; function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee; function Engineer () { this.dept = "engineering"; this.machine = ""; } Engineer.prototype = new WorkerBee; var employee = new Employee(); var manager = new Manager(); var workerbee = new WorkerBee(); var salesperson = new SalesPerson(); var engineer = new Engineer(); new TestCase( SECTION, "employee.__proto__ == Employee.prototype", true, employee.__proto__ == Employee.prototype ); new TestCase( SECTION, "manager.__proto__ == Manager.prototype", true, manager.__proto__ == Manager.prototype ); new TestCase( SECTION, "workerbee.__proto__ == WorkerBee.prototype", true, workerbee.__proto__ == WorkerBee.prototype ); new TestCase( SECTION, "salesperson.__proto__ == SalesPerson.prototype", true, salesperson.__proto__ == SalesPerson.prototype ); new TestCase( SECTION, "engineer.__proto__ == Engineer.prototype", true, engineer.__proto__ == Engineer.prototype ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/proto_10.js0000755000175000017500000000677014433667662025505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_10.js'; /** File Name: proto_10.js Section: Description: Determining Instance Relationships This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_10"; var VERSION = "JS1_3"; var TITLE = "Determining Instance Relationships"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function InstanceOf( object, constructor ) { while ( object != null ) { if ( object == constructor.prototype ) { return true; } object = object.__proto__; } return false; } function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( name, dept, projs ) { this.base = Employee; this.base( name, dept) this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer ( name, projs, machine ) { this.base = WorkerBee; this.base( name, "engineering", projs ) this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Engineer(); new TestCase( SECTION, "pat.__proto__ == Engineer.prototype", true, pat.__proto__ == Engineer.prototype ); new TestCase( SECTION, "pat.__proto__.__proto__ == WorkerBee.prototype", true, pat.__proto__.__proto__ == WorkerBee.prototype ); new TestCase( SECTION, "pat.__proto__.__proto__.__proto__ == Employee.prototype", true, pat.__proto__.__proto__.__proto__ == Employee.prototype ); new TestCase( SECTION, "pat.__proto__.__proto__.__proto__.__proto__ == Object.prototype", true, pat.__proto__.__proto__.__proto__.__proto__ == Object.prototype ); new TestCase( SECTION, "pat.__proto__.__proto__.__proto__.__proto__.__proto__ == null", true, pat.__proto__.__proto__.__proto__.__proto__.__proto__ == null ); new TestCase( SECTION, "InstanceOf( pat, Engineer )", true, InstanceOf( pat, Engineer ) ); new TestCase( SECTION, "InstanceOf( pat, WorkerBee )", true, InstanceOf( pat, WorkerBee ) ); new TestCase( SECTION, "InstanceOf( pat, Employee )", true, InstanceOf( pat, Employee ) ); new TestCase( SECTION, "InstanceOf( pat, Object )", true, InstanceOf( pat, Object ) ); new TestCase( SECTION, "InstanceOf( pat, SalesPerson )", false, InstanceOf ( pat, SalesPerson ) ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/proto_5.js0000755000175000017500000000526414433667662025426 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'proto_5.js'; /** File Name: proto_5.js Section: Description: Logical OR || in Constructors This tests Object Hierarchy and Inheritance, as described in the document Object Hierarchy and Inheritance in JavaScript, last modified on 12/18/97 15:19:34 on http://devedge.netscape.com/. Current URL: http://devedge.netscape.com/docs/manuals/communicator/jsobj/contents.htm This tests the syntax ObjectName.prototype = new PrototypeObject using the Employee example in the document referenced above. This tests the logical OR opererator || syntax in constructors. Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "proto_5"; var VERSION = "JS1_3"; var TITLE = "Logical OR || in Constructors"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function Employee ( name, dept ) { this.name = name || ""; this.dept = dept || "general"; } function Manager () { this.reports = []; } Manager.prototype = new Employee(); function WorkerBee ( projs ) { this.projects = projs || new Array(); } WorkerBee.prototype = new Employee(); function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee(); function Engineer ( machine ) { this.dept = "engineering"; this.machine = machine || ""; } Engineer.prototype = new WorkerBee(); var pat = new Engineer( "indy" ); var les = new Engineer(); new TestCase( SECTION, "var pat = new Engineer(\"indy\"); pat.name", "", pat.name ); new TestCase( SECTION, "pat.dept", "engineering", pat.dept ); new TestCase( SECTION, "pat.projects.length", 0, pat.projects.length ); new TestCase( SECTION, "pat.machine", "indy", pat.machine ); new TestCase( SECTION, "pat.__proto__ == Engineer.prototype", true, pat.__proto__ == Engineer.prototype ); new TestCase( SECTION, "var les = new Engineer(); les.name", "", les.name ); new TestCase( SECTION, "les.dept", "engineering", les.dept ); new TestCase( SECTION, "les.projects.length", 0, les.projects.length ); new TestCase( SECTION, "les.machine", "", les.machine ); new TestCase( SECTION, "les.__proto__ == Engineer.prototype", true, les.__proto__ == Engineer.prototype ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/browser.js0000644000175000017500000000000014433667662025476 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/script-001.js0000755000175000017500000000570714433667662025643 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'script-001.js'; /** File Name: script-001.js Section: Description: new NativeScript object js> parseInt(123,"hi") 123 js> parseInt(123, "blah") 123 js> s js: s is not defined js> s = new Script undefined; js> s = new Script() undefined; js> s.getJSClass js> s.getJSClass = Object.prototype.toString function toString() { [native code] } js> s.getJSClass() [object Script] js> s.compile( "return 3+4" ) js: JavaScript exception: javax.javascript.EvaluatorException: " s.compile( "3+4" ) 3 + 4; js> typeof s function js> s() Jit failure! invalid opcode: 1 Jit Pass1 Failure! javax/javascript/gen/c13 initScript (Ljavax/javascript/Scriptable;)V An internal JIT error has occurred. Please report this with .class jit-bugs@itools.symantec.com 7 js> s.compile("3+4") 3 + 4; js> s() Jit failure! invalid opcode: 1 Jit Pass1 Failure! javax/javascript/gen/c17 initScript (Ljavax/javascript/Scriptable;)V An internal JIT error has occurred. Please report this with .class jit-bugs@itools.symantec.com 7 js> quit() C:\src\ns_priv\js\tests\ecma>shell C:\src\ns_priv\js\tests\ecma>java -classpath c:\cafe\java\JavaScope; :\src\ns_priv\js\tests javax.javascript.examples.Shell Symantec Java! JustInTime Compiler Version 210.054 for JDK 1.1.2 Copyright (C) 1996-97 Symantec Corporation js> s = new Script("3+4") 3 + 4; js> s() 7 js> s2 = new Script(); undefined; js> s.compile( "3+4") 3 + 4; js> s() Jit failure! invalid opcode: 1 Jit Pass1 Failure! javax/javascript/gen/c7 initScript (Ljavax/javascript/Scriptable;)V An internal JIT error has occurred. Please report this with .class jit-bugs@itools.symantec.com 7 js> quit() Author: christine@netscape.com Date: 12 november 1997 */ var SECTION = "script-001"; var VERSION = "JS1_3"; var TITLE = "NativeScript"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); if (typeof Script == 'undefined') { print('Test skipped. Script not defined.'); new TestCase( SECTION, "var s = new Script(); typeof s", "Script not supported, test skipped.", "Script not supported, test skipped." ); } else { var s = new Script(); s.getJSClass = Object.prototype.toString; new TestCase( SECTION, "var s = new Script(); typeof s", "function", typeof s ); new TestCase( SECTION, "s.getJSClass()", "[object Script]", s.getJSClass() ); } test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/CVS/0000755000175000017500000000000014433667662024122 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/CVS/Repository0000644000175000017500000000004214433667662026220 0ustar apoapomozilla/js/tests/js1_3/extensions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/CVS/Entries0000644000175000017500000000040614433667662025456 0ustar apoapo/browser.js/1.1/Fri Feb 9 00:00:52 2007// /proto_10.js/1.2/Sat May 26 00:19:34 2007// /proto_2.js/1.2/Sat May 26 00:19:34 2007// /proto_5.js/1.2/Sat May 26 00:19:34 2007// /script-001.js/1.2/Sat May 26 00:19:34 2007// /shell.js/1.2/Sat May 26 00:19:34 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/extensions/CVS/Root0000644000175000017500000000006314433667662024767 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/browser.js0000644000175000017500000000043514433667662023313 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/CVS/0000755000175000017500000000000014433667662021723 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/CVS/Repository0000644000175000017500000000002714433667662024024 0ustar apoapomozilla/js/tests/js1_3 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/CVS/Entries0000644000175000017500000000025014433667662023254 0ustar apoapo/README/1.1/Fri Mar 18 19:09:51 2005// /browser.js/1.17/Sat May 26 00:19:34 2007// /jsref.js/1.3/Mon Oct 30 16:48:35 2006// /shell.js/1.17/Sat May 26 00:19:34 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/CVS/Root0000644000175000017500000000006314433667662022570 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_3/CVS/Entries.Log0000644000175000017500000000013714433667662024000 0ustar apoapoA D/Boolean//// A D/Script//// A D/extensions//// A D/inherit//// A D/misc//// A D/regress//// closure-compiler-20130227+dfsg1/rhino/testsrc/tests/slow-n-1.9.2-browser.tests0000644000175000017500000000315014433667662025001 0ustar apoapoe4x/GC/regress-324278.js e4x/Regress/regress-319872.js e4x/Regress/regress-473709.js e4x/Regress/regress-474319.js e4x/XML/regress-324422-2.js e4x/extensions/regress-393874.js ecma/Date/15.9.5.10-2.js ecma_3/Array/regress-322135-03.js ecma_3/Array/regress-322135-04.js ecma_3/RegExp/regress-307456.js ecma_3/RegExp/regress-330684.js js1_5/Array/regress-350256-03.js js1_5/Array/regress-465980-02.js js1_5/GC/regress-319980-01.js js1_5/GC/regress-338653.js js1_5/GC/regress-346794.js js1_5/GC/regress-348532.js js1_5/GC/regress-469621.js js1_5/Regress/regress-271716-n.js js1_5/Regress/regress-303213.js js1_5/Regress/regress-451322.js js1_5/Regress/regress-484693.js js1_5/extensions/regress-336409-1.js js1_5/extensions/regress-336410-1.js js1_5/extensions/regress-342960.js js1_5/extensions/regress-345967.js js1_5/extensions/regress-350531.js js1_5/extensions/regress-365527.js js1_5/extensions/regress-391033-01.js js1_5/extensions/regress-407501.js js1_5/extensions/regress-407720.js js1_5/extensions/regress-414755.js js1_5/extensions/regress-416354.js js1_5/extensions/regress-454142.js js1_5/extensions/regress-472787.js js1_6/extensions/regress-455464-04.js js1_6/extensions/regress-456826.js js1_7/GC/regress-381374.js js1_7/extensions/regress-458288.js js1_7/extensions/regress-458679.js js1_7/regress/regress-474771.js js1_8/extensions/regress-417131.js js1_8/extensions/regress-476414-01.js js1_8/extensions/regress-476414-02.js js1_8/extensions/regress-476427.js js1_8/extensions/regress-476869.js js1_8/regress/regress-464096.js js1_8/regress/regress-464418.js js1_8/regress/regress-474771.js js1_8_1/trace/regress-451673.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/0000755000175000017500000000000014433667662021275 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/shell.js0000644000175000017500000000061014433667662022737 0ustar apoapo/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsuite = 'js1_8'; // explicitly turn on js18 if (typeof version != 'undefined') { version(180); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/0000755000175000017500000000000014433667662022746 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/shell.js0000644000175000017500000000035114433667662024412 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'genexps'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-380237-04.js0000644000175000017500000002103414433667662025743 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Generator expressions parenthesization test'; var actual = ''; var expect = ''; /* Given that parentheization seems so fragile *and* the rules for where genexps are allowed keep changing, I thought it would be good to have a way to test that: 1) unparenthesized genexps are allowed in some places and the decompilation is sane and not over-parenthesized 2) unparenthesized genexps are disallowed in many places and when there are parens, the decompilation is sane and not over-parenthesized */ // |genexp| must have the exact same whitespace the decompiler uses genexp = "x * x for (x in [])"; genexpParened = "(" + genexp + ")"; genexpParenedTwice = "(" + genexpParened + ")"; // Warning: be careful not to put [] around stuff, because that would // cause it to be treated as an array comprehension instead of a // generator expression! // Statements doesNotNeedParens(1, "if (xx) { }"); needParens(2, "if (1, xx) { }"); needParens(3, "if (xx, 1) { }"); doesNotNeedParens(4, "do { } while (xx);"); doesNotNeedParens(5, "while (xx) { }"); doesNotNeedParens(6, "switch (xx) { }"); doesNotNeedParens(7, "with (xx) { }"); needParens(8, "switch (x) { case xx: }"); needParens(9, "return xx;"); needParens(10, "yield xx;"); needParens(11, "for (xx;;) { }"); needParens(12, "for (;xx;) { }", "function anonymous() {\n for (;;) {\n }\n}"); needParens(13, "for (;;xx) { }"); needParens(14, "for (i in xx) { }"); needParens(15, "throw xx"); needParens(16, "try { } catch (e if xx) { }"); needParens(17, "let (x=3) xx"); needParens(18, "let (x=xx) 3"); // Function calls doesNotNeedParens(19, "f(xx);"); needParens(20, "f(xx, 1);"); needParens(21, "f(1, xx);"); doesNotNeedParens(22, "/x/(xx);"); needParens(23, "/x/(xx, 1);"); needParens(24, "/x/(1, xx);"); // eval is special and often confuses the decompiler. doesNotNeedParens(25, "eval(xx);"); needParens(26, "eval(xx, 1);"); needParens(27, "eval(1, xx);"); // Expressions needParens(28, "xx;"); // ??? needParens(29, "var g = xx;"); // ??? needParens(30, "g += xx;"); needParens(31, "xx();"); needParens(32, "xx() = 3;"); needParens(33, "a ? xx : c"); needParens(34, "xx ? b : c"); needParens(35, "a ? b : xx"); needParens(36, "1 ? xx : c"); needParens(37, "0 ? b : xx"); needParens(38, "1 + xx"); needParens(39, "xx + 1"); needParens(40, "1, xx"); doesNotNeedParens(41, "+(xx)"); doesNotNeedParens(42, "!(xx)"); needParens(43, "xx, 1"); needParens(44, "[1, xx]"); needParens(45, "[xx, 1]"); needParens(46, "[#1=xx,3]"); needParens(47, "[#1=xx,#1#]"); needParens(48, "xx.p"); needParens(49, "xx.@p"); needParens(50, "typeof xx;"); needParens(51, "void xx;"); needParens(52, "({ a: xx })"); needParens(53, "({ a: 1, b: xx })"); needParens(54, "({ a: xx, b: 1 })"); needParens(55, "({ a getter: xx })"); needParens(56, ""); doesNotNeedParens(57, "new (xx);"); doesNotNeedParens(58, "new a(xx);"); // Generator expressions cannot be used as LHS, even though they're syntactic // sugar for something that looks a lot like an "lvalue return": (f() = 3). rejectLHS(59, "++ (xx);"); rejectLHS(60, "delete xx;"); rejectLHS(61, "delete (xx);"); rejectLHS(62, "for (xx in []) { }"); rejectLHS(63, "for ((xx) in []) { }"); rejectLHS(64, "try { } catch(xx) { }"); rejectLHS(65, "try { } catch([(xx)]) { }"); rejectLHS(66, "xx += 3;"); rejectLHS(67, "(xx) += 3;"); rejectLHS(68, "xx = 3;"); // Assignment rejectLHS(69, " (xx) = 3;"); rejectLHS(70, "var (xx) = 3;"); rejectLHS(71, "const (xx) = 3;"); rejectLHS(72, "let (xx) = 3;"); // Destructuring assignment rejectLHS(73, " [(xx)] = 3;"); rejectLHS(74, "var [(xx)] = 3;"); rejectLHS(75, "const [(xx)] = 3;"); rejectLHS(76, "let [(xx)] = 3;"); // Group assignment (Spidermonkey optimization for certain // destructuring assignments) rejectLHS(77, " [(xx)] = [3];"); rejectLHS(78, "var [(xx)] = [3];"); rejectLHS(79, "const [(xx)] = [3];"); rejectLHS(80, "let [(xx)] = [3];"); // Destructuring & group assignment for array comprehensions, just for kicks. rejectLHS(81, " [xx] = [3];"); rejectLHS(82, "var [xx] = [3];"); rejectLHS(83, "const [xx] = [3];"); rejectLHS(84, "let [xx] = 3;"); rejectLHS(85, " [xx] = 3;"); rejectLHS(86, "var [xx] = 3;"); rejectLHS(87, "const [xx] = 3;"); rejectLHS(88, "let [xx] = 3;"); // This is crazy, ambiguous, and/or buggy. // See https://bugzilla.mozilla.org/show_bug.cgi?id=380237#c23 et seq. //doesNotNeedParens("(yield xx);"); print("Done!"); function doesNotNeedParens(section, pat) { print("Testing section " + section + " pattern " + pat); var f, ft; sanityCheck(section, pat); expect = 'No Error'; actual = ''; ft = pat.replace(/xx/, genexp); try { f = new Function(ft); actual = 'No Error'; } catch(e) { print("Unparenthesized genexp SHOULD have been accepted here!"); actual = e + ''; } reportCompare(expect, actual, summary + ': doesNotNeedParens section ' + section + ' pattern ' + pat); roundTripTest(section, f); // Make sure the decompilation is not over-parenthesized. var uf = "" + f; if (pat.indexOf("(xx)") != -1) overParenTest(section, f); // else // print("Skipping the over-parenthesization test, because I don't know how to test for over-parenthesization when the pattern doesn't have parens snugly around it.") } function needParens(section, pat, exp) { print("Testing section " + section + " pattern " + pat); var f, ft; sanityCheck(section, pat); expect = 'SyntaxError'; actual = ''; ft = pat.replace(/xx/, genexp); try { f = new Function(ft); print("Unparenthesized genexp should NOT have been accepted here!"); } catch(e) { /* expected to throw */ actual = e.name; } reportCompare(expect, actual, summary + ': needParens section ' + section + ' pattern ' + pat); expect = 'No Error'; actual = ''; ft = pat.replace(/xx/, genexpParened); try { f = new Function(ft); actual = 'No Error'; } catch(e) { print("Yikes!"); actual = e + ''; } reportCompare(expect, actual, summary + ': needParens section ' + section + ' ft ' + ft); roundTripTest(section, f, exp); overParenTest(section, f, exp); } function rejectLHS(section, pat) { print("Testing section " + section + " pattern " + pat); // sanityCheck(pat); // because 'z' should be accepted as an LHS or binding var ft; expect = 'SyntaxError'; actual = ''; ft = pat.replace(/xx/, genexp) try { new Function(ft); print("That should have been a syntax error!"); actual = 'No Error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, summary + ': rejectLHS section ' + section); } function overParenTest(section, f, exp) { var uf = "" + f; if (uf == exp) return; reportCompare(false, uf.indexOf(genexpParened) == -1, summary + ': overParenTest genexp snugly in parentheses: section ' + section + ' uf ' + uf); if (uf.indexOf(genexpParened) != -1) { reportCompare(true, uf.indexOf(genexpParenedTwice) == -1, summary + ': overParensTest decompilation should not be over-parenthesized: section ' + ' uf ' + uf); } } function sanityCheck(section, pat) { expect = ''; actual = ''; if (pat.indexOf("xx") == -1) { actual += "No 'xx' in this pattern? "; } var f, ft; ft = pat.replace(/xx/, "z"); try { f = new Function(ft); } catch(e) { actual += "Yowzers! Probably a bogus test!"; } reportCompare(expect, actual, summary + ': sanityCheck section ' + section + ' pattern ' + pat); } function roundTripTest(section, f, exp) { // Decompile var uf = "" + f; // Recompile expect = 'No Error'; actual = ''; var euf; try { euf = eval("(" + uf + ")"); actual = 'No Error'; reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); } catch(e) { actual = e + ''; reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); return; } // Decompile again and make sure the decompilations match exactly. expect = exp || uf; actual = "" + euf; reportCompare(expect, actual, summary + ': roundTripTest no round-trip change: section ' + section); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-349326.js0000755000175000017500000000625014433667662025534 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-349326.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 349326; var summary = 'closing generators'; var actual = 'PASS'; var expect = 'PASS'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); let closed; function gen() { try { yield 1; yield 2; } finally { closed = true; } } // Test that return closes the generator function test_return() { for (let i in gen()) { if (i != 1) throw "unexpected generator value"; return 10; } } closed = false; test_return(); if (closed !== true) throw "return does not close generator"; // test that break closes generator closed = false; for (let i in gen()) { if (i != 1) throw "unexpected generator value"; break; } if (closed !== true) throw "break does not close generator"; label: { for (;;) { closed = false; for (let i in gen()) { if (i != 1) throw "unexpected generator value"; break label; } } } if (closed !== true) throw "break does not close generator"; // test that an exception closes generator function function_that_throws() { throw function_that_throws; } try { closed = false; for (let i in gen()) { if (i != 1) throw "unexpected generator value"; function_that_throws(); } } catch (e) { if (e !== function_that_throws) throw e; } if (closed !== true) throw "exception does not close generator"; // Check consistency of finally execution in presence of generators let gen2_was_closed = false; let gen3_was_closed = false; let finally_was_executed = false; function gen2() { try { yield 2; } finally { if (gen2_was_closed || !finally_was_executed || !gen3_was_closed) throw "bad oder of gen2 finally execution" gen2_was_closed = true; throw gen2; } } function gen3() { try { yield 3; } finally { if (gen2_was_closed || finally_was_executed || gen3_was_closed) throw "bad oder of gen3 finally execution" gen3_was_closed = true; } } label2: { try { for (let i in gen2()) { try { for (let j in gen3()) { break label2; } } finally { if (gen2_was_closed || finally_was_executed || !gen3_was_closed) throw "bad oder of try-finally execution"; finally_was_executed = true; } } throw "gen2 finally should throw"; } catch (e) { if (e != gen2) throw e; } } print("OK"); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-347739.js0000755000175000017500000000243014433667662025536 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-347739.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 347739; var summary = 'generator_instance.close readonly and immune'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function gen_test(test_index) { try { yield 1; } finally { actual += "Inside finally: "+test_index + ' '; } } actual = ''; expect = 'Inside finally: 1 Inside finally: 2 '; var iter1 = gen_test(1); var close = iter1.close; iter1.close = null; delete iter1.close; iter1.next(); iter1.close(); var iter2 = gen_test(2); for (i in iter2) { iter2.close = null; delete iter2.close; } reportCompare(expect, actual, summary + ': 2'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-349331.js0000755000175000017500000000444514433667662025534 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-349331.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 349331; var summary = 'generator.close without GeneratorExit'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var catch1, catch2, catch3, finally1, finally2, finally3; var iter; function gen() { yield 1; try { try { try { yield 2; } catch (e) { catch1 = true; } finally { finally1 = true; } } catch (e) { catch2 = true; } finally { finally2 = true; } } catch (e) { catch3 = true; } finally { finally3 = true; } } // test explicit close call catch1 = catch2 = catch3 = finally1 = finally2 = finally3 = false; iter = gen(); iter.next(); iter.next(); iter.close(); var passed = !catch1 && !catch2 && !catch3 && finally1 && finally2 && finally3; if (!passed) { print("Failed!"); print("catch1=" + catch1 + " catch2=" + catch2 + " catch3=" + catch3); print("finally1=" + finally1 + " finally2=" + finally2 + " finally3=" + finally3); } reportCompare(true, passed, 'test explicit close call'); // test for-in invoked close catch1 = catch2 = catch3 = finally1 = finally2 = finally3 = false; iter = gen(); for (var i in iter) { if (i == 2) break; } var passed = !catch1 && !catch2 && !catch3 && finally1 && finally2 && finally3; if (!passed) { print("Failed!"); print("catch1=" + catch1 + " catch2=" + catch2 + " catch3=" + catch3); print("finally1=" + finally1 + " finally2=" + finally2 + " finally3="+finally3); } reportCompare(true, passed, 'test GC-invoke close'); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-380237-02.js0000755000175000017500000000174414433667662025752 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Decompilation of generator expressions'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = function() { g = (d for (d in [0])); g.next(); }; expect = 'function() { g = (d for (d in [0])); g.next(); }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-380237-03.js0000755000175000017500000001131114433667662025742 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Decompilation of generator expressions'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- expect = 'No Error'; actual = ''; try { var g = ((yield i) for (i in [1,2,3])); actual = 'No Error'; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': top level'); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = (function() { g = (d for (d in [0]) for (e in [1])); }); expect = 'function() { g = (d for (d in [0]) for (e in [1])); }'; actual = f + ''; compareSource(expect, actual, summary + ': see bug 380506'); f = function() { return (1 for (i in [])) }; expect = 'function() { return (1 for (i in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { with((x for (x in []))) { } }; expect = 'function() { with(x for (x in [])) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (0)) }); expect = 'function() { (1 for (w in []) if (0)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (x)) }); expect = 'function() { (1 for (w in []) if (x)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (1)) }); expect = 'function() { (1 for (w in []) ); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (x for ([{}, {}] in [])); }); expect = 'function() { (x for ([[], []] in [])); }'; actual = f + ''; compareSource(expect, actual, summary); expect = 'SyntaxError: invalid assignment left-hand side'; actual = ''; try { eval('(function() { (x for each (x in [])) = 3; })'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL'); f = (function() { (x*x for (x in a)); }); expect = 'function() { (x*x for (x in a)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function () { (1 for (y in (yield 3))); }); expect = 'function () { (1 for (y in yield 3)); }'; actual = f + ''; compareSource(expect, actual, summary); expect = 'SyntaxError: invalid delete operand'; try { eval('(function () { delete (x for (x in [])); })'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL'); f = (function() { ([yield] for (x in [])); }); expect = 'function() { ([yield] for (x in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { if(1, (x for (x in []))) { } }; expect = 'function() { if(1, (x for (x in []))) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = function () {return(i*j for each (i in [1,2,3,4]) for each (j in [5,6,7,8]))}; expect = 'function () {return(i*j for each (i in [1,2,3,4]) ' + 'for each (j in [5,6,7,8]));}'; actual = f + ''; compareSource(expect, actual, summary); expect = 'No Error'; actual = ''; try { var g = ((yield i) for (i in [1,2,3])); actual = 'No Error'; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': nested'); f = function() { try { } catch(x if (1 for (x in []))) { } finally { } }; expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }'; actual = f + ''; compareSource(expect, actual, summary); f = eval(uneval(f)); expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }'; actual = f + ''; compareSource(expect, actual, summary + ': eval(uneval())'); f = function() { if (1, (x for (x in []))) { } }; expect = 'function() { if (1, (x for (x in []))) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { ((a, b) for (x in [])) }; expect = 'function() { ((a, b) for (x in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { ({x setter: (function () {}).x }) }); expect = 'function() { ({x setter: function () {}.x }); }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/browser.js0000644000175000017500000000000014433667662024755 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-380237-01.js0000755000175000017500000001371414433667662025751 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Generator expressions - sudoku'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (this.version) version(180); // XXX should be standard (and named clone, after Java?) Object.prototype.copy = function () { let o = {} for (let i in this) o[i] = this[i] return o } // Make arrays and strings act more like Python lists by iterating their values, not their keys. Array.prototype.__iterator__ = String.prototype.__iterator__ = function () { for (let i = 0; i < this.length; i++) yield this[i] } // Containment testing for arrays and strings that should be coherent with their __iterator__. Array.prototype.contains = String.prototype.contains = function (e) { return this.indexOf(e) != -1 } Array.prototype.repeat = String.prototype.repeat = function (n) { let s = this.constructor() for (let i = 0; i < n; i++) s = s.concat(this) return s } String.prototype.center = function (w) { let n = this.length if (w <= n) return this let m = Math.floor((w - n) / 2) return ' '.repeat(m) + this + ' '.repeat(w - n - m) } Array.prototype.toString = Array.prototype.toSource Object.prototype.toString = Object.prototype.toSource // XXX thought spurred by the next two functions: array extras should default to identity function function all(seq) { for (let e in seq) if (!e) return false return true } function some(seq) { for (let e in seq) if (e) return e return false } function cross(A, B) { return [a+b for (a in A) for (b in B)] } function dict(A) { let d = {} for (let e in A) d[e[0]] = e[1] return d } function set(A) { let s = [] for (let e in A) if (!s.contains(e)) s.push(e) return s } function zip(A, B) { let z = [] let n = Math.min(A.length, B.length) for (let i = 0; i < n; i++) z.push([A[i], B[i]]) return z } rows = 'ABCDEFGHI' cols = '123456789' digits = '123456789' squares = cross(rows, cols) unitlist = [cross(rows, c) for (c in cols)] .concat([cross(r, cols) for (r in rows)]) .concat([cross(rs, cs) for (rs in ['ABC','DEF','GHI']) for (cs in ['123','456','789'])]) units = dict([s, [u for (u in unitlist) if (u.contains(s))]] for (s in squares)) peers = dict([s, set([s2 for (u in units[s]) for (s2 in u) if (s2 != s)])] for (s in squares)) // Given a string of 81 digits (or . or 0 or -), return a dict of {cell:values}. function parse_grid(grid) { grid = [c for (c in grid) if ('0.-123456789'.contains(c))] let values = dict([s, digits] for (s in squares)) for (let [s, d] in zip(squares, grid)) if (digits.contains(d) && !assign(values, s, d)) return false return values } // Eliminate all the other values (except d) from values[s] and propagate. function assign(values, s, d) { if (all(eliminate(values, s, d2) for (d2 in values[s]) if (d2 != d))) return values return false } // Eliminate d from values[s]; propagate when values or places <= 2. function eliminate(values, s, d) { if (!values[s].contains(d)) return values // Already eliminated values[s] = values[s].replace(d, '') if (values[s].length == 0) return false // Contradiction: removed last value if (values[s].length == 1) { // If there is only one value (d2) left in square, remove it from peers let d2 = values[s][0] if (!all(eliminate(values, s2, d2) for (s2 in peers[s]))) return false } // Now check the places where d appears in the units of s for (let u in units[s]) { let dplaces = [s for (s in u) if (values[s].contains(d))] if (dplaces.length == 0) return false if (dplaces.length == 1) // d can only be in one place in unit; assign it there if (!assign(values, dplaces[0], d)) return false } return values } // Used for debugging. function print_board(values) { let width = 1 + Math.max.apply(Math, [values[s].length for (s in squares)]) let line = '\n' + ['-'.repeat(width*3)].repeat(3).join('+') for (let r in rows) print([values[r+c].center(width) + ('36'.contains(c) && '|' || '') for (c in cols)].join('') + ('CF'.contains(r) && line || '')) print('\n') } easy = "..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3.." print_board(parse_grid(easy)) // Using depth-first search and constraint propagation, try all possible values. function search(values) { if (!values) return false // Failed earlier if (all(values[s].length == 1 for (s in squares))) return values // Solved! // Choose the unfilled square s with the fewest possibilities // XXX Math.min etc. should work with generator expressions and other iterators // XXX Math.min etc. should work on arrays (lists or tuples in Python) as well as numbers let a = [values[s].length + s for (s in squares) if (values[s].length > 1)].sort() let s = a[0].slice(-2) return some(search(assign(values.copy(), s, d)) for (d in values[s])) } hard = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......' print_board(search(parse_grid(hard))) delete Object.prototype.copy; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-349012-01.js0000755000175000017500000000241614433667662025742 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-349012-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 349012; var summary = 'closing a generator fails to report error if yield during close is ignored'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- if (typeof quit != 'undefined') { quit(0); } function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = "Inner finally,Outer finally"; function gen() { try { try { yield 1; } finally { actual += "Inner finally"; yield 2; } } finally { actual += ",Outer finally"; } } try { for (var i in gen()) break; } catch (e) { if (!(e instanceof TypeError)) throw e; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/CVS/0000755000175000017500000000000014433667662023401 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/CVS/Repository0000644000175000017500000000003714433667662025503 0ustar apoapomozilla/js/tests/js1_8/genexps closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/CVS/Entries0000644000175000017500000000104714433667662024737 0ustar apoapo/browser.js/1.1/Sat May 26 16:10:20 2007// /regress-347739.js/1.1/Sun Aug 5 09:31:32 2007// /regress-349012-01.js/1.1/Sun Aug 5 10:22:16 2007// /regress-349326.js/1.1/Fri Jul 13 17:23:49 2007// /regress-349331.js/1.1/Sun Aug 5 10:22:16 2007// /regress-380237-01.js/1.3/Fri Mar 6 01:38:59 2009// /regress-380237-02.js/1.1/Sat May 26 16:10:20 2007// /regress-380237-03.js/1.3/Thu May 31 19:46:36 2007// /regress-380237-04.js/1.3/Thu Oct 30 22:13:41 2008// /regress-384991.js/1.1/Tue Mar 25 11:51:04 2008// /shell.js/1.1/Sat May 26 16:10:20 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/CVS/Root0000644000175000017500000000006314433667662024246 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/genexps/regress-384991.js0000755000175000017500000000255414433667662025546 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-384991.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 384991; var summary = ' w(yield) should not cause "yield expression must be parenthesized" syntax error'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'No Error'; try { actual = 'No Error'; (function() { w((yield)); }); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': 1'); try { actual = 'No Error'; (function() { w(1 ? yield : 0); }); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': 2'); try { actual = 'No Error'; (function () { f(x = yield); const x; }); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': 3'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/0000755000175000017500000000000014433667662022747 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-483749.js0000644000175000017500000000140414433667662025536 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-483749.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 483749; var summary = 'Do not assert: !js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x in ['']) { for (var b = 0; b < 5; ++b) { if (b % 5 == 3) { with([]) this; } } } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-481800.js0000644000175000017500000000174514433667662025530 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-481800.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 481800; var summary = 'TM: Do not assert: (!lhs->isQuad() && !rhs->isQuad()) || (lhs->isQuad() && rhs->isQuad())'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x in ['', 0, 0, eval]) { y = x } ( function(){} ); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/shell.js0000644000175000017500000000035114433667662024413 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'regress'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-467495-06.js0000644000175000017500000000213514433667662025763 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-467495-06.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 467495; var summary = 'TCF_FUN_CLOSURE_VS_VAR is necessary'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(x) { var y = 1; if (Math) function x() { } if (Math) function y() { } return [x, y]; } var r = f(0); if (typeof(r[0]) != "function") actual += "Bad r[0]"; if (typeof(r[1]) != "function") throw "Bad r[1]"; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-459389.js0000755000175000017500000000560614433667662025554 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-459389.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 459389; var summary = 'Do not crash with JIT'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); print('mmmm, food!'); jit(true); var SNI = {}; SNI.MetaData={}; SNI.MetaData.Parameter=function() { var parameters={}; this.addParameter=function(key,value) { parameters[key]=[]; parameters[key].push(value); }; this.getParameter=function(key,separator){ if(!parameters[key]) { return; } return parameters[key].join(separator); }; this.getKeys=function() { return parameters; }; }; SNI.MetaData.Manager=function(){ var m=new SNI.MetaData.Parameter(); this.getParameter=m.getParameter; }; var MetaDataManager=SNI.MetaData.Manager; SNI.Ads={ }; SNI.Ads.Url=function(){ var p=new SNI.MetaData.Parameter(); this.addParameter=p.addParameter; this.getParameter=p.getParameter; }; function Ad() { var url=new SNI.Ads.Url(); this.addParameter=url.addParameter; this.getParameter=url.getParameter; } function DartAd() AdUrl.prototype=new Ad(); function AdUrl() { } function AdRestriction() { var p=new SNI.MetaData.Parameter(); this.addParameter=p.addParameter; this.getParameter=p.getParameter; this.getKeys=p.getKeys; } function AdRestrictionManager(){ this.restriction=[]; this.isActive=isActive; this.isMatch=isMatch; this.startMatch=startMatch; function isActive(ad,mdm){ var value=false; for(var i=0;i for (y in x)]); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-463334-01.js0000644000175000017500000000170614433667662025745 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-463334-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 463334; var summary = 'TM: Do not crash in isPromoteInt'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); u = 3; for (let i in (function() { for (var j=0;j<4;++j) { void u; yield; } })()); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-384412.js0000755000175000017500000001150314433667662025525 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-384412.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 384412; var summary = 'Exercise frame handling code'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); /* * Generators */ /* Generator yields properly */ f = (function(n) { for (var i = 0; i != n; i++) yield i }); g = f(3); expect(0, g.next()); expect(1, g.next()); expect(2, g.next()); s = "no exception"; try { g.next(); } catch (e) { s = e + ""; } expect("[object StopIteration]", s); /* Generator yields properly in finally */ f = (function(n) { try { for (var i = 0; i != n; i++) yield i; } finally { yield "finally"; } }); g = f(3); expect(0, g.next()); expect(1, g.next()); expect(2, g.next()); expect("finally", g.next()); /* Generator throws when closed with yield in finally */ g = f(3); expect(0, g.next()); s = "no exception"; try { g.close(); } catch (e) { s = e + ""; }; expect("TypeError: yield from closing generator " + f.toSource(), s); /* * XML predicates */ t = ichjoki; /* Predicates, nested predicates and empty lists */ expect(joki, t.eins.(name == "joki")); expect(t.eins, t.eins.(t.eins.(true))); expect(t.(false), t.eins.(false).(true)); /* Predicate with yield throws */ f = (function() { t.eins.(yield true); }); g = f(); s = "no exception"; try { g.next(); } catch (e) { s = e + ""; } expect("no exception", s); /* Function with predicate without return returns void */ f = (function() { t.eins.(true); }); expect(undefined, f()); /* XML filter predicate in finally preserves return value */ f = (function() { try { return "hallo"; } finally { t.eins.(true); } }); expect("hallo", f()); /* * Calls that have been replaced with js_PushFrame() &c... */ f = (function() { return arguments[(arguments.length - 1) / 2]; }); expect(2, f(1, 2, 3)); expect(2, f.call(null, 1, 2, 3)); expect(2, f.apply(null, [1, 2, 3])); expect("a1c", "abc".replace("b", f)); s = "no exception"; try { "abc".replace("b", (function() { throw "hello" })); } catch (e) { s = e + ""; } expect("hello", s); expect(6, [1, 2, 3].reduce(function(a, b) { return a + b; })); s = "no exception"; try { [1, 2, 3].reduce(function(a, b) { if (b == 2) throw "hello"; }); } catch (e) { s = e + ""; } expect("hello", s); /* * __noSuchMethod__ */ o = {}; s = "no exception"; try { o.hello(); } catch (e) { s = e + ""; } expect("TypeError: o.hello is not a function", s); o.__noSuchMethod__ = (function() { return "world"; }); expect("world", o.hello()); o.__noSuchMethod__ = 1; s = "no exception"; try { o.hello(); } catch (e) { s = e + ""; } expect("TypeError: o.hello is not a function", s); o.__noSuchMethod__ = {}; s = "no exception"; try { o.hello(); } catch (e) { s = e + ""; } expect("TypeError: o.hello() is not a function", s); s = "no exception"; try { eval("o.hello()"); } catch (e) { s = e + ""; } expect("TypeError: o.hello() is not a function", s); s = "no exception"; try { [2, 3, 0].sort({}); } catch (e) { s = e + ""; } expect("TypeError: [2, 3, 0].sort({}) is not a function", s); /* * Generator expressions. */ String.prototype.__iterator__ = (function () { /* * NOTE: * Without the "0 + ", the loop over does not terminate because * the iterator gets run on a string with an empty length property. */ for (let i = 0; i != 0 + this.length; i++) yield this[i]; }); expect(["a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3"] + "", ([a + b for (a in 'abc') for (b in '123')]) + ""); expect("", ([x for (x in )]) + ""); /* * Version switching */ if (typeof version == 'function') { var v = version(150); f = new Function("return version(arguments[0])"); version(v); expect(150, f()); expect(150, eval("f()")); expect(0, eval("f(0); f()")); version(v); } print("End of Tests"); /* * Utility functions */ function expect(a, b) { print('expect: ' + a + ', actual: ' + b); reportCompare(a, b, summary); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-477581.js0000644000175000017500000000204114433667662025531 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-477581.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 477581; var summary = 'Do not assert: !JSVAL_IS_PRIMITIVE(regs.sp[-2])'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); function g() { yield 2; } var iterables = [[1], [], [], [], g()]; for (let i = 0; i < iterables.length; i++) for each (let j in iterables[i]) ; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-459186.js0000755000175000017500000000164314433667662025544 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-459186.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 459186; var summary = 'Do not crash in CheckDestructuring'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { for (var [,{y}] = 1 in []) {} } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-472703.js0000644000175000017500000000204714433667662025526 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472703.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472703; var summary = 'Do not assert: regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { eval( 'for (var z = 0; z < 2; ++z) { with({}) for(let y in [1, null]); let(x)' + '(function(){})(); }' ); } catch(ex) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-472528-02.js0000644000175000017500000000176714433667662025762 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472528-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472528; var summary = 'Do not assert: !fp->blockChain'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { for (let i = 0; i < 4; ++i) { for (let j = 0; j < 2; ++j) { } let (x) (function(){}); } } catch(ex) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-472528-01.js0000644000175000017500000000202614433667662025746 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472528-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472528; var summary = 'Do not assert: !js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { for (var i = 0; i < 4; ++i) { for (let j = 0; j < 2; ++j) { } let (x) (function(){}); } } catch(ex) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-467495-05.js0000644000175000017500000000170514433667662025764 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-467495-05.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 467495; var summary = 'TCF_FUN_CLOSURE_VS_VAR is necessary'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function x() {\n}'; function g(x) { if (1) function x() {} return x; } print(actual = g(1) + ''); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-07.js0000644000175000017500000000177214433667662025760 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-07.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = actual = 'pass'; jit (true); try { e = ; for (j=0;j<3;++j) { 3 | e; } "PASS"; } catch(ex) { actual = ex + ''; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465483.js0000755000175000017500000000176514433667662025546 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465483.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465483; var summary = 'Type instability leads to undefined being added as a string instead of as a number'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'NaN'; jit(true); for each (i in [4, 'a', 'b', (void 0)]) print(actual = '' + (i + i)); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-472450-01.js0000644000175000017500000000201514433667662025736 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472450-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472450; var summary = 'TM: Do not assert: StackBase(fp) + blockDepth == regs.sp'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x in [function(){}, {}, {}, function(){}, function(){}, function(){}]) { ([ for (y in x)]); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-05.js0000644000175000017500000000163414433667662025753 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-05.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (var j = 0; j < 3; ++j) { 1 & Date; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-10.js0000644000175000017500000000163514433667662025750 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-10.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let i = 0; i < 2; ++i) { ({}) + 3; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-459185.js0000755000175000017500000000165214433667662025543 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-459185.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 459185; var summary = 'Do not assert: pn->pn_arity == PN_BINARY'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { for (var {a: []} = 2 in []) { } } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-12.js0000644000175000017500000000164614433667662025754 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-12.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (var j = 0; j < 2; ++j) { if (null > "") { } } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-464092-02.js0000644000175000017500000000166014433667662025747 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-464092-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 464092; var summary = 'Censor block objects'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { let (a) 'b'.replace(/b/g, function() c = this ); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465261.js0000644000175000017500000000172714433667662025533 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465261.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465261; var summary = 'TM: Do not assert: '; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let z = 0; z < 2; ++z) { for each (let x in [0, true, (void 0), 0, (void 0)]) { if(x){} } }; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-433279-03.js0000755000175000017500000000171114433667662025753 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-433279-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 433279; var summary = 'Do not assert: pn != tc->parseContext->nodeList'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { eval('({a}) = b; with({}) { for(let y in z) { } }'); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465454.js0000644000175000017500000000201214433667662025523 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465454.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465454; var summary = 'TM: do not crash with type-unstable loop'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let b in [(-1/0), new String(''), new String(''), null, (-1/0), (-1/0), new String(''), new String(''), null]) '' + b; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-08.js0000644000175000017500000000165414433667662025760 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-08.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let _ in [{}, {}, null, null, null, null]) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-09.js0000644000175000017500000000167414433667662025763 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-09.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let i in (function() { for (var j = 0; j < 3; ++j) yield; })()) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-464096.js0000644000175000017500000000204714433667662025534 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-464096.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 464096; var summary = 'TM: Do not assert: tm->recoveryDoublePoolPtr > tm->recoveryDoublePool'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let f in [1,1]); Object.prototype.__defineGetter__('x', function() gc()); (function() { for each (let j in [1,1,1,1,1]) { var y = .2; } })(); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-479740.js0000644000175000017500000000176214433667662025541 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479740.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479740; var summary = 'TM: Do not crash @ TraceRecorder::test_property_cache'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { eval( ' function f() {' + ' for ( foobar in (function() {' + ' for (var x = 0; x < 2; ++x) {' + ' if (x % 2 == 1) { yield (this.z.z) = function(){} }' + ' eval("this", false);' + ' }' + ' })());' + ' function(){}' + ' }' + ' f();' ); } catch(ex) { } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-467495-04.js0000644000175000017500000000174714433667662025771 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-467495-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 467495; var summary = 'TCF_FUN_CLOSURE_VS_VAR is necessary'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function g() { if (1) function x() {}; var x; const y = 1; } try { g(); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-469625-02.js0000644000175000017500000000166614433667662025764 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-469625-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 469625; var summary = 'group assignment with rhs containing holes'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'y'; Array.prototype[1] = 'y'; var [x, y, z] = ['x', , 'z']; actual = y; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-404734.js0000755000175000017500000000164714433667662025535 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-404734.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 404734; var summary = 'Object destructuring shorthand'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var o = {p: 42, q: true}; var {p, q} = o; expect = '42,true'; actual = p + ',' + q; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-06.js0000644000175000017500000000165714433667662025761 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-06.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x in [1, {}, 1, null, 1, {}, 1, null, 1]) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465239.js0000644000175000017500000000167414433667662025541 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465239.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465239; var summary = '"1e+81" ^ 3'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '3,3,3,3,3,'; actual = ''; jit(true); for (let j = 0; j < 5; ++j) actual += ("1e+81" ^ 3) + ','; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-474935.js0000644000175000017500000000213214433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-474935.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 474935; var summary = 'Do not assert: !ti->typeMap.matches(ti_other->typeMap)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); var a = ["", 0, 0, 0, 0, 0, "", "", 0, "", 0, ""]; var i = 0; var g = 0; for each (let e in a) { "" + [e]; if (i == 3 || i == 7) { for each (g in [1]) { } } ++i; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-467495-02.js0000644000175000017500000000167114433667662025763 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-467495-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 467495; var summary = 'Do not crash @ js_Interpret'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { eval("(function(){const y = 1; function x() '' let functional, x})();"); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/browser.js0000644000175000017500000000000014433667662024756 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-453492.js0000644000175000017500000000162714433667662025535 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-453492.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 453492; var summary = 'Do not assert: op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); print(function() { [(let(a)1)[2]] = 3; }); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-474769.js0000644000175000017500000000177414433667662025552 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-474769.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 474769; var summary = 'TM: nested for each type-unstable loops'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 1; jit(true); for each (b in [1, 1, 1, 1.5, 1, 1]) { (function() { for each (let h in [0, 0, 1.4, ""]) {} })(); } actual = b; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-464418.js0000644000175000017500000000226314433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-464418.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 464418; var summary = 'Do not assert: fp->slots + fp->script->nfixed + ' + 'js_ReconstructStackDepth(cx, fp->script, fp->regs->pc) == fp->regs->sp'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (typeof gczeal == 'function') { gczeal(2); } for (let q = 0; q < 50; ++q) { new Function("for (var i = 0; i < 5; ++i) { } ")(); var w = "r".match(/r/); new Function("for (var j = 0; j < 1; ++j) { } ")(); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-366941.js0000755000175000017500000000333614433667662025541 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-366941.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 366941; var summary = 'Destructuring enumerations, iterations'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var list1 = [[1,2],[3,4],[5,6]]; var list2 = [[1,2,3],[4,5,6],[7,8,9]]; expect = '1,2;3,4;5,6;'; actual = ''; for each (var [foo, bar] in list1) { actual += foo + "," + bar + ";"; } reportCompare(expect, actual, summary + ': 1'); expect = '1,2,3;4,5,6;7,8,9;'; actual = ''; for each (var [foo, bar, baz] in list2) { actual += foo + "," + bar + "," + baz + ";"; } reportCompare(expect, actual, summary + ': 2'); function gen(list) { for each (var test in list) { yield test; } } var iter1 = gen(list1); expect = '1,2;3,4;5,6;'; actual = ''; for (var [foo, bar] in iter1) { actual += foo + "," + bar + ";"; } reportCompare(expect, actual, summary + ': 3'); var iter2 = gen(list2); expect = '1,2,3;4,5,6;7,8,9;'; actual = ''; for (var [foo, bar, baz] in iter2) { actual += foo + "," + bar + "," + baz + ";"; } reportCompare(expect, actual, summary + ': 4'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465220.js0000644000175000017500000000225014433667662025516 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465220.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465220; var summary = 'Do not assert: anti-nesting'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: can\'t convert o to primitive type'; jit(true); try { var o = {toString: function()(i > 2) ? this : "foo"}; var s = ""; for (var i = 0; i < 5; i++) s += o + o; print(s); actual = 'No Exception'; } catch(ex) { actual = 'TypeError: can\'t convert o to primitive type'; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-466787.js0000644000175000017500000000201014433667662025533 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466787.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466787; var summary = 'TM: new Number() should stay a number during recording'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '4444'; jit(true); for (let j = 0; j < 4; ++j) { for each (let one in [new Number(1)]) { print(actual += '' + (3 + one)); } } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465241.js0000644000175000017500000000166514433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465241.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465241; var summary = '"0" in [3]'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = true; actual = true; jit(true); for (let j = 0; j < 5; ++j) actual = actual && ("0" in [3]); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-01.js0000644000175000017500000000173014433667662025744 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '11111'; jit(true); (function(d) { for (let j = 0; j < 5; ++j) { actual += ('' + d); } })({valueOf: function()1}); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-461932.js0000644000175000017500000000171614433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-461932.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 461932; var summary = 'TM: Do not crash in nanojit::LIns::isop'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); function gen() { for (var j = 0; j < 4; ++j) { NaN; yield 3; } } for (let i in gen()) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-463783.js0000644000175000017500000000175414433667662025542 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-463783.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 463783; var summary = 'TM: Do not assert: "need a way to EOT now, since this is trace end": 0'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); var a = [0,1,2,3,4,5]; for (let f in a); [(function(){})() for each (x in a) if (x)]; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-457065-01.js0000644000175000017500000000177614433667662025760 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-457065-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 457065; var summary = 'Do not assert: !fp->callee || fp->thisp == JSVAL_TO_OBJECT(fp->argv[-1])'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); var e = eval; for (var a in this) { } (function() { eval("this; for (let b in [0,1,2]) { }"); })(); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-463334-02.js0000644000175000017500000000172714433667662025751 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-463334-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 463334; var summary = 'TM: Do not crash in isPromoteInt'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let i in (function() { for (let j = 0; j < 4; ++j) with({t: NaN}) yield; })()) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-474771.js0000644000175000017500000000175714433667662025544 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-474771.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 474771; var summary = 'gczeal, prototype mangling, for..in'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'PASS'; if (typeof gczeal == 'function') { gczeal(2); } Object.prototype.q = 3; for each (let x in [6, 7]) { } print(actual = "PASS"); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/CVS/0000755000175000017500000000000014433667662023402 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/CVS/Repository0000644000175000017500000000003714433667662025504 0ustar apoapomozilla/js/tests/js1_8/regress closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/CVS/Entries0000644000175000017500000001037614433667662024745 0ustar apoapo/browser.js/1.1/Wed May 2 16:43:32 2007// /regress-366941.js/1.1/Sun May 27 20:13:35 2007// /regress-384412.js/1.1/Sat Apr 5 08:12:31 2008// /regress-384758.js/1.1/Fri Mar 6 01:38:59 2009// /regress-404734.js/1.1/Fri Feb 22 01:00:36 2008// /regress-427798.js/1.1/Wed Aug 6 09:37:58 2008// /regress-433279-01.js/1.1/Wed Aug 6 17:47:54 2008// /regress-433279-02.js/1.1/Wed Aug 6 17:47:54 2008// /regress-433279-03.js/1.1/Wed Aug 6 17:47:54 2008// /regress-442333-01.js/1.1/Thu Aug 7 12:00:08 2008// /regress-452491.js/1.1/Thu Sep 18 20:16:25 2008// /regress-453492.js/1.1/Fri Aug 7 21:17:12 2009// /regress-455981-01.js/1.1/Fri Aug 7 21:20:59 2009// /regress-455981-02.js/1.1/Fri Aug 7 21:20:59 2009// /regress-457065-01.js/1.1/Fri Mar 20 05:00:41 2009// /regress-457065-02.js/1.1/Fri Mar 20 05:00:41 2009// /regress-458076.js/1.1/Sun Oct 12 03:49:46 2008// /regress-459185.js/1.2/Sat Feb 21 01:30:27 2009// /regress-459186.js/1.1/Sun Oct 12 03:49:46 2008// /regress-459389.js/1.1/Mon Oct 20 15:46:09 2008// /regress-461930.js/1.1/Thu Dec 4 13:02:00 2008// /regress-461932.js/1.1/Thu Dec 4 13:02:00 2008// /regress-463334-01.js/1.1/Thu Dec 4 13:02:00 2008// /regress-463334-02.js/1.1/Thu Dec 4 13:02:00 2008// /regress-463783.js/1.1/Wed Jan 28 16:55:45 2009// /regress-464092-01.js/1.1/Fri Mar 6 01:39:00 2009// /regress-464092-02.js/1.1/Fri Mar 6 01:39:00 2009// /regress-464096.js/1.1/Fri Mar 6 01:39:00 2009// /regress-464418.js/1.1/Thu Dec 4 13:02:00 2008// /regress-464978.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465220.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465234.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465239.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465241.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465249.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465261.js/1.1/Thu Dec 4 13:02:00 2008// /regress-465308.js/1.1/Thu Nov 27 11:28:26 2008// /regress-465454.js/1.1/Sat Feb 21 01:30:27 2009// /regress-465460-01.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-02.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-03.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-04.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-05.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-06.js/1.1/Sat Jan 24 13:07:23 2009// /regress-465460-07.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465460-08.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465460-09.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465460-10.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465460-11.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465460-12.js/1.1/Sat Jan 24 13:07:24 2009// /regress-465483.js/1.1/Thu Nov 27 11:19:06 2008// /regress-465567-01.js/1.1/Fri Mar 6 01:39:00 2009// /regress-465567-02.js/1.2/Fri Aug 7 21:09:04 2009// /regress-465688.js/1.1/Thu Nov 27 11:17:12 2008// /regress-466128.js/1.1/Thu Nov 27 11:15:00 2008// /regress-466787.js/1.1/Sat Feb 21 01:30:27 2009// /regress-467495-01.js/1.1/Fri Mar 6 01:39:00 2009// /regress-467495-02.js/1.1/Fri Mar 6 01:39:00 2009// /regress-467495-03.js/1.1/Fri Mar 6 01:39:00 2009// /regress-467495-04.js/1.1/Fri Mar 6 01:39:00 2009// /regress-467495-05.js/1.1/Fri Mar 6 01:39:00 2009// /regress-467495-06.js/1.1/Fri Mar 6 01:39:00 2009// /regress-468711.js/1.1/Fri Mar 6 01:39:00 2009// /regress-469547.js/1.1/Mon Feb 22 18:53:46 2010// /regress-469625-02.js/1.1/Sat Jan 24 13:07:24 2009// /regress-469625-03.js/1.1/Sat Jan 24 13:07:24 2009// /regress-471373.js/1.1/Fri Mar 20 05:00:41 2009// /regress-471660.js/1.1/Fri Mar 20 05:00:41 2009// /regress-472450-01.js/1.1/Fri Mar 6 01:39:00 2009// /regress-472450-02.js/1.1/Fri Mar 6 01:39:00 2009// /regress-472528-01.js/1.1/Fri Mar 20 05:00:41 2009// /regress-472528-02.js/1.1/Fri Mar 20 05:00:41 2009// /regress-472703.js/1.1/Fri Mar 20 05:00:41 2009// /regress-474769.js/1.1/Wed Jan 28 16:55:45 2009// /regress-474771.js/1.1/Sat Feb 21 01:30:27 2009// /regress-474935.js/1.1/Wed Jan 28 16:55:45 2009// /regress-476655.js/1.1/Fri Mar 20 05:00:41 2009// /regress-477234.js/1.1/Sat Feb 21 01:30:27 2009// /regress-477581.js/1.1/Sat Feb 21 01:30:27 2009// /regress-478205.js/1.1/Sat Feb 21 01:30:27 2009// /regress-479353.js/1.1/Fri Mar 6 01:39:00 2009// /regress-479740.js/1.1/Sat Mar 14 22:48:21 2009// /regress-481800.js/1.1/Sat Mar 14 22:48:21 2009// /regress-483749.js/1.1/Fri Mar 20 05:00:41 2009// /shell.js/1.2/Sat May 26 00:19:44 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/CVS/Root0000644000175000017500000000006314433667662024247 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-471373.js0000644000175000017500000000220414433667662025523 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-471373.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 471373; var summary = 'TM: do not assert: (size_t)(regs.pc - script->code) < script->length'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof window == 'undefined') { expectExitCode(5); } jit(true); function g() { var x = ; for (var b = 0; b < 2; ++b) { yield x; for (var c = 0; c < 10;++c) { x.r = x; } } } for (let y in g()) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-464092-01.js0000644000175000017500000000163314433667662025746 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-464092-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 464092; var summary = 'Do not assert: OBJ_IS_CLONED_BLOCK(obj)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); let (a) 'b'.replace(/b/g, function() { c = this; }); c.d = 3; c.d; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-458076.js0000755000175000017500000000167714433667662025550 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-458076.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 458076; var summary = 'Do not assert with JIT: !lhs->isQuad() && !rhs->isQuad()'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let j = 0; j < 3; ++j) { true == 0; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-478205.js0000644000175000017500000000162514433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-478205.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 478205; var summary = 'Do not assert: p->isQuad()'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x in ['', '']) { switch([]) {} } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-471660.js0000644000175000017500000000201014433667662025515 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-471660.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 471660; var summary = 'TM: Do not assert: !(fp->flags & JSFRAME_POP_BLOCKS)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); y = ; for (var w = 0; w < 5; ++w) { let (y) { do break ; while (true); } for each (let x in [{}, function(){}]) {y} } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-479353.js0000644000175000017500000000136614433667662025541 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479353.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479353; var summary = 'Do not assert: (uint32)(index_) < atoms_->length'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); function f(code) { (eval("(function(){" + code + "});"))(); } x = ; f("y = this;"); f("x, y; for each (let x in [arguments]) {}"); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-455981-01.js0000644000175000017500000000204414433667662025752 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455981-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455981; var summary = 'Do not assert: entry->localKind == JSLOCAL_ARG'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: duplicate argument is mixed with destructuring pattern'; try { eval('(function ({a, b, c, d, e, f, g, h, q}, q) { })'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-452491.js0000755000175000017500000000165014433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452491.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452491; var summary = 'Do not crash with JIT: with new'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (var j=0;j<5;++j) (new (function(q) q)).a; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-03.js0000644000175000017500000000163114433667662025746 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit (true); for each (let j in [null, 2, 3]) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465688.js0000755000175000017500000000165714433667662025555 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465688.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465688; var summary = 'Do not assert: (m != JSVAL_INT) || isInt32(*vp),'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let d in [-0x80000000, -0x80000000]) - -d; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-433279-02.js0000755000175000017500000000166414433667662025761 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-433279-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 433279; var summary = 'Do not assert: pn != tc->parseContext->nodeList'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { eval('var {a} = b; c(d + 1);'); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-442333-01.js0000755000175000017500000000174314433667662025745 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-442333-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 442333; var summary = 'Remove eval\'s optional second argument'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'ReferenceError: a is not defined'; var o = {a : 1}; try { eval('a', o); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-457065-02.js0000644000175000017500000000174214433667662025752 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-457065-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 457065; var summary = 'Do not assert: !fp->callee || fp->thisp == JSVAL_TO_OBJECT(fp->argv[-1])'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); (function(){ eval('this'); (function(){ for(let y in [0,1,2]) 6;})(); })(); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465567-01.js0000644000175000017500000000142314433667662025753 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465567-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465567; var summary = 'TM: Weirdness with var, let, multiple assignments'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); expect = '99999'; jit(true); for (let j = 0; j < 5; ++j) { e = 9; print(actual += '' + e); e = 47; if (e & 0) { var e; let e; } } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-476655.js0000644000175000017500000000206214433667662025535 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476655.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476655; var summary = 'Do not assert: depth <= (size_t) (fp->regs->sp - StackBase(fp))'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { eval( "for(let y in ['', '']) try {for(let y in ['', '']) ( /x/g ); } finally {" + "with({}){} } this.zzz.zzz" ); } catch(ex) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-469625-03.js0000644000175000017500000000202614433667662025754 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-469625-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 469625; var summary = 'Do not assert: script->objectsOffset != 0'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(x) { var [a, b, [c0, c1]] = [x, x, x]; } expect = 'TypeError: x is null'; actual = 'No Error'; try { f(null); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-467495-03.js0000644000175000017500000000221214433667662025754 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-467495-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 467495; var summary = 'TCF_FUN_CLOSURE_VS_VAR is necessary'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(x) { actual = ''; var g; print(actual += typeof g + ','); if (x) function g(){}; print(actual += g); } expect = 'undefined,undefined'; f(0); reportCompare(expect, actual, summary + ': f(0): '); expect = 'undefined,function g() {\n}'; f(1); reportCompare(expect, actual, summary + ': f(1): '); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465249.js0000644000175000017500000000170114433667662025531 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465249.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465249; var summary = 'Do not assert: (m != JSVAL_INT) || isInt32(*vp)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); eval("for (let j = 0; j < 5; ++j) { (0x50505050) + (0x50505050); }"); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-468711.js0000644000175000017500000000165614433667662025537 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-468711.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 468711; var summary = 'TM: Do not assert: !JS_ON_TRACE(cx)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); (5).toString(); for each (let b in [3, this]) { b.toString(); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465234.js0000644000175000017500000000166514433667662025534 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465234.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465234; var summary = '"" <= null'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = true; actual = true; jit(true); for (let j = 0; j < 5; ++j) actual = actual && ("" <= null); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-464978.js0000644000175000017500000000162114433667662025542 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-464978.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 464978; var summary = 'Do not hang with [] + null'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let j = 0; j < 2; ++j) { [] + null; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-433279-01.js0000755000175000017500000000162614433667662025756 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-433279-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 433279; var summary = 'Do not assert: pn != tc->parseContext->nodeList'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var { sin, PI } = Math; sin(PI / 2); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-455981-02.js0000644000175000017500000000206414433667662025755 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455981-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455981; var summary = 'Do not assert: entry->localKind == JSLOCAL_ARG'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: duplicate argument is mixed with destructuring pattern'; try { eval('(function ({a: {b: bb, c: cc, d: dd}, m: [x, n, o, p]}, x) {});'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465460-11.js0000644000175000017500000000167114433667662025751 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465460-11.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465460; var summary = 'TM: valueOf in a loop: do not assert'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (let d=0;d<2;++d) for (let a=0;a<1;++a) "" + (d && function(){}); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/regress/regress-465308.js0000755000175000017500000000201414433667662025526 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465308.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465308; var summary = '((0x60000009) * 0x60000009))'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,'; jit(true); for (let j=0;j<5;++j) print(actual += "" + (0 | ((0x60000009) * 0x60000009)) + ','); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/README0000755000175000017500000000001714433667662022156 0ustar apoapoJavaScript 1.8 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/0000755000175000017500000000000014433667662023474 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-475971.js0000644000175000017500000000264514433667662026271 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-475971.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 475971; var summary = 'js_CheckRedeclaration should unlock object on failures'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof scatter != 'function') { print(expect = actual = 'Test skipped - requires threaded build with scatter'); } else { function x() { return 1; }; // g must run sufficiently long to ensure that the global scope is accessed // from the parallel threads. function g() { var sum = 0; try { for (var i = 0; i != 10000; ++i) { sum += x(); } } catch (e) { } } scatter([g, g]); try { eval("const x = 1"); } catch (e) { } scatter([g, g]); print("Done"); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-455973.js0000644000175000017500000000147614433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455973.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455973; var summary = 'Do not assert: !cx->throwing'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { for (let i = 0; i < 5; ++i) void (this["y" + i] = ""); this.__defineGetter__("z", function () { throw 2; }); for (let j = 0; j < 2; ++j) { [1 for each (q in this) if ('')]; } } catch(ex) { } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476871-02.js0000644000175000017500000000146514433667662026507 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476871-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476871; var summary = 'Do not crash @ js_StepXMLListFilter'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { this.watch("NaN", /a/g) let(x) ((function(){ for each (NaN in [null, '', '', '', '']) true; } )()); NaN.( /x/ ); } catch(ex) { print(ex + ''); } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/shell.js0000644000175000017500000000035414433667662025143 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'extensions'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-394709.js0000755000175000017500000000246414433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-394709.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 394709; var summary = 'Do not leak with object.watch and closure'; var actual = 'No Leak'; var expect = 'No Leak'; if (typeof countHeap == 'undefined') { countHeap = function () { print('This test requires countHeap which is not supported'); return 0; }; } //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); runtest(); gc(); var counter = countHeap(); runtest(); gc(); if (counter != countHeap()) throw "A leaky watch point is detected"; function runtest () { var obj = { b: 0 }; obj.watch('b', watcher); function watcher(id, old, value) { ++obj.n; return value; } } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476414-02.js0000644000175000017500000000373114433667662026476 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476414-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476414; var summary = 'Do not crash @ js_NativeSet'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); function whatToTestSpidermonkeyTrunk(code) { return { allowExec: true }; } whatToTest = whatToTestSpidermonkeyTrunk; function tryItOut(code) { var wtt = whatToTest(code.replace(/\n/g, " ").replace(/\r/g, " ")); var f = new Function(code); if (wtt.allowExec && f) { rv = tryRunning(f, code); } } function tryRunning(f, code) { try { var rv = f(); } catch(runError) {} } var realFunction = Function; var realUneval = uneval; var realToString = toString; var realToSource = toSource; function tryEnsureSanity() { delete Function; delete uneval; delete toSource; delete toString; Function = realFunction; uneval = realUneval; toSource = realToSource; toString = realToString; } for (let iters = 0; iters < 2000; ++iters) { count=27745; tryItOut("with({x: (c) = (x2 = [])})false;"); tryEnsureSanity(); count=35594; tryItOut("switch(null) { case this.__defineSetter__(\"window\", function () { yield \"\" } ): break; }"); tryEnsureSanity(); count=45020; tryItOut("with({}) { (this.__defineGetter__(\"x\", function (y)this)); } "); tryEnsureSanity(); count=45197; tryItOut("M:with((p={}, (p.z = === '' )()))/*TUUL*/for each (let y in [true, {}, {}, (void 0), true, true, true, (void 0), true, (void 0)]) { return; }"); tryEnsureSanity(); gc(); tryEnsureSanity(); count=45254; tryItOut("for each (NaN in this);"); tryEnsureSanity(); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-479252.js0000644000175000017500000000220714433667662026257 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479252.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479252; var summary = 'Avoid watchdog ticks when idle in shell'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof sleep != 'function' || typeof scatter != 'function' || typeof timeout != 'function') { print(expect = actual = 'Test skipped: requires mulithreads and timeout.'); } else { expectExitCode(6); function f() { sleep(100); } timeout(1.0); scatter([f,f,f,f,f]); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-445818.js0000755000175000017500000000215214433667662026262 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-445818.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 445818; var summary = 'Do not crash with threads'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- printBugNumber(BUGNUMBER); printStatus (summary); if (typeof scatter == 'undefined') { print(expect = actual = 'Test skipped. scatter not defined'); } else { var array = [{}, {}, {}, {}]; function test() { for (var i = 0; i != 42*42*42; ++i) { var obj = array[i % array.length]; obj["a"+i] = 1; var tmp = {}; tmp["a"+i] = 2; } } scatter([test, test, test, test]); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-378789.js0000755000175000017500000000173614433667662026305 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-378789.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 378789; var summary = 'js_PutEscapedString should handle nulls'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof dumpHeap == 'undefined') { print('dumpHeap not supported'); } else { dumpHeap(null, [ "a\0b" ], null, 1); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-417131.js0000644000175000017500000000403014433667662026237 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-417131.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 417131; var summary = 'stress test for cache'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(N) { for (var i = 0; i != N; ++i) { var obj0 = {}, obj1 = {}, obj2 = {}; obj1['a'+i] = 0; obj2['b'+i] = 0; obj2['b'+(i+1)] = 1; for (var repeat = 0;repeat != 2; ++repeat) { var count = 0; for (var j in obj1) { if (j !== 'a'+i) throw "Bad:"+j; for (var k in obj2) { if (i == Math.floor(N/3) || i == Math.floor(2*N/3)) gc(); var expected; switch (count) { case 0: expected='b'+i; break; case 1: expected='b'+(i+1); break; default: throw "Bad count: "+count; } if (expected != k) throw "Bad k, expected="+expected+", actual="+k; for (var l in obj0) ++count; ++count; } } if (count !== 2) throw "Bad count: "+count; } } } var array = [function() { f(10); }, function() { f(50); }, function() { f(200); }, function() { f(400); } ]; if (typeof scatter == "function") { scatter(array); } else { for (var i = 0; i != array.length; ++i) array[i](); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-385393-10.js0000755000175000017500000000163614433667662026507 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-385393-10.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 385393; var summary = 'Regression test for bug 385393'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { [1 for (x in [])].watch(); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-452476.js0000755000175000017500000000151014433667662026255 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452476.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452476; var summary = 'Do not assert with JIT: !cx->runningJittedCode'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (var j = 0; j < 10; j++) { for (var i = 0; i < j; ++i) this["n" + i] = 1; __defineGetter__('w', (function(){})); [1 for each (g in this) for each (t in /x/g)]; } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/lamport.js0000644000175000017500000000430014433667662025505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'lamport.js' //----------------------------------------------------------------------- var summary = "Lamport Bakery's algorithm for mutual exclusion"; // Adapted from pseudocode in Wikipedia: // http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm printStatus(summary); var N = 15; // Number of threads. var LOOP_COUNT = 10; // Number of times each thread should loop function range(n) { for (let i = 0; i < n; i++) yield i; } function max(a) { let x = Number.NEGATIVE_INFINITY; for each (let i in a) if (i > x) x = i; return x; } // the mutex mechanism var entering = [false for (i in range(N))]; var ticket = [0 for (i in range(N))]; // the object being protected var counter = 0; function lock(i) { entering[i] = true; ticket[i] = 1 + max(ticket); entering[i] = false; for (let j = 0; j < N; j++) { // If thread j is in the middle of getting a ticket, wait for that to // finish. while (entering[j]) ; // Wait until all threads with smaller ticket numbers or with the same // ticket number, but with higher priority, finish their work. while ((ticket[j] != 0) && ((ticket[j] < ticket[i]) || ((ticket[j] == ticket[i]) && (i < j)))) ; } } function unlock(i) { ticket[i] = 0; } function worker(i) { for (let k = 0; k < LOOP_COUNT; k++) { lock(i); // The critical section let x = counter; sleep(0.003); counter = x + 1; unlock(i); } return 'ok'; } function makeWorker(id) { return function () { return worker(id); }; } var expect; var actual; if (typeof scatter == 'undefined' || typeof sleep == 'undefined') { print('Test skipped. scatter or sleep not defined.'); expect = actual = 'Test skipped.'; } else { scatter([makeWorker(i) for (i in range(N))]); expect = "counter: " + (N * LOOP_COUNT); actual = "counter: " + counter; } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-385393-11.js0000755000175000017500000000166014433667662026505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-385393-11.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 385393; var summary = 'Regression test for bug 385393'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { var n = null; [1 for (x in [])](n.b()); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-482263.js0000644000175000017500000000144014433667662026251 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-482263.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 482263; var summary = 'TM: Do not assert: x->oprnd2() == lirbuf->sp || x->oprnd2() == gp_ins'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); __proto__.x getter= function () { return .([]) }; for each (let x in []) { for each (let x in ['', '']) { } } jit(true); reportCompare(expect, actual, summary); delete __proto__.x; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-465453.js0000755000175000017500000000260114433667662026256 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465453.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465453; var summary = 'Do not convert (undefined) to "undefined"'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '[(new Boolean(true)), (void 0), (new Boolean(true)), ' + '(new Boolean(true)), (void 0), (void 0), "", "", (void 0)]'; jit(true); var out = []; for each (var e in [(new Boolean(true)), (void 0), (new Boolean(true)), (new Boolean(true)), (void 0), (void 0), "", "", (void 0)]) out.push(e); print(actual = uneval(out)); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476869.js0000644000175000017500000000235114433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476869.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476869; var summary = 'Do not assert: v != JSVAL_ERROR_COOKIE'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof gczeal == 'undefined') { gczeal = (function (){}); } jit(true); function f() { (new Function("gczeal(1); for each (let y in [/x/,'',new Boolean(false),new Boolean(false),new Boolean(false),'',/x/,new Boolean(false),new Boolean(false)]){}"))(); } __proto__.__iterator__ = this.__defineGetter__("", function(){}) f(); jit(false); delete __proto__.__iterator__; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/for-in.js0000644000175000017500000000244214433667662025226 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'for-in.js'; //----------------------------------------------------------------------------- var summary = "Contention among threads enumerating properties"; // Adapted from mozilla/js/src/xpconnect/tests/js/old/threads.js printStatus (summary); var LOOP_COUNT = 1000; var THREAD_COUNT = 10; var foo; var bar; function makeWorkerFn(id) { return function() { foo = id + 1; bar[id] = {p: 0}; var n, m; for (n in bar) { for (m in bar[n]) {} } for (n in {}.__parent__) {} }; } function range(n) { for (let i = 0; i < n; i++) yield i; } var expect; var actual; expect = actual = 'No crash'; if (typeof scatter == 'undefined') { print('Test skipped. scatter not defined.'); } else if (!("__parent__" in {})) { print('Test skipped. __parent__ not defined.'); } else { for (let i = 0; i < LOOP_COUNT; i++) { foo = 0; bar = new Array(THREAD_COUNT); scatter([makeWorkerFn(j) for (j in range(THREAD_COUNT))]); } } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/simple-tree.js0000644000175000017500000000247014433667662026263 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'simple-tree.js'; //----------------------------------------------------------------------------- var summary = "Create a tree of threads"; var N = 50; // number of threads to create printStatus (summary); function range(start, stop) { var a = []; for (var i = start; i < stop; i++) a.push(i); return a; } function tree(start, stop) { sleep(0.001); if (start >= stop) return []; else if (start + 1 >= stop) return [start]; sleep(0.001); let mid = start + Math.floor((stop - start) / 2); let halves = scatter([function () { return tree(start, mid); }, function () { return tree(mid, stop); }]); sleep(0.001); return Array.prototype.concat.apply([], halves); } var expect; var actual; if (typeof scatter == 'undefined' || typeof sleep == 'undefined') { print('Test skipped. scatter or sleep not defined.'); expect = actual = 'Test skipped.'; } else { expect = range(0, N).toSource(); actual = tree(0, N).toSource(); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-473040.js0000644000175000017500000000150514433667662026244 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-473040.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 473040; var summary = 'Do not assert: tm->reservedDoublePoolPtr > tm->reservedDoublePool'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); __proto__.functional getter= (new Function("gc()")); for each (let x in [new Boolean(true), new Boolean(true), -0, new Boolean(true), -0]) { undefined; } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-385393-01.js0000755000175000017500000000163414433667662026505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-385393-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 385393; var summary = 'Regression test for bug 385393'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { [].map(1 for (x in [])); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/dekker.js0000644000175000017500000000315614433667662025304 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'dekker.js'; //----------------------------------------------------------------------------- var summary = "Dekker's algorithm for mutual exclusion"; // Adapted from pseudocode in Wikipedia: // http://en.wikipedia.org/wiki/Dekker%27s_algorithm printStatus (summary); var N = 500; // number of iterations // the mutex mechanism var f = [false, false]; var turn = 0; // resource being protected var counter = 0; function worker(me) { let him = 1 - me; for (let i = 0; i < N; i++) { // enter the mutex f[me] = true; while (f[him]) { if (turn != me) { f[me] = false; while (turn != me) ; // busy wait f[me] = true; } } // critical section let x = counter; sleep(0.003); counter = x + 1; // leave the mutex turn = him; f[me] = false; } return 'ok'; } var expect; var actual; if (typeof scatter == 'undefined' || typeof sleep == 'undefined') { print('Test skipped. scatter or sleep not defined.'); expect = actual = 'Test skipped.'; } else { var results = scatter([function () { return worker(0); }, function () { return worker(1); }]); expect = "Thread status: [ok,ok], counter: " + (2 * N); actual = "Thread status: [" + results + "], counter: " + counter; } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-385729.js0000755000175000017500000000271714433667662026275 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-385729.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 385729; var summary = 'uneval(eval(expression closure))'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof eval != 'undefined' && typeof uneval != 'undefined') { expect = '(function f () /x/g)'; try { // mozilla 1.9 actual = uneval(eval(expect)); } catch(ex) { // mozilla 1.8 expect = 'SyntaxError: missing { before function body'; actual = ex + ''; } compareSource(expect, actual, summary); expect = '({get f () /x/g})'; try { // mozilla 1.9 actual = uneval(eval("({get f () /x/g})")); } catch(ex) { // mozilla 1.8 expect = 'SyntaxError: missing { before function body'; actual = ex + ''; } compareSource(expect, actual, summary); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-417817.js0000755000175000017500000000227414433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-417817.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 417817; var summary = 'Do not assert: ASSERT_VALID_PROPERTY_CACHE_HIT'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); const numThreads = 2; const numPasses = 1000; var tests = { 0: function first(myAn) { /* This print statement is needed to reliably trigger the bug */ print("Hello, World!"); }, length: 1 }; function runAllTests() { var passes; var i; for (passes = 0; passes < numPasses; passes++) { for (i = 0; i < tests.length; i++) { tests[0](); } } } if (typeof scatter == 'undefined') { print(expect = actual = 'Test skipped. Requires scatter.'); } else { var i; var a = []; for (i = 0; i < numThreads; i++) a.push(runAllTests); scatter(a); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-446169-01.js0000755000175000017500000000235414433667662026504 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-446169-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 446169; var summary = 'Do not assert: Thin_GetWait(tl->owner) in thread-safe build'; var actual = 'No Crash'; var expect = 'No Crash'; var array = [{}, {}, {}, {}]; function foo() { for (var i = 0; i != 42*42*42; ++i) { var obj = array[i % array.length]; obj["a"+i] = 1; var tmp = {}; tmp["a"+i] = 2; } } //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof scatter == 'function') { scatter([foo, foo, foo, foo]); } else { print('Test skipped. Requires thread-safe build with scatter function.'); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/browser.js0000644000175000017500000000000014433667662025503 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476414-01.js0000644000175000017500000000373514433667662026501 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476414-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476414; var summary = 'Do not crash @ GetGCThingFlags'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); function whatToTestSpidermonkeyTrunk(code) { return { allowExec: true }; } whatToTest = whatToTestSpidermonkeyTrunk; function tryItOut(code) { var wtt = whatToTest(code.replace(/\n/g, " ").replace(/\r/g, " ")); var f = new Function(code); if (wtt.allowExec && f) { rv = tryRunning(f, code); } } function tryRunning(f, code) { try { var rv = f(); } catch(runError) {} } var realFunction = Function; var realUneval = uneval; var realToString = toString; var realToSource = toSource; function tryEnsureSanity() { delete Function; delete uneval; delete toSource; delete toString; Function = realFunction; uneval = realUneval; toSource = realToSource; toString = realToString; } for (let iters = 0; iters < 2000; ++iters) { count=27745; tryItOut("with({x: (c) = (x2 = [])})false;"); tryEnsureSanity(); count=35594; tryItOut("switch(null) { case this.__defineSetter__(\"window\", function () { yield \"\" } ): break; }"); tryEnsureSanity(); count=45020; tryItOut("with({}) { (this.__defineGetter__(\"x\", function (y)this)); } "); tryEnsureSanity(); count=45197; tryItOut("M:with((p={}, (p.z = === '' )()))/*TUUL*/for each (let y in [true, {}, {}, (void 0), true, true, true, (void 0), true, (void 0)]) { return; }"); tryEnsureSanity(); gc(); tryEnsureSanity(); count=45254; tryItOut("for each (NaN in this);"); tryEnsureSanity(); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-469625.js0000644000175000017500000000204514433667662026262 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-469625.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 469625; var summary = 'TM: Array prototype and expression closures'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: [] is not a function'; jit(true); Array.prototype.__proto__ = function () 3; try { [].__proto__(); } catch(ex) { print(actual = ex + ''); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-479381.js0000644000175000017500000000232614433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479381.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479381; var summary = 'Do not crash @ js_FinalizeStringRT with multi-threads.'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof gczeal != 'function' || typeof scatter != 'function') { print(expect = actual = 'Test skipped: requires mulithreads'); } else { expect = actual = 'No Crash'; gczeal(2); function f() { var s; for (var i = 0; i < 9999; i++) s = 'a' + String(i)[3] + 'b'; return s; } print(scatter([f, f, f, f])); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-353116.js0000755000175000017500000000304414433667662026250 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-353116.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 353116; var summary = 'Improve errors messages for null, undefined properties'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: undefined has no properties'; actual = 'No Error'; try { undefined.y; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); expect = 'TypeError: null has no properties'; actual = 'No Error'; try { null.y; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); expect = 'TypeError: x is undefined'; actual = 'No Error'; try { x = undefined; x.y; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); expect = 'TypeError: x is null'; actual = 'No Error'; try { x = null; x.y; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-471197.js0000644000175000017500000000214014433667662026253 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-471197.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 471197; var summary = 'Do not crash when cx->thread is null'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var results; jit(true); function f() { for (var i = 0; i != 1000; ++i) { } } if (typeof scatter == 'function') { results = scatter([f, f]); } else { print('Test skipped due to lack of scatter threadsafe function'); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-481989.js0000644000175000017500000000131214433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-481989.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 481989; var summary = 'TM: Do not assert: SPROP_HAS_STUB_SETTER(sprop)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); y = this.watch("x", function(){}); for each (let y in ['', '']) x = y; jit(true); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/peterson.js0000644000175000017500000000265114433667662025675 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'peterson.js'; //----------------------------------------------------------------------- var summary = "Peterson's algorithm for mutual exclusion"; printStatus(summary); var N = 500; // number of iterations // the mutex mechanism var f = [false, false]; var turn = 0; // the resource being protected var counter = 0; function worker(me) { let him = 1 - me; for (let i = 0; i < N; i++) { // enter the mutex f[me] = true; turn = him; while (f[him] && turn == him) ; // busy wait // critical section let x = counter; sleep(0.003); counter = x+1; // leave the mutex f[me] = false; } return 'ok'; } var expect; var actual; if (typeof scatter == 'undefined' || typeof sleep == 'undefined') { print('Test skipped. scatter or sleep not defined.'); expect = actual = 'Test skipped.'; } else { var results = scatter ([function() { return worker(0); }, function() { return worker(1); }]); expect = "Thread status: [ok,ok], counter: " + (2 * N); actual = "Thread status: [" + results + "], counter: " + counter; } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-446169-02.js0000755000175000017500000000230714433667662026503 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-446169-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 446169; var summary = 'Do not assert: Thin_GetWait(tl->owner) in thread-safe build'; var actual = 'No Crash'; var expect = 'No Crash'; var array = []; for (var i = 0; i != 100*1000; i += 2) { array[i] = i; array[i+1] = i; } var src = array.join(';x'); function f() { new Function(src); } //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof scatter == 'function') { scatter([f, f, f, f]); } else { print('Test skipped. Requires thread-safe build with scatter function.'); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476427.js0000644000175000017500000000174014433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476427.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476427; var summary = 'Do not assert: v != JSVAL_ERROR_COOKIE'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); function a1(){} a2 = a1; a3 = a1; a4 = a1; c1 = toString; c2 = toSource; function foo(code) { (new Function(code))(); delete toSource; delete toString; toSource = c2; toString = c1; } let z; for (z = 1; z <= 16322; ++z) { this.__defineGetter__('functional', function x(){ yield; } ); foo("this.__defineSetter__('', function(){});"); foo("for each (y in this);"); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-419091.js0000755000175000017500000000222214433667662026252 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-419091.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 419091; var summary = 'Do not assert: JS_PROPERTY_CACHE(cx).disabled >= 0'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof scatter == 'undefined') { print(expect = actual = 'Test skipped. Requires scatter.'); } else { gczeal(2); function f() { for (let i = 0; i < 10; i++) { let y = { x: i } } } for (let i = 0; i < 10; i++) scatter([f, f]); gczeal(0); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/CVS/0000755000175000017500000000000014433667662024127 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/CVS/Repository0000644000175000017500000000004214433667662026225 0ustar apoapomozilla/js/tests/js1_8/extensions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/CVS/Entries0000644000175000017500000000427514433667662025473 0ustar apoapo/browser.js/1.1/Wed May 2 16:43:32 2007// /dekker.js/1.1/Wed Feb 20 11:27:41 2008// /for-in.js/1.1/Wed Feb 20 11:27:41 2008// /lamport.js/1.1/Wed Feb 20 11:27:41 2008// /peterson.js/1.1/Wed Feb 20 11:27:41 2008// /regress-353116.js/1.1/Sun Dec 9 00:28:59 2007// /regress-378789.js/1.2/Sat May 26 00:19:44 2007// /regress-385393-01.js/1.1/Fri Aug 10 21:41:59 2007// /regress-385393-10.js/1.1/Fri Aug 10 21:41:59 2007// /regress-385393-11.js/1.2/Wed Jun 25 14:45:47 2008// /regress-385729.js/1.1/Fri Jul 13 01:49:58 2007// /regress-394709.js/1.1/Tue Sep 11 18:03:23 2007// /regress-415721.js/1.1/Wed Feb 20 11:27:41 2008// /regress-417131.js/1.1/Wed Jan 14 16:13:12 2009// /regress-417817.js/1.1/Fri Oct 17 21:14:40 2008// /regress-419091.js/1.1/Fri Oct 17 21:14:40 2008// /regress-422269.js/1.1/Wed Aug 6 09:24:15 2008// /regress-445818.js/1.1/Fri Oct 17 21:14:40 2008// /regress-446169-01.js/1.1/Thu Aug 7 02:58:16 2008// /regress-446169-02.js/1.1/Thu Aug 7 02:58:16 2008// /regress-452476.js/1.1/Sun Oct 12 03:49:45 2008// /regress-452913.js/1.1/Sat Mar 14 22:48:22 2009// /regress-454744.js/1.1/Thu Sep 18 20:08:18 2008// /regress-455973.js/1.1/Thu Dec 4 13:01:58 2008// /regress-465337.js/1.1/Thu Nov 27 11:26:45 2008// /regress-465453.js/1.1/Thu Nov 27 11:21:00 2008// /regress-469625.js/1.1/Fri Mar 6 01:38:58 2009// /regress-471197.js/1.1/Wed Jan 28 16:55:06 2009// /regress-472450-03.js/1.1/Fri Mar 6 01:38:58 2009// /regress-472450-04.js/1.1/Fri Mar 6 01:38:58 2009// /regress-473040.js/1.1/Tue Aug 18 08:50:48 2009// /regress-475971.js/1.1/Fri Aug 7 23:47:56 2009// /regress-476414-01.js/1.1/Sat Feb 21 01:30:17 2009// /regress-476414-02.js/1.1/Sat Feb 21 01:30:17 2009// /regress-476427.js/1.1/Sat Feb 21 01:30:17 2009// /regress-476653.js/1.1/Mon Feb 22 18:53:46 2010// /regress-476869.js/1.1/Sat Feb 21 01:30:17 2009// /regress-476871-01.js/1.1/Mon Feb 22 18:53:46 2010// /regress-476871-02.js/1.1/Mon Feb 22 18:53:46 2010// /regress-479252.js/1.1/Fri Mar 20 05:01:02 2009// /regress-479381.js/1.1/Fri Mar 20 05:01:02 2009// /regress-481989.js/1.1/Sat Mar 14 22:48:22 2009// /regress-482263.js/1.1/Sat Mar 14 22:48:22 2009// /shell.js/1.2/Sat May 26 00:19:44 2007// /simple-tree.js/1.1/Wed Feb 20 11:27:41 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/CVS/Root0000644000175000017500000000006314433667662024774 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-415721.js0000644000175000017500000000210114433667662026237 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-415721.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 415721; var summary = 'jsatom.c double hashing re-validation logic is unsound'; printStatus (summary); var VARS = 10000; var TRIES = 100; function atomizeStressTest() { var fn = "function f() {var "; for (var i = 0; i < VARS; i++) fn += '_f' + i + ', '; fn += 'q;}'; function e() { eval(fn); } for (var i = 0; i < TRIES; i++) { scatter([e,e]); gc(); } } var expect; var actual; expect = actual = 'No crash'; if (typeof scatter == 'undefined' || typeof gc == 'undefined') { print('Test skipped. scatter or gc not defined.'); expect = actual = 'Test skipped.'; } else { atomizeStressTest(); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476871-01.js0000644000175000017500000000137314433667662026504 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476871-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476871; var summary = 'Do not assert: *(JSObject**)slot == NULL'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); let ([] = false) { (this.watch("x", /a/g)); }; (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })(); jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-465337.js0000755000175000017500000000174614433667662026270 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465337.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465337; var summary = 'Do not assert: (m != JSVAL_INT) || isInt32(*vp)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); var out = []; for (let j = 0; j < 5; ++j) { out.push(6 - ((void 0) ^ 0x80000005)); } print(uneval(out)); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-476653.js0000644000175000017500000000157114433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476653.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476653; var summary = 'Do not crash @ QuoteString'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for each (let x1 in ['']) for (i = 0; i < 1; ++i) {} delete uneval; for (i = 0; i < 1; ++i) {} for each (let x in [new String('q'), '', /x/, '', /x/]) { for (var y = 0; y < 7; ++y) { if (y == 2 || y == 6) { setter = x; } } } try { this.(z); } catch(ex) { } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-454744.js0000755000175000017500000000201714433667662026260 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-454744.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 454744; var summary = 'Do not assert with JIT: PCVAL_IS_SPROP(entry->vword)'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { this.__defineGetter__('x', function() 2); for (var j=0;j<4;++j) { x=1; } } catch(ex) { print(ex + ''); } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-472450-03.js0000644000175000017500000000206014433667662026465 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472450-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472450; var summary = 'TM: Do not assert: StackBase(fp) + blockDepth == regs.sp'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); ({__proto__: #1=[#1#]}) for (var y = 0; y < 3; ++y) { for each (let z in ['', function(){}]) { let x = 1, c = []; } } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-452913.js0000644000175000017500000000134314433667662026252 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452913.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452913; var summary = 'Do not crash with defined getter and for (let)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); (this.__defineGetter__("x", function (x)'foo'.replace(/o/g, [1].push))); for(let y in [,,,]) for(let y in [,,,]) x = x; reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-472450-04.js0000644000175000017500000000210414433667662026465 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472450-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472450; var summary = 'TM: Do not assert: StackBase(fp) + blockDepth == regs.sp'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); ({__proto__: #1=[#1#]}); function f(){ eval("for (var y = 0; y < 1; ++y) { for each (let z in [null, function(){}, null, '', null, '', null]) { let x = 1, c = []; } }"); } f(); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/extensions/regress-422269.js0000755000175000017500000000246314433667662026262 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-422269.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 422269; var summary = 'Compile-time let block should not capture runtime references'; var actual = 'No leak'; var expect = 'No leak'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f() { let m = {sin: Math.sin}; (function() { m.sin(1); })(); return m; } if (typeof countHeap == 'undefined') { expect = actual = 'Test skipped'; print('Test skipped. Requires countHeap function.'); } else { var x = f(); gc(); var n = countHeap(); x = null; gc(); var n2 = countHeap(); if (n2 >= n) actual = "leak is detected, something roots the result of f"; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/browser.js0000755000175000017500000000043514433667662023323 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/CVS/0000755000175000017500000000000014433667662021730 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/CVS/Repository0000644000175000017500000000002714433667662024031 0ustar apoapomozilla/js/tests/js1_8 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/CVS/Entries0000644000175000017500000000025114433667662023262 0ustar apoapo/README/1.1/Wed May 2 16:43:32 2007// /browser.js/1.2/Sat May 19 20:05:08 2007// /shell.js/1.5/Wed Aug 13 12:16:59 2008// /template.js/1.3/Wed Aug 13 12:10:56 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/CVS/Root0000644000175000017500000000006314433667662022575 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/CVS/Entries.Log0000644000175000017500000000011114433667662023775 0ustar apoapoA D/decompilation//// A D/extensions//// A D/genexps//// A D/regress//// closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/template.js0000755000175000017500000000144514433667662023455 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'template.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 99999; var summary = ''; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/0000755000175000017500000000000014433667662024124 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-381372.js0000755000175000017500000000173614433667662026713 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-381372.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 381372; var summary = 'Decompilation of genexp in "with"'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = (function () { with (3 || (1 for (x in []))) {} }); expect = 'function () { with (3 || (1 for (x in []))) {} }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/shell.js0000644000175000017500000000035714433667662025576 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'decompilation'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-460504.js0000644000175000017500000000176114433667662026701 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-460504.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 460504; var summary = 'Decompilation of genexp in for-loop condition'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function () { for(; x, (1 for each (y in [])); ) { } }'; f = (function () { for(; x, (1 for each (y in [])); ) { } }); actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-382981.js0000755000175000017500000000207114433667662026713 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-382981.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 382981; var summary = 'decompilation of expcio body with delete ++x'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function () (++x, true)'; var f = (function () delete ++x); actual = f + ''; compareSource(expect, actual, summary); expect = 'function () (*, true)'; var f = (function () delete *) ; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-381963-01.js0000644000175000017500000000174514433667662027134 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-381963-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 381963; var summary = 'Decompilation of genexp in |while|'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function() { while(1, (2 for (x in []))) { break; } }'; var f = function() { while(1, (2 for (x in []))) break; }; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-469625-01.js0000644000175000017500000000175114433667662027133 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-469625-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 469625; var summary = 'decompile [a, b, [c0, c1]] = [x, x, x];'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(x) { var [a, b, [c0, c1]] = [x, x, x]; } expect = 'function f(x) { var [a, b, [c0, c1]] = [x, x, x]; }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/browser.js0000644000175000017500000000000014433667662026133 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-381963-02.js0000644000175000017500000000173314433667662027132 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-381963-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 381963; var summary = 'Decompilation of genexp in |while|'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function f() { while (false) { print(2); } }'; function f() { while (0 && (b for (x in 3))) print(2) } actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-346749.js0000644000175000017500000000171314433667662026714 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-346749.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 346749; var summary = 'let declarations at function level should turn into var declarations'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function () { var k; }'; var f = (function () { let k; }); actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-381504.js0000755000175000017500000000272314433667662026705 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-381504.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 381504; var summary = 'Decompilation of dynamic member access if some parts are local variables '; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function _1() { return foo[bar]; } expect = 'function _1() { return foo[bar]; }'; actual = _1 + ''; compareSource(expect, actual, summary); function _2() { var bar; return foo[bar]; } expect = 'function _2() { var bar; return foo[bar]; }'; actual = _2 + ''; compareSource(expect, actual, summary); function _3() { return foo[bar.baz]; } expect = 'function _3() { return foo[bar.baz]; }'; actual = _3 + ''; compareSource(expect, actual, summary); function _4() { var bar; return foo[bar.baz]; } expect = 'function _4() { var bar; return foo[bar.baz]; }'; actual = _4 + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/CVS/0000755000175000017500000000000014433667662024557 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/CVS/Repository0000644000175000017500000000004514433667662026660 0ustar apoapomozilla/js/tests/js1_8/decompilation closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/CVS/Entries0000644000175000017500000000120514433667662026111 0ustar apoapo/browser.js/1.1/Wed May 2 16:43:32 2007// /regress-260106.js/1.1/Sat Mar 29 23:14:23 2008// /regress-346749.js/1.1/Fri Mar 6 01:38:56 2009// /regress-381372.js/1.1/Sat May 26 01:40:22 2007// /regress-381504.js/1.1/Sat May 26 01:20:26 2007// /regress-381963-01.js/1.1/Fri Mar 6 01:38:57 2009// /regress-381963-02.js/1.1/Fri Mar 6 01:38:57 2009// /regress-382981.js/1.1/Fri Jul 13 17:08:03 2007// /regress-443074.js/1.1/Mon Oct 20 15:46:08 2008// /regress-460504.js/1.1/Sat Mar 14 23:13:16 2009// /regress-461233.js/1.1/Tue Nov 11 21:04:43 2008// /regress-469625-01.js/1.1/Sat Jan 24 13:07:21 2009// /shell.js/1.2/Sat May 26 00:19:43 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/CVS/Root0000644000175000017500000000006314433667662025424 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-461233.js0000755000175000017500000000164314433667662026703 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-461233.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 461233; var summary = 'Decompilation of function () [(1,2)]'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); f = (function() [(1, 2)]); expect = 'function() [(1, 2)]'; actual = f + ''; compareSource(expect, actual, expect); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-260106.js0000755000175000017500000000172514433667662026700 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-260106.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 260106; var summary = 'Elisions in array literals should not create properties'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var g = (function f(a,b,c,d)[(a,b),(c,d)]); expect = 'function f(a,b,c,d)[(a,b),(c,d)]'; actual = g + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8/decompilation/regress-443074.js0000755000175000017500000000177014433667662026707 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-443074.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 443074; var summary = 'Decompilation of genexp in for loop condition'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = function () { for(; x || (1 for each (y in [])); ) { } }; expect = 'function () { for(; x || (1 for each (y in [])); ) { } }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/0000755000175000017500000000000014433667662021515 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/0000755000175000017500000000000014433667662022613 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/math-trace-tests.js0000644000175000017500000006707114433667662026351 0ustar apoapo/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'math-trace-tests.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 'none'; var summary = 'trace-capability math mini-testsuite'; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); /** * A number of the tests in this file depend on the setting of * HOTLOOP. Define some constants up front, so they're easy to grep * for. */ // The HOTLOOP constant we depend on; only readable from our stats // object in debug builds. const haveTracemonkey = !!(this.tracemonkey) const HOTLOOP = haveTracemonkey ? tracemonkey.HOTLOOP : 2; // The loop count at which we trace const RECORDLOOP = HOTLOOP; // The loop count at which we run the trace const RUNLOOP = HOTLOOP + 1; var testName = null; if ("arguments" in this && arguments.length > 0) testName = arguments[0]; var fails = [], passes=[]; function jitstatHandler(f) { if (!haveTracemonkey) return; // XXXbz this is a nasty hack, but I can't figure out a way to // just use jitstats.tbl here f("recorderStarted"); f("recorderAborted"); f("traceCompleted"); f("sideExitIntoInterpreter"); f("typeMapMismatchAtEntry"); f("returnToDifferentLoopHeader"); f("traceTriggered"); f("globalShapeMismatchAtEntry"); f("treesTrashed"); f("slotPromoted"); f("unstableLoopVariable"); f("noCompatInnerTrees"); f("breakLoopExits"); f("returnLoopExits"); } function test(f) { if (!testName || testName == f.name) { // Collect our jit stats var localJITstats = {}; jitstatHandler(function(prop, local, global) { localJITstats[prop] = tracemonkey[prop]; }); check(f.name, f(), f.expected, localJITstats, f.jitstats); } } function map_test(t, cases) { for (var i = 0; i < cases.length; i++) { function c() { return t(cases[i].input); } c.expected = cases[i].expected; c.name = t.name + "(" + uneval(cases[i].input) + ")"; test(c); } } // Use this function to compare expected and actual test results. // Types must match. // For numbers, treat NaN as matching NaN, distinguish 0 and -0, and // tolerate a certain degree of error for other values. // // These are the same criteria used by the tests in js/tests, except that // we distinguish 0 and -0. function close_enough(expected, actual) { if (typeof expected != typeof actual) return false; if (typeof expected != 'number') return actual == expected; // Distinguish NaN from other values. Using x != x comparisons here // works even if tests redefine isNaN. if (actual != actual) return expected != expected if (expected != expected) return false; // Tolerate a certain degree of error. if (actual != expected) return Math.abs(actual - expected) <= 1E-10; // Distinguish 0 and -0. if (actual == 0) return (1 / actual > 0) == (1 / expected > 0); return true; } function check(desc, actual, expected, oldJITstats, expectedJITstats) { var pass = false; if (close_enough(expected, actual)) { pass = true; jitstatHandler(function(prop) { if (expectedJITstats && prop in expectedJITstats && expectedJITstats[prop] != tracemonkey[prop] - oldJITstats[prop]) { pass = false; } }); if (pass) { reportCompare(expected, actual, desc); passes.push(desc); return print(desc, ": passed"); } } if (expected instanceof RegExp) { pass = reportMatch(expected, actual + '', desc); if (pass) { jitstatHandler(function(prop) { if (expectedJITstats && prop in expectedJITstats && expectedJITstats[prop] != tracemonkey[prop] - oldJITstats[prop]) { pass = false; } }); } if (pass) { passes.push(desc); return print(desc, ": passed"); } } reportCompare(expected, actual, desc); fails.push(desc); var expectedStats = ""; if (expectedJITstats) { jitstatHandler(function(prop) { if (prop in expectedJITstats) { if (expectedStats) expectedStats += " "; expectedStats += prop + ": " + expectedJITstats[prop]; } }); } var actualStats = ""; if (expectedJITstats) { jitstatHandler(function(prop) { if (prop in expectedJITstats) { if (actualStats) actualStats += " "; actualStats += prop + ": " + (tracemonkey[prop]-oldJITstats[prop]); } }); } print(desc, ": FAILED: expected", typeof(expected), "(", uneval(expected), ")", (expectedStats ? " [" + expectedStats + "] " : ""), "!= actual", typeof(actual), "(", uneval(actual), ")", (actualStats ? " [" + actualStats + "] " : "")); } /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ // Apply FUNCNAME to ARGS, and check against EXPECTED. // Expect a loop containing such a call to be traced. // FUNCNAME and ARGS are both strings. // ARGS has the form of an argument list: a comma-separated list of expressions. // Certain Tracemonkey limitations require us to pass FUNCNAME as a string. // Passing ARGS as a string allows us to assign better test names: // expressions like Math.PI/4 haven't been evaluated to big hairy numbers. function testmath(funcname, args, expected) { var i, j; var arg_value_list = eval("[" + args + "]"); var arity = arg_value_list.length; // Build the string "a[i][0],...,a[i][ARITY-1]". var actuals = [] for (i = 0; i < arity; i++) actuals.push("a[i][" + i + "]"); actuals = actuals.join(","); // Create a function that maps FUNCNAME across an array of input values. // Unless we eval here, the call to funcname won't get traced. // FUNCNAME="Infinity/Math.abs" and cases like that happen to // parse, too, in a twisted way. var mapfunc = eval("(function(a) {\n" + " for (var i = 0; i < a.length; i++)\n" + " a[i] = " + funcname + "(" + actuals +");\n" + " })\n"); // To prevent the compiler from doing constant folding, produce an // array to pass to mapfunc that contains enough dummy // values at the front to get the loop body jitted, and then our // actual test value. var dummies_and_input = []; for (i = 0; i < RUNLOOP; i++) { var dummy_list = []; for (j = 0; j < arity; j++) dummy_list[j] = .0078125 * ((i + j) % 128); dummies_and_input[i] = dummy_list; } dummies_and_input[RUNLOOP] = arg_value_list; function testfunc() { // Map the function across the dummy values and the test input. mapfunc(dummies_and_input); return dummies_and_input[RUNLOOP]; } testfunc.name = funcname + "(" + args + ")"; testfunc.expected = expected; // Disable jitstats check. This never worked right. The actual part of the // loop we cared about was never traced. We traced the filler parts early // and then took a mismatch side exit on every subequent array read with // a different type (gal, discovered when fixing bug 479110). // testfunc.jitstats = { // recorderStarted: 1, // recorderAborted: 0, // traceTriggered: 1 // }; test(testfunc); } testmath("Math.abs", "void 0", Number.NaN) testmath("Math.abs", "null", 0) testmath("Math.abs", "true", 1) testmath("Math.abs", "false", 0) testmath("Math.abs", "\"a string primitive\"", Number.NaN) testmath("Math.abs", "new String( 'a String object' )", Number.NaN) testmath("Math.abs", "Number.NaN", Number.NaN) testmath("Math.abs", "0", 0) testmath("Math.abs", "-0", 0) testmath("Infinity/Math.abs", "-0", Infinity) testmath("Math.abs", "Number.NEGATIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.abs", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.abs", "- Number.MAX_VALUE", Number.MAX_VALUE) testmath("Math.abs", "-Number.MIN_VALUE", Number.MIN_VALUE) testmath("Math.abs", "Number.MAX_VALUE", Number.MAX_VALUE) testmath("Math.abs", "Number.MIN_VALUE", Number.MIN_VALUE) testmath("Math.abs", "-1", 1) testmath("Math.abs", "new Number(-1)", 1) testmath("Math.abs", "1", 1) testmath("Math.abs", "Math.PI", Math.PI) testmath("Math.abs", "-Math.PI", Math.PI) testmath("Math.abs", "-1/100000000", 1/100000000) testmath("Math.abs", "-Math.pow(2,32)", Math.pow(2,32)) testmath("Math.abs", "Math.pow(2,32)", Math.pow(2,32)) testmath("Math.abs", "-0xfff", 4095) testmath("Math.abs", "-0777", 511) testmath("Math.abs", "'-1e-1'", 0.1) testmath("Math.abs", "'0xff'", 255) testmath("Math.abs", "'077'", 77) testmath("Math.abs", "'Infinity'", Infinity) testmath("Math.abs", "'-Infinity'", Infinity) testmath("Math.acos", "void 0", Number.NaN) testmath("Math.acos", "null", Math.PI/2) testmath("Math.acos", "Number.NaN", Number.NaN) testmath("Math.acos", "\"a string\"", Number.NaN) testmath("Math.acos", "'0'", Math.PI/2) testmath("Math.acos", "'1'", 0) testmath("Math.acos", "'-1'", Math.PI) testmath("Math.acos", "1.00000001", Number.NaN) testmath("Math.acos", "-1.00000001", Number.NaN) testmath("Math.acos", "1", 0) testmath("Math.acos", "-1", Math.PI) testmath("Math.acos", "0", Math.PI/2) testmath("Math.acos", "-0", Math.PI/2) testmath("Math.acos", "Math.SQRT1_2", Math.PI/4) testmath("Math.acos", "-Math.SQRT1_2", Math.PI/4*3) testmath("Math.acos", "0.9999619230642", Math.PI/360) testmath("Math.acos", "-3.0", Number.NaN) testmath("Math.asin", "void 0", Number.NaN) testmath("Math.asin", "null", 0) testmath("Math.asin", "Number.NaN", Number.NaN) testmath("Math.asin", "\"string\"", Number.NaN) testmath("Math.asin", "\"0\"", 0) testmath("Math.asin", "\"1\"", Math.PI/2) testmath("Math.asin", "\"-1\"", -Math.PI/2) testmath("Math.asin", "Math.SQRT1_2+''", Math.PI/4) testmath("Math.asin", "-Math.SQRT1_2+''", -Math.PI/4) testmath("Math.asin", "1.000001", Number.NaN) testmath("Math.asin", "-1.000001", Number.NaN) testmath("Math.asin", "0", 0) testmath("Math.asin", "-0", -0) testmath("Infinity/Math.asin", "-0", -Infinity) testmath("Math.asin", "1", Math.PI/2) testmath("Math.asin", "-1", -Math.PI/2) testmath("Math.asin", "Math.SQRT1_2", Math.PI/4) testmath("Math.asin", "-Math.SQRT1_2", -Math.PI/4) testmath("Math.atan", "void 0", Number.NaN) testmath("Math.atan", "null", 0) testmath("Math.atan", "Number.NaN", Number.NaN) testmath("Math.atan", "\"a string\"", Number.NaN) testmath("Math.atan", "'0'", 0) testmath("Math.atan", "'1'", Math.PI/4) testmath("Math.atan", "'-1'", -Math.PI/4) testmath("Math.atan", "'Infinity'", Math.PI/2) testmath("Math.atan", "'-Infinity'", -Math.PI/2) testmath("Math.atan", "0", 0) testmath("Math.atan", "-0", -0) testmath("Infinity/Math.atan", "-0", -Infinity) testmath("Math.atan", "Number.POSITIVE_INFINITY", Math.PI/2) testmath("Math.atan", "Number.NEGATIVE_INFINITY", -Math.PI/2) testmath("Math.atan", "1", Math.PI/4) testmath("Math.atan", "-1", -Math.PI/4) testmath("Math.atan2", "Number.NaN,0", Number.NaN) testmath("Math.atan2", "null, null", 0) testmath("Math.atan2", "void 0, void 0", Number.NaN) testmath("Math.atan2", "0,Number.NaN", Number.NaN) testmath("Math.atan2", "1,0", Math.PI/2) testmath("Math.atan2", "1,-0", Math.PI/2) testmath("Math.atan2", "0,0.001", 0) testmath("Math.atan2", "0,0", 0) testmath("Math.atan2", "0,-0", Math.PI) testmath("Math.atan2", "0, -1", Math.PI) testmath("Math.atan2", "-0, 1", -0) testmath("Infinity/Math.atan2", "-0,1", -Infinity) testmath("Math.atan2", "-0,0", -0) testmath("Math.atan2", "-0, -0", -Math.PI) testmath("Math.atan2", "-0, -1", -Math.PI) testmath("Math.atan2", "-1, 0", -Math.PI/2) testmath("Math.atan2", "-1, -0", -Math.PI/2) testmath("Math.atan2", "1, Number.POSITIVE_INFINITY", 0) testmath("Math.atan2", "1, Number.NEGATIVE_INFINITY", Math.PI) testmath("Math.atan2", "-1,Number.POSITIVE_INFINITY", -0) testmath("Infinity/Math.atan2", "-1,Infinity", -Infinity) testmath("Math.atan2", "-1,Number.NEGATIVE_INFINITY", -Math.PI) testmath("Math.atan2", "Number.POSITIVE_INFINITY, 0", Math.PI/2) testmath("Math.atan2", "Number.POSITIVE_INFINITY, 1", Math.PI/2) testmath("Math.atan2", "Number.POSITIVE_INFINITY,-1", Math.PI/2) testmath("Math.atan2", "Number.POSITIVE_INFINITY,-0", Math.PI/2) testmath("Math.atan2", "Number.NEGATIVE_INFINITY, 0", -Math.PI/2) testmath("Math.atan2", "Number.NEGATIVE_INFINITY,-0", -Math.PI/2) testmath("Math.atan2", "Number.NEGATIVE_INFINITY, 1", -Math.PI/2) testmath("Math.atan2", "Number.NEGATIVE_INFINITY,-1", -Math.PI/2) testmath("Math.atan2", "Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY", Math.PI/4) testmath("Math.atan2", "Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY", 3*Math.PI/4) testmath("Math.atan2", "Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY", -Math.PI/4) testmath("Math.atan2", "Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY", -3*Math.PI/4) testmath("Math.atan2", "-1, 1", -Math.PI/4) testmath("Math.ceil", "Number.NaN", Number.NaN) testmath("Math.ceil", "null", 0) testmath("Math.ceil", "void 0", Number.NaN) testmath("Math.ceil", "'0'", 0) testmath("Math.ceil", "'-0'", -0) testmath("Infinity/Math.ceil", "'0'", Infinity) testmath("Infinity/Math.ceil", "'-0'", -Infinity) testmath("Math.ceil", "0", 0) testmath("Math.ceil", "-0", -0) testmath("Infinity/Math.ceil", "0", Infinity) testmath("Infinity/Math.ceil", "-0", -Infinity) testmath("Math.ceil", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.ceil", "Number.NEGATIVE_INFINITY", Number.NEGATIVE_INFINITY) testmath("Math.ceil", "-Number.MIN_VALUE", -0) testmath("Infinity/Math.ceil", "-Number.MIN_VALUE", -Infinity) testmath("Math.ceil", "1", 1) testmath("Math.ceil", "-1", -1) testmath("Math.ceil", "-0.9", -0) testmath("Infinity/Math.ceil", "-0.9", -Infinity) testmath("Math.ceil", "0.9", 1) testmath("Math.ceil", "-1.1", -1) testmath("Math.ceil", "1.1", 2) testmath("Math.ceil", "Number.POSITIVE_INFINITY", -Math.floor(-Infinity)) testmath("Math.ceil", "Number.NEGATIVE_INFINITY", -Math.floor(Infinity)) testmath("Math.ceil", "-Number.MIN_VALUE", -Math.floor(Number.MIN_VALUE)) testmath("Math.ceil", "1", -Math.floor(-1)) testmath("Math.ceil", "-1", -Math.floor(1)) testmath("Math.ceil", "-0.9", -Math.floor(0.9)) testmath("Math.ceil", "0.9", -Math.floor(-0.9)) testmath("Math.ceil", "-1.1", -Math.floor(1.1)) testmath("Math.ceil", "1.1", -Math.floor(-1.1)) testmath("Math.cos", "void 0", Number.NaN) testmath("Math.cos", "false", 1) testmath("Math.cos", "null", 1) testmath("Math.cos", "'0'", 1) testmath("Math.cos", "\"Infinity\"", Number.NaN) testmath("Math.cos", "'3.14159265359'", -1) testmath("Math.cos", "Number.NaN", Number.NaN) testmath("Math.cos", "0", 1) testmath("Math.cos", "-0", 1) testmath("Math.cos", "Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.cos", "Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.cos", "0.7853981633974", 0.7071067811865) testmath("Math.cos", "1.570796326795", 0) testmath("Math.cos", "2.356194490192", -0.7071067811865) testmath("Math.cos", "3.14159265359", -1) testmath("Math.cos", "3.926990816987", -0.7071067811865) testmath("Math.cos", "4.712388980385", 0) testmath("Math.cos", "5.497787143782", 0.7071067811865) testmath("Math.cos", "Math.PI*2", 1) testmath("Math.cos", "Math.PI/4", Math.SQRT2/2) testmath("Math.cos", "Math.PI/2", 0) testmath("Math.cos", "3*Math.PI/4", -Math.SQRT2/2) testmath("Math.cos", "Math.PI", -1) testmath("Math.cos", "5*Math.PI/4", -Math.SQRT2/2) testmath("Math.cos", "3*Math.PI/2", 0) testmath("Math.cos", "7*Math.PI/4", Math.SQRT2/2) testmath("Math.cos", "2*Math.PI", 1) testmath("Math.cos", "-0.7853981633974", 0.7071067811865) testmath("Math.cos", "-1.570796326795", 0) testmath("Math.cos", "2.3561944901920", -.7071067811865) testmath("Math.cos", "3.14159265359", -1) testmath("Math.cos", "3.926990816987", -0.7071067811865) testmath("Math.cos", "4.712388980385", 0) testmath("Math.cos", "5.497787143782", 0.7071067811865) testmath("Math.cos", "6.28318530718", 1) testmath("Math.cos", "-Math.PI/4", Math.SQRT2/2) testmath("Math.cos", "-Math.PI/2", 0) testmath("Math.cos", "-3*Math.PI/4", -Math.SQRT2/2) testmath("Math.cos", "-Math.PI", -1) testmath("Math.cos", "-5*Math.PI/4", -Math.SQRT2/2) testmath("Math.cos", "-3*Math.PI/2", 0) testmath("Math.cos", "-7*Math.PI/4", Math.SQRT2/2) testmath("Math.cos", "-Math.PI*2", 1) testmath("Math.exp", "null", 1) testmath("Math.exp", "void 0", Number.NaN) testmath("Math.exp", "1", Math.E) testmath("Math.exp", "true", Math.E) testmath("Math.exp", "false", 1) testmath("Math.exp", "'1'", Math.E) testmath("Math.exp", "'0'", 1) testmath("Math.exp", "Number.NaN", Number.NaN) testmath("Math.exp", "0", 1) testmath("Math.exp", "-0", 1) testmath("Math.exp", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.exp", "Number.NEGATIVE_INFINITY", 0) testmath("Math.floor", "void 0", Number.NaN) testmath("Math.floor", "null", 0) testmath("Math.floor", "true", 1) testmath("Math.floor", "false", 0) testmath("Math.floor", "\"1.1\"", 1) testmath("Math.floor", "\"-1.1\"", -2) testmath("Math.floor", "\"0.1\"", 0) testmath("Math.floor", "\"-0.1\"", -1) testmath("Math.floor", "Number.NaN", Number.NaN) testmath("Math.floor(Number.NaN) == -Math.ceil", "-Number.NaN", false) testmath("Math.floor", "0", 0) testmath("Math.floor(0) == -Math.ceil", "-0", true) testmath("Math.floor", "-0", -0) testmath("Infinity/Math.floor", "-0", -Infinity) testmath("Math.floor(-0)== -Math.ceil", "0", true) testmath("Math.floor", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.floor(Number.POSITIVE_INFINITY) == -Math.ceil", "Number.NEGATIVE_INFINITY", true) testmath("Math.floor", "Number.NEGATIVE_INFINITY", Number.NEGATIVE_INFINITY) testmath("Math.floor(Number.NEGATIVE_INFINITY) == -Math.ceil", "Number.POSITIVE_INFINITY", true) testmath("Math.floor", "0.0000001", 0) testmath("Math.floor(0.0000001)==-Math.ceil", "-0.0000001", true) testmath("Math.floor", "-0.0000001", -1) testmath("Math.floor(-0.0000001)==-Math.ceil", "0.0000001", true) testmath("Math.log", "void 0", Number.NaN) testmath("Math.log", "null", Number.NEGATIVE_INFINITY) testmath("Math.log", "true", 0) testmath("Math.log", "false", -Infinity) testmath("Math.log", "'0'", -Infinity) testmath("Math.log", "'1'", 0) testmath("Math.log", "\"Infinity\"", Infinity) testmath("Math.log", "Number.NaN", Number.NaN) testmath("Math.log", "-0.000001", Number.NaN) testmath("Math.log", "-1", Number.NaN) testmath("Math.log", "0", Number.NEGATIVE_INFINITY) testmath("Math.log", "-0", Number.NEGATIVE_INFINITY) testmath("Math.log", "1", 0) testmath("Math.log", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.log", "Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.max", "void 0, 1", Number.NaN) testmath("Math.max", "void 0, void 0", Number.NaN) testmath("Math.max", "null, 1", 1) testmath("Math.max", "-1, null", 0) testmath("Math.max", "true,false", 1) testmath("Math.max", "\"-99\",\"99\"", 99) testmath("Math.max", "Number.NaN,Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.max", "Number.NaN, 0", Number.NaN) testmath("Math.max", "\"a string\", 0", Number.NaN) testmath("Math.max", "Number.NaN,1", Number.NaN) testmath("Math.max", "\"a string\", Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.max", "Number.POSITIVE_INFINITY, Number.NaN", Number.NaN) testmath("Math.max", "Number.NaN, Number.NaN", Number.NaN) testmath("Math.max", "0,Number.NaN", Number.NaN) testmath("Math.max", "1, Number.NaN", Number.NaN) testmath("Math.max", "0,0", 0) testmath("Math.max", "0,-0", 0) testmath("Math.max", "-0,0", 0) testmath("Math.max", "-0,-0", -0) testmath("Infinity/Math.max", "-0,-0", -Infinity) testmath("Math.max", "Number.POSITIVE_INFINITY, Number.MAX_VALUE", Number.POSITIVE_INFINITY) testmath("Math.max", "Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.max", "Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY", Number.NEGATIVE_INFINITY) testmath("Math.max", "1,.99999999999999", 1) testmath("Math.max", "-1,-.99999999999999", -.99999999999999) testmath("Math.min", "void 0, 1", Number.NaN) testmath("Math.min", "void 0, void 0", Number.NaN) testmath("Math.min", "null, 1", 0) testmath("Math.min", "-1, null", -1) testmath("Math.min", "true,false", 0) testmath("Math.min", "\"-99\",\"99\"", -99) testmath("Math.min", "Number.NaN,0", Number.NaN) testmath("Math.min", "Number.NaN,1", Number.NaN) testmath("Math.min", "Number.NaN,-1", Number.NaN) testmath("Math.min", "0,Number.NaN", Number.NaN) testmath("Math.min", "1,Number.NaN", Number.NaN) testmath("Math.min", "-1,Number.NaN", Number.NaN) testmath("Math.min", "Number.NaN,Number.NaN", Number.NaN) testmath("Math.min", "1,1.0000000001", 1) testmath("Math.min", "1.0000000001,1", 1) testmath("Math.min", "0,0", 0) testmath("Math.min", "0,-0", -0) testmath("Math.min", "-0,-0", -0) testmath("Infinity/Math.min", "0,-0", -Infinity) testmath("Infinity/Math.min", "-0,-0", -Infinity) testmath("Math.pow", "null,null", 1) testmath("Math.pow", "void 0, void 0", Number.NaN) testmath("Math.pow", "true, false", 1) testmath("Math.pow", "false,true", 0) testmath("Math.pow", "'2','32'", 4294967296) testmath("Math.pow", "1,Number.NaN", Number.NaN) testmath("Math.pow", "0,Number.NaN", Number.NaN) testmath("Math.pow", "Number.NaN,0", 1) testmath("Math.pow", "Number.NaN,-0", 1) testmath("Math.pow", "Number.NaN, 1", Number.NaN) testmath("Math.pow", "Number.NaN, .5", Number.NaN) testmath("Math.pow", "1.00000001, Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.pow", "1.00000001, Number.NEGATIVE_INFINITY", 0) testmath("Math.pow", "-1.00000001,Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.pow", "-1.00000001,Number.NEGATIVE_INFINITY", 0) testmath("Math.pow", "1, Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.pow", "1, Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.pow", "-1, Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.pow", "-1, Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.pow", ".0000000009, Number.POSITIVE_INFINITY", 0) testmath("Math.pow", "-.0000000009, Number.POSITIVE_INFINITY", 0) testmath("Math.pow", "-.0000000009, Number.NEGATIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.POSITIVE_INFINITY,.00000000001", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.POSITIVE_INFINITY, 1", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.POSITIVE_INFINITY, -.00000000001", 0) testmath("Math.pow", "Number.POSITIVE_INFINITY, -1", 0) testmath("Math.pow", "Number.NEGATIVE_INFINITY, 1", Number.NEGATIVE_INFINITY) testmath("Math.pow", "Number.NEGATIVE_INFINITY, 333", Number.NEGATIVE_INFINITY) testmath("Math.pow", "Number.POSITIVE_INFINITY, 2", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.NEGATIVE_INFINITY, 666", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.NEGATIVE_INFINITY, 0.5", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.pow", "Number.NEGATIVE_INFINITY, -1", -0) testmath("Infinity/Math.pow", "Number.NEGATIVE_INFINITY, -1", -Infinity) testmath("Math.pow", "Number.NEGATIVE_INFINITY, -3", -0) testmath("Math.pow", "Number.NEGATIVE_INFINITY, -2", 0) testmath("Math.pow", "Number.NEGATIVE_INFINITY,-0.5", 0) testmath("Math.pow", "Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY", 0) testmath("Math.pow", "0,1", 0) testmath("Math.pow", "0,0", 1) testmath("Math.pow", "1,0", 1) testmath("Math.pow", "-1,0", 1) testmath("Math.pow", "0,0.5", 0) testmath("Math.pow", "0,1000", 0) testmath("Math.pow", "0, Number.POSITIVE_INFINITY", 0) testmath("Math.pow", "0, -1", Number.POSITIVE_INFINITY) testmath("Math.pow", "0, -0.5", Number.POSITIVE_INFINITY) testmath("Math.pow", "0, -1000", Number.POSITIVE_INFINITY) testmath("Math.pow", "0, Number.NEGATIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.pow", "-0, 1", -0) testmath("Math.pow", "-0,3", -0) testmath("Infinity/Math.pow", "-0, 1", -Infinity) testmath("Infinity/Math.pow", "-0,3", -Infinity) testmath("Math.pow", "-0,2", 0) testmath("Math.pow", "-0, Number.POSITIVE_INFINITY", 0) testmath("Math.pow", "-0, -1", Number.NEGATIVE_INFINITY) testmath("Math.pow", "-0, -10001", Number.NEGATIVE_INFINITY) testmath("Math.pow", "-0, -2", Number.POSITIVE_INFINITY) testmath("Math.pow", "-0, 0.5", 0) testmath("Math.pow", "-0, Number.POSITIVE_INFINITY", 0) testmath("Math.pow", "-1, 0.5", Number.NaN) testmath("Math.pow", "-1, Number.NaN", Number.NaN) testmath("Math.pow", "-1, -0.5", Number.NaN) testmath("Math.round", "0", 0) testmath("Math.round", "void 0", Number.NaN) testmath("Math.round", "true", 1) testmath("Math.round", "false", 0) testmath("Math.round", "'.99999'", 1) testmath("Math.round", "'12345e-2'", 123) testmath("Math.round", "Number.NaN", Number.NaN) testmath("Math.round", "0", 0) testmath("Math.round", "-0", -0) testmath("Infinity/Math.round", "-0", -Infinity) testmath("Math.round", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.round", "Number.NEGATIVE_INFINITY", Number.NEGATIVE_INFINITY) testmath("Math.round", "0.49", 0) testmath("Math.round", "0.5", 1) testmath("Math.round", "0.51", 1) testmath("Math.round", "-0.49", -0) testmath("Math.round", "-0.5", -0) testmath("Infinity/Math.round", "-0.49", -Infinity) testmath("Infinity/Math.round", "-0.5", -Infinity) testmath("Math.round", "-0.51", -1) testmath("Math.round", "3.5", 4) testmath("Math.round", "-3", -3) testmath("Math.sin", "null", 0) testmath("Math.sin", "void 0", Number.NaN) testmath("Math.sin", "false", 0) testmath("Math.sin", "'2.356194490192'", 0.7071067811865) testmath("Math.sin", "Number.NaN", Number.NaN) testmath("Math.sin", "0", 0) testmath("Math.sin", "-0", -0) testmath("Math.sin", "Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.sin", "Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.sin", "0.7853981633974", 0.7071067811865) testmath("Math.sin", "1.570796326795", 1) testmath("Math.sin", "2.356194490192", 0.7071067811865) testmath("Math.sin", "3.14159265359", 0) testmath("Math.sqrt", "void 0", Number.NaN) testmath("Math.sqrt", "null", 0) testmath("Math.sqrt", "1", 1) testmath("Math.sqrt", "false", 0) testmath("Math.sqrt", "'225'", 15) testmath("Math.sqrt", "Number.NaN", Number.NaN) testmath("Math.sqrt", "Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.sqrt", "-1", Number.NaN) testmath("Math.sqrt", "-0.5", Number.NaN) testmath("Math.sqrt", "0", 0) testmath("Math.sqrt", "-0", -0) testmath("Infinity/Math.sqrt", "-0", -Infinity) testmath("Math.sqrt", "Number.POSITIVE_INFINITY", Number.POSITIVE_INFINITY) testmath("Math.sqrt", "1", 1) testmath("Math.sqrt", "2", Math.SQRT2) testmath("Math.sqrt", "0.5", Math.SQRT1_2) testmath("Math.sqrt", "4", 2) testmath("Math.sqrt", "9", 3) testmath("Math.sqrt", "16", 4) testmath("Math.sqrt", "25", 5) testmath("Math.sqrt", "36", 6) testmath("Math.sqrt", "49", 7) testmath("Math.sqrt", "64", 8) testmath("Math.sqrt", "256", 16) testmath("Math.sqrt", "10000", 100) testmath("Math.sqrt", "65536", 256) testmath("Math.sqrt", "0.09", 0.3) testmath("Math.sqrt", "0.01", 0.1) testmath("Math.sqrt", "0.00000001", 0.0001) testmath("Math.tan", "void 0", Number.NaN) testmath("Math.tan", "null", 0) testmath("Math.tan", "false", 0) testmath("Math.tan", "Number.NaN", Number.NaN) testmath("Math.tan", "0", 0) testmath("Math.tan", "-0", -0) testmath("Math.tan", "Number.POSITIVE_INFINITY", Number.NaN) testmath("Math.tan", "Number.NEGATIVE_INFINITY", Number.NaN) testmath("Math.tan", "Math.PI/4", 1) testmath("Math.tan", "3*Math.PI/4", -1) testmath("Math.tan", "Math.PI", -0) testmath("Math.tan", "5*Math.PI/4", 1) testmath("Math.tan", "7*Math.PI/4", -1) testmath("Infinity/Math.tan", "-0", -Infinity) jit(false); /* Keep these at the end so that we can see the summary after the trace-debug spew. */ if (0) { print("\npassed:", passes.length && passes.join(",")); print("\nFAILED:", fails.length && fails.join(",")); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/shell.js0000644000175000017500000000034514433667662024262 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='trace'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-09.js0000644000175000017500000000331114433667662025622 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-09.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace []'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { []; } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-01.js0000644000175000017500000000332314433667662025615 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace Array()'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { Array(); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-07.js0000644000175000017500000000334314433667662025625 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-07.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace new Array(1, 2)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { new Array(1, 2); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-03.js0000644000175000017500000000333314433667662025620 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace Array(1, 2)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { Array(1, 2); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/trace-test.js0000644000175000017500000041155414433667662025236 0ustar apoapo/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'trace-test.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 'none'; var summary = 'trace-capability mini-testsuite'; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); /** * A number of the tests in this file depend on the setting of * HOTLOOP. Define some constants up front, so they're easy to grep * for. */ // The HOTLOOP constant we depend on; only readable from our stats // object in debug builds. const haveTracemonkey = !!(this.tracemonkey) const HOTLOOP = haveTracemonkey ? tracemonkey.HOTLOOP : 2; // The loop count at which we trace const RECORDLOOP = HOTLOOP; // The loop count at which we run the trace const RUNLOOP = HOTLOOP + 1; var gDoMandelbrotTest = true; if ("gSkipSlowTests" in this && gSkipSlowTests) { print("** Skipping slow tests"); gDoMandelbrotTest = false; } if (!('gSrcdir' in this)) gSrcdir = '.'; if (!('gReportSummary' in this)) gReportSummary = true; var testName = null; if ("arguments" in this && arguments.length > 0) testName = arguments[0]; var fails = [], passes=[]; function jitstatHandler(f) { if (!haveTracemonkey) return; // XXXbz this is a nasty hack, but I can't figure out a way to // just use jitstats.tbl here f("recorderStarted"); f("recorderAborted"); f("traceCompleted"); f("sideExitIntoInterpreter"); f("timeoutIntoInterpreter"); f("typeMapMismatchAtEntry"); f("returnToDifferentLoopHeader"); f("traceTriggered"); f("globalShapeMismatchAtEntry"); f("treesTrashed"); f("slotPromoted"); f("unstableLoopVariable"); f("breakLoopExits"); f("returnLoopExits"); f("mergedLoopExits"); f("noCompatInnerTrees"); } var jitProps = {}; jitstatHandler(function(prop) { jitProps[prop] = true; }); var hadJITstats = false; for (var p in jitProps) hadJITstats = true; function test(f) { // Clear out any accumulated confounding state in the oracle / JIT cache. gc(); if (!testName || testName == f.name) { var expectedJITstats = f.jitstats; if (hadJITstats && expectedJITstats) { var expectedProps = {}; jitstatHandler(function(prop) { if (prop in expectedJITstats) expectedProps[prop] = true; }); for (var p in expectedJITstats) { if (!(p in expectedProps)) throw "Bad property in " + f.name + ".jitstats: " + p; } } // Collect our jit stats var localJITstats = {}; jitstatHandler(function(prop) { localJITstats[prop] = tracemonkey[prop]; }); check(f.name, f(), f.expected, localJITstats, expectedJITstats); } } function map_test(t, cases) { for (var i = 0; i < cases.length; i++) { function c() { return t(cases[i].input); } c.expected = cases[i].expected; c.name = t.name + "(" + uneval(cases[i].input) + ")"; test(c); } } // Use this function to compare expected and actual test results. // Types must match. // For numbers, treat NaN as matching NaN, distinguish 0 and -0, and // tolerate a certain degree of error for other values. // // These are the same criteria used by the tests in js/tests, except that // we distinguish 0 and -0. function close_enough(expected, actual) { if (typeof expected != typeof actual) return false; if (typeof expected != 'number') return actual == expected; // Distinguish NaN from other values. Using x != x comparisons here // works even if tests redefine isNaN. if (actual != actual) return expected != expected if (expected != expected) return false; // Tolerate a certain degree of error. if (actual != expected) return Math.abs(actual - expected) <= 1E-10; // Distinguish 0 and -0. if (actual == 0) return (1 / actual > 0) == (1 / expected > 0); return true; } function check(desc, actual, expected, oldJITstats, expectedJITstats) { var pass = false; if (close_enough(expected, actual)) { pass = true; jitstatHandler(function(prop) { if (expectedJITstats && prop in expectedJITstats && expectedJITstats[prop] != tracemonkey[prop] - oldJITstats[prop]) { pass = false; } }); if (pass) { reportCompare(expected, actual, desc); passes.push(desc); return print("TEST-PASS | trace-test.js |", desc); } } if (expected instanceof RegExp) { pass = reportMatch(expected, actual + '', desc); if (pass) { jitstatHandler(function(prop) { if (expectedJITstats && prop in expectedJITstats && expectedJITstats[prop] != tracemonkey[prop] - oldJITstats[prop]) { pass = false; } }); } if (pass) { passes.push(desc); return print(desc, ": passed"); } } reportCompare(expected, actual, desc); fails.push(desc); var expectedStats = ""; if (expectedJITstats) { jitstatHandler(function(prop) { if (prop in expectedJITstats) { if (expectedStats) expectedStats += " "; expectedStats += prop + ": " + expectedJITstats[prop]; } }); } var actualStats = ""; if (expectedJITstats) { jitstatHandler(function(prop) { if (prop in expectedJITstats) { if (actualStats) actualStats += " "; actualStats += prop + ": " + (tracemonkey[prop]-oldJITstats[prop]); } }); } print("TEST-UNEXPECTED-FAIL | trace-test.js |", desc, ": expected", typeof(expected), "(", uneval(expected), ")", (expectedStats ? " [" + expectedStats + "] " : ""), "!= actual", typeof(actual), "(", uneval(actual), ")", (actualStats ? " [" + actualStats + "] " : "")); } function ifInsideLoop() { var cond = true, intCond = 5, count = 0; for (var i = 0; i < 100; i++) { if (cond) count++; if (intCond) count++; } return count; } ifInsideLoop.expected = 200; test(ifInsideLoop); function bitwiseAnd_inner(bitwiseAndValue) { for (var i = 0; i < 60000; i++) bitwiseAndValue = bitwiseAndValue & i; return bitwiseAndValue; } function bitwiseAnd() { return bitwiseAnd_inner(12341234); } bitwiseAnd.expected = 0; test(bitwiseAnd); if (!testName || testName == "bitwiseGlobal") { bitwiseAndValue = Math.pow(2,32); for (var i = 0; i < 60000; i++) bitwiseAndValue = bitwiseAndValue & i; check("bitwiseGlobal", bitwiseAndValue, 0); } function equalInt() { var i1 = 55, one = 1, zero = 0, undef; var o1 = { }, o2 = { }; var s = "5"; var hits = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; for (var i = 0; i < 5000; i++) { if (i1 == 55) hits[0]++; if (i1 != 56) hits[1]++; if (i1 < 56) hits[2]++; if (i1 > 50) hits[3]++; if (i1 <= 60) hits[4]++; if (i1 >= 30) hits[5]++; if (i1 == 7) hits[6]++; if (i1 != 55) hits[7]++; if (i1 < 30) hits[8]++; if (i1 > 90) hits[9]++; if (i1 <= 40) hits[10]++; if (i1 >= 70) hits[11]++; if (o1 == o2) hits[12]++; if (o2 != null) hits[13]++; if (s < 10) hits[14]++; if (true < zero) hits[15]++; if (undef > one) hits[16]++; if (undef < zero) hits[17]++; } return hits.toString(); } equalInt.expected = "5000,5000,5000,5000,5000,5000,0,0,0,0,0,0,0,5000,5000,0,0,0"; test(equalInt); var a; function setelem() { a = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; a = a.concat(a, a, a); var l = a.length; for (var i = 0; i < l; i++) { a[i] = i; } return a.toString(); } setelem.expected = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83"; test(setelem); function getelem_inner(a) { var accum = 0; var l = a.length; for (var i = 0; i < l; i++) { accum += a[i]; } return accum; } function getelem() { return getelem_inner(a); } getelem.expected = 3486; test(getelem); globalName = 907; function name() { var a = 0; for (var i = 0; i < 100; i++) a = globalName; return a; } name.expected = 907; test(name); var globalInt = 0; if (!testName || testName == "globalGet") { for (var i = 0; i < 500; i++) globalInt = globalName + i; check("globalGet", globalInt, globalName + 499); } if (!testName || testName == "globalSet") { for (var i = 0; i < 500; i++) globalInt = i; check("globalSet", globalInt, 499); } function arith() { var accum = 0; for (var i = 0; i < 100; i++) { accum += (i * 2) - 1; } return accum; } arith.expected = 9800; test(arith); function lsh_inner(n) { var r; for (var i = 0; i < 35; i++) r = 0x1 << n; return r; } map_test (lsh_inner, [{input: 15, expected: 32768}, {input: 55, expected: 8388608}, {input: 1, expected: 2}, {input: 0, expected: 1}]); function rsh_inner(n) { var r; for (var i = 0; i < 35; i++) r = 0x11010101 >> n; return r; } map_test (rsh_inner, [{input: 8, expected: 1114369}, {input: 5, expected: 8914952}, {input: 35, expected: 35659808}, {input: -1, expected: 0}]); function ursh_inner(n) { var r; for (var i = 0; i < 35; i++) r = -55 >>> n; return r; } map_test (ursh_inner, [{input: 8, expected: 16777215}, {input: 33, expected: 2147483620}, {input: 0, expected: 4294967241}, {input: 1, expected: 2147483620}]); function doMath_inner(cos) { var s = 0; var sin = Math.sin; for (var i = 0; i < 200; i++) s = -Math.pow(sin(i) + cos(i * 0.75), 4); return s; } function doMath() { return doMath_inner(Math.cos); } doMath.expected = -0.5405549555611059; test(doMath); function fannkuch() { var count = Array(8); var r = 8; var done = 0; while (done < 40) { // write-out the first 30 permutations done += r; while (r != 1) { count[r - 1] = r; r--; } while (true) { count[r] = count[r] - 1; if (count[r] > 0) break; r++; } } return done; } fannkuch.expected = 41; test(fannkuch); function xprop() { a = 0; for (var i = 0; i < 20; i++) a += 7; return a; } xprop.expected = 140; test(xprop); var a = 2; function getprop_inner(o2) { var o = {a:5}; var t = this; var x = 0; for (var i = 0; i < 20; i++) { t = this; x += o.a + o2.a + this.a + t.a; } return x; } function getprop() { return getprop_inner({a:9}); } getprop.expected = 360; test(getprop); function mod() { var mods = [-1,-1,-1,-1]; var a = 9.5, b = -5, c = 42, d = (1/0); for (var i = 0; i < 20; i++) { mods[0] = a % b; mods[1] = b % 1; mods[2] = c % d; mods[3] = c % a; mods[4] = b % 0; } return mods.toString(); } mod.expected = "4.5,0,42,4,NaN"; test(mod); function glob_f1() { return 1; } function glob_f2() { return glob_f1(); } function call() { var q1 = 0, q2 = 0, q3 = 0, q4 = 0, q5 = 0; var o = {}; function f1() { return 1; } function f2(f) { return f(); } o.f = f1; for (var i = 0; i < 100; ++i) { q1 += f1(); q2 += f2(f1); q3 += glob_f1(); q4 += o.f(); q5 += glob_f2(); } var ret = String([q1, q2, q3, q4, q5]); return ret; } call.expected = "100,100,100,100,100"; test(call); function setprop() { var obj = { a:-1 }; var obj2 = { b:-1, a:-1 }; for (var i = 0; i < 20; i++) { obj2.b = obj.a = i; } return [obj.a, obj2.a, obj2.b].toString(); } setprop.expected = "19,-1,19"; test(setprop); function testif() { var q = 0; for (var i = 0; i < 100; i++) { if ((i & 1) == 0) q++; else q--; } return q; } testif.expected = 0; test(testif); var globalinc = 0; function testincops(n) { var i = 0, o = {p:0}, a = [0]; n = 100; for (i = 0; i < n; i++); while (i-- > 0); for (i = 0; i < n; ++i); while (--i >= 0); for (o.p = 0; o.p < n; o.p++) globalinc++; while (o.p-- > 0) --globalinc; for (o.p = 0; o.p < n; ++o.p) ++globalinc; while (--o.p >= 0) globalinc--; ++i; // set to 0 for (a[i] = 0; a[i] < n; a[i]++); while (a[i]-- > 0); for (a[i] = 0; a[i] < n; ++a[i]); while (--a[i] >= 0); return [++o.p, ++a[i], globalinc].toString(); } testincops.expected = "0,0,0"; test(testincops); function trees() { var i = 0, o = [0,0,0]; for (i = 0; i < 100; ++i) { if ((i & 1) == 0) o[0]++; else if ((i & 2) == 0) o[1]++; else o[2]++; } return String(o); } trees.expected = "50,25,25"; test(trees); function unboxint() { var q = 0; var o = [4]; for (var i = 0; i < 100; ++i) q = o[0] << 1; return q; } unboxint.expected = 8; test(unboxint); function strings() { var a = [], b = -1; var s = "abcdefghij", s2 = "a"; var f = "f"; var c = 0, d = 0, e = 0, g = 0; for (var i = 0; i < 10; i++) { a[i] = (s.substring(i, i+1) + s[i] + String.fromCharCode(s2.charCodeAt(0) + i)).concat(i) + i; if (s[i] == f) c++; if (s[i] != 'b') d++; if ("B" > s2) g++; // f already used if (s2 < "b") e++; b = s.length; } return a.toString() + b + c + d + e + g; } strings.expected = "aaa00,bbb11,ccc22,ddd33,eee44,fff55,ggg66,hhh77,iii88,jjj991019100"; test(strings); function dependentStrings() { var a = []; var t = "abcdefghijklmnopqrst"; for (var i = 0; i < 10; i++) { var s = t.substring(2*i, 2*i + 2); a[i] = s + s.length; } return a.join(""); } dependentStrings.expected = "ab2cd2ef2gh2ij2kl2mn2op2qr2st2"; test(dependentStrings); function stringConvert() { var a = []; var s1 = "F", s2 = "1.3", s3 = "5"; for (var i = 0; i < 10; i++) { a[0] = 1 >> s1; a[1] = 10 - s2; a[2] = 15 * s3; a[3] = s3 | 32; a[4] = s2 + 60; // a[5] = 9 + s3; // a[6] = -s3; a[7] = s3 & "7"; // a[8] = ~s3; } return a.toString(); } stringConvert.expected = "1,8.7,75,37,1.360,,,5"; test(stringConvert); function orTestHelper(a, b, n) { var k = 0; for (var i = 0; i < n; i++) { if (a || b) k += i; } return k; } var orNaNTest1, orNaNTest2; orNaNTest1 = new Function("return orTestHelper(NaN, NaN, 10);"); orNaNTest1.name = 'orNaNTest1'; orNaNTest1.expected = 0; orNaNTest2 = new Function("return orTestHelper(NaN, 1, 10);"); orNaNTest2.name = 'orNaNTest2'; orNaNTest2.expected = 45; test(orNaNTest1); test(orNaNTest2); function andTestHelper(a, b, n) { var k = 0; for (var i = 0; i < n; i++) { if (a && b) k += i; } return k; } if (!testName || testName == "truthies") { (function () { var opsies = ["||", "&&"]; var falsies = [null, undefined, false, NaN, 0, ""]; var truthies = [{}, true, 1, 42, 1/0, -1/0, "blah"]; var boolies = [falsies, truthies]; // The for each here should abort tracing, so that this test framework // relies only on the interpreter while the orTestHelper and andTestHelper // functions get trace-JITed. for each (var op in opsies) { for (var i in boolies) { for (var j in boolies[i]) { var x = uneval(boolies[i][j]); for (var k in boolies) { for (var l in boolies[k]) { var y = uneval(boolies[k][l]); var prefix = (op == "||") ? "or" : "and"; var f = new Function("return " + prefix + "TestHelper(" + x + "," + y + ",10)"); f.name = prefix + "Test(" + x + "," + y + ")"; f.expected = eval(x + op + y) ? 45 : 0; test(f); } } } } } })(); } function nonEmptyStack1Helper(o, farble) { var a = []; var j = 0; for (var i in o) a[j++] = i; return a.join(""); } function nonEmptyStack1() { return nonEmptyStack1Helper({a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8}, "hi"); } nonEmptyStack1.expected = "abcdefgh"; test(nonEmptyStack1); function nonEmptyStack2() { var a = 0; for (var c in {a:1, b:2, c:3}) { for (var i = 0; i < 10; i++) a += i; } return String(a); } nonEmptyStack2.expected = "135"; test(nonEmptyStack2); function arityMismatchMissingArg(arg) { for (var a = 0, i = 1; i < 10000; i *= 2) { a += i; } return a; } arityMismatchMissingArg.expected = 16383; test(arityMismatchMissingArg); function arityMismatchExtraArg() { return arityMismatchMissingArg(1, 2); } arityMismatchExtraArg.expected = 16383; test(arityMismatchExtraArg); function MyConstructor(i) { this.i = i; } MyConstructor.prototype.toString = function() {return this.i + ""}; function newTest() { var a = []; for (var i = 0; i < 10; i++) a[i] = new MyConstructor(i); return a.join(""); } newTest.expected = "0123456789"; test(newTest); // The following functions use a delay line of length 2 to change the value // of the callee without exiting the traced loop. This is obviously tuned to // match the current HOTLOOP setting of 2. function shapelessArgCalleeLoop(f, g, h, a) { for (var i = 0; i < 10; i++) { f(i, a); f = g; g = h; } } function shapelessVarCalleeLoop(f0, g, h, a) { var f = f0; for (var i = 0; i < 10; i++) { f(i, a); f = g; g = h; } } function shapelessLetCalleeLoop(f0, g, h, a) { for (var i = 0; i < 10; i++) { let f = f0; f(i, a); f = g; g = h; } } function shapelessUnknownCalleeLoop(n, f, g, h, a) { for (var i = 0; i < 10; i++) { (n || f)(i, a); f = g; g = h; } } function shapelessCalleeTest() { var a = []; var helper = function (i, a) a[i] = i; shapelessArgCalleeLoop(helper, helper, function (i, a) a[i] = -i, a); helper = function (i, a) a[10 + i] = i; shapelessVarCalleeLoop(helper, helper, function (i, a) a[10 + i] = -i, a); helper = function (i, a) a[20 + i] = i; shapelessLetCalleeLoop(helper, helper, function (i, a) a[20 + i] = -i, a); helper = function (i, a) a[30 + i] = i; shapelessUnknownCalleeLoop(null, helper, helper, function (i, a) a[30 + i] = -i, a); try { helper = {hack: 42}; shapelessUnknownCalleeLoop(null, helper, helper, helper, a); } catch (e) { if (e + "" != "TypeError: f is not a function") print("shapelessUnknownCalleeLoop: unexpected exception " + e); } return a.join(""); } shapelessCalleeTest.expected = "01-2-3-4-5-6-7-8-901-2-3-4-5-6-7-8-9012345678901-2-3-4-5-6-7-8-9"; test(shapelessCalleeTest); function typeofTest() { var values = ["hi", "hi", "hi", null, 5, 5.1, true, undefined, /foo/, typeofTest, [], {}], types = []; for (var i = 0; i < values.length; i++) types[i] = typeof values[i]; return types.toString(); } typeofTest.expected = "string,string,string,object,number,number,boolean,undefined,object,function,object,object"; test(typeofTest); function joinTest() { var s = ""; var a = []; for (var i = 0; i < 8; i++) a[i] = [String.fromCharCode(97 + i)]; for (i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) a[i][1 + j] = j; } for (i = 0; i < 8; i++) s += a[i].join(","); return s; } joinTest.expected = "a,0,1,2,3,4,5,6,7b,0,1,2,3,4,5,6,7c,0,1,2,3,4,5,6,7d,0,1,2,3,4,5,6,7e,0,1,2,3,4,5,6,7f,0,1,2,3,4,5,6,7g,0,1,2,3,4,5,6,7h,0,1,2,3,4,5,6,7"; test(joinTest); function arity1(x) { return (x == undefined) ? 1 : 0; } function missingArgTest() { var q; for (var i = 0; i < 10; i++) { q = arity1(); } return q; } missingArgTest.expected = 1; test(missingArgTest); JSON = function () { return { stringify: function stringify(value, whitelist) { switch (typeof(value)) { case "object": return value.constructor.name; } } }; }(); function missingArgTest2() { var testPairs = [ ["{}", {}], ["[]", []], ['{"foo":"bar"}', {"foo":"bar"}], ] var a = []; for (var i=0; i < testPairs.length; i++) { var s = JSON.stringify(testPairs[i][1]) a[i] = s; } return a.join(","); } missingArgTest2.expected = /(Object,Array,Object|{},\[\],{"foo":"bar"})/; test(missingArgTest2); function deepForInLoop() { // NB: the number of props set in C is arefully tuned to match HOTLOOP = 2. function C(){this.p = 1, this.q = 2} C.prototype = {p:1, q:2, r:3, s:4, t:5}; var o = new C; var j = 0; var a = []; for (var i in o) a[j++] = i; return a.join(""); } deepForInLoop.expected = "pqrst"; test(deepForInLoop); function nestedExit(x) { var q = 0; for (var i = 0; i < 10; ++i) { if (x) ++q; } } function nestedExitLoop() { for (var j = 0; j < 10; ++j) nestedExit(j < 7); return "ok"; } nestedExitLoop.expected = "ok"; test(nestedExitLoop); function bitsinbyte(b) { var m = 1, c = 0; while(m<0x100) { if(b & m) c++; m <<= 1; } return 1; } function TimeFunc(func) { var x,y; for(var y=0; y<256; y++) func(y); } function nestedExit2() { TimeFunc(bitsinbyte); return "ok"; } nestedExit2.expected = "ok"; test(nestedExit2); function parsingNumbers() { var s1 = "123"; var s1z = "123zzz"; var s2 = "123.456"; var s2z = "123.456zzz"; var e1 = 123; var e2 = 123.456; var r1, r1z, r2, r2z; for (var i = 0; i < 10; i++) { r1 = parseInt(s1); r1z = parseInt(s1z); r2 = parseFloat(s2); r2z = parseFloat(s2z); } if (r1 == e1 && r1z == e1 && r2 == e2 && r2z == e2) return "ok"; return "fail"; } parsingNumbers.expected = "ok"; test(parsingNumbers); function matchInLoop() { var k = "hi"; for (var i = 0; i < 10; i++) { var result = k.match(/hi/) != null; } return result; } matchInLoop.expected = true; test(matchInLoop); function testMatchAsCondition() { var a = ['0', '0', '0', '0']; var r = /0/; "x".q; for (var z = 0; z < 4; z++) a[z].match(r) ? 1 : 2; } test(testMatchAsCondition); function deep1(x) { if (x > 90) return 1; return 2; } function deep2() { for (var i = 0; i < 100; ++i) deep1(i); return "ok"; } deep2.expected = "ok"; test(deep2); function heavyFn1(i) { if (i == 3) { var x = 3; return [0, i].map(function (i) i + x); } return []; } function testHeavy() { for (var i = 0; i <= 3; i++) heavyFn1(i); } test(testHeavy); function heavyFn2(i) { if (i < 1000) return heavyFn1(i); return function () i; } function testHeavy2() { for (var i = 0; i <= 3; i++) heavyFn2(i); } test(testHeavy2); var merge_type_maps_x = 0, merge_type_maps_y = 0; function merge_type_maps() { for (merge_type_maps_x = 0; merge_type_maps_x < 50; ++merge_type_maps_x) if ((merge_type_maps_x & 1) == 1) ++merge_type_maps_y; return [merge_type_maps_x,merge_type_maps_y].join(","); } merge_type_maps.expected = "50,25"; test(merge_type_maps) function inner_double_outer_int() { function f(i) { for (var m = 0; m < 20; ++m) for (var n = 0; n < 100; n += i) ; return n; } return f(.5); } inner_double_outer_int.expected = 100; test(inner_double_outer_int); function newArrayTest() { var a = []; for (var i = 0; i < 10; i++) a[i] = new Array(); return a.map(function(x) x.length).toString(); } newArrayTest.expected="0,0,0,0,0,0,0,0,0,0"; test(newArrayTest); function stringSplitTest() { var s = "a,b" var a = null; for (var i = 0; i < 10; ++i) a = s.split(","); return a.join(); } stringSplitTest.expected="a,b"; test(stringSplitTest); function stringSplitIntoArrayTest() { var s = "a,b" var a = []; for (var i = 0; i < 10; ++i) a[i] = s.split(","); return a.join(); } stringSplitIntoArrayTest.expected="a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b,a,b"; test(stringSplitIntoArrayTest); function forVarInWith() { function foo() ({notk:42}); function bar() ({p:1, q:2, r:3, s:4, t:5}); var o = foo(); var a = []; with (o) { for (var k in bar()) a[a.length] = k; } return a.join(""); } forVarInWith.expected = "pqrst"; test(forVarInWith); function inObjectTest() { var o = {p: 1, q: 2, r: 3, s: 4, t: 5}; var r = 0; for (var i in o) { if (!(i in o)) break; if ((i + i) in o) break; ++r; } return r; } inObjectTest.expected = 5; test(inObjectTest); function inArrayTest() { var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for (var i = 0; i < a.length; i++) { if (!(i in a)) break; } return i; } inArrayTest.expected = 10; test(inArrayTest); function innerLoopIntOuterDouble() { var n = 10000, i=0, j=0, count=0, limit=0; for (i = 1; i <= n; ++i) { limit = i * 1; for (j = 0; j < limit; ++j) { ++count; } } return "" + count; } innerLoopIntOuterDouble.expected="50005000"; test(innerLoopIntOuterDouble); function outerline(){ var i=0; var j=0; for (i = 3; i<= 100000; i+=2) { for (j = 3; j < 1000; j+=2) { if ((i & 1) == 1) break; } } return "ok"; } outerline.expected="ok"; test(outerline); function addAccumulations(f) { var a = f(); var b = f(); return a() + b(); } function loopingAccumulator() { var x = 0; return function () { for (var i = 0; i < 10; ++i) { ++x; } return x; } } function testLoopingAccumulator() { var x = addAccumulations(loopingAccumulator); return x; } testLoopingAccumulator.expected = 20; test(testLoopingAccumulator); function testBranchingLoop() { var x = 0; for (var i=0; i < 100; ++i) { if (i == 51) { x += 10; } x++; } return x; } testBranchingLoop.expected = 110; test(testBranchingLoop); function testBranchingUnstableLoop() { var x = 0; for (var i=0; i < 100; ++i) { if (i == 51) { x += 10.1; } x++; } return x; } testBranchingUnstableLoop.expected = 110.1; test(testBranchingUnstableLoop); function testBranchingUnstableLoopCounter() { var x = 0; for (var i=0; i < 100; ++i) { if (i == 51) { i += 1.1; } x++; } return x; } testBranchingUnstableLoopCounter.expected = 99; test(testBranchingUnstableLoopCounter); function testBranchingUnstableObject() { var x = {s: "a"}; var t = ""; for (var i=0; i < 100; ++i) { if (i == 51) { x.s = 5; } t += x.s; } return t.length; } testBranchingUnstableObject.expected = 100; test(testBranchingUnstableObject); function testArrayDensityChange() { var x = []; var count = 0; for (var i=0; i < 100; ++i) { x[i] = "asdf"; } for (var i=0; i < x.length; ++i) { if (i == 51) { x[199] = "asdf"; } if (x[i]) count += x[i].length; } return count; } testArrayDensityChange.expected = 404; test(testArrayDensityChange); function testDoubleToStr() { var x = 0.0; var y = 5.5; for (var i = 0; i < 200; i++) { x += parseFloat(y.toString()); } return x; } testDoubleToStr.expected = 5.5*200; test(testDoubleToStr); function testNumberToString() { var x = new Number(0); for (var i = 0; i < 4; i++) x.toString(); } test(testNumberToString); function testDecayingInnerLoop() { var i, j, k = 10; for (i = 0; i < 5000; ++i) { for (j = 0; j < k; ++j); --k; } return i; } testDecayingInnerLoop.expected = 5000; test(testDecayingInnerLoop); function testContinue() { var i; var total = 0; for (i = 0; i < 20; ++i) { if (i == 11) continue; total++; } return total; } testContinue.expected = 19; test(testContinue); function testContinueWithLabel() { var i = 0; var j = 20; checkiandj : while (i<10) { i+=1; checkj : while (j>10) { j-=1; if ((j%2)==0) continue checkj; } } return i + j; } testContinueWithLabel.expected = 20; test(testContinueWithLabel); function testDivision() { var a = 32768; var b; while (b !== 1) { b = a / 2; a = b; } return a; } testDivision.expected = 1; test(testDivision); function testDivisionFloat() { var a = 32768.0; var b; while (b !== 1) { b = a / 2.0; a = b; } return a === 1.0; } testDivisionFloat.expected = true; test(testDivisionFloat); function testToUpperToLower() { var s = "Hello", s1, s2; for (i = 0; i < 100; ++i) { s1 = s.toLowerCase(); s2 = s.toUpperCase(); } return s1 + s2; } testToUpperToLower.expected = "helloHELLO"; test(testToUpperToLower); function testReplace2() { var s = "H e l l o", s1; for (i = 0; i < 100; ++i) s1 = s.replace(" ", ""); return s1; } testReplace2.expected = "He l l o"; test(testReplace2); function testBitwise() { var x = 10000; var y = 123456; var z = 987234; for (var i = 0; i < 50; i++) { x = x ^ y; y = y | z; z = ~x; } return x + y + z; } testBitwise.expected = -1298; test(testBitwise); function testSwitch() { var x = 0; var ret = 0; for (var i = 0; i < 100; ++i) { switch (x) { case 0: ret += 1; break; case 1: ret += 2; break; case 2: ret += 3; break; case 3: ret += 4; break; default: x = 0; } x++; } return ret; } testSwitch.expected = 226; test(testSwitch); function testSwitchString() { var x = "asdf"; var ret = 0; for (var i = 0; i < 100; ++i) { switch (x) { case "asdf": x = "asd"; ret += 1; break; case "asd": x = "as"; ret += 2; break; case "as": x = "a"; ret += 3; break; case "a": x = "foo"; ret += 4; break; default: x = "asdf"; } } return ret; } testSwitchString.expected = 200; test(testSwitchString); function testNegZero1Helper(z) { for (let j = 0; j < 5; ++j) { z = -z; } return Math.atan2(0, -0) == Math.atan2(0, z); } var testNegZero1 = function() { return testNegZero1Helper(0); } testNegZero1.expected = true; testNegZero1.name = 'testNegZero1'; testNegZero1Helper(1); test(testNegZero1); // No test case, just make sure this doesn't assert. function testNegZero2() { var z = 0; for (let j = 0; j < 5; ++j) { ({p: (-z)}); } } testNegZero2(); function testConstSwitch() { var x; for (var j=0;j<5;++j) { switch(1.1) { case NaN: case 2: } x = 2; } return x; } testConstSwitch.expected = 2; test(testConstSwitch); function testConstSwitch2() { var x; for (var j = 0; j < 4; ++j) { switch(0/0) { } } return "ok"; } testConstSwitch2.expected = "ok"; test(testConstSwitch2); function testConstIf() { var x; for (var j=0;j<5;++j) { if (1.1 || 5) { } x = 2;} return x; } testConstIf.expected = 2; test(testConstIf); function testTypeofHole() { var a = new Array(6); a[5] = 3; for (var i = 0; i < 6; ++i) a[i] = typeof a[i]; return a.join(","); } testTypeofHole.expected = "undefined,undefined,undefined,undefined,undefined,number" test(testTypeofHole); function testNativeLog() { var a = new Array(5); for (var i = 0; i < 5; i++) { a[i] = Math.log(Math.pow(Math.E, 10)); } return a.join(","); } testNativeLog.expected = "10,10,10,10,10"; test(testNativeLog); function test_JSOP_ARGSUB() { function f0() { return arguments[0]; } function f1() { return arguments[1]; } function f2() { return arguments[2]; } function f3() { return arguments[3]; } function f4() { return arguments[4]; } function f5() { return arguments[5]; } function f6() { return arguments[6]; } function f7() { return arguments[7]; } function f8() { return arguments[8]; } function f9() { return arguments[9]; } var a = []; for (var i = 0; i < 10; i++) { a[0] = f0('a'); a[1] = f1('a','b'); a[2] = f2('a','b','c'); a[3] = f3('a','b','c','d'); a[4] = f4('a','b','c','d','e'); a[5] = f5('a','b','c','d','e','f'); a[6] = f6('a','b','c','d','e','f','g'); a[7] = f7('a','b','c','d','e','f','g','h'); a[8] = f8('a','b','c','d','e','f','g','h','i'); a[9] = f9('a','b','c','d','e','f','g','h','i','j'); } return a.join(""); } test_JSOP_ARGSUB.expected = "abcdefghij"; test(test_JSOP_ARGSUB); function test_JSOP_ARGCNT() { function f0() { return arguments.length; } function f1() { return arguments.length; } function f2() { return arguments.length; } function f3() { return arguments.length; } function f4() { return arguments.length; } function f5() { return arguments.length; } function f6() { return arguments.length; } function f7() { return arguments.length; } function f8() { return arguments.length; } function f9() { return arguments.length; } var a = []; for (var i = 0; i < 10; i++) { a[0] = f0('a'); a[1] = f1('a','b'); a[2] = f2('a','b','c'); a[3] = f3('a','b','c','d'); a[4] = f4('a','b','c','d','e'); a[5] = f5('a','b','c','d','e','f'); a[6] = f6('a','b','c','d','e','f','g'); a[7] = f7('a','b','c','d','e','f','g','h'); a[8] = f8('a','b','c','d','e','f','g','h','i'); a[9] = f9('a','b','c','d','e','f','g','h','i','j'); } return a.join(","); } test_JSOP_ARGCNT.expected = "1,2,3,4,5,6,7,8,9,10"; test(test_JSOP_ARGCNT); function testNativeMax() { var out = [], k; for (var i = 0; i < 5; ++i) { k = Math.max(k, i); } out.push(k); k = 0; for (var i = 0; i < 5; ++i) { k = Math.max(k, i); } out.push(k); for (var i = 0; i < 5; ++i) { k = Math.max(0, -0); } out.push((1 / k) < 0); return out.join(","); } testNativeMax.expected = "NaN,4,false"; test(testNativeMax); function testFloatArrayIndex() { var a = []; for (var i = 0; i < 10; ++i) { a[3] = 5; a[3.5] = 7; } return a[3] + "," + a[3.5]; } testFloatArrayIndex.expected = "5,7"; test(testFloatArrayIndex); function testStrict() { var n = 10, a = []; for (var i = 0; i < 10; ++i) { a[0] = (n === 10); a[1] = (n !== 10); a[2] = (n === null); a[3] = (n == null); } return a.join(","); } testStrict.expected = "true,false,false,false"; test(testStrict); function testSetPropNeitherMissNorHit() { for (var j = 0; j < 5; ++j) { if (({}).__proto__ = 1) { } } return "ok"; } testSetPropNeitherMissNorHit.expected = "ok"; test(testSetPropNeitherMissNorHit); function testPrimitiveConstructorPrototype() { var f = function(){}; f.prototype = false; for (let j=0;j<5;++j) { new f; } return "ok"; } testPrimitiveConstructorPrototype.expected = "ok"; test(testPrimitiveConstructorPrototype); function testSideExitInConstructor() { var FCKConfig = {}; FCKConfig.CoreStyles = { 'Bold': { }, 'Italic': { }, 'FontFace': { }, 'Size' : { Overrides: [ ] }, 'Color' : { Element: '', Styles: { }, Overrides: [ ] }, 'BackColor': { Element : '', Styles : { 'background-color' : '' } }, }; var FCKStyle = function(A) { A.Element; }; var pass = true; for (var s in FCKConfig.CoreStyles) { var x = new FCKStyle(FCKConfig.CoreStyles[s]); if (!x) pass = false; } return pass; } testSideExitInConstructor.expected = true; test(testSideExitInConstructor); function testNot() { var a = new Object(), b = null, c = "foo", d = "", e = 5, f = 0, g = 5.5, h = -0, i = true, j = false, k = undefined; var r; for (var i = 0; i < 10; ++i) r = [!a, !b, !c, !d, !e, !f, !g, !h, !i, !j, !k]; return r.join(","); } testNot.expected = "false,true,false,true,false,true,false,true,false,true,true"; test(testNot); function doTestDifferingArgc(a, b) { var k = 0; for (var i = 0; i < 10; i++) { k += i; } return k; } function testDifferingArgc() { var x = 0; x += doTestDifferingArgc(1, 2); x += doTestDifferingArgc(1); x += doTestDifferingArgc(1, 2, 3); return x; } testDifferingArgc.expected = 45*3; test(testDifferingArgc); function doTestMoreArgcThanNargs() { var x = 0; for (var i = 0; i < 10; i++) { x = x + arguments[3]; } return x; } function testMoreArgcThanNargs() { return doTestMoreArgcThanNargs(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); } testMoreArgcThanNargs.expected = 4*10; test(testMoreArgcThanNargs); // Test stack reconstruction after a nested exit function testNestedExitStackInner(j, counter) { ++counter; var b = 0; for (var i = 1; i <= RUNLOOP; i++) { ++b; var a; // Make sure that once everything has been traced we suddenly switch to // a different control flow the first time we run the outermost tree, // triggering a side exit. if (j < RUNLOOP) a = 1; else a = 0; ++b; b += a; } return counter + b; } function testNestedExitStackOuter() { var counter = 0; for (var j = 1; j <= RUNLOOP; ++j) { for (var k = 1; k <= RUNLOOP; ++k) { counter = testNestedExitStackInner(j, counter); } } return counter; } testNestedExitStackOuter.expected = 81; test(testNestedExitStackOuter); function testHOTLOOPSize() { return HOTLOOP > 1; } testHOTLOOPSize.expected = true; test(testHOTLOOPSize); function testMatchStringObject() { var a = new String("foo"); var b; for (i = 0; i < 300; i++) b = a.match(/bar/); return b; } testMatchStringObject.expected = null; test(testMatchStringObject); function innerSwitch(k) { var m = 0; switch (k) { case 0: m = 1; break; } return m; } function testInnerSwitchBreak() { var r = new Array(5); for (var i = 0; i < 5; i++) { r[i] = innerSwitch(0); } return r.join(","); } testInnerSwitchBreak.expected = "1,1,1,1,1"; test(testInnerSwitchBreak); function testArrayNaNIndex() { for (var j = 0; j < 4; ++j) { [this[NaN]]; } for (var j = 0; j < 5; ++j) { if([1][-0]) { } } return "ok"; } testArrayNaNIndex.expected = "ok"; test(testArrayNaNIndex); function innerTestInnerMissingArgs(a,b,c,d) { if (a) { } else { } } function doTestInnerMissingArgs(k) { for (i = 0; i < 10; i++) { innerTestInnerMissingArgs(k); } } function testInnerMissingArgs() { doTestInnerMissingArgs(1); doTestInnerMissingArgs(0); return 1; } testInnerMissingArgs.expected = 1; //Expected: that we don't crash. test(testInnerMissingArgs); function regexpLastIndex() { var n = 0; var re = /hi/g; var ss = " hi hi hi hi hi hi hi hi hi hi"; for (var i = 0; i < 10; i++) { // re.exec(ss); n += (re.lastIndex > 0) ? 3 : 0; re.lastIndex = 0; } return n; } regexpLastIndex.expected = 0; // 30; test(regexpLastIndex); function testHOTLOOPCorrectness() { var b = 0; for (var i = 0; i < HOTLOOP; ++i) ++b; return b; } testHOTLOOPCorrectness.expected = HOTLOOP; testHOTLOOPCorrectness.jitstats = { recorderStarted: 1, recorderAborted: 0, traceTriggered: 0 }; // Change the global shape right before doing the test this.testHOTLOOPCorrectnessVar = 1; test(testHOTLOOPCorrectness); function testRUNLOOPCorrectness() { var b = 0; for (var i = 0; i < RUNLOOP; ++i) { ++b; } return b; } testRUNLOOPCorrectness.expected = RUNLOOP; testRUNLOOPCorrectness.jitstats = { recorderStarted: 1, recorderAborted: 0, traceTriggered: 1 }; // Change the global shape right before doing the test this.testRUNLOOPCorrectnessVar = 1; test(testRUNLOOPCorrectness); function testDateNow() { // Accessing global.Date for the first time will change the global shape, // so do it before the loop starts; otherwise we have to loop an extra time // to pick things up. var time = Date.now(); for (var j = 0; j < RUNLOOP; ++j) time = Date.now(); return "ok"; } testDateNow.expected = "ok"; testDateNow.jitstats = { recorderStarted: 1, recorderAborted: 0, traceTriggered: 1 }; test(testDateNow); function testINITELEM() { var x; for (var i = 0; i < 10; ++i) x = { 0: 5, 1: 5 }; return x[0] + x[1]; } testINITELEM.expected = 10; test(testINITELEM); function testUndefinedBooleanCmp() { var t = true, f = false, x = []; for (var i = 0; i < 10; ++i) { x[0] = t == undefined; x[1] = t != undefined; x[2] = t === undefined; x[3] = t !== undefined; x[4] = t < undefined; x[5] = t > undefined; x[6] = t <= undefined; x[7] = t >= undefined; x[8] = f == undefined; x[9] = f != undefined; x[10] = f === undefined; x[11] = f !== undefined; x[12] = f < undefined; x[13] = f > undefined; x[14] = f <= undefined; x[15] = f >= undefined; } return x.join(","); } testUndefinedBooleanCmp.expected = "false,true,false,true,false,false,false,false,false,true,false,true,false,false,false,false"; test(testUndefinedBooleanCmp); function testConstantBooleanExpr() { for (var j = 0; j < 3; ++j) { if(true <= true) { } } return "ok"; } testConstantBooleanExpr.expected = "ok"; test(testConstantBooleanExpr); function testNegativeGETELEMIndex() { for (let i=0;i<3;++i) /x/[-4]; return "ok"; } testNegativeGETELEMIndex.expected = "ok"; test(testNegativeGETELEMIndex); function doTestInvalidCharCodeAt(input) { var q = ""; for (var i = 0; i < 10; i++) q += input.charCodeAt(i); return q; } function testInvalidCharCodeAt() { return doTestInvalidCharCodeAt(""); } testInvalidCharCodeAt.expected = "NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN"; test(testInvalidCharCodeAt); function FPQuadCmp() { for (let j = 0; j < 3; ++j) { true == 0; } return "ok"; } FPQuadCmp.expected = "ok"; test(FPQuadCmp); function testDestructuring() { var t = 0; for (var i = 0; i < HOTLOOP + 1; ++i) { var [r, g, b] = [1, 1, 1]; t += r + g + b; } return t } testDestructuring.expected = (HOTLOOP + 1) * 3; test(testDestructuring); function loopWithUndefined1(t, val) { var a = new Array(6); for (var i = 0; i < 6; i++) a[i] = (t > val); return a; } loopWithUndefined1(5.0, 2); //compile version with val=int function testLoopWithUndefined1() { return loopWithUndefined1(5.0).join(","); //val=undefined }; testLoopWithUndefined1.expected = "false,false,false,false,false,false"; test(testLoopWithUndefined1); function loopWithUndefined2(t, dostuff, val) { var a = new Array(6); for (var i = 0; i < 6; i++) { if (dostuff) { val = 1; a[i] = (t > val); } else { a[i] = (val == undefined); } } return a; } function testLoopWithUndefined2() { var a = loopWithUndefined2(5.0, true, 2); var b = loopWithUndefined2(5.0, true); var c = loopWithUndefined2(5.0, false, 8); var d = loopWithUndefined2(5.0, false); return [a[0], b[0], c[0], d[0]].join(","); } testLoopWithUndefined2.expected = "true,true,false,true"; test(testLoopWithUndefined2); //test no multitrees assert function testBug462388() { var c = 0, v; for each (let x in ["",v,v,v]) { for (c=0;c<4;++c) { } } return true; } testBug462388.expected = true; test(testBug462388); //test no multitrees assert function testBug462407() { for each (let i in [0, {}, 0, 1.5, {}, 0, 1.5, 0, 0]) { } return true; } testBug462407.expected = true; test(testBug462407); //test no multitrees assert function testBug463490() { function f(a, b, d) { for (var i = 0; i < 10; i++) { if (d) b /= 2; } return a + b; } //integer stable loop f(2, 2, false); //double stable loop f(3, 4.5, false); //integer unstable branch f(2, 2, true); return true; }; testBug463490.expected = true; test(testBug463490); // Test no assert (bug 464089) function shortRecursiveLoop(b, c) { for (var i = 0; i < c; i++) { if (b) shortRecursiveLoop(c - 1); } } function testClosingRecursion() { shortRecursiveLoop(false, 1); shortRecursiveLoop(true, 3); return true; } testClosingRecursion.expected = true; test(testClosingRecursion); // Test no assert or crash from outer recorders (bug 465145) function testBug465145() { this.__defineSetter__("x", function(){}); this.watch("x", function(){}); y = this; for (var z = 0; z < 2; ++z) { x = y }; this.__defineSetter__("x", function(){}); for (var z = 0; z < 2; ++z) { x = y }; } function testTrueShiftTrue() { var a = new Array(5); for (var i=0;i<5;++i) a[i] = "" + (true << true); return a.join(","); } testTrueShiftTrue.expected = "2,2,2,2,2"; test(testTrueShiftTrue); // Test no assert or crash function testBug465261() { for (let z = 0; z < 2; ++z) { for each (let x in [0, true, (void 0), 0, (void 0)]) { if(x){} } }; return true; } testBug465261.expected = true; test(testBug465261); function testBug465272() { var a = new Array(5); for (j=0;j<5;++j) a[j] = "" + ((5) - 2); return a.join(","); } testBug465272.expected = "3,3,3,3,3" test(testBug465272); function testBug465483() { var a = new Array(4); var c = 0; for each (i in [4, 'a', 'b', (void 0)]) a[c++] = '' + (i + i); return a.join(','); } testBug465483.expected = '8,aa,bb,NaN'; test(testBug465483); function testNullCallee() { try { function f() { var x = new Array(5); for (var i = 0; i < 5; i++) x[i] = a[i].toString(); return x.join(','); } f([[1],[2],[3],[4],[5]]); f([null, null, null, null, null]); } catch (e) { return true; } return false; } testNullCallee.expected = true; test(testNullCallee); //test no multitrees assert function testBug466128() { for (let a = 0; a < 3; ++a) { for each (let b in [1, 2, "three", 4, 5, 6, 7, 8]) { } } return true; } testBug466128.expected = true; test(testBug466128); //test no assert function testBug465688() { for each (let d in [-0x80000000, -0x80000000]) - -d; return true; } testBug465688.expected = true; test(testBug465688); //test no assert function testBug466262() { var e = 1; for (var d = 0; d < 3; ++d) { if (d == 2) { e = ""; } } return true; } testBug466262.expected = true; test(testBug466262); function testNewDate() { // Accessing global.Date for the first time will change the global shape, // so do it before the loop starts; otherwise we have to loop an extra time // to pick things up. var start = new Date(); var time = new Date(); for (var j = 0; j < RUNLOOP; ++j) time = new Date(); return time > 0 && time >= start; } testNewDate.expected = true; testNewDate.jitstats = { recorderStarted: 1, recorderAborted: 0, traceTriggered: 1 }; test(testNewDate); function testArrayPushPop() { var a = [], sum1 = 0, sum2 = 0; for (var i = 0; i < 10; ++i) sum1 += a.push(i); for (var i = 0; i < 10; ++i) sum2 += a.pop(); a.push(sum1); a.push(sum2); return a.join(","); } testArrayPushPop.expected = "55,45"; test(testArrayPushPop); function testSlowArrayPop() { var a = []; for (var i = 0; i < RUNLOOP; i++) a[i] = [0]; a[RUNLOOP-1].__defineGetter__("0", function () { return 'xyzzy'; }); var last; for (var i = 0; i < RUNLOOP; i++) last = a[i].pop(); // reenters interpreter in getter return last; } testSlowArrayPop.expected = 'xyzzy'; test(testSlowArrayPop); // Same thing but it needs to reconstruct multiple stack frames (so, // multiple functions called inside the loop) function testSlowArrayPopMultiFrame() { var a = []; for (var i = 0; i < RUNLOOP; i++) a[i] = [0]; a[RUNLOOP-1].__defineGetter__("0", function () { return 23; }); function child(a, i) { return a[i].pop(); // reenters interpreter in getter } function parent(a, i) { return child(a, i); } function gramps(a, i) { return parent(a, i); } var last; for (var i = 0; i < RUNLOOP; i++) last = gramps(a, i); return last; } testSlowArrayPopMultiFrame.expected = 23; test(testSlowArrayPopMultiFrame); // Same thing but nested trees, each reconstructing one or more stack frames // (so, several functions with loops, such that the loops end up being // nested though they are not lexically nested) function testSlowArrayPopNestedTrees() { var a = []; for (var i = 0; i < RUNLOOP; i++) a[i] = [0]; a[RUNLOOP-1].__defineGetter__("0", function () { return 3.14159 }); function child(a, i, j, k) { var last = 2.71828; for (var l = 0; l < RUNLOOP; l++) if (i == RUNLOOP-1 && j == RUNLOOP-1 && k == RUNLOOP-1) last = a[l].pop(); // reenters interpreter in getter return last; } function parent(a, i, j) { var last; for (var k = 0; k < RUNLOOP; k++) last = child(a, i, j, k); return last; } function gramps(a, i) { var last; for (var j = 0; j < RUNLOOP; j++) last = parent(a, i, j); return last; } var last; for (var i = 0; i < RUNLOOP; i++) last = gramps(a, i); return last; } testSlowArrayPopNestedTrees.expected = 3.14159; test(testSlowArrayPopNestedTrees); function testResumeOp() { var a = [1,"2",3,"4",5,"6",7,"8",9,"10",11,"12",13,"14",15,"16"]; var x = ""; while (a.length > 0) x += a.pop(); return x; } testResumeOp.expected = "16151413121110987654321"; test(testResumeOp); function testUndefinedCmp() { var a = false; for (var j = 0; j < 4; ++j) { if (undefined < false) { a = true; } } return a; } testUndefinedCmp.expected = false; test(testUndefinedCmp); function reallyDeepNestedExit(schedule) { var c = 0, j = 0; for (var i = 0; i < 5; i++) { for (j = 0; j < 4; j++) { c += (schedule[i*4 + j] == 1) ? 1 : 2; } } return c; } function testReallyDeepNestedExit() { var c = 0; var schedule1 = new Array(5*4); var schedule2 = new Array(5*4); for (var i = 0; i < 5*4; i++) { schedule1[i] = 0; schedule2[i] = 0; } /** * First innermost compile: true branch runs through. * Second '': false branch compiles new loop edge. * First outer compile: expect true branch. * Second '': hit false branch. */ schedule1[0*4 + 3] = 1; var schedules = [schedule1, schedule2, schedule1, schedule2, schedule2]; for (var i = 0; i < 5; i++) { c += reallyDeepNestedExit(schedules[i]); } return c; } testReallyDeepNestedExit.expected = 198; test(testReallyDeepNestedExit); function testRegExpTest() { var r = /abc/; var flag = false; for (var i = 0; i < 10; ++i) flag = r.test("abc"); return flag; } testRegExpTest.expected = true; test(testRegExpTest); function testNumToString() { var r = []; var d = 123456789; for (var i = 0; i < 10; ++i) { r = [ d.toString(), (-d).toString(), d.toString(10), (-d).toString(10), d.toString(16), (-d).toString(16), d.toString(36), (-d).toString(36) ]; } return r.join(","); } testNumToString.expected = "123456789,-123456789,123456789,-123456789,75bcd15,-75bcd15,21i3v9,-21i3v9"; test(testNumToString); function testLongNumToString() { var s; for (var i = 0; i < 5; i++) s = (0x08000000).toString(2); return s; } testLongNumToString.expected = '1000000000000000000000000000'; test(testLongNumToString); function testSubstring() { for (var i = 0; i < 5; ++i) { actual = "".substring(5); } return actual; } testSubstring.expected = ""; test(testSubstring); function testForInLoopChangeIteratorType() { for(y in [0,1,2]) y = NaN; (function(){ [].__proto__.u = void 0; for (let y in [5,6,7,8]) y = NaN; delete [].__proto__.u; })() return "ok"; } testForInLoopChangeIteratorType.expected = "ok"; test(testForInLoopChangeIteratorType); function testGrowDenseArray() { var a = new Array(); for (var i = 0; i < 10; ++i) a[i] |= 5; return a.join(","); } testGrowDenseArray.expected = "5,5,5,5,5,5,5,5,5,5"; test(testGrowDenseArray); function testCallProtoMethod() { function X() { this.x = 1; } X.prototype.getName = function () { return "X"; } function Y() { this.x = 2; } Y.prototype.getName = function() "Y"; var a = [new X, new X, new X, new X, new Y]; var s = ''; for (var i = 0; i < a.length; i++) s += a[i].getName(); return s; } testCallProtoMethod.expected = 'XXXXY'; test(testCallProtoMethod); function testTypeUnstableForIn() { var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; var x = 0; for (var i in a) { i = parseInt(i); x++; } return x; } testTypeUnstableForIn.expected = 16; test(testTypeUnstableForIn); function testAddUndefined() { for (var j = 0; j < 3; ++j) (0 + void 0) && 0; } test(testAddUndefined); function testStringify() { var t = true, f = false, u = undefined, n = 5, d = 5.5, s = "x"; var a = []; for (var i = 0; i < 10; ++i) { a[0] = "" + t; a[1] = t + ""; a[2] = "" + f; a[3] = f + ""; a[4] = "" + u; a[5] = u + ""; a[6] = "" + n; a[7] = n + ""; a[8] = "" + d; a[9] = d + ""; a[10] = "" + s; a[11] = s + ""; } return a.join(","); } testStringify.expected = "true,true,false,false,undefined,undefined,5,5,5.5,5.5,x,x"; test(testStringify); function testObjectToString() { var o = {toString: function()"foo"}; var s = ""; for (var i = 0; i < 10; i++) s += o; return s; } testObjectToString.expected = "foofoofoofoofoofoofoofoofoofoo"; test(testObjectToString); function testObjectToNumber() { var o = {valueOf: function()-3}; var x = 0; for (var i = 0; i < 10; i++) x -= o; return x; } testObjectToNumber.expected = 30; test(testObjectToNumber); function my_iterator_next() { if (this.i == 10) { this.i = 0; throw this.StopIteration; } return this.i++; } function testCustomIterator() { var o = { __iterator__: function () { return { i: 0, next: my_iterator_next, StopIteration: StopIteration }; } }; var a=[]; for (var k = 0; k < 100; k += 10) { for(var j in o) { a[k + (j >> 0)] = j*k; } } return a.join(); } testCustomIterator.expected = "0,0,0,0,0,0,0,0,0,0,0,10,20,30,40,50,60,70,80,90,0,20,40,60,80,100,120,140,160,180,0,30,60,90,120,150,180,210,240,270,0,40,80,120,160,200,240,280,320,360,0,50,100,150,200,250,300,350,400,450,0,60,120,180,240,300,360,420,480,540,0,70,140,210,280,350,420,490,560,630,0,80,160,240,320,400,480,560,640,720,0,90,180,270,360,450,540,630,720,810"; test(testCustomIterator); function bug464403() { print(8); var u = [print, print, function(){}] for each (x in u) for (u.e in [1,1,1,1]); return "ok"; } bug464403.expected = "ok"; test(bug464403); function testBoxDoubleWithDoubleSizedInt() { var i = 0; var a = new Array(3); while (i < a.length) a[i++] = 0x5a827999; return a.join(","); } testBoxDoubleWithDoubleSizedInt.expected = "1518500249,1518500249,1518500249"; test(testBoxDoubleWithDoubleSizedInt); function testObjectOrderedCmp() { var a = new Array(5); for(var i=0;i<5;++i) a[i] = ({} < {}); return a.join(","); } testObjectOrderedCmp.expected = "false,false,false,false,false"; test(testObjectOrderedCmp); function testObjectOrderedCmp2() { var a = new Array(5); for(var i=0;i<5;++i) a[i] = ("" <= null); return a.join(","); } testObjectOrderedCmp2.expected = "true,true,true,true,true"; test(testObjectOrderedCmp2); function testLogicalNotNaN() { var i = 0; var a = new Array(5); while (i < a.length) a[i++] = !NaN; return a.join(); } testLogicalNotNaN.expected = "true,true,true,true,true"; test(testLogicalNotNaN); function testStringToInt32() { var s = ""; for (let j = 0; j < 5; ++j) s += ("1e+81" ^ 3); return s; } testStringToInt32.expected = "33333"; test(testStringToInt32); function testIn() { var array = [3]; var obj = { "-1": 5, "1.7": 3, "foo": 5, "1": 7 }; var a = []; for (let j = 0; j < 5; ++j) { a.push("0" in array); a.push(-1 in obj); a.push(1.7 in obj); a.push("foo" in obj); a.push(1 in obj); a.push("1" in array); a.push(-2 in obj); a.push(2.7 in obj); a.push("bar" in obj); a.push(2 in obj); } return a.join(","); } testIn.expected = "true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false"; test(testIn); function testBranchCse() { empty = []; out = []; for (var j=0;j<10;++j) { empty[42]; out.push((1 * (1)) | ""); } return out.join(","); } testBranchCse.expected = "1,1,1,1,1,1,1,1,1,1"; test(testBranchCse); function testMulOverflow() { var a = []; for (let j=0;j<5;++j) a.push(0 | ((0x60000009) * 0x60000009)); return a.join(","); } testMulOverflow.expected = "-1073741824,-1073741824,-1073741824,-1073741824,-1073741824"; test(testMulOverflow); function testThinLoopDemote() { function f() { var k = 1; for (var n = 0; n < 4; n++) { k = (k * 10); } return k; } f(); return f(); } testThinLoopDemote.expected = 10000; testThinLoopDemote.jitstats = { recorderStarted: 2, recorderAborted: 0, traceCompleted: 2, traceTriggered: 2, unstableLoopVariable: 1 }; test(testThinLoopDemote); var global = this; function testWeirdDateParseOuter() { var vDateParts = ["11", "17", "2008"]; var out = []; for (var vI = 0; vI < vDateParts.length; vI++) out.push(testWeirdDateParseInner(vDateParts[vI])); /* Mutate the global shape so we fall off trace; this causes * additional oddity */ global.x = Math.random(); return out; } function testWeirdDateParseInner(pVal) { var vR = 0; for (var vI = 0; vI < pVal.length; vI++) { var vC = pVal.charAt(vI); if ((vC >= '0') && (vC <= '9')) vR = (vR * 10) + parseInt(vC); } return vR; } function testWeirdDateParse() { var result = []; result.push(testWeirdDateParseInner("11")); result.push(testWeirdDateParseInner("17")); result.push(testWeirdDateParseInner("2008")); result.push(testWeirdDateParseInner("11")); result.push(testWeirdDateParseInner("17")); result.push(testWeirdDateParseInner("2008")); result = result.concat(testWeirdDateParseOuter()); result = result.concat(testWeirdDateParseOuter()); result.push(testWeirdDateParseInner("11")); result.push(testWeirdDateParseInner("17")); result.push(testWeirdDateParseInner("2008")); return result.join(","); } testWeirdDateParse.expected = "11,17,2008,11,17,2008,11,17,2008,11,17,2008,11,17,2008"; testWeirdDateParse.jitstats = { recorderStarted: 8, recorderAborted: 1, traceCompleted: 7, traceTriggered: 14, unstableLoopVariable: 3, noCompatInnerTrees: 1 }; test(testWeirdDateParse); function testUndemotableBinaryOp() { var out = []; for (let j = 0; j < 5; ++j) { out.push(6 - ((void 0) ^ 0x80000005)); } return out.join(","); } testUndemotableBinaryOp.expected = "2147483649,2147483649,2147483649,2147483649,2147483649"; test(testUndemotableBinaryOp); function testNullRelCmp() { var out = []; for(j=0;j<3;++j) { out.push(3 > null); out.push(3 < null); out.push(0 == null); out.push(3 == null); } return out.join(","); } testNullRelCmp.expected = "true,false,false,false,true,false,false,false,true,false,false,false"; test(testNullRelCmp); function testEqFalseEmptyString() { var x = []; for (var i=0;i<5;++i) x.push(false == ""); return x.join(","); } testEqFalseEmptyString.expected = "true,true,true,true,true"; test(testEqFalseEmptyString); function testIncDec2(ii) { var x = []; for (let j=0;j<5;++j) { ii=j; jj=j; var kk=j; x.push(ii--); x.push(jj--); x.push(kk--); x.push(++ii); x.push(++jj); x.push(++kk); } return x.join(","); } function testIncDec() { return testIncDec2(0); } testIncDec.expected = "0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4"; test(testIncDec); function testApply() { var q = []; for (var i = 0; i < 10; ++i) Array.prototype.push.apply(q, [5]); return q.join(","); } testApply.expected = "5,5,5,5,5,5,5,5,5,5"; test(testApply); function testNestedForIn() { var a = {x: 1, y: 2, z: 3}; var s = ''; for (var p1 in a) for (var p2 in a) s += p1 + p2 + ' '; return s; } testNestedForIn.expected = 'xx xy xz yx yy yz zx zy zz '; test(testNestedForIn); function testForEach() { var r; var a = ["zero", "one", "two", "three"]; for (var i = 0; i < RUNLOOP; i++) { r = ""; for each (var s in a) r += s + " "; } return r; } testForEach.expected = "zero one two three "; test(testForEach); function testThinForEach() { var a = ["red"]; var n = 0; for (var i = 0; i < 10; i++) for each (var v in a) if (v) n++; return n; } testThinForEach.expected = 10; test(testThinForEach); function testComparisons() { // All the special values from each of the types in // ECMA-262, 3rd ed. section 8 var undefinedType, nullType, booleanType, stringType, numberType, objectType; var types = []; types[undefinedType = 0] = "Undefined"; types[nullType = 1] = "Null"; types[booleanType = 2] = "Boolean"; types[stringType = 3] = "String"; types[numberType = 4] = "Number"; types[objectType = 5] = "Object"; var JSVAL_INT_MIN = -Math.pow(2, 30); var JSVAL_INT_MAX = Math.pow(2, 30) - 1; // Values from every ES3 type, hitting all the edge-case and special values // that can be dreamed up var values = { "undefined": { value: function() { return undefined; }, type: undefinedType }, "null": { value: function() { return null; }, type: nullType }, "true": { value: function() { return true; }, type: booleanType }, "false": { value: function() { return false; }, type: booleanType }, '""': { value: function() { return ""; }, type: stringType }, '"a"': { // a > [, for string-object comparisons value: function() { return "a"; }, type: stringType }, '"Z"': { // Z < [, for string-object comparisons value: function() { return "Z"; }, type: stringType }, "0": { value: function() { return 0; }, type: numberType }, "-0": { value: function() { return -0; }, type: numberType }, "1": { value: function() { return 1; }, type: numberType }, "Math.E": { value: function() { return Math.E; }, type: numberType }, "JSVAL_INT_MIN - 1": { value: function() { return JSVAL_INT_MIN - 1; }, type: numberType }, "JSVAL_INT_MIN": { value: function() { return JSVAL_INT_MIN; }, type: numberType }, "JSVAL_INT_MIN + 1": { value: function() { return JSVAL_INT_MIN + 1; }, type: numberType }, "JSVAL_INT_MAX - 1": { value: function() { return JSVAL_INT_MAX - 1; }, type: numberType }, "JSVAL_INT_MAX": { value: function() { return JSVAL_INT_MAX; }, type: numberType }, "JSVAL_INT_MAX + 1": { value: function() { return JSVAL_INT_MAX + 1; }, type: numberType }, "Infinity": { value: function() { return Infinity; }, type: numberType }, "-Infinity": { value: function() { return -Infinity; }, type: numberType }, "NaN": { value: function() { return NaN; }, type: numberType }, "{}": { value: function() { return {}; }, type: objectType }, "{ valueOf: undefined }": { value: function() { return { valueOf: undefined }; }, type: objectType }, "[]": { value: function() { return []; }, type: objectType }, '[""]': { value: function() { return [""]; }, type: objectType }, '["a"]': { value: function() { return ["a"]; }, type: objectType }, "[0]": { value: function() { return [0]; }, type: objectType } }; var orderOps = { "<": function(a, b) { return a < b; }, ">": function(a, b) { return a > b; }, "<=": function(a, b) { return a <= b; }, ">=": function(a, b) { return a >= b; } }; var eqOps = { "==": function(a, b) { return a == b; }, "!=": function(a, b) { return a != b; }, "===": function(a, b) { return a === b; }, "!==": function(a, b) { return a !== b; } }; var notEqualIncomparable = { eq: { "==": false, "!=": true, "===": false, "!==": true }, order: { "<": false, ">": false, "<=": false, ">=": false } }; var notEqualLessThan = { eq: { "==": false, "!=": true, "===": false, "!==": true }, order: { "<": true, ">": false, "<=": true, ">=": false } }; var notEqualGreaterThan = { eq: { "==": false, "!=": true, "===": false, "!==": true }, order: { "<": false, ">": true, "<=": false, ">=": true } }; var notEqualNorDifferent = { eq: { "==": false, "!=": true, "===": false, "!==": true }, order: { "<": false, ">": false, "<=": true, ">=": true } }; var strictlyEqual = { eq: { "==": true, "!=": false, "===": true, "!==": false }, order: { "<": false, ">": false, "<=": true, ">=": true } }; var looselyEqual = { eq: { "==": true, "!=": false, "===": false, "!==": true }, order: { "<": false, ">": false, "<=": true, ">=": true } }; var looselyEqualNotDifferent = { eq: { "==": true, "!=": false, "===": false, "!==": true }, order: { "<": false, ">": false, "<=": true, ">=": true } }; var looselyEqualIncomparable = { eq: { "==": true, "!=": false, "===": false, "!==": true }, order: { "<": false, ">": false, "<=": false, ">=": false } }; var strictlyEqualNotDifferent = { eq: { "==": true, "!=": false, "===": true, "!==": false }, order: { "<": false, ">": false, "<=": true, ">=": true } }; var strictlyEqualIncomparable = { eq: { "==": true, "!=": false, "===": true, "!==": false }, order: { "<": false, ">": false, "<=": false, ">=": false } }; var comparingZeroToSomething = { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": looselyEqual, '""': looselyEqualNotDifferent, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": strictlyEqual, "-0": strictlyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": looselyEqual, '[""]': looselyEqual, '["a"]': notEqualIncomparable, "[0]": looselyEqual }; var comparingObjectOrObjectWithValueUndefined = { "undefined": notEqualIncomparable, "null": notEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualGreaterThan, '"a"': notEqualLessThan, '"Z"': notEqualGreaterThan, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualNorDifferent, "{ valueOf: undefined }": notEqualNorDifferent, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualLessThan, "[0]": notEqualGreaterThan }; // Constructed expected-value matrix var expected = { "undefined": { "undefined": strictlyEqualIncomparable, "null": looselyEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualIncomparable, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualIncomparable, '[""]': notEqualIncomparable, '["a"]': notEqualIncomparable, "[0]": notEqualIncomparable }, "null": { "undefined": looselyEqualIncomparable, "null": strictlyEqualNotDifferent, "true": notEqualLessThan, "false": notEqualNorDifferent, '""': notEqualNorDifferent, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualNorDifferent, "-0": notEqualNorDifferent, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualNorDifferent, '[""]': notEqualNorDifferent, '["a"]': notEqualIncomparable, "[0]": notEqualNorDifferent }, "true": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": strictlyEqual, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": looselyEqual, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "false": { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": strictlyEqual, '""': looselyEqualNotDifferent, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": looselyEqual, "-0": looselyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": looselyEqual, '[""]': looselyEqual, '["a"]': notEqualIncomparable, "[0]": looselyEqual }, '""': { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": looselyEqual, '""': strictlyEqual, '"a"': notEqualLessThan, '"Z"': notEqualLessThan, "0": looselyEqual, "-0": looselyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualLessThan, "{ valueOf: undefined }": notEqualLessThan, "[]": looselyEqual, '[""]': looselyEqual, '["a"]': notEqualLessThan, "[0]": notEqualLessThan }, '"a"': { "undefined": notEqualIncomparable, "null": notEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualGreaterThan, '"a"': strictlyEqual, '"Z"': notEqualGreaterThan, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualGreaterThan, "{ valueOf: undefined }": notEqualGreaterThan, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': looselyEqualNotDifferent, "[0]": notEqualGreaterThan }, '"Z"': { "undefined": notEqualIncomparable, "null": notEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualGreaterThan, '"a"': notEqualLessThan, '"Z"': strictlyEqual, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualLessThan, "{ valueOf: undefined }": notEqualLessThan, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualLessThan, "[0]": notEqualGreaterThan }, "0": comparingZeroToSomething, "-0": comparingZeroToSomething, "1": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": looselyEqual, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": strictlyEqual, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "Math.E": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": notEqualGreaterThan, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": notEqualGreaterThan, "Math.E": strictlyEqual, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "JSVAL_INT_MIN - 1": { "undefined": notEqualIncomparable, "null": notEqualLessThan, "true": notEqualLessThan, "false": notEqualLessThan, '""': notEqualLessThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualLessThan, "-0": notEqualLessThan, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": strictlyEqual, "JSVAL_INT_MIN": notEqualLessThan, "JSVAL_INT_MIN + 1": notEqualLessThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualLessThan, '[""]': notEqualLessThan, '["a"]': notEqualIncomparable, "[0]": notEqualLessThan }, "JSVAL_INT_MIN": { "undefined": notEqualIncomparable, "null": notEqualLessThan, "true": notEqualLessThan, "false": notEqualLessThan, '""': notEqualLessThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualLessThan, "-0": notEqualLessThan, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": strictlyEqual, "JSVAL_INT_MIN + 1": notEqualLessThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualLessThan, '[""]': notEqualLessThan, '["a"]': notEqualIncomparable, "[0]": notEqualLessThan }, "JSVAL_INT_MIN + 1": { "undefined": notEqualIncomparable, "null": notEqualLessThan, "true": notEqualLessThan, "false": notEqualLessThan, '""': notEqualLessThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualLessThan, "-0": notEqualLessThan, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": strictlyEqual, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualLessThan, '[""]': notEqualLessThan, '["a"]': notEqualIncomparable, "[0]": notEqualLessThan }, "JSVAL_INT_MAX - 1": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": notEqualGreaterThan, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": notEqualGreaterThan, "Math.E": notEqualGreaterThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": strictlyEqual, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "JSVAL_INT_MAX": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": notEqualGreaterThan, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": notEqualGreaterThan, "Math.E": notEqualGreaterThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualGreaterThan, "JSVAL_INT_MAX": strictlyEqual, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "JSVAL_INT_MAX + 1": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": notEqualGreaterThan, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": notEqualGreaterThan, "Math.E": notEqualGreaterThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualGreaterThan, "JSVAL_INT_MAX": notEqualGreaterThan, "JSVAL_INT_MAX + 1": strictlyEqual, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "Infinity": { "undefined": notEqualIncomparable, "null": notEqualGreaterThan, "true": notEqualGreaterThan, "false": notEqualGreaterThan, '""': notEqualGreaterThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualGreaterThan, "-0": notEqualGreaterThan, "1": notEqualGreaterThan, "Math.E": notEqualGreaterThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualGreaterThan, "JSVAL_INT_MAX": notEqualGreaterThan, "JSVAL_INT_MAX + 1": notEqualGreaterThan, "Infinity": strictlyEqual, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualIncomparable, "[0]": notEqualGreaterThan }, "-Infinity": { "undefined": notEqualIncomparable, "null": notEqualLessThan, "true": notEqualLessThan, "false": notEqualLessThan, '""': notEqualLessThan, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualLessThan, "-0": notEqualLessThan, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualLessThan, "JSVAL_INT_MIN": notEqualLessThan, "JSVAL_INT_MIN + 1": notEqualLessThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": strictlyEqual, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualLessThan, '[""]': notEqualLessThan, '["a"]': notEqualIncomparable, "[0]": notEqualLessThan }, "NaN": { "undefined": notEqualIncomparable, "null": notEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualIncomparable, '"a"': notEqualIncomparable, '"Z"': notEqualIncomparable, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualIncomparable, "{ valueOf: undefined }": notEqualIncomparable, "[]": notEqualIncomparable, '[""]': notEqualIncomparable, '["a"]': notEqualIncomparable, "[0]": notEqualIncomparable }, "{}": comparingObjectOrObjectWithValueUndefined, "{ valueOf: undefined }": comparingObjectOrObjectWithValueUndefined, "[]": { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": looselyEqual, '""': looselyEqual, '"a"': notEqualLessThan, '"Z"': notEqualLessThan, "0": looselyEqual, "-0": looselyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualLessThan, "{ valueOf: undefined }": notEqualLessThan, "[]": notEqualNorDifferent, '[""]': notEqualNorDifferent, '["a"]': notEqualLessThan, "[0]": notEqualLessThan }, '[""]': { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": looselyEqual, '""': looselyEqual, '"a"': notEqualLessThan, '"Z"': notEqualLessThan, "0": looselyEqual, "-0": looselyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualLessThan, "{ valueOf: undefined }": notEqualLessThan, "[]": notEqualNorDifferent, '[""]': notEqualNorDifferent, '["a"]': notEqualLessThan, "[0]": notEqualLessThan }, '["a"]': { "undefined": notEqualIncomparable, "null": notEqualIncomparable, "true": notEqualIncomparable, "false": notEqualIncomparable, '""': notEqualGreaterThan, '"a"': looselyEqual, '"Z"': notEqualGreaterThan, "0": notEqualIncomparable, "-0": notEqualIncomparable, "1": notEqualIncomparable, "Math.E": notEqualIncomparable, "JSVAL_INT_MIN - 1": notEqualIncomparable, "JSVAL_INT_MIN": notEqualIncomparable, "JSVAL_INT_MIN + 1": notEqualIncomparable, "JSVAL_INT_MAX - 1": notEqualIncomparable, "JSVAL_INT_MAX": notEqualIncomparable, "JSVAL_INT_MAX + 1": notEqualIncomparable, "Infinity": notEqualIncomparable, "-Infinity": notEqualIncomparable, "NaN": notEqualIncomparable, "{}": notEqualGreaterThan, "{ valueOf: undefined }": notEqualGreaterThan, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualNorDifferent, "[0]": notEqualGreaterThan }, "[0]": { "undefined": notEqualIncomparable, "null": notEqualNorDifferent, "true": notEqualLessThan, "false": looselyEqual, '""': notEqualGreaterThan, '"a"': notEqualLessThan, '"Z"': notEqualLessThan, "0": looselyEqual, "-0": looselyEqual, "1": notEqualLessThan, "Math.E": notEqualLessThan, "JSVAL_INT_MIN - 1": notEqualGreaterThan, "JSVAL_INT_MIN": notEqualGreaterThan, "JSVAL_INT_MIN + 1": notEqualGreaterThan, "JSVAL_INT_MAX - 1": notEqualLessThan, "JSVAL_INT_MAX": notEqualLessThan, "JSVAL_INT_MAX + 1": notEqualLessThan, "Infinity": notEqualLessThan, "-Infinity": notEqualGreaterThan, "NaN": notEqualIncomparable, "{}": notEqualLessThan, "{ valueOf: undefined }": notEqualLessThan, "[]": notEqualGreaterThan, '[""]': notEqualGreaterThan, '["a"]': notEqualLessThan, "[0]": notEqualNorDifferent } }; var failures = []; function fail(a, ta, b, tb, ex, ac, op) { failures.push("(" + a + " " + op + " " + b + ") wrong: " + "expected " + ex + ", got " + ac + " (types " + types[ta] + ", " + types[tb] + ")"); } var result = false; for (var i in values) { for (var j in values) { // Constants, so hoist to help JIT know that var vala = values[i], valb = values[j]; var a = vala.value(), b = valb.value(); for (var opname in orderOps) { var op = orderOps[opname]; var expect = expected[i][j].order[opname]; var failed = false; for (var iter = 0; iter < 5; iter++) { result = op(a, b); failed = failed || result !== expect; } if (failed) fail(i, vala.type, j, valb.type, expect, result, opname); } for (var opname in eqOps) { var op = eqOps[opname]; var expect = expected[i][j].eq[opname]; var failed = false; for (var iter = 0; iter < 5; iter++) { result = op(a, b); failed = failed || result !== expect; } if (failed) fail(i, vala.type, j, valb.type, expect, result, opname); } } } if (failures.length == 0) return "no failures reported!"; return "\n" + failures.join(",\n"); } testComparisons.expected = "no failures reported!"; test(testComparisons); function testCaseAbort() { var four = "4"; var r = 0; for (var i = 0; i < 5; i++) { switch (i) { case four: r += 1; break; default: r += 2; break; } } return "" + r; } testCaseAbort.expected = "10"; testCaseAbort.jitstats = { recorderAborted: 0 }; test(testCaseAbort); function testApplyCallHelper(f) { var r = []; for (var i = 0; i < 10; ++i) f.call(); r.push(x); for (var i = 0; i < 10; ++i) f.call(this); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0]); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0,1); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0,1]); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0,1,2); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2]); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3]); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3,4); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3,4]); r.push(x); for (var i = 0; i < 10; ++i) f.call(this,0,1,2,3,4,5); r.push(x); for (var i = 0; i < 10; ++i) f.apply(this,[0,1,2,3,4,5]) r.push(x); return(r.join(",")); } function testApplyCall() { var r = testApplyCallHelper(function (a0,a1,a2,a3,a4,a5,a6,a7) { x = [a0,a1,a2,a3,a4,a5,a6,a7]; }); r += testApplyCallHelper(function (a0,a1,a2,a3,a4,a5,a6,a7) { x = [a0,a1,a2,a3,a4,a5,a6,a7]; }); return r; } testApplyCall.expected = ",,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,0,1,,,,,,,0,1,,,,,,,0,1,2,,,,,,0,1,2,,,,,,0,1,2,3,,,,,0,1,2,3,,,,,0,1,2,3,4,,,,0,1,2,3,4,,,,0,1,2,3,4,5,,,0,1,2,3,4,5,," + ",,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,,0,,,,,,,,0,1,,,,,,,0,1,,,,,,,0,1,2,,,,,,0,1,2,,,,,,0,1,2,3,,,,,0,1,2,3,,,,,0,1,2,3,4,,,,0,1,2,3,4,,,,0,1,2,3,4,5,,,0,1,2,3,4,5,,"; test(testApplyCall); function testApplyUnboxHelper(f,a) { var q; for (var i = 0; i < 10; ++i) q = f.apply(f,a); return q; } function testApplyUnbox() { var f = function(x) { return x; } return [testApplyUnboxHelper(f,[1]), testApplyUnboxHelper(f,[true])].join(","); } testApplyUnbox.expected = "1,true"; test(testApplyUnbox); function testCallPick() { function g(x,a) { x.f(); } var x = []; x.f = function() { } var y = []; y.f = function() { } var z = [x,x,x,x,x,y,y,y,y,y]; for (var i = 0; i < 10; ++i) g.call(this, z[i], ""); return true; } testCallPick.expected = true; test(testCallPick); function testInvertNullAfterNegateNull() { for (var i = 0; i < 5; i++) !null; for (var i = 0; i < 5; i++) -null; return "no assertion"; } testInvertNullAfterNegateNull.expected = "no assertion"; test(testInvertNullAfterNegateNull); function testUnaryImacros() { function checkArg(x) { return 1; } var o = { valueOf: checkArg, toString: null }; var count = 0; var v = 0; for (var i = 0; i < 5; i++) v += +o + -(-o); var results = [v === 10 ? "valueOf passed" : "valueOf failed"]; o.valueOf = null; o.toString = checkArg; for (var i = 0; i < 5; i++) v += +o + -(-o); results.push(v === 20 ? "toString passed" : "toString failed"); return results.join(", "); } testUnaryImacros.expected = "valueOf passed, toString passed"; test(testUnaryImacros); function testAddAnyInconvertibleObject() { var count = 0; function toString() { ++count; if (count == 95) return {}; return "" + count; } var threw = false; try { for (var i = 0; i < 100; i++) { var o = {valueOf: undefined, toString: toString}; var q = 5 + o; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== "594") return "expected q === '594', got " + q + " (type " + typeof q + ")"; if (count !== 95) return "expected count === 95, got " + count; } if (!threw) return "expected throw with 5 + o"; // hey, a rhyme! return "pass"; } testAddAnyInconvertibleObject.expected = "pass"; testAddAnyInconvertibleObject.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testAddAnyInconvertibleObject); function testAddInconvertibleObjectAny() { var count = 0; function toString() { ++count; if (count == 95) return {}; return "" + count; } var threw = false; try { for (var i = 0; i < 100; i++) { var o = {valueOf: undefined, toString: toString}; var q = o + 5; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== "945") return "expected q === '945', got " + q + " (type " + typeof q + ")"; if (count !== 95) return "expected count === 95, got " + count; } if (!threw) return "expected throw with o + 5"; return "pass"; } testAddInconvertibleObjectAny.expected = "pass"; testAddInconvertibleObjectAny.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testAddInconvertibleObjectAny); function testAddInconvertibleObjectInconvertibleObject() { var count1 = 0; function toString1() { ++count1; if (count1 == 95) return {}; return "" + count1; } var count2 = 0; function toString2() { ++count2; if (count2 == 95) return {}; return "" + count2; } var threw = false; try { for (var i = 0; i < 100; i++) { var o1 = {valueOf: undefined, toString: toString1}; var o2 = {valueOf: undefined, toString: toString2}; var q = o1 + o2; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== "9494") return "expected q === '9494', got " + q + " (type " + typeof q + ")"; if (count1 !== 95) return "expected count1 === 95, got " + count1; if (count2 !== 94) return "expected count2 === 94, got " + count2; } if (!threw) return "expected throw with o1 + o2"; return "pass"; } testAddInconvertibleObjectInconvertibleObject.expected = "pass"; testAddInconvertibleObjectInconvertibleObject.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testAddInconvertibleObjectInconvertibleObject); function testBitOrAnyInconvertibleObject() { var count = 0; function toString() { ++count; if (count == 95) return {}; return count; } var threw = false; try { for (var i = 0; i < 100; i++) { var o = {valueOf: undefined, toString: toString}; var q = 1 | o; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== 95) return "expected q === 95, got " + q; if (count !== 95) return "expected count === 95, got " + count; } if (!threw) return "expected throw with 2 | o"; // hey, a rhyme! return "pass"; } testBitOrAnyInconvertibleObject.expected = "pass"; testBitOrAnyInconvertibleObject.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testBitOrAnyInconvertibleObject); function testBitOrInconvertibleObjectAny() { var count = 0; function toString() { ++count; if (count == 95) return {}; return count; } var threw = false; try { for (var i = 0; i < 100; i++) { var o = {valueOf: undefined, toString: toString}; var q = o | 1; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== 95) return "expected q === 95, got " + q; if (count !== 95) return "expected count === 95, got " + count; } if (!threw) return "expected throw with o | 2"; return "pass"; } testBitOrInconvertibleObjectAny.expected = "pass"; testBitOrInconvertibleObjectAny.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testBitOrInconvertibleObjectAny); function testBitOrInconvertibleObjectInconvertibleObject() { var count1 = 0; function toString1() { ++count1; if (count1 == 95) return {}; return count1; } var count2 = 0; function toString2() { ++count2; if (count2 == 95) return {}; return count2; } var threw = false; try { for (var i = 0; i < 100; i++) { var o1 = {valueOf: undefined, toString: toString1}; var o2 = {valueOf: undefined, toString: toString2}; var q = o1 | o2; } } catch (e) { threw = true; if (i !== 94) return "expected i === 94, got " + i; if (q !== 94) return "expected q === 94, got " + q; if (count1 !== 95) return "expected count1 === 95, got " + count1; if (count2 !== 94) return "expected count2 === 94, got " + count2; } if (!threw) return "expected throw with o1 | o2"; return "pass"; } testBitOrInconvertibleObjectInconvertibleObject.expected = "pass"; testBitOrInconvertibleObjectInconvertibleObject.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 93 }; test(testBitOrInconvertibleObjectInconvertibleObject); function testCaseTypeMismatchBadness() { for (var z = 0; z < 3; ++z) { switch ("") { default: case 9: break; case "": case : break; } } return "no crash"; } testCaseTypeMismatchBadness.expected = "no crash"; testCaseTypeMismatchBadness.jitstats = { recorderAborted: 0 }; test(testCaseTypeMismatchBadness); function testDoubleComparison() { for (var i = 0; i < 500000; ++i) { switch (1 / 0) { case Infinity: } } return "finished"; } testDoubleComparison.expected = "finished"; testDoubleComparison.jitstats = { sideExitIntoInterpreter: 1 }; test(testDoubleComparison); function testLirBufOOM() { var a = [ "12345678901234", "123456789012", "1234567890123456789012345678", "12345678901234567890123456789012345678901234567890123456", "f", "$", "", "f()", "(\\*)", "b()", "()", "(#)", "ABCDEFGHIJK", "ABCDEFGHIJKLM", "ABCDEFGHIJKLMNOPQ", "ABCDEFGH", "(.)", "(|)", "()$", "/()", "(.)$" ]; for (var j = 0; j < 200; ++j) { var js = "" + j; for (var i = 0; i < a.length; i++) "".match(a[i] + js) } return "ok"; } testLirBufOOM.expected = "ok"; test(testLirBufOOM); function testStringResolve() { var x = 0; for each (let d in [new String('q'), new String('q'), new String('q')]) { if (("" + (0 in d)) === "true") x++; } return x; } testStringResolve.expected = 3; test(testStringResolve); //test no multitrees assert function testGlobalMultitrees1() { (function() { for (var j = 0; j < 4; ++j) { for each (e in ['A', 1, 'A']) { } } })(); return true; } testGlobalMultitrees1.expected = true; test(testGlobalMultitrees1); var q = []; for each (b in [0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF]) { for each (let e in [{}, {}, {}, "", {}]) { b = (b | 0x40000000) + 1; q.push(b); } } function testLetWithUnstableGlobal() { return q.join(","); } testLetWithUnstableGlobal.expected = "2147483648,-1073741823,-1073741822,-1073741821,-1073741820,2147483648,-1073741823,-1073741822,-1073741821,-1073741820,2147483648,-1073741823,-1073741822,-1073741821,-1073741820"; test(testLetWithUnstableGlobal); delete b; delete q; for each (testBug474769_b in [1, 1, 1, 1.5, 1, 1]) { (function() { for each (let testBug474769_h in [0, 0, 1.4, ""]) {} })() } function testBug474769() { return testBug474769_b; } testBug474769.expected = 1; test(testBug474769); function testReverseArgTypes() { for (var j = 0; j < 4; ++j) ''.replace('', /x/); return 1; } testReverseArgTypes.expected = 1; test(testReverseArgTypes); function testBug458838() { var a = 1; function g() { var b = 0 for (var i = 0; i < 10; ++i) { b += a; } return b; } return g(); } testBug458838.expected = 10; testBug458838.jitstats = { recorderStarted: 1, recorderAborted: 0, traceCompleted: 1 }; test(testBug458838); function testInterpreterReentry() { this.__defineSetter__('x', function(){}) for (var j = 0; j < 5; ++j) { x = 3; } return 1; } testInterpreterReentry.expected = 1; test(testInterpreterReentry); function testInterpreterReentry2() { var a = false; var b = {}; var c = false; var d = {}; this.__defineGetter__('e', function(){}); for (let f in this) print(f); [1 for each (g in this) for each (h in [])] return 1; } testInterpreterReentry2.expected = 1; test(testInterpreterReentry2); function testInterpreterReentry3() { for (let i=0;i<5;++i) this["y" + i] = function(){}; this.__defineGetter__('e', function (x2) { yield; }); [1 for each (a in this) for (b in {})]; return 1; } testInterpreterReentry3.expected = 1; test(testInterpreterReentry3); function testInterpreterReentry4() { var obj = {a:1, b:1, c:1, d:1, get e() 1000 }; for (var p in obj) obj[p]; } test(testInterpreterReentry4); function testInterpreterReentry5() { var arr = [0, 1, 2, 3, 4]; arr.__defineGetter__("4", function() 1000); for (var i = 0; i < 5; i++) arr[i]; for (var p in arr) arr[p]; } test(testInterpreterReentry5); function testInterpreterReentry6() { var obj = {a:1, b:1, c:1, d:1, set e(x) { this._e = x; }}; for (var p in obj) obj[p] = "grue"; return obj._e; } testInterpreterReentry6.expected = "grue"; test(testInterpreterReentry6); function testInterpreterReentry7() { var arr = [0, 1, 2, 3, 4]; arr.__defineSetter__("4", function(x) { this._4 = x; }); for (var i = 0; i < 5; i++) arr[i] = "grue"; var tmp = arr._4; for (var p in arr) arr[p] = "bleen"; return tmp + " " + arr._4; } testInterpreterReentry7.expected = "grue bleen"; test(testInterpreterReentry7); // Bug 462027 comment 54. function testInterpreterReentery8() { var e = ; for (var j = 0; j < 4; ++j) { +[e]; } } test(testInterpreterReentery8); function testHolePushing() { var a = ["foobar", "baz"]; for (var i = 0; i < 5; i++) a = [, "overwritten", "new"]; var s = "["; for (i = 0; i < a.length; i++) { s += (i in a) ? a[i] : ""; if (i != a.length - 1) s += ","; } return s + "], " + (0 in a); } testHolePushing.expected = "[,overwritten,new], false"; test(testHolePushing); function testDeepBail1() { var y = ; for (var i = 0; i < RUNLOOP; i++) "" in y; } test(testDeepBail1); /* Array comprehension tests */ function Range(start, stop) { this.i = start; this.stop = stop; } Range.prototype = { __iterator__: function() this, next: function() { if (this.i >= this.stop) throw StopIteration; return this.i++; } }; function range(start, stop) { return new Range(start, stop); } function testArrayComp1() { return [a for (a in range(0, 10))].join(''); } testArrayComp1.expected='0123456789'; test(testArrayComp1); function testArrayComp2() { return [a + b for (a in range(0, 5)) for (b in range(0, 5))].join(''); } testArrayComp2.expected='0123412345234563456745678'; test(testArrayComp2); function testSwitchUndefined() { var x = undefined; var y = 0; for (var i = 0; i < 5; i++) { switch (x) { default: y++; } } return y; } testSwitchUndefined.expected = 5; test(testSwitchUndefined); function testGeneratorDeepBail() { function g() { yield 2; } var iterables = [[1], [], [], [], g()]; var total = 0; for (let i = 0; i < iterables.length; i++) for each (let j in iterables[i]) total += j; return total; } testGeneratorDeepBail.expected = 3; test(testGeneratorDeepBail); function testRegexpGet() { var re = /hi/; var a = []; for (let i = 0; i < 5; ++i) a.push(re.source); return a.toString(); } testRegexpGet.expected = "hi,hi,hi,hi,hi"; test(testRegexpGet); function testThrowingObjectEqUndefined() { try { var obj = { toString: function() { throw 0; } }; for (var i = 0; i < 5; i++) "" + (obj == undefined); return i === 5; } catch (e) { return "" + e; } } testThrowingObjectEqUndefined.expected = true; testThrowingObjectEqUndefined.jitstats = { sideExitIntoInterpreter: 1 }; test(testThrowingObjectEqUndefined); function x4(v) { return "" + v + v + v + v; } function testConvertibleObjectEqUndefined() { var compares = [ false, false, false, false, undefined, undefined, undefined, undefined, false, false, false, false, undefined, undefined, undefined, undefined, false, false, false, false, undefined, undefined, undefined, undefined, false, false, false, false, undefined, undefined, undefined, undefined, false, false, false, false, undefined, undefined, undefined, undefined, ]; var count = 0; var obj = { valueOf: function() { count++; return 1; } }; var results = compares.map(function(v) { return "unwritten"; }); for (var i = 0, sz = compares.length; i < sz; i++) results[i] = compares[i] == obj; return results.join("") + count; } testConvertibleObjectEqUndefined.expected = x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + x4(false) + "20"; testConvertibleObjectEqUndefined.jitstats = { sideExitIntoInterpreter: 3 }; test(testConvertibleObjectEqUndefined); function testUndefinedPropertyAccess() { var x = [1,2,3]; var y = {}; var a = { foo: 1 }; y.__proto__ = x; var z = [x, x, x, y, y, y, y, a, a, a]; var s = ""; for (var i = 0; i < z.length; ++i) s += z[i].foo; return s; } testUndefinedPropertyAccess.expected = "undefinedundefinedundefinedundefinedundefinedundefinedundefined111"; testUndefinedPropertyAccess.jitstats = { traceCompleted: 3 }; test(testUndefinedPropertyAccess); q = ""; function g() { q += "g"; } function h() { q += "h"; } a = [g, g, g, g, h]; for (i=0; i<5; i++) { f = a[i]; f(); } function testRebranding() { return q; } testRebranding.expected = "ggggh"; test(testRebranding); delete q; delete g; delete h; delete a; delete f; function testLambdaCtor() { var a = []; for (var x = 0; x < RUNLOOP; ++x) { var f = function(){}; a[a.length] = new f; } // This prints false until the upvar2 bug is fixed: // print(a[HOTLOOP].__proto__ !== a[HOTLOOP-1].__proto__); // Assert that the last f was properly constructed. return a[RUNLOOP-1].__proto__ === f.prototype; } testLambdaCtor.expected = true; test(testLambdaCtor); function testNonStubGetter() { let ([] = false) { (this.watch("x", /a/g)); }; (function () { (eval("(function(){for each (x in [1, 2, 2]);});"))(); })(); this.unwatch("x"); return "ok"; } testNonStubGetter.expected = "ok"; test(testNonStubGetter); function testString() { var q; for (var i = 0; i <= RUNLOOP; ++i) { q = []; q.push(String(void 0)); q.push(String(true)); q.push(String(5)); q.push(String(5.5)); q.push(String("5")); q.push(String([5])); } return q.join(","); } testString.expected = "undefined,true,5,5.5,5,5"; testString.jitstats = { recorderStarted: 1, sideExitIntoInterpreter: 1 }; test(testString); function testToStringBeforeValueOf() { var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } }; var a = []; for (var i = 0; i < 10; i++) a.push(String(o)); return a.join(","); } testToStringBeforeValueOf.expected = "s,s,s,s,s,s,s,s,s,s"; testToStringBeforeValueOf.jitstats = { recorderStarted: 1, sideExitIntoInterpreter: 1 }; test(testToStringBeforeValueOf); function testNullToString() { var a = []; for (var i = 0; i < 10; i++) a.push(String(null)); for (i = 0; i < 10; i++) { var t = typeof a[i]; if (t != "string") a.push(t); } return a.join(","); } testNullToString.expected = "null,null,null,null,null,null,null,null,null,null"; testNullToString.jitstats = { recorderStarted: 2, sideExitIntoInterpreter: 2, recorderAborted: 0 }; test(testNullToString); function testAddNull() { var rv; for (var x = 0; x < HOTLOOP + 1; ++x) rv = null + [,,]; return rv; } testAddNull.expected = "null,"; testAddNull.jitstats = { recorderStarted: 1, sideExitIntoInterpreter: 1, recorderAborted: 0 }; test(testAddNull); function testClosures() { function MyObject(id) { var thisObject = this; this.id = id; this.toString = str; function str() { return "" + this.id + thisObject.id; } } var a = []; for (var i = 0; i < 5; i++) a.push(new MyObject(i)); return a.toString(); } testClosures.expected = "00,11,22,33,44"; test(testClosures); function testMoreClosures() { var f = {}, max = 3; var hello = function(n) { function howdy() { return n * n } f.test = howdy; }; for (var i = 0; i <= max; i++) hello(i); return f.test(); } testMoreClosures.expected = 9; test(testMoreClosures); function testLambdaInitedVar() { var jQuery = function (a, b) { return jQuery && jQuery.length; } return jQuery(); } testLambdaInitedVar.expected = 2; test(testLambdaInitedVar); function testNestedEscapingLambdas() { try { return (function() { var a = [], r = []; function setTimeout(f, t) { a.push(f); } function runTimeouts() { for (var i = 0; i < a.length; i++) a[i](); } var $foo = "#nothiddendiv"; setTimeout(function(){ r.push($foo); setTimeout(function(){ r.push($foo); }, 100); }, 100); runTimeouts(); return r.join(""); })(); } catch (e) { return e; } } testNestedEscapingLambdas.expected = "#nothiddendiv#nothiddendiv"; test(testNestedEscapingLambdas); function testPropagatedFunArgs() { var win = this; var res = [], q = []; function addEventListener(name, func, flag) { q.push(func); } var pageInfo, obs; addEventListener("load", handleLoad, true); var observer = { observe: function(win, topic, data) { // obs.removeObserver(observer, "page-info-dialog-loaded"); handlePageInfo(); } }; function handleLoad() { pageInfo = { toString: function() { return "pageInfo"; } }; obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } }; obs.addObserver(observer, "page-info-dialog-loaded", false); } function handlePageInfo() { res.push(pageInfo); function $(aId) { res.push(pageInfo); }; var feedTab = $("feedTab"); } q[0](); return res.join(','); } testPropagatedFunArgs.expected = "pageInfo,pageInfo"; test(testPropagatedFunArgs); // Second testPropagatedFunArgs test -- this is a crash-test. (function () { var escapee; function testPropagatedFunArgs() { const magic = 42; var win = this; var res = [], q = []; function addEventListener(name, func, flag) { q.push(func); } var pageInfo = "pageInfo", obs; addEventListener("load", handleLoad, true); var observer = { observe: function(win, topic, data) { // obs.removeObserver(observer, "page-info-dialog-loaded"); handlePageInfo(); } }; function handleLoad() { //pageInfo = { toString: function() { return "pageInfo"; } }; obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } }; obs.addObserver(observer, "page-info-dialog-loaded", false); } function handlePageInfo() { res.push(pageInfo); function $(aId) { function notSafe() { return magic; } notSafe(); res.push(pageInfo); }; var feedTab = $("feedTab"); } escapee = q[0]; return res.join(','); } testPropagatedFunArgs(); escapee(); })(); function testStringLengthNoTinyId() { var x = "unset"; var t = new String(""); for (var i = 0; i < 5; i++) x = t["-1"]; var r = "t['-1'] is " + x; t["-1"] = "foo"; r += " when unset, '" + t["-1"] + "' when set"; return r; } testStringLengthNoTinyId.expected = "t['-1'] is undefined when unset, 'foo' when set"; test(testStringLengthNoTinyId); function testLengthInString() { var s = new String(); var res = "length" in s; for (var i = 0; i < 5; i++) res = res && ("length" in s); res = res && s.hasOwnProperty("length"); for (var i = 0; i < 5; i++) res = res && s.hasOwnProperty("length"); return res; } testLengthInString.expected = true; test(testLengthInString); function testSlowArrayLength() { var counter = 0; var a = []; a[10000000 - 1] = 0; for (var i = 0; i < a.length; i++) counter++; return counter; } testSlowArrayLength.expected = 10000000; testSlowArrayLength.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 1 }; test(testSlowArrayLength); function testObjectLength() { var counter = 0; var a = {}; a.length = 10000000; for (var i = 0; i < a.length; i++) counter++; return counter; } testObjectLength.expected = 10000000; testObjectLength.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 1 }; test(testObjectLength); function testChangingObjectWithLength() { var obj = { length: 10 }; var dense = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; var slow = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; slow.slow = 5; /* * The elements of objs constitute a De Bruijn sequence repeated 4x to trace * and run native code for every object and transition. */ var objs = [obj, obj, obj, obj, obj, obj, obj, obj, dense, dense, dense, dense, obj, obj, obj, obj, slow, slow, slow, slow, dense, dense, dense, dense, dense, dense, dense, dense, slow, slow, slow, slow, slow, slow, slow, slow, obj, obj, obj, obj]; var counter = 0; for (var i = 0, sz = objs.length; i < sz; i++) { var o = objs[i]; for (var j = 0; j < o.length; j++) counter++; } return counter; } testChangingObjectWithLength.expected = 400; testChangingObjectWithLength.jitstats = { recorderAborted: 0, sideExitIntoInterpreter: 15 // empirically determined }; test(testChangingObjectWithLength); function testNEWINIT() { var a; for (var i = 0; i < 10; ++i) a = [{}]; return uneval(a); } testNEWINIT.expected = "[{}]"; test(testNEWINIT); function testNEWINIT_DOUBLE() { for (var z = 0; z < 2; ++z) { ({ 0.1: null })} return "ok"; } testNEWINIT_DOUBLE.expected = "ok"; test(testNEWINIT_DOUBLE); function testIntOverflow() { // int32_max - 7 var ival = 2147483647 - 7; for (var i = 0; i < 30; i++) { ival += 2; } return (ival < 2147483647); } testIntOverflow.expected = false; testIntOverflow.jitstats = { recorderStarted: 2, recorderAborted: 0, traceCompleted: 2, traceTriggered: 2, }; test(testIntOverflow); function testIntUnderflow() { // int32_min + 8 var ival = -2147483648 + 8; for (var i = 0; i < 30; i++) { ival -= 2; } return (ival > -2147483648); } testIntUnderflow.expected = false; testIntUnderflow.jitstats = { recorderStarted: 2, recorderAborted: 0, traceCompleted: 2, traceTriggered: 2, }; test(testIntUnderflow); function testCALLELEM() { function f() { return 5; } function g() { return 7; } var x = [f,f,f,f,g]; var y = 0; for (var i = 0; i < 5; ++i) y = x[i](); return y; } testCALLELEM.expected = 7; test(testCALLELEM); function testNewString() { var o = { toString: function() { return "string"; } }; var r = []; for (var i = 0; i < 5; i++) r.push(typeof new String(o)); for (var i = 0; i < 5; i++) r.push(typeof new String(3)); for (var i = 0; i < 5; i++) r.push(typeof new String(2.5)); for (var i = 0; i < 5; i++) r.push(typeof new String("string")); for (var i = 0; i < 5; i++) r.push(typeof new String(null)); for (var i = 0; i < 5; i++) r.push(typeof new String(true)); for (var i = 0; i < 5; i++) r.push(typeof new String(undefined)); return r.length === 35 && r.every(function(v) { return v === "object"; }); } testNewString.expected = true; testNewString.jitstats = { recorderStarted: 7, recorderAborted: 0, traceCompleted: 7, sideExitIntoInterpreter: 7 }; test(testNewString); function testWhileObjectOrNull() { try { for (var i = 0; i < 3; i++) { var o = { p: { p: null } }; while (o.p) o = o.p; } return "pass"; } catch (e) { return "threw exception: " + e; } } testWhileObjectOrNull.expected = "pass"; test(testWhileObjectOrNull); function testDenseArrayProp() { [].__proto__.x = 1; ({}).__proto__.x = 2; var a = [[],[],[],({}).__proto__]; for (var i = 0; i < a.length; ++i) uneval(a[i].x); delete [].__proto__.x; delete ({}).__proto__.x; return "ok"; } testDenseArrayProp.expected = "ok"; test(testDenseArrayProp); function testNewWithNonNativeProto() { function f() { } var a = f.prototype = []; for (var i = 0; i < 5; i++) var o = new f(); return Object.getPrototypeOf(o) === a && o.splice === Array.prototype.splice; } testNewWithNonNativeProto.expected = true; testNewWithNonNativeProto.jitstats = { recorderStarted: 1, recorderAborted: 0, sideExitIntoInterpreter: 1 }; test(testNewWithNonNativeProto); function testLengthOnNonNativeProto() { var o = {}; o.__proto__ = [3]; for (var j = 0; j < 5; j++) o[0]; var o2 = {}; o2.__proto__ = []; for (var j = 0; j < 5; j++) o2.length; function foo() { } foo.__proto__ = []; for (var j = 0; j < 5; j++) foo.length; return "no assertion"; } testLengthOnNonNativeProto.expected = "no assertion"; test(testLengthOnNonNativeProto); function testDeepPropertyShadowing() { function h(node) { var x = 0; while (node) { x++; node = node.parent; } return x; } var tree = {__proto__: {__proto__: {parent: null}}}; h(tree); h(tree); tree.parent = {}; assertEq(h(tree), 2); } test(testDeepPropertyShadowing); // Complicated whitebox test for bug 487845. function testGlobalShapeChangeAfterDeepBail() { function f(name) { this[name] = 1; // may change global shape for (var i = 0; i < 4; i++) ; // MonitorLoopEdge eventually triggers assertion } // When i==3, deep-bail, then change global shape enough times to exhaust // the array of GlobalStates. var arr = [[], [], [], ["bug0", "bug1", "bug2", "bug3", "bug4"]]; for (var i = 0; i < arr.length; i++) arr[i].forEach(f); } test(testGlobalShapeChangeAfterDeepBail); for (let i = 0; i < 5; i++) delete this["bug" + i]; function testFunctionIdentityChange() { function a() {} function b() {} var o = { a: a, b: b }; for (var prop in o) { for (var i = 0; i < 1000; i++) o[prop](); } return true; } testFunctionIdentityChange.expected = true; testFunctionIdentityChange.jitstats = { recorderStarted: 2, traceCompleted: 2, sideExitIntoInterpreter: 3 }; test(testFunctionIdentityChange); function testStringObjectLength() { var x = new String("foo"), y = 0; for (var i = 0; i < 10; ++i) y = x.length; return y; } testStringObjectLength.expected = 3; test(testStringObjectLength); var _quit; function testNestedDeepBail() { _quit = false; function loop() { for (var i = 0; i < 4; i++) ; } loop(); function f() { loop(); _quit = true; } var stk = [[1], [], [], [], []]; while (!_quit) stk.pop().forEach(f); } test(testNestedDeepBail); delete _quit; function testSlowNativeCtor() { for (var i = 0; i < 4; i++) new Date().valueOf(); } test(testSlowNativeCtor); function testSlowNativeBail() { var a = ['0', '1', '2', '3', '+']; try { for (var i = 0; i < a.length; i++) new RegExp(a[i]); } catch (exc) { assertEq(""+exc.stack.match(/^RegExp/), "RegExp"); } } test(testSlowNativeBail); /* Test the proper operation of the left shift operator. This is especially * important on ARM as an explicit mask is required at the native instruction * level. */ function testShiftLeft() { var r = []; var i = 0; var j = 0; var shifts = [0,1,7,8,15,16,23,24,31]; /* Samples from the simple shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = 1 << shifts[i]; /* Samples outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = 1 << (shifts[i] + 32); /* Samples far outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = 1 << (shifts[i] + 224); for (i = 0; i < shifts.length; i++) r[j++] = 1 << (shifts[i] + 256); return r.join(","); } testShiftLeft.expected = "1,2,128,256,32768,65536,8388608,16777216,-2147483648,"+ "1,2,128,256,32768,65536,8388608,16777216,-2147483648,"+ "1,2,128,256,32768,65536,8388608,16777216,-2147483648,"+ "1,2,128,256,32768,65536,8388608,16777216,-2147483648"; test(testShiftLeft); /* Test the proper operation of the logical right shift operator. This is * especially important on ARM as an explicit mask is required at the native * instruction level. */ function testShiftRightLogical() { var r = []; var i = 0; var j = 0; var shifts = [0,1,7,8,15,16,23,24,31]; /* Samples from the simple shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >>> shifts[i]; /* Samples outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >>> (shifts[i] + 32); /* Samples far outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >>> (shifts[i] + 224); for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >>> (shifts[i] + 256); return r.join(","); } /* Note: Arguments to the ">>>" operator are converted to unsigned 32-bit * integers during evaluation. As a result, -2147483648 >>> 0 evaluates to the * unsigned interpretation of the same value, which is 2147483648. */ testShiftRightLogical.expected = "2147483648,1073741824,16777216,8388608,65536,32768,256,128,1,"+ "2147483648,1073741824,16777216,8388608,65536,32768,256,128,1,"+ "2147483648,1073741824,16777216,8388608,65536,32768,256,128,1,"+ "2147483648,1073741824,16777216,8388608,65536,32768,256,128,1"; test(testShiftRightLogical); /* Test the proper operation of the arithmetic right shift operator. This is * especially important on ARM as an explicit mask is required at the native * instruction level. */ function testShiftRightArithmetic() { var r = []; var i = 0; var j = 0; var shifts = [0,1,7,8,15,16,23,24,31]; /* Samples from the simple shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >> shifts[i]; /* Samples outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >> (shifts[i] + 32); /* Samples far outside the normal shift range. */ for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >> (shifts[i] + 224); for (i = 0; i < shifts.length; i++) r[j++] = -2147483648 >> (shifts[i] + 256); return r.join(","); } testShiftRightArithmetic.expected = "-2147483648,-1073741824,-16777216,-8388608,-65536,-32768,-256,-128,-1,"+ "-2147483648,-1073741824,-16777216,-8388608,-65536,-32768,-256,-128,-1,"+ "-2147483648,-1073741824,-16777216,-8388608,-65536,-32768,-256,-128,-1,"+ "-2147483648,-1073741824,-16777216,-8388608,-65536,-32768,-256,-128,-1"; test(testShiftRightArithmetic); function testStringConstructorWithExtraArg() { for (let i = 0; i < 5; ++i) new String(new String(), 2); return "ok"; } testStringConstructorWithExtraArg.expected = "ok"; test(testStringConstructorWithExtraArg); function testConstructorBail() { for (let i = 0; i < 5; ++i) new Number(/x/); } test(testConstructorBail); function testNewArrayCount() { var a = []; for (var i = 0; i < 5; i++) a = [0]; assertEq(a.__count__, 1); for (var i = 0; i < 5; i++) a = [0, , 2]; assertEq(a.__count__, 2); } test(testNewArrayCount); function testNewArrayCount2() { var x = 0; for (var i = 0; i < 10; ++i) x = new Array(1,2,3).__count__; return x; } testNewArrayCount2.expected = 3; test(testNewArrayCount2); /***************************************************************************** * * * _____ _ _ _____ ______ _____ _______ * * |_ _| \ | |/ ____| ____| __ \__ __| * * | | | \| | (___ | |__ | |__) | | | * * | | | . ` |\___ \| __| | _ / | | * * _| |_| |\ |____) | |____| | \ \ | | * * |_____|_| \_|_____/|______|_| \_\ |_| * * * * * * _______ ______ _____ _______ _____ * * |__ __| ____|/ ____|__ __/ ____| * * | | | |__ | (___ | | | (___ * * | | | __| \___ \ | | \___ \ * * | | | |____ ____) | | | ____) | * * |_| |______|_____/ |_| |_____/ * * * * * * ____ ______ ______ ____ _____ ______ _ _ ______ _____ ______ * * | _ \| ____| ____/ __ \| __ \| ____| | | | | ____| __ \| ____| * * | |_) | |__ | |__ | | | | |__) | |__ | |__| | |__ | |__) | |__ * * | _ <| __| | __|| | | | _ /| __| | __ | __| | _ /| __| * * | |_) | |____| | | |__| | | \ \| |____ | | | | |____| | \ \| |____ * * |____/|______|_| \____/|_| \_\______| |_| |_|______|_| \_\______| * * * *****************************************************************************/ // math-trace-tests.js is a separate file here. // MANDELBROT STUFF deleted /***************************************************************************** * _ _ ____ _ __ ____ _____ ______ * * | \ | |/ __ \ | \/ |/ __ \| __ \| ____| * * | \| | | | | | \ / | | | | |__) | |__ * * | . ` | | | | | |\/| | | | | _ /| __| * * | |\ | |__| | | | | | |__| | | \ \| |____ * * |_| \_|\____/ |_| |_|\____/|_| \_\______| * * * * _______ ______ _____ _______ _____ * * |__ __| ____|/ ____|__ __/ ____| * * | | | |__ | (___ | | | (___ * * | | | __| \___ \ | | \___ \ * * | | | |____ ____) | | | ____) | * * |_| |______|_____/ |_| |_____/ * * * * ______ _______ ______ _____ _ _ ______ _____ ______ _ * * /\ | ____|__ __| ____| __ \ | | | | ____| __ \| ____| | * * / \ | |__ | | | |__ | |__) | | |__| | |__ | |__) | |__ | | * * / /\ \ | __| | | | __| | _ / | __ | __| | _ /| __| | | * * / ____ \| | | | | |____| | \ \ | | | | |____| | \ \| |____|_| * * /_/ \_\_| |_| |______|_| \_\ |_| |_|______|_| \_\______(_) * * * *****************************************************************************/ /* NOTE: Keep this test last, since it screws up all for...in loops after it. */ function testGlobalProtoAccess() { return "ok"; } this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; } testGlobalProtoAccess.expected = "ok"; test(testGlobalProtoAccess); jit(false); /* Keep these at the end so that we can see the summary after the trace-debug spew. */ if (gReportSummary) { print("\npassed:", passes.length && passes.join(",")); print("\nFAILED:", fails.length && fails.join(",")); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-12.js0000644000175000017500000000332714433667662025623 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-12.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace [1, 2, 3]'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { [1, 2, 3]; } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-08.js0000644000175000017500000000335114433667662025625 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-08.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace new Array(1, 2, 3)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { new Array(1, 2, 3); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-471635.js0000644000175000017500000000301514433667662025371 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-471635.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 471635; var summary = 'TM: trace js shell print()'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); (function(){ for (var i = 1; i < 20; ++i) { print("#"); } })(); var recorderStarted; var recorderAborted; var traceCompleted; if (this.tracemonkey) { recorderStarted = this.tracemonkey.recorderStarted; recorderAborted = this.tracemonkey.recorderAborted; traceCompleted = this.tracemonkey.traceCompleted; } jit(false); if (this.tracemonkey) { expect = 'recorderStarted=1, recorderAborted=0, traceCompleted=1'; actual = 'recorderStarted=' + recorderStarted + ', recorderAborted=' + recorderAborted + ', traceCompleted=' + traceCompleted; } else { expect = actual = 'Test skipped due to lack of tracemonkey jitstats object.'; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-10.js0000644000175000017500000000331314433667662025614 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-10.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace [1]'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { [1]; } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-02.js0000644000175000017500000000332514433667662025620 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace Array(1)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { Array(1); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-452498-01.js0000644000175000017500000002262214433667662025622 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2: jit heavyweight functions'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); function complex(aReal, aImag) { this.r = aReal; this.i = aImag; this.square = function() { return new complex(this.r * this.r - this.i * this.i, 2 * this.r * this.i); } this.dist = function() { return Math.sqrt(this.r * this.r + this.i * this.i); } this.add = function(aComplex) { return new complex(this.r + aComplex.r, this.i + aComplex.i); } } function mandelbrotValueOO (aC, aIterMax) { let Z = new complex(0.0, 0.0); for (var iter = 0; iter < aIterMax; iter++) { Z = Z.square().add(aC); if (Z.r * Z.r + Z.i * Z.i > 256) { break; } } return iter; } function f(trace) { jit(trace); var start = Date.now(); const width = 60; const height = 60; const max_iters = 50; var output = []; for (let img_x = 0; img_x < width; img_x++) { for (let img_y = 0; img_y < height; img_y++) { let C = new complex(-2 + (img_x / width) * 3, -1.5 + (img_y / height) * 3); var res = mandelbrotValueOO(C, max_iters); if (output.length > 0 && output[output.length -1][0] == res) { output[output.length-1][1]++; } else { output.push([res, 1]); } } } jit(false); const reference = "[[2, 6], [3, 17], [4, 6], [5, 1], [50, 1], [5, 1], [4, 6], [3, 17], [2, 10], [3, 17], [4, 6], [5, 1], [6, 1], [50, 1], [6, 1], [5, 1], [4, 6], [3, 17], [2, 8], [3, 17], [4, 6], [5, 2], [6, 1], [50, 1], [6, 1], [5, 2], [4, 6], [3, 17], [2, 6], [3, 17], [4, 6], [5, 2], [6, 1], [7, 1], [50, 1], [7, 1], [6, 1], [5, 2], [4, 6], [3, 17], [2, 4], [3, 17], [4, 7], [5, 2], [6, 1], [8, 1], [50, 1], [8, 1], [6, 1], [5, 2], [4, 7], [3, 17], [2, 2], [3, 17], [4, 7], [5, 3], [6, 1], [9, 1], [50, 1], [9, 1], [6, 1], [5, 3], [4, 7], [3, 17], [2, 1], [3, 16], [4, 7], [5, 3], [6, 2], [8, 1], [50, 1], [8, 1], [6, 2], [5, 3], [4, 7], [3, 32], [4, 7], [5, 4], [6, 1], [7, 1], [8, 1], [50, 1], [8, 1], [7, 1], [6, 1], [5, 4], [4, 7], [3, 31], [4, 7], [5, 3], [6, 2], [7, 1], [8, 1], [50, 1], [8, 1], [7, 1], [6, 2], [5, 3], [4, 7], [3, 30], [4, 7], [5, 4], [6, 2], [7, 1], [8, 1], [50, 1], [8, 1], [7, 1], [6, 2], [5, 4], [4, 7], [3, 28], [4, 7], [5, 4], [6, 2], [7, 1], [8, 1], [10, 1], [50, 1], [10, 1], [8, 1], [7, 1], [6, 2], [5, 4], [4, 7], [3, 26], [4, 7], [5, 4], [6, 2], [7, 1], [8, 1], [9, 1], [11, 1], [50, 1], [11, 1], [9, 1], [8, 1], [7, 1], [6, 2], [5, 4], [4, 7], [3, 25], [4, 6], [5, 3], [6, 3], [7, 1], [8, 1], [18, 1], [13, 1], [15, 1], [50, 1], [15, 1], [13, 1], [18, 1], [8, 1], [7, 1], [6, 3], [5, 3], [4, 6], [3, 24], [4, 7], [5, 2], [6, 2], [7, 3], [8, 1], [10, 1], [14, 1], [50, 3], [14, 1], [10, 1], [8, 1], [7, 3], [6, 2], [5, 2], [4, 7], [3, 23], [4, 6], [5, 3], [7, 1], [8, 1], [9, 1], [8, 2], [10, 1], [11, 1], [15, 1], [50, 3], [15, 1], [11, 1], [10, 1], [8, 2], [9, 1], [8, 1], [7, 1], [5, 3], [4, 6], [3, 22], [4, 7], [5, 2], [6, 1], [7, 1], [14, 1], [16, 1], [11, 1], [10, 1], [12, 1], [20, 1], [23, 1], [46, 1], [50, 1], [46, 1], [23, 1], [20, 1], [12, 1], [10, 1], [11, 1], [16, 1], [14, 1], [7, 1], [6, 1], [5, 2], [4, 7], [3, 20], [4, 7], [5, 3], [6, 1], [7, 1], [8, 1], [10, 1], [17, 1], [16, 1], [20, 1], [50, 7], [20, 1], [16, 1], [17, 1], [10, 1], [8, 1], [7, 1], [6, 1], [5, 3], [4, 7], [3, 19], [4, 7], [5, 3], [6, 2], [7, 1], [10, 1], [21, 1], [50, 11], [21, 1], [10, 1], [7, 1], [6, 2], [5, 3], [4, 7], [3, 18], [4, 7], [5, 4], [6, 1], [7, 1], [8, 1], [9, 1], [13, 1], [25, 1], [50, 9], [25, 1], [13, 1], [9, 1], [8, 1], [7, 1], [6, 1], [5, 4], [4, 7], [3, 17], [4, 7], [5, 4], [6, 1], [7, 1], [8, 1], [14, 2], [50, 11], [14, 2], [8, 1], [7, 1], [6, 1], [5, 4], [4, 7], [3, 16], [4, 7], [5, 4], [6, 2], [7, 1], [8, 1], [11, 1], [36, 1], [50, 11], [36, 1], [11, 1], [8, 1], [7, 1], [6, 2], [5, 4], [4, 7], [3, 15], [4, 7], [5, 4], [6, 2], [7, 1], [8, 1], [9, 1], [14, 1], [50, 11], [14, 1], [9, 1], [8, 1], [7, 1], [6, 2], [5, 4], [4, 7], [3, 14], [4, 7], [5, 4], [6, 3], [7, 1], [8, 1], [9, 1], [12, 1], [26, 1], [50, 9], [26, 1], [12, 1], [9, 1], [8, 1], [7, 1], [6, 3], [5, 4], [4, 7], [3, 13], [4, 7], [5, 4], [6, 2], [7, 2], [8, 1], [9, 1], [10, 1], [15, 1], [50, 9], [15, 1], [10, 1], [9, 1], [8, 1], [7, 2], [6, 2], [5, 4], [4, 7], [3, 12], [4, 7], [5, 4], [6, 3], [7, 1], [8, 2], [9, 1], [10, 1], [12, 1], [16, 1], [50, 7], [16, 1], [12, 1], [10, 1], [9, 1], [8, 2], [7, 1], [6, 3], [5, 4], [4, 7], [3, 11], [4, 6], [5, 4], [6, 3], [7, 1], [8, 2], [9, 1], [11, 1], [12, 1], [14, 1], [17, 1], [23, 1], [34, 1], [50, 3], [34, 1], [23, 1], [17, 1], [14, 1], [12, 1], [11, 1], [9, 1], [8, 2], [7, 1], [6, 3], [5, 4], [4, 6], [3, 10], [4, 7], [5, 3], [6, 2], [7, 2], [8, 1], [9, 1], [22, 1], [12, 1], [50, 1], [25, 1], [50, 11], [25, 1], [50, 1], [12, 1], [22, 1], [9, 1], [8, 1], [7, 2], [6, 2], [5, 3], [4, 7], [3, 9], [4, 6], [5, 4], [6, 1], [7, 1], [8, 2], [9, 1], [14, 1], [50, 1], [21, 1], [50, 15], [21, 1], [50, 1], [14, 1], [9, 1], [8, 2], [7, 1], [6, 1], [5, 4], [4, 6], [3, 8], [4, 7], [5, 3], [6, 2], [9, 1], [14, 1], [13, 1], [11, 1], [13, 1], [26, 1], [50, 17], [26, 1], [13, 1], [11, 1], [13, 1], [14, 1], [9, 1], [6, 2], [5, 3], [4, 7], [3, 7], [4, 6], [5, 4], [6, 1], [7, 1], [9, 1], [49, 1], [43, 1], [50, 23], [43, 1], [49, 1], [9, 1], [7, 1], [6, 1], [5, 4], [4, 6], [3, 7], [4, 5], [5, 4], [6, 2], [7, 1], [9, 1], [13, 1], [50, 25], [13, 1], [9, 1], [7, 1], [6, 2], [5, 4], [4, 5], [3, 6], [4, 6], [5, 3], [6, 2], [7, 2], [9, 1], [11, 1], [17, 1], [50, 23], [17, 1], [11, 1], [9, 1], [7, 2], [6, 2], [5, 3], [4, 6], [3, 5], [4, 5], [5, 3], [6, 3], [7, 1], [8, 1], [9, 1], [50, 1], [26, 1], [50, 23], [26, 1], [50, 1], [9, 1], [8, 1], [7, 1], [6, 3], [5, 3], [4, 5], [3, 5], [4, 4], [5, 3], [6, 3], [7, 1], [8, 2], [10, 1], [21, 1], [50, 25], [21, 1], [10, 1], [8, 2], [7, 1], [6, 3], [5, 3], [4, 4], [3, 5], [4, 4], [5, 2], [6, 3], [7, 1], [12, 1], [9, 1], [10, 1], [11, 1], [50, 27], [11, 1], [10, 1], [9, 1], [12, 1], [7, 1], [6, 3], [5, 2], [4, 4], [3, 5], [4, 3], [5, 2], [6, 2], [7, 2], [9, 1], [42, 1], [15, 1], [23, 1], [14, 1], [50, 27], [14, 1], [23, 1], [15, 1], [42, 1], [9, 1], [7, 2], [6, 2], [5, 2], [4, 3], [3, 5], [4, 3], [5, 1], [6, 1], [20, 1], [9, 1], [8, 1], [9, 1], [10, 1], [16, 1], [50, 33], [16, 1], [10, 1], [9, 1], [8, 1], [9, 1], [20, 1], [6, 1], [5, 1], [4, 3], [3, 5], [4, 3], [5, 1], [6, 1], [9, 1], [13, 1], [12, 1], [11, 1], [38, 1], [25, 1], [50, 33], [25, 1], [38, 1], [11, 1], [12, 1], [13, 1], [9, 1], [6, 1], [5, 1], [4, 3], [3, 5], [4, 3], [5, 2], [6, 1], [7, 1], [10, 1], [24, 1], [25, 1], [50, 35], [25, 1], [24, 1], [10, 1], [7, 1], [6, 1], [5, 2], [4, 3], [3, 5], [4, 4], [5, 1], [6, 1], [7, 1], [11, 2], [13, 1], [19, 1], [50, 33], [19, 1], [13, 1], [11, 2], [7, 1], [6, 1], [5, 1], [4, 4], [3, 5], [4, 4], [5, 2], [6, 1], [50, 1], [8, 2], [17, 1], [19, 1], [35, 1], [14, 1], [24, 1], [50, 25], [24, 1], [14, 1], [35, 1], [19, 1], [17, 1], [8, 2], [50, 1], [6, 1], [5, 2], [4, 4], [3, 5], [4, 5], [5, 2], [6, 2], [7, 1], [8, 1], [9, 2], [11, 1], [38, 1], [50, 25], [38, 1], [11, 1], [9, 2], [8, 1], [7, 1], [6, 2], [5, 2], [4, 5], [3, 6], [4, 4], [5, 3], [6, 2], [7, 2], [8, 1], [9, 1], [15, 1], [50, 25], [15, 1], [9, 1], [8, 1], [7, 2], [6, 2], [5, 3], [4, 4], [3, 7], [4, 5], [5, 3], [6, 3], [7, 1], [9, 1], [42, 1], [21, 1], [50, 23], [21, 1], [42, 1], [9, 1], [7, 1], [6, 3], [5, 3], [4, 5], [3, 8], [4, 5], [5, 3], [6, 2], [7, 1], [8, 1], [9, 1], [13, 1], [50, 23], [13, 1], [9, 1], [8, 1], [7, 1], [6, 2], [5, 3], [4, 5], [3, 9], [4, 6], [5, 3], [6, 2], [7, 1], [9, 1], [14, 1], [50, 23], [14, 1], [9, 1], [7, 1], [6, 2], [5, 3], [4, 6], [3, 10], [4, 6], [5, 3], [6, 1], [7, 1], [9, 1], [16, 1], [50, 2], [35, 1], [50, 8], [13, 1], [50, 8], [35, 1], [50, 2], [16, 1], [9, 1], [7, 1], [6, 1], [5, 3], [4, 6], [3, 12], [4, 6], [5, 2], [6, 1], [19, 1], [16, 1], [17, 1], [25, 1], [21, 1], [13, 1], [18, 1], [50, 6], [11, 1], [9, 1], [11, 1], [50, 6], [18, 1], [13, 1], [21, 1], [25, 1], [17, 1], [16, 1], [19, 1], [6, 1], [5, 2], [4, 6], [3, 14], [4, 5], [5, 3], [6, 1], [8, 1], [16, 1], [10, 1], [8, 2], [11, 1], [50, 1], [16, 1], [15, 1], [32, 1], [29, 1], [9, 1], [8, 1], [7, 1], [8, 1], [9, 1], [29, 1], [32, 1], [15, 1], [16, 1], [50, 1], [11, 1], [8, 2], [10, 1], [16, 1], [8, 1], [6, 1], [5, 3], [4, 5], [3, 15], [4, 6], [5, 3], [6, 4], [7, 1], [20, 1], [19, 1], [9, 3], [7, 3], [6, 1], [7, 3], [9, 3], [19, 1], [20, 1], [7, 1], [6, 4], [5, 3], [4, 6], [3, 16], [4, 7], [5, 4], [6, 3], [7, 1], [6, 13], [7, 1], [6, 3], [5, 4], [4, 7], [3, 18], [4, 7], [5, 27], [4, 7], [3, 20], [4, 9], [5, 21], [4, 9], [3, 23], [4, 12], [5, 11], [4, 12], [3, 26], [4, 33], [3, 29], [4, 29], [3, 33], [4, 25], [3, 38], [4, 19], [3, 20], [2, 1], [3, 26], [4, 7], [3, 26], [2, 2], [3, 57], [2, 1]]"; reportCompare(reference, output.toSource(), summary + ': correctness jit=' + trace); return (Date.now() - start); } var timenonjit = f(false); var timejit = f(true); expect = true; actual = timejit < timenonjit/2; print('time nonjit: ' + timenonjit + ', time jit: ' + timejit); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-469927.js0000644000175000017500000000236614433667662025414 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-469927.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 469927; var summary = 'TM: jit should not slow down short loop with let'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function letitbe() { var start = new Date(); for (let i = 0; i < 500000; ++i) { for (let j = 0; j < 4; ++j) { } } var stop = new Date(); return stop - start; } jit(false); var timenonjit = letitbe(); jit(true); var timejit = letitbe(); jit(false); print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; actual = timejit < timenonjit/2; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/browser.js0000644000175000017500000000000014433667662024622 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-470739.js0000644000175000017500000000230314433667662025374 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-470739.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 470739; var summary = 'TM: never abort on =='; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function loop() { var i; var start = new Date(); for(i=0;i<500000;++i) { var r = (void 0) == null; } var stop = new Date(); return stop - start; } jit(false); var timenonjit = loop(); jit(true); var timejit = loop(); jit(false); print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; actual = timejit < timenonjit/2; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-458838.js0000644000175000017500000000325514433667662025411 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-458838.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 458838; var summary = 'TM: do not fall off trace when nested function accesses var of outer function'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); function f() { var a = 1; function g() { var b = 0 for (var i = 0; i < 10; ++i) { b += a; } return b; } return g(); } expect = 10; actual = f(); var recorderStarted; var recorderAborted; var traceCompleted; if (this.tracemonkey) { recorderStarted = this.tracemonkey.recorderStarted; recorderAborted = this.tracemonkey.recorderAborted; traceCompleted = this.tracemonkey.traceCompleted; } jit(false); reportCompare(expect, actual, summary + ': return value 10'); if (this.tracemonkey) { expect = 'recorderStarted=1, recorderAborted=0, traceCompleted=1'; actual = 'recorderStarted=' + recorderStarted + ', recorderAborted=' + recorderAborted + ', traceCompleted=' + traceCompleted; reportCompare(expect, actual, summary + ': trace'); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-451974-01.js0000644000175000017500000000235214433667662025616 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-451974-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 451974; var summary = 'TM: loops with anon functions should not be slower with jit enabled'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); var chars = '0123456789abcdef'; var size = 1000; var mult = 100; var densearray = []; var lsize = size; while (lsize--) { densearray.push(chars); } function loop() { var start = new Date(); for (var a = 0; a < mult; a++) { var f = (function(x){}); for (var i = 0, len = densearray.length; i < len; i++) { f(densearray[i]); } } var stop = new Date(); return stop - start; } jit(false); var timenonjit = loop(); jit(true); var timejit = loop(); jit(false); print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; actual = timejit < timenonjit/2; reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-451673.js0000644000175000017500000000351514433667662025376 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-451673.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 451673; var summary = 'TM: Tracing prime number generation'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function doTest(enablejit) { if (enablejit) jit(true); else jit(false); var n = 1000000; var start = new Date(); var i=0; var j=0; var numprimes=0; var limit=0; numprimes = 1; // 2 is prime var mceil = Math.floor; var msqrt = Math.sqrt; var isPrime = 1; for (i = 3; i<= n; i+=2) { isPrime=1; limit = mceil(msqrt(i)+1) + 1; for (j = 3; j < limit; j+=2) { if (i % j == 0) { isPrime = 0; break; } } if (isPrime) { numprimes ++; } } var end = new Date(); var timetaken = end - start; timetaken = timetaken / 1000; if (enablejit) jit(false); print((enablejit ? ' JIT' : 'Non-JIT') + ": Number of primes up to: " + n + " is " + numprimes + ", counted in " + timetaken + " secs."); return timetaken; } var timenonjit = doTest(false); var timejit = doTest(true); expect = true; actual = timejit < timenonjit/4; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-04.js0000644000175000017500000000334114433667662025620 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace Array(1, 2, 3)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { Array(1, 2, 3); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-05.js0000644000175000017500000000333314433667662025622 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-05.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace new Array()'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { new Array(); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-06.js0000644000175000017500000000333514433667662025625 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-06.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace new Array(1)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { new Array(1); } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/CVS/0000755000175000017500000000000014433667662023246 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/CVS/Repository0000644000175000017500000000003714433667662025350 0ustar apoapomozilla/js/tests/js1_8_1/trace closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/CVS/Entries0000644000175000017500000000240014433667662024576 0ustar apoapo/browser.js/1.1/Thu Aug 14 06:51:39 2008// /math-trace-tests.js/1.2/Fri Aug 7 23:42:17 2009// /regress-451673.js/1.1/Fri Mar 6 01:39:01 2009// /regress-451974-01.js/1.2/Sat Feb 21 01:30:52 2009// /regress-451974-02.js/1.2/Sat Feb 21 01:30:52 2009// /regress-452498-01.js/1.1/Fri Mar 20 05:00:21 2009// /regress-458838.js/1.1/Wed Jan 28 16:55:46 2009// /regress-462459-01.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-02.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-03.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-04.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-05.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-06.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-07.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-08.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-09.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-10.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-11.js/1.1/Sat Feb 21 01:30:52 2009// /regress-462459-12.js/1.1/Sat Feb 21 01:30:52 2009// /regress-469927.js/1.2/Sat Feb 21 01:30:52 2009// /regress-470739.js/1.2/Sat Feb 21 01:30:52 2009// /regress-471635.js/1.1/Wed Jan 28 16:55:46 2009// /regress-489682.js/1.1/Mon Feb 22 18:53:47 2010// /shell.js/1.1/Thu Aug 14 06:51:39 2008// /trace-test.js/1.14/Fri Aug 7 21:33:30 2009// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/CVS/Root0000644000175000017500000000006314433667662024113 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-489682.js0000644000175000017500000000155514433667662025413 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-489682.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 489682; var summary = 'TM: wrong number with nested type-unstable loops'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); var v = 0; for each (var a in [0, {}, {}, {}]) { print(v); v = v >>> 0; for each (var b in [{}, {}, new String(''), 42, new String(''), {}, 42]) { } } print(v); jit(false); expect = '0'; actual = v + ''; reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-462459-11.js0000644000175000017500000000332114433667662025614 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-462459-11.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 462459; var summary = 'TM: trace [1, 2]'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (!this.tracemonkey) { jit(false); expect = actual = 'Test skipped due to lack of tracemonkey jitstats'; reportCompare(expect, actual, summary); } else { jit(true); expect = 'recorder started, recorder not aborted, trace completed'; actual = ''; var recorderStartedStart = this.tracemonkey.recorderStarted; var recorderAbortedStart = this.tracemonkey.recorderAborted; var traceCompletedStart = this.tracemonkey.traceCompleted; for (var i = 0; i < 5; i++) { [1, 2]; } jit(false); var recorderStartedEnd = this.tracemonkey.recorderStarted; var recorderAbortedEnd = this.tracemonkey.recorderAborted; var traceCompletedEnd = this.tracemonkey.traceCompleted; if (recorderStartedEnd > recorderStartedStart) { actual = 'recorder started, '; } else { actual = 'recorder not started, '; } if (recorderAbortedEnd > recorderAbortedStart) { actual += 'recorder aborted, '; } else { actual += 'recorder not aborted, '; } if (traceCompletedEnd > traceCompletedStart) { actual += 'trace completed'; } else { actual += 'trace not completed'; } reportCompare(expect, actual, summary); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/trace/regress-451974-02.js0000644000175000017500000000303014433667662025611 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-451974-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 451974; var summary = 'TM: loops with anon functions should not be slower with jit enabled'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var chars = '0123456789abcdef'; var size = 1000; var mult = 100; var densearray = []; var lsize = size; while (lsize--) { densearray.push(chars); } function loop() { var start = new Date(); for (var a = 0; a < mult; a++) { var f = (function(x){}); for (var i = 0, len = densearray.length; i < len; i++) { f(densearray[i]); } } var stop = new Date(); return stop - start; } jit(false); var timenonjit = loop(); jit(true); var timejit = loop(); jit(false); print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; actual = timejit < timenonjit/2; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/shell.js0000644000175000017500000000061314433667662023162 0ustar apoapo/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsuite = 'js1_8_1'; // explicitly turn on js181 if (typeof version != 'undefined') { version(180); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/0000755000175000017500000000000014433667662023167 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-112.js0000644000175000017500000000203714433667662026257 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-112.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #112 From Jesse Ruderman expect = 'TypeError: q is not a function'; try { q = new Function("(function() { q(3); })(); const q;"); q(); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-110.js0000644000175000017500000000274614433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-110.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #110 From Brendan Eich // (In reply to comment #107) function f(a) { const b = a; print(++b); return b; } expect = 'function f(a) { const b = a; print(+ b + 1); return b; }'; actual = f + ''; compareSource(expect, actual, 'function f(a) { const b = a; print(++b); return b; }'); expect = '01'; actual = 0; function g(a) { const b = a; print(actual = ++b); return b; } actual = String(actual) + g(1); reportCompare(expect, actual, 'function g(a) { const b = a; print(actual = ++b); return b; }'); expect = '21'; actual = 0; const x = 1; print(actual = ++x); actual = String(actual) + x; reportCompare(expect, actual, 'const x = 1; print(actual = ++x); '); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-135.js0000644000175000017500000000436714433667662026274 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-135.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #135 From Gary Kwong [:nth10sd] // -j is not required: // === for (let i = 0; i < 9; ++i) { let m = i; if (i % 3 == 1) { print('' + (function() { return m; })()); } } // Assertion failure: fp2->fun && fp2->script, at ../jsinterp.cpp:5633 // Opt crash [@ js_Interpret] // === try { (x for each (c in [])) x } catch(ex) { } // Assertion failure: dn_kind == JSDefinition::VAR || dn_kind == JSDefinition::CONST, at ../jsemit.cpp:2187 // === "" + (function(){L:if (*::*){ var x } function x(){}}) // Assertion failure: slot < StackDepth(jp->script), at ../jsopcode.cpp:1329 // === "" + (function(){if (*::*){ var x } function x(){}}) // Assertion failure: (uintN)i < ss->top, at ../jsopcode.cpp:2825 // === "" + (function(){{; throw ;for(var x = [] in false) return } function x(){}}) // Assertion failure: ss->printer->pcstack, at ../jsopcode.cpp:909 // === try { (function(){for(; (this); ((window for (x in [])) for (y in []))) 0}); } catch(ex) { } // Assertion failure: level >= tc->staticLevel, at ../jsparse.cpp:5773 // === eval(uneval( function(){ ((function()y)() for each (x in this)) } )) // Debug & opt crash [@ BindNameToSlot] // -j is required: // === for (let a=0;a<3;++a) for (let b=0;b<3;++b) if ((g=a|(a%b))) with({}){} // Assertion failure: OBJ_IS_CLONED_BLOCK(obj), at ../jsobj.cpp:2392 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-187.js0000644000175000017500000000201714433667662026271 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-187.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #187 From Jesse Ruderman //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for/in left-hand side'; try { eval('const x; for (x in []);'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/shell.js0000644000175000017500000000034714433667662024640 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='regress'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-063.js0000644000175000017500000000172514433667662026267 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-063.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #63 From Brendan Eich function f(that) { for (ix in this) print(ix); for (let ix in that) ; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-102.js0000644000175000017500000000343614433667662026262 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-102.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #102 From Gary Kwong [:nth10sd] // ===== (function(){function x(){} function x()y})(); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== function f() { "" + (function(){ for( ; [function(){}] ; x = 0) with({x: ""}) const x = [] }); } f(); // Assertion failure: ss->top - saveTop <= 1U, at ../jsopcode.cpp:2156 // ===== try { function f() { var x; eval("const x = [];"); } f(); } catch(ex) { } // Assertion failure: regs.sp == StackBase(fp), at ../jsinterp.cpp:2984 // ===== try { do {x} while([[] for (x in []) ]); } catch(ex) { } // Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 // ===== try { {x} ((x=[] for (x in []))); x; } catch(ex) { } // Assertion failure: cg->staticLevel >= level, at ../jsemit.cpp:2014 // Crash [@ BindNameToSlot] in opt without -j // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-466905-01.js0000644000175000017500000000172514433667662026175 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466905-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466905; var summary = 'Do not assert: v_ins->isCall() && v_ins->callInfo() == &js_FastNewArray_ci'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function f(a) { for each (let c in a) [(c > 5) ? 'A' : 'B']; } f([true, 8]); f([2]); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-091.js0000644000175000017500000000174514433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-091.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #91 From Jesse Ruderman expect = 'function eval() {\n eval("v");\n}'; f = (function eval() { eval("v"); }); actual = f + ''; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-079.js0000644000175000017500000000172714433667662026300 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-079.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #79 From Jason Orendorff x; var x; function x() 0 // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:635 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-103.js0000644000175000017500000000177614433667662026270 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-103.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #103 From Jesse Ruderman (function(a) { v = 5; let [v] = [3]; (function(){ v; })(); })(); // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:638 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-006.js0000644000175000017500000000215414433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-006.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #6 From Andreas Gal :gal function foo() { var x = 4; var f = (function() { return x++; }); var g = (function() { return x++; }); return [f,g]; } var bar = foo(); expect = '9'; actual = 0; bar[0](); bar[1](); actual = String(expect); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-099.js0000644000175000017500000000256414433667662026302 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-099.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #99 From Gary Kwong [:nth10sd] try { eval("(function x(){x.(this)} )();"); } catch(ex) { } // Assertion failure: (uint32)(index_) < atoms_->length, at ../jsinterp.cpp:327 // Crash [@ js_FullTestPropertyCache] at null in opt, -j not required. // ===== try { (function(){try {x} finally {}; ([x in []] for each (x in x))})(); } catch(ex) { } // Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1735 // Crash [@ BindNameToSlot] near null in opt, -j not required. // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-114.js0000644000175000017500000000266514433667662026270 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-114.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #114 From Gary Kwong [:nth10sd] for (var x = 0; x < 3; ++x){ y = function (){} } // glorp! // Assertion failed: "Constantly false guard detected": 0 (../nanojit/LIR.cpp:999) // (note, this is with -j; I don't know what the glorp! message is about.) // ===== function y([{x: x, y}]){} // Assertion failure: UPVAR_FRAME_SKIP(pn->pn_cookie) == (pn->pn_defn ? cg->staticLevel : 0), at ../jsemit.cpp:3547 // ===== try { eval("(1.3.__defineGetter__(\"\"));let (y, x) { var z = true, x = 0; }"); } catch(ex) { } // Assertion failure: ATOM_IS_STRING(atom), at ../jsinterp.cpp:5691 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-053.js0000644000175000017500000000407214433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-053.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #53 From Jason Orendorff // Assertion failure: (slot) < (uint32)(obj)->dslots[-1] // at ../jsobj.cpp:5559 // On the last line of BindLet, we have // JS_SetReservedSlot(cx, blockObj, index, PRIVATE_TO_JSVAL(pn)); // but this uses reserved slots as though they were unlimited. // blockObj only has 2. let (a=0, b=1, c=2) {} // In RecycleTree at ../jsparse.cpp:315, we hit // JS_NOT_REACHED("RecycleUseDefKids"); // pn->pn_type is TOK_UNARYOP // pn->pn_op is JSOP_XMLNAME // pn->pn_defn is 1 // pn->pn_used is 1 try { @foo; 0; } catch(ex) { } // Calls LinkUseToDef with pn->pn_defn == 1. // // If you say "var x;" first, then run this case, it gets further, // crashing in NoteLValue like the first case in comment 52. // try { for (var [x] = x in y) var x; } catch(ex) { } // Assertion failure: !pn2->pn_defn, at ../jsparse.h:461 // Another case where some optimization is going on. try { if (true && @foo) ; } catch(ex) { } // Assertion failure: scope->object == ctor // in js_FastNewObject at ../jsbuiltins.cpp:237 // // With the patch, we're new-ing a different function each time, and the // .prototype property is missing. // for (var z = 0; z < 3; z++) { (new function(){}); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-101.js0000644000175000017500000000202214433667662026247 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-101.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #101 From Gary Kwong [:nth10sd] uneval(function(){with({functional: []}){x5, y = this;const y }}); // Assertion failure: strcmp(rval, with_cookie) == 0, at ../jsopcode.cpp:2567 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-068.js0000644000175000017500000000326514433667662026275 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-068.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #68 From Gary Kwong [:nth10sd] try { eval("*;", (3/0 ? function(id) { return id } : <>)); } catch(ex) { } // ===== foo = "" + new Function("while(\u3056){let \u3056 = x}"); // ===== function a(){ let c; eval("let c, y"); } a(); // ===== try { {x: 1e+81 ? c : arguments} } catch(ex) { } // ===== (function(q){return q;} for each (\u3056 in [])) // ===== try { for(let x = <{x}/> in ) x2; } catch(ex) { } // ===== for( const NaN; this.__defineSetter__("x4", function(){}); (eval("", (p={})))) let ({} = (((x ))(function ([]) {})), x1) y; // ===== function f(){ var c; eval("{var c = NaN, c;}"); } f(); // ===== try { eval( ' x\n' + ' let(x) {\n' + ' var x\n' ); } catch(ex) { } // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-479430-05.js0000644000175000017500000000210014433667662026162 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479430-05.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479430; var summary = 'Missing operation callback checks'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof timeout == 'function') { expectExitCode(6); timeout(0.01); function f(n) { if (n != 0) { try { f(n - 1); } finally { f(n - 1); } } name_that_does_not_exists; } f(100); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-058.js0000644000175000017500000000204214433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-058.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #58 From Gary Kwong [:nth10sd] function foo(x) { var x = x } // Assertion failure: dn->kind() == ((data->op == JSOP_DEFCONST) ? JSDefinition::CONST : JSDefinition::VAR), at ../jsparse.cpp:2595 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-104.js0000644000175000017500000000174714433667662026267 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-104.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #104 From Jesse Ruderman (function(a) { function b() { a; } function a() { } })(); // Assertion failure: pn_defn, at ../jsparse.h:655 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-160.js0000644000175000017500000000252514433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-160.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #160 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2034 (function(){for(var x in (x::window = x for (x in []))[[]]){}})(); reportCompare(expect, actual, summary + ': 1'); // crash [@ js_Interpret] (eval("(function(){ watch(\"x\", function () { new function ()y } ); const y });"))(); x = NaN; reportCompare(expect, actual, summary + ': 2'); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 ({ set z(){}, set y x()--x }); reportCompare(expect, actual, summary + ': 3'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-092.js0000644000175000017500000000226014433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-092.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #92 From Jesse Ruderman expect = 'TypeError: redeclaration of formal parameter e'; try { eval('(function (e) { var e; const e; });'); } catch(ex) { actual = ex + ''; } // Without patch: "TypeError: redeclaration of var e" // expected new behavior // With patch: "TypeError: redeclaration of formal parameter e:" reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-107.js0000644000175000017500000000201314433667662026255 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-107.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #107 From Jesse Ruderman // bug fix gives new decompilation behavior function d() { const d; ++d; } expect = 'function d() { const d; + d + 1; }'; actual = d + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-027.js0000644000175000017500000000173214433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-027.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '5'; // ------- Comment #27 From Brendan Eich function f(x){function g(y)x+y;return g} g = f(2); actual = String(g(3)); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-130.js0000644000175000017500000000306414433667662026260 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-130.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #130 From Gary Kwong [:nth10sd] // Does not require -j: // ===== ((function x()x in []) for (y in [])) //Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1778 // Opt crash [@ JSCompiler::setFunctionKinds] // ===== let(x=[]) function(){try {x} catch(x) {} } // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2034 // ===== try { eval('for(let [y] = (let (x) (y)) in []) function(){}'); } catch(ex) { } // Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 // ===== // Requires -j: // ===== for (var x = 0; x < 3; ++x) { new function(){} } // Assertion failure: cx->bailExit, at ../jstracer.cpp:4672 // Opt crash [@ LeaveTree] near null reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-168-2.js0000644000175000017500000000177414433667662026440 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-168-2.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #168 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2047 for (let x; __defineSetter__; (<{x}> for (x in x))) {} reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-098.js0000644000175000017500000000222214433667662026270 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-098.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #98 From Gary Kwong [:nth10sd] try { for(let [x] = (x) in []) {} // Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 } catch(ex) { } uneval(function(){(Number(0) for each (NaN in []) for each (x4 in this))}); // Assertion failure: pos == 0, at ../jsopcode.cpp:2963 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-075.js0000644000175000017500000000200414433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-075.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #75 From Jesse Ruderman (function p(){ p = 3; }); (function p(){ p = 3; return p; })() // Assertion failure: regs.sp == StackBase(fp), at ../jsinterp.cpp:2980 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-030.js0000644000175000017500000000164414433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-030.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #30 From Mike Shaver function f() { var i = 0; var i = 5; } f(); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-050.js0000644000175000017500000000214214433667662026255 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-050.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #50 From Jason Orendorff // Do not crash // compiler bug when a block introduces no names let ({}={}) {} try { // compiler incorrectly emits GETLOCAL for first `x`, // triggering decompiler assertion while (x.y) { let x; } } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-111.js0000644000175000017500000000202414433667662026252 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-111.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #111 From Gary Kwong [:nth10sd] options("anonfunfix"); new Function("{function x(){}}"); // Assertion failure: pn->pn_defn || (fun->flags & JSFUN_LAMBDA), at ../jsemit.cpp:4149 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-129.js0000644000175000017500000000240014433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-129.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #129 From Gary Kwong [:nth10sd] // Does not require -j: // ===== try { eval("({ set x x () { for(x in function(){}){}} })"); } catch (e) { } // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== try { (function (){ eval("(function(){delete !function(){}});"); })(); } catch(ex) { } // Debug crash [@ JSParseNode::isFunArg] at null // Opt crash [@ FindFunArgs] near null // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-479430-04.js0000644000175000017500000000213514433667662026171 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479430-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479430; var summary = 'Missing operation callback checks'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof timeout== 'function') { expectExitCode(6); timeout(0.01); function f(n) { if (n != 0) { try { f(n - 1); } catch (e) {} try { f(n - 1); } catch (e) {} } name_that_does_not_exists; } f(100); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-038.js0000644000175000017500000000161414433667662026266 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-038.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #38 From Jesse Ruderman [0 for (a in [])]; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-099-a.js0000644000175000017500000000227214433667662026514 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-099-a.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #99 From Gary Kwong [:nth10sd] if (typeof timeout == 'function') { expectExitCode(6); timeout(3); while( getter = function() { return y } for (y in y) )( /x/g ); } // Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1771 // Crash [@ JSCompiler::setFunctionKinds] near null in opt, -j not required. reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-077.js0000644000175000017500000000174714433667662026300 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-077.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '1'; // ------- Comment #77 From Brendan Eich (function () { const [d] = [1]; [d] = [2]; print(actual = d);})(); actual = String(actual); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-466905-02.js0000644000175000017500000000166314433667662026177 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466905-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466905; var summary = 'Do not assert: v_ins->isCall() && v_ins->callInfo() == &js_FastNewArray_ci'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); for (var i = 0; i < 5; i++) [(i > 3) ? 'a' : 'b']; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-108.js0000644000175000017500000000167514433667662026273 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-108.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #108 From Jesse Ruderman function p(){p} expect = 'function p(){p;}'; actual = p + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-135-a.js0000644000175000017500000000200614433667662026476 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-135-a.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #135 From Gary Kwong [:nth10sd] if (typeof timeout == 'function') { expectExitCode(6); timeout(3); eval("do ([]); while(y for each (x in []))"); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-139.js0000644000175000017500000000217514433667662026273 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-139.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #139 From Gary Kwong [:nth10sd] // Does not require -j: // === try { (function(){var x = x (x() for each (x in []))})(); } catch(ex) { } // Assertion failure: (fun->u.i.script)->upvarsOffset != 0, at ../jsfun.cpp:1541 // Opt crash near null [@ js_NewFlatClosure] // === reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-054.js0000644000175000017500000000216514433667662026266 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-054.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #54 From Jason Orendorff // Assertion failure: cg->flags & TCF_IN_FUNCTION // at ../jsemit.cpp:1991 // function f() { var x; eval("let x, x;"); } f(); // Assertion failure: fp2->fun && fp2->script, // at ../jsinterp.cpp:5589 // eval("let(x) function(){ x = this; }()"); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-479430-01.js0000644000175000017500000000202314433667662026162 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479430-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479430; var summary = 'Missing operation callback checks'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof timeout == 'function') { expectExitCode(6); timeout(0.01); function f(n) { if (n != 0) { f(n - 1); f(n - 1); } } f(100); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-191.js0000644000175000017500000000224314433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-191.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #191 From Brendan Eich //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = actual = 'No Error'; try { eval('{ var x; {let x;} }'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); expect = 'TypeError: redeclaration of let x'; try { eval('{ let x; {var x;} }'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-052-a.js0000644000175000017500000000225514433667662026502 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-052-a.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #52 From Jason Orendorff // Assertion failure: pn_arity == PN_FUNC || pn_arity == PN_NAME, at ../jsparse.h:444 // Here the function node has been morphed into a JSOP_TRUE node, but we're in // FindFunArgs poking it anyway. if (typeof timeout == 'function') { expectExitCode(6); timeout(3); while(function(){}); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-072.js0000644000175000017500000000174114433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-072.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #72 From Jesse Ruderman v = function p() { delete p; }; // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1711 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-082.js0000644000175000017500000000545314433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-082.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #82 From Gary Kwong [:nth10sd] // ===== (function(){function x(){} {let x = [] }}); // ===== var f = new Function("new function x(){ return x |= function(){} } ([], function(){})"); "" + f; // ===== var f = new Function("for(let [] = [0]; (y) = *; new (*::*)()) {}"); "" + f; // ===== uneval(function(){[y] = [x];}); // ===== function g(code) { var f = new Function(code); f(); } g("for (var x = 0; x < 3; ++x)(new (function(){})());"); // ===== try { (function(){new (function ({}, x) { yield (x(1e-81) for (x4 in undefined)) })()})(); } catch(ex) { } // ===== try { (function(){[(function ([y]) { })() for each (x in [])];})(); } catch(ex) { } // ===== try { eval('(function(){for(var x2 = [function(id) { return id } for each (x in []) if ([])] in functional) function(){};})();'); } catch(ex) { } // ===== if (typeof window == 'undefined') global = this; else global = window; try { eval('(function(){with(global){1e-81; }for(let [x, x3] = global -= x in []) function(){}})();'); } catch(ex) { } // ===== try { eval( 'for(let [\n' + 'function x () { M:if([1,,]) }\n' ); } catch(ex) { } // ===== try { function foo(code) { var c; eval("const c, x5 = c;"); } foo(); } catch(ex) { } // ===== var f = new Function("try { with({}) x = x; } catch(\u3056 if (function(){x = x2;})()) { let([] = [1.2e3.valueOf(\"number\")]) ((function(){})()); } "); "" + f; // ===== var f = new Function("[] = [( '' )()];"); "" + f; // ===== var f = new Function("let ([] = [({ get x5 this (x) {} })]) { for(let y in []) with({}) {} }"); "" + f; // ===== try { eval( 'for(let x;' + ' ([,,,]' + ' .toExponential(new Function(), (function(){}))); [] = {})' + ' for(var [x, x] = * in this.__defineSetter__("", function(){}));' ); } catch(ex) { } // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-118.js0000644000175000017500000000202114433667662026256 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-118.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #118 From Jesse Ruderman (function() { (function() { e *= 4; })(); var e; })(); //Without patch: no output //With patch: ReferenceError: reference to undefined property "e" reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-155.js0000644000175000017500000000162514433667662026270 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-155.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { delete (1 ? window : function(){}); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-138.js0000644000175000017500000000234014433667662026264 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-138.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #138 From Gary Kwong [:nth10sd] // Does not require -j: // === ((function x(){ yield (x = undefined) } ) for (y in /x/)); // Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1820 // === try { for(let x in ( x for (y in x) for each (x in []) )) y; } catch(ex) { } // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2034 // === reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-178.js0000644000175000017500000000176214433667662026277 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-178.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #178 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: slot < fp2->script->nfixed, at ../jsinterp.cpp:5610 eval("with({}) let(x=[])(function(){#2=x})()"); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-039.js0000644000175000017500000000171714433667662026273 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-039.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '10'; // ------- Comment #39 From Jesse Ruderman const e = 0; print(actual = ++e); actual = String(actual) + e; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/browser.js0000644000175000017500000000000014433667662025176 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-181.js0000644000175000017500000000170414433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-181.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #181 From Jesse Ruderman //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 3; (function(print) { delete print; })(); print(actual = 3); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-479430-03.js0000644000175000017500000000213014433667662026163 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479430-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479430; var summary = 'Missing operation callback checks'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof timeout == 'function') { expectExitCode(6); timeout(0.01); function f(n) { if (n != 0) { f(n - 1); f(n - 1); } try { return 0; } finally { n++; } } f(100); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-071.js0000644000175000017500000000170614433667662026265 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-071.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #71 From Jesse Ruderman x; var x // Assertion failure: pn->pn_op == JSOP_NOP, at ../jsparse.cpp:1118 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-185.js0000644000175000017500000000203714433667662026271 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-185.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #185 From Jesse Ruderman //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: redeclaration of variable e'; try { eval('{ var e = 3; let e = ""; } print(typeof e);'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-116.js0000644000175000017500000000176214433667662026267 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-116.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #116 From Gary Kwong [:nth10sd] // -j (new Function("for (var x = 0; x < 3; ++x) { (function(){})() } "))(); //Crash [@ js_IsActiveWithOrBlock] reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-117.js0000644000175000017500000000653614433667662026274 0ustar apoapo03/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-117.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #117 From Gary Kwong [:nth10sd] // The following all do not require -j. // ===== try { eval('x; function x(){}; const x;'); } catch(ex) { } // Assertion failure: !pn->isPlaceholder(), at ../jsparse.cpp:4876 // ===== (function(){ var x; eval("var x; x = null"); })() // Assertion failure: regs.sp == StackBase(fp), at ../jsinterp.cpp:2984 // ===== function this ({x}) { function x(){} } // Assertion failure: cg->stackDepth == stackDepth, at ../jsemit.cpp:3664 // ===== for(let x = [ "" for (y in /x/g ) if (x)] in ("")); // Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818 // ===== (function(){const x = 0, y = delete x;})() // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== try { (function(){(yield []) (function(){with({}){x} }); const x;})(); } catch(ex) { } // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:2022 // ===== try { (function(){([]) ((function(q) { return q; })for (each in *))})(); } catch(ex) { } // Assertion failure: lexdep->frameLevel() <= funbox->level, at ../jsparse.cpp:1782 // Opt crash [@ JSCompiler::setFunctionKinds] near null // ===== try { eval("((x1) > [(x)(function() { x;}) for each (x in x)])()"); } catch(ex) { } // Assertion failure: pnu->pn_lexdef == dn, at ../jsemit.cpp:1817 // ===== uneval(function(){for(var [arguments] = ({ get y(){} }) in y ) (x);}); // Assertion failure: slot < StackDepth(jp->script), at ../jsopcode.cpp:1318 // ===== uneval(function(){([] for ([,,]in <>));}); // Assertion failure: n != 0, at ../jsfun.cpp:2689 // ===== try { eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x;} }})();'); } catch(ex) { } // Assertion failure: op == JSOP_GETLOCAL, at ../jsemit.cpp:4557 // ===== try { (eval("(function(){let x , x = (x for (x in null))});"))(); } catch(ex) { } // Assertion failure: (fun->u.i.script)->upvarsOffset != 0, at ../jsfun.cpp:1537 // Opt crash [@ js_NewFlatClosure] near null // ===== "" + function(){for(var [x] in x1) ([]); function x(){}} // Assertion failure: cg->stackDepth == stackDepth, at ../jsemit.cpp:3664 // Opt crash [@ JS_ArenaRealloc] near null // ===== try { eval( "for (a in (function(){" + " for each (let x in ['']) { return new function x1 (\u3056) { yield x } ();" + " }})())" + " function(){}" ); } catch(ex) { } // Crash [@ js_Interpret] near null // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-052.js0000644000175000017500000000310314433667662026255 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-052.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #52 From Jason Orendorff // Crash in NoteLValue, called from BindDestructuringVar. // NoteLValue assumes pn->pn_lexdef is non-null, but here // pn is itself the definition of x. for (var [x]=0 in null) ; // This one only crashes when executed from a file. // Assertion failure: pn != dn->dn_uses, at ../jsparse.cpp:1131 for (var f in null) ; var f = 1; (f) // Assertion failure: pnu->pn_cookie == FREE_UPVAR_COOKIE, at ../jsemit.cpp:1815 // In EmitEnterBlock. x has one use, which is pnu here. // pnu is indeed a name, but pnu->pn_cookie is 0. try { eval('let (x = 1) { var x; }'); } catch(ex) {} // Assertion failure: cg->upvars.lookup(atom), at ../jsemit.cpp:1992 // atom="x", upvars is empty. (1 for each (x in x)); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/CVS/0000755000175000017500000000000014433667662023622 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/CVS/Repository0000644000175000017500000000004114433667662025717 0ustar apoapomozilla/js/tests/js1_8_1/regress closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/CVS/Entries0000644000175000017500000001004514433667662025156 0ustar apoapo/browser.js/1.1/Thu Nov 27 11:38:03 2008// /regress-420399.js/1.1/Thu Nov 27 11:38:03 2008// /regress-452498-006.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-027.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-030.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-038.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-039.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-040.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-050.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-051.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-052-a.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-052.js/1.2/Fri Aug 7 21:09:06 2009// /regress-452498-053.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-054.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-058.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-062.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-063.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-068.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-071.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-072.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-073.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-074.js/1.2/Fri Apr 10 19:44:02 2009// /regress-452498-075.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-076.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-077.js/1.2/Fri Apr 10 19:44:02 2009// /regress-452498-079.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-082.js/1.2/Fri Aug 7 21:09:06 2009// /regress-452498-091.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-092.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-098.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-099-a.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-099.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-101.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-102.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-103.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-104.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-107.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-108.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-110.js/1.2/Fri Aug 7 21:09:06 2009// /regress-452498-111.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-112.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-114-a.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-114.js/1.2/Mon Mar 23 17:59:24 2009// /regress-452498-116.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-117.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-118.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-119.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-121.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-123.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-129.js/1.2/Fri Apr 10 19:44:02 2009// /regress-452498-130.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-131.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-135-a.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-135.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-138.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-139.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-155.js/1.1/Fri Mar 20 05:00:18 2009// /regress-452498-160.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-168-1.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-168-2.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-176.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-178.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-181.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-184.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-185.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-187.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-191.js/1.1/Fri Aug 7 21:09:06 2009// /regress-452498-192.js/1.1/Fri Aug 7 21:09:06 2009// /regress-466905-01.js/1.1/Sat Feb 21 01:30:48 2009// /regress-466905-02.js/1.1/Sat Feb 21 01:30:48 2009// /regress-479430-01.js/1.1/Fri Mar 20 05:00:18 2009// /regress-479430-02.js/1.1/Fri Mar 20 05:00:18 2009// /regress-479430-03.js/1.1/Fri Mar 20 05:00:18 2009// /regress-479430-04.js/1.1/Fri Mar 20 05:00:18 2009// /regress-479430-05.js/1.1/Fri Mar 20 05:00:18 2009// /regress-495907.js/1.1/Fri Aug 7 23:57:59 2009// /shell.js/1.1/Thu Nov 27 11:38:03 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/CVS/Root0000644000175000017500000000006314433667662024467 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-051.js0000644000175000017500000000227314433667662026263 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-051.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #51 From Jason Orendorff // Assertion failure: UPVAR_FRAME_SKIP(uva->vector[i]) == 0 // at ../jsopcode.cpp:2791 // // when decompiling the eval code, which is: // // main: // 00000: 10 getupvar 0 // 00003: 10 getprop "y" // 00006: 10 popv // 00007: 10 stop try { function f() { var x; eval("x.y"); } f(); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-479430-02.js0000644000175000017500000000211514433667662026165 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479430-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479430; var summary = 'Missing operation callback checks'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof timeout == 'function') { expectExitCode(6); timeout(0.01); function f(n) { if (n != 0) { try { f(n - 1); } catch (e) {} try { f(n - 1); } catch (e) {} } throw 0; } f(100); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-123.js0000644000175000017500000000307414433667662026263 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-123.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #123 From Gary Kwong [:nth10sd] // Does not require -j: // ===== try { eval('y = (function (){y} for (x in []);'); } catch(ex) { } // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:651 // ===== (function(){for(var x = arguments in []){} function x(){}})(); // Assertion failure: dn->pn_defn, at ../jsemit.cpp:1873 // ===== // Requires -j: // ===== (function(){ eval("for (x in ['', {}, '', {}]) { this; }" )})(); // Assertion failure: fp->thisp == JSVAL_TO_OBJECT(fp->argv[-1]), at ../jstracer.cpp:4172 // ===== for each (let x in ['', '', '']) { (new function(){} )} // Assertion failure: scope->object == ctor, at ../jsbuiltins.cpp:236 // Opt crash [@ js_FastNewObject] near null // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-131.js0000644000175000017500000000237014433667662026260 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-131.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #131 From Gary Kwong [:nth10sd] // Does not require -j: // ===== try { eval('((__defineGetter__, function (x) { function x(){} }) for'); } catch(ex) { } // Assertion failure: pn->pn_cookie == FREE_UPVAR_COOKIE, at ../jsparse.cpp:5698 // ===== try { eval('( "" ? 1.3 : x); *::*; x::x;'); } catch(ex) { } // Assertion failure: pn != dn->dn_uses, at ../jsparse.cpp:1171 // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-062.js0000644000175000017500000000216114433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-062.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #62 From Gary Kwong [:nth10sd] try { eval( '(function(){' + ' var x;' + ' this.init_by_array = function()' + ' x = 0;' + '})();' ); } catch(ex) { } // Assertion failure: ATOM_IS_STRING(atom), at ../jsinterp.cpp:5686 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-420399.js0000755000175000017500000000177614433667662025765 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-420399.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 420399; var summary = 'Let expression error involving undefined'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = /TypeError: let \(a = undefined\) a (is undefined|has no properties)/; try { (let (a=undefined) a).b = 3; } catch(ex) { actual = ex + ''; } reportMatch(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-114-a.js0000644000175000017500000000220414433667662026473 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-114-a.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #114 From Gary Kwong [:nth10sd] if (typeof timeout == 'function') { timeout(3); try { eval('while(x|=#3={}) with({}) const x;'); } catch(ex) { } reportCompare(expect, actual, ''); } // Assertion failure: cg->stackDepth >= 0, at ../jsemit.cpp:185 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-176.js0000644000175000017500000000177214433667662026276 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-176.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #176 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: pn_arity == PN_FUNC || pn_arity == PN_NAME, at ../jsparse.h:449 if(delete( 5 ? [] : (function(){})() )) []; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-040.js0000644000175000017500000000175714433667662026267 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-040.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #40 From Jesse Ruderman function m() { function a() { } function b() { a(); } this.c = function () { b(); } } (new m).c(); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-073.js0000644000175000017500000000200614433667662026261 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-073.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #73 From Jesse Ruderman try { eval('function() { var arguments }'); } catch(ex) { } // Assertion failure: (uintN)i < ss->top, at ../jsopcode.cpp:2801 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-074.js0000644000175000017500000000172514433667662026271 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-074.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '1'; // ------- Comment #74 From Jesse Ruderman const [d] = [1]; [d] = [2]; print(actual = d); actual = String(actual); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-495907.js0000644000175000017500000000221414433667662025755 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-495907.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 495907; var summary = 'Read upvar from trace-entry frame when created with top-level let'; var actual = '' var expect = '00112233'; //----------------------------------------------------------------------------- // The test code needs to run at top level in order to expose the bug. start_test(); var o = []; for (let a = 0; a < 4; ++a) { (function () {for (var b = 0; b < 2; ++b) {o.push(a);}}()); } actual = o.join(""); finish_test(); //----------------------------------------------------------------------------- function start_test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); } function finish_test() { jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-119.js0000644000175000017500000000330314433667662026263 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-119.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #119 From Gary Kwong [:nth10sd] // The following additional testcases also do not require -j. // ===== function f() { var x; eval("for(let y in [false]) var x, x = 0"); } f(); // Assertion failure: !JSVAL_IS_PRIMITIVE(regs.sp[-2]), at ../jsinterp.cpp:3243 // Opt crash [@ JS_GetMethodById] near null // ===== new Function("for(x1 in ((function (){ yield x } )())){var c, x = []} function x(){} "); // Assertion failure: pn_used, at ../jsparse.h:401 // Opt crash [@ FindFunArgs] at null // ===== uneval(new Function("[(x = x) for (c in []) if ([{} for (x in [])])]")) // Assertion failure: (uintN)i < ss->top, at ../jsopcode.cpp:2814 // ===== function f() { var x; (function(){})(); eval("if(x|=[]) {const x; }"); } f(); // Opt crash [@ js_ValueToNumber] at 0xc3510424 // Dbg crash [@ js_ValueToNumber] at 0xdadadad8 // ===== reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-192.js0000644000175000017500000000163514433667662026272 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-192.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #192 From Brendan Eich //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); with({x: (x -= 0)}){([]); const x } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-121.js0000644000175000017500000000174414433667662026263 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-121.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #121 From Gary Kwong [:nth10sd] // without -j x = function()x; // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:651 reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-184.js0000644000175000017500000000167314433667662026275 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-184.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #184 From Jesse Ruderman //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '11'; const e = 8; print(actual = '' + ((e += 3))); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-076.js0000644000175000017500000000170314433667662026267 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-076.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #76 From Jesse Ruderman for (let d = 0; d < 4; ++d) { d; } // 1: ReferenceError: d is not defined reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/regress/regress-452498-168-1.js0000644000175000017500000000203614433667662026427 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-168-1.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #168 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: (fun->u.i.script)->upvarsOffset != 0, at ../jsfun.cpp:1543 ( new Function("const x = (function () { if (1e+81){} else{x} } )" ))(); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/0000755000175000017500000000000014433667662022763 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/shell.js0000644000175000017500000000034614433667662024433 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='String'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/browser.js0000644000175000017500000000000014433667662024772 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/CVS/0000755000175000017500000000000014433667662023416 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/CVS/Repository0000644000175000017500000000004014433667662025512 0ustar apoapomozilla/js/tests/js1_8_1/String closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/CVS/Entries0000644000175000017500000000021014433667662024743 0ustar apoapo/browser.js/1.1/Wed Aug 13 10:25:16 2008// /regress-305064.js/1.2/Wed Aug 13 12:01:08 2008// /shell.js/1.1/Wed Aug 13 10:25:16 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/CVS/Root0000644000175000017500000000006314433667662024263 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/String/regress-305064.js0000644000175000017500000001045014433667662025532 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-305064.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 305064; var summary = 'Tests the trim, trimRight and trimLeft methods'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); var trimMethods = ['trim', 'trimLeft', 'trimRight']; /** List from ES 3.1 Recommendation for String.trim (bug 305064) **/ var whitespace = [ {s : '\u0009', t : 'HORIZONTAL TAB'}, {s : '\u000B', t : 'VERTICAL TAB'}, {s : '\u000C', t : 'FORMFEED'}, {s : '\u0020', t : 'SPACE'}, {s : '\u00A0', t : 'NO-BREAK SPACE'}, {s : '\u1680', t : 'OGHAM SPACE MARK'}, {s : '\u180E', t : 'MONGOLIAN VOWEL SEPARATOR'}, {s : '\u2000', t : 'EN QUAD'}, {s : '\u2001', t : 'EM QUAD'}, {s : '\u2002', t : 'EN SPACE'}, {s : '\u2003', t : 'EM SPACE'}, {s : '\u2004', t : 'THREE-PER-EM SPACE'}, {s : '\u2005', t : 'FOUR-PER-EM SPACE'}, {s : '\u2006', t : 'SIX-PER-EM SPACE'}, {s : '\u2007', t : 'FIGURE SPACE'}, {s : '\u2008', t : 'PUNCTUATION SPACE'}, {s : '\u2009', t : 'THIN SPACE'}, {s : '\u200A', t : 'HAIR SPACE'}, {s : '\u202F', t : 'NARROW NO-BREAK SPACE'}, {s : '\u205F', t : 'MEDIUM MATHEMATICAL SPACE'}, {s : '\u3000', t : 'IDEOGRAPHIC SPACE'}, {s : '\u000A', t : 'LINE FEED OR NEW LINE'}, {s : '\u000D', t : 'CARRIAGE RETURN'}, {s : '\u2028', t : 'LINE SEPARATOR'}, {s : '\u2029', t : 'PARAGRAPH SEPARATOR'}, {s : '\u200B', t : 'ZERO WIDTH SPACE (category Cf)'} ]; for (var j = 0; j < trimMethods.length; ++j) { var str; var method = trimMethods[j]; if (typeof String.prototype[method] == 'undefined') { reportCompare(true, true, 'Test skipped. String.prototype.' + method + ' is not supported'); continue; } print('Test empty string.'); str = ''; expected = ''; actual = str[method](); reportCompare(expected, actual, '"' + toPrinted(str) + '".' + method + '()'); print('Test string with no whitespace.'); str = 'a'; expected = 'a'; actual = str[method](); reportCompare(expected, actual, '"' + toPrinted(str) + '".' + method + '()'); for (var i = 0; i < whitespace.length; ++i) { var v = whitespace[i].s; var t = whitespace[i].t; v = v + v + v; print('======================================='); print('Test ' + method + ' with with only whitespace. : ' + t); str = v; expected = ''; actual = str[method](); reportCompare(expected, actual, t + ':' + '"' + toPrinted(str) + '".' + method + '()'); print('Test ' + method + ' with with no leading or trailing whitespace. : ' + t); str = 'a' + v + 'b'; expected = str; actual = str[method](); reportCompare(expected, actual, t + ':' + '"' + toPrinted(str) + '".' + method + '()'); print('Test ' + method + ' with with leading whitespace. : ' + t); str = v + 'a'; switch(method) { case 'trim': expected = 'a'; break; case 'trimLeft': expected = 'a'; break; case 'trimRight': expected = str; break; } actual = str[method](); reportCompare(expected, actual, t + ':' + '"' + toPrinted(str) + '".' + method + '()'); print('Test ' + method + ' with with trailing whitespace. : ' + t); str = 'a' + v; switch(method) { case 'trim': expected = 'a'; break; case 'trimLeft': expected = str; break; case 'trimRight': expected = 'a'; break; } actual = str[method](); reportCompare(expected, actual, t + ':' + '"' + toPrinted(str) + '".' + method + '()'); print('Test ' + method + ' with with leading and trailing whitepace.'); str = v + 'a' + v; switch(method) { case 'trim': expected = 'a'; break; case 'trimLeft': expected = 'a' + v; break; case 'trimRight': expected = v + 'a'; break; } actual = str[method](); reportCompare(expected, actual, t + ':' + '"' + toPrinted(str) + '".' + method + '()'); } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/README0000755000175000017500000000002114433667662022371 0ustar apoapoJavaScript 1.8.1 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/0000755000175000017500000000000014433667662023714 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/shell.js0000644000175000017500000000035214433667662025361 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='extensions'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-353214-02.js0000644000175000017500000000163014433667662026702 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-353214-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 353214; var summary = 'bug 353214'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = function ([x]) { let x; } expect = 'function ([x]) { var x; }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-352281.js0000755000175000017500000000211114433667662026464 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352281.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352281; var summary = 'decompilation of |while| and function declaration'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f, g; f = function() { { while(0) function t() { } } } expect = 'function() { while(false) { function t() { } }}'; actual = f + ''; compareSource(expect, actual, summary); g = eval(uneval(actual)); actual = g + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-466905-04.js0000644000175000017500000000252214433667662026721 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466905-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466905; var summary = 'Prototypes of sandboxed arrays'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof evalcx != 'function') { expect = actual = 'Test skipped: requires evalcx support'; } else { expect = true; function createArray() { var a; for (var i = 0; i < 10; i++) a = [1, 2, 3, 4, 5]; return a; } var sandbox = evalcx("lazy"); sandbox.createArray = createArray; var p1 = Object.getPrototypeOf(createArray()); var p2 = Object.getPrototypeOf(evalcx("createArray()", sandbox)); print(actual = (p1 === p2)); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-452498-162.js0000644000175000017500000000155614433667662027016 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-162.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #162 From Gary Kwong printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: !OBJ_GET_CLASS(cx, proto)->getObjectOps, at ../jsobj.cpp:2030 jit(true); __defineGetter__("x3", Function); undefined = x3; undefined.prototype = []; for (var z = 0; z < 4; ++z) { new undefined() } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-466905-05.js0000644000175000017500000000222714433667662026724 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466905-05.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466905; var summary = 'Sandbox shapes'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof evalcx != 'function') { expect = actual = 'Test skipped: requires evalcx support'; } else if (typeof shapeOf != 'function') { expect = actual = 'Test skipped: requires shapeOf support'; } else { var s1 = evalcx('lazy'); var s2 = evalcx('lazy'); expect = shapeOf(s1); actual = shapeOf(s2); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-452498-196.js0000644000175000017500000000237114433667662027021 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-196.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #196 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST, at ../jsfun.cpp:916 watch("x", Function); NaN = uneval({ get \u3056 (){ return undefined } }); x+=NaN; reportCompare(expect, actual, summary + ': 1'); // Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900 (function (){ var x; eval("const x; (function ()x)"); } )(); reportCompare(expect, actual, summary + ': 2'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-477158.js0000644000175000017500000000173514433667662026507 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-477158.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 477158; var summary = 'Do not assert: v == JSVAL_TRUE || v == JSVAL_FALSE'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); x = 0; x = x.prop; for each (let [] in ['', '']) { switch(x) { default: (function(){}); } }; jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/browser.js0000644000175000017500000000000014433667662025723 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-452498-193.js0000644000175000017500000000201514433667662027011 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-193.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #193 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: afunbox->parent, at ../jsparse.cpp:1912 watch("x", Function); NaN = uneval({ get \u3056 (){ return undefined } }); x+=NaN; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-437288-01.js0000755000175000017500000000201714433667662026722 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-437288-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 437288; var summary = 'for loop turning into a while loop'; var actual = 'No Hang'; var expect = 'No Hang'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for/in left-hand side'; try { eval('(function() { const x = 1; for (x in null); })();'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/CVS/0000755000175000017500000000000014433667662024347 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/CVS/Repository0000644000175000017500000000004414433667662026447 0ustar apoapomozilla/js/tests/js1_8_1/extensions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/CVS/Entries0000644000175000017500000000123014433667662025677 0ustar apoapo/browser.js/1.1/Fri Oct 17 21:14:43 2008// /regress-352281.js/1.2/Thu Oct 30 22:13:43 2008// /regress-353214-02.js/1.1/Tue Aug 18 07:46:09 2009// /regress-437288-01.js/1.1/Thu Oct 30 22:13:43 2008// /regress-452498-162.js/1.1/Fri Aug 7 21:09:05 2009// /regress-452498-193.js/1.1/Fri Aug 7 21:09:05 2009// /regress-452498-196.js/1.1/Fri Aug 7 21:09:05 2009// /regress-452498-224.js/1.1/Fri Aug 7 21:09:05 2009// /regress-466905-04.js/1.1/Sat Feb 21 01:30:47 2009// /regress-466905-05.js/1.1/Sat Feb 21 01:30:47 2009// /regress-477158.js/1.1/Sat Feb 21 01:30:47 2009// /regress-477187.js/1.2/Wed Feb 25 19:35:35 2009// /shell.js/1.1/Fri Oct 17 21:14:43 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/CVS/Root0000644000175000017500000000006314433667662025214 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-452498-224.js0000644000175000017500000000174114433667662027011 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-452498-224.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 452498; var summary = 'TM: upvar2 regression tests'; var actual = ''; var expect = ''; //------- Comment #224 From Gary Kwong //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // Assertion failure: cg->flags & TCF_HAS_SHARPS, at ../jsemit.cpp:6439 ((#0={}) for(x in null)); reportCompare(expect, actual, summary + ': 2'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/extensions/regress-477187.js0000644000175000017500000000236314433667662026507 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-477187.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 477187; var summary = 'timeout script'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); if (typeof window != 'undefined' || typeof timeout != 'function') { print(expect = actual = 'Test skipped due to lack of timeout function'); reportCompare(expect, actual, summary); } else { expectExitCode(6); timeout(0.01); // Call reportCompare early here to get a result. The test will fail if // the timeout doesn't work and the test framework is forced to terminate // the test. reportCompare(expect, actual, summary); while(1); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/browser.js0000755000175000017500000000043514433667662023543 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/CVS/0000755000175000017500000000000014433667662022150 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/CVS/Repository0000644000175000017500000000003114433667662024244 0ustar apoapomozilla/js/tests/js1_8_1 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/CVS/Entries0000644000175000017500000000025114433667662023502 0ustar apoapo/README/1.1/Wed Aug 13 10:25:15 2008// /browser.js/1.1/Wed Aug 13 10:25:15 2008// /shell.js/1.1/Wed Aug 13 10:25:15 2008// /template.js/1.1/Wed Aug 13 10:25:15 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/CVS/Root0000644000175000017500000000006314433667662023015 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/CVS/Entries.Log0000644000175000017500000000014314433667662024222 0ustar apoapoA D/JSON//// A D/String//// A D/decompilation//// A D/extensions//// A D/regress//// A D/trace//// closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/0000755000175000017500000000000014433667662022266 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/shell.js0000644000175000017500000000034414433667662023734 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='JSON'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/regress-458959.js0000755000175000017500000000164014433667662025067 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-458959.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 458959; var summary = 'this.JSON should not be enumerable'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); for (var i in this) { if (i.toString() == 'JSON') actual = i; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/browser.js0000644000175000017500000000000014433667662024275 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/regress-459293.js0000755000175000017500000000126414433667662025061 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-459293.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 459293; var summary = 'Allow redeclaration of JSON'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); try { eval('var JSON = "foo";'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/CVS/0000755000175000017500000000000014433667662022721 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/CVS/Repository0000644000175000017500000000003614433667662025022 0ustar apoapomozilla/js/tests/js1_8_1/JSON closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/CVS/Entries0000644000175000017500000000027214433667662024256 0ustar apoapo/browser.js/1.1/Sun Oct 12 03:49:47 2008// /regress-458959.js/1.1/Sun Oct 12 03:49:47 2008// /regress-459293.js/1.1/Mon Oct 20 15:46:11 2008// /shell.js/1.1/Sun Oct 12 03:49:47 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/JSON/CVS/Root0000644000175000017500000000006314433667662023566 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/template.js0000755000175000017500000000144514433667662023675 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'template.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 99999; var summary = ''; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/0000755000175000017500000000000014433667662024344 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/shell.js0000644000175000017500000000035514433667662026014 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite='decompilation'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-373678-01.js0000755000175000017500000000202114433667662027347 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-373678-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 373678; var summary = 'Missing quotes around string in decompilation, with for..in and do..while '; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = function() { do {for(a.b in []) { } } while("c\\d"); }; expect = 'function() { do {for(a.b in []) { } } while(true); }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-380237-04.js0000644000175000017500000002127514433667662027350 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Generator expressions parenthesization test'; var actual = ''; var expect = ''; /* Given that parentheization seems so fragile *and* the rules for where genexps are allowed keep changing, I thought it would be good to have a way to test that: 1) unparenthesized genexps are allowed in some places and the decompilation is sane and not over-parenthesized 2) unparenthesized genexps are disallowed in many places and when there are parens, the decompilation is sane and not over-parenthesized */ // |genexp| must have the exact same whitespace the decompiler uses genexp = "x * x for (x in [])"; genexpParened = "(" + genexp + ")"; genexpParenedTwice = "(" + genexpParened + ")"; // Warning: be careful not to put [] around stuff, because that would // cause it to be treated as an array comprehension instead of a // generator expression! // Statements doesNotNeedParens(1, "if (xx) { }"); needParens(2, "if (1, xx) { }"); needParens(3, "if (xx, 1) { }"); doesNotNeedParens(4, "do { } while (xx);"); doesNotNeedParens(5, "while (xx) { }"); doesNotNeedParens(6, "switch (xx) { }"); doesNotNeedParens(7, "with (xx) { }"); needParens(8, "switch (x) { case xx: }"); needParens(9, "return xx;"); needParens(10, "yield xx;"); needParens(11, "for (xx;;) { }"); needParens(12, "for (;xx;) { }", "function anonymous() {\n for (;;) {\n }\n}"); needParens(13, "for (;;xx) { }"); needParens(14, "for (i in xx) { }"); needParens(15, "throw xx"); needParens(16, "try { } catch (e if xx) { }"); needParens(17, "let (x=3) xx"); needParens(18, "let (x=xx) 3"); // Function calls doesNotNeedParens(19, "f(xx);"); needParens(20, "f(xx, 1);"); needParens(21, "f(1, xx);"); doesNotNeedParens(22, "/x/(xx);"); needParens(23, "/x/(xx, 1);"); needParens(24, "/x/(1, xx);"); // eval is special and often confuses the decompiler. doesNotNeedParens(25, "eval(xx);"); needParens(26, "eval(xx, 1);"); needParens(27, "eval(1, xx);"); // Expressions needParens(28, "xx;"); // ??? needParens(29, "var g = xx;"); // ??? needParens(30, "g += xx;"); needParens(31, "xx();"); needParens(32, "xx() = 3;"); needParens(33, "a ? xx : c"); needParens(34, "xx ? b : c"); needParens(35, "a ? b : xx"); needParens(36, "1 ? xx : c"); needParens(37, "0 ? b : xx"); needParens(38, "1 + xx"); needParens(39, "xx + 1"); needParens(40, "1, xx"); doesNotNeedParens(41, "+(xx)"); doesNotNeedParens(42, "!(xx)"); needParens(43, "xx, 1"); needParens(44, "[1, xx]"); needParens(45, "[xx, 1]"); needParens(46, "[#1=xx,3]"); needParens(47, "[#1=xx,#1#]"); needParens(48, "xx.p"); needParens(49, "xx.@p"); needParens(50, "typeof xx;"); needParens(51, "void xx;"); needParens(52, "({ a: xx })"); needParens(53, "({ a: 1, b: xx })"); needParens(54, "({ a: xx, b: 1 })"); needParens(55, "({ a getter: xx })"); needParens(56, ""); doesNotNeedParens(57, "new (xx);"); doesNotNeedParens(58, "new a(xx);"); // Generator expressions cannot be used as LHS, even though they're syntactic // sugar for something that looks a lot like an "lvalue return": (f() = 3). rejectLHS(59, "++ (xx);"); rejectLHS(60, "delete xx;"); rejectLHS(61, "delete (xx);"); rejectLHS(62, "for (xx in []) { }"); rejectLHS(63, "for ((xx) in []) { }"); rejectLHS(64, "try { } catch(xx) { }"); rejectLHS(65, "try { } catch([(xx)]) { }"); rejectLHS(66, "xx += 3;"); rejectLHS(67, "(xx) += 3;"); rejectLHS(68, "xx = 3;"); // Assignment rejectLHS(69, " (xx) = 3;"); rejectLHS(70, "var (xx) = 3;"); rejectLHS(71, "const (xx) = 3;"); rejectLHS(72, "let (xx) = 3;"); // Destructuring assignment rejectLHS(73, " [(xx)] = 3;"); rejectLHS(74, "var [(xx)] = 3;"); rejectLHS(75, "const [(xx)] = 3;"); rejectLHS(76, "let [(xx)] = 3;"); // Group assignment (Spidermonkey optimization for certain // destructuring assignments) rejectLHS(77, " [(xx)] = [3];"); rejectLHS(78, "var [(xx)] = [3];"); rejectLHS(79, "const [(xx)] = [3];"); rejectLHS(80, "let [(xx)] = [3];"); // Destructuring & group assignment for array comprehensions, just for kicks. rejectLHS(81, " [xx] = [3];"); rejectLHS(82, "var [xx] = [3];"); rejectLHS(83, "const [xx] = [3];"); rejectLHS(84, "let [xx] = 3;"); rejectLHS(85, " [xx] = 3;"); rejectLHS(86, "var [xx] = 3;"); rejectLHS(87, "const [xx] = 3;"); rejectLHS(88, "let [xx] = 3;"); // This is crazy, ambiguous, and/or buggy. // See https://bugzilla.mozilla.org/show_bug.cgi?id=380237#c23 et seq. //doesNotNeedParens("(yield xx);"); print("Done!"); function doesNotNeedParens(section, pat) { print("Testing section " + section + " pattern " + pat); var f, ft; sanityCheck(section, pat); expect = 'No Error'; actual = ''; ft = pat.replace(/xx/, genexp); try { f = new Function(ft); actual = 'No Error'; } catch(e) { print("Unparenthesized genexp SHOULD have been accepted here!"); actual = e + ''; } reportCompare(expect, actual, summary + ': doesNotNeedParens section ' + section + ' pattern ' + pat); roundTripTest(section, f); // Make sure the decompilation is not over-parenthesized. var uf = "" + f; if (pat.indexOf("(xx)") != -1) overParenTest(section, f); // else // print("Skipping the over-parenthesization test, because I don't know how to test for over-parenthesization when the pattern doesn't have parens snugly around it.") } function needParens(section, pat, exp) { print("Testing section " + section + " pattern " + pat); var f, ft; sanityCheck(section, pat); expect = 'SyntaxError'; actual = ''; ft = pat.replace(/xx/, genexp); try { f = new Function(ft); print("Unparenthesized genexp should NOT have been accepted here!"); } catch(e) { /* expected to throw */ actual = e.name; } reportCompare(expect, actual, summary + ': needParens section ' + section + ' pattern ' + pat); expect = 'No Error'; actual = ''; ft = pat.replace(/xx/, genexpParened); try { f = new Function(ft); actual = 'No Error'; } catch(e) { print("Yikes!"); actual = e + ''; } reportCompare(expect, actual, summary + ': needParens section ' + section + ' ft ' + ft); roundTripTest(section, f, exp); overParenTest(section, f, exp); } function rejectLHS(section, pat) { print("Testing section " + section + " pattern " + pat); // sanityCheck(pat); // because 'z' should be accepted as an LHS or binding var ft; expect = 'SyntaxError'; actual = ''; ft = pat.replace(/xx/, genexp) try { new Function(ft); print("That should have been a syntax error!"); actual = 'No Error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, summary + ': rejectLHS section ' + section); } function overParenTest(section, f, exp) { var uf = "" + f; if (uf == exp) return; if (uf.indexOf(genexp) == -1) { print('openParenTest: section ' + section + ' expected ' + exp + ' uf ' + uf + ' genexp optimized away'); return; } reportCompare(false, uf.indexOf(genexpParened) == -1, summary + ': overParenTest genexp snugly in parentheses: section ' + section + ' uf ' + uf); if (uf.indexOf(genexpParened) != -1) { reportCompare(true, uf.indexOf(genexpParenedTwice) == -1, summary + ': overParensTest decompilation should not be over-parenthesized: section ' + ' uf ' + uf); } } function sanityCheck(section, pat) { expect = ''; actual = ''; if (pat.indexOf("xx") == -1) { actual += "No 'xx' in this pattern? "; } var f, ft; ft = pat.replace(/xx/, "z"); try { f = new Function(ft); } catch(e) { actual += "Yowzers! Probably a bogus test!"; } reportCompare(expect, actual, summary + ': sanityCheck section ' + section + ' pattern ' + pat); } function roundTripTest(section, f, exp) { // Decompile var uf = "" + f; // Recompile expect = 'No Error'; actual = ''; var euf; try { euf = eval("(" + uf + ")"); actual = 'No Error'; reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); } catch(e) { actual = e + ''; reportCompare(expect, actual, summary + ': roundTripTest: section ' + section + ' uf ' + uf); return; } // Decompile again and make sure the decompilations match exactly. expect = exp || uf; actual = "" + euf; reportCompare(expect, actual, summary + ': roundTripTest no round-trip change: section ' + section); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-352026.js0000755000175000017500000000265214433667662027123 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352026.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352026; var summary = 'decompilation of yield in argument lists'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function() { z((yield 3)) } expect = 'function() { z((yield 3)); }'; actual = f + ''; compareSource(expect, actual, summary + ': 1'); f = function f(){g((let(a=b)c,d),e)} expect = 'function f(){g((let(a=b)c,d),e);}'; actual = f + ''; compareSource(expect, actual, summary + ': 2'); f = function() { let(s=4){foo:"bar"} } expect = 'function() { let(s=4){foo:"bar";} }'; actual = f + ''; compareSource(expect, actual, summary + ': 3'); f = function() { (let(s=4){foo:"bar"}) } expect = 'function() { let(s=4)({foo:"bar"}); }'; actual = f + ''; compareSource(expect, actual, summary + ': 4'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-352011.js0000755000175000017500000000235614433667662027116 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352011.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352011; var summary = 'decompilation of statements that begin with object literals'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function() { ({}.y = i); } expect = 'function() { ({}.y = i); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { let(x) ({t:x}) } expect = 'function() { let(x) ({t:x}); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { (let(x) {y: z}) } expect = 'function() { let(x) ({y: z}); }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-466905-03.js0000644000175000017500000000317114433667662027351 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-466905-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 466905; var summary = 'decompile anonymous functions returning arrays'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'function () {\n' + 'return [];\n' + '},function () {\n' + 'return [1];\n' + '},function () {\n' + 'return [1, ];\n' + '},function () {\n' + 'return [1, 2];\n' + '},function () {\n' + 'return [1, , 3];\n' + '},function () {\n' + 'return [1, , 3, ];\n' + '},function () {\n' + 'return [(a, b)];\n' + '},function () {\n' + 'return foo((a, b));\n' + '}'; actual = ([ function() { return []; }, function() { return [1]; }, function() { return [1, ]; }, function() { return [1, 2]; }, function() { return [1, , 3]; }, function() { return [1, , 3, ]; }, function() { return [(a, b)]; }, function() { return foo((a, b)); }, ]) + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-352022.js0000755000175000017500000000223614433667662027115 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352022.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352022; var summary = 'decompilation of let, delete and parens'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function() { g(h) = (delete let (y) 3); } actual = f + ''; expect = 'function () {\n g(h) = (let (y) 3, true);\n}'; compareSource(expect, actual, summary + ': 1'); f = function () { g(h) = ((let (y) 3), true);} actual = f + ''; expect = 'function () {\n g(h) = (let (y) 3, true);\n}'; compareSource(expect, actual, summary + ': 2'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-351070-01.js0000755000175000017500000000366014433667662027337 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-351070-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 351070; var summary = 'decompilation of let declaration should not change scope'; var summarytrunk = 'let declaration must be direct child of block or top-level implicit block'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; var c; try { c = '(function () { var a = 2; if (!!true) let a = 3; return a; })'; f = eval(c); expect = 'function ( ) { var a = 2 ; { let a = 3 ; } return a ; }'; actual = f + ''; compareSource(expect, actual, summary); expect = 3; actual = f(); reportCompare(expect, actual, summary); } catch(ex) { // See https://bugzilla.mozilla.org/show_bug.cgi?id=408957 expect = 'SyntaxError'; actual = ex.name; reportCompare(expect, actual, summarytrunk + ': ' + c); } try { c = '(function () { var a = 2; if (!!true) {let a = 3;} return a; })'; f = eval(c); expect = 'function () { var a = 2; { let a = 3;} return a; }'; actual = f + ''; compareSource(expect, actual, summary); expect = 2; actual = f(); reportCompare(expect, actual, summary); } catch(ex) { // See https://bugzilla.mozilla.org/show_bug.cgi?id=408957 expect = 'SyntaxError'; actual = ex.name; reportCompare(expect, actual, summarytrunk + ': ' + c); } exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-380237-03.js0000755000175000017500000001131714433667662027346 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-380237-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 380237; var summary = 'Decompilation of generator expressions'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- expect = 'No Error'; actual = ''; try { var g = ((yield i) for (i in [1,2,3])); actual = 'No Error'; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': top level'); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = (function() { g = (d for (d in [0]) for (e in [1])); }); expect = 'function() { g = (d for (d in [0]) for (e in [1])); }'; actual = f + ''; compareSource(expect, actual, summary + ': see bug 380506'); f = function() { return (1 for (i in [])) }; expect = 'function() { return (1 for (i in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { with((x for (x in []))) { } }; expect = 'function() { with(x for (x in [])) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (0)) }); expect = 'function() { (1 for (w in []) if (false)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (x)) }); expect = 'function() { (1 for (w in []) if (x)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (1 for (w in []) if (1)) }); expect = 'function() { (1 for (w in []) ); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { (x for ([{}, {}] in [])); }); expect = 'function() { (x for ([[], []] in [])); }'; actual = f + ''; compareSource(expect, actual, summary); expect = 'SyntaxError: invalid assignment left-hand side'; actual = ''; try { eval('(function() { (x for each (x in [])) = 3; })'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL'); f = (function() { (x*x for (x in a)); }); expect = 'function() { (x*x for (x in a)); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function () { (1 for (y in (yield 3))); }); expect = 'function () { (1 for (y in yield 3)); }'; actual = f + ''; compareSource(expect, actual, summary); expect = 'SyntaxError: invalid delete operand'; try { eval('(function () { delete (x for (x in [])); })'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': Do not Assert: *pc == JSOP_CALL'); f = (function() { ([yield] for (x in [])); }); expect = 'function() { ([(yield)] for (x in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { if(1, (x for (x in []))) { } }; expect = 'function() { if(1, (x for (x in []))) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = function () {return(i*j for each (i in [1,2,3,4]) for each (j in [5,6,7,8]))}; expect = 'function () {return(i*j for each (i in [1,2,3,4]) ' + 'for each (j in [5,6,7,8]));}'; actual = f + ''; compareSource(expect, actual, summary); expect = 'No Error'; actual = ''; try { var g = ((yield i) for (i in [1,2,3])); actual = 'No Error'; } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': nested'); f = function() { try { } catch(x if (1 for (x in []))) { } finally { } }; expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }'; actual = f + ''; compareSource(expect, actual, summary); f = eval(uneval(f)); expect = 'function() { try {} catch(x if (1 for (x in []))) {} finally {} }'; actual = f + ''; compareSource(expect, actual, summary + ': eval(uneval())'); f = function() { if (1, (x for (x in []))) { } }; expect = 'function() { if (1, (x for (x in []))) { } }'; actual = f + ''; compareSource(expect, actual, summary); f = function() { ((a, b) for (x in [])) }; expect = 'function() { ((a, b) for (x in [])); }'; actual = f + ''; compareSource(expect, actual, summary); f = (function() { ({x setter: (function () {}).x }) }); expect = 'function() { ({x setter: function () {}.x }); }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/browser.js0000644000175000017500000000000014433667662026353 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-346642-01.js0000755000175000017500000001243414433667662027347 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-346642-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 346642; var summary = 'decompilation of destructuring assignment'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function () {({a:{c:x}, b:x}) = ({b:3})} expect = 'function () {({a:{c:x}, b:x} = {b:3});}'; actual = f + ''; compareSource(expect, actual, summary + ': 1'); f = function() { for(; let (x=3,y=4) null;) { } } expect = 'function() { for(; let (x=3,y=4) null;) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 2'); f = function() { let (x=3,y=4) { } x = 5; } expect = 'function() { let (x=3,y=4) { } x = 5; }'; actual = f + ''; compareSource(expect, actual, summary + ': 3'); f = function () { for ([11].x;;) break; } expect = 'function () { for ([11].x;;) { break;} }'; actual = f + ''; compareSource(expect, actual, summary + ': 4'); f = function() { new ({ a: b } = c) } expect = 'function() { new ({ a: b } = c); }'; actual = f + ''; compareSource(expect, actual, summary + ': 5'); f = function () { (let (x) 3)(); } expect = 'function () { (let (x) 3)(); }'; actual = f + ''; compareSource(expect, actual, summary + ': 6'); f = function () { (let (x) 3).foo(); } expect = 'function () { (let (x) 3).foo(); }'; actual = f + ''; compareSource(expect, actual, summary + ': 7'); f = function () { ({x: a(b)}) = window; } expect = 'function () { ({x: a(b)} = window); }'; actual = f + ''; compareSource(expect, actual, summary + ': 8'); f = function() { let ([a]=x) { } } expect = 'function() { let ([a]=x) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 9'); f = function( ){ new ([x] = k) } expect = 'function( ){ new ([x] = k); }'; actual = f + ''; compareSource(expect, actual, summary + ': 10'); f = function() { [a] = [b] = c } expect = 'function() { [a] = [b] = c; }'; actual = f + ''; compareSource(expect, actual, summary + ': 11'); f = function() { [] = 3 } expect = 'function() { [] = 3; }'; actual = f + ''; compareSource(expect, actual, summary + ': 12'); f = function() { ({}) = 3 } expect = 'function() { [] = 3; }'; actual = f + ''; compareSource(expect, actual, summary + ': 13'); f = function () { while( {} = e ) ; } expect = 'function () { while( ( [] = e ) ) {} }'; actual = f + ''; compareSource(expect, actual, summary + ': 14'); f = function () { while( {} = (a)(b) ) ; } expect = 'function () { while( ( [] = a(b) ) ) {} }'; actual = f + ''; compareSource(expect, actual, summary + ': 15'); f = function (){[] = [a,b,c]} expect = 'function (){[] = [a,b,c];}'; actual = f + ''; compareSource(expect, actual, summary + ': 16'); f = function (){for([] = [a,b,c];;);} expect = 'function (){for([] = [a,b,c];;){}}'; actual = f + ''; compareSource(expect, actual, summary + ': 17'); f = function (){for(;;[] = [a,b,c]);} expect = 'function (){for(;;[] = [a,b,c]){}}'; actual = f + ''; compareSource(expect, actual, summary + ': 18'); f = function (){for(let [] = [a,b,c];;);} expect = 'function (){for(let [] = [a,b,c];;){}}'; actual = f + ''; compareSource(expect, actual, summary + ': 19'); f = function() { for (;; [x] = [1]) { } } expect = 'function() { for (;; [x] = [1]) { } } '; actual = f + ''; compareSource(expect, actual, summary + ': 20'); f = function() { [g['//']] = h } expect = 'function() { [g["//"]] = h; }'; actual = f + ''; compareSource(expect, actual, summary + ': 21'); f = (function() { for ( let [a,b]=[c,d] in [3]) { } }) expect = 'function() { [c, d]; for ( let [a,b] in [3]) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 22'); f = function () { while(1) [a] = [b]; } expect = 'function () { while(true) {[a] = [b];} } '; actual = f + ''; compareSource(expect, actual, summary + ': 23'); f = function () { for(var [x, y] = r in p) { } } expect = 'function () { var [x, y] = r; for( [x, y] in p) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 24'); f = function() { for([x] = [];;) { } } expect = 'function() { for([x] = [];;) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 25'); f = function () { let ([y] = delete [1]) { } } expect = 'function () { let ([y] = ([1], true)) { } }'; actual = f + ''; compareSource(expect, actual, summary + ': 26'); f = function () { delete 4..x } expect = 'function () { delete (4).x; }'; actual = f + ''; compareSource(expect, actual, summary + ': 27'); f = function() { return [({ x: y }) = p for (z in 5)] } expect = 'function() { return [{ x: y } = p for (z in 5)]; }'; actual = f + ''; compareSource(expect, actual, summary + ': 28'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-371802.js0000644000175000017500000000177614433667662027131 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-371802.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 371802; var summary = 'Do not assert with group assignment'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = (function (a,b,n){for(var [i,j]=[a,b];i.(1) < let (z) eval('3'); for (x in this) {} }); expect = 'function () { let (x) .((1)) < (let (z) eval("3")); ' + 'for (x in this) {} }'; actual = f + ''; compareSource(expect, actual, summary); // do not crash() f(); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-349634.js0000644000175000017500000000174414433667662027134 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-349634.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 349634; var summary = 'decompilation of {} and let'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function () { let a = 3; { let a = 4; } } actual = f + ''; expect = 'function () {\n var a = 3;\n {\n let a = 4;\n }\n}'; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-350991.js0000755000175000017500000000257614433667662027141 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-350991.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 350991; var summary = 'decompilation of function () { for (let...;...;}} '; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; expect = 'function () {\n for (let (y) 3;;) {\n }\n}'; try { f = eval('(function () { for ((let (y) 3); ;) { } })'); actual = f + ''; } catch(ex) { actual = ex + ''; } compareSource(expect, actual, summary); expect = 'function () {\n var x = 5;\n while (x-- > 0) {\n' + ' for (let x = x, q = 5;;) {\n }\n }\n}'; try { f = function() { let x = 5; while (x-- > 0) { for (let x = x, q = 5;;); } } actual = f + ''; } catch(ex) { actual = ex + ''; } compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-351626.js0000755000175000017500000000167414433667662027133 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-351626.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 351626; var summary = 'decompilation of if(lamda)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function () { if (function () {}) { g(); } } actual = f + ''; expect = 'function () {\n g();\n}'; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_8_1/decompilation/regress-443074.js0000755000175000017500000000174614433667662027132 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-443074.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 443074; var summary = 'Decompilation of genexp in for loop condition'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f = function () { for(; x || (1 for each (y in [])); ) { } }; expect = 'function () { for(; x || true; ) { } }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/changes.sh0000755000175000017500000000321414433667662022320 0ustar apoapo#!/bin/bash # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # usage: changes.sh [prefix] # # combines the {prefix}*possible-fixes.log files into {prefix}possible-fixes.log # and {prefix}*possible-regressions.log files into # possible-regressions.log. # # This script is useful in cases where log files from different machines, branches # and builds are being investigated. export LC_ALL=C if cat /dev/null | sed -r 'q' > /dev/null 2>&1; then SED="sed -r" elif cat /dev/null | sed -E 'q' > /dev/null 2>&1; then SED="sed -E" else echo "Neither sed -r or sed -E is supported" exit 2 fi workfile=`mktemp work.XXXXXXXX` if [ $? -ne 0 ]; then echo "Unable to create working temp file" exit 2 fi for f in ${1}*results-possible-fixes.log*; do case $f in *.log) CAT=cat ;; *.log.bz2) CAT=bzcat ;; *.log.gz) CAT=zcat ;; *.log.zip) CAT="unzip -c" ;; *) echo "unknown log type: $f" exit 2 ;; esac $CAT $f | $SED "s|$|:$f|" >> $workfile done sort -u $workfile > ${1}possible-fixes.log rm $workfile for f in ${1}*results-possible-regressions.log*; do case $f in *.log) CAT=cat ;; *.log.bz2) CAT=bzcat ;; *.log.gz) CAT=zcat ;; *.log.zip) CAT="unzip -c" ;; *) echo "unknown log type: $f" exit 2 ;; esac $CAT $f >> $workfile done sort -u $workfile > ${1}possible-regressions.log rm $workfile closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/0000755000175000017500000000000014433667662021271 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/shell.js0000644000175000017500000000046514433667662022743 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsuite = 'js1_4'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/jsref.js0000644000175000017500000001125214433667662022741 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var completed = false; var testcases; var BUGNUMBER=""; var EXCLUDE = ""; var TT = ""; var TT_ = ""; var BR = ""; var NBSP = " "; var CR = "\n"; var FONT = ""; var FONT_ = ""; var FONT_RED = ""; var FONT_GREEN = ""; var B = ""; var B_ = "" var H2 = ""; var H2_ = ""; var HR = ""; var PASSED = " PASSED!" var FAILED = " FAILED! expected: "; version( 140 ); function test() { for ( tc=0; tc < testcases.length; tc++ ) { testcases[tc].passed = writeTestCaseResult( testcases[tc].expect, testcases[tc].actual, testcases[tc].description +" = "+ testcases[tc].actual ); testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value "; } stopTest(); return ( testcases ); } function TestCase( n, d, e, a ) { this.name = n; this.description = d; this.expect = e; this.actual = a; this.passed = true; this.reason = ""; this.bugnumber = BUGNUMBER; this.passed = getTestCaseResult( this.expect, this.actual ); } function startTest() { /* // JavaScript 1.3 is supposed to be compliant ecma version 1.0 if ( VERSION == "ECMA_1" ) { version ( "130" ); } if ( VERSION == "JS_1.3" ) { version ( "130" ); } if ( VERSION == "JS_1.2" ) { version ( "120" ); } if ( VERSION == "JS_1.1" ) { version ( "110" ); } // for ecma version 2.0, we will leave the javascript version to // the default ( for now ). */ } function getTestCaseResult( expect, actual ) { // because ( NaN == NaN ) always returns false, need to do // a special compare to see if we got the right result. if ( actual != actual ) { if ( typeof actual == "object" ) { actual = "NaN object"; } else { actual = "NaN number"; } } if ( expect != expect ) { if ( typeof expect == "object" ) { expect = "NaN object"; } else { expect = "NaN number"; } } var passed = ( expect == actual ) ? true : false; // if both objects are numbers, give a little leeway for rounding. if ( !passed && typeof(actual) == "number" && typeof(expect) == "number" ) { if ( Math.abs(actual-expect) < 0.0000001 ) { passed = true; } } // verify type is the same if ( typeof(expect) != typeof(actual) ) { passed = false; } return passed; } function writeTestCaseResult( expect, actual, string ) { var passed = getTestCaseResult( expect, actual ); writeFormattedResult( expect, actual, string, passed ); return passed; } function writeFormattedResult( expect, actual, string, passed ) { var s = TT + string ; for ( k = 0; k < (60 - string.length >= 0 ? 60 - string.length : 5) ; k++ ) { // s += NBSP; } s += B ; s += ( passed ) ? FONT_GREEN + NBSP + PASSED : FONT_RED + NBSP + FAILED + expect + TT_ ; print( s + FONT_ + B_ + TT_ ); return passed; } function writeHeaderToLog( string ) { print( H2 + string + H2_ ); } function stopTest() { var sizeTag = "<#TEST CASES SIZE>"; var doneTag = "<#TEST CASES DONE>"; var beginTag = "<#TEST CASE "; var endTag = ">"; print(sizeTag); print(testcases.length); for (tc = 0; tc < testcases.length; tc++) { print(beginTag + 'PASSED' + endTag); print(testcases[tc].passed); print(beginTag + 'NAME' + endTag); print(testcases[tc].name); print(beginTag + 'EXPECTED' + endTag); print(testcases[tc].expect); print(beginTag + 'ACTUAL' + endTag); print(testcases[tc].actual); print(beginTag + 'DESCRIPTION' + endTag); print(testcases[tc].description); print(beginTag + 'REASON' + endTag); print(( testcases[tc].passed ) ? "" : "wrong value "); print(beginTag + 'BUGNUMBER' + endTag); print( BUGNUMBER ); } print(doneTag); gc(); } function getFailedCases() { for ( var i = 0; i < testcases.length; i++ ) { if ( ! testcases[i].passed ) { print( testcases[i].description +" = " +testcases[i].actual +" expected: "+ testcases[i].expect ); } } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/0000755000175000017500000000000014433667662023241 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/shell.js0000644000175000017500000000035314433667662024707 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Functions'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/function-001.js0000644000175000017500000000462614433667662025732 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-001.js'; /** * File Name: function-001.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=324455 * * Earlier versions of JavaScript supported access to the arguments property * of the function object. This property held the arguments to the function. * function f() { * return f.arguments[0]; // deprecated * } * var x = f(3); // x will be 3 * * This feature is not a part of the final ECMA standard. Instead, scripts * should simply use just "arguments": * * function f() { * return arguments[0]; // okay * } * * var x = f(3); // x will be 3 * * Again, this feature was motivated by performance concerns. Access to the * arguments property is not threadsafe, which is of particular concern in * server environments. Also, the compiler can generate better code for * functions because it can tell when the arguments are being accessed only by * name and avoid setting up the arguments object. * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "function-001.js"; var VERSION = "JS1_4"; var TITLE = "Accessing the arguments property of a function object"; var BUGNUMBER="324455"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "return function.arguments", "P", TestFunction_2("P", "A","S","S")[0] +""); new TestCase( SECTION, "return arguments", "P", TestFunction_1( "P", "A", "S", "S" )[0] +""); new TestCase( SECTION, "return arguments when function contains an arguments property", "PASS", TestFunction_3( "P", "A", "S", "S" ) +""); new TestCase( SECTION, "return function.arguments when function contains an arguments property", "PASS", TestFunction_4( "F", "A", "I", "L" ) +""); test(); function TestFunction_1( a, b, c, d, e ) { return arguments; } function TestFunction_2( a, b, c, d, e ) { return TestFunction_2.arguments; } function TestFunction_3( a, b, c, d, e ) { var arguments = "PASS"; return arguments; } function TestFunction_4( a, b, c, d, e ) { var arguments = "PASS"; return TestFunction_4.arguments; } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/browser.js0000644000175000017500000000000014433667662025250 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/CVS/0000755000175000017500000000000014433667662023674 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/CVS/Repository0000644000175000017500000000004114433667662025771 0ustar apoapomozilla/js/tests/js1_4/Functions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/CVS/Entries0000644000175000017500000000020614433667662025226 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:54 2005// /function-001.js/1.5/Sat May 26 00:19:35 2007// /shell.js/1.2/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Functions/CVS/Root0000644000175000017500000000006314433667662024541 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/README0000755000175000017500000000001714433667662022152 0ustar apoapoJavaScript 1.4 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/0000755000175000017500000000000014433667662022160 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/shell.js0000644000175000017500000000034614433667662023630 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Eval'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/eval-002.js0000644000175000017500000000340714433667662023750 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'eval-002.js'; /** * File Name: eval-002.js * Description: (SEE REVISED DESCRIPTION FURTHER BELOW) * * The global eval function may not be accessed indirectly and then called. * This feature will continue to work in JavaScript 1.3 but will result in an * error in JavaScript 1.4. This restriction is also in place for the With and * Closure constructors. * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=324451 * * Author: christine@netscape.com * Date: 11 August 1998 * * * REVISION: 05 February 2001 * Author: pschwartau@netscape.com * * Indirect eval IS NOT ILLEGAL per ECMA3!!! See * * http://bugzilla.mozilla.org/show_bug.cgi?id=38512 * * ------- Additional Comments From Brendan Eich 2001-01-30 17:12 ------- * ECMA-262 Edition 3 doesn't require implementations to throw EvalError, * see the short, section-less Chapter 16. It does say an implementation that * doesn't throw EvalError must allow assignment to eval and indirect calls * of the evalnative method. * */ var SECTION = "eval-002.js"; var VERSION = "JS1_4"; var TITLE = "Calling eval indirectly should NOT fail in version 140"; var BUGNUMBER="38512"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var MY_EVAL = eval; var RESULT = ""; var EXPECT = 1 + "testString" EvalTest(); test(); function EvalTest() { MY_EVAL( "RESULT = EXPECT" ); new TestCase( SECTION, "Call eval indirectly", EXPECT, RESULT ); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/browser.js0000644000175000017500000000000014433667662024167 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/CVS/0000755000175000017500000000000014433667662022613 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/CVS/Repository0000644000175000017500000000003414433667662024712 0ustar apoapomozilla/js/tests/js1_4/Eval closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/CVS/Entries0000644000175000017500000000033214433667662024145 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:54 2005// /eval-001.js/1.6/Sat May 26 00:19:35 2007// /eval-002.js/1.6/Sat May 26 00:19:35 2007// /eval-003.js/1.6/Sat May 26 00:19:35 2007// /shell.js/1.2/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/CVS/Root0000644000175000017500000000006314433667662023460 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/eval-003.js0000644000175000017500000000371114433667662023747 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'eval-003.js'; /** * File Name: eval-003.js * Description: (SEE REVISED DESCRIPTION FURTHER BELOW) * * The global eval function may not be accessed indirectly and then called. * This feature will continue to work in JavaScript 1.3 but will result in an * error in JavaScript 1.4. This restriction is also in place for the With and * Closure constructors. * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=324451 * * Author: christine@netscape.com * Date: 11 August 1998 * * * REVISION: 05 February 2001 * Author: pschwartau@netscape.com * * Indirect eval IS NOT ILLEGAL per ECMA3!!! See * * http://bugzilla.mozilla.org/show_bug.cgi?id=38512 * * ------- Additional Comments From Brendan Eich 2001-01-30 17:12 ------- * ECMA-262 Edition 3 doesn't require implementations to throw EvalError, * see the short, section-less Chapter 16. It does say an implementation that * doesn't throw EvalError must allow assignment to eval and indirect calls * of the evalnative method. * */ var SECTION = "eval-003.js"; var VERSION = "JS1_4"; var TITLE = "Calling eval indirectly should NOT fail in version 140"; var BUGNUMBER="38512"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var MY_EVAL = eval; var RESULT = ""; var EXPECT= ""; var h = function f(x,y){var g = function(z){return Math.exp(z);}; return g(x+y);}; new EvalTest(); test(); function EvalTest() { with( this ) { MY_EVAL( "RESULT = h(-1, 1)" ); EXPECT = 1; //The base e to the power (-1 + 1), i.e. the power 0, equals 1 .... new TestCase( SECTION, "Call eval indirectly", EXPECT, RESULT ); } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Eval/eval-001.js0000644000175000017500000000332214433667662023743 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'eval-001.js'; /** * File Name: eval-001.js * Original Description: (SEE REVISED DESCRIPTION FURTHER BELOW) * * The global eval function may not be accessed indirectly and then called. * This feature will continue to work in JavaScript 1.3 but will result in an * error in JavaScript 1.4. This restriction is also in place for the With and * Closure constructors. * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=324451 * * Author: christine@netscape.com * Date: 11 August 1998 * * * REVISION: 05 February 2001 * Author: pschwartau@netscape.com * * Indirect eval IS NOT ILLEGAL per ECMA3!!! See * * http://bugzilla.mozilla.org/show_bug.cgi?id=38512 * * ------- Additional Comments From Brendan Eich 2001-01-30 17:12 ------- * ECMA-262 Edition 3 doesn't require implementations to throw EvalError, * see the short, section-less Chapter 16. It does say an implementation that * doesn't throw EvalError must allow assignment to eval and indirect calls * of the evalnative method. * */ var SECTION = "eval-001.js"; var VERSION = "JS1_4"; var TITLE = "Calling eval indirectly should NOT fail in version 140"; var BUGNUMBER="38512"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var MY_EVAL = eval; var RESULT = ""; var EXPECT = "abcdefg"; MY_EVAL( "RESULT = EXPECT" ); new TestCase( SECTION, "Call eval indirectly", EXPECT, RESULT ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/browser.js0000644000175000017500000000043614433667662023315 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Exceptions/0000755000175000017500000000000014433667662023412 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Exceptions/CVS/0000755000175000017500000000000014433667662024045 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Exceptions/CVS/Repository0000644000175000017500000000004214433667662026143 0ustar apoapomozilla/js/tests/js1_4/Exceptions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Exceptions/CVS/Entries0000644000175000017500000000000214433667662025371 0ustar apoapoD closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Exceptions/CVS/Root0000644000175000017500000000006314433667662024712 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/0000755000175000017500000000000014433667662022703 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/shell.js0000644000175000017500000000035114433667662024347 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Regress'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/function-001.js0000644000175000017500000000261214433667662025365 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-001.js'; /** * File Name: function-001.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=325843 * js> function f(a){var a,b;} * * causes an an assert on a null 'sprop' in the 'Variables' function in * jsparse.c This will crash non-debug build. * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "function-001.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 325843"; var BUGNUMBER="3258435"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); eval("function f1 (a){ var a,b; }"); function f2( a ) { var a, b; }; new TestCase( SECTION, "eval(\"function f1 (a){ var a,b; }\"); "+ "function f2( a ) { var a, b; }; typeof f1", "function", typeof f1 ); // force a function decompilation new TestCase( SECTION, "typeof f1.toString()", "string", typeof f1.toString() ); new TestCase( SECTION, "typeof f2", "function", typeof f2 ); // force a function decompilation new TestCase( SECTION, "typeof f2.toString()", "string", typeof f2.toString() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/function-003.js0000644000175000017500000000270114433667662025366 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-003.js'; /** * File Name: function-003.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=104766 * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "toString-001.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 104766"; var BUGNUMBER="310514"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "StripSpaces(Array.prototype.concat.toString()).substring(0,17)", "functionconcat(){", StripSpaces(Array.prototype.concat.toString()).substring(0,17)); test(); function StripSpaces( s ) { for ( var currentChar = 0, strippedString=""; currentChar < s.length; currentChar++ ) { if (!IsWhiteSpace(s.charAt(currentChar))) { strippedString += s.charAt(currentChar); } } return strippedString; } function IsWhiteSpace( string ) { var cc = string.charCodeAt(0); switch (cc) { case (0x0009): case (0x000B): case (0x000C): case (0x0020): case (0x000A): case (0x000D): case ( 59 ): // let's strip out semicolons, too return true; break; default: return false; } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/date-001-n.js0000644000175000017500000000213414433667662024707 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'date-001-n.js'; /** * File Name: date-001-n.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=299903 * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "date-001-n.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 299903"; var BUGNUMBER="299903"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); function MyDate() { this.foo = "bar"; } MyDate.prototype = new Date(); DESCRIPTION = "function MyDate() { this.foo = \"bar\"; }; MyDate.prototype = new Date(); new MyDate().toString()"; EXPECTED = "error"; new TestCase( SECTION, "function MyDate() { this.foo = \"bar\"; }; "+ "MyDate.prototype = new Date(); " + "new MyDate().toString()", "error", new MyDate().toString() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/toString-001-n.js0000644000175000017500000000176214433667662025611 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'toString-001-n.js'; /** * File Name: toString-001-n.js * Description: * * Function.prototype.toString is not generic. * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "toString-001.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 310514"; var BUGNUMBER="310514"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var o = {}; o.toString = Function.prototype.toString; DESCRIPTION = "var o = {}; o.toString = Function.prototype.toString; o.toString();"; EXPECTED = "error"; new TestCase( SECTION, "var o = {}; o.toString = Function.prototype.toString; o.toString();", "error", o.toString() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/function-002.js0000644000175000017500000000524614433667662025374 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-002.js'; /** * File Name: function-002.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=330462 * js> function f(a){var a,b;} * * causes an an assert on a null 'sprop' in the 'Variables' function in * jsparse.c This will crash non-debug build. * * Author: christine@netscape.com * Date: 11 August 1998 * REVISED: 04 February 2001 * (changed the comma expressions from trivial to non-trivial) * Author: pschwartau@netscape.com * * Brendan: "The test seemed to require something that ECMA does not * guarantee, and that JS1.4 didn't either. For example, given * * dec2 = "function f2(){1,2}"; * * the engine is free to decompile a function object compiled from this source, * via Function.prototype.toString(), into some other string that compiles to * an equivalent function. The engine now eliminates the useless comma expression * 1,2, giving function f2(){}. This should be legal by the testsuite's lights." * */ var SECTION = "function-002.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 325843"; var BUGNUMBER="330462"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); dec1 = "function f1(x,y){++x, --y}"; dec2 = "function f2(){var y; f1(1,2); y=new Date(); print(y.toString())}"; eval(dec1); eval(dec2); new TestCase( SECTION, "typeof f1", "function", typeof f1 ); // force a function decompilation new TestCase( SECTION, "f1.toString() == dec1", true, StripSpaces(f1.toString()) == StripSpaces(dec1)); new TestCase( SECTION, "typeof f2", "function", typeof f2 ); // force a function decompilation new TestCase( SECTION, "f2.toString() == dec2", true, StripSpaces(f2.toString().replace(/new Date\(\)/g, 'new Date')) == StripSpaces(dec2.replace(/new Date\(\)/g, 'new Date'))); test(); function StripSpaces( s ) { var strippedString = ""; for ( var currentChar = 0; currentChar < s.length; currentChar++ ) { if (!IsWhiteSpace(s.charAt(currentChar))) { strippedString += s.charAt(currentChar); } } return strippedString; } function IsWhiteSpace( string ) { var cc = string.charCodeAt(0); switch (cc) { case (0x0009): case (0x000B): case (0x000C): case (0x0020): case (0x000A): case (0x000D): case ( 59 ): // let's strip out semicolons, too return true; break; default: return false; } } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/regress-7224.js0000644000175000017500000000353514433667662025315 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'regress-7224.js'; /** * File Name: regress-7224.js * Reference: js1_2 * Description: Remove support for the arg * Author: ** replace with your e-mail address ** */ var SECTION = "regress"; // provide a document reference (ie, ECMA section) var VERSION = "JS1_4"; // Version of JavaScript or ECMA var TITLE = "Regression test for bugzilla #7224"; // Provide ECMA section title or a description var BUGNUMBER = "http://bugzilla.mozilla.org/show_bug.cgi?id=7224"; // Provide URL to bugsplat or bugzilla report startTest(); // leave this alone /* * Calls to AddTestCase here. AddTestCase is a function that is defined * in shell.js and takes three arguments: * - a string representation of what is being tested * - the expected result * - the actual result * * For example, a test might look like this: * * var zip = /[\d]{5}$/; * * AddTestCase( * "zip = /[\d]{5}$/; \"PO Box 12345 Boston, MA 02134\".match(zip)", // description of the test * "02134", // expected result * "PO Box 12345 Boston, MA 02134".match(zip) ); // actual result * */ var f = new Function( "return arguments.caller" ); var o = {}; o.foo = f; o.foo("a", "b", "c"); AddTestCase( "var f = new Function( 'return arguments.caller' ); f()", undefined, f() ); AddTestCase( "var o = {}; o.foo = f; o.foo('a')", undefined, o.foo('a') ); test(); // leave this alone. this executes the test cases and // displays results. closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/browser.js0000644000175000017500000000000014433667662024712 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/CVS/0000755000175000017500000000000014433667662023336 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/CVS/Repository0000644000175000017500000000003714433667662025440 0ustar apoapomozilla/js/tests/js1_4/Regress closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/CVS/Entries0000644000175000017500000000065014433667662024673 0ustar apoapo/browser.js/1.1/Fri Mar 18 19:09:54 2005// /date-001-n.js/1.6/Sat May 26 00:19:36 2007// /function-001.js/1.5/Sat May 26 00:19:36 2007// /function-002.js/1.9/Sat May 26 00:19:36 2007// /function-003.js/1.6/Sat May 26 00:19:36 2007// /function-004-n.js/1.5/Sat May 26 00:19:36 2007// /regress-7224.js/1.6/Sat May 26 00:19:36 2007// /shell.js/1.2/Sat May 26 00:19:36 2007// /toString-001-n.js/1.5/Sat May 26 00:19:36 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/CVS/Root0000644000175000017500000000006314433667662024203 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/Regress/function-004-n.js0000644000175000017500000000172714433667662025631 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestfile = 'function-004-n.js'; /** * File Name: function-004.js * Description: * * http://scopus.mcom.com/bugsplat/show_bug.cgi?id=310502 * * Author: christine@netscape.com * Date: 11 August 1998 */ var SECTION = "funtion-004-n.js"; var VERSION = "JS1_4"; var TITLE = "Regression test case for 310502"; var BUGNUMBER="310502"; startTest(); writeHeaderToLog( SECTION + " "+ TITLE); var o = {}; o.call = Function.prototype.call; DESCRIPTION = "var o = {}; o.call = Function.prototype.call; o.call()"; EXPECTED = "error"; new TestCase( SECTION, "var o = {}; o.call = Function.prototype.call; o.call()", "error", o.call() ); test(); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/CVS/0000755000175000017500000000000014433667662021724 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/CVS/Repository0000644000175000017500000000002714433667662024025 0ustar apoapomozilla/js/tests/js1_4 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/CVS/Entries0000644000175000017500000000025014433667662023255 0ustar apoapo/README/1.1/Fri Mar 18 19:09:53 2005// /browser.js/1.20/Sat May 26 00:19:35 2007// /jsref.js/1.2/Mon Oct 30 16:48:35 2006// /shell.js/1.17/Sat May 26 00:19:35 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/CVS/Root0000644000175000017500000000006314433667662022571 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_4/CVS/Entries.Log0000644000175000017500000000010214433667662023771 0ustar apoapoA D/Eval//// A D/Exceptions//// A D/Functions//// A D/Regress//// closure-compiler-20130227+dfsg1/rhino/testsrc/tests/runtests.sh0000755000175000017500000002046614433667662022607 0ustar apoapo#!/bin/bash -e # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. if [[ -z "$TEST_DIR" ]]; then cat <&1 | tee -a $testlogfilelist testlogfiles="`grep '^log:' $testlogfilelist|sed 's|^log: ||'`" fatalerrors=`grep 'FATAL ERROR' $testlogfiles | cat` if [[ -n "$fatalerrors" ]]; then testlogarray=( $testlogfiles ) let itestlog=${#testlogarray[*]}-1 error "`tail -n 20 ${testlogarray[$itestlog]}`" $LINENO fi for testlogfile in $testlogfiles; do if [[ -n "$DEBUG" ]]; then dumpvars testlogfile fi case "$testlogfile" in *,js,*) testtype=shell;; *,firefox,*) testtype=browser;; *,thunderbird,*) testtype=browser;; *,fennec,*) testtype=browser;; *) error "unknown testtype in logfile $testlogfile" $LINENO;; esac case "$testlogfile" in *,opt,*) buildtype=opt;; *,debug,*) buildtype=debug;; *,nightly*) buildtype=opt;; *) error "unknown buildtype in logfile $testlogfile" $LINENO;; esac branch=`echo $testlogfile | sed 's|.*,\([0-9]\.[0-9]*\.[0-9]*\).*|\1|'` repo=`grep -m 1 '^environment: TEST_MOZILLA_HG=' $testlogfile | sed 's|.*TEST_MOZILLA_HG=http://hg.mozilla.org.*/\([^\/]*\)|\1|'` if [[ -z "$repo" ]]; then repo=CVS fi debug "repo=$repo" outputprefix=$testlogfile if [[ -n "$DEBUG" ]]; then dumpvars branch buildtype testtype OSID testlogfile TEST_PROCESSORTYPE TEST_KERNEL outputprefix fi if ! $TEST_DIR/tests/mozilla.org/js/known-failures.pl \ -b $branch \ -T $buildtype \ -R $repo \ -t $testtype \ -J "$javascriptoptions" \ -o "$OSID" \ -K "$TEST_KERNEL" \ -A "$TEST_PROCESSORTYPE" \ -M "$TEST_MEMORY" \ -z `date +%z` \ -l $testlogfile \ -r $TEST_JSDIR/failures.txt \ -O $outputprefix; then error "known-failures.pl" $LINENO fi if [[ -n "$summary" ]]; then # use let to work around mac problem where numbers were # output with leading characters. # if let's arg evaluates to 0, let will return 1 # so we need to test if let npass="`grep TEST_RESULT=PASSED ${outputprefix}-results-all.log | wc -l`"; then true; fi if let nfail="`cat ${outputprefix}-results-failures.log | wc -l`"; then true; fi if let nfixes="`cat ${outputprefix}-results-possible-fixes.log | wc -l`"; then true; fi if let nregressions="`cat ${outputprefix}-results-possible-regressions.log | wc -l`"; then true; fi echo -e "\nJavaScript Tests $branch $buildtype $testtype\n" echo -e "\nFailures:\n" cat "${outputprefix}-results-failures.log" echo -e "\nPossible Fixes:\n" cat "${outputprefix}-results-possible-fixes.log" echo -e "\nPossible Regressions:\n" cat "${outputprefix}-results-possible-regressions.log" echo -e "\nTinderboxPrint:
      \n" echo -e "\nTinderboxPrint:js tests
      $branch $buildtype $testtype
      $npass/$nfail
      F:$nfixes R:$nregressions" echo -e "\nTinderboxPrint:
      \n" fi done closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js-test-driver-standards.html0000644000175000017500000000156014433667662026104 0ustar apoapo{# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. #} JavaScript Test Driver - standards mode closure-compiler-20130227+dfsg1/rhino/testsrc/tests/slow-n-1.9.1.tests0000644000175000017500000000147014433667662023322 0ustar apoapoe4x/Regress/regress-319872.js ecma/Date/15.9.5.10-2.js ecma_3/Array/regress-322135-03.js ecma_3/Array/regress-322135-04.js ecma_3/RegExp/regress-307456.js ecma_3/RegExp/regress-330684.js js1_5/Array/regress-465980-02.js js1_5/GC/regress-338653.js js1_5/GC/regress-346794.js js1_5/GC/regress-348532.js js1_5/Regress/regress-271716-n.js js1_5/Regress/regress-303213.js js1_5/Regress/regress-451322.js js1_5/Regress/regress-484693.js js1_5/extensions/regress-342960.js js1_5/extensions/regress-345967.js js1_5/extensions/regress-350531.js js1_6/extensions/regress-455464-04.js js1_7/extensions/regress-458679.js js1_8/extensions/regress-476414-01.js js1_8/extensions/regress-476414-02.js js1_8/extensions/regress-476427.js js1_8/regress/regress-464096.js js1_8_1/regress/regress-452498-168-2.js js1_8_1/trace/regress-451673.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/known-failures.pl0000755000175000017500000004602014433667662023657 0ustar apoapo#!/usr/bin/perl # -*- Mode: Perl; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. use strict; use Getopt::Mixed "nextOption"; # predeclarations sub debug; sub usage; sub parse_options; sub escape_string; sub escape_pattern; sub unescape_pattern; # option arguments my $option_desc = "b=s branch>b T=s buildtype>T R=s repo>R t=s testtype>t o=s os>o K=s kernel>K A=s arch>A M=s memory>M z=s timezone>z J=s jsoptions>J l=s rawlogfile>l f=s failurelogfile>f r=s patterns>r O=s outputprefix>O D debug>D"; my $testid; my $branch; my $repo; my $buildtype; my $testtype; my $rawlogfile; my $failurelogfile; my $os; my $patterns; my $timezone; my $jsoptions; my $outputprefix; my $arch; my $kernel; my $memory; my $debug = $ENV{DEBUG}; # pattern variables my $knownfailurebranchpattern; my $failurebranchpattern; my $knownfailureospattern; my $failureospattern; my $knownfailurerepopattern; my $failurerepopattern; my $knownfailurebuildtypepattern; my $failurebuildtypepattern; my $knownfailuretesttypepattern; my $failuretesttypepattern; my $knownfailuretimezonepattern; my $failuretimezonepattern; my $knownfailurejsoptionspattern; my $failurejsoptionspattern; my $knownfailurearchpattern; my $failurearchpattern; my $knownfailurekernelpattern; my $failurekernelpattern; my $knownfailurememorypattern; my $failurememorypattern; my @patterns; my $pattern; my @failures; my @fixes; my @excludedtests; my $excludedtest; my $excludedfile; my %includedtests = {}; my $includedfile; my @results; my $regchars = '\[\^\-\]\|\{\}\?\*\+\.\<\>\$\(\)'; &parse_options; my $jsdir = $ENV{TEST_JSDIR}; if (!defined($jsdir)) { $jsdir = "/work/mozilla/mozilla.com/test.mozilla.com/www/tests/mozilla.org/js"; } my @excludedfiles = ("excluded-$branch-$testtype-$buildtype.tests"); my @includedfiles = ("included-$branch-$testtype-$buildtype.tests"); # create working patterns file consisting of matches to users selection # and which has the test description patterns escaped # remove the excluded tests from the possible fixes log foreach $excludedfile ( @excludedfiles ) { open EXCLUDED, "<$jsdir/$excludedfile" or die "Unable to open excluded file $jsdir/$excludedfile: $!\n"; while () { chomp; next if ($_ =~ /^\#/); s/\s+$//; push @excludedtests, ($_); } close EXCLUDED; } @excludedtests = sort @excludedtests; foreach $includedfile ( @includedfiles ) { open INCLUDED, "<$jsdir/$includedfile" or die "Unable to open included file $jsdir/$includedfile: $!\n"; while () { chomp; next if ($_ =~ /^\#/); s/\s+$//; $includedtests{$_} = 1; } close INCLUDED; } debug "loading patterns $patterns"; debug "pattern filter: ^TEST_ID=[^,]*, TEST_BRANCH=$knownfailurebranchpattern, TEST_REPO=$knownfailurerepopattern, TEST_BUILDTYPE=$knownfailurebuildtypepattern, TEST_TYPE=$knownfailuretesttypepattern, TEST_OS=$knownfailureospattern, TEST_KERNEL=$knownfailurekernelpattern, TEST_PROCESSORTYPE=$knownfailurearchpattern, TEST_MEMORY=$knownfailurememorypattern, TEST_TIMEZONE=$knownfailuretimezonepattern, TEST_OPTIONS=$knownfailurejsoptionspattern,"; open PATTERNS, "<$patterns" or die "Unable to open known failure patterns file $patterns: $!\n"; while () { chomp; s/\s+$//; ($testid) = $_ =~ /^TEST_ID=([^,]*),/; if (!$includedtests{$testid}) { debug "test $testid was not included during this run"; } elsif ($_ =~ /^TEST_ID=[^,]*, TEST_BRANCH=$knownfailurebranchpattern, TEST_REPO=$knownfailurerepopattern, TEST_BUILDTYPE=$knownfailurebuildtypepattern, TEST_TYPE=$knownfailuretesttypepattern, TEST_OS=$knownfailureospattern, TEST_KERNEL=$knownfailurekernelpattern, TEST_PROCESSORTYPE=$knownfailurearchpattern, TEST_MEMORY=$knownfailurememorypattern, TEST_TIMEZONE=$knownfailuretimezonepattern, TEST_OPTIONS=$knownfailurejsoptionspattern,/) { debug "adding pattern : $_"; push @patterns, (escape_pattern($_)); } else { debug "skipping pattern: $_"; } } close PATTERNS; # create a working copy of the current failures which match the users selection debug "failure filter: ^TEST_ID=[^,]*, TEST_BRANCH=$failurebranchpattern, TEST_REPO=$failurerepopattern, TEST_BUILDTYPE=$failurebuildtypepattern, TEST_TYPE=$failuretesttypepattern, TEST_OS=$failureospattern, TEST_KERNEL=$failurekernelpattern, TEST_PROCESSORTYPE=$failurearchpattern, TEST_MEMORY=$failurememorypattern, TEST_TIMEZONE=$failuretimezonepattern, TEST_OPTIONS=$failurejsoptionspattern, TEST_RESULT=FAIL[^,]*,/"; if (defined($rawlogfile)) { $failurelogfile = "$outputprefix-results-failures.log"; my $alllog = "$outputprefix-results-all.log"; debug "writing failures $failurelogfile"; open INPUTLOG, "$jsdir/post-process-logs.pl $rawlogfile |" or die "Unable to open $rawlogfile $!\n"; open ALLLOG, ">$alllog" or die "Unable to open $alllog $!\n"; open FAILURELOG, ">$failurelogfile" or die "Unable to open $failurelogfile $!\n"; while () { chomp; print ALLLOG "$_\n"; if ($_ =~ /^TEST_ID=[^,]*, TEST_BRANCH=$failurebranchpattern, TEST_REPO=$failurerepopattern, TEST_BUILDTYPE=$failurebuildtypepattern, TEST_TYPE=$failuretesttypepattern, TEST_OS=$failureospattern, TEST_KERNEL=$failurekernelpattern, TEST_PROCESSORTYPE=$failurearchpattern, TEST_MEMORY=$failurememorypattern, TEST_TIMEZONE=$failuretimezonepattern, TEST_OPTIONS=$failurejsoptionspattern, TEST_RESULT=FAIL[^,]*,/) { debug "failure: $_"; push @failures, ($_); print FAILURELOG "$_\n"; } } close INPUTLOG; my $inputrc = $?; close ALLLOG; close FAILURELOG; die "FATAL ERROR in post-process-logs.pl" if $inputrc != 0; } else { debug "loading failures $failurelogfile"; my $failurelogfilemode; if ($failurelogfile =~ /\.bz2$/) { $failurelogfilemode = "bzcat $failurelogfile|"; } elsif ($failurelogfile =~ /\.gz$/) { $failurelogfilemode = "zcat $failurelogfile|"; } else { $failurelogfilemode = "<$failurelogfile"; } open FAILURES, "$failurelogfilemode" or die "Unable to open current failure log $failurelogfile: $!\n"; while () { chomp; if ($_ =~ /^TEST_ID=[^,]*, TEST_BRANCH=$failurebranchpattern, TEST_REPO=$failurerepopattern, TEST_BUILDTYPE=$failurebuildtypepattern, TEST_TYPE=$failuretesttypepattern, TEST_OS=$failureospattern, TEST_KERNEL=$failurekernelpattern, TEST_PROCESSORTYPE=$failurearchpattern, TEST_MEMORY=$failurememorypattern, TEST_TIMEZONE=$failuretimezonepattern, TEST_OPTIONS=$failurejsoptionspattern, TEST_RESULT=FAIL[^,]*,/) { debug "failure: $_"; push @failures, ($_); } } close FAILURES; } debug "finding fixed bugs"; unlink "$outputprefix-results-possible-fixes.log"; foreach $pattern (@patterns) { # look for known failure patterns that don't have matches in the # the current failures selected by the user. debug "searching for matches to $pattern\n"; @results = grep m@^$pattern@, @failures; if ($debug) { my $failure; foreach $failure (@failures) { if ($failure =~ $pattern) { debug "MATCH: $pattern - $failure\n"; } else { debug "NOMATCH: $pattern - $failure\n"; } } } if ($#results == -1) { debug "fix: '$pattern'"; push @fixes, ($pattern) } } foreach $excludedtest ( @excludedtests ) { # remove any potential fixes which are due to the test being excluded if ($debug) { @results = grep m@$excludedtest@, @fixes; if ($#results > -1) { print "excluding: " . (join ', ', @results) . "\n"; } } @results = grep !m@$excludedtest@, @fixes; @fixes = @results; } my $fix; open OUTPUT, ">$outputprefix-results-possible-fixes.log" or die "Unable to open $outputprefix-results-possible-fixes.log: $!"; foreach $fix (@fixes) { print OUTPUT unescape_pattern($fix) . "\n"; if ($debug) { debug "fix: $fix"; } } close OUTPUT; print STDOUT "log: $outputprefix-results-possible-fixes.log\n"; debug "finding regressions"; my $pass = 0; my $changed = ($#patterns != -1); debug "changed=$changed, \$#patterns=$#patterns, \$#failures=$#failures"; while ($changed) { $pass = $pass + 1; $changed = 0; debug "pass $pass"; foreach $pattern (@patterns) { debug "Pattern: $pattern"; my @nomatches = grep !m@^$pattern@, @failures; my @matches = grep m@^$pattern@, @failures; if ($debug) { my $temp = join ', ', @nomatches; debug "nomatches: $#nomatches $temp"; $temp = join ', ', @matches; debug "matches: $#matches $temp"; } @failures = @nomatches; if ($#matches > -1) { $changed = 1; } debug "*****************************************"; } } debug "\$#excludedtests=$#excludedtests, \$#failures=$#failures"; foreach $excludedtest ( @excludedtests ) { if ($debug) { @results = grep m@$excludedtest@, @failures; if ($#results > -1) { print "excluding: " . (join ', ', @results) . "\n"; } } @results = grep !m@$excludedtest@, @failures; debug "\$#results=$#results, \$excludedtest=$excludedtest, \$#failures=$#failures"; @failures = @results; } debug "possible regressions: \$#failures=$#failures"; open OUTPUT, ">$outputprefix-results-possible-regressions.log" or die "Unable to open $outputprefix-results-possible-regressions.log: $!"; my $failure; foreach $failure (@failures) { print OUTPUT "$failure\n"; if ($debug) { debug "regression: $failure"; } } close OUTPUT; print STDOUT "log: $outputprefix-results-possible-regressions.log\n"; sub debug { if ($debug) { my $msg = shift; print STDERR "DEBUG: $msg\n"; } } sub usage { my $msg = shift; print STDERR < Core JavaScript Tests

      Core JavaScript Tests

      Browser Test Options

      <script > DOCTYPE Output Format Failures only

      closure-compiler-20130227+dfsg1/rhino/testsrc/tests/post-process-logs.pl0000755000175000017500000004203214433667662024315 0ustar apoapo#!/usr/bin/perl -w # -*- Mode: Perl; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. use Getopt::Mixed "nextOption"; use File::Temp qw/ tempfile tempdir /; use File::Basename; sub dbg; sub outresults; sub outputrecord; local $file; local $temp; my $debug = $ENV{DEBUG}; my $test_dir = $ENV{TEST_DIR}; die "FATAL ERROR: environment variable TEST_DIR must be set to the Sisyphus root directory" unless ($test_dir); # required for mac os x 10.5 to prevent sort from # complaining about illegal byte sequences $ENV{LC_ALL} = 'C'; (undef, $temp) = tempfile(); open TEMP, ">$temp" or die "FATAL ERROR: Unable to open temporary file $temp for writing: $!\n"; local ($test_id, $tmp_test_id, $tmp_test_exit_status, %test_id, %test_reported, $test_result, $test_type, $tmp_test_type, $test_description, $test_jsoptions, @messages, $test_processortype, $test_kernel, $test_suite, $test_exit_status, @expected_exit_code_list, $expected_exit_code, $exit_code, $state); local $test_memory = 0; local %test_reported = (); local $test_repo = 'CVS'; $test_jsoptions = 'none'; while ($file = shift @ARGV) { @messages = (); dbg "file: $file"; my $filename = basename $file; dbg "filename: $file"; local ($test_date, $test_product, $test_branchid, $test_buildtype, $test_os, $test_machine,$test_global_target) = split /,/, $filename; $test_branchid =~ s/[^0-9.]//g; $test_global_target =~ s/.log$//; local ($test_timezone) = $test_date; $test_timezone =~ s/.*([-+]\d{4,4})/$1/; my $filemode; if ($file =~ /\.bz2$/) { $filemode = "bzcat $file|"; } elsif ($file =~ /\.gz$/) { $filemode = "zcat $file|"; } else { $filemode = "<$file"; } open FILE, "$filemode" or die "FATAL ERROR: unable to open $file for reading: $!\n"; dbg "process header with environment variables used in test"; while () { $state = 'failure'; chomp; # remove carriage returns, bels and other annoyances. $_ =~ s/[\r]$//; $_ =~ s/[\r]/CR/g; $_ =~ s/[\x01-\x08]//g; $_ =~ s/\s+$//; if ($debug) { dbg "\nINPUT: $_"; } last if ( $_ =~ /^include:/); if (($envval) = $_ =~ /^environment: TEST_MOZILLA_HG=http:\/\/hg.mozilla.org.*\/([^\/]+)/ ) { $test_repo = $envval; } elsif (($envvar, $envval) = $_ =~ /^environment: (TEST_[A-Z0-9_]*)=(.*)/ ) { dbg "envvar=$envvar, envval=$envval"; if ($envvar =~ /TEST_KERNEL/) { $envval =~ s/([0-9]+)\.([0-9]+)\.([0-9]+).*/$1.$2.$3/; dbg "found TEST_KERNEL"; } $envvar =~ tr/A-Z/a-z/; $$envvar = $envval; dbg $envvar . "=" . $$envvar; } elsif (($envval) = $_ =~ /^environment: OSID=(.*)/ ) { $test_os = $envval; } if ($_ =~ /^arguments: javascriptoptions/) { my ($o, @s, $j); ($o) = $_ =~ /^arguments: javascriptoptions=(.*)/; $o =~ s/(-\w) (\w)/$1$2/g; @s = sort split / /, $o; $j = join(" ", @s); $j =~ s/(-\w)(\w)/$1 $2/g; $test_jsoptions = $j || "none"; dbg "javascriptoptions: $test_jsoptions"; } } if ($test_product eq "js") { $test_type = "shell"; } elsif ($test_product eq "firefox" || $test_product eq "thunderbird" || $test_product eq "fennec") { $test_buildtype = "nightly" unless $test_buildtype; $test_type = "browser"; } # Expected sequence if all output written to the log. # # Input # ----------------------------- # JavaScriptTest: Begin Run # JavaScriptTest: Begin Test t; # jstest: t # t:.*EXIT STATUS: # JavaScriptTest: End Test t # JavaScriptTest: End Run # EOF # %test_id = (); @messages = (); $test_exit_status = ''; $state = 'idle'; while () { chomp; if ($debug) { dbg "\nINPUT: '$_'"; } $_ =~ s/[\r]$//; $_ =~ s/[\r]/CR/g; $_ =~ s/[\x01-\x08]//g; $_ =~ s/\s+$//; if ( /^JavaScriptTest: Begin Run/) { dbg "Begin Run"; if ($state eq 'idle') { $state = 'beginrun'; } else { warn "WARNING: state: $state, expected: idle, log: $file"; $state = 'beginrun'; } } elsif ( ($tmp_test_id) = $_ =~ /^JavaScriptTest: Begin Test ([^ ]*)/) { dbg "Begin Test: $tmp_test_id"; if ($state eq 'beginrun' || $state eq 'endtest') { $state = 'runningtest'; } else { warn "WARNING: state: $state, expected: beginrun, endtest, log: $file"; $state = 'runningtest'; } $test_id{$state} = $tmp_test_id; @messages = (); @expected_exit_code_list = (); $expected_exit_code = (); $test_id = ''; $test_result = ''; $test_exit_status = 'NORMAL'; # default to normal, so subtests will have a NORMAL status $test_description = ''; push @expected_exit_code_list, (3) if ($tmp_test_id =~ /-n.js$/); } elsif ( ($expected_exit_code) = $_ =~ /WE EXPECT EXIT CODE ([0-9]*)/ ) { dbg "Expected Exit Code: $expected_exit_code"; push @expected_exit_code_list, ($expected_exit_code); } elsif ( ($tmp_test_id) = $_ =~ /^jstest: (.*?) *bug:/) { dbg "jstest: $tmp_test_id"; # if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) # { # warn "WARNING: state: $state, expected runningtest, reportingtest. mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; # } if ($state eq 'runningtest') { $state = 'reportingtest'; } elsif ($state eq 'reportingtest') { $state = 'reportingtest'; } else { warn "WARNING: test_id: $test_id{$state}, state: $state, expected: runningtest, reportingtest, log: $file"; $state = 'reportingtest'; } ($test_result) = $_ =~ /result: (.*?) *type:/; ($tmp_test_type) = $_ =~ /type: (.*?) *description:/; die "FATAL ERROR: test_id: $test_id{$state}, jstest test type mismatch: start test_type: $test_type, current test_type: $tmp_test_type, test state: $state, log: $file" if ($test_type ne $tmp_test_type); ($test_description) = $_ =~ /description: (.*)/; if (!$test_description) { $test_description = ""; } $test_description .= '; messages: ' . (join '; ', @messages) . ';'; outputrecord $tmp_test_id, $test_description, $test_result; $test_id{$state} = $tmp_test_id; } elsif ( $state ne 'idle' && (($tmp_test_id) = $_ =~ /^([^:]*):.* EXIT STATUS: NORMAL/)) { $test_exit_status = 'NORMAL'; dbg "Exit Status Normal: $tmp_test_id, $test_exit_status"; if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) { warn "WARNING: state: $state, mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; } if ($state eq 'reportingtest' || $state eq 'runningtest') { $state = 'exitedtest'; } else { warn "WARNING: state: $state, expected: reportingtest, runningtest, log: $file"; $state = 'exitedtest'; } if (! $test_reported{$tmp_test_id}) { dbg "No test results reported: $tmp_test_id"; $test_result = 'FAILED'; $test_description = 'No test results reported; messages: ' . (join '; ', @messages) . ';'; outputrecord $tmp_test_id, $test_description, $test_result; } $test_id{$state} = $tmp_test_id; } elsif ( $state ne 'idle' && (($tmp_test_id) = $_ =~ /^([^:]*):.* EXIT STATUS: TIMED OUT/)) { $test_exit_status = 'TIMED OUT'; dbg "Exit Status Timed Out: $tmp_test_id, $test_exit_status"; if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) { warn "WARNING: state: $state, mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; } if ($state eq 'reportingtest' || $state eq 'runningtest') { $state = 'exitedtest'; } else { dbg "state: $state, expected: reportingtest, runningtest"; $state = 'exitedtest'; } $test_result = 'FAILED'; $test_description .= '; messages: ' . (join '; ', @messages) . ';'; outputrecord $tmp_test_id, $test_description, $test_result; $test_id{$state} = $tmp_test_id; } elsif ( $state ne 'idle' && (($tmp_test_id, $tmp_test_exit_status) = $_ =~ /^([^:]*):.* EXIT STATUS: (CRASHED signal [0-9]+ [A-Z]+) \([0-9.]+ seconds\)/)) { $test_exit_status = $tmp_test_exit_status; dbg "Exit Status Crashed: $tmp_test_id, $test_exit_status"; if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) { warn "WARNING: state: $state, mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; } if ($state eq 'reportingtest' || $state eq 'runningtest') { $state = 'exitedtest'; } else { dbg "state: $state, expected: reportingtest, runningtest"; $state = 'exitedtest'; } $test_result = 'FAILED'; $test_description .= '; messages: ' . (join '; ', @messages) . ';'; outputrecord $tmp_test_id, $test_description, $test_result; $test_id{$state} = $tmp_test_id; } elsif ( $state ne 'idle' && (($tmp_test_id, $tmp_test_exit_status) = $_ =~ /^([^:]*):.* EXIT STATUS: (ABNORMAL [0-9]+) \([0-9.]+ seconds\)/)) { $test_exit_status = $tmp_test_exit_status; dbg "Exit Status Abnormal: $tmp_test_id, $test_exit_status"; if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) { warn "WARNING: state: $state, mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; } if ($state eq 'reportingtest' || $state eq 'runningtest') { $state = 'exitedtest'; } else { dbg "state: $state, expected: reportingtest, runningtest"; $state = 'exitedtest'; } ($exit_code) = $test_exit_status =~ /ABNORMAL ([0-9]+)/; if (grep /$exit_code/, @expected_exit_code_list) { $test_result = 'PASSED'; } else { $test_result = 'FAILED'; } $test_description .= '; messages: ' . (join '; ', @messages) . ';'; dbg "Exit Code: $exit_code, Test Result: $test_result, Expected Exit Codes: " . (join '; ', @expected_exit_code_list); outputrecord $tmp_test_id, $test_description, $test_result; $test_id{$state} = $tmp_test_id; } elsif ( ($tmp_test_id) = $_ =~ /^JavaScriptTest: End Test ([^ ]*)/) { dbg "End Test: $tmp_test_id"; if ($test_id{$state} && $tmp_test_id ne $test_id{$state}) { warn "WARNING: state: $state, mismatched test_id: expected: $tmp_test_id, actual: $test_id{$state}, log: $file"; } if ($state eq 'exitedtest' || $state eq 'runningtest' || $state eq 'reportingtest') { $state = 'endtest'; } else { warn "WARNING: state: $state, expected: runningtest, reportingtest, exitedtest, log: $file"; $state = 'endtest'; } $test_id{$state} = $tmp_test_id; } elsif ( /^JavaScriptTest: End Run/) { dbg "End Run"; if ($state eq 'endtest') { $state = 'endrun'; } else { warn "WARNING: state: $state, expected: endtest, log: $file"; $state = 'endrun'; } } elsif ($_ && !/^\s+$/ && !/^(STATUS:| *PASSED!| *FAILED!)/ && !/^JavaScriptTest:/ && !/^[*][*][*]/ && !/^[-+]{2,2}(WEBSHELL|DOMWINDOW)/ && !/^Spider:/ && !/real.*user.*sys.*$/ && !/user.*system.*elapsed/) { if ('runningtest, reportingtest' =~ /$state/ && $#messages < 1000) { # limit the number of processed and collected messages since firefox can # go berserk and dump a couple of million output lines for a single test # if things go horribly wrong. if (/error: can.t allocate region/ || /set a breakpoint in malloc_error_break/ || /set a breakpoint in szone_error to debug/ || /malloc:.*mmap/ || /vm_allocate/ || /terminate called after throwing an instance of .*bad_alloc/) { dbg "Adding message: $_ converted to /$test_id{$state}:0: out of memory"; push @messages, ('/' . $test_id{$state} . ':0: out of memory'); } elsif (/\.js, line [0-9]+: out of memory/ ) { s/\.js, line ([0-9]+): out of memory/\.js:$1:/; dbg "Adding message: $_ converted to /$test_id{$state}:0: out of memory"; push @messages, ('/' . $test_id{$state} . ':0: out of memory'); } else { dbg "Adding message: $_"; push @messages, ($_); } } } elsif ($debug) { dbg "Skipping: $_"; } if ($debug) { if ($test_id{$state}) { dbg "test_id{$state}=$test_id{$state}, " . (join '; ', @messages); } else { dbg "state=$state, " . (join '; ', @messages); } } } if ($state eq 'endrun') { $state = 'success'; } die "FATAL ERROR: Test run terminated prematurely. state: $state, log: $file" if ($state ne 'success'); } close FILE; close TEMP; undef $test_branchid; undef $test_date; undef $test_buildtype; undef $test_machine; undef $test_product; undef $test_suite; outresults; unlink $temp; sub dbg { if ($debug) { my $msg = shift; print STDERR "DEBUG: $msg\n"; } } sub outresults { dbg "sorting temp file $temp"; system("sort -u < $temp"); dbg "finished sorting"; } sub outputrecord { my ($test_id, $test_description, $test_result) = @_; # cut off the extra jstest: summaries as they duplicate the other # output and follow it. $test_description =~ s/jstest:.*//; # if (length($test_description) > 6000) # { # $test_description = substr($test_description, 0, 6000); # } # my $output = "TEST_ID=$test_id, " . "TEST_BRANCH=$test_branchid, " . "TEST_REPO=$test_repo, " . "TEST_BUILDTYPE=$test_buildtype, " . "TEST_TYPE=$test_type, " . "TEST_OS=$test_os, " . "TEST_KERNEL=$test_kernel, " . "TEST_PROCESSORTYPE=$test_processortype, " . "TEST_MEMORY=$test_memory, " . "TEST_TIMEZONE=$test_timezone, " . "TEST_OPTIONS=$test_jsoptions, " . "TEST_RESULT=$test_result, " . "TEST_EXITSTATUS=$test_exit_status, " . "TEST_DESCRIPTION=$test_description, " . "TEST_MACHINE=$test_machine, " . "TEST_DATE=$test_date" . "\n"; if ($debug) { dbg "RECORD: $output"; } print TEMP $output; $test_reported{$test_id} = 1; @messages = (); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/menufoot.html0000644000175000017500000000054714433667662023101 0ustar apoapo{# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. #}
      closure-compiler-20130227+dfsg1/rhino/testsrc/tests/mkhtml.pl0000644000175000017500000000530014433667662022200 0ustar apoapo#!/ns/tools/bin/perl5 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # mkhtml.pl cruises through your $MOZ_SRC/mozilla/js/tests/ subdirectories, # and for any .js file it finds, creates an HTML file that includes: # $MOZ_SRC/mozilla/js/tests/$suite/shell.js, $ # MOZ_SRC/mozilla/js/tests/$suite/browser.js, # and the test.js file. # # $moz_src = $ENV{"MOZ_SRC"} || die ("You need to set your MOZ_SRC environment variable.\n"); $test_home = $moz_src ."/js/tests/"; opendir (TEST_HOME, $test_home); @__suites = readdir (TEST_HOME); closedir TEST_HOME; foreach (@__suites ) { if ( -d $_ && $_ !~ /\./ && $_ !~ 'CVS' ) { $suites[$#suites+1] = $_; } } if ( ! $ARGV[0] ) { die ( "Specify a directory: ". join(" ", @suites) ."\n" ); } $js_test_dir = $moz_src . "/js/tests/" . $ARGV[0] ."/"; print "Generating html files for the tests in $js_test_dir\n"; $shell_js = $js_test_dir . "shell.js"; $browser_js = $js_test_dir . "browser.js"; # cd to the test directory chdir $js_test_dir || die "Couldn't chdir to js_test_dir, which is $js_test_dir\n"; print ( "js_test_dir is $js_test_dir\n" ); # read the test directory opendir ( JS_TEST_DIR, $js_test_dir ); # || die "Couldn't open js_test_dir, which is $js_test_dir\n"; @js_test_dir_items = readdir( JS_TEST_DIR ); # || die "Couldn't read js_test_dir, which is $js_test_dir\n"; closedir( JS_TEST_DIR ); print ("The js_test_dir_items are: " . join( ",", @js_test_dir_items ) . "\n"); # figure out which of the items are directories foreach $js_test_subdir ( @js_test_dir_items ) { if ( -d $js_test_subdir ) { $js_test_subdir = $js_test_dir ."/" . $js_test_subdir; chdir $js_test_subdir || die "Couldn't chdir to js_test_subdir $js_test_subdir\n"; print "Just chdir'd to $js_test_subdir \n"; opendir( JS_TEST_SUBDIR, $js_test_subdir ); @subdir_tests = readdir( JS_TEST_SUBDIR ); closedir( JS_TEST_SUBDIR ); foreach ( @subdir_tests ) { $js_test = $_; if ( $_ =~ /\.js$/ ) { s/\.js$/\.html/; print $_ ."\n"; open( HTML_TEST, "> $_") || die "Can't open html file $test_html\n"; print HTML_TEST ''; print HTML_TEST ''; print HTML_TEST ''; close HTML_TEST; } } } chdir $js_test_dir; } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/performance-1.9.2.tests0000644000175000017500000000116314433667662024404 0ustar apoapoecma_3/RegExp/regress-289669.js ecma_3/RegExp/regress-311414.js js1_5/Array/regress-99120-01.js js1_5/Array/regress-99120-02.js js1_5/GC/regress-383269-01.js js1_5/GC/regress-383269-02.js js1_5/Regress/regress-169559.js js1_5/Regress/regress-313967-01.js js1_5/Regress/regress-313967-02.js js1_5/Regress/regress-347306-01.js js1_5/Regress/regress-416628.js js1_5/String/regress-157334-01.js js1_5/String/regress-314890.js js1_5/String/regress-322772.js js1_5/String/regress-56940-01.js js1_5/String/regress-56940-02.js js1_5/extensions/regress-335700.js js1_5/extensions/regress-347306-02.js js1_5/extensions/regress-363258.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/spidermonkey-n-1.9.0.tests0000755000175000017500000000436414433667662025056 0ustar apoapo# Obsolete SpiderMonkey tests # # invalidated by bug 10278 # js1_2/function/function-001-n.js js1_3/Script/function-001-n.js js1_3/regress/function-001-n.js # # WONTFIX bug 119719 # js1_5/Regress/regress-119719.js # # Spidermonkey now defaults lineNumber and fileName # to the location and file where the exception occured. # exclude original test which assumes the defaults are # 0 and '' # js1_5/extensions/regress-50447.js # # Invalid bug # e4x/Regress/regress-278112.js # # behavior changed to match MSIE/Opera/etc # see bug 309840 # js1_5/Regress/regress-173067.js # # per comment in bug, this test is obsolete # for spidermonkey and rhino. # ecma_3/Statements/regress-121744.js # # remove version dependent tests # see bug 325921 # js1_2/Array/array_split_1.js js1_2/Array/tostring_1.js js1_2/Array/tostring_2.js js1_2/Objects/toString-001.js js1_2/String/concat.js js1_2/function/Function_object.js js1_2/function/Number.js js1_2/function/String.js js1_2/function/function-001-n.js js1_2/function/length.js js1_2/function/regexparg-2-n.js js1_2/function/tostring-1.js js1_2/function/tostring-2.js js1_2/operator/equality.js js1_2/regexp/RegExp_lastIndex.js js1_2/regexp/string_split.js js1_2/version120/boolean-001.js js1_2/version120/regress-99663.js js1_3/Script/delete-001.js js1_3/Script/function-001-n.js js1_3/regress/delete-001.js js1_3/regress/function-001-n.js # # tests not yet implemented # e4x/TypeConversion/10.5.1.js e4x/TypeConversion/10.5.js e4x/TypeConversion/10.6.1.js e4x/TypeConversion/10.6.js e4x/Types/9.1.1.10.js e4x/Types/9.1.1.11.js e4x/Types/9.1.1.12.js e4x/Types/9.1.1.13.js e4x/Types/9.1.1.4.js e4x/Types/9.1.1.5.js e4x/Types/9.1.1.7.js e4x/Types/9.1.1.8.js e4x/Types/9.2.1.10.js e4x/Types/9.2.1.3.js e4x/Types/9.2.1.4.js e4x/Types/9.2.1.5.js e4x/Types/9.2.1.6.js e4x/Types/9.2.1.7.js e4x/XML/13.4.4.1.js ecma_2/RegExp/exec-001.js ecma_2/String/replace-001.js # # pre ecma warnings are doa # js1_5/Regress/regress-106244.js # # do not ignore unicode formatting chars/trunk - bug 274152 # ecma_3/Unicode/uc-001.js # bug 408002 js1_5/Regress/regress-320119.js # bug 380469 js1_7/geniter/regress-347739.js js1_7/geniter/regress-349012-01.js js1_7/geniter/regress-349331.js js1_7/iterable/regress-340526-02.js # bug 452498 js1_5/decompilation/regress-352073.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/importList.html0000644000175000017500000000140514433667662023405 0ustar apoapo{# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. #}

      closure-compiler-20130227+dfsg1/rhino/testsrc/tests/remove-fixed-failures.sh0000755000175000017500000000243214433667662025113 0ustar apoapo#!/bin/bash # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. if [[ ! -e "$1" || ! -e "$2" ]]; then cat < ${workfailures}.temp mv $workfailures.temp $workfailures mv $workfailures $failures closure-compiler-20130227+dfsg1/rhino/testsrc/tests/slow-n-1.9.2.tests0000644000175000017500000000142614433667662023324 0ustar apoapoe4x/Regress/regress-319872.js ecma/Date/15.9.5.10-2.js ecma_3/Array/regress-322135-03.js ecma_3/Array/regress-322135-04.js ecma_3/RegExp/regress-307456.js ecma_3/RegExp/regress-330684.js js1_5/Array/regress-465980-02.js js1_5/GC/regress-338653.js js1_5/GC/regress-346794.js js1_5/GC/regress-348532.js js1_5/Regress/regress-303213.js js1_5/Regress/regress-451322.js js1_5/Regress/regress-484693.js js1_5/extensions/regress-342960.js js1_5/extensions/regress-345967.js js1_5/extensions/regress-350531.js js1_6/extensions/regress-455464-04.js js1_7/extensions/regress-458679.js js1_8/extensions/regress-476414-01.js js1_8/extensions/regress-476414-02.js js1_8/extensions/regress-476427.js js1_8/regress/regress-464096.js js1_8_1/regress/regress-452498-168-2.js js1_8_1/trace/regress-451673.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/0000755000175000017500000000000014433667662021273 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/shell.js0000755000175000017500000000061014433667662022740 0ustar apoapo/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsuite = 'js1_6'; // explicitly turn on js16 if (typeof version != 'undefined') { version(160); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/0000755000175000017500000000000014433667662022541 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/shell.js0000755000175000017500000000035014433667662024207 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'String'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/browser.js0000755000175000017500000000000014433667662024553 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/CVS/0000755000175000017500000000000014433667662023174 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/CVS/Repository0000644000175000017500000000003614433667662025275 0ustar apoapomozilla/js/tests/js1_6/String closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/CVS/Entries0000644000175000017500000000021014433667662024521 0ustar apoapo/browser.js/1.2/Mon Oct 30 16:48:37 2006// /regress-306591.js/1.3/Sat May 26 00:19:40 2007// /shell.js/1.3/Sat May 26 00:19:40 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/CVS/Root0000644000175000017500000000006314433667662024041 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/String/regress-306591.js0000755000175000017500000000447114433667662025327 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-306591.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 306591; var summary = 'String static methods'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); printStatus ('See https://bugzilla.mozilla.org/show_bug.cgi?id=304828'); expect = ['a', 'b', 'c'].toString(); actual = String.split(new String('abc'), '').toString(); reportCompare(expect, actual, summary + " String.split(new String('abc'), '')"); expect = '2'; actual = String.substring(new Number(123), 1, 2); reportCompare(expect, actual, summary + " String.substring(new Number(123), 1, 2)"); expect = 'TRUE'; actual = String.toUpperCase(new Boolean(true)); reportCompare(expect, actual, summary + " String.toUpperCase(new Boolean(true))"); // null means the global object is passed expect = (typeof window == 'undefined') ? 9 : -1; actual = String.indexOf(null, 'l'); reportCompare(expect, actual, summary + " String.indexOf(null, 'l')"); expect = 2; actual = String.indexOf(String(null), 'l'); reportCompare(expect, actual, summary + " String.indexOf(String(null), 'l')"); expect = ['a', 'b', 'c'].toString(); actual = String.split('abc', '').toString(); reportCompare(expect, actual, summary + " String.split('abc', '')"); expect = '2'; actual = String.substring(123, 1, 2); reportCompare(expect, actual, summary + " String.substring(123, 1, 2)"); expect = 'TRUE'; actual = String.toUpperCase(true); reportCompare(expect, actual, summary + " String.toUpperCase(true)"); // null means the global object is passed expect = (typeof window == 'undefined') ? -1 : 11; actual = String.indexOf(undefined, 'd'); reportCompare(expect, actual, summary + " String.indexOf(undefined, 'd')"); expect = 2; actual = String.indexOf(String(undefined), 'd'); reportCompare(expect, actual, summary + " String.indexOf(String(undefined), 'd')"); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/README0000755000175000017500000000001714433667662022154 0ustar apoapoJavaScript 1.6 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/0000755000175000017500000000000014433667662023472 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/shell.js0000644000175000017500000000035414433667662025141 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'extensions'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-479567.js0000644000175000017500000000146514433667662026273 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-479567.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 479567; var summary = 'Do not assert: thing'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); function f() { (eval("(function(){for each (y in [false, false, false]);});"))(); } try { this.__defineGetter__("x", gc); uneval(this.watch("y", this.toSource)) f(); throw x; } catch(ex) { } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-470310.js0000644000175000017500000000220314433667662026233 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-470310.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 470310; var summary = 'Do not assert: (uint32)((atoms - script->atomMap.vector + ' + '((uintN)(((regs.pc + 0)[1] << 8) | (regs.pc + 0)[2])))) < objects_->length'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: 6 is not a function'; this.__defineSetter__('m', [].map); function f() { for (var j = 0; j < 4; ++j) if (j == 3) m = 6; } try { f(); } catch(e) { print(actual = e + ''); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-312385-01.js0000755000175000017500000000530014433667662026464 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-312385-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 312385; var summary = 'Generic methods with null or undefined |this|'; var actual = ''; var expect = true; var voids = [null, undefined]; var generics = { String: [{ quote: [] }, { substring: [] }, { toLowerCase: [] }, { toUpperCase: [] }, { charAt: [] }, { charCodeAt: [] }, { indexOf: [] }, { lastIndexOf: [] }, { toLocaleLowerCase: [] }, { toLocaleUpperCase: [] }, { localeCompare: [] }, { match: [/(?:)/] }, // match(regexp) { search: [] }, { replace: [] }, { split: [] }, { substr: [] }, { concat: [] }, { slice: [] }], Array: [{ join: [] }, { reverse: [] }, { sort: [] }, // { push: [0] }, // push(item1, ...) // { pop: [] }, // { shift: [] }, { unshift: [] }, // { splice: [0, 0, 1] }, // splice(start, deleteCount, item1, ...) { concat: [] }, { indexOf: [] }, { lastIndexOf: [] }, // forEach is excluded since it does not return a value... /* { forEach: [noop] }, // forEach(callback, thisObj) */ { map: [noop] }, // map(callback, thisObj) { filter: [noop] }, // filter(callback, thisObj) { some: [noop] }, // some(callback, thisObj) { every: [noop] } // every(callback, thisObj) ] }; printBugNumber(BUGNUMBER); printStatus (summary); for (var c in generics) { var methods = generics[c]; for (var i = 0; i < methods.length; i++) { var method = methods[i]; for (var methodname in method) { for (var v = 0; v < voids.length; v++) { var lhs = c + '.' + methodname + '(' + voids[v] + (method[methodname].length ?(', ' + method[methodname].toString()):'') + ')'; var rhs = c + '.prototype.' + methodname + '.apply(' + voids[v] + ', ' + method[methodname].toSource() + ')'; var expr = lhs + ' == ' + rhs; printStatus('Testing ' + expr); try { printStatus('lhs ' + lhs + ': ' + eval(lhs)); } catch(ex) { printStatus(ex + ''); } try { printStatus('rhs ' + rhs + ': ' + eval(rhs)); } catch(ex) { printStatus(ex + ''); } try { actual = comparelr(eval(lhs), eval(rhs)); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, expr); printStatus(''); } } } } function comparelr(lhs, rhs) { if (lhs.constructor.name != 'Array') { return (lhs == rhs); } return (lhs.toSource() == rhs.toSource()); } function noop() { } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-455464-04.js0000755000175000017500000000160714433667662026503 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455464-04.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455464; var summary = 'Do not assert with JIT, gczeal 2: !TRACE_RECORDER(cx) ^ (jumpTable == recordingJumpTable)'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); if (typeof gczeal == 'undefined') { expect = actual = 'Test requires gczeal, skipped.'; } else { jit(true); gczeal(2); a=b=c=d=0; this.__defineGetter__('g', gc); for each (y in this); gczeal(0); jit(false); } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-465443.js0000644000175000017500000000207214433667662026252 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-465443.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 465443; var summary = ''; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for/in left-hand side'; jit(true); try { eval('(function () { const b = 16; var out = []; for each (b in [true, "", true, "", true, ""]) out.push(b >> 1) })();'); } catch(ex) { actual = ex + ''; } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-457521.js0000644000175000017500000000134114433667662026246 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-457521.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 457521; var summary = 'Do not crash @ js_DecompileValueGenerator'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); try { this.__defineSetter__("x", [,,,].map); this.watch("x", (new Function("var y, eval"))); x = true; } catch(ex) { } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-455464-02.js0000755000175000017500000000175514433667662026505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455464-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455464; var summary = 'Do not assert with JIT: !TRACE_RECORDER(cx) ^ (jumpTable == recordingJumpTable)'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); a=b=c=d=0; this.__defineGetter__('g', gc); for each (y in this); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-472508.js0000644000175000017500000000150314433667662026250 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-472508.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 472508; var summary = 'TM: Do not crash @ TraceRecorder::emitTreeCall'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); for (var x in this) { } var a = [false, false, false]; a.__defineGetter__("q", function() { }); a.__defineGetter__("r", function() { }); for (var i = 0; i < 2; ++i) for each (var e in a) { } jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/browser.js0000644000175000017500000000000014433667662025501 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-475144.js0000644000175000017500000000144614433667662026255 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-475144.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 475144; var summary = 'TM: Do not assert: !JS_ON_TRACE(cx)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); function a() {} function b() {} function c() {} eval("this.__defineGetter__(\"\", function(){ return new Function } )"); [[].some for each (x in this) for each (y in /x/g)]; jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-352392.js0000755000175000017500000000213214433667662026250 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352392.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352392; var summary = 'Do not hang/crash |for each| over object with getter set to map'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for each loop'; try { var obj = { }; obj.y getter = Array.prototype.map; eval('(function() { for each(let z in obj) { } })()'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-385393-08.js0000755000175000017500000000126514433667662026512 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-385393-08.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 385393; var summary = 'Regression test for bug 385393'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); try { this.__proto__ = []; [1,2,3,4].map.call(); } catch(ex) { } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/CVS/0000755000175000017500000000000014433667662024125 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/CVS/Repository0000644000175000017500000000004214433667662026223 0ustar apoapomozilla/js/tests/js1_6/extensions closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/CVS/Entries0000644000175000017500000000152614433667662025465 0ustar apoapo/browser.js/1.1/Fri Feb 9 00:00:52 2007// /regress-312385-01.js/1.2/Sat May 26 00:19:41 2007// /regress-352392.js/1.2/Sat May 26 00:19:41 2007// /regress-385393-08.js/1.1/Fri Aug 10 21:41:58 2007// /regress-414098.js/1.2/Sat Feb 21 01:30:12 2009// /regress-455464-01.js/1.1/Wed Sep 17 18:34:45 2008// /regress-455464-02.js/1.1/Wed Sep 17 18:34:45 2008// /regress-455464-03.js/1.1/Wed Sep 17 18:34:45 2008// /regress-455464-04.js/1.3/Sat Feb 21 01:30:12 2009// /regress-456826.js/1.1/Sun Oct 12 03:49:42 2008// /regress-457521.js/1.1/Fri Mar 6 01:38:53 2009// /regress-465443.js/1.2/Sat Feb 21 01:30:12 2009// /regress-470310.js/1.1/Sat Feb 21 01:30:12 2009// /regress-472508.js/1.1/Fri Mar 6 01:38:53 2009// /regress-475144.js/1.1/Fri Mar 6 01:38:53 2009// /regress-479567.js/1.1/Mon Feb 22 18:53:44 2010// /shell.js/1.2/Sat May 26 00:19:41 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/CVS/Root0000644000175000017500000000006314433667662024772 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-456826.js0000755000175000017500000000567214433667662026273 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-456826.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 456826; var summary = 'Do not assert with JIT during OOM'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); if (typeof gcparam != 'undefined') { gcparam("maxBytes", 22000); } const numRows = 600; const numCols = 600; var scratch = {}; var scratchZ = {}; function complexMult(a, b) { var newr = a.r * b.r - a.i * b.i; var newi = a.r * b.i + a.i * b.r; scratch.r = newr; scratch.i = newi; return scratch; } function complexAdd(a, b) { scratch.r = a.r + b.r; scratch.i = a.i + b.i; return scratch; } function abs(a) { return Math.sqrt(a.r * a.r + a.i * a.i); } function computeEscapeSpeed(c) { scratchZ.r = scratchZ.i = 0; const scaler = 5; const threshold = (colors.length - 1) * scaler + 1; for (var i = 1; i < threshold; ++i) { scratchZ = complexAdd(c, complexMult(scratchZ, scratchZ)); if (scratchZ.r * scratchZ.r + scratchZ.i * scratchZ.i > 4) { return Math.floor((i - 1) / scaler) + 1; } } return 0; } const colorStrings = [ "black", "green", "blue", "red", "purple", "orange", "cyan", "yellow", "magenta", "brown", "pink", "chartreuse", "darkorange", "crimson", "gray", "deeppink", "firebrick", "lavender", "lawngreen", "lightsalmon", "lime", "goldenrod" ]; var colors = []; function createMandelSet(realRange, imagRange) { var start = new Date(); // Set up our colors for each (var color in colorStrings) { var [r, g, b] = [0, 0, 0]; colors.push([r, g, b, 0xff]); } var realStep = (realRange.max - realRange.min)/numCols; var imagStep = (imagRange.min - imagRange.max)/numRows; for (var i = 0, curReal = realRange.min; i < numCols; ++i, curReal += realStep) { for (var j = 0, curImag = imagRange.max; j < numRows; ++j, curImag += imagStep) { var c = { r: curReal, i: curImag } var n = computeEscapeSpeed(c); } } print(Date.now() - start); } var realRange = { min: -2.1, max: 2 }; var imagRange = { min: -2, max: 2 }; createMandelSet(realRange, imagRange); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-414098.js0000644000175000017500000000245014433667662026252 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-414098.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 414098; var summary = 'Getter behavior on arrays'; var actual = ''; var expect = ''; var a=[1,2,3]; var foo = 44; a.__defineGetter__(1, function() { return foo + 10; }); actual = String(a); reportCompare("1,54,3", actual, "getter 1"); actual = String(a.reverse()); reportCompare("3,54,1", actual, "reverse"); var s = ""; a.forEach(function(e) { s += e + "|"; }); actual = s; reportCompare("3|54|1|", actual, "forEach"); actual = a.join(' - '); reportCompare("3 - 54 - 1", actual, "join"); try { actual = String(a.sort()); } catch(ex) { actual = ex + ''; } reportCompare("TypeError: setting a property that has only a getter", actual, "sort"); try { a[2]=3; } catch(ex) { actual = ex + ''; } reportCompare("TypeError: setting a property that has only a getter", actual, "setter"); actual = a.pop(); reportCompare(3, actual, "pop"); actual = a.pop(); reportCompare(54, actual, "pop 2"); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-455464-01.js0000755000175000017500000000137014433667662026475 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455464-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455464; var summary = 'Do not assert with JIT: !TRACE_RECORDER(cx) ^ (jumpTable == recordingJumpTable)'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); jit(true); a=b=c=d=0; this.__defineGetter__('g', gc); for each (y in this); jit(false); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/extensions/regress-455464-03.js0000755000175000017500000000205714433667662026502 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-455464-03.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 455464; var summary = 'Do not assert with JIT: !TRACE_RECORDER(cx) ^ (jumpTable == recordingJumpTable)'; var actual = 'No Crash'; var expect = 'No Crash'; a=b=c=d=0; this.__defineGetter__('g', gc); for each (y in this); //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); a=b=c=d=0; this.__defineGetter__('g', gc); for each (y in this); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/browser.js0000755000175000017500000000043514433667662023321 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/0000755000175000017500000000000014433667662022705 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-352271.js0000755000175000017500000000170014433667662025457 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352271.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352271; var summary = 'Do not crash with |getter| |for each|'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { eval('[window.x getter= t for each ([*].a(v) in [])]'); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-311157-01.js0000755000175000017500000000121614433667662025675 0ustar apoapo/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-311157-01.js'; var BUGNUMBER = 311157; var summary = 'Comment-hiding compromise left E4X parsing/scanning inconsistent'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); try { eval('var x = \n there '); } catch(e) { } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/shell.js0000644000175000017500000000035114433667662024351 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Regress'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-351795.js0000755000175000017500000000175714433667662025505 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-351795.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 351795; var summary = 'Do not assert: top < ss->printer->script->depth'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { p={}; (p.z = [1].some(function(y) { return y > 0; }) ? 4 : [6])(5); } catch(ex) { print(ex + ''); } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-311157-02.js0000755000175000017500000000122314433667662025674 0ustar apoapo/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-311157-02.js'; var BUGNUMBER = 311157; var summary = 'Comment-hiding compromise left E4X parsing/scanning inconsistent'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); try { eval('var x = \n there '); } catch(e) { } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-355002.js0000755000175000017500000000202014433667662025446 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-355002.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 355002; var summary = 'Do not assert on |for each (this in []) { }|'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'SyntaxError: invalid for/in left-hand side'; actual = ''; try { eval('for each (this in []) { }'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-378492.js0000755000175000017500000000170614433667662025502 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-378492.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 378492; var summary = 'namespace_trace/qname_trace should check for null private, ' + 'WAY_TOO_MUCH_GC'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); options('xml'); x = ; for each(x.t in x) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-372565.js0000755000175000017500000000173614433667662025500 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-372565.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 372565; var summary = 'Do not assert: top < ss->printer->script->depth" decompiling a function where a const identifier is used as a for-loop variable'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); (function() { for each(x in y) { } const x; }); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-475469.js0000644000175000017500000000212414433667662025474 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-475469.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 475469; var summary = 'TM: Do not crash @ FramePCOffset'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // print('Note this test originally required jit enabled with the environment '); // print('variable TRACEMONKEY=verbose defined. Note that the calls to enable '); // print('jit are necessary for the crash.'); jit(true); [1,2,3].map(/a/gi); jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-382509.js0000755000175000017500000000260214433667662025470 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-382509.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 382509; var summary = 'Disallow non-global indirect eval'; var actual = ''; var expect = ''; var global = typeof window == 'undefined' ? this : window; var object = {}; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); global.foo = eval; global.a = 'global'; expect = 'global indirect'; actual = String(['a+" indirect"'].map(global.foo)); reportCompare(expect, actual, summary + ': global indirect'); object.foo = eval; object.a = 'local'; expect = 'EvalError: function eval must be called directly, and not by way of a function of another name'; try { actual = String(['a+" indirect"'].map(object.foo, object)); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary + ': local indirect'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-353078.js0000755000175000017500000000172414433667662025473 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-353078.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 353078; var summary = 'Do not assert with bogus toString, map, split'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { this.toString = function() { return {}; }; p = [11].map('foo'.split); } catch(ex) { } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-320172.js0000755000175000017500000000133314433667662025454 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-320172.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 320172; var summary = 'Regression from bug 285219'; var actual = 'No Crash'; var expect = 'No Crash'; enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { (function xxx(){ ["var x"].forEach(eval); })(); } catch(ex) { } printStatus('No Crash'); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/browser.js0000644000175000017500000000000014433667662024714 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-301574.js0000755000175000017500000000154414433667662025465 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-301574.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 301574; var summary = 'E4X should be enabled even when e4x=1 not specified'; var actual = 'No error'; var expect = 'No error'; printBugNumber(BUGNUMBER); printStatus (summary); try { var xml = XML(''); } catch(e) { actual = 'error: ' + e; } reportCompare(expect, actual, summary + ': XML()'); try { var xml = XMLList('

      text

      '); } catch(e) { actual = 'error: ' + e; } reportCompare(expect, actual, summary + ': XMLList()'); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/CVS/0000755000175000017500000000000014433667662023340 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/CVS/Repository0000644000175000017500000000003714433667662025442 0ustar apoapomozilla/js/tests/js1_6/Regress closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/CVS/Entries0000644000175000017500000000151214433667662024673 0ustar apoapo/browser.js/1.2/Mon Oct 30 16:48:36 2006// /regress-301574.js/1.4/Sat May 26 00:19:40 2007// /regress-311157-01.js/1.2/Sat May 26 00:19:40 2007// /regress-311157-02.js/1.2/Sat May 26 00:19:40 2007// /regress-314887.js/1.2/Sat May 26 00:19:40 2007// /regress-320172.js/1.2/Sat May 26 00:19:40 2007// /regress-350417.js/1.2/Sat May 26 00:19:40 2007// /regress-351795.js/1.1/Thu Feb 7 14:10:49 2008// /regress-352271.js/1.2/Sat May 26 00:19:40 2007// /regress-353078.js/1.3/Tue Aug 18 08:09:36 2009// /regress-355002.js/1.2/Sat May 26 00:19:40 2007// /regress-372565.js/1.4/Thu Jul 12 17:49:31 2007// /regress-378492.js/1.2/Sat May 26 00:19:40 2007// /regress-382509.js/1.2/Wed Jun 13 20:51:06 2007// /regress-475469.js/1.1/Fri Mar 6 01:38:52 2009// /regress-476655.js/1.1/Fri Mar 20 05:01:03 2009// /shell.js/1.3/Sat May 26 00:19:40 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/CVS/Root0000644000175000017500000000006314433667662024205 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-476655.js0000644000175000017500000000215414433667662025475 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-476655.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 476655; var summary = 'TM: Do not assert: count <= (size_t) (fp->regs->sp - StackBase(fp) - depth)'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); jit(true); try { eval( "(function (){ for (var y in this) {} })();" + "[''.watch(\"\", function(){}) for each (x in ['', '', eval, '', '']) if " + "(x)].map(Function)" ); } catch(ex) { } jit(false); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-350417.js0000755000175000017500000000204114433667662025456 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-350417.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 350417; var summary = 'Do not crash decompiling "is not function" msg'; var actual = 'No Crash'; var expect = 'No Crash'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: y.a = [2 for each (p in [])] is not a function'; try { eval('y = {}; (y.a = [2 for each (p in [])])();'); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Regress/regress-314887.js0000755000175000017500000000133414433667662025475 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-314887.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 314887; var summary = 'Do not crash when morons embed script tags in external script files'; var actual = 'No Crash'; var expect = 'No Crash'; printBugNumber(BUGNUMBER); printStatus (summary); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/CVS/0000755000175000017500000000000014433667662021726 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/CVS/Repository0000644000175000017500000000002714433667662024027 0ustar apoapomozilla/js/tests/js1_6 closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/CVS/Entries0000644000175000017500000000025314433667662023262 0ustar apoapo/README/1.1/Sun Oct 9 23:24:21 2005// /browser.js/1.14/Sat May 19 20:05:07 2007// /shell.js/1.21/Wed Aug 13 12:16:59 2008// /template.js/1.5/Wed Aug 13 12:10:56 2008// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/CVS/Root0000644000175000017500000000006314433667662022573 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/CVS/Entries.Log0000644000175000017500000000012614433667662024001 0ustar apoapoA D/Array//// A D/Regress//// A D/String//// A D/decompilation//// A D/extensions//// closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/template.js0000755000175000017500000000144514433667662023453 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'template.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 99999; var summary = ''; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/0000755000175000017500000000000014433667662024122 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/shell.js0000644000175000017500000000035714433667662025574 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'decompilation'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/regress-352613-01.js0000755000175000017500000000235214433667662027116 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352613-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352613; var summary = 'decompilation of |switch| |case| with computed value'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function () { switch(8) { case 1: a; case ('fafafa'.replace(/a/g, [1,2,3,4].map)): b; } } expect = 'function () { switch(8) { case 1: a; case "fafafa".replace(/a/g, [1,2,3,4].map): b; default:;} }'; actual = f + ''; compareSource(expect, actual, summary); expect = 'TypeError: "a" is not a function'; try { f(); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/browser.js0000644000175000017500000000000014433667662026131 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/regress-352613-02.js0000755000175000017500000000234714433667662027123 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352613-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352613; var summary = 'decompilation of |switch| |case| with computed value'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function () { switch(8) { case 7: a; case ('fafafa'.replace(/a/g, [1,2,3,4].map)): b; }} expect = 'function () {switch(8) { case 7: a; case "fafafa".replace(/a/g, [1,2,3,4].map): b; default:;}}'; actual = f + ''; compareSource(expect, actual, summary); expect = 'TypeError: "a" is not a function'; try { f(); } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/regress-352084.js0000755000175000017500000000264314433667662026705 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352084.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352084; var summary = 'decompilation of comma expression lists'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); var f; f = function() { h = {x:5, y:(a,b)}} ; expect = 'function() { h = {x:5, y:(a,b)};}'; actual = f + ''; compareSource(expect, actual, summary); f = function() { x(a, (b,c)) }; expect = 'function() { x(a, (b,c));}'; actual = f + ''; compareSource(expect, actual, summary); f = function() { return [(x, y) for each (z in [])] }; expect = 'function() { return [(x, y) for each (z in [])];}'; actual = f + ''; compareSource(expect, actual, summary); f = function() { return [(a, b), c]; }; expect = 'function() { return [(a, b), c]; }'; actual = f + ''; compareSource(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/CVS/0000755000175000017500000000000014433667662024555 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/CVS/Repository0000644000175000017500000000004514433667662026656 0ustar apoapomozilla/js/tests/js1_6/decompilation closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/CVS/Entries0000644000175000017500000000036214433667662026112 0ustar apoapo/browser.js/1.1/Sun May 6 23:27:00 2007// /regress-352084.js/1.2/Sat May 26 00:19:40 2007// /regress-352613-01.js/1.2/Sat May 26 00:19:40 2007// /regress-352613-02.js/1.2/Sat May 26 00:19:40 2007// /shell.js/1.2/Sat May 26 00:19:40 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/decompilation/CVS/Root0000644000175000017500000000006314433667662025422 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/0000755000175000017500000000000014433667662022351 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-310425-01.js0000755000175000017500000000154614433667662025344 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-310425-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 310425; var summary = 'Array.indexOf/lastIndexOf edge cases'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); expect = -1; actual = [].lastIndexOf(undefined, -1); reportCompare(expect, actual, summary); expect = -1; actual = [].indexOf(undefined, -1); reportCompare(expect, actual, summary); var x = []; x[1 << 30] = 1; expect = (1 << 30); actual = x.lastIndexOf(1); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/shell.js0000644000175000017500000000034714433667662024022 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ gTestsubsuite = 'Array'; closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-352742-01.js0000755000175000017500000000204114433667662025343 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352742-01.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352742; var summary = 'Array filter on {valueOf: Function}'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); print('If the test harness fails, this test fails.'); expect = 4; z = {valueOf: Function}; actual = 2; try { [11].filter(z); } catch(e) { actual = 3; print(e); } actual = 4; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-290592.js0000755000175000017500000003254014433667662025140 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-290592.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 290592; var summary = 'Array extras: forEach, indexOf, filter, map'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); // Utility functions function identity(v, index, array) { reportCompare(v, array[index], 'identity: check callback argument consistency'); return v; } function mutate(v, index, array) { reportCompare(v, array[index], 'mutate: check callback argument consistency'); if (index == 0) { array[1] = 'mutated'; delete array[2]; array.push('not visited'); } return v; } function mutateForEach(v, index, array) { reportCompare(v, array[index], 'mutateForEach: check callback argument consistency'); if (index == 0) { array[1] = 'mutated'; delete array[2]; array.push('not visited'); } actual += v + ','; } function makeUpperCase(v, index, array) { reportCompare(v, array[index], 'makeUpperCase: check callback argument consistency'); try { return v.toUpperCase(); } catch(e) { } return v; } function concat(v, index, array) { reportCompare(v, array[index], 'concat: check callback argument consistency'); actual += v + ','; } function isUpperCase(v, index, array) { reportCompare(v, array[index], 'isUpperCase: check callback argument consistency'); try { return v == v.toUpperCase(); } catch(e) { } return false; } function isString(v, index, array) { reportCompare(v, array[index], 'isString: check callback argument consistency'); return typeof v == 'string'; } // callback object. function ArrayCallback(state) { this.state = state; } ArrayCallback.prototype.makeUpperCase = function (v, index, array) { reportCompare(v, array[index], 'ArrayCallback.prototype.makeUpperCase: check callback argument consistency'); try { return this.state ? v.toUpperCase() : v.toLowerCase(); } catch(e) { } return v; }; ArrayCallback.prototype.concat = function(v, index, array) { reportCompare(v, array[index], 'ArrayCallback.prototype.concat: check callback argument consistency'); actual += v + ','; }; ArrayCallback.prototype.isUpperCase = function(v, index, array) { reportCompare(v, array[index], 'ArrayCallback.prototype.isUpperCase: check callback argument consistency'); try { return this.state ? true : (v == v.toUpperCase()); } catch(e) { } return false; }; ArrayCallback.prototype.isString = function(v, index, array) { reportCompare(v, array[index], 'ArrayCallback.prototype.isString: check callback argument consistency'); return this.state ? true : (typeof v == 'string'); }; function dumpError(e) { var s = e.name + ': ' + e.message + ' File: ' + e.fileName + ', Line: ' + e.lineNumber + ', Stack: ' + e.stack; return s; } var obj; var strings = ['hello', 'Array', 'WORLD']; var mixed = [0, '0', 0]; var sparsestrings = new Array(); sparsestrings[2] = 'sparse'; if ('map' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:map // test Array.map // map has 1 required argument expect = 1; actual = Array.prototype.map.length; reportCompare(expect, actual, 'Array.prototype.map.length == 1'); // throw TypeError if no callback function specified expect = 'TypeError'; try { strings.map(); actual = 'no error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, 'Array.map(undefined) throws TypeError'); try { // identity map expect = 'hello,Array,WORLD'; actual = strings.map(identity).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: identity'); try { expect = 'hello,mutated,'; actual = strings.map(mutate).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: mutate'); strings = ['hello', 'Array', 'WORLD']; try { // general map expect = 'HELLO,ARRAY,WORLD'; actual = strings.map(makeUpperCase).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: uppercase'); try { // pass object method as map callback expect = 'HELLO,ARRAY,WORLD'; var obj = new ArrayCallback(true); actual = strings.map(obj.makeUpperCase, obj).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: uppercase with object callback'); try { expect = 'hello,array,world'; obj = new ArrayCallback(false); actual = strings.map(obj.makeUpperCase, obj).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: lowercase with object callback'); try { // map on sparse arrays expect = ',,SPARSE'; actual = sparsestrings.map(makeUpperCase).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.map: uppercase on sparse array'); } if ('forEach' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach // test Array.forEach // forEach has 1 required argument expect = 1; actual = Array.prototype.forEach.length; reportCompare(expect, actual, 'Array.prototype.forEach.length == 1'); // throw TypeError if no callback function specified expect = 'TypeError'; try { strings.forEach(); actual = 'no error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, 'Array.forEach(undefined) throws TypeError'); try { // general forEach expect = 'hello,Array,WORLD,'; actual = ''; strings.forEach(concat); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.forEach'); try { expect = 'hello,mutated,'; actual = ''; strings.forEach(mutateForEach); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.forEach: mutate'); strings = ['hello', 'Array', 'WORLD']; try { // pass object method as forEach callback expect = 'hello,Array,WORLD,'; actual = ''; obj = new ArrayCallback(true); strings.forEach(obj.concat, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.forEach with object callback 1'); try { expect = 'hello,Array,WORLD,'; actual = ''; obj = new ArrayCallback(false); strings.forEach(obj.concat, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.forEach with object callback 2'); try { // test forEach on sparse arrays // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 expect = 'sparse,'; actual = ''; sparsestrings.forEach(concat); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.forEach on sparse array'); } if ('filter' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter // test Array.filter // filter has 1 required argument expect = 1; actual = Array.prototype.filter.length; reportCompare(expect, actual, 'Array.prototype.filter.length == 1'); // throw TypeError if no callback function specified expect = 'TypeError'; try { strings.filter(); actual = 'no error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, 'Array.filter(undefined) throws TypeError'); try { // test general filter expect = 'WORLD'; actual = strings.filter(isUpperCase).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.filter'); try { expect = 'WORLD'; obj = new ArrayCallback(false); actual = strings.filter(obj.isUpperCase, obj).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.filter object callback 1'); try { expect = 'hello,Array,WORLD'; obj = new ArrayCallback(true); actual = strings.filter(obj.isUpperCase, obj).toString(); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'Array.filter object callback 2'); } if ('every' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:every // test Array.every // every has 1 required argument expect = 1; actual = Array.prototype.every.length; reportCompare(expect, actual, 'Array.prototype.every.length == 1'); // throw TypeError if no every callback function specified expect = 'TypeError'; try { strings.every(); actual = 'no error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, 'Array.every(undefined) throws TypeError'); // test general every try { expect = true; actual = strings.every(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'strings: every element is a string'); try { expect = false; actual = mixed.every(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'mixed: every element is a string'); try { // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 expect = true; actual = sparsestrings.every(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'sparsestrings: every element is a string'); // pass object method as map callback obj = new ArrayCallback(false); try { expect = true; actual = strings.every(obj.isString, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'strings: every element is a string, via object callback'); try { expect = false; actual = mixed.every(obj.isString, obj); } catch(e) { actual = dumpError(e) ; } reportCompare(expect, actual, 'mixed: every element is a string, via object callback'); try { // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 expect = true; actual = sparsestrings.every(obj.isString, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'sparsestrings: every element is a string, via object callback'); } if ('some' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:some // test Array.some // some has 1 required argument expect = 1; actual = Array.prototype.some.length; reportCompare(expect, actual, 'Array.prototype.some.length == 1'); // throw TypeError if no some callback function specified expect = 'TypeError'; try { strings.some(); actual = 'no error'; } catch(e) { actual = e.name; } reportCompare(expect, actual, 'Array.some(undefined) throws TypeError'); // test general some try { expect = true; actual = strings.some(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'strings: some element is a string'); try { expect = true; actual = mixed.some(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'mixed: some element is a string'); try { expect = true; actual = sparsestrings.some(isString); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'sparsestrings: some element is a string'); // pass object method as map callback obj = new ArrayCallback(false); try { expect = true; actual = strings.some(obj.isString, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'strings: some element is a string, via object callback'); try { expect = true; actual = mixed.some(obj.isString, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'mixed: some element is a string, via object callback'); try { expect = true; actual = sparsestrings.some(obj.isString, obj); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'sparsestrings: some element is a string, via object callback'); } if ('indexOf' in Array.prototype) { // see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf // test Array.indexOf // indexOf has 1 required argument expect = 1; actual = Array.prototype.indexOf.length; reportCompare(expect, actual, 'Array.prototype.indexOf.length == 1'); // test general indexOf try { expect = -1; actual = mixed.indexOf('not found'); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'indexOf returns -1 if value not found'); try { expect = 0; actual = mixed.indexOf(0); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'indexOf matches using strict equality'); try { expect = 1; actual = mixed.indexOf('0'); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'indexOf matches using strict equality'); try { expect = 2; actual = mixed.indexOf(0, 1); } catch(e) { actual = dumpError(e); } reportCompare(expect, actual, 'indexOf begins searching at fromIndex'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-415451.js0000644000175000017500000000156114433667662025125 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-415451.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 415451; var summary = 'indexOf/lastIndexOf behavior'; var expected = "3,0,3,3,3,-1,-1"; results = []; var a = [1,2,3,1]; for (var i=-1; i < a.length+2; i++) results.push(a.indexOf(1,i)); var actual = String(results); reportCompare(expected, actual, "indexOf"); results = []; var expected = "3,0,0,0,3,3,3"; for (var i=-1; i < a.length+2; i++) results.push(a.lastIndexOf(1,i)); var actual = String(results); reportCompare(expected, actual, "lastIndexOf"); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-320887.js0000755000175000017500000000132014433667662025131 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-320887.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 320887; var summary = 'var x should not throw a ReferenceError'; var actual = 'No error'; var expect = 'No error'; printBugNumber(BUGNUMBER); printStatus (summary); try { (function xxx() { ["var x"].map(eval); })() } catch(ex) { actual = ex + ''; } reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-304828.js0000755000175000017500000001013714433667662025134 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-304828.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 304828; var summary = 'Array Generic Methods'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); var value; // use Array methods on a String // join value = '123'; expect = '1,2,3'; try { actual = Array.prototype.join.call(value); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': join'); // reverse value = '123'; expect = '123'; try { actual = Array.prototype.reverse.call(value) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': reverse'); // sort value = 'cba'; expect = 'cba'; try { actual = Array.prototype.sort.call(value) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': sort'); // push value = 'abc'; expect = 6; try { actual = Array.prototype.push.call(value, 'd', 'e', 'f'); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': push'); reportCompare('abc', value, summary + ': push'); // pop value = 'abc'; expect = 'c'; try { actual = Array.prototype.pop.call(value); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': pop'); reportCompare('abc', value, summary + ': pop'); // unshift value = 'def'; expect = 6; try { actual = Array.prototype.unshift.call(value, 'a', 'b', 'c'); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': unshift'); reportCompare('def', value, summary + ': unshift'); // shift value = 'abc'; expect = 'a'; try { actual = Array.prototype.shift.call(value); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': shift'); reportCompare('abc', value, summary + ': shift'); // splice value = 'abc'; expect = 'b'; try { actual = Array.prototype.splice.call(value, 1, 1) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': splice'); // concat value = 'abc'; expect = 'abc,d,e,f'; try { actual = Array.prototype.concat.call(value, 'd', 'e', 'f') + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': concat'); // slice value = 'abc'; expect = 'b'; try { actual = Array.prototype.slice.call(value, 1, 2) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': slice'); // indexOf value = 'abc'; expect = 1; try { actual = Array.prototype.indexOf.call(value, 'b'); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': indexOf'); // lastIndexOf value = 'abcabc'; expect = 4; try { actual = Array.prototype.lastIndexOf.call(value, 'b'); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': lastIndexOf'); // forEach value = 'abc'; expect = 'ABC'; actual = ''; try { Array.prototype.forEach.call(value, function (v, index, array) {actual += array[index].toUpperCase();}); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': forEach'); // map value = 'abc'; expect = 'A,B,C'; try { actual = Array.prototype.map.call(value, function (v, index, array) {return v.toUpperCase();}) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': map'); // filter value = '1234567890'; expect = '2,4,6,8,0'; try { actual = Array.prototype.filter.call(value, function (v, index, array) {return array[index] % 2 == 0;}) + ''; } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': filter'); // every value = '1234567890'; expect = false; try { actual = Array.prototype.every.call(value, function (v, index, array) {return array[index] % 2 == 0;}); } catch(e) { actual = e + ''; } reportCompare(expect, actual, summary + ': every'); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/filter.js0000755000175000017500000000237314433667662024204 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'filter.js'; //----------------------------------------------------------------------------- var BUGNUMBER = "364603"; var summary = "The value placed in a filtered array for an element is the " + " element's value before the callback is run, not after"; var actual, expect; printBugNumber(BUGNUMBER); printStatus(summary); /************** * BEGIN TEST * **************/ var failed = false; function mutate(val, index, arr) { arr[index] = "mutated"; return true; } function assertEqual(v1, v2, msg) { if (v1 !== v2) throw msg; } try { var a = [1, 2]; var m = a.filter(mutate); assertEqual(a[0], "mutated", "Array a not mutated!"); assertEqual(a[1], "mutated", "Array a not mutated!"); assertEqual(m[0], 1, "Filtered value is value before callback is run"); assertEqual(m[1], 2, "Filtered value is value before callback is run"); } catch (e) { failed = e; } expect = false; actual = failed; reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-386030.js0000755000175000017500000000320614433667662025126 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-386030.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 386030; var summary = 'Array.reduce should ignore holes'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); function add(a, b) { return a + b; } function testreduce(v) { return v == 3 ? "PASS" : "FAIL"; } expect = 'PASS'; try { a = new Array(2); a[1] = 3; actual = testreduce(a.reduce(add)); } catch (e) { actual = "FAIL, reduce"; } reportCompare(expect, actual, summary + ': 1'); try { a = new Array(2); a[0] = 3; actual = testreduce(a.reduceRight(add)); } catch (e) { actual = "FAIL, reduceRight"; } reportCompare(expect, actual, summary + ': 2'); try { a = new Array(2); a.reduce(add); actual = "FAIL, empty reduce"; } catch (e) { actual = "PASS"; } reportCompare(expect, actual, summary + ': 3'); try { a = new Array(2); print(a.reduceRight(add)); actual = "FAIL, empty reduceRight"; } catch (e) { actual = "PASS"; } reportCompare(expect, actual, summary + ': 4'); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/browser.js0000644000175000017500000000000014433667662024360 0ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-310425-02.js0000755000175000017500000000121614433667662025337 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-310425-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 310425; var summary = 'Array.indexOf/lastIndexOf edge cases'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); expect = -1; actual = Array(1).indexOf(1); reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/CVS/0000755000175000017500000000000014433667662023004 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/CVS/Repository0000644000175000017500000000003514433667662025104 0ustar apoapomozilla/js/tests/js1_6/Array closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/CVS/Entries0000644000175000017500000000126214433667662024341 0ustar apoapo/browser.js/1.2/Mon Oct 30 16:48:36 2006// /filter.js/1.3/Sat May 26 00:19:40 2007// /regress-290592.js/1.2/Sat May 26 00:19:40 2007// /regress-304828.js/1.2/Sat May 26 00:19:40 2007// /regress-305002.js/1.2/Sat May 26 00:19:40 2007// /regress-310425-01.js/1.2/Sat May 26 00:19:40 2007// /regress-310425-02.js/1.3/Sat May 26 00:19:40 2007// /regress-320887.js/1.2/Sat May 26 00:19:40 2007// /regress-352742-01.js/1.3/Sat May 26 00:19:40 2007// /regress-352742-02.js/1.3/Sat May 26 00:19:40 2007// /regress-386030.js/1.1/Fri Jul 13 01:13:41 2007// /regress-415451.js/1.1/Tue Feb 5 21:52:34 2008// /regress-415540.js/1.1/Mon Feb 11 16:55:10 2008// /shell.js/1.3/Sat May 26 00:19:40 2007// D closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/CVS/Root0000644000175000017500000000006314433667662023651 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-305002.js0000755000175000017500000000131514433667662025113 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-305002.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 305002; var summary = '[].every(f) == true'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); var notcalled = true; function callback() { notcalled = false; } expect = true; actual = [].every(callback) && notcalled; reportCompare(expect, actual, summary); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-352742-02.js0000755000175000017500000000172014433667662025347 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-352742-02.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 352742; var summary = 'eval("return") in toString'; var actual = ''; var expect = ''; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 3; var j = ({toString: function() { eval("return"); }}); actual = 2; try { "" + j; } catch(e){print(e)} actual = 3; reportCompare(expect, actual, summary); exitFunc ('test'); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/js1_6/Array/regress-415540.js0000755000175000017500000000144614433667662025131 0ustar apoapo/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ var gTestfile = 'regress-415540.js'; //----------------------------------------------------------------------------- var BUGNUMBER = 415540; var summary = 'Array.push' var actual = ''; var expect = ''; var Constr = function() {}; Constr.prototype = []; var c = new Constr(); c.push(5); c.push(6); var actual = Array.push(c,7); reportCompare(3, actual, "result of Array.push is length"); reportCompare(5, c[0], "contents of c[0]"); reportCompare(6, c[1], "contents of c[1]"); reportCompare(7, c[2], "contents of c[2]"); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/mklistpage.pl0000644000175000017500000002622014433667662023050 0ustar apoapo#!/usr/bin/perl # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # Creates the meat of a test suite manager page, requites menuhead.html and menufoot.html # to create the complete page. The test suite manager lets you choose a subset of tests # to run under the runtests2.pl script. local $lxr_url = "http://lxr.mozilla.org/mozilla/source/js/tests/"; local $suite_path = $ARGV[0] || "./"; local $uid = 0; # radio button unique ID local $html = ""; # html output local $javascript = ""; # script output # # automatically exclude spidermonkey-n.tests # XXXbc better to emulate jsDriver.pl's option processing # and allow -L file1 file2 etc. # local $excludedtest; local @excludedlist = expand_user_test_list('spidermonkey-n.tests'); local %excludedhash = {}; foreach $excludedtest (@excludedlist) { $excludedhash{"./" . $excludedtest} = 1; } &main; print (&scriptTag($javascript) . "\n"); print ($html); sub main { local $i, @suite_list; if (!($suite_path =~ /\/$/)) { $suite_path = $suite_path . "/"; } @suite_list = sort(&get_subdirs ($suite_path)); $javascript .= "\n"; $javascript .= "var suites = {};\n"; $javascript .= "function populateSuites()\n"; $javascript .= "{\n"; $javascript .= "var currSuite;\n"; $javascript .= "var currDirectory;\n"; $javascript .= "var currTestDirs;\n"; $javascript .= "var currTests;\n"; $html .= "

      Test Suites:

      \n"; $html .= "
      \n"; $html .= " \n"; $html .= " \n"; # suite menu $html .= "\n"; foreach $suite (@suite_list) { local @readme_text = ("No description available."); if (open (README, $suite_path . $suite . "/README")) { @readme_text = ; close (README); } $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= "\n"; $html .= ""; $html .= ""; } $html .= "
      \n"; $html .= "$suite\n"; $html .= "@readme_text\n"; $html .= " \n"; $html .= "\n"; $html .= "
      \n"; $html .= "
      \n"; $html .= "
      \n"; $html .= "
      \n"; foreach $i (0 .. $#suite_list) { local $prev_href = ($i > 0) ? "\#SUITE_" . $suite_list[$i - 1] : ""; local $next_href = ($i < $#suite_list) ? "\#SUITE_" . $suite_list[$i + 1] : ""; &process_suite ($suite_path, $suite_list[$i], $prev_href, $next_href); } $html .= "
      \n"; $javascript .= "}\n"; $javascript .= "populateSuites();\n"; } # # Append detail from a 'suite' directory (eg: ecma, ecma_2, js1_1, etc.), calling # process_test_dir for subordinate categories. # sub process_suite { local ($suite_path, $suite, $prev_href, $next_href) = @_; local $i, @test_dir_list; local $suite_count = 0; # suite js object $javascript .= "currSuite = suites[\"$suite\"] = " . "{testDirs:{}, count:0, selected:0};\n"; $javascript .= "currTestDirs = currSuite.testDirs;\n"; @test_dir_list = sort(&get_subdirs ($test_home . $suite)); # suite header $html .= "
      \n"; $html .= "$suite " . "(" . ($#test_dir_list + 1) . " Sub-Categories)\n"; $html .= "\n"; $html .= " \n"; $html .= "[ "; $html .= "Top of page \n"; if ($prev_href) { $html .= " | "; $html .= "Previous Suite \n"; } if ($next_href) { $html .= " | "; $html .= "Next Suite \n"; } $html .= "]\n"; $html .= "
      \n"; $html .= "
      \n"; foreach $i (0 .. $#test_dir_list) { local $prev_href = ($i > 0) ? "\#TESTDIR_" . $suite . $test_dir_list[$i - 1] : ""; local $next_href = ($i < $#test_dir_list) ? "\#TESTDIR_" . $suite . $test_dir_list[$i + 1] : ""; &process_test_dir ($suite_path . $suite . "/", $test_dir_list[$i], $suite, $prev_href, $next_href); } $javascript .= "currSuite.count = $suite_count;\n"; $html .= "
      \n"; } # # Append detail from a test directory, # calling process_test for subordinate js files # sub process_test_dir { local ($test_dir_path, $test_dir, $suite, $prev_href, $next_href) = @_; @test_list = sort(&get_js_files ($test_dir_path . $test_dir)); if ($#test_list >= 0) { $suite_count += @test_list; $javascript .= "currDirectory = currTestDirs[\"$test_dir\"] = " . "{tests:{}};\n"; $javascript .= "currTests = currDirectory.tests;\n"; $html .= "
      \n"; $html .= "
      \n"; $html .= "\n"; $html .= "$test_dir (" . ($#test_list + 1) . " tests)\n"; $html .= "\n"; $html .= " "; $html .= "[ Top of $suite Suite "; if ($prev_href) { $html .= "| Previous Category "; } if ($next_href) { $html .= " | Next Category "; } $html .= "]\n"; $html .= "
      \n"; $html .= "
      \n"; foreach $test (@test_list) { &process_test ($test_dir_path . $test_dir, $test); } $html .= "
      \n"; } } # # Append detail from a single JavaScript file. # sub process_test { local ($test_dir_path, $test) = @_; local $title = ""; # ignore excluded tests if ($excludedhash{"$test_dir_path/$test"}) { --$suite_count; return; } $uid++; open (TESTCASE, $test_dir_path . "/" . $test) || die ("Error opening " . $test_dir_path . "/" . $test); while () { if (/.*(TITLE|summary)\s+\=\s+[\"\'](.*)[\"\']/i) { $title = $2; break; } if (/.*START\([\"\'](.*)[\"\']\);/) { $title = $1; break; } } close (TESTCASE); $javascript .= "currTests[\"$test\"] = {id:\"t$uid\"}\n"; $html .= "
      \n"; $html .= " \n"; $html .= "" . "$test $title\n"; $html .= "
      \n"; } sub scriptTag { return (""); } # # given a directory, return an array of all subdirectories # sub get_subdirs { local ($dir) = @_; local @subdirs; if (!($dir =~ /\/$/)) { $dir = $dir . "/"; } opendir (DIR, $dir) || die ("couldn't open directory $dir: $!"); local @testdir_contents = readdir(DIR); closedir(DIR); foreach (@testdir_contents) { if ((-d ($dir . $_)) && ($_ ne 'CVS') && ($_ ne '.') && ($_ ne '..')) { @subdirs[$#subdirs + 1] = $_; } } return @subdirs; } # # given a directory, return an array of all the js files that are in it. # sub get_js_files { local ($test_subdir) = @_; local @js_file_array; opendir ( TEST_SUBDIR, $test_subdir) || die ("couldn't open directory " . "$test_subdir: $!"); @subdir_files = readdir( TEST_SUBDIR ); closedir( TEST_SUBDIR ); foreach ( @subdir_files ) { # print "excluded $test_subdir/$_\n" if $excludedhash{"$test_subdir/$_"}; if ( ($_ =~ /\.js$/) && ($_ ne 'shell.js') && ($_ ne 'browser.js') && (!$excludedhash{"$test_subdir/$_"}) ) { $js_file_array[$#js_file_array+1] = $_; } } return @js_file_array; } # copied from jsDriver.pl # # reads $list_file, storing non-comment lines into an array. # lines in the form suite_dir/[*] or suite_dir/test_dir/[*] are expanded # to include all test files under the specified directory # sub expand_user_test_list { my ($list_file) = @_; my @retval = (); # # Trim off the leading path separator that begins relative paths on the Mac. # Each path will get concatenated with $opt_suite_path, which ends in one. # # Also note: # # We will call expand_test_list_entry(), which does pattern-matching on $list_file. # This will make the pattern-matching the same as it would be on Linux/Windows - # if ($os_type eq "MAC") { $list_file =~ s/^$path_sep//; } if ($list_file =~ /\.js$/ || -d $opt_suite_path . $list_file) { push (@retval, &expand_test_list_entry($list_file)); } else { open (TESTLIST, $list_file) || die("Error opening test list file '$list_file': $!\n"); while () { s/\r*\n*$//; if (!(/\s*\#/)) { # It's not a comment, so process it push (@retval, &expand_test_list_entry($_)); } } close (TESTLIST); } return @retval; } # # Currently expect all paths to be RELATIVE to the top-level tests directory. # One day, this should be improved to allow absolute paths as well - # sub expand_test_list_entry { my ($entry) = @_; my @retval; if ($entry =~ /\.js$/) { # it's a regular entry, add it to the list if (-f $opt_suite_path . $entry) { push (@retval, $entry); } else { status ("testcase '$entry' not found."); } } elsif ($entry =~ /(.*$path_sep[^\*][^$path_sep]*)$path_sep?\*?$/) { # Entry is in the form suite_dir/test_dir[/*] # so iterate all tests under it my $suite_and_test_dir = $1; my @test_files = &get_js_files ($opt_suite_path . $suite_and_test_dir); my $i; foreach $i (0 .. $#test_files) { $test_files[$i] = $suite_and_test_dir . $path_sep . $test_files[$i]; } splice (@retval, $#retval + 1, 0, @test_files); } elsif ($entry =~ /([^\*][^$path_sep]*)$path_sep?\*?$/) { # Entry is in the form suite_dir[/*] # so iterate all test dirs and tests under it my $suite = $1; my @test_dirs = &get_subdirs ($opt_suite_path . $suite); my $test_dir; foreach $test_dir (@test_dirs) { my @test_files = &get_js_files ($opt_suite_path . $suite . $path_sep . $test_dir); my $i; foreach $i (0 .. $#test_files) { $test_files[$i] = $suite . $path_sep . $test_dir . $path_sep . $test_files[$i]; } splice (@retval, $#retval + 1, 0, @test_files); } } else { die ("Dont know what to do with list entry '$entry'.\n"); } return @retval; } sub status { print STDERR ("-#- ", @_ , "\n"); } closure-compiler-20130227+dfsg1/rhino/testsrc/tests/runtests2.pl0000755000175000017500000000043014433667662022657 0ustar apoapo# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. print ("This is no longer the test driver, please use jsDriver.pl instead."); closure-compiler-20130227+dfsg1/rhino/testsrc/tests/slow-n-1.8.1.tests0000644000175000017500000000220114433667662023312 0ustar apoapoe4x/GC/regress-324278.js e4x/Regress/regress-319872.js ecma/Date/15.9.5.10-2.js ecma_3/Array/regress-322135-02.js ecma_3/Array/regress-322135-03.js ecma_3/Array/regress-322135-04.js ecma_3/RegExp/regress-307456.js ecma_3/RegExp/regress-330684.js ecma_3/RegExp/regress-367888.js js1_5/Array/regress-330812.js js1_5/Array/regress-350256-03.js js1_5/Function/regress-338121-01.js js1_5/Function/regress-338121-02.js js1_5/GC/regress-338653.js js1_5/GC/regress-346794.js js1_5/GC/regress-348532.js js1_5/Regress/regress-271716-n.js js1_5/Regress/regress-303213.js js1_5/Regress/regress-312588.js js1_5/Regress/regress-329530.js js1_5/Regress/regress-484693.js js1_5/extensions/regress-330569.js js1_5/extensions/regress-336409-1.js js1_5/extensions/regress-336409-2.js js1_5/extensions/regress-336410-1.js js1_5/extensions/regress-336410-2.js js1_5/extensions/regress-342960.js js1_5/extensions/regress-345967.js js1_5/extensions/regress-350531.js js1_5/extensions/regress-351448.js js1_6/extensions/regress-455464-04.js js1_6/extensions/regress-456826.js js1_7/extensions/regress-458679.js js1_7/iterable/regress-341815.js js1_7/iterable/regress-341821.js closure-compiler-20130227+dfsg1/rhino/testsrc/tests/get-universe.sh0000755000175000017500000000174214433667662023331 0ustar apoapo#!/bin/bash -e # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # usage: get-universe.sh logfile(s) > universe.data # # get-universe.sh reads the processed javascript logs and writes to # stdout the unique set of fields to be used as the "universe" of test # run data. These values are used by pattern-expander.pl and # pattern-extracter.pl to encode the known failure files into regular # expressions. export LC_ALL=C # handle all character sets (for f in $@; do grep -h -m 1 TEST_ID $f | tr -dc '[\040-\177\n]' | sed 's|^TEST_ID=[^,]*, \(TEST_BRANCH=[^,]*, TEST_REPO=[^,]*, TEST_BUILDTYPE=[^,]*, TEST_TYPE=[^,]*\), \(TEST_OS=[^,]*, TEST_KERNEL=[^,]*, TEST_PROCESSORTYPE=[^,]*, TEST_MEMORY=[^,]*, TEST_TIMEZONE=[^,]*, TEST_OPTIONS=[^,]*\),.*|\2, \1|' done) | sort -u closure-compiler-20130227+dfsg1/rhino/testsrc/tests/process-logs.sh0000755000175000017500000001166514433667662023341 0ustar apoapo#!/bin/bash -e # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. if [[ -z "$TEST_DIR" ]]; then cat < $worktestlogfile ;; *.log.gz) worktestlogfile=`mktemp $testlogfile.XXXXXX` gunzip -c $testlogfile > $worktestlogfile ;; *) echo "unknown log type: $f" exit 2 ;; esac case "$testlogfile" in *,js,*) testtype=shell;; *,firefox,*) testtype=browser;; *,thunderbird,*) testtype=browser;; *,fennec,*) testtype=browser;; *) error "unknown testtype in logfile $testlogfile" $LINENO;; esac debug "testtype=$testtype" case "$testlogfile" in *,nightly*) buildtype=nightly;; *,opt,*) buildtype=opt;; *,debug,*) buildtype=debug;; *) error "unknown buildtype in logfile $testlogfile" $LINENO; esac debug "buildtype=$buildtype" branch=`echo $testlogfile | sed 's|.*,\([0-9]\.[0-9]*\.[0-9]*\).*|\1|'` debug "branch=$branch" repo=`grep -m 1 '^environment: TEST_MOZILLA_HG=' $worktestlogfile | sed 's|.*TEST_MOZILLA_HG=http://hg.mozilla.org.*/\([^\/]*\)|\1|'` if [[ -z "$repo" ]]; then repo=CVS fi debug "repo=$repo" case "$testlogfile" in *,nt,*) OSID=nt;; *,linux,*) OSID=linux;; *,darwin,*) OSID=darwin;; *) OSID=`grep -m 1 '^environment: OSID=' $worktestlogfile | sed 's|.*OSID=\(.*\)|\1|'` if [[ -z "$OSID" ]]; then error "unknown OS in logfile $testlogfile" $LINENO fi ;; esac debug "OSID=$OSID" kernel=`grep -m 1 '^environment: TEST_KERNEL=' $worktestlogfile | sed 's|.*TEST_KERNEL=\(.*\)|\1|'` if [[ "$OSID" == "linux" ]]; then kernel=`echo $kernel | sed 's|\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*|\1.\2.\3|'` fi debug "kernel=$kernel" arch=`grep -m 1 '^environment: TEST_PROCESSORTYPE=' $worktestlogfile | sed 's|.*TEST_PROCESSORTYPE=\(.*\)|\1|'` debug "arch=$arch" memory=`grep -m 1 '^environment: TEST_MEMORY=' $worktestlogfile | sed 's|.*TEST_MEMORY=\(.*\)|\1|'` timezone=`basename $testlogfile | sed 's|^[-0-9]*\([-+]\)\([0-9]\{4,4\}\),.*|\1\2|'` debug "timezone=$timezone" jsoptions=`grep -m 1 '^arguments: javascriptoptions=' $worktestlogfile | sed 's|.*javascriptoptions=\(.*\)|\1|'` if [[ -z "$jsoptions" ]]; then jsoptions=none fi debug "jsoptions=$jsoptions" outputprefix=$testlogfile includetests="included-$branch-$testtype-$buildtype.tests" excludetests="excluded-$branch-$testtype-$buildtype.tests" grep '^include: ' $worktestlogfile | sed 's|include: ||' > $TEST_DIR/tests/mozilla.org/js/$includetests grep '^exclude: ' $worktestlogfile | sed 's|exclude: ||' > $TEST_DIR/tests/mozilla.org/js/$excludetests $TEST_DIR/tests/mozilla.org/js/known-failures.pl \ -b "$branch" \ -T "$buildtype" \ -R "$repo" \ -t "$testtype" \ -o "$OSID" \ -K "$kernel" \ -A "$arch" \ -M "$memory" \ -z "$timezone" \ -J "$jsoptions" \ -r "$TEST_JSDIR/failures.txt" \ -l "$worktestlogfile" \ -O "$outputprefix" if [[ "$testlogfile" != "$worktestlogfile" ]]; then rm $worktestlogfile unset worktestlogfile fi done closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/0000755000175000017500000000000014433667662021140 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/0000755000175000017500000000000014433667662021716 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/0000755000175000017500000000000014433667662023520 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/0000755000175000017500000000000014433667662025666 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/0000755000175000017500000000000014433667662026267 5ustar apoapoclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/0000755000175000017500000000000014433667662030600 5ustar apoapo././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000755000175000017500000000000014433667662031630 5ustar apoapo././@LongLink0000644000000000000000000000017200000000000011603 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/JSObject_001.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000644000175000017500000000711714433667662031640 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.netscape.javascript.qa.liveconnect.jsobject; import netscape.javascript.*; import com.netscape.javascript.qa.liveconnect.LiveConnectTest; /** * Try to get properties of the global object. This test verifies that * getMember can access default properties of the global object, and that * the Object returned is an instance of netscape.javascript.JSObject * for objects, and java.lang.Double for numbers (NaN, Infinity). * * @see com.netscape.javascript.qa.liveconnect.LiveConnectTest * * @author christine@netscape.com * */ public class JSObject_001 extends LiveConnectTest { public JSObject_001() { super(); } public static void main( String[] args ) { JSObject_001 test = new JSObject_001(); test.start(); } public void executeTest() { getDouble( "NaN" ); getDouble( "Infinity" ); getJSObject( "parseInt" ); getJSObject( "parseFloat" ); getJSObject( "eval" ); getJSObject( "escape" ); getJSObject( "unescape" ); getJSObject( "isNaN" ); getJSObject( "isFinite" ); getJSObject( "Object" ); getJSObject( "Function" ); getJSObject( "Array" ); getJSObject( "String" ); getJSObject( "Boolean" ); getJSObject( "Number" ); getJSObject( "Date" ); getJSObject( "Math" ); } /** * Try to get a property of the JavaScript Global object. The * type of this property should be a java.lang.Double. If it * is not, the test will fail with a java.lang.ClassCastException. * * @param property name of the JavaScript property to get. */ public void getDouble( String property ) { Object jsobject = null; String exception = null; String identifier = this.getClass().toString(); String description = "Object jsobject = global.getMember(\""+ property +"\"); jsobject instanceof java.lang.Double "; try { jsobject = global.getMember( property ); } catch ( Exception e ) { exception = ("Exception getting " + property +": "+ e.toString()); System.err.println( exception ); } String expect = "true"; String actual = (jsobject instanceof java.lang.Double) +""; addTestCase( description, expect, actual, exception ); } /** * Try to get a property of the JavaScript Global object. The type of * this property should be a netscape.javascript.JSObject. If it is not, * the test will fail with a java.lang.ClassCastException. * * @param property name of the JavaScript property to get. */ public void getJSObject( String property ) { Object jsobject = null; String exception = null; String identifier = this.getClass().toString(); String description = "Object jsobject = global.getMember(\""+ property +"\"); jsobject instanceof JSObject "; try { jsobject = global.getMember( property ); } catch ( Exception e ) { exception = ("Exception getting " + property +": "+ e.toString()); System.err.println( exception ); } String expect = "true"; String actual = (jsobject instanceof JSObject) +""; addTestCase( description, expect, actual, exception ); } } ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/CVS/closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000755000175000017500000000000014433667662031630 5ustar apoapo././@LongLink0000644000000000000000000000016700000000000011607 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/CVS/Repositoryclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000644000175000017500000000010514433667662031626 0ustar apoapomozilla/js/tests/src/com/netscape/javascript/qa/liveconnect/jsobject ././@LongLink0000644000000000000000000000016400000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/CVS/Entriesclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000644000175000017500000000006414433667662031632 0ustar apoapo/JSObject_001.java/1.1/Wed May 26 22:14:43 1999// D ././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobject/CVS/Rootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/jsobj0000644000175000017500000000006314433667662031631 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot ././@LongLink0000644000000000000000000000016100000000000011601 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/JSObjectEval.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/JSObj0000644000175000017500000000132114433667662031467 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.netscape.javascript.qa.liveconnect; import netscape.javascript.JSObject; /** * Class with one static method that exercises JSObject.eval with * any JSObject and any JS code. Used by tests in * ns/js/tests/lc3/exceptions * * */ public class JSObjectEval { /** * Given a JSObject and some JavaScript code, have the object * evaluate the JavaScript code. * */ public static Object eval(JSObject obj, String code) { obj.eval(code); return null; } } ././@LongLink0000644000000000000000000000016200000000000011602 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/DataTypeClass.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/DataT0000644000175000017500000007737414433667662031542 0ustar apoapo/* -*- Mode: java; tab-width: 8 -*- * Copyright (C) 1997, 1998 Netscape Communications Corporation, All Rights Reserved. */ package com.netscape.javascript.qa.liveconnect; /** * Attempt to get and set the public fields. * * XXX need to override toString, toNumber with values in * static fields. * * XXX override both instance and static versions of the method * @author christine m begle */ public class DataTypeClass { /** * Constructor stuff * */ public DataTypeClass() { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_NONE; } // instance setters public DataTypeClass(boolean b) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_BOOLEAN; } public DataTypeClass(Boolean b) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_BOOLEAN_OBJECT; } public DataTypeClass(byte b) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_BYTE; } public DataTypeClass(Byte b) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_BYTE_OBJECT; } public DataTypeClass(Integer i) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_INTEGER_OBJECT; } public DataTypeClass(int i) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_INT; } public DataTypeClass(Double d ) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_DOUBLE_OBJECT; } public DataTypeClass(double d) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_DOUBLE; } public DataTypeClass(Float f) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_FLOAT_OBJECT; } public DataTypeClass(float f) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_FLOAT; } public DataTypeClass(Long l) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_LONG_OBJECT; } public DataTypeClass(long l) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_LONG; } public DataTypeClass(Short s) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_SHORT_OBJECT; } public DataTypeClass(short s) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_SHORT; } public DataTypeClass(String s) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_STRING; } public DataTypeClass(Object o ) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_OBJECT; } public DataTypeClass(char c) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_CHAR; } public DataTypeClass(Character c) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_CHAR_OBJECT; } public DataTypeClass( byte b[] ) { PUB_INT_CONSTRUCTOR_ARG = CONSTRUCTOR_ARG_BYTE_ARRAY; } public int PUB_INT_CONSTRUCTOR_ARG; public int CONSTRUCTOR_ARG_NONE = 0; public int CONSTRUCTOR_ARG_BOOLEAN = 1; public int CONSTRUCTOR_ARG_BOOLEAN_OBJECT=2; public int CONSTRUCTOR_ARG_BYTE=3; public int CONSTRUCTOR_ARG_BYTE_OBJECT=4; public int CONSTRUCTOR_ARG_INT=5; public int CONSTRUCTOR_ARG_INTEGER_OBJECT=6; public int CONSTRUCTOR_ARG_DOUBLE=7; public int CONSTRUCTOR_ARG_DOUBLE_OBJECT=8; public int CONSTRUCTOR_ARG_FLOAT=9; public int CONSTRUCTOR_ARG_FLOAT_OBJECT=10; public int CONSTRUCTOR_ARG_LONG=11; public int CONSTRUCTOR_ARG_LONG_OBJECT=12; public int CONSTRUCTOR_ARG_SHORT=13; public int CONSTRUCTOR_ARG_SHORT_OBJECT=14; public int CONSTRUCTOR_ARG_STRING=15; public int CONSTRUCTOR_ARG_OBJECT=16; public int CONSTRUCTOR_ARG_CHAR=17; public int CONSTRUCTOR_ARG_CHAR_OBJECT=18; public int CONSTRUCTOR_ARG_BYTE_ARRAY=19; /** * Override toNumber * */ public static double PUB_DOUBLE_REPRESENTATION = 0.2134; public double doubleValue() { return PUB_DOUBLE_REPRESENTATION; } public int toNumber() { return PUB_NUMBER_REPRESENTATION; } public boolean booleanValue() { return PUB_BOOLEAN_REPRESENTATION; } public boolean PUB_BOOLEAN_REPRESENTATION = true; public static int PUB_NUMBER_REPRESENTATION = 2134; /** * Override toString */ public String toString() { return PUB_STRING_REPRESENTATION; } public static String PUB_STRING_REPRESENTATION = "DataTypeClass Instance"; /** * Have a method that has the same name as a field. */ public String amIAFieldOrAMethod() { return "METHOD!"; } public String amIAFieldOrAMethod = "FIELD!"; public static boolean staticGetBoolean() { return PUB_STATIC_BOOLEAN; } public static Boolean staticGetBooleanObject() { return PUB_STATIC_BOOLEAN_OBJECT; } public static byte staticGetByte() { return PUB_STATIC_BYTE; } public static Byte staticGetByteObject() { return PUB_STATIC_BYTE_OBJECT; } public static Integer staticGetIntegerObject() { return PUB_STATIC_INTEGER_OBJECT; } public static int staticGetInteger() { return new Integer(PUB_STATIC_INT).intValue(); } public static Double staticGetDoubleObject() { return PUB_STATIC_DOUBLE_OBJECT; } public static double staticGetDouble() { return new Double(PUB_STATIC_DOUBLE).doubleValue(); } public static Float staticGetFloatObject() { return PUB_STATIC_FLOAT_OBJECT; } public static float staticGetFloat() { return new Float(PUB_STATIC_FLOAT).floatValue(); } public static Long staticGetLongObject() { return PUB_STATIC_LONG_OBJECT; } public static long staticGetLong() { return new Long(PUB_STATIC_LONG).longValue(); } public static Short staticGetShortObject() { return PUB_STATIC_SHORT_OBJECT; } public static short staticGetShort() { return new Short(PUB_STATIC_SHORT).shortValue(); } public static String staticGetStringObject() { return PUB_STATIC_STRING; } public static char staticGetChar() { return PUB_STATIC_CHAR; } public static Character staticGetCharacter() { return PUB_STATIC_CHAR_OBJECT; } // instance getters public boolean getBoolean() { return PUB_BOOLEAN; } public Boolean getBooleanObject() { return PUB_BOOLEAN_OBJECT; } public byte getByte() { return PUB_BYTE; } public Byte getByteObject() { return PUB_BYTE_OBJECT; } public Integer getIntegerObject() { return PUB_INTEGER_OBJECT; } public int getInteger() { return new Integer(PUB_INT).intValue(); } public Double getDoubleObject() { return PUB_DOUBLE_OBJECT; } public double getDouble() { return new Double(PUB_DOUBLE).doubleValue(); } public Float getFloatObject() { return PUB_FLOAT_OBJECT; } public float getFloat() { return new Float(PUB_FLOAT).floatValue(); } public Long getLongObject() { return PUB_LONG_OBJECT; } public long getLong() { return new Long(PUB_LONG).longValue(); } public String getStringObject() { return PUB_STRING; } public Object getObject() { return PUB_OBJECT; } public Short getShortObject() { return PUB_SHORT_OBJECT; } public short getShort() { return new Short(PUB_SHORT).shortValue(); } public char getChar() { return PUB_CHAR; } public Character getCharacter() { return PUB_CHAR_OBJECT; } // SETTERS public static void staticSetBoolean(boolean b) { PUB_STATIC_BOOLEAN = b; } public static void staticSetBooleanObject(Boolean b) { PUB_STATIC_BOOLEAN_OBJECT = b; } public static void staticSetByte(byte b) { PUB_STATIC_BYTE = b; } public static void staticSetByteObject(Byte b) { PUB_STATIC_BYTE_OBJECT = b; } public static void staticSetIntegerObject(Integer i) { PUB_STATIC_INTEGER_OBJECT = i; } public static void staticSetInteger(int i) { PUB_STATIC_INT = i; } public static void staticSetDoubleObject( Double d ) { PUB_STATIC_DOUBLE_OBJECT = d; } public static void staticSetDouble(double d) { PUB_STATIC_DOUBLE = d; } public static void staticSetFloatObject(Float f) { PUB_STATIC_FLOAT_OBJECT = f ; } public static void staticSetFloat(float f) { PUB_STATIC_FLOAT = f; } public static void staticSetLongObject(Long l) { PUB_STATIC_LONG_OBJECT = l ; } public static void staticSetLong(long l) { PUB_STATIC_LONG = l ; } public static void staticSetShortObject(Short s) { PUB_STATIC_SHORT_OBJECT = s; } public static void staticSetShort(short s) { PUB_STATIC_SHORT = s; } public static void staticSetStringObject(String s) { PUB_STATIC_STRING = s; } public static void staticSetChar(char c) { PUB_STATIC_CHAR = c; } public static void staticSetCharacter(Character c) { PUB_STATIC_CHAR_OBJECT = c ; } // instance setters public void setBoolean(boolean b) { PUB_BOOLEAN = b; } public void setBooleanObject(Boolean b) { PUB_BOOLEAN_OBJECT = b; } public void setByte(byte b) { PUB_BYTE = b; } public void setByteObject(Byte b) { PUB_BYTE_OBJECT = b; } public void setIntegerObject(Integer i) { PUB_INTEGER_OBJECT = i; } public void setInteger(int i) { PUB_INT = i; } public void setDoubleObject( Double d ) { PUB_DOUBLE_OBJECT = d; } public void setDouble(double d) { PUB_DOUBLE = d; } public void setFloatObject(Float f) { PUB_FLOAT_OBJECT = f ; } public void setFloat(float f) { PUB_FLOAT = f; } public void setLongObject(Long l) { PUB_LONG_OBJECT = l ; } public void setLong(long l) { PUB_LONG = l ; } public void setShortObject(Short s) { PUB_SHORT_OBJECT = s; } public void setShort(short s) { PUB_SHORT = s; } public void setStringObject(String s) { PUB_STRING = s; } public void setObject( Object o ) { PUB_OBJECT = o; } public void setChar(char c) { PUB_CHAR = c; } public void setCharacter(Character c) { PUB_CHAR_OBJECT = c ; } // STATIC FIELDS public static final Class PUB_STATIC_FINAL_CLASS = null; public static Class PUB_STATIC_CLASS = PUB_STATIC_FINAL_CLASS; public Class PUB_CLASS = PUB_STATIC_FINAL_CLASS; public Class instanceGetClass() { return PUB_CLASS; } public void setClass( Class c ) { PUB_CLASS = c; } public Class staticGetClass() { return PUB_STATIC_CLASS; } public void staticSetClass( Class c ) { PUB_STATIC_CLASS = c; } public static final boolean PUB_STATIC_FINAL_BOOLEAN = true; public static final byte PUB_STATIC_FINAL_BYTE = Byte.MAX_VALUE; public static final short PUB_STATIC_FINAL_SHORT = Short.MAX_VALUE; public static final int PUB_STATIC_FINAL_INT = Integer.MAX_VALUE; public static final long PUB_STATIC_FINAL_LONG = Long.MAX_VALUE; public static final float PUB_STATIC_FINAL_FLOAT = Float.MAX_VALUE; public static final double PUB_STATIC_FINAL_DOUBLE = Double.MAX_VALUE; public static final char PUB_STATIC_FINAL_CHAR = Character.MAX_VALUE; public static final Object PUB_STATIC_FINAL_OBJECT = new Object(); public static final Boolean PUB_STATIC_FINAL_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); public static final Byte PUB_STATIC_FINAL_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); public static final Short PUB_STATIC_FINAL_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); public static final Integer PUB_STATIC_FINAL_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); public static final Long PUB_STATIC_FINAL_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); public static final Float PUB_STATIC_FINAL_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); public static final Double PUB_STATIC_FINAL_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); public static final Character PUB_STATIC_FINAL_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); public static final String PUB_STATIC_FINAL_STRING = new String("JavaScript Test"); public static boolean PUB_STATIC_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; public static byte PUB_STATIC_BYTE = PUB_STATIC_FINAL_BYTE; public static short PUB_STATIC_SHORT = PUB_STATIC_FINAL_SHORT; public static int PUB_STATIC_INT = PUB_STATIC_FINAL_INT; public static long PUB_STATIC_LONG = PUB_STATIC_FINAL_LONG; public static float PUB_STATIC_FLOAT = PUB_STATIC_FINAL_FLOAT; public static double PUB_STATIC_DOUBLE = PUB_STATIC_FINAL_DOUBLE; public static char PUB_STATIC_CHAR = PUB_STATIC_FINAL_CHAR; public static Object PUB_STATIC_OBJECT = PUB_STATIC_FINAL_OBJECT; public static Boolean PUB_STATIC_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); public static Byte PUB_STATIC_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); public static Short PUB_STATIC_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); public static Integer PUB_STATIC_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); public static Long PUB_STATIC_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); public static Float PUB_STATIC_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); public static Double PUB_STATIC_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); public static Character PUB_STATIC_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); public static String PUB_STATIC_STRING = PUB_STATIC_FINAL_STRING; private static final boolean PRI_STATIC_FINAL_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; private static final byte PRI_STATIC_FINAL_BYTE = PUB_STATIC_FINAL_BYTE; private static final short PRI_STATIC_FINAL_SHORT = PUB_STATIC_FINAL_SHORT; private static final int PRI_STATIC_FINAL_INT = PUB_STATIC_FINAL_INT; private static final long PRI_STATIC_FINAL_LONG = PUB_STATIC_FINAL_LONG; private static final float PRI_STATIC_FINAL_FLOAT = PUB_STATIC_FINAL_FLOAT; private static final double PRI_STATIC_FINAL_DOUBLE = PUB_STATIC_FINAL_DOUBLE; private static final char PRI_STATIC_FINAL_CHAR = PUB_STATIC_FINAL_CHAR; private static final Object PRI_STATIC_FINAL_OBJECT = PUB_STATIC_FINAL_OBJECT; private static final Boolean PRI_STATIC_FINAL_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); private static final Byte PRI_STATIC_FINAL_BYTE_OBJECT = new Byte( PUB_STATIC_FINAL_BYTE ); private static final Short PRI_STATIC_FINAL_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); private static final Integer PRI_STATIC_FINAL_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); private static final Long PRI_STATIC_FINAL_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); private static final Float PRI_STATIC_FINAL_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); private static final Double PRI_STATIC_FINAL_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); private static final Character PRI_STATIC_FINAL_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); private static final String PRI_STATIC_FINAL_STRING = PUB_STATIC_FINAL_STRING; private static boolean PRI_STATIC_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; private static byte PRI_STATIC_BYTE = PUB_STATIC_FINAL_BYTE; private static short PRI_STATIC_SHORT = PUB_STATIC_FINAL_SHORT; private static int PRI_STATIC_INT = PUB_STATIC_FINAL_INT; private static long PRI_STATIC_LONG = PUB_STATIC_FINAL_LONG; private static float PRI_STATIC_FLOAT = PUB_STATIC_FINAL_FLOAT; private static double PRI_STATIC_DOUBLE = PUB_STATIC_FINAL_DOUBLE; private static char PRI_STATIC_CHAR = PUB_STATIC_FINAL_CHAR; private static Object PRI_STATIC_OBJECT = PUB_STATIC_FINAL_OBJECT; private static Boolean PRI_STATIC_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); private static Byte PRI_STATIC_BYTE_OBJECT = new Byte( PUB_STATIC_FINAL_BYTE ); private static Short PRI_STATIC_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); private static Integer PRI_STATIC_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); private static Long PRI_STATIC_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); private static Float PRI_STATIC_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); private static Double PRI_STATIC_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); private static Character PRI_STATIC_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); private static String PRI_STATIC_STRING = PUB_STATIC_FINAL_STRING; protected static final boolean PRO_STATIC_FINAL_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; protected static final byte PRO_STATIC_FINAL_BYTE = PUB_STATIC_FINAL_BYTE; protected static final short PRO_STATIC_FINAL_SHORT = PUB_STATIC_FINAL_SHORT; protected static final int PRO_STATIC_FINAL_INT = PUB_STATIC_FINAL_INT; protected static final long PRO_STATIC_FINAL_LONG = PUB_STATIC_FINAL_LONG; protected static final float PRO_STATIC_FINAL_FLOAT = PUB_STATIC_FINAL_FLOAT; protected static final double PRO_STATIC_FINAL_DOUBLE = PUB_STATIC_FINAL_DOUBLE; protected static final char PRO_STATIC_FINAL_CHAR = PUB_STATIC_FINAL_CHAR; protected static final Object PRO_STATIC_FINAL_OBJECT = PUB_STATIC_FINAL_OBJECT; protected static final Boolean PRO_STATIC_FINAL_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); protected static final Byte PRO_STATIC_FINAL_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); protected static final Short PRO_STATIC_FINAL_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); protected static final Integer PRO_STATIC_FINAL_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); protected static final Long PRO_STATIC_FINAL_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); protected static final Float PRO_STATIC_FINAL_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); protected static final Double PRO_STATIC_FINAL_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); protected static final Character PRO_STATIC_FINAL_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); protected static final String PRO_STATIC_FINAL_STRING = PUB_STATIC_FINAL_STRING; protected static boolean PRO_STATIC_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; protected static byte PRO_STATIC_BYTE = PUB_STATIC_FINAL_BYTE; protected static short PRO_STATIC_SHORT = PUB_STATIC_FINAL_SHORT; protected static int PRO_STATIC_INT = PUB_STATIC_FINAL_INT; protected static long PRO_STATIC_LONG = PUB_STATIC_FINAL_LONG; protected static float PRO_STATIC_FLOAT = PUB_STATIC_FINAL_FLOAT; protected static double PRO_STATIC_DOUBLE = PUB_STATIC_FINAL_DOUBLE; protected static char PRO_STATIC_CHAR = PUB_STATIC_FINAL_CHAR; protected static Object PRO_STATIC_OBJECT; protected static Boolean PRO_STATIC_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); protected static Byte PRO_STATIC_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); protected static Short PRO_STATIC_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); protected static Integer PRO_STATIC_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); protected static Long PRO_STATIC_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); protected static Float PRO_STATIC_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); protected static Double PRO_STATIC_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); protected static Character PRO_STATIC_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); protected static String PRO_STATIC_STRING = PUB_STATIC_FINAL_STRING; static final boolean STATIC_FINAL_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; static final byte STATIC_FINAL_BYTE = PUB_STATIC_FINAL_BYTE; static final short STATIC_FINAL_SHORT = PUB_STATIC_FINAL_SHORT; static final int STATIC_FINAL_INT = PUB_STATIC_FINAL_INT; static final long STATIC_FINAL_LONG = PUB_STATIC_FINAL_LONG; static final float STATIC_FINAL_FLOAT = PUB_STATIC_FINAL_FLOAT; static final double STATIC_FINAL_DOUBLE = PUB_STATIC_FINAL_DOUBLE; static final char STATIC_FINAL_CHAR = PUB_STATIC_FINAL_CHAR; static final Object STATIC_FINAL_OBJECT = PUB_STATIC_FINAL_OBJECT; static final Boolean STATIC_FINAL_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); static final Byte STATIC_FINAL_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); static final Short STATIC_FINAL_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); static final Integer STATIC_FINAL_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); static final Long STATIC_FINAL_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); static final Float STATIC_FINAL_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); static final Double STATIC_FINAL_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); static final Character STATIC_FINAL_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); static final String STATIC_FINAL_STRING = PUB_STATIC_FINAL_STRING; static boolean STATIC_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; static byte STATIC_BYTE = PUB_STATIC_FINAL_BYTE; static short STATIC_SHORT = PUB_STATIC_FINAL_SHORT; static int STATIC_INT = PUB_STATIC_FINAL_INT; static long STATIC_LONG = PUB_STATIC_FINAL_LONG; static float STATIC_FLOAT = PUB_STATIC_FINAL_FLOAT; static double STATIC_DOUBLE = PUB_STATIC_FINAL_DOUBLE; static char STATIC_CHAR = PUB_STATIC_FINAL_CHAR; static Object STATIC_OBJECT = PUB_STATIC_FINAL_OBJECT; static Boolean STATIC_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); static Byte STATIC_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); static Short STATIC_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); static Integer STATIC_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); static Long STATIC_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); static Float STATIC_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); static Double STATIC_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); static Character STATIC_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); static String STATIC_STRING = PUB_STATIC_FINAL_STRING; // INSTANCE FIELDS boolean BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; byte BYTE = PUB_STATIC_FINAL_BYTE; short SHORT = PUB_STATIC_FINAL_SHORT; int INT = PUB_STATIC_FINAL_INT; long LONG = PUB_STATIC_FINAL_LONG; float FLOAT = PUB_STATIC_FINAL_FLOAT; double DOUBLE = PUB_STATIC_FINAL_DOUBLE; char CHAR = PUB_STATIC_FINAL_CHAR; Boolean BOOLEAN_OBJECT = new Boolean( BOOLEAN ); Byte BYTE_OBJECT = new Byte( BYTE ); Integer INTEGER_OBJECT = new Integer( INT ); Long LONG_OBJECT = new Long( LONG ); Float FLOAT_OBJECT = new Float( FLOAT ); Double DOUBLE_OBJECT = new Double( DOUBLE ); Character CHARACTER = new Character( CHAR ); Object OBJECT = new Object(); String STRING = new String("PASSED"); Short SHORT_OBJECT = new Short( SHORT ); public boolean PUB_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; public byte PUB_BYTE = PUB_STATIC_FINAL_BYTE; public short PUB_SHORT = PUB_STATIC_FINAL_SHORT; public int PUB_INT = PUB_STATIC_FINAL_INT; public long PUB_LONG = PUB_STATIC_FINAL_LONG; public float PUB_FLOAT = PUB_STATIC_FINAL_FLOAT; public double PUB_DOUBLE = PUB_STATIC_FINAL_DOUBLE; public char PUB_CHAR = PUB_STATIC_FINAL_CHAR; public Object PUB_OBJECT = new Object(); public Boolean PUB_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); public Byte PUB_BYTE_OBJECT = new Byte( BYTE ); public Integer PUB_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); public Short PUB_SHORT_OBJECT = new Short( PUB_STATIC_FINAL_SHORT ); public Long PUB_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); public Float PUB_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); public Double PUB_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); public Character PUB_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); public String PUB_STRING = PUB_STATIC_FINAL_STRING; private boolean PRI_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; private byte PRI_BYTE = PUB_STATIC_FINAL_BYTE; private short PRI_SHORT = PUB_STATIC_FINAL_SHORT; private int PRI_INT = PUB_STATIC_FINAL_INT; private long PRI_LONG = PUB_STATIC_FINAL_LONG; private float PRI_FLOAT = PUB_STATIC_FINAL_FLOAT; private double PRI_DOUBLE = PUB_STATIC_FINAL_DOUBLE; private char PRI_CHAR = PUB_STATIC_FINAL_CHAR; private Object PRI_OBJECT = PUB_STATIC_FINAL_OBJECT; private Boolean PRI_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); private Byte PRI_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); private Short PRI_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); private Integer PRI_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); private Long PRI_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); private Float PRI_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); private Double PRI_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); private Character PRI_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); private String PRI_STRING = PUB_STATIC_FINAL_STRING; protected boolean PRO_BOOLEAN = PUB_STATIC_FINAL_BOOLEAN; protected byte PRO_BYTE = PUB_STATIC_FINAL_BYTE; protected short PRO_SHORT = PUB_STATIC_FINAL_SHORT; protected int PRO_INT = PUB_STATIC_FINAL_INT; protected long PRO_LONG = PUB_STATIC_FINAL_LONG; protected float PRO_FLOAT = PUB_STATIC_FINAL_FLOAT; protected double PRO_DOUBLE = PUB_STATIC_FINAL_DOUBLE; protected char PRO_CHAR = PUB_STATIC_FINAL_CHAR; protected Object PRO_OBJECT = PUB_STATIC_FINAL_OBJECT; protected Boolean PRO_BOOLEAN_OBJECT = new Boolean(PUB_STATIC_FINAL_BOOLEAN); protected Byte PRO_BYTE_OBJECT = new Byte(PUB_STATIC_FINAL_BYTE); protected Short PRO_SHORT_OBJECT = new Short(PUB_STATIC_FINAL_SHORT); protected Integer PRO_INTEGER_OBJECT = new Integer(PUB_STATIC_FINAL_INT); protected Long PRO_LONG_OBJECT = new Long(PUB_STATIC_FINAL_LONG); protected Float PRO_FLOAT_OBJECT = new Float(PUB_STATIC_FINAL_FLOAT); protected Double PRO_DOUBLE_OBJECT = new Double(PUB_STATIC_FINAL_DOUBLE); protected Character PRO_CHAR_OBJECT = new Character(PUB_STATIC_FINAL_CHAR); protected String PRO_STRING = PUB_STATIC_FINAL_STRING; // STATIC ARRAYS public static byte[] staticGetByteArray() { return PUB_STATIC_ARRAY_BYTE; } public static char[] staticGetCharArray() { return PUB_STATIC_ARRAY_CHAR; } public static double[] staticGetDoubleArray() { return PUB_STATIC_ARRAY_DOUBLE; } public static short[] staticGetShortArray() { return PUB_STATIC_ARRAY_SHORT; } public static long[] staticGetLongArray() { return PUB_STATIC_ARRAY_LONG; } public static int[] staticGetIntArray() { return PUB_STATIC_ARRAY_INT; } public static float[] staticGetFloatArray() { return PUB_STATIC_ARRAY_FLOAT; } public static Object[] staticGetObjectArray() { return PUB_STATIC_ARRAY_OBJECT; } // INSTANCE ARRAYS public byte[] getByteArray() { return PUB_STATIC_ARRAY_BYTE; } public char[] getCharArray() { return PUB_STATIC_ARRAY_CHAR; } public double[] getDoubleArray() { return PUB_STATIC_ARRAY_DOUBLE; } public short[] getShortArray() { return PUB_STATIC_ARRAY_SHORT; } public long[] getLongArray() { return PUB_STATIC_ARRAY_LONG; } public int[] getIntArray() { return PUB_STATIC_ARRAY_INT; } public float[] getFloatArray() { return PUB_STATIC_ARRAY_FLOAT; } public Object[] getObjectArray() { return PUB_STATIC_ARRAY_OBJECT; } // INSTANCE ARRAY SETTERS public void setByteArray(byte[] b) { PUB_STATIC_ARRAY_BYTE = b; } public void setCharArray(char[] c) { PUB_STATIC_ARRAY_CHAR = c; } public void setDoubleArray(double[] d) { PUB_STATIC_ARRAY_DOUBLE = d; } public void setShortArray(short[] s) { PUB_STATIC_ARRAY_SHORT = s; } public void setLongArray(long[] l) { PUB_STATIC_ARRAY_LONG = l; } public void setIntArray(int[] i) { PUB_STATIC_ARRAY_INT= i; } public void setFloatArray(float[] f) { PUB_STATIC_ARRAY_FLOAT = f; } public void setObjectArray(Object[] o) { PUB_STATIC_ARRAY_OBJECT = o; } // STATIC ARRAY DEFINITIONS public static byte PUB_STATIC_ARRAY_BYTE[] = new String( PUB_STATIC_FINAL_STRING ).getBytes(); public static char PUB_STATIC_ARRAY_CHAR[] = new String( PUB_STATIC_FINAL_STRING).toCharArray(); public static double PUB_STATIC_ARRAY_DOUBLE[] = { Double.MIN_VALUE, Double.MAX_VALUE, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }; public static short PUB_STATIC_ARRAY_SHORT[] = { Short.MIN_VALUE, Short.MAX_VALUE }; public static int PUB_STATIC_ARRAY_INT[] = { Integer.MIN_VALUE, Integer.MAX_VALUE}; public static long PUB_STATIC_ARRAY_LONG[] = { Long.MIN_VALUE, Long.MAX_VALUE}; public static float PUB_STATIC_ARRAY_FLOAT[] = { Float.MIN_VALUE, Float.MAX_VALUE, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY }; public static Object PUB_STATIC_ARRAY_OBJECT[] = { PUB_STATIC_ARRAY_BYTE, PUB_STATIC_ARRAY_CHAR, PUB_STATIC_ARRAY_DOUBLE, PUB_STATIC_ARRAY_SHORT, PUB_STATIC_ARRAY_INT, PUB_STATIC_ARRAY_LONG, PUB_STATIC_ARRAY_FLOAT }; public static String PUB_STATIC_ARRAY_STRING[] = { "JavaScript", "LiveConnect", "Java" }; // public static JSObject PUB_STATIC_ARRAY_JSOBJECT[] // INSTANCE ARRAY DEFINITIONS public byte PUB_ARRAY_BYTE[] = new String( PUB_STATIC_FINAL_STRING ).getBytes(); public char PUB_ARRAY_CHAR[] = new String( PUB_STATIC_FINAL_STRING ).toCharArray(); public double PUB_ARRAY_DOUBLE[] = { Double.MIN_VALUE, Double.MAX_VALUE, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }; public short PUB_ARRAY_SHORT[] = { Short.MIN_VALUE, Short.MAX_VALUE }; public int PUB_ARRAY_INT[] = { Integer.MIN_VALUE, Integer.MAX_VALUE}; public long PUB_ARRAY_LONG [] = { Long.MIN_VALUE, Long.MAX_VALUE}; public float PUB_ARRAY_FLOAT[] = { Float.MIN_VALUE, Float.MAX_VALUE, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY }; public Object PUB_ARRAY_OBJECT[] = { PUB_ARRAY_BYTE, PUB_ARRAY_CHAR, PUB_ARRAY_DOUBLE, PUB_ARRAY_SHORT, PUB_ARRAY_INT, PUB_ARRAY_LONG, PUB_ARRAY_FLOAT }; public String PUB_ARRAY_STRING[] = { "JavaScript", "LiveConnect", "Java" }; // public JSObject PUB_ARRAY_JSOBJECT[] }closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000755000175000017500000000000014433667662031513 5ustar apoapo././@LongLink0000644000000000000000000000015100000000000011600 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/CVS/closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000755000175000017500000000000014433667662031513 5ustar apoapo././@LongLink0000644000000000000000000000016300000000000011603 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/CVS/Repositoryclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000644000175000017500000000010114433667662031505 0ustar apoapomozilla/js/tests/src/com/netscape/javascript/qa/liveconnect/call ././@LongLink0000644000000000000000000000016000000000000011600 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/CVS/Entriesclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000644000175000017500000000006014433667662031511 0ustar apoapo/Call_001.java/1.1/Wed May 26 22:14:29 1999// D ././@LongLink0000644000000000000000000000015500000000000011604 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/CVS/Rootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000644000175000017500000000006314433667662031514 0ustar apoapo:pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot ././@LongLink0000644000000000000000000000016200000000000011602 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/Call_001.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/call/0000644000175000017500000000776114433667662031530 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.netscape.javascript.qa.liveconnect.call; import com.netscape.javascript.qa.liveconnect.*; import netscape.javascript.*; /** * Instantiate a JavaScript object. From java, use JSObject.call to invoke a * method of that object. Verify the return value. * * call global method. call built in method. cal user defined method. call * method that takes no arguments. * * @see netscape.javascript.JSObject * * @author christine m begle */ public class Call_001 extends LiveConnectTest { public Call_001() { super(); } public static void main( String[] args ) { Call_001 test = new Call_001(); test.start(); } public void setupTestEnvironment() { super.setupTestEnvironment(); } public void executeTest() { Object data[] = getDataArray(); for ( int i = 0; i < data.length; i++ ) { JSObject jsObject = getJSObject( (Object[]) data[i] ); call( jsObject, (Object[]) data[i] ); } } /** * Create and return a JSObject using data in the data array. * * @param data Object array containing name of JSObject, and assignment * expression * @return the JSObject */ public JSObject getJSObject( Object[] data ) { String constructor = data[0] +" = " + data[1]; JSObject theThis = (JSObject) global.eval( constructor ); return theThis; } /** * Use JSObject call to invoke a JavaScript method. Verify the value and * class of the object returned. * * @param theThis JSObject whose method will be called will be checked * @param data Object array containing the name of the method, arguments * to be passed to that method, and the expected return value of the * method. */ public void call( JSObject theThis, Object[] data ) { String exception = null; String method = (String) data[2]; Object args[] = (Object[]) data[3]; Object eValue = data[4]; Object aValue = null; Class eClass = null; Class aClass = null; try { aValue = theThis.call( method, (Object[]) args ); if ( aValue != null ) { eClass = eValue.getClass(); aClass = aValue.getClass(); } } catch ( Exception e ) { exception = theThis +".call( " + method + ", " + args+" ) threw "+ e.toString(); file.exception += exception; e.printStackTrace(); } finally { if ( aValue == null ) { } else { // check the value of the property addTestCase( "[ getMember returned " + aValue +" ] "+ theThis+".call( "+method+", "+args+" ).equals( "+eValue+" )", "true", aValue.equals(eValue) +"", exception ); // check the class of the property addTestCase ( "[ "+ aValue+".getClass() returned "+aClass.getName()+"] "+ aClass.getName() +".equals( " +eClass.getName() +" )", "true", aClass.getName().equals(eClass.getName()) +"", exception ); } } } /** * Get the data array, which is an object array data arrays, which are * also object arrays. * *
        *
      • Identifier for JavaScript object *
      • Assignment expression to initialize JavaScript object *
      • Method of the JavaScript object *
      • Arguments to pass to the method *
      • Value returned by the method *
      * * To add test cases to this test, modify this method. * * @return the data array. */ public Object[] getDataArray() { Object d0[] = { new String( "boo" ), // 0 identifier new String( "new Boolean()"), // 1 assignment expression new String( "valueOf" ), // 2 method null, // 3 argument array new Boolean( "false" ), // 4 return value }; Object d1[] = { new String( "date" ), new String( "new Date(0)"), new String( "getUTCFullYear" ), null, new Double( "1970" ), }; Object dataArray[] = { d0, d1 }; return dataArray; } } ././@LongLink0000644000000000000000000000016700000000000011607 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/JSObjectConversion.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/JSObj0000644000175000017500000000465214433667662031501 0ustar apoapo/* -*- Mode: java; tab-width: 8 -*- * Copyright (C) 1997, 1998 Netscape Communications Corporation, All Rights Reserved. */ package com.netscape.javascript.qa.liveconnect; import netscape.javascript.JSObject; /** * * This is the same as DataTypeClass, but only contains conversion fields * and methods for JSObject. JSObject is not included in DataTypeClass * so that tests can be run on Rhino (which does not implement JSObject). * * Attempt to get and set the public fields. * * XXX need to override toString, toNumber with values in * static fields. * * XXX override both instance and static versions of the method * @author christine m begle */ public class JSObjectConversion { /** * Override toNumber * */ public static double PUB_DOUBLE_REPRESENTATION = 0.2134; public double doubleValue() { return PUB_DOUBLE_REPRESENTATION; } public int toNumber() { return PUB_NUMBER_REPRESENTATION; } /** * Override booleanValue * */ public boolean booleanValue() { return PUB_BOOLEAN_REPRESENTATION; } public boolean PUB_BOOLEAN_REPRESENTATION = true; public static int PUB_NUMBER_REPRESENTATION = 2134; /** * Override toString */ public String toString() { return PUB_STRING_REPRESENTATION; } public static String PUB_STRING_REPRESENTATION = "DataTypeClass Instance"; public static JSObject staticGetJSObject() { return PUB_STATIC_JSOBJECT; } public JSObject getJSObject() { return PUB_JSOBJECT; } public static void staticSetJSObject(JSObject jso) { PUB_STATIC_JSOBJECT = jso; } public void setJSObject(JSObject jso) { PUB_JSOBJECT = jso; } public JSObject[] createJSObjectArray(int length) { PUB_JSOBJECT_ARRAY = new JSObject[length]; return PUB_JSOBJECT_ARRAY; } public static JSObject[] staticCreateJSObjectArray(int length) { PUB_STATIC_JSOBJECT_ARRAY = new JSObject[length]; return PUB_STATIC_JSOBJECT_ARRAY; } // STATIC FIELDS public static final JSObject PUB_STATIC_FINAL_JSOBJECT = null; public static JSObject PUB_STATIC_JSOBJECT = PUB_STATIC_FINAL_JSOBJECT; public JSObject PUB_JSOBJECT = PUB_STATIC_FINAL_JSOBJECT; public JSObject[] PUB_JSOBJECT_ARRAY; public static JSObject[] PUB_STATIC_JSOBJECT_ARRAY; }././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/datatypes/closure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/datat0000755000175000017500000000000014433667662031616 5ustar apoapo././@LongLink0000644000000000000000000000017400000000000011605 Lustar rootrootclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/datatypes/DataTypes_008.javaclosure-compiler-20130227+dfsg1/rhino/testsrc/tests/src/com/netscape/javascript/qa/liveconnect/datat0000644000175000017500000000647114433667662031630 0ustar apoapo/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.netscape.javascript.qa.liveconnect.datatypes; import com.netscape.javascript.qa.liveconnect.*; /** * Tests for converting Java byte[] to JavaScript variables. * * Create a JavaScript variable. Use JSObject.eval to call a Java method * that returns a Java array. Assign the array to the JavaScript variable. * *

      * * Iterate through the array in JavaScript. Get the value, and compare it * to the value in the original Java array. * *

      * * Use JSObject.getMember to get the JavaScript variable. Verify that the * array is the same object that was used to assign the original JS variable. * * @see com.netscape.javascript.qa.liveconnect.DataTypesClass * @see netscape.javascript.JSObject * * @author christine m begle */ public class DataTypes_008 extends LiveConnectTest { public DataTypes_008() { super(); } public static void main( String[] args ) { DataTypes_008 test = new DataTypes_008(); test.start(); } public void setupTestEnvironment() { super.setupTestEnvironment(); global.eval( "var DT = "+ "Packages.com.netscape.javascript.qa.liveconnect.DataTypeClass"); global.eval( "var dt = new DT();" ); } public void executeTest() { doArrayTest( "DT.PUB_STATIC_ARRAY_BYTE;", true); doArrayTest( "dt.getByteArray();", true ); doArrayTest( "dt.PUB_ARRAY_BYTE;", false ); } /** * Assign a java byte array to a JavaScript variable in the following ways: *